Skip to content

Articles

AI Code Review Has a Correctness Problem

Over the past two years, AI code review has gone from novelty to something many developers use every day, and the feedback can be genuinely impressive. Ask a modern model to review a pull request and you'll often get thoughtful comments about readability, naming, architecture, test coverage — the kind of observations that would have taken a human reviewer considerably longer to make.

I've spent the past while benchmarking several leading models against real pull requests, and the longer I looked at the results, the more one observation kept surfacing.

The reviews were often excellent.

The software wasn't.

When "Duplicate Users" Stopped Being a Problem

There was a period in my career when seeing the same business concept appear in multiple systems made me deeply uncomfortable. If I found a User table in one system and another User table somewhere else, my instinct was to assume that something had gone wrong. The same was true for Accounts, Employees, Customers, Sites, and Organizations. Surely there should be a single authoritative service for each of these concepts. If there wasn't, hadn't we duplicated the data? Weren't we creating conflicting sources of truth?

That intuition drove many of my architectural decisions. Whenever a new system was being designed, one of the first questions I would ask was: who owns Users? It felt like the right question, and for a long time I believed it was.

Distributed Systems Are an Ownership Problem

I've been trying to understand why distributed systems felt so much harder than monoliths when I first started building them.

Not technically. I expected them to be technically harder. What surprised me was how much harder they were to reason about. I'd read about eventual consistency, retries, idempotency, message ordering, and distributed transactions, and none of those ideas were beyond me individually — if someone explained a pattern or a technology, I could usually follow along without much trouble. But when I sat down to design a distributed system myself, something felt different. Two experienced developers could look at exactly the same problem and arrive at completely different architectures. Both could sound reasonable. Both could work. Yet somehow one solution would almost always prove easier to evolve a year later, and for a long time I couldn't explain why.

I thought I just needed to learn more technology. Now I think I was trying to solve the wrong problem.

The Day It Felt Like AI Had a Conscience

I've been thinking about a conversation I had with an AI recently. Not because it generated brilliant code. Not because it hallucinated. And not even because it refused one of my requests. I already knew AI systems could refuse things.

What surprised me was something else. For the first time, I found myself arguing with it. Not about code. Not about facts. About right and wrong. Somewhere in the middle of that conversation, I stopped feeling like I was talking to a tool. It felt like I was talking to something that believed it had a moral obligation. That realization has stayed with me far longer than the request that started it.

The Work Happens Before the Prompt

Every week I see another discussion about becoming better at AI.

People compare models. They trade prompt libraries. They debate whether Claude writes cleaner code than GPT, or whether a particular workflow saves a few more minutes.

Those conversations are useful, but after spending the past year working with AI on real software projects, I keep coming back to the same conclusion:

I don't think the biggest productivity gains come from prompting.

I think they happen long before the first prompt is ever written.

Why I'm Finally Starting This Blog

I've wanted to write for a long time. Not because I wanted to become a blogger, build an audience, or establish myself as a thought leader — quite the opposite, actually.

Over the past twenty-plus years, I've had the privilege of working on some fascinating engineering problems. I've learned from incredible mentors, worked alongside talented developers, made more mistakes than I can count, and slowly developed opinions about software architecture, engineering culture, leadership, DevOps, AI, and what it means to build systems that last.

I've always wanted to share those experiences.

Semantic Versioning for APIs and Libraries: Why CI/CD Should Produce Versioned Releases

Ask most teams what their CI/CD pipeline does and you will hear some variation of "it runs the tests and deploys to production." That answer describes deployment automation, and deployment automation is genuinely valuable — but it is only half of what a pipeline should do. The other half is producing releases: repeatable, traceable, versioned units of software that you can point to, reason about, promote, and roll back. For APIs, libraries, and anything else with downstream consumers, the difference between "we deploy" and "we release" is the difference between a pipeline that moves code and a pipeline that communicates intent.