3 description: Go style rules. Use before you write or review Go code.
10 - Keep Go code in one root `main` package. Do not add `internal/`,
11 `cmd/`, or one-file packages until the user asks for them.
12 - Keep `main.go` limited to the entry point. Name other files for their
13 function. Do not create `main_test.go`.
14 - A file carries its own context. Keep each struct and all of its
15 methods in one file. Give a separate responsibility its own type.
16 - When code in one file gets a user in another file for an unrelated
17 purpose, move it to a file named for its function in the same change.
18 - Start each package-level name that belongs to one feature or
19 integration with the name of that feature. Start the names of its
20 files with the same name. Keep a name generic only when more than one
22 - Write type names with a capital letter, so that a variable can have
23 the name of its type. Write all other package-level names in lower
25 - Write all the letters of an initialism in one case. `revive` checks
27 - Order declarations as imports, constants, variables, types,
28 constructors, methods, then standalone helpers. Types include structs,
29 interfaces, and aliases. Put all types before any constructor or
30 method, also when a file holds more than one type. The Tests section
31 gives the exception for files with tests.
32 - Keep each type's methods in one group. Do not put a helper between
33 them. Order functions by first use within their declaration group, not
34 across groups. Entry points do not go before types or constructors
35 merely because they are entry points.
36 - Group related constants in one `const (...)` block and related
37 variables in one `var (...)` block. Put an empty line between entries
40 ## Entry point and context
42 - `main` calls `run()` and handles its returned error. Do not use
44 - `run()` creates the SIGINT/SIGTERM signal context and defers its
45 cancellation. Pass that context through operations and let cleanup
46 finish before `main` exits.
47 - Use `ctx` for a context and `cancel` for its cancellation function.
48 Call `defer cancel()` immediately after you create the context.
49 - Reuse `ctx` when a child context replaces its parent. Use a different
50 name only when both contexts stay in use. Return `ctx.Err()` directly.
51 - Derive each context from the context of the caller. When work must
52 finish even after the caller is cancelled, such as a final write, use
53 `context.WithoutCancel` of the caller's context. Use
54 `context.Background()` only where no caller context exists: in `run`,
55 and in methods that follow `io.Closer`.
56 - Name a duration that limits one operation after that operation, and
57 end the name with `Timeout`, so that the name says what it limits
58 without its comment. Define it in the file that applies it, not in the
63 - Handle every returned error, including an error assigned to `_`. Never
64 use a blank assignment to hide an unchecked error from a linter.
65 Handle meaningful errors. When the user approves an intentional
66 discard, call the operation with an explicit `// nolint: errcheck`
67 directive and a comment that gives the reason, not a blank assignment.
68 The `check` skill gives the approval rule.
69 - Reuse an existing `err` for later operations in the same scope. Use a
70 different variable only to keep or join errors.
71 - Use `errors.Is` and `errors.As` to examine errors. Do not compare an
72 error directly with a sentinel, because errors can be wrapped.
73 `errorlint` checks comparisons and type assertions on errors.
74 - Do not match the text of an error from another package. When an
75 external fault leaves no other way, test it through the real code
76 path, so that a change of the text fails a test.
77 - Handle a documented sentinel error explicitly when it does not show a
79 - When a function has more than one error path, wrap each returned error
80 with the context of its operation. A function with one error path can
81 return the error directly. `wrapcheck` checks package and interface
82 calls, with the exceptions in `.golangci.yml`. Review local calls and
83 the number of error paths by hand.
84 - When deferred cleanup can fail, use a named `err` result and join the
85 cleanup error with `errors.Join`.
87 ## Logs and cancellation
89 - Log with the `*slog.Logger` of the part (`daemonLogger`). The handler
90 formats the line; do not format values into the message.
91 - Write the message as a short event in plain words, such as "receive
92 failed" or "subagent done". Do not explain what it means or what the
94 - Put names, IDs, counts, and durations in attributes, with short keys
95 (`name`, `id`, `after`, `retry`). Put the cause in `err`; the handler
97 - Use info for normal events, warn for a failure that is retried or
98 loses nothing, and error for a failure that loses data or stops a
100 - Cancellation of a service or request context is an expected stop, not
101 a failure. Do not log it as an error, and do not hide all errors only
102 because the context is canceled. A joined error with an independent
103 failure stays a failure.
104 - Use the worker or request context for this check. An operation that
105 reaches its own timeout while its worker is active stays a failure.
106 - Never log secrets, tokens, message text, or phone numbers. Redact IDs
107 as the existing code does.
111 - Keep function signatures on one line. Write function bodies on more
112 than one line, also for anonymous functions, deferred cleanup, and
114 - Do not use short `if` initializers.
115 - Use a one-letter receiver name for each method.
116 - Use field names in struct literals. Put each field on its own line
117 when you set more than one field. This includes the cases of a
118 table-driven test. A small type of two fields that acts as one value,
119 such as a key or a coordinate pair, can use a positional literal on
121 - Put an empty line between logical blocks, such as two independent `if`
122 statements. Put an empty line before and after loops, switches,
123 selects, standalone blocks, and multiline declarations, assignments,
124 literals, and calls, when there is an adjacent statement. These
125 boundaries also apply when a loop immediately uses the declaration
127 - The only exception after a multiline construct is its immediate error
128 check. Keep a single-line declaration with its immediate use, and an
129 operation with its immediate error check. A result assertion does not
130 waive the separator after a multiline construct. `wsl_v5` and
131 `nlreturn` check only part of these rules. Review the other boundaries
133 - Group imports in this order: standard library, external dependencies,
134 project imports. Review these groups by hand when the formatter does
136 - Use GNU-style command flags: two dashes for long flags, such as
137 `--cwd`, and one dash only for one-letter flags, such as `-h`.
138 - Prefer the Go standard library. Prefer Go over cgo.
139 - Do not add compile-time interface assertions. Prove compatibility
140 through actual assignments and calls.
141 - Use `// nolint: name` for lint directives.
145 - After a change to `go.mod` or `go.sum`, update `vendorHash` in
146 `nix/package.nix`. While the module has no dependencies, `vendorHash`
151 - Add tests for behavior changes. Do not add tests that only repeat
152 implementation details, and do not add a test file without behavior
154 - Use `testify/require` for assertions. `testifylint` checks the
156 - Name a table-driven case slice `testCases` and its range variable
157 `tc`. A subtest name describes the behavior, not a generic outcome or
159 - Each file with tests must have the name of its owning production file
160 with `_test` before `.go`: `memory_test.go` tests `memory.go`. Move
161 scenario and integration tests into their owner's test file. Do not
162 add an empty production file to make a test filename valid. Only
163 `_helper_test.go` files are exempt from this pairing rule.
164 - In an ordinary `_test.go` file, order declarations as imports,
165 constants, variables, tests, supporting types, constructors, methods,
166 then helpers. This tests-first order is the exception to the
167 production order. Put all supporting types before their constructors
169 - A `_helper_test.go` file has no tests. Order its declarations as
170 imports, constants, variables, types, constructors, methods, then
171 helpers, as in a production file.
172 - Keep a helper that only one test file uses in that file. Put a helper
173 that more than one test file uses in a file without tests. Name that
174 file for what it holds, and end the name with `_helper_test.go`.
175 - Supply valid required dependencies in tests. Do not make a dependency
176 optional or add a nil fallback only to make a test simpler.