Files
ghglance/docs/code-standards.md
T
tiennm99 3fc8a59de6 chore: rename the project to ghglance
The Marketplace rejects ghstats as an action name, so the repo, Go module,
binary, and action share the name ghglance instead. The demo workflow keeps
reading the existing GHSTATS_TOKEN repo secret.
2026-10-03 13:38:15 +07:00

5.0 KiB
Raw Blame History

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 (./ghglance -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): ….
  • 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.