# Architectural Decisions This document records key architectural decisions made for this project. ## Table of Contents - [ADR-001: No Data Caching](#adr-001-no-data-caching) *(Superseded by ADR-004)* - [ADR-002: Static Dates Instead of Git Integration](#adr-002-static-dates-instead-of-git-integration) - [ADR-003: CI/CD with GitHub Actions](#adr-003-cicd-with-github-actions) - [ADR-004: Application-Level Data Caching](#adr-004-application-level-data-caching) --- ## ADR-001: No Data Caching **Status:** Superseded by [ADR-004](#adr-004-application-level-data-caching) **Date:** 2025-11-30 ### Context The CV data (JSON files) is loaded from disk on every request. A caching layer could reduce disk I/O and improve response times. ### Decision **No caching will be implemented for CV data.** ### Rationale 1. **Project Size**: This is a small, personal CV website with minimal traffic 2. **Simplicity**: Caching adds complexity (cache invalidation, memory management, TTL configuration) 3. **Performance is Already Good**: JSON file loading takes <10ms, which is acceptable 4. **Hot Reload**: In development, we want fresh data on every request for testing 5. **YAGNI**: We don't need caching until we have evidence of performance issues ### Consequences - Simple, maintainable code - No cache invalidation bugs - Slightly higher disk I/O (negligible for this scale) - If traffic increases significantly, this decision can be revisited --- ## ADR-002: Static Dates Instead of Git Integration **Status:** Accepted **Date:** 2025-11-30 ### Context Previously, the project had a feature to dynamically fetch project start dates from git repository first commit dates using `exec.CommandContext` to run `git log` commands. ### Decision **Git command execution has been removed. Use static dates in JSON files instead.** ### Rationale 1. **Security Risk**: Executing shell commands (even with path validation) poses injection risks 2. **Symlink Bypass**: Path validation can be bypassed with symbolic links 3. **Unnecessary Complexity**: Static dates in JSON are simpler and more maintainable 4. **Control**: Static dates give full control over what's displayed 5. **Performance**: No external process spawning ### Implementation Instead of `gitRepoUrl` in project data, use `startDate` directly: ```json { "title": "My Project", "startDate": "2024-06", "current": true } ``` ### Consequences - More secure codebase - Simpler implementation - Manual date updates required when adding new projects - No external dependencies on git binary --- ## ADR-003: CI/CD with GitHub Actions **Status:** Implemented **Date:** 2025-11-30 ### Context The project needs automated testing, linting, and deployment. ### Decision **GitHub Actions is used for CI/CD with two workflows:** 1. **test.yml** - Runs on PRs and pushes to main/develop 2. **deploy.yml** - Deploys to production on push to main ### Workflows #### Test Workflow (`.github/workflows/test.yml`) Triggers: `push` and `pull_request` to `main` and `develop` branches Steps: 1. Checkout code 2. Setup Go 1.25.1 3. Install and verify dependencies 4. Run golangci-lint 5. Run unit tests with coverage 6. Generate coverage report 7. Check coverage threshold (target: 70%) 8. Upload coverage to Codecov 9. Run benchmarks 10. Build binary 11. Upload artifacts #### Deploy Workflow (`.github/workflows/deploy.yml`) Triggers: `push` to `main` branch or manual dispatch Steps: 1. SSH into production server 2. Fix repository permissions 3. Stash any local changes 4. Pull latest changes 5. Restart systemd service 6. Verify health check ### Required Secrets - `SSH_PRIVATE_KEY` - SSH private key for server access - `SSH_HOST` - Server IP or domain - `SSH_USER` - SSH username - `SSH_PORT` (optional, default: 22) - `SERVICE_NAME` (optional, default: cv) - `REPO_PATH` (optional, default: /home/txeo/Git/yo/cv) ### Consequences - Automated quality checks on every PR - Consistent deployment process - Health check verification after deployment - Coverage tracking with Codecov - Binary artifacts available for download --- ## ADR-004: Application-Level Data Caching **Status:** Accepted **Date:** 2025-12-06 **Supersedes:** [ADR-001](#adr-001-no-data-caching) ### Context As the CV site evolved to support multiple languages and increased usage, the original decision (ADR-001) to avoid caching was reconsidered. While the site traffic remains modest, the benefits of eliminating per-request file I/O became clear: 1. **Consistency**: Every request reads the same data 2. **Performance**: Eliminates disk I/O from hot paths 3. **Reliability**: Fail-fast at startup catches data errors early 4. **Simplicity**: No cache invalidation needed (data is static) ### Decision **Implement application-level data caching with startup-time loading.** The `internal/cache` package provides: - `DataCache` struct holding CV and UI data for all supported languages - Single load at application startup - Thread-safe read access via `sync.RWMutex` - Language-keyed retrieval (`GetCV(lang)`, `GetUI(lang)`) ### Implementation ```go // At startup (main.go) dataCache, err := cache.New([]string{"en", "es"}) if err != nil { log.Fatalf("Failed to initialize data cache: %v", err) } // In handlers cv := h.dataCache.GetCV(lang) ui := h.dataCache.GetUI(lang) ``` ### Rationale 1. **Zero Per-Request I/O**: Data loaded once, served from memory 2. **Fail-Fast**: All data issues caught at startup, not runtime 3. **Thread-Safe**: `sync.RWMutex` optimized for read-heavy workloads 4. **Minimal Complexity**: Simple map-based storage, no TTL/invalidation 5. **Testable**: 95.7% test coverage, including concurrency tests ### Consequences - **Positive:** - Faster request handling (no disk I/O) - Earlier error detection (startup validation) - Consistent data across requests - Simple, well-tested implementation - **Considerations:** - Requires application restart to pick up data changes - Memory usage increases slightly (minimal - ~KB per language) - Deep copies required when handlers mutate data ### Documentation See [23-DATA-CACHE.md](23-DATA-CACHE.md) for complete API reference and usage patterns. --- ## How to Add New Decisions When making significant architectural decisions, add a new section following this template: ```markdown ## ADR-XXX: Title **Status:** Proposed | Accepted | Deprecated | Superseded **Date:** YYYY-MM-DD ### Context What is the issue that we're seeing that is motivating this decision? ### Decision What is the change that we're proposing? ### Rationale Why is this the best choice? ### Consequences What are the results of this decision? ```