Most of what I've read about AI-assisted development is either uncritically enthusiastic or uncritically skeptical. Neither version matches what the work has actually looked like for me, so what follows is an account of my own use rather than a case for or against the tools.

I've been building with Claude Code as my primary development partner since November 2025, across several projects. The largest was a production CRM and marketing platform for a Tanzania-based safari operator, a ground-up rebuild in TypeScript on Next.js of a static site I had hand-coded the year before. It is live now, running the business day to day, and the biggest thing I have shipped working alone.

The shape of the collaboration

The collaboration is asymmetric. I bring the context: the domain knowledge, the architectural intent, the judgment about what's worth building. Claude Code brings raw fluency in TypeScript, fast recall of Next.js patterns, and the willingness to write a hundred lines of test scaffolding without complaint. Neither of us could do this work alone in this timeframe.

The pattern that works for me has four steps:

  1. I describe the problem in full sentences. Not "add a booking form," but "add a three-step booking wizard with cross-step validation, session persistence, and conditional fields that change based on package selection."
  2. Claude Code drafts an implementation. The first draft is usually wrong in some way, but in the right neighborhood. It looks like the kind of code I'd write if I were typing fast.
  3. I read the draft carefully. The draft is not a finished thing. It is a proposal, and I read it to check both that it does what I described and that it does not do things I did not ask for, because code that runs is not the same as code that is correct.
  4. I push back, refine, or accept. Sometimes I rewrite parts by hand because the model's pattern doesn't match the rest of the codebase. Sometimes the draft is exactly right and I commit it. Most often I refine, pointing out what's wrong, suggesting a different abstraction, asking for a smaller change.

Most drafts go through at least one round of this before I commit anything. The back and forth is the normal case rather than a sign that something has gone wrong, so I plan for it when I estimate how long a feature will take.

What makes a draft good or bad

I've gotten better at reading drafts critically. The ones that ship without modification share a few properties:

  • The function does one thing, and the boundaries are clear.
  • The error handling matches what's already in the codebase, not the model's defaults.
  • The naming aligns with conventions established earlier in the project.
  • There are no redundant helpers, no defensive validation for inputs that come from internal code.

The drafts that need work tend to have one of these problems:

  • The implementation is plausibly correct, but the off-by-one or the missed edge case is hidden in plain sight.
  • The code reaches for a pattern from a different codebase instead of matching what already exists.
  • The error handling is overgrown: try/catch around things that can't fail, fallbacks for scenarios that don't happen.
  • The comment says what the code obviously does, instead of why.

These aren't syntactic errors. They are intent errors, and catching them is the part of the work that doesn't get easier.

The productivity question

The question people ask first is how much faster this makes me, and I do not have a clean number for it. In rough terms, my experience is this. For things with well-known patterns (auth flows, form validation, REST endpoints, test scaffolding), the cost of an additional feature drops enough that features I would have cut from v1 now make it in. For things that require novel logic, where the pattern doesn't exist in some other codebase, the gain is much smaller, and sometimes there isn't one.

I cannot give you precise numbers. I haven't run the controlled experiment, and the experience is too noisy to reduce to a single number. What I can say is that the projects I'm willing to attempt have changed.

The clearest case is one I lived. In December 2024 I hand-built a static marketing site for a safari operator: hand-maintained HTML served by a small Express app, professional-looking, and inert, because a real CRM behind it was more than I could justify building alone. Just over a year later I rebuilt the whole thing with AI assistance, a booking pipeline, an admin console, AI-drafted itineraries, the back-office that actually runs the business. Same developer, same nights and weekends. I had another year of practice by then too, so I can't hand the whole difference to the tool. What I can say is that the cost of attempting something that large came down far enough that I tried. That is the shift that matters to me, and line counts are a secondary signal at best.

Where the leverage doesn't reach

It does not help with product decisions. It can lay out options; it can't tell me what customers actually need, or which trade-off is worth making. It does not help when the codebase is unusual, when the patterns are bespoke, the model's defaults fight the conventions I've established, and the back-and-forth becomes friction instead of velocity.

It also does not help with the parts that require domain context the model doesn't have. When I needed to model how a Tanzanian safari business actually quotes prices across multiple parks, multiple seasons, multiple permit fees, and four currencies, Claude Code could implement what I described, but I had to do the modeling. The structure of the domain came from me.

The discipline this requires

What working with Claude Code has trained in me is reading code carefully. Not for syntax, but for intent. Most of the bugs I've shipped came from drafts I half-read. The cost of moving fast with AI tools is that the bug surface area per unit time goes up. If you skip review, you ship bugs faster.

The pattern that keeps me honest is treating each draft as if a colleague had submitted it to me for review. I read it, I push back where I disagree, and I don't commit anything I haven't understood. That sounds simple. It is not always easy when the draft looks right at a glance and the next problem is waiting.

The objections I take seriously

Everything above is the account from inside my own workflow, so it describes what the tool does for me and not what it costs elsewhere. Three objections land for me, and I don't think "you're holding it wrong" answers any of them.

Skill atrophy is real, and I feel the pull. The same loop that makes me fast (describe, read, refine) degrades easily into describe, skim, accept. When it does, I stop learning the thing I'm shipping. I've caught myself reaching for the model on a problem I could have reasoned through in ten minutes, and the cost isn't the ten minutes. It's the reps I didn't do. For someone with years of pattern-matching already in muscle memory, that's a slow tax on sharpness. For someone earlier on, it can be the difference between building the fundamentals and renting them. Working code can look like understanding long before the understanding is there, and the tool will cheerfully hide that gap from them and from whoever reviews them. "The AI can just do it" is the sentence I worry about most, and it lands hardest on the people with the least experience to catch what it gets wrong.

The code is plausible, which is exactly the problem. I said earlier that the bug surface per unit time goes up. The reason that matters is the shape of the errors rather than their number. The drafts that are wrong are rarely wrong in ways that look wrong. They're wrong in the off-by-one, the unhandled null, the auth check that's a little too permissive, the dependency pulled in for a single function. Security is where this worries me most. Insecure code looks exactly as plausible as secure code. The over-broad CORS rule, the string-concatenated query, the secret logged "just for now" — none of it announces itself, and fluency makes it easier to skim past, not easier to catch. The main thing between that and production is a human reading every line as if a stranger wrote it, because one did. If you don't yet have the experience to read that way, the tool hasn't removed the danger. It's removed your ability to see it.

The labor question. The optimistic story (the floor rises, the ceiling rises, judgment gets more valuable) has a hole in it. Seniors are made out of juniors. If the entry-level work that used to train people is the first work to get automated, the pipeline that produces the experienced engineers everyone still agrees you need starts to break, and it breaks quietly, years before anyone feels it. "New kinds of jobs will appear" can be true in aggregate and still be cold comfort to a specific person whose role got compressed this quarter. I benefit from these tools. That benefit is not costless, and it is not evenly shared. I don't have the policy answer, and I'm wary of anyone who says they do, in either direction.

I have not changed how I work because of any of this. What has changed is that I no longer describe the tool without also describing what it costs, and to whom.

What I think is coming

I'll resist the grand prediction. The near-term thing I'm fairly sure of is smaller. The gap widens between people who use these tools with judgment and people who let the tools do the judging. The first group looks like clear thinkers who can specify a problem precisely, read a draft critically, and hold a codebase coherent while it grows faster than their typing. The second ships more bugs, faster, and feels productive the entire time.

That's not "AI replaces engineers," and it's not "AI changes nothing." It is a tool that raises what's possible and, in the same motion, raises the cost of using it carelessly: to your codebase, to your own skill, and, at the scale of an industry, to the people whose way into the field used to run through exactly the work it now does first. I find it genuinely useful, and I'm trying to hold that and its costs together rather than pick one.

There is more I want to write about specific patterns: how I structure context for long conversations, how I keep architectural consistency across a few hundred files, what I do when the model's pattern fights mine.