lexicon: a shared language for requirements that compiles to Gherkin and Gauge

A previous post argued that Dev, QA, and Product read the same requirement differently not because anyone is careless, but because prose leaves three things implicit — the precondition, the action, and the outcome — and that a structured format forces those three things into the open. It also argued that the specific tool doesn’t matter much: Gherkin’s Given/When/Then is one well-known way to get the structure, not the only one, and a team is free to define its own. ...

July 16, 2026 · 12 min · Omar Crosby

Static analysis for Go: what golangci-lint catches, and why it belongs in CI

Code review catches the bugs a human happens to notice. Static analysis catches the bugs a specific, well-defined check is built to notice — every time, on every line, before anyone opens the diff. Those are different guarantees, and the difference matters most for the bug classes reviewers are worst at: an HTTP response body that never gets closed three call sites deep, an error return silently discarded in a one-line change, a security-sensitive value generated with the wrong random source. None of these look wrong at a glance. All of them are mechanically detectable. ...

July 9, 2026 · 21 min · Omar Crosby

Testing Go with Ginkgo and Gomega without giving up go test

If you’ve never looked at Ginkgo and Gomega, there’s a decent chance you’re picturing something that replaces go test — a separate test runner, a separate CI step, another tool your team has to adopt wholesale before anyone sees a benefit. That picture is wrong, and it’s worth correcting up front, because it’s the single biggest reason Go developers who’d genuinely enjoy Ginkgo never try it. Here’s the fact that should change your mind: a Ginkgo spec is a Go test. It compiles into the same test binary, it runs when you type go test ./..., and your existing CI pipeline doesn’t need to know Ginkgo exists. What Ginkgo adds is a richer vocabulary for organizing specs — nested Describe/Context/It blocks instead of a flat list of Test* functions — plus first-class suite lifecycle (BeforeSuite/AfterSuite), table-driven specs, async timeouts, and labels for slicing a suite into subsets. Gomega adds the assertion language (Expect(x).To(Equal(y))) that makes failures read like a sentence instead of a %v != %v diff. ...

July 9, 2026 · 12 min · Omar Crosby

How writing tests first changes the shape of Go code

Most of the debate around test-first development gets stuck on process — red-green-refactor, coverage percentages, whether it slows you down on day one. That debate misses a simpler, more concrete effect, and you don’t need to know Go, or even know how to code, to follow it: Whichever thing you write second has to bend to fit whatever you wrote first. Write the code first and the test second, and the test has no choice but to accommodate whatever the code already does — however it talks to the database, however it calls the network, however it happens to be built. Write the test first, and it’s the code’s turn to bend: it has to be built in whatever shape lets the test — which doesn’t have a real database or a real network sitting around — actually run. That’s the entire mechanism. Nothing about willpower, experience, or caring more. Just: which one existed first gets to set the terms. ...

July 9, 2026 · 13 min · Omar Crosby

kyber: function-level code quality analysis for Go

Existing Go linters tell you which lines break a rule. kyber tells you which functions carry the most risk, using twelve metrics — McCabe, cognitive, Halstead, maintainability index, and structural signals — in one pipeline.

July 5, 2026 · 8 min · Omar Crosby