Files
ghstats/docs/code-standards.md
T
tiennm99 94e13d2c65 docs: resync with current state across all project docs
- design-guidelines: every dimension was stale — card frame 340x200
  (was 500x220), corner radius 6, title 15px at (20,30), row y0/dy,
  donut centre (250,110) r=55/30, topN=5, legend y0=55 dy=20, bar chart
  area [35,325]x[45,155], area chart [28,312]x[45,150], icon scale 0.75.
- code-standards: FetchContributionsAllTime signature now ctx-first,
  viewbox 500x220 → 340x200.
- codebase-summary: test coverage lists main_test.go +
  TestDonutSingleSlice/Empty; filename convention says plain kebab-case
  (no numeric prefix).
- project-overview-pdr: forks/private defaults now on, not off.
- project-roadmap: add Phase 7 (Marketplace polish — resize, numeric-
  prefix drop, v1 floating tag, rename-rollback). Renumber planned
  phases 8-11. Fix "hard width 500 px" limitation.
- deployment-guide: document update-major-tag job; note
  Marketplace listing name is `ghstats-cards`.
2026-04-19 00:09:00 +07:00

92 lines
3.8 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` (`&`, `<`, `>`, `"`, `'`).
- Numbers formatted via `formatInt` with thousands separators.
- Stable viewbox per card (`340×200`) matching github-profile-summary-cards.
- 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.