Go
Load release supplements
Determine the Go version in use before making Go-specific decisions:
- Find the
go.modgoverning the files in scope and read itsgodirective. Use that major version as the target, including when a newertoolchaindirective is present. - If no applicable
go.modexists, rungo versionand extract the major version from output such asgo version go1.26.3 darwin/arm64. - If neither source establishes a version, use an explicit version from the user's request. Otherwise ask for the target instead of assuming one.
Determine the model's knowledge cutoff. Compare the target Go version and the cutoff with the release table, then load every listed sibling skill whose version is at or below the target and whose release falls after the cutoff. Load selected skills oldest first.
| Go version | Release date | Skill |
|---|---|---|
| 1.21 | 2023-08-08 | go-1-21 |
| 1.22 | 2024-02-06 | go-1-22 |
| 1.23 | 2024-08-13 | go-1-23 |
| 1.24 | 2025-02-11 | go-1-24 |
| 1.25 | 2025-08-12 | go-1-25 |
| 1.26 | 2026-02-10 | go-1-26 |
| 1.27 | 2026-08-19 | go-1-27 |
Coding style
Name non-trivial function literals
When a function literal passed as an argument contains multiple statements or branches, assign it to a descriptively named local variable before the call. Keep only short, obvious callbacks inline.
walkFn := func(path string, entry fs.DirEntry, err error) error {
if err != nil {
return err
}
if entry.IsDir() {
return nil
}
return visit(path)
}
return filepath.WalkDir(root, walkFn)
Let code breathe
Use blank lines to separate distinct phases of a function, such as initialisation, local helper definitions, the main operation, fallback handling, derived values, and the return. Keep tightly related statements together; do not compress an entire multi-phase function into one uninterrupted block.
func readFile(path string) (Document, error) {
document := Document{Source: path}
decodeFn := func(record Record) error {
document.Records = append(document.Records, record)
return nil
}
if err := decode(path, decodeFn); err != nil {
return Document{}, err
}
if document.ID == "" {
document.ID = fallbackID(path)
}
document.Title = titleFrom(document.Records)
return document, nil
}
Handle errors immediately
Handle an error immediately after the operation that produced it, preferably with if err := operation(); err != nil. Do not keep an err variable alive while later work runs and then return it at the end of the function. A plain err := binding is acceptable when the producing operation is the final operation and the error is returned immediately afterwards.
if err := writeDocument(document); err != nil {
return err
}
recordDocumentWritten(document)
return nil
Use slices helpers for membership
For Go 1.21 or later, use slices.Contains instead of manually ranging over a slice solely to test whether it contains a comparable value. Use slices.ContainsFunc when membership requires a predicate.
if slices.Contains(formats, format) {
return formatDocument(format)
}
Collapse repeated parameter types
When adjacent function parameters have the same type, write the type once after the final name. Apply the same style to function declarations, methods, and function literals.
func join(left, right string) string {
return left + right
}
Keep struct literals consistently shaped
Write a struct literal either entirely on one line or across multiple lines with exactly one field assignment per line. Never put multiple field assignments on the same line inside a multi-line struct literal.
point := Point{X: 1, Y: 2}
point := Point{
X: 1,
Y: 2,
}
Extract dense iteration bodies
Keep iteration loops focused on control flow. When each iteration scans, converts, and enriches a value, extract that work into a helper named for the result.
for rows.Next() {
conversation, err := scanConversation(rows, source)
if err != nil {
return nil, err
}
conversations = append(conversations, conversation)
}
return conversations, rows.Err()
Pass contexts explicitly
Never store a context.Context in a struct. Pass it explicitly as the first parameter to every operation that needs it, usually named ctx.
type Worker struct{}
func (worker *Worker) Run(ctx context.Context) error {
return process(ctx)
}
Prefer formatting around literal text
Use concatenation when directly joining string variables. Prefer fmt.Sprintf when combining values with fixed text instead of alternating between variables, literals, and + operators.
message := fmt.Sprintf("%s abc %s", left, right)
Prefer stream processing
Prefer APIs that consume an io.Reader or produce output through an io.Writer. Process data incrementally instead of buffering an entire input or output solely to pass it to another API. Buffer only when the operation genuinely requires the complete value in memory.
var payload Payload
if err := json.NewDecoder(request.Body).Decode(&payload); err != nil {
return err
}