# 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-005: Self-Hosted Frontend Dependencies](#adr-005-self-hosted-frontend-dependencies) --- ## 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. --- ## ADR-005: Self-Hosted Frontend Dependencies **Status:** Accepted **Date:** 2026-05-14 ### Context HTMX (1.9.10) and Hyperscript (0.9.14) were loaded from the unpkg.com CDN. This introduced a single point of failure — if unpkg goes down, the site loses all interactivity. Additionally, each CDN request adds DNS resolution, TLS negotiation, and potential redirect overhead. HTMX 1.9.10 was also two major versions behind the current 2.0.10 release. ### Decision **Self-host HTMX and Hyperscript as vendored files. Remove unpkg.com from the Content Security Policy.** - `static/htmx/htmx.min.js` — HTMX 2.0.10 (51KB) - `static/hyperscript/_hyperscript.min.js` — Hyperscript 0.9.91 (172KB) - Iconify remains on jsdelivr CDN (icon rendering, acceptable external dependency) ### Rationale 1. **No external SPOF**: The site functions fully even if all CDNs are down 2. **Faster loading**: Same-origin assets skip DNS lookup and TLS handshake 3. **Smaller CSP surface**: `unpkg.com` removed from `script-src` whitelist 4. **Version upgrade**: HTMX 1.9.10 → 2.0.10 with zero breaking changes (all `hx-*` attributes are compatible; `hx-head` moved from extension to built-in) 5. **Cache alignment**: Libraries cached alongside the site's own assets ### Consequences - **Positive:** - Zero dependency on unpkg.com availability - Reduced CSP attack surface - HTMX 2.0 features available (built-in head support) - Faster page loads (no cross-origin requests for core libs) - **Considerations:** - Version updates require manually downloading new files - Must track current versions in documentation - File sizes add ~223KB to the repository (acceptable trade-off) ### Documentation See [02-MODERN-WEB-TECHNIQUES.md](02-MODERN-WEB-TECHNIQUES.md) Phase 11 for implementation details. --- ## 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? ```