There’s a gap between how AI is sold and how it actually works in a real codebase.
The pitch is speed. The reality, for a lot of engineers, is bloated PRs full of defensive code that nobody wants to review. Vague output. Over-generalised abstractions. Code that technically runs but doesn’t feel like yours.
I’ve been there. I’ve watched AI generate hundreds of lines that solved the wrong problem in the right syntax. I’ve seen engineers lose trust. Not because the AI failed, but because they couldn’t tell whether it had succeeded.
Here’s the reframe I keep coming back to:
AI isn’t unreliable. Unconstrained usage is.
The problem isn’t the model. The problem is how we’re using it.
When you give AI a vague prompt (“build this feature” or “refactor this component”) you’re asking it to make decisions it shouldn’t be making. It doesn’t know your domain. It doesn’t know your team’s conventions. It doesn’t know why you’re doing this in the first place.
So it guesses. And it hedges. You get defensive code everywhere because it doesn’t know what’s actually safe. You get abstraction layers you didn’t ask for. You get something that looks like output but doesn’t feel like progress.
The symptoms are predictable:
- Huge PRs that are painful to review
- Overly defensive logic (because the AI doesn’t have types to lean on)
- Code that works but isn’t readable
- Engineers who stop trusting the output
This isn’t an AI problem. It’s an engineering problem.
I learned this properly during a content migration. The kind of work where you’re transforming thousands of pieces of legacy content into a new structure. Systematic, repetitive, but full of edge cases.
Early on, I was giving the AI too much freedom. “Here’s the old format, here’s the new format, write the migration.” The output was technically correct but impossible to verify. I couldn’t tell if it was handling edge cases or ignoring them. I couldn’t review it without reading every line.
Then I changed approach.
Instead of describing the problem loosely, I started specifying exactly what I wanted. This is the input shape. This is the output shape. This is why we’re doing it. This is what success looks like.
The more I constrained the problem, the better the output became.
I started thinking of it as specification-driven development. Not “ask AI to build something” but “define what right looks like, then let AI fill in the systematic parts.”
In practice, this is what works for me:
Typed code. I work in PHP, which is dynamic by default. But I type everything. Types aren’t just for the compiler. They’re constraints that help AI understand what’s expected. Typed inputs, predictable outputs.
Tests that verify behaviour. Not coverage for coverage’s sake. Tests that encode what correct actually means. When the AI writes code, I can run the tests and know whether it worked. I’m not guessing.
Plan before execution. I use plan mode heavily. I describe what I want, why I want it, and what shape the output should take. Then I let the AI propose a plan. I refine that plan until I understand what it’s going to do. Only then do I let it execute.
By the time the agent is writing code, I already know what it’s going to write. That’s the point.
The engineers I see struggling with AI aren’t lazy. They’re following the hype.
They’ve been told to “just ask” and “let AI handle it.” They’re doing what the marketing promised. And then they’re left with output they can’t trust, PRs they can’t review, and a growing sense that maybe AI isn’t for them.
It is for them. But not like this.
AI becomes powerful when you pair it with structure, constraints, and engineering discipline. Not when you let it run wild.
The fix isn’t better prompts. The fix is clearer specifications. Typed inputs. Tests that encode intent. Plans you understand before execution.
Structure reduces ambiguity. And reduced ambiguity is where AI actually shines.
What to change tomorrow
Stop: Asking AI to “build this feature” or “refactor this component” without specifying what success looks like. Stop treating vague output as a model problem. Stop reviewing AI code line by line when you could be reviewing tests.
Start: Defining input and output shapes before execution. Writing plans you’d be comfortable handing to a junior engineer. Using types and tests as constraints, not just safety nets. Treating AI like a teammate who needs clarity, not a tool that needs tricks.
One shift: more structure upfront, less cleanup downstream.
This is what I’ll be writing about: AI-assisted engineering that’s calm, structured, and actually works. Not hype. Not magic. Just disciplined use of a powerful tool.
If that sounds useful, you’re in the right place.
This post introduces the core philosophy behind the Controlled Acceleration Model.