Patterns & Conventions
What I've learned about how we write code in this project.
Go Style
Loop Idiom
Always range N for integer loops. Never for i := 0; i < N; i++. This is a hard rule Fritz set early.
Table-Driven Tests
Every test file uses t.Run with named subtests. The pattern:
tests := []struct {
name string
// inputs...
// expected...
}{
{"descriptive name", ...},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// test body
})
}
Mock Streams for Handlers
Handler tests construct a bytes.Buffer with a raw request, pass it as a stream, and read the response. No QUIC, no network, fast tests.
t.TempDir() for Fixtures
Never create test fixtures in the working directory. Always t.TempDir() — Go cleans it up automatically.
Development Workflow
Build Rule
Always make client or go build -o bin/<name> ./cmd/<name>/. Never bare go build ./cmd/<name>/ — binaries must land in bin/.
Pre-Commit
Run bash pre-commit.sh before committing. Formats, vets, and lints all modules.
Conventional Commits
Module-scoped: feat(server): description, fix(client): description. This drives auto-versioning and release tagging (server/v0.1.0, etc.).
Philosophy
Small and Incremental
Every change should be the smallest working increment. Get something tested and working before moving on. Don't batch up large changes.
Robustness First
Handle the error. Test the edge case. Make it correct before making it elegant.
Simplest Solution
Short functions, clear names, obvious flow. If I find myself writing a comment to explain what code does, the code should be rewritten to not need the comment. Comments explain why, not what.
No Over-Engineering
Don't add features beyond what's asked. Don't refactor surroundings while fixing a bug. Don't add abstractions for one-time operations. Three similar lines are better than a premature helper function.
What I've Learned About Working With Fritz
Fritz values directness. Short answers over long explanations. Working code over architecture astronautics. He'll push back on unnecessary complexity and he's usually right when he does. The best sessions are when we move fast through small, clean changes — each one tested, each one committed. Momentum matters.