How I Actually Work With AI on a Live Production System
Most AI-coding stories are toy projects built from scratch. Here's what it actually looks like pairing with Claude on a real, years-old system with real clients and real data.
Most AI-Coding Stories Are Toy Examples
Ask an AI to build a todo app or a chatbot from scratch and it’ll look impressive in a screen recording. That’s not the situation most working developers are actually in. I spend most of my time on a case-management platform used by a public-sector client — years of accumulated modules, dashboards, and report exports, other people’s code, real cases with real people’s data behind them, and a project process that expects properly logged, reviewable work rather than “it works on my machine.” Nothing about that project gets to be rebuilt clean. It has to be understood and extended carefully.
This is what that actually looks like with an AI assistant in the loop — not a demo, the day-to-day.
What a Session Actually Looks Like
Work comes in through the team’s issue tracker, not a prompt I make up. Over a few months of working this way, a routine took shape — not Claude’s default behavior, something I had to establish and correct a few times before it stuck:
- Read the request properly before touching anything
- Investigate the existing code and data first — verify assumptions against the actual schema, not a guess at what it probably does
- If something’s ambiguous or the numbers don’t add up, ask before building on top of a guess
- Implement
- Test against real data, not just “it runs without errors”
- Log time, update the ticket, and note anything surprising for next time
The part that actually changes the experience is step 6 landing somewhere persistent. Established patterns, past gotchas, and rules I’ve set stick around between sessions instead of evaporating at the end of a chat window. Picking a task back up doesn’t mean re-explaining the system from zero.
Two Examples of Where the Discipline Paid Off
A silently-wrong report. One export report had been quietly hiding its first two rows of real data — not a crash, just an invisible layout bug in how header rows were being inserted, inconsistent with the pattern every other report in the system used. Nobody had noticed because nothing errored. Walking the export logic side by side and diffing it against a known-working report surfaced the mismatch in minutes rather than a long manual trace.
A leak that had already happened. An office coordinator flagged, almost in passing, that someone outside a case had seen details they shouldn’t have. That’s a very different kind of bug to chase than “the button doesn’t work” — it meant systematically testing every role, every office-assignment state, and every view against real data instead of guessing at what the access rules probably were. That process turned up two separate gaps, not one, including a second one nobody had reported yet. Both got scoped and fixed the same day, with the fix shaped by how the client actually wanted access to work, not just a patch that made the symptom go away.
Neither of those came from “AI writes code fast.” They came from being able to methodically check every case instead of the two or three a rushed manual review would cover.
The Guardrails That Make Speed Safe
None of this works without rules that don’t bend:
- Confirm what’s about to be committed, every time, before committing it
- Never push to a remote without an explicit go-ahead — that decision stays mine
- Stay inside the ticket’s actual scope; don’t quietly fix unrelated things a teammate didn’t ask for and might not want touched
- Check that test runs are hitting an isolated database, not development data, before anything destructive runs
- Don’t mark a task done until I’ve actually confirmed the fix works — not before
These aren’t limitations I’m working around. I set every one of them, deliberately, and they’re the reason I trust moving fast in the first place.
Where the Real Advantage Shows Up
The moment this pays off isn’t a single fast fix — it’s a single client meeting generating a dozen follow-up requests in one sitting, some trivial, some needing real design decisions, most touching modules I hadn’t looked at in weeks. Working through that used to mean a slow ramp-up per item: re-reading old code, remembering why a field was named the odd thing it’s named, re-establishing the rules for that corner of the system.
With context and established patterns carried between sessions, that ramp-up mostly disappears. Triage happens fast — which items are well-defined and ready to build versus which ones need a clarifying question first — and the well-defined ones move the same day. The win isn’t code generation speed. It’s that a batch of work which used to force a slowdown from sheer complexity increasingly doesn’t.
What Hasn’t Changed
The client conversations, the tradeoff calls, what ships and when — that’s all still mine, and it should stay that way. What’s different is how much of the surrounding work — investigation, exhaustive testing, documentation, triage — I can hand off and actually trust. That’s what lets me take on more at once without the quality dropping, which is the whole point.