Day 37 — Go 1.26 quality pass
Day 37 — Go 1.26 quality pass
Stage IV · ~3h (theory-heavy + hygiene)
Goal: Apply a deliberate Go 1.26.x quality pass: understand Green Tea GC as default, use new(expr), run the modernized go fix, and leave Stage IV code cleaner and more idiomatic.
This day is about toolchain literacy, not chasing every experimental GOEXPERIMENT. Prefer stable defaults; opt into experiments only with notes.
Why this day exists
Go releases quietly change:
- Runtime performance (GC)
- Language ergonomics (
new)
- Tooling (
go fixmodernizers)
- Testing/runtime diagnostics
If you never re-read release notes, you write 2019-style code on a 2026 compiler. Today you align your labs with 1.26.
Theory 1 — Confirm your toolchain
go version
# expect go1.26.x
go env GOVERSION GOTOOLCHAIN GOROOTModule line:
// go.mod
go 1.26If go mod init on 1.26 created go 1.25.0 (policy: default to N-1), raise deliberately:
go get go@1.26.0
# or edit go.mod go line and go mod tidyKnow the policy: new modules may target the previous minor for broader compatibility—this volume still teaches 1.26 features.
Theory 2 — Green Tea GC is default
From Go 1.26 release notes:
- Green Tea garbage collector (experimental in 1.25) is enabled by default
- Aims for better locality/scalability marking small objects
- Expected ~10–40% reduction in GC overhead in GC-heavy real programs (workload-dependent)
- Additional gains possible on newer amd64 with vectorized scanning
- Opt out (temporary): build with
GOEXPERIMENT=nogreenteagc
- Opt-out expected to go away in a future release (notes mention 1.27 direction)
What you should do as an app developer
- Don’t micro-tune GC before measuring (Stage VII covers pprof).
- Do re-run critical latency benches when upgrading 1.25 → 1.26.
- If you suspect GC issues after upgrade, compare with
GOEXPERIMENT=nogreenteagconly as a diagnostic, then file issues if truly regressing—don’t permanently cargo-cult the opt-out.
# diagnostic A/B (same benchmark)
go test -bench=BenchmarkFoo -benchmem ./...
GOEXPERIMENT=nogreenteagc go test -bench=BenchmarkFoo -benchmem ./...Theory 3 — Language: new(expr)
Historically:
p := new(int)
*p = 42Go 1.26 allows an expression operand:
p := new(42) // *int pointing to 42
q := new(yearsSince(born)) // allocate + initialize from call resultIf expr has type T, new(expr) has type *T and the pointed-to value is initialized to expr.
Why it matters
Optional JSON/protobuf fields often need pointers:
type Person struct {
Name string `json:"name"`
Age *int `json:"age"`
}
age := 40
p := Person{Name: "Ann", Age: &age}
// 1.26:
p = Person{Name: "Ann", Age: new(40)}
// or new(yearsSince(born))Cleaner than temporary variables for one-off pointers.
Not a replacement for everything
- Prefer values when nil-ability isn’t required
- Don’t invent pointer forests for vanity
Theory 4 — Self-referential type parameters (recap)
Also in 1.26 language notes (Day 32):
type Adder[A Adder[A]] interface {
Add(A) A
}Quality pass: if you wrote awkward workarounds, simplify constraints where self-ref is natural.
Theory 5 — Rewritten go fix (modernizers)
In Go 1.26, go fix is revamped:
- Built on the same analysis framework as
go vet
- Home of modernizers—push-button updates toward modern idioms/APIs
- Old historical fixers removed
- Includes suite of language/library modernizations + source-level inlining via
//go:fix inline(advanced)
go fix ./...
go vet ./...
go test -race ./...Discipline
- Clean git status before
go fix
- Run fix
- Review diffs like a PR
- Run tests
- Commit separately:
chore: go fix modernizers
Do not blind-apply across huge monorepos without review.
Theory 6 — Testing quality on 1.26
Awareness items from release notes:
t.ArtifactDir/-artifactsfor retained test outputs
B.Loopinlining improvements for benchmarks
- Prefer modern test helpers you already use (
t.TempDir,t.Cleanup)
Optional experiment: goroutine leak profiles in tests for Stage III engines.
Theory 7 — Quality checklist (Stage IV modules)
| Check | Command / action |
|---|---|
| Version | go version → 1.26.x |
| Modules tidy | go mod tidy |
| Vet | go vet ./... |
| Fix | go fix ./... + review |
| Test race | go test -race ./... |
| Fuzz smoke | go test (seeds) |
| Formatting | gofmt -w . / go fmt ./... |
| Static hints | optional staticcheck (Stage VII deep) |
Worked example — new(expr) in tests
func TestPersonJSON(t *testing.T) {
p := Person{Name: "Ada", Age: new(36)}
b, err := json.Marshal(p)
if err != nil {
t.Fatal(err)
}
if !bytes.Contains(b, []byte(`"age":36`)) {
t.Fatalf("%s", b)
}
}Worked example — GC note in README
## Runtime
Built with Go 1.26+. Green Tea GC is the default; no GODEBUG required.
If diagnosing GC regressions vs 1.25, compare GOEXPERIMENT=nogreenteagc builds.Lab 1 — Inventory & upgrade
Workspace: pick day31–day36 modules or a combined Stage IV folder.
go version
# ensure go.mod go 1.26 (or document N-1 policy choice)
go mod tidyJournal any mismatch between local toolchain and module directive.
Lab 2 — Apply new(expr)
Find ≥2 places using temp vars only to take addresses for optional fields. Rewrite with new(expr) where clearer. Tests remain green.
Lab 3 — go fix pass
git status # clean
go fix ./...
git diff
go test -race ./...Write 5 bullet notes on what changed (or “no changes—already modern”).
Lab 4 — GC / runtime awareness lab
- Write a small allocation-heavy benchmark (e.g. many small struct allocs).
- Run with default GC.
- Run with
GOEXPERIMENT=nogreenteagc.
- Record ns/op and note N=1 machine anecdote ≠ truth—learn the method.
go test -bench=BenchmarkAlloc -benchmem
GOEXPERIMENT=nogreenteagc go test -bench=BenchmarkAlloc -benchmemOptional: enable leak profile experiment on a known-leaky snippet from Day 25.
Common gotchas
| Gotcha | Fix |
|---|---|
Blind go fix on dirty tree |
Commit first |
Pinning nogreenteagc forever |
Diagnostic only |
| Raising go version without CI | Align CI Quarto/book env notes separately; app modules use 1.26 |
Overusing pointers via new |
Values by default |
| Skipping tests after fix | Always re-test |
Checkpoint
- Running Go 1.26.x locally
- Explains Green Tea GC default + opt-out diagnostic
- Used
new(expr)idiomatically
- Ran
go fixand reviewed
go test -racegreen after pass
- Recorded bench A/B method (even if noisy)
Commit
git add .
git commit -m "day37: Go 1.26 quality — new(expr), go fix, GC notes"Write three personal gotchas before continuing.
Tomorrow
Day 38 — Stage IV gate: generic helper + deep tests (fuzz and/or goldens + concurrent tests) on a real package—prove Stage IV complete.