- Plan before acting: For ≤2 files, state a brief plan then implement. For ≥3 files, write a step-by-step plan first.
- Read before editing: Always read files before modifying. Search over guessing.
- Fix root causes: Do not produce local workarounds—fix the real issue.
- Be concise: Give short status updates during multi-step work. Present a short summary when done.
- Go 1.24+. Format with
go fmt ./.... - Imports: Group stdlib separate from external. Let gofmt manage ordering.
- Files: Use
lower_snake_case.go. Keep ≤1000 lines; split proactively. - Naming: Packages are lowercase and short. Exported identifiers need GoDoc. Avoid stutter.
- Types: Use
anyoverinterface{}. Prefer concrete types overinterface{}. - Errors: Wrap with
%w. Useerrors.Is/As. Never ignore errors or use_ = call(). - Signatures: Keep on one line when ≤100 columns. Only wrap genuinely long signatures.
- Slice/map nil: Do not check nil before
len.len(nil)returns 0. Uselen(x) == 0directly.
- Always place a newline after
{and before}forif,for,switch,func,type. - No single-line blocks:
if cond { do() }→ use multiple lines. - Short struct literals are fine inline:
&T{A: 1}. Break long literals to one field per line with trailing commas.
Order declarations as:
- Types (public, then private) in a single
type (...)block when practical - Constants (public, then private)
- Variables (public, then private)
- Public functions
- Public methods
- Private functions
- Private methods
No commented-out code—delete dead code.
- Always check errors. Never discard with
_. - Strong contracts: Goa validates payloads at boundaries. Do not re-validate inside service code.
- No defensive programming: Do not add nil/empty guards for values guaranteed by construction, Goa, or prior validation.
- Validate only at boundaries: HTTP/gRPC handlers, event consumers, DB results, third-party APIs,
ctx.Value(), type assertions, required map lookups. - Fail fast: Unexpected states are bugs. Return precise errors or panic—do not silently recover or skip.
- Never edit
gen/: Always regenerate. - DSL validation: Put validations (lengths, enums, formats) in the design. Do not re-validate in code.
- Avoid
Any: Use concrete types to enable gRPC generation.
- Use NameScope helpers for type references:
GoTypeRef,GoFullTypeRef,GoTypeName. Never concatenate strings for types. - Let Goa decide pointer/value semantics. Do not force
pointer=trueexcept in transport validation. - Keep helper visibility minimal: If logic is shared only inside one codegen area, keep it package-private or move it under an
internalpackage. Do not export helpers from a parent package just to share them across sibling generators. - Avoid pass-through wrappers: When two helper functions differ only by forwarding arguments or hard-coding
nil, collapse them into a single implementation instead of adding an extra layer.
- Every exported type, function, method, and field must have a GoDoc comment explaining its contract—like Go stdlib documentation.
| Action | Policy |
|---|---|
git clean/stash/reset/checkout |
FORBIDDEN |
go clean -cache |
FORBIDDEN during normal work |
Edit gen/ directly |
FORBIDDEN |
| Changes ≥3 files | Describe plan first |
| New dependencies | Explain why first |
- Write table-driven tests in
*_test.go. - Name tests
TestXxx. Keep fast and deterministic. - Use
testify/requirefor assertions. - Prefer
t.Errorfovert.Fatalfso tests report multiple failures.
dsl/: Public DSL definitions (dot imports allowed per.golangci.yml)expr/: Internal AST and validationcodegen/: Generators for transports, types, docshttp/,grpc/,jsonrpc/: Transport-specific codegenmiddleware/: Built-in interceptorspkg/: Core runtimecmd/goa/: CLI source
make lint # Run linters
make test # Run tests
cd cmd/goa && go install . # Install CLI locally- After modifying goa source,
goa genandgoa exampleautomatically compile and use your changes—no manual rebuild needed. goa gendeletes and recreates the entiregen/directory.goa exampleonly creates new files; it does not overwrite existingcmd/files.
To reproduce a codegen issue:
- Create
~/src/repros/<issue>/design/design.go go mod init <issue>in the issue directorygoa gen <issue>/designgo mod tidygo mod edit -replace goa.design/goa/v3=$HOME/src/goagoa gen <issue>/designagain with local goa- Optional:
goa example <issue>/design
Do not rely on nil vs empty to encode presence. Goa uses omitempty—both nil and empty serialize as "missing". If empty is valid, do not mark the field as required.