Where AI Coding Assistants Fall Short (And Why That's OK)
My last post was about how much AI assistants have changed my daily work for the better. This one is the other half of that honesty: where they consistently fail, and why I don't think that failure is a problem to be solved so much as a boundary to be understood. After two decades of code review — first as a developer, then as a lead, now reviewing work from a team of engineers I'm accountable for — I've started to recognize AI's failure modes almost as reliably as I recognize a junior developer's.
Confident, Plausible, Wrong
The single most dangerous pattern is code that looks correct, compiles cleanly, and passes a superficial read, but is wrong in a way that only shows up under a specific edge case. I've seen assistants generate carrier-integration logic that handles the happy path perfectly and silently mishandles a partial shipment, or produce a currency calculation that's correct until you hit a locale where the decimal separator isn't a period. A human developer who doesn't know the answer usually signals uncertainty. An assistant, more often than not, doesn't — it produces the same confident tone whether it's right or fabricating an API method that doesn't exist.
The Architectural Blind Spots
This is where my background as a Solution Architect matters most in review. AI-generated code tends toward anemic domain models — getters and setters wrapped around what should be rich behavior — because that pattern is overrepresented in the training data relative to how often it's actually the right choice. It tends to reach for a new service or a new table before checking whether the concern belongs inside an existing bounded context. It rarely questions whether a synchronous call across a service boundary is the right choice versus an event, because it doesn't have visibility into your system's actual failure and scaling characteristics — it's pattern-matching against generic examples, not reasoning about your specific constraints.
The same applies to two things I flag by habit in every review, AI-assisted or not: N+1 query risk and missing transaction boundaries. AI-generated data access code frequently loads a collection and then lazily triggers a query per item without realizing it, because the code is syntactically correct and the performance problem only shows up at scale, which the assistant has no way to observe. Transaction boundaries are worse — getting them wrong doesn't throw an error, it just quietly corrupts data under concurrent load, which is exactly the kind of failure that's expensive to trace back to its origin months later.
An assistant can write code that passes every test you thought to write. It can't yet tell you which tests you forgot to write.
No Skin in the Game
The deeper limitation isn't technical, it's organizational. When I make an architectural call, I'm accountable for it — to my team, to stakeholders, to whoever inherits the system in three years. That accountability shapes judgment in ways that are hard to articulate but easy to feel: I ask "what happens when this fails at 2 a.m. during peak warehouse throughput" because I've been the person woken up by that failure. An assistant optimizing for "plausible answer to the prompt" has no equivalent pressure. It can't negotiate a trade-off with a product owner, can't read the political reality that a "quick fix" now will become permanent, and can't weigh a decision against the specific scar tissue a team carries from its last outage.
The Guardrails That Actually Work
None of this means avoiding the tools — it means not changing your review discipline because of them. On my teams, AI-authored code goes through the same gates as anyone else's: code review by a human who understands the domain, automated tests that assert behavior rather than just checking for absence of exceptions, and for anything touching architecture, the same scrutiny I'd apply to a design doc. Where I have added something new, it's a habit of explicitly asking "what would this get wrong, and how would we find out" before merging anything AI-assisted that touches money, data integrity, or an external integration — carrier APIs and warehouse hardware interfaces in my world.
Why This Is Fine, Not a Complaint
I don't read any of this as a case against the tools. A junior engineer straight out of college also writes confidently wrong code, also misses transaction boundaries, also reaches for a new service when an existing one would do. The difference is that junior engineers grow into judgment over years of feedback and consequence. AI assistants, as they exist today, don't carry that arc — they're the same tool tomorrow that they were today, which means the judgment has to keep living with the human reviewing the output. That's not a flaw to be patched. It's just where the actual engineering work moved to.
← Back to all posts