Day 14 — Fuzzing, Golden Files & Go 1.26 Quality
Day 14 — Fuzzing, Golden Files & Go 1.26 Quality
Day 34 — Fuzzing
Stage IV · ~3h (theory-heavy)
Goal: Use Go’s built-in fuzzing (go test -fuzz) to stress parsers and codecs, seed corpora wisely, and turn crashers into regression tests.
Fuzzing finds inputs you didn’t think of. It complements—not replaces—table-driven tests (Day 16).
Why this day exists
Table tests encode human imagination. Fuzzers encode mutation search:
- Parsers (
strconv, custom config, URL path rules)
- Decoders (JSON subsets, binary headers)
- Any function
[]byte → Torstring → Twith invariants
If you ship parsers without fuzzing, production becomes your fuzzer—with worse UX.
Theory 1 — Fuzz targets in Go
func FuzzParse(f *testing.F) {
// seed corpus
f.Add("42")
f.Add("-1")
f.Add("")
f.Fuzz(func(t *testing.T, in string) {
_, _ = Parse(in) // must not panic
})
}| Piece | Role |
|---|---|
FuzzXxx |
Entry; name starts with Fuzz |
f.Add(...) |
Seed interesting inputs |
f.Fuzz(fn) |
fn args after *testing.T are fuzzed params |
| Supported param types | e.g. []byte, string, integrals, bool, … |
Run:
go test -fuzz=FuzzParse -fuzztime=30sWithout -fuzz, Fuzz tests still execute seed corpus like tests (good for CI smoke).
Theory 2 — What to assert
Fuzz targets should check invariants, not full equality to an oracle you don’t have.
Good invariants:
- No panic
Parseerror ⇒ do not use value
Parsesuccess ⇒Format(Parse(x))round-trip (when defined)
- Output length bounds
errors.Iscategories
f.Fuzz(func(t *testing.T, in string) {
v, err := Parse(in)
if err != nil {
return
}
out, err := Format(v)
if err != nil {
t.Fatalf("format: %v", err)
}
v2, err := Parse(out)
if err != nil || v2 != v {
t.Fatalf("roundtrip %q → %q → %v", in, out, v2)
}
})Theory 3 — Corpus management
On failure, Go writes a file under:
testdata/fuzz/FuzzParse/<hash>
Commit these files—they become regression seeds.
go test # runs seeds
go test -fuzz=FuzzParse -fuzztime=1x # limitedNever discard crashers without minimizing understanding.
Theory 4 — Structured fuzzing with multiple args
f.Add(uint8(1), []byte("ab"))
f.Fuzz(func(t *testing.T, n uint8, b []byte) {
// ...
})Engine mutates each argument. Keep domains meaningful—or clamp inside:
if len(b) > 1<<20 {
t.Skip()
}Prefer fixing parser bounds over huge skips.
Theory 5 — Fuzzing vs race detector
You can combine:
go test -race -fuzz=FuzzFoo -fuzztime=20sSlower but valuable for concurrent parsers. Day 36 deepens concurrent testing; today keep most fuzz targets single-goroutine pure functions.
Theory 6 — When fuzzing won’t help
- UI layout
- Business workflows needing external systems
- Functions without interesting input domains
- Non-deterministic time/random without injection
Fuzz pure, deterministic cores first.
Worked example — integer list parser
package parse
import (
"fmt"
"strconv"
"strings"
)
// ParseList parses "1,2,3" into ints; empty string → empty slice.
func ParseList(s string) ([]int, error) {
if s == "" {
return nil, nil
}
parts := strings.Split(s, ",")
out := make([]int, 0, len(parts))
for _, p := range parts {
p = strings.TrimSpace(p)
if p == "" {
return nil, fmt.Errorf("empty element")
}
n, err := strconv.Atoi(p)
if err != nil {
return nil, err
}
out = append(out, n)
}
return out, nil
}
func FormatList(xs []int) string {
parts := make([]string, len(xs))
for i, n := range xs {
parts[i] = strconv.Itoa(n)
}
return strings.Join(parts, ",")
}Fuzz:
func FuzzParseList(f *testing.F) {
f.Add("")
f.Add("1")
f.Add("1,2,3")
f.Add(" 1 , 2 ")
f.Fuzz(func(t *testing.T, s string) {
xs, err := ParseList(s)
if err != nil {
return
}
s2 := FormatList(xs)
ys, err := ParseList(s2)
if err != nil {
t.Fatal(err)
}
if len(xs) != len(ys) {
t.Fatalf("%v vs %v", xs, ys)
}
for i := range xs {
if xs[i] != ys[i] {
t.Fatalf("%v vs %v", xs, ys)
}
}
})
}Note: round-trip may fail if you allow spaces in input but format without spaces—that’s a spec decision. Fix Format/Parse or loosen invariant.
Lab 1 — Seed and run
Workspace: ~/lab/90daysofx/01-go/day34
mkdir -p ~/lab/90daysofx/01-go/day34 && cd ~/lab/90daysofx/01-go/day34
go mod init example.com/day34Implement a small parser (CSV ints, key=value config, or subset JSON). Add Fuzz with ≥4 seeds.
go test
go test -fuzz=FuzzParse -fuzztime=30sLab 2 — Plant a bug, catch it
Introduce a panic on a rare input (e.g. single "%" character). Run fuzz until it fails. Commit the crasher corpus file. Fix the bug; ensure seeds still pass.
Lab 3 — Invariant design
Write three invariants for your parser. Rank them by strength. Implement the strongest that is true.
Lab 4 — CI-friendly fuzz smoke
Document in README:
# CI
go test ./...
# Nightly / local deep
go test -fuzz=FuzzParse -fuzztime=5mOptional: -fuzzminimizetime awareness when minimizing crashers.
Common gotchas
| Gotcha | Fix |
|---|---|
| Non-deterministic fuzz body | Inject clocks/rng |
| Huge allocations hanging host | Bound input size |
| Asserting exact error strings | Assert classes/invariants |
| Not committing testdata/fuzz | Commit crashers |
| Fuzzing through network | Fuzz pure decode only |
Checkpoint
- Wrote a
Fuzztarget with seeds
- Ran
-fuzzfor ≥30s
- Fixed at least one issue (planted or real)
- Corpus under
testdata/fuzzunderstood
- Round-trip or no-panic invariant justified
go testpasses seeds in non-fuzz mode
Commit
git add .
git commit -m "day34: fuzzing — parser Fuzz target + corpus"Write three personal gotchas before continuing.
Tomorrow
Day 35 — Golden files & t.Cleanup: snapshot testing for encodings and deterministic artifacts with safe cleanup.
Day 35 — Golden files & Cleanup
Stage IV · ~3h (theory-heavy)
Goal: Use golden-file (snapshot) tests for encodings and rendered output; manage temp files with t.TempDir and t.Cleanup; know when goldens help vs harm.
Golden tests freeze observable output. They excel at codecs, CLIs, and formatters—and rot when output is noisy or non-deterministic.
Why this day exists
Some correctness is “matches the fixture”:
- Pretty-printed JSON
- Serialized config
- CLI help/output slices
- Canonical error messages for stable APIs
Hand-asserting large strings is painful. Goldens store expected bytes on disk.
Theory 1 — Golden file pattern
testdata/
foo.input.json
foo.golden.json
func TestEncode(t *testing.T) {
in, err := os.ReadFile("testdata/foo.input.json")
if err != nil {
t.Fatal(err)
}
var v Value
if err := json.Unmarshal(in, &v); err != nil {
t.Fatal(err)
}
got, err := EncodePretty(v)
if err != nil {
t.Fatal(err)
}
wantPath := "testdata/foo.golden.json"
if *update {
if err := os.WriteFile(wantPath, got, 0o644); err != nil {
t.Fatal(err)
}
}
want, err := os.ReadFile(wantPath)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(got, want) {
t.Fatalf("mismatch\n got: %s\nwant: %s", got, want)
}
}Update flag
var update = flag.Bool("update", false, "update golden files")go test -update ./...
# note: flag must be defined in TestMain or via flag.Parse in init carefullyCommon pattern:
func TestMain(m *testing.M) {
flag.Parse()
os.Exit(m.Run())
}Or use environment variable UPDATE_GOLDEN=1 to avoid flag plumbing conflicts.
Theory 2 — t.TempDir and t.Cleanup
dir := t.TempDir() // auto-removed after test
path := filepath.Join(dir, "out.txt")f, err := os.CreateTemp("", "day35-*")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { os.Remove(f.Name()) })Rules:
- Prefer
t.TempDirover manual deletes
t.Cleanupruns even on failure (LIFO)
- Don’t rely on process exit for temp hygiene in long test binaries
Go 1.26 also adds t.ArtifactDir() for intentional retained outputs when -artifacts is set—use for CI-collected blobs; goldens stay in testdata/.
Theory 3 — Determinism requirements
Goldens fail if output includes:
- Timestamps
- Map iteration order (for text that ranges maps)
- Random IDs
- Absolute paths
Fixes:
- Inject clock
- Sort keys (
json.Encoder.SetIndent+ structured maps with sorted encode)
- Normalize paths in comparison
- Compare AST/semantic equality instead of bytes when needed
Theory 4 — Diff UX
On mismatch, print a readable diff:
import "github.com/google/go-cmp/cmp" // optional dep
// or simple line-oriented diff helperFor labs, a short helper is enough:
func diff(a, b string) string {
// naive: show first mismatch index
}Never dump megabytes without truncation in t.Fatalf.
Theory 5 — When not to golden
| Prefer unit asserts | Prefer golden |
|---|---|
| Numeric results | Large structured text |
| Error sentinel types | Exact formatted documents |
| Branch logic | Regression on pretty printers |
Goldens that update weekly become ignored noise—treat updates as reviews.
Theory 6 — Encoding lab target
Pick one:
- Canonical JSON for a config struct
- Custom line protocol encode/decode
- Markdown table renderer
Round-trip tests + golden for encode path.
Worked example — stable JSON golden
package conf
import (
"bytes"
"encoding/json"
)
type Config struct {
Name string `json:"name"`
Ports []int `json:"ports"`
Tags []string `json:"tags"`
}
func Encode(c Config) ([]byte, error) {
var buf bytes.Buffer
enc := json.NewEncoder(&buf)
enc.SetIndent("", " ")
if err := enc.Encode(c); err != nil {
return nil, err
}
return buf.Bytes(), nil
}Test uses testdata/config.golden.json. Note Encode adds trailing newline—golden must match.
Worked example — Cleanup with servers
func TestServer(t *testing.T) {
// if using httptest:
// srv := httptest.NewServer(...)
// t.Cleanup(srv.Close)
}Always cleanup listeners, temp files, and chdir restores:
wd, _ := os.Getwd()
t.Cleanup(func() { _ = os.Chdir(wd) })Lab 1 — Golden encode
Workspace: ~/lab/90daysofx/01-go/day35
mkdir -p ~/lab/90daysofx/01-go/day35/testdata
cd ~/lab/90daysofx/01-go/day35
go mod init example.com/day35- Define a struct + pretty encoder.
- Write golden file.
- Test equality.
- Change encoder; see failure; update golden deliberately; review diff.
go test ./...Lab 2 — Update workflow
Implement -update flag or UPDATE_GOLDEN env. Document:
UPDATE_GOLDEN=1 go test ./...
git diff testdata/Treat the diff as a PR review surface.
Lab 3 — TempDir pipeline
Write a test that:
- Creates input file in
t.TempDir
- Runs a function writing output beside it
- Compares to golden content (not path)
- Leaves no residue outside temp
go test -race ./...Lab 4 — Non-determinism drill
Intentionally include time.Now() in output; watch golden flake. Fix by injecting a func() time.Time clock. Re-stabilize.
Common gotchas
| Gotcha | Fix |
|---|---|
| CRLF vs LF | Normalize or run on consistent OS; prefer \n |
| Map range order in custom encoders | Sort keys |
| Updating goldens blindly | Review every byte change |
| Storing secrets in testdata | Never |
| Huge binary goldens | Prefer text or semantic compares |
Checkpoint
- Golden test for an encoder
- Update workflow documented
- Uses
t.TempDir/t.Cleanupcorrectly
- Fixed a non-determinism issue
- Understands 1.26
ArtifactDirvs goldens
go testgreen
Commit
git add .
git commit -m "day35: golden files + TempDir/Cleanup labs"Write three personal gotchas before continuing.
Tomorrow
Day 36 — Concurrent tests / synctest: testing concurrent code without flakes—race detector, parallelism, and modern sync-test awareness.
Day 36 — Concurrent tests / synctest
Stage IV · ~3h (theory-heavy)
Goal: Test concurrent code without flakes: t.Parallel discipline, race detector in CI shape, timeout assertions, and synctest awareness for deterministic concurrency testing where available.
Flaky concurrent tests train teams to ignore red CI. Your job is deterministic failure when code is wrong—and green when code is right.
Why this day exists
Concurrent tests go wrong by:
- Sleeping “enough”
- Depending on scheduler luck
- Sharing global state across tests
- Forgetting
-race
- Deadlocking the test binary
You already have primitives (Days 19–28). Today is the test harness craft.
Theory 1 — What good concurrent tests prove
| Property | How to test |
|---|---|
| Race-freedom | go test -race |
| Cancel exits | assert return before deadline |
| Concurrency bound | atomic in-flight max |
| Correctness under load | many goroutines + final assert |
| No leak (coarse) | NumGoroutine settle / join API |
Prefer observable postconditions over “didn’t crash once.”
Theory 2 — t.Parallel rules
func TestA(t *testing.T) {
t.Parallel()
// ...
}Rules:
- No shared mutable globals without sync
- Package
TestMain/ suite setup must be parallel-safe
- Use subtests carefully—
t.Parallelin subtests aftert.Runscaffolding
- Parallel + time-sensitive sleeps ⇒ flakes
Pattern for subtests:
func TestAll(t *testing.T) {
t.Parallel()
cases := []string{"a", "b"}
for _, tc := range cases {
tc := tc
t.Run(tc, func(t *testing.T) {
t.Parallel()
// use tc
})
}
}Theory 3 — Never sleep for sync (almost)
Bad:
go doWork()
time.Sleep(100 * time.Millisecond)
assertDone()Better:
done := make(chan struct{})
go func() {
defer close(done)
doWork()
}()
select {
case <-done:
case <-time.After(2 * time.Second):
t.Fatal("timeout")
}Or block on errgroup.Wait / API that joins.
Sleep is OK only as a scheduler settle upper bound after proper joins—not as the join itself.
Theory 4 — Testing cancel
func TestCancel(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
started := make(chan struct{})
errCh := make(chan error, 1)
go func() {
errCh <- engine.Run(ctx, slowItems(), func(ctx context.Context, it Item) error {
close(started)
<-ctx.Done()
return ctx.Err()
})
}()
<-started
cancel()
select {
case err := <-errCh:
if !errors.Is(err, context.Canceled) {
t.Fatalf("got %v", err)
}
case <-time.After(2 * time.Second):
t.Fatal("Run did not return")
}
}Theory 5 — Synctest awareness
Go’s testing ecosystem has been moving toward better deterministic concurrency testing (often discussed as synctest / bubble-style testing: control when goroutines advance, avoid real wall-clock flakiness).
What you should know on Go 1.26.x path
- Classical tools remain foundational:
-race, channels, ctx, errgroup,testingtimeouts (go test -timeout).
- Experimental / evolving helpers may allow running concurrent logic in a controlled scheduler bubble so tests don’t depend on real
time.Sleepdurations.
- For this volume: do not depend on unstable APIs as your only skill—learn the portable patterns above first.
- When your toolchain includes
testing/synctest(or successor docs), practice one example from official docs and note version requirements in your journal.
// Pseudocode / version-gated sketch — verify against your Go 1.26.x docs:
//
// synctest.Run(func() {
// // goroutines + fake time advancement
// })If the package is not available in your exact patch level, complete labs with classical techniques and write a short note: “synctest: checked docs on <date>.”
Theory 6 — go test flags for concurrency work
go test -race -count=20 ./...
go test -timeout 30s ./...
go test -parallel 4 ./...
go test -run TestCancel -vCI snippet:
go test -race -count=1 -timeout 5m ./...-count=1 disables cache; critical for flake hunting.
Theory 7 — Deadlocking tests
If a test hangs:
go test -timeout 5sto fail fast
- Check missing
Done/ channel receive
- Avoid waiting on locks held by the test goroutine
For interactive debug, SIGQUIT (where supported) dumps stacks.
Worked example — bound test
func TestWorkerLimit(t *testing.T) {
var (
current atomic.Int64
max atomic.Int64
)
items := make([]int, 50)
_, err := MapLimit(context.Background(), items, 5, func(ctx context.Context, v int) (int, error) {
c := current.Add(1)
for {
m := max.Load()
if c <= m || max.CompareAndSwap(m, c) {
break
}
}
time.Sleep(5 * time.Millisecond)
current.Add(-1)
return v, nil
})
if err != nil {
t.Fatal(err)
}
if max.Load() > 5 {
t.Fatalf("max in-flight %d", max.Load())
}
}Lab 1 — Flake then fix
Workspace: ~/lab/90daysofx/01-go/day36
- Write a deliberately flaky test using
Sleep.
- Run
-count=50to see intermittent failures (or force failure).
- Rewrite with channel/join.
- Prove green at
-count=50.
go test -race -count=50 ./...Lab 2 — Parallel suite hygiene
Create two tests that would race on a package-level map if both parallel. Show failure with -race, then isolate state (or protect) so t.Parallel is safe.
Lab 3 — Cancel + timeout tests
Port Day 29/30 engine tests: cancel and deadline paths with 2s upper bound. No sleeps longer than needed inside assertions.
Lab 4 — Synctest spike
- Run
go doc testing/ search release notes for synctest on your 1.26.x.
- If present, implement one official-style example.
- If absent, document classical substitutes and keep labs green.
Common gotchas
| Gotcha | Fix |
|---|---|
| Global test counters | Per-test state |
t.Parallel + shared files |
TempDir per test |
| Infinite Wait on bug | -timeout |
| Ignoring race in parallel tests | Always -race for concurrent packages |
| Over-synchronizing production code for tests | Prefer injectable seams |
Checkpoint
- Eliminated a sleep-based flake
- Safe
t.Parallelusage demonstrated
- Cancel test fails if cancel ignored
-race -count=20+clean
- Synctest researched for 1.26.x
- Knows
-timeoutand-countroles
Commit
git add .
git commit -m "day36: concurrent tests — de-flake, parallel, cancel asserts"Write three personal gotchas before continuing.
Tomorrow
Day 37 — Go 1.26 quality pass: Green Tea GC default, new(expr), rewritten go fix, and a deliberate modernization sweep.
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.