Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 60 additions & 31 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,6 @@ just deps

## Code style

These conventions are shared across every Go repository in the organization and
are specified in the `go-code-standards` capability in
[osapi-io/specs](https://github.com/osapi-io/specs). They are restated here
because a contributor should not have to read another repository to learn how to
write code in this one. Where the two disagree, the specification wins.

Go code is formatted by [gofumpt] and linted using [golangci-lint], enforced by
CI.

Expand All @@ -73,13 +67,27 @@ just go-fmt # Auto-fix formatting
just go-vet # Run linter
```

golangci-lint runs errcheck, errname, goimports, govet, prealloc, predeclared,
revive, and staticcheck. Generated files (`*.gen.go`, `*.pb.go`) are excluded
from formatting.
The linters that run are declared in `.golangci.yml`. Read them there rather
than looking for a list here — a copied list goes stale the first time the
configuration changes. Generated files (`*.gen.go`, `*.pb.go`) are excluded from
formatting.

### Documentation

Markdown files are formatted with [mdformat] through `uvx`. This style is
enforced by CI.

```bash
just md-fmt-check # Check formatting
just md-fmt # Auto-fix formatting
```

## Code standards

### Function signatures

Functions with parameters use multi-line format, one parameter per line:
Functions with parameters use multi-line format — one parameter per line, with
the closing parenthesis and the return types on a line of their own:

```go
func FunctionName(
Expand All @@ -89,24 +97,38 @@ func FunctionName(
}
```

Zero-parameter functions stay on one line.
Functions taking no parameters stay on one line:

### Go patterns
```go
func Name() string {
}
```

- Error wrapping: `fmt.Errorf("context: %w", err)`
- Early returns over nested if-else
- Unused parameters: rename to `_`
- Import order: stdlib, third-party, local (blank-line separated)
Adding a parameter then shows as one added line rather than a rewritten
signature.

### Documentation
### File naming

Markdown files are formatted with [mdformat] through `uvx`. This style is
enforced by CI.
Name a file for what it holds. Avoid `helpers.go`, `utils.go`, and names of that
kind: they describe where code was put rather than what it is, and they
accumulate whatever has no other home.

```bash
just md-fmt-check # Check formatting
just md-fmt # Auto-fix formatting
```
`types.go` holds only type declarations — structs, interfaces, constants, and
aliases. A function belongs in a file named for what it does.

A test file is named for the production file it tests. Where tests grow too
large to read, split the production file first so each test file keeps a
counterpart, rather than splitting tests away from the file they cover.

### Go patterns

- Error wrapping: `fmt.Errorf("context: %w", err)`, so the chain names each
layer it passed through and stays inspectable with `errors.Is` and
`errors.As`.
- Early returns rather than nesting the successful path inside conditionals.
- Unused parameters: rename to `_`.
- Import order: standard library, third party, then local, separated by blank
lines.

## Testing

Expand All @@ -129,17 +151,24 @@ module — change both together.

### Test file conventions

- Public tests: `*_public_test.go` in `package server_test`, exercising the
exported surface. This is the default.
- Internal tests: `*_test.go` in `package server`, for what the exported surface
- Public tests: `*_public_test.go` in the package's `_test` package, exercising
the exported surface. This is the default.
- Internal tests: `*_test.go` in the same package, for what the exported surface
cannot reach.
- Suite naming: `*_public_test.go` → `{Name}PublicTestSuite`, `*_test.go` →
`{Name}TestSuite`.
- `testify/suite` with table-driven cases and `validateFunc` callbacks.
- One suite method per function under test — all scenarios for a function
(success, error codes, transport failures, nil responses) are rows in one
table, never separate `TestFoo` / `TestFooError` methods.
- Mocks are generated with `go.uber.org/mock` and committed; never hand-written.
- `testify/suite` with table-driven cases.
- One suite method per function under test — success, errors, and edge cases are
rows in one table, not separate methods.
- `export_test.go` exposes unexported symbols to external tests, by alias or by
setter. Do not use an alias to re-cover behavior the caller's own test already
reaches; a helper with its own contract is what the pattern is for.
- Mocks are generated with `go.uber.org/mock` and committed, never hand-written.
A double that carries a real implementation — signing with a real key, serving
real HTTP — is not a mock and does not need generating.

External tests in this repository live in `package server_test`, and tables
carry `validateFunc` callbacks.

## Before committing

Expand Down
5 changes: 0 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ require (
github.com/antithesishq/antithesis-sdk-go v0.7.2-default-no-op // indirect
github.com/ashanbrown/forbidigo/v2 v2.3.1 // indirect
github.com/ashanbrown/makezero/v2 v2.2.1 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bkielbasa/cyclop v1.2.3 // indirect
github.com/blizzy78/varnamelen v0.8.0 // indirect
Expand All @@ -59,10 +58,8 @@ require (
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/charithe/durationcheck v0.0.11 // indirect
github.com/charmbracelet/colorprofile v0.4.3 // indirect
github.com/charmbracelet/lipgloss v1.1.0 // indirect
github.com/charmbracelet/ultraviolet v0.0.0-20251205161215-1948445e3318 // indirect
github.com/charmbracelet/x/ansi v0.11.7 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
github.com/charmbracelet/x/term v0.2.2 // indirect
github.com/charmbracelet/x/termios v0.1.1 // indirect
github.com/charmbracelet/x/windows v0.2.2 // indirect
Expand Down Expand Up @@ -131,7 +128,6 @@ require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jedib0t/go-pretty/v6 v6.6.5 // indirect
github.com/jgautheron/goconst v1.10.0 // indirect
github.com/jingyugao/rowserrcheck v1.1.1 // indirect
github.com/jjti/go-spancheck v0.6.5 // indirect
github.com/julz/importas v0.2.0 // indirect
github.com/karamaru-alpha/copyloopvar v1.2.2 // indirect
Expand Down Expand Up @@ -167,7 +163,6 @@ require (
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moricho/tparallel v0.3.2 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nakabonne/nestif v0.3.1 // indirect
github.com/nats-io/jsm.go v0.1.2 // indirect
Expand Down
Loading
Loading