Files
ghstats/docs/code-standards.md
T
tiennm99 734ac21be9 docs: resync after card fixes — title auto-fit, truncate, abbreviated ticks (#15)
Several recent code changes hadn't propagated to the docs:

- design-guidelines
  * Card frame title row: document the 11–15 px auto-shrink (not a flat
    15 px anymore).
  * Donut Top-N: already 7 (updated earlier).
  * Bar-chart section renamed to cover weekday + by-year too; document
    the peak-vs-dim highlight convention and the niceTicks yMax ≥ max
    invariant.
  * Add Heatmap / Stat-column (streak) / List (top-starred) card
    sections — they were missing entirely.
  * Rewrite "Text overflow" from "we don't truncate" to the current
    truth (truncate helper, formatTick abbreviations).
  * Replace dangling `truncateName` reference with `truncate`.

- code-standards
  * SVG output standards: call out truncate, formatTick abbreviation,
    header auto-fit so the card-review gate reflects what the renderers
    actually do.

- codebase-summary
  * Layout tree: svg.go / axis.go comments list the helpers they now
    contain; productive.go notes the weekday histogram.
  * demo/ tree shows the index vs per-theme split.
  * Data-flow diagram includes Weekday in the productive pass.
  * Test coverage row lists TestCardsFitFrame / TestFitTitleFontSize /
    TestNiceTicksCoversMax — the new invariant guards.

- system-architecture
  * Shared primitives list adds renderWeekday and renderHeatmap; donut
    blurb updated to "top 7".
  * New "Chart-geometry invariants" block documents niceTicks ceiling,
    formatTick abbreviation, header auto-fit.

- project-roadmap
  * Phase 7.5 bullet updated to describe the index + per-theme demo
    split (was single-README TOC).
2026-04-19 10:22:03 +07:00

105 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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(ctx, 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` (`&`, `<`, `>`, `"`, `'`).
- Long user-controlled strings (bio, names, company, repo slugs) go through `truncate` in `svg.go` before rendering — never print raw multi-line input that could push a later element off-screen.
- Body numbers formatted via `formatInt` (thousands separators). Y-axis tick numbers go through `formatTick`, which abbreviates ≥ 1000 to `k` / `M` / `B` so no label exceeds 4 chars.
- Stable viewbox per card (`340×200`) matching github-profile-summary-cards.
- Titles render through `header()` which auto-shrinks font-size (15 → 11 px) when the string wouldn't fit in `width 20 4`.
- 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.
## Card review checklist (pre-release)
In addition to the compile/test gate above, any change that touches files under `internal/card/` **must** be reviewed against `docs/design-guidelines.md` → "Fit-the-frame invariant" before merging. Specifically:
- Render the dracula theme against the **tiny / typical / adversarial** profile fixtures and visually verify nothing overflows or overlaps.
- No hard-coded dimensions that only work for the reviewer's own profile (names, digit counts, year spans).
- Every right-anchored value or text-anchor="middle" element fits inside its column / safety margin.
- The CI-built `demo/<theme>/` gallery is the canonical "dracula on the author's profile" view; don't rely on a local run alone.
This is a hard gate: a card that overflows on a realistic profile does not ship, even if tests pass.
## 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.