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.
37 ## Entry point and context
39 - `main` calls `run()` and handles its returned error. Do not use
41 - `run()` creates the SIGINT/SIGTERM signal context and defers its
42 cancellation. Pass that context through operations and let cleanup
43 finish before `main` exits.
44 - Use `ctx` for a context and `cancel` for its cancellation function.
45 Call `defer cancel()` immediately after you create the context.
46 - Reuse `ctx` when a child context replaces its parent. Use a different
47 name only when both contexts stay in use. Return `ctx.Err()` directly.
48 - Derive each context from the context of the caller. When work must
49 finish even after the caller is cancelled, such as a final write, use
50 `context.WithoutCancel` of the caller's context. Use
51 `context.Background()` only where no caller context exists: in `run`,
52 and in methods that follow `io.Closer`.
53 - Name a duration that limits one operation after that operation, and
54 end the name with `Timeout`, so that the name says what it limits
55 without its comment. Define it in the file that applies it, not in the
60 - Handle every returned error, including an error assigned to `_`. Never
61 use a blank assignment to hide an unchecked error from a linter.
62 Handle meaningful errors. When the user approves an intentional
63 discard, call the operation with an explicit `// nolint: errcheck`
64 directive and a comment that gives the reason, not a blank assignment.
65 The `check` skill gives the approval rule.
66 - Reuse an existing `err` for later operations in the same scope. Use a
67 different variable only to keep or join errors.
68 - Use `errors.Is` and `errors.As` to examine errors. Do not compare an
69 error directly with a sentinel, because errors can be wrapped.
70 `errorlint` checks comparisons and type assertions on errors.
71 - When a function has more than one error path, wrap each returned error
72 with the context of its operation. A function with one error path can
73 return the error directly. `wrapcheck` checks package and interface
74 calls, with the exceptions in `.golangci.yml`. Review local calls and
75 the number of error paths by hand.
76 - When deferred cleanup can fail, use a named `err` result and join the
77 cleanup error with `errors.Join`.
81 - Log with the `*slog.Logger` of the part (`daemonLogger`). The handler
82 formats the line; do not format values into the message.
83 - Write the message as a short event in plain words, such as "receive
84 failed" or "subagent done". Do not explain what it means or what the
86 - Put names, IDs, counts, and durations in attributes, with short keys
87 (`name`, `id`, `after`, `retry`). Put the cause in `err`; the handler
89 - Use info for normal events, warn for a failure that is retried or
90 loses nothing, and error for a failure that loses data or stops a
92 - Never log secrets, tokens, message text, or phone numbers. Redact IDs
93 as the existing code does.
97 - Keep function signatures on one line. Write function bodies on more
98 than one line, also for anonymous functions, deferred cleanup, and
100 - Do not use short `if` initializers.
101 - Use a one-letter receiver name for each method.
102 - Use field names in struct literals. Put each field on its own line
103 when you set more than one field. This includes the cases of a
104 table-driven test. A small type of two fields that acts as one value,
105 such as a key or a coordinate pair, can use a positional literal on
107 - Put an empty line between logical blocks and after multiline
108 declarations, assignments, literals, and calls. The only exception
109 after a multiline construct is its immediate error check. Separate a
110 multiline literal from a following loop, even when the loop uses that
111 literal. Keep a single-line declaration with its immediate use, and an
112 operation with its immediate error check. This does not waive the
113 separator after a multiline construct. `wsl_v5` and `nlreturn` check
114 only part of this rule. Review the other boundaries by hand.
115 - Use GNU-style command flags: two dashes for long flags, such as
116 `--cwd`, and one dash only for one-letter flags, such as `-h`.
117 - Prefer the Go standard library. Prefer Go over cgo.
118 - Do not add compile-time interface assertions. Prove compatibility
119 through actual assignments and calls.
120 - Use `// nolint: name` for lint directives.
124 - After a change to `go.mod` or `go.sum`, update `vendorHash` in
125 `nix/package.nix`. While the module has no dependencies, `vendorHash`
130 - Add tests for meaningful behavior. Do not add tests that only repeat
131 implementation details.
132 - Use `testify/require` for assertions. `testifylint` checks the
134 - Name a table-driven case slice `testCases` and its range variable
135 `tc`. A subtest name describes the behavior, not a generic outcome or
137 - Each file with tests must have the name of its owning production file
138 with `_test` before `.go`: `memory_test.go` tests `memory.go`. Move
139 scenario and integration tests into their owner's test file. Do not
140 add an empty production file to make a test filename valid. Only
141 `_helper_test.go` files are exempt from this pairing rule.
142 - In an ordinary `_test.go` file, order declarations as imports,
143 constants, variables, tests, supporting types, constructors, methods,
144 then helpers. This tests-first order is the exception to the
145 production order. Put all supporting types before their constructors
147 - A `_helper_test.go` file has no tests. Order its declarations as
148 imports, constants, variables, types, constructors, methods, then
149 helpers, as in a production file.
150 - Keep a helper that only one test file uses in that file. Put a helper
151 that more than one test file uses in a file without tests. Name that
152 file for what it holds, and end the name with `_helper_test.go`.
153 - Supply valid required dependencies in tests. Do not make a dependency
154 optional only to make a test simpler.