Remove the module "xxx" { source = "./module" } indirection layer
from all 66 service stacks. Resources are now defined directly in
each stack's main.tf instead of through a wrapper module.
- Merge module/main.tf contents into stack main.tf
- Apply variable replacements (var.tier -> local.tiers.X, renamed vars)
- Fix shared module paths (one fewer ../ at each level)
- Move extra files/dirs (factory/, chart_values, subdirs) to stack root
- Update state files to strip module.<name>. prefix
- Update CLAUDE.md to reflect flat structure
Verified: terragrunt plan shows 0 add, 0 destroy across all stacks.
6.2 KiB
6.2 KiB
Coding Conventions
Analysis Date: 2026-02-17
Naming Patterns
Files:
- Go packages: lowercase, single word when possible (e.g.,
auth,store,proxy) - Go files: lowercase with descriptive names (e.g.,
server.go,middleware.go,reddit.go) - JSON files: snake_case (e.g.,
users.json,sessions.json,scraped_links.json) - JavaScript files: camelCase (e.g.,
app.js,auth.js,streams.js)
Functions and Methods:
- Go: PascalCase for exported functions (e.g.,
New,BeginRegistration,ServeHTTP) - Go: camelCase for unexported functions (e.g.,
randomID,isF1Post,normalizeURL) - JavaScript: camelCase for all functions (e.g.,
showToast,switchTab,doRegister)
Variables and Fields:
- Go: camelCase for local variables (e.g.,
streams,userID,sessionTTL) - Go: PascalCase for exported struct fields (e.g.,
ID,Username,IsAdmin) - Go: prefixed mutex pattern:
resourceMufor mutex protecting resource (e.g.,streamsMu,usersMu,sessionsMu) - JavaScript: camelCase for all variables (e.g.,
currentUser,beginResp,container)
Types and Constants:
- Go: PascalCase for exported types (e.g.,
Server,Auth,Store,User) - Go: camelCase for unexported types (e.g.,
contextKey,bucket,redditListing) - Go: SCREAMING_SNAKE_CASE for constants (e.g.,
maxBodySize,rateLimit,bucketCleanup)
Interfaces:
- Go context keys use private types with exported constants (e.g.,
type contextKey string; const userKey contextKey = "user")
Code Style
Formatting:
- Language: Go (no automated formatter config detected, using standard gofmt conventions)
- Import organization: Standard library → local packages (separated by blank line)
- File layout: Package declaration → Imports → Constants/Variables → Types → Functions
Linting:
- No eslint or golangci-yml configuration found
- Go code follows idiomatic Go conventions: error checking, defer cleanup, interface composition
Import Organization
Go Order:
- Standard library imports (context, encoding/json, fmt, log, etc.)
- Blank line
- Local f1-stream packages (internal/auth, internal/models, etc.)
- Blank line
- External third-party packages (github.com/...)
Example from internal/auth/auth.go:
import (
"crypto/rand"
"encoding/json"
"fmt"
"log"
"net/http"
"regexp"
"sync"
"time"
"f1-stream/internal/models"
"f1-stream/internal/store"
"github.com/go-webauthn/webauthn/webauthn"
)
JavaScript:
- No explicit import organization (vanilla JavaScript, no modules)
- HTML file loads scripts in order: utils → app → auth/streams
Error Handling
Patterns:
- Go: Explicit error return as second value (e.g.,
err := operation(); if err != nil { return err }) - Go: Wrapping errors with context:
fmt.Errorf("operation failed: %w", err) - Go: String matching on error messages for classification (see
internal/server/server.goline 196-205) - Go: Logging errors with
log.Printf()for non-critical failures,log.Fatalf()for startup errors - Go: HTTP errors returned via
http.Error(w, message, statusCode)for API endpoints - JavaScript: Try-catch blocks for async operations, error fields in UI (e.g.,
errEl.textContent = err.error || 'Operation failed')
HTTP Error Responses:
- Standard JSON format:
{"error":"description"} - Success responses vary by endpoint (JSON arrays,
{"ok":true}, encoded objects viajson.NewEncoder)
Logging
Framework: log package (standard library)
Patterns:
- Informational:
log.Printf("message with %v context", value) - Errors:
log.Printf("operation failed: %v", err) - Startup:
log.Fatalf("critical: %v", err)for initialization failures - Component prefixes:
log.Printf("scraper: action description")
Example from internal/scraper/scraper.go:
log.Printf("scraper: starting scrape")
log.Printf("scraper: error after %v: %v", time.Since(start).Round(time.Millisecond), err)
log.Printf("scraper: done in %v, added %d new links (total: %d)", time.Since(start).Round(time.Millisecond), added, len(existing))
Comments
When to Comment:
- Explain WHY, not WHAT (code shows what, comments explain reasoning)
- Used for non-obvious logic or security concerns
- Example from
internal/proxy/proxy.goline 123:// Explicitly do NOT copy X-Frame-Options or CSP - Example from
internal/auth/auth.goline 120:// Store user temporarily - will be committed on finish
Patterns:
- Short inline comments before complex sections
- Package-level comments before exported types explaining purpose
- Security/business logic gets explained
Function Design
Size: Functions keep complexity low, typically 20-50 lines; larger operations split across helpers
Parameters:
- Receiver methods use pointer receivers:
func (s *Store) GetSession(token string) ... - Constructor pattern returns initialized type and error:
func New(...) (*Type, error) - HTTP handlers follow signature:
func(w http.ResponseWriter, r *http.Request)
Return Values:
- Errors always returned as last value:
(result, error) - Multiple return values when needed:
(*Type, error)or([]Type, error) - HTTP handlers write directly to ResponseWriter, return via
http.Error()or direct writes - Query methods return nil for "not found" rather than error (see
internal/store/users.goline 33)
Module Design
Exports:
- Exported names start with capital letter (e.g.,
New,User,Server) - Unexported helpers start with lowercase
- Types exported when they're part of public API
- Helper functions (e.g.,
isF1Post,normalizeURL) kept unexported
Barrel Files: Not used; single concerns per file
Package Organization:
internal/auth/: Authentication and WebAuthn implementationinternal/store/: Data persistence (users.go, streams.go, sessions.go, scraped.go, store.go)internal/server/: HTTP routing and middlewareinternal/scraper/: Reddit scraping logicinternal/proxy/: HTTP proxy with rate limitinginternal/models/: Type definitions only
Struct Composition:
Serverstruct holds dependencies injected at construction (line 15-21 ininternal/server/server.go)- Methods extend functionality through receiver pattern
- No inheritance, composition via embedded types used sparingly
Convention analysis: 2026-02-17