go-idiomatic
Go engineering standards for this repo — package layout, error handling, logging, testing, concurrency and design patterns. TRIGGER — load BEFORE writing or editing Go, not after: any .go, go.mod, go.sum or go.work file is touched, Go or Golang is named, or the working directory contains go.mod and the task is implementation, refactor, review or test-writing. Plain feature requests in a Go repo ("add an endpoint", "extract a helper", "write a test for this") count; the user will not say "idiomatic". Load it even when another skill (tdd, simplify) is active. SKIP only when no Go file is involved and Go is not named.
- License 许可
- MIT
- 最近更新
- 2026-11-11
- Stars
- ★ 0
- Forks
- 0
01 前言 Front Matter
- name
- go-idiomatic
- description
- Go engineering standards for this repo — package layout, error handling, logging, testing, concurrency and design patterns. TRIGGER — load BEFORE writing or editing Go, not after: any .go, go.mod, go.sum or go.work file is touched, Go or Golang is named, or the working directory contains go.mod and the task is implementation, refactor, review or test-writing. Plain feature requests in a Go repo ("add an endpoint", "extract a helper", "write a test for this") count; the user will not say "idiomatic". Load it even when another skill (tdd, simplify) is active. SKIP only when no Go file is involved and Go is not named.
- allowed-tools
- Read, Edit, Write, Glob, Grep, Bash(go:*), Bash(golangci-lint:*), Bash(git:*), Agent
02 资源文件 20
- 参考文档evals/evals.json9.5 KB
- 其他evals/files/optional-field/go.mod37 B
- 脚本evals/files/optional-field/internal/user/user.go313 B
- 其他evals/files/refactor-store/go.mod34 B
- 脚本evals/files/refactor-store/helpers/helpers.go125 B
- 脚本evals/files/refactor-store/store/store.go979 B
- 脚本evals/files/server-settings/cmd/app/main.go248 B
- 其他evals/files/server-settings/go.mod32 B
- 脚本evals/files/server-settings/internal/server/server.go275 B
- 参考文档references/concurrency.md14.3 KB
- 参考文档references/database.md6.0 KB
- 参考文档references/errors.md9.1 KB
- 参考文档references/exec.md4.9 KB
- 参考文档references/http-client.md2.7 KB
- 参考文档references/logging.md3.2 KB
- 参考文档references/middleware.md2.8 KB
- 参考文档references/optional.md5.0 KB
- 参考文档references/patterns.md4.0 KB
- 参考文档references/secrets.md3.0 KB
- 参考文档references/value-vs-pointer.md3.5 KB
03 SKILL.md 正文
Go Idiomatic
This repo's rules for Go, where they differ from or sharpen general Go practice. The rules below apply to every change; the references hold the detail and worked examples.
Where to Look
Route to the right reference before reading further:
| If you're working on… | Load this |
|---|---|
| Goroutines, channels, sync primitives, errgroup, leaks, races | references/concurrency.md |
Adding a sentinel or error type, translating errors between packages or to HTTP responses, logging where an error came from, cleanup in defer, panic |
references/errors.md |
| HTTP middleware: request Content-Type, CSRF, session cookie attributes | references/middleware.md |
| Storing session or reset tokens, password reset or change, encrypting a column | references/secrets.md |
Giving main or a component a logger, choosing a level, CLI output vs logs |
references/logging.md |
| Interface design, zero values, identifier types, typed value sets, anti-patterns | references/patterns.md |
| Writing or reviewing tests, benchmarks, fuzzing, test databases | the go-testing skill |
Optional fields or return values — when to use Option[T] |
references/optional.md |
| Pointer vs value receivers, mutation vs optionality | references/value-vs-pointer.md |
Scaffolding a Go HTTP client for a third-party API, writing a NewXxxClient, or attaching auth to outbound HTTP calls |
references/http-client.md |
Shelling out with os/exec to git, gh or another CLI, parsing its --json output, faking it in tests |
references/exec.md |
| Storing data in SQL: store package boundary, transactions, NULLs, constraint errors, pool setup | references/database.md |
Rendering html/template for htmx: page vs fragment vs redirect, HX-* headers, .tmpl files |
the htmx-go-templates skill |
Errors and Logging
Nearly every change returns an error or reports one, so these rules apply to all Go work, not only to a task described as error handling:
- Wrap with
fmt.Errorf("<verb> <noun>: %w", err). Error strings are lowercase, with no punctuation and no "failed to". - Handle an error once: return it with context, or log it at the top of the chain. Never both.
- Export a sentinel only when a caller in another package branches on it. Define it where the condition is detected, and translate a dependency's errors into your own at the package seam.
- Never discard an error with
_ =. Join a cleanup error into the returned error, or log it atWarnwhen there is nothing to return to. paniconly for programmer errors:Musthelpers on constant input, impossible cases.- Only components where an error stops travelling (handlers, workers, long-lived services) take
an injected
*slog.Logger, as a constructor field. Code below them returns errors and takes no logger.mainbuilds the one logger; nothing callslog.*,slog.Infoorslog.Default(). - Log messages are constant; variable data goes in snake_case key-value attributes, with
errfor the error. Never log secrets or personal data. - Output the user asked for goes to an injected
io.Writer, not to the logger.
A routine return fmt.Errorf(...) or log line needs only these rules. Load references/errors.md
when adding a sentinel or error type, turning an error into an HTTP response, wrapping a driver or
outbound-call error, or handling a cleanup error, and references/logging.md when
wiring a logger, choosing a level or separating CLI output.
Testing
Writing, editing or reviewing a _test.go file: load the go-testing skill, which owns how Go
tests are written, including test databases. What to test (the kinds of test, what each is
worth, the reusable patterns) is the repo's testing-policy when it ships one.
Code Organisation
- Applications keep
mainincmd/<app>/main.go, which only wires dependencies, and everything else underinternal/.pkg/is for library modules imported from outside the repo; in an application it signals a public API that doesn't exist. - Extract shared logic on the third repetition across meaningfully different packages, not
the second. A helper coupled to one domain lives in
internal/next to its primary caller. - Name a package by what it does (
stringx,idgen,timeutil).utilsis allowed only when it is used across many packages and imports nothing frominternal/.helpersandcommonare never allowed: a real name is waiting to be found. - No package-level state such as a global
*sql.DBset ininit(). Pass dependencies through constructors. mainparses the environment once into a typed config struct, validates it, and exits on a bad value at startup rather than on the first request that needs it. Constructors receive only the fields they use, not the whole struct. Nothing belowcmd/callsos.Getenv.
Function Design
-
Past 3–4 arguments, group them into a struct, or use functional options (
opts ...Option) for a constructor that will grow; a new option is backward-compatible, a new positional argument is not. -
Declare complex arguments as named locals before the call instead of nesting constructor calls inline:
sentAt := time.Now().Add(-24 * time.Hour) priority := Priority(3) client.Send(NewMessage(sender, recipient, sentAt, priority)) -
Guard and return early; the happy path stays at the bottom, unindented, with no
elseafter areturn. Useswitchoverif/else ifchains. -
Use pointers for mutation and measurably large structs, values for small immutable types. Never use
*Tto mean "might not be set"; that isOption[T].
Naming and Comments
Name functions by what they return or do (ParseConfig, IsExpired) and booleans as predicates
(canRetry). Abbreviate only by Go convention (ctx, err, id, r, w).
~/.claude/rules/comments.md decides what earns a comment. It is the authority and this
skill does not restate or soften it. Read it before editing a file and apply it as written,
including its line cap and its exported-identifiers-only rule for doc comments. internal/
is not public API. When reviewing Go code for comment quality, use the clean-comments skill.
Do not instruct it to match the file's existing comment density.
Tooling
Run gofmt, goimports, go vet ./... and golangci-lint run before finishing. Leave
mechanical rules to the linter rather than restating them here. Never add //nolint to existing
violations: fix them, or leave the rule off until they are fixed.
See Also
| Skill | When to reach for it |
|---|---|
go-testing |
How tests are written: assertions, doubles, tables, test databases |
tdd |
TDD cycle, red-green-refactor, writing the test first |
clean-comments |
Audit and remove explanatory comments across Go code |
improve-codebase-architecture |
Identify structural improvements, shallow modules |
simplify |
Review changed code for quality, reuse, and efficiency after implementation |
04 同仓库更多 Skills
vault-recall
Answer the user's conceptual or technical question using their Obsidian vault as well as general knowledge. Use when the user asks a question (not a task) whose topic appears in the vault MOC digest from session start. Not for task instructions, not mid-task, and not for writing to the vault (that is to-vault).
ingest
Ingest one external source (an article, course lesson, doc, PDF or URL) into the user's Obsidian vault: save the original to raw/, then build wiki/ pages per the vault's § Ingest. Use when the user types /ingest, or says "ingest this", "add this source to my wiki", "put this article/lesson in the vault as a wiki page". Works from any repo. Routing rule: someone else's words go to wiki/ (this skill); the user's own learnings and decisions go to notes/ (/to-vault).
to-tickets
Breaks a source into independently-grabbable GitHub tickets. For a code feature the source is the plan (`docs/plans/<feature>.md`, from /to-plan) and every new or changed symbol lands in exactly one ticket, its signatures copied into the ticket body; a non-code feature is cut from the PRD's slices. Cuts tickets for one feature per run. If a code feature has no approved plan, offers /to-plan instead of cutting from prose. In a repo that runs command-centre, also emits `[[task]]` blocks into its config; in a plain treepad repo, emits a Batch Manifest instead so the tickets materialise as stacked worktrees.
to-seams
Records an agreed-but-unlanded cross-repo interface as a seam: the file a consumer's agent gets pasted into its prompt as ground truth, plus the `[[seam]]` block command-centre reads for the retirement pointer. Use when two repos need to build against an interface neither has landed yet — "record a seam for X", "we agreed on this schema, seam it", "add a seam so Y can build against Z's new field". Halts for approval before writing either file.
to-roadmap
Prioritises settled features into a Now / Next / Later kanban board, rendered as a self-contained ./ideas/roadmap.html. Each card is a feature, not a ticket — one card later becomes several tickets via /to-prd -> /to-plan -> /to-tickets. Boards the features in ./docs/approach.md, or the "## Accepted ideas" in ./ideas/CONTEXT.md when there is no approach doc. The approach doc is settled truth: this assigns tiers and reasons about ordering, it never re-litigates what was decided.
to-prd
Turns one feature idea into a PRD built on journeys: a one-line problem, journeys (each ending in an observable state) and delivery slices that say what is usable after each. Interviews, explores the codebase, and saves to docs/prds/<feature>.md. Covers exactly one feature: given a multi-feature source, ask which one. Reads any approach doc or ideate report as priors so it doesn't re-ask what's settled, but needs none. Starts the per-feature spine (/to-prd -> /to-plan -> /to-tickets).