I recently ran into one of those AI-assisted development failures that was frustrating while it was happening and useful once I had a little distance from it.
That’s usually how these lessons show up.
They don’t arrive as clean little principles.
They arrive after you’ve spent too much time trying to figure out why something that should be straightforward keeps coming back wrong.
In this case, we were working on PageSnip.
PageSnip isn’t a tiny demo app. It sits on top of a shared application framework we’ve been building for a while. That framework already defines the main shell, the left and right rails, the surface layout, the workspace panels, the theme rules, and the general flow of the application.
The idea is simple enough.
The shared shell provides the application surface.
The product builds on top of that surface.
The product doesn’t get to wander back down into the shell and corrupt it.
That boundary matters.
PageSnip is the first real product we’ve built seriously on top of that shared framework, and for the most part the approach has worked well. When we find something useful that belongs in the framework, we mark it for backport. When something belongs only to PageSnip, it stays in PageSnip.
That discipline is part of what keeps the project from turning into a pile of one-off fixes.
The feature sounded simple enough
One of the newer things we’ve been building in PageSnip is an AI Helper system.
Part of that work involved creating a lab inside the program where I could build and test helpers more conveniently during development.
I wanted the lab to show a live preview.
On the left side, I could edit the helper content.
On the right side, I could see what the helper would look like when it ran.
That wasn’t a wild idea.
We’d already proven the general concept elsewhere. PageSnip already had the shell. It already had the surfaces. It already had the theme system. It already had a way the application was supposed to be put together.
So this wasn’t supposed to be a blank-canvas redesign.
It was supposed to fit into the existing application architecture.
That was the important part.
The AI kept missing the point
We started working through the feature with AI assistance.
And the AI kept getting it wrong.
Not because the idea was impossible.
Not because React couldn’t do it.
Not because the preview couldn’t be rendered.
The problem was that the AI kept acting as if it understood the codebase without really grounding itself in the codebase.
It would summarize.
It would infer.
It would describe what was probably happening.
It would make a plausible patch.
But it wasn’t really following the existing PageSnip structure.
That’s a hard thing to catch if you’re not watching closely.
To a casual user, it would just look like the AI had suddenly gotten stupid.
But that wasn’t really what was happening.
The AI wasn’t incapable.
It was guessing.
Why I caught it
I use session-based coding, and one of the advantages of that workflow is that I can watch the work unfold.
I can see when the AI is inspecting files.
I can see when it’s reasoning from actual code.
I can also see when it’s skating across the top of the project and pretending that a few summaries are enough.
That’s what was happening here.
We’d given it the usual rules.
Read the docs.
Look at the repo.
Respect the shared shell.
Follow the existing surfaces.
Don’t make broad rewrites.
Make small surgical changes.
Those instructions weren’t decorative. They were the difference between a clean implementation and another round of architectural drift.
But the AI wasn’t really obeying them.
It was taking the shape of the instruction and skipping the work underneath it.
The moment I stopped it
Eventually I had to stop the session.
Not gently.
I had to tell it, in effect:
Don’t skim.
Don’t summarize.
Don’t assume.
Open the actual files.
Read how the shell is constructed.
Read how the surface is plugged in.
Read how the helper preview is supposed to render.
Show me the actual pattern before you change anything else.
That changed the session.
Once the AI finally inspected the real code instead of guessing from the conversation history, the next implementation worked much more like it should have worked in the first place.
That was the revealing part.
The problem hadn’t been that the task was beyond the AI.
The problem was that the AI had been allowed to proceed without evidence.
The lesson wasn’t just about one feature
That’s the part I think matters beyond this one PageSnip feature.
When AI works on a real codebase, it has to be forced back into the code.
Otherwise it may start treating the project like a prompt instead of a system.
A prompt can be improvised around.
A system has rules.
In PageSnip, those rules matter. The rails aren’t just visual decoration. The surfaces aren’t just random panels. The shared framework isn’t just a folder full of reusable pieces that can be casually edited to make today’s bug go away.
The architecture is part of the product.
If the AI doesn’t respect that, it can produce code that is locally clever and globally wrong.
That’s one of the real dangers of AI-assisted development.
The AI can move fast enough that it creates damage before you realize it has stopped following the map.
The human still has to manage the work
This is why I don’t think of AI coding as “push a button and get software.”
That’s not the job.
The job is closer to managing a very fast assistant.
Sometimes that assistant is excellent.
Sometimes it’s lazy.
Sometimes it’s overconfident.
Sometimes it does exactly what a junior developer might do under pressure: it guesses at the pattern instead of opening the file.
That’s not a reason to throw AI away.
It’s a reason to manage it better.
Ask for receipts.
Make it name the files.
Make it describe the existing pattern.
Make it state what it’s not allowed to change.
And when it starts looping, don’t ask for another patch.
Stop it.
Send it back to the code.
What I changed after that
The practical change is simple.
For small work, I may still let the AI move quickly.
But for anything that touches architecture, shared surfaces, storage, rendering, product boundaries, editor behavior, or framework behavior, the AI doesn’t get to code first.
It has to inspect first.
It has to show me what it looked at.
It has to explain the existing pattern.
Then it can make the smallest safe change.
That sounds like a slowdown.
It isn’t.
The slowdown was letting it guess.
The speed came back when it finally read the code.
-- Charles
Related guide
For the broader workflow lesson, see Make the AI Show Its Receipts.