mirror of
https://github.com/tiennm99/ghstats.git
synced 2026-05-20 12:24:08 +00:00
734ac21be9
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).
5.1 KiB
5.1 KiB
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.gosuffix.
Package structure
main.goat 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.godecides whether to exit or warn.
No hidden state
- Profile fetchers mutate the
*Profileargument 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_000constant — a comment explains why fixed-point and not float. - Hidden invariant or constraint. Example: the comment on
contributionYearQuerynoting themaxRepositories: 100cap. - Workaround for an upstream behavior. Example: clamping the current year's
totonowso 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
truncateinsvg.gobefore 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 throughformatTick, which abbreviates ≥ 1000 tok/M/Bso 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 inwidth − 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:ordocs: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.modlists norequireentries. - 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.