Go pack

Last updated

Cards specific to go. Use them with the level page that matches your input. Report each check as PASS, FAIL, or N/A with file:line evidence. A FAIL carries the card's severity. Any block FAIL means request changes. A should-fix or discuss FAIL goes in the review but does not block.

Verdict: request changes - change, function - 2 block, 1 should-fix FAIL, 1 PASS, 14 N/A
FAIL [block] chg-expand-contract - src/a.py:42 - renames the column and drops the old one in one migration
FAIL [block] svc-sqli-authn - src/db.py:88 - proven: builds the WHERE clause with an f-string from request.args
FAIL [should-fix] chg-one-concern - src/b.py:7 - a logging rewrite rides along with the bug fix
PASS chg-breaking-change-taxonomy - src/c.py:15 - no signature or return shape changes
N/A: 14 cards, applies_when does not hold
What the badges mean

Draft cards

No stable cards yet.

7 draft cards

A draft card is not vetted. Report a FAIL of one under Draft findings, at should-fix at most, never in the ledger or the verdict. How a draft becomes stable

go-govulncheck-in-ci

should-fixdraftsecurity

Applies when: The repository is a Go module with a CI pipeline.

Rule. Run govulncheck ./... in CI and fail the build on a reported vulnerability that your code calls.

Why. Dependencies pick up known vulnerabilities after you pin them. A scan that only runs on a laptop finds them when someone remembers. govulncheck reports only vulnerabilities in functions your code reaches, so a failing run is worth acting on.

FAIL
jobs:
  test:
    steps:
      - uses: actions/checkout@v4
      - run: go test ./...
PASS
jobs:
  test:
    steps:
      - uses: actions/checkout@v4
      - run: go test ./...
      - run: go run golang.org/x/vuln/cmd/govulncheck@latest ./...

Counterpoint. "A new advisory breaks an unrelated pull request." That is the signal; the scan needs a fix or a recorded exception. A scheduled run on the main branch keeps the check alive between pull requests.

Why it is a draft: too few organisations back a clause of the Rule. Only the Go team states it; needs two more organisations that run govulncheck in CI How a draft becomes stable

Tool gate: govulncheck

Cites 1 organisation, backing unjudged:

go-http-client-timeout

should-fixdraftreliabilityperformance

Applies when: A Go function makes an HTTP or other network call.

Rule. Network calls carry a deadline: a context.Context passed to NewRequestWithContext, or an http.Client with Timeout, never http.DefaultClient without either.

Why. A zero timeout means no timeout. One stalled peer hangs the goroutine, and with it the caller, until the process restarts.

FAIL
resp, err := http.Get(url)
PASS
var client = &http.Client{Timeout: 15 * time.Second}

req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
	return err
}
resp, err := client.Do(req)

Counterpoint. "Streaming bodies outlive any fixed timeout." Then set per-phase timeouts on the transport and use the context for the total deadline; keep the deadline.

Why it is a draft: too few organisations back a clause of the Rule. Cloudflare and the Go docs (Google) state it; a third independent organisation is missing How a draft becomes stable

Tool gate: noctx, bodyclose (golangci-lint)

Cites 2 organisations, backing unjudged:

go-mod-readonly-verified

should-fixdraftsecurityreliability

Applies when: The repository is a Go module built in CI.

Rule. Commit go.sum, build in CI with -mod=readonly, and run go mod verify.

Why. go.sum pins the hash of every dependency, so a re-uploaded version fails the build. A build that rewrites go.mod on its own hides drift. go mod verify catches a tampered module cache.

FAIL
# go.sum is in .gitignore
- run: go mod tidy && go build -mod=mod ./...
PASS
- run: go mod verify
- run: go build -mod=readonly ./...
- run: go mod tidy -diff

Counterpoint. "The default is already readonly since Go 1.16." True for go build; the explicit flag and the go.sum check keep a stray GOFLAGS=-mod=mod from changing that.

Why it is a draft: too few organisations back a clause of the Rule. Only the Go team states it; needs two more organisations How a draft becomes stable

Tool gate: go mod verify, go mod tidy -diff

Cites 1 organisation, backing unjudged:

go-no-init-side-effects

discussdraftmaintainabilitycorrectness

Applies when: A Go file defines func init().

Rule. init() does no I/O, reads no environment and does not depend on other init() functions; do that work from main().

Why. init() runs on import, in an order readers do not see, before tests can set anything up. A network call or env read there makes importing the package a side effect.

FAIL
var db *sql.DB

func init() {
	db, _ = sql.Open("postgres", os.Getenv("DATABASE_URL"))
}
PASS
func main() {
	db, err := sql.Open("postgres", os.Getenv("DATABASE_URL"))
	if err != nil {
		log.Fatal(err)
	}
	run(db)
}

Counterpoint. "Registering a driver needs init()." Pure registration into a map is deterministic and has no I/O; that is the allowed case.

Why it is a draft: too few organisations back a clause of the Rule. Only Uber states it; needs two more organisations How a draft becomes stable

Tool gate: gochecknoinits (golangci-lint)

Cites 1 organisation, backing unjudged:

go-test-race-in-ci

should-fixdraftcorrectnessreliabilitytesting

Applies when: A Go module uses goroutines, channels or shared state and has CI.

Rule. CI runs the test suite with go test -race ./....

Why. Data races pass ordinary tests and fail in production, rarely and without a trace. The detector finds them only when the racing code runs under test, so the flag has to be on in the run that every change passes.

FAIL
- run: go test ./...
PASS
- run: go test -race ./...

Counterpoint. "The race detector slows tests and needs cgo." It costs several times the runtime and memory, so run it on every pull request if the suite is fast; otherwise at least on the main branch.

Why it is a draft: too few organisations back a clause of the Rule. Only the Go team states it; needs two more organisations How a draft becomes stable

Tool gate: go test -race

Cites 1 organisation, backing unjudged:

go-vet-lint-gate

should-fixdraftcorrectnessmaintainability

Applies when: The repository is a Go module with a CI pipeline.

Rule. CI runs go vet and a linter set that includes staticcheck and errcheck, and a finding fails the build.

Why. These tools catch unchecked errors, misused Printf verbs and dead code before a reviewer sees them. If review comments repeat, the gate is missing. Lint the same way across the whole codebase; consistency matters more than the exact set.

FAIL
- run: go build ./... && go test ./...
PASS
- run: go vet ./...
- uses: golangci/golangci-lint-action@v6   # .golangci.yml enables errcheck, staticcheck
- run: go test ./...

Counterpoint. "A new linter floods an old codebase." Enable it with new-from-rev so only new code is held to it, then widen.

Why it is a draft: too few organisations back a clause of the Rule. Only Uber states it in a published guide; needs two more organisations How a draft becomes stable

Tool gate: go vet, staticcheck, errcheck via golangci-lint

Cites 1 organisation, backing unjudged:

go-wrap-errors-with-w

should-fixdraftcorrectnessreliability

Applies when: A Go function returns an error it received from a call.

Rule. Check every returned err; when you add context, wrap with fmt.Errorf("...: %w", err) unless the cause must stay hidden.

Why. %v flattens the cause into text, so callers cannot use errors.Is or errors.As. An ignored err turns a failure into silent wrong output.

FAIL
data, _ := os.ReadFile(path)
if err := json.Unmarshal(data, &cfg); err != nil {
	return fmt.Errorf("parse config: %v", err)
}
PASS
data, err := os.ReadFile(path)
if err != nil {
	return fmt.Errorf("read config: %w", err)
}
if err := json.Unmarshal(data, &cfg); err != nil {
	return fmt.Errorf("parse config: %w", err)
}

Counterpoint. "%w makes the wrapped error part of my API." Right: callers may depend on it. Use %v at a package boundary when you do not want that, and say so.

Why it is a draft: too few organisations back a clause of the Rule. Go team and Uber state it; a third independent organisation is missing How a draft becomes stable

Tool gate: errcheck, errorlint

Cites 2 organisations, backing unjudged: