infra/stacks/f1-stream/files/.planning/codebase/CONVENTIONS.md
Viktor Barzin c7c7047f1c [ci skip] Flatten module wrappers into stack roots
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.
2026-02-22 15:13:55 +00:00

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: resourceMu for 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:

  1. Standard library imports (context, encoding/json, fmt, log, etc.)
  2. Blank line
  3. Local f1-stream packages (internal/auth, internal/models, etc.)
  4. Blank line
  5. 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.go line 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 via json.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.go line 123: // Explicitly do NOT copy X-Frame-Options or CSP
  • Example from internal/auth/auth.go line 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.go line 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 implementation
  • internal/store/: Data persistence (users.go, streams.go, sessions.go, scraped.go, store.go)
  • internal/server/: HTTP routing and middleware
  • internal/scraper/: Reddit scraping logic
  • internal/proxy/: HTTP proxy with rate limiting
  • internal/models/: Type definitions only

Struct Composition:

  • Server struct holds dependencies injected at construction (line 15-21 in internal/server/server.go)
  • Methods extend functionality through receiver pattern
  • No inheritance, composition via embedded types used sparingly

Convention analysis: 2026-02-17