docs: add project documentation set

Seven canonical docs under docs/ per the project structure convention:

- project-overview-pdr.md   users, non-goals, requirements
- codebase-summary.md       directory layout, module responsibilities
- system-architecture.md    runtime phases, GraphQL flow, SVG primitives
- code-standards.md         YAGNI/KISS/DRY, Go conventions, commit rules
- design-guidelines.md      frame dimensions, theme roles, per-card specs
- deployment-guide.md       Action/binary/Docker paths, release process
- project-roadmap.md        done phases (0-5), planned phases (6-9)

All files under the 800-line cap. Each leans on tables; grammar
sacrificed for concision per project rules.
This commit is contained in:
2026-04-18 22:10:09 +07:00
parent 0168fd1f56
commit 8b3a8e1932
7 changed files with 740 additions and 0 deletions
+91
View File
@@ -0,0 +1,91 @@
# Code Standards
## Principles
Applied in order: **YAGNI → KISS → DRY**.
- No feature flags, no plugin systems, no abstractions for hypothetical callers.
- One way to do a thing. A helper emerges only after the second call site, never before.
- Three similar lines beats a premature abstraction. Extract when a fourth arrives.
## Go conventions
### File naming
- Multi-word files use `snake_case` (Go ecosystem standard): `repos_per_language.go`, `contributions_all_time.go`.
- Single-word files stay single-word: `client.go`, `model.go`, `profile.go`.
- Test files adjacent to the unit under test with `_test.go` suffix.
### Package structure
- `main.go` at repo root — the CLI wrapper only.
- All reusable code under `internal/` so it can't accidentally become an API.
- Each package owns one concern: `github` = network, `card` = render, `theme` = palette data.
### Error handling
- Wrap errors with `fmt.Errorf("%w: …", err)` when adding context; bare return when the caller already has enough context.
- Sentinel errors (`errors.New`) only when callers need to type-check. We have none today — don't invent them.
- Network/API errors in fetchers bubble up; `main.go` decides whether to exit or warn.
### No hidden state
- Profile fetchers mutate the `*Profile` argument in-place (`FetchContributionsAllTime(p, opts)`) — explicit ownership, no package-level caches.
- Renderers are pure functions of `(*Profile, theme.Theme)`. No side effects, no goroutines.
### Comments
Default: **no comments**. Exceptions:
- **Why non-obvious code is non-obvious.** Example: the `scaleFactor = 10_000` constant — a comment explains why fixed-point and not float.
- **Hidden invariant or constraint.** Example: the comment on `contributionYearQuery` noting the `maxRepositories: 100` cap.
- **Workaround for an upstream behavior.** Example: clamping the current year's `to` to `now` so GitHub doesn't reject future timestamps.
Never write comments that describe what well-named code does (`// increment counter`). Never reference "the recent fix" or "for issue #42" — that belongs in git.
### Function length
- No hard limit. 200-line functions are fine when the logic is linear.
- Extract a helper only when (a) the same shape repeats twice, or (b) a block needs an independent name to be read at the call site.
### Exported vs unexported
- Start unexported. Export only when a test file or another package needs the symbol.
- Types on the public API: `Profile`, `RepoInfo`, `LangStat`, `LangEdge`, `DailyContribution`, `FetchOptions`, `Client`, `Theme`, `Card`.
- Everything else stays lowercase.
## SVG output standards
- Always XML-escape user-controlled strings through `escapeXML` (`&`, `<`, `>`, `"`, `'`).
- Numbers formatted via `formatInt` with thousands separators.
- Stable viewbox per card (`500×220` for most, `500×220` for profile too).
- No `<script>` tags, no event handlers. Cards are pure markup.
## Testing
- Unit tests in the same package, no mocking of http.Client — we test rendering and pure helpers.
- Network tests are omitted; integration verification is manual (`./ghstats -token ...`).
- `go vet ./...` clean before every commit.
## Commit conventions
- Conventional commit prefixes: `feat:`, `fix:`, `refactor:`, `docs:`, `test:`, `build:`, `ci:`, `chore:`.
- Scope is optional: `refactor(card): …`.
- Never use `chore:` or `docs:` for `.claude/` directory changes (project rule).
- No AI references. No "generated by" footers. Keep the subject ≤ 72 chars.
## Pre-commit checklist
```
go vet ./...
go test ./...
go build ./...
```
All three must pass. If a test is failing, fix the test before committing — don't skip.
## Dependency policy
- **Stdlib only.** `go.mod` lists no `require` entries.
- If a future feature needs a third-party module, add it in a dedicated commit with justification in the message.
- Keep binary size under 15 MB.