Skip to content
All articles
Engineering

I Don't Ship AI Code I Can't Explain

Biju DeyBiju Dey··9 min read

Most of my coding day now starts with a prompt.

I open Claude Code, describe the feature, point it at the right folder and go make chai. When I come back there's usually a working branch waiting for me. Tests pass. The screen looks right in the simulator. Not long ago that would have been a full day of work.

I'm not complaining. This is the best tooling I've ever had, and models like Claude Opus 5.5, Fable 5.1 and GPT-6 Astra keep getting better every few months.

But there's a habit spreading through our industry that worries me, and I'd be lying if I said I've never done it. We merge code we haven't really read. It looks fine, the tests are green, and the next ticket is already waiting.

So I follow one simple rule now. If I can't explain what a change does, I don't ship it.

I don't mean line by line. I mean I should be able to tell a teammate what the change touches, what new things it pulls in, and what happens when it fails. If I can't do that in a couple of minutes, I'm not done yet.

The tools are good. That part is settled.

I'll say this first, because a lot of "be careful with AI" posts skip it. The productivity gains are real.

Google's DORA report for 2025 found that 90% of software professionals now use AI at work, and more than 80% say it has made them more productive. In 2024 the same team had found AI adoption linked to lower delivery throughput. A year later that flipped.

Microsoft studied tens of thousands of its own engineers during its early 2026 rollout of Claude Code and GitHub Copilot CLI. The engineers who picked up those tools merged about 24% more pull requests than they otherwise would have, and the lift held for the full four months they measured. On Stack Overflow's 2026 survey, Claude Code was the most used coding agent (65.5% of people who used one), followed by GitHub Copilot at 58.7%.

So if someone tells you AI makes developers slower, they're quoting old numbers.

The question I care about now is a different one. If code is being written this much faster, is anyone still understanding it at the same speed?

Developers already trust it the right way

Stack Overflow's 2026 survey asked developers how much they trust the output of AI tools. The most common answer wasn't "a lot" or "not at all". It was this:

Most developers trust AI when they can check it

“How much do you trust the output from AI tools or AI agents as part of your workflow?” (Stack Overflow, 2026)

I trust it when I can easily verify the output

48%

Many tasks, but not important work decisions

16.3%

Low-risk work tasks only

16.3%

I do not trust it for most work tasks

12.3%

Many tasks, including important work decisions

6.6%

n = 14,304. The remaining 0.5% answered “Not sure / I do not use AI tools”.

View data as a table
MeasureShare of respondents
I trust it when I can easily verify the output48%
Many tasks, but not important work decisions16.3%
Low-risk work tasks only16.3%
I do not trust it for most work tasks12.3%
Many tasks, including important work decisions6.6%

Source: Stack Overflow Developer Survey 2026: Trust in AI tools

Nearly half of them trust it when they can check it. Only 6.6% trust it with important decisions.

I think that's exactly right, and it's more or less this whole post in one bar chart. Trust comes from checking. Skip the checking and you're relying on luck.

Working code and safe code are different things

Veracode has been testing code from the big models since 2025. Their July 2026 report has a result I think every developer should see.

The models write code that compiles almost 100% of the time. But on average only 56% of it passes their security checks. Last year that number was 55%. The models got a lot smarter in between, and the security number barely moved.

AI code almost always runs. It isn't always safe.

Veracode GenAI Code Security Report · standard prompts with no security instructions

Syntactically correct code

≈100%

Secure code, average across models (2025)

55%

Secure code, average across models (2026)

56%

Secure code, best model in 2026 (GPT-5.5)

68%

Veracode describes syntax correctness as “nearly 100% of the time”. In the 2026 tests, cross-site scripting defences passed 15% of the time and log-injection defences 12%.

View data as a table
MeasurePass rate
Syntactically correct code≈100%
Secure code, average across models (2025)55%
Secure code, average across models (2026)56%
Secure code, best model in 2026 (GPT-5.5)68%

Source: Veracode 2026 GenAI Code Security Report

The weakest areas were the boring web basics. Protection against cross-site scripting passed 15% of the time. Log injection passed 12%. Veracode's own summary says it better than I can: "functional output and secure output are fully separate."

Green tests tell you the feature works. They tell you very little about whether it's safe.

The part that worries me most

A paper from July 2026 has a title I wish I'd come up with: "(Im)Paired Programming: Coding Agents Improve Productivity but Harm Understanding."

The researchers had 54 students build a website, some with a coding agent and some with a normal chatbot. The agent group got through the task more easily. Then everyone answered questions about their own code and tried to extend it without the agent. The agent group did worse on both.

What stuck with me was which habits hurt most. Copy-pasting prompts and auto-accepting edits. And even though people knew they understood their code less, they still preferred the agent because it was quick and easy.

It's a small study, and they were students rather than working engineers, so I don't want to stretch it too far. But the pattern feels familiar. Auto-accept feels like speed on the day. A few weeks later, when something breaks in that part of the app, it feels like debugging someone else's code.

Packages that don't exist

One more, because it's sneaky. Models sometimes import packages that don't exist. If an attacker registers that name on npm or PyPI with something nasty inside, they just have to wait for someone to install it. People call this slopsquatting.

Newer models do it a lot less. A 2026 preprint tested five recent models and found rates of about 4.6% to 6.1%, down from as high as 21.7% for older open-source models. But the same paper found 127 made-up package names that all five models invented in exactly the same way, and 53 of those names were still free for anyone to register.

No unit test will catch that. Ten seconds looking at your package.json will.

Give the agent rules, and write them down

This is the part I feel strongest about, and this year there's finally some data behind it.

A study of 441 repositories, published in August 2026, looked at teams that keep AI configuration in their repo: written rules, coding standards and instructions for their agents. Think of a CLAUDE.md or AGENTS.md file. Every group got faster after adopting agents, with 28 to 38% more commits. But the agent-first repos with no written rules saw their code complexity grow about twice as much.

Repos with written agent rules saw less complexity creep

Increase in cognitive complexity after adopting coding agents · agent-first repositories

With committed AI configuration

+27%

Without committed AI configuration

+53%

441 repositories. Static-analysis warnings also grew 1.7× more without configuration. This is an observational study, and the authors present it as hypothesis-generating.

View data as a table
MeasureIncrease in cognitive complexity
With committed AI configuration+27%
Without committed AI configuration+53%

Source: Denisov-Blanch et al., “A Few Pages of Markdown” (2026)

The authors are careful to call this a correlation. Teams that bother to write rules are probably more disciplined in other ways too. Fair enough. But writing a few pages of markdown takes maybe an hour, so it's a cheap bet.

This is roughly what I'd put in one for a React Native app:

# AGENTS.md

## Stack
- React Native + Expo, TypeScript strict mode
- State: Zustand. Server data: TanStack Query. Navigation: Expo Router.

## Rules
- Run `npm run typecheck && npm test` before saying a task is done.
- Every new screen needs loading, empty and error states.
- Never add a dependency without asking first. Explain why it's needed.
- Never touch auth, payments or database migrations without asking.
- Never put secrets or API keys in the app. Use the backend.
- Keep changes small. One feature per change.

## When unsure
- Ask before guessing. Give me 2 or 3 options with trade-offs.

It won't stop every bad decision. It does stop a lot of the small annoying ones, like the agent pulling in a second date library because it didn't notice we already have one.

How I work with agents now

Here's my process. It isn't fancy.

1. I describe the feature properly before the agent writes anything

Compare these two prompts for the same feature.

Add image upload to the chat screen.
Add image sending to the chat screen.

- Use expo-image-picker for selection (gallery and camera).
- Compress to max 1600px on the long edge, JPEG quality around 0.7, before upload.
- Upload to S3 with a pre-signed URL from POST /uploads/sign. No AWS keys in the app.
- Show the image in the thread straight away with a progress ring. Allow cancel.
- If the network fails, retry 3 times with backoff, then show "Tap to retry".
- If photo permission is denied, explain why and offer a button to open Settings.
- Reject files over 10 MB with a clear message.
- No new dependencies besides expo-image-picker and expo-image-manipulator.

Test cases I care about:
1. Pick, compress, upload, message shows as sent.
2. Airplane mode mid-upload: retries, shows "Tap to retry", works when back online.
3. Permission denied: explanation shown, no crash.
4. 15 MB image: rejected with a message, nothing uploaded.

The first one is a wish. The agent has to guess the library, the limits, where files go and what happens offline, and whatever it guesses becomes your architecture.

With the second one the agent still writes almost all the code. I've just made the decisions that matter: which libraries, how it behaves, how it fails, and how I'll know it works.

If I'm not sure what the best approach is, I ask the agent for a few options first and then pick one myself. Asking for advice is fine. Letting it decide something I don't understand is where things go wrong.

2. I read the shape of the diff, not every line

I don't read every line of a generated form or a styled component. I do check a few things every time:

  • Which files changed, and is anything unrelated in there?
  • Any new dependencies? Do they actually exist, and are they maintained?
  • Where does the data go, from user input to storage to screen?
  • What happens when the network fails, the list is empty or a value is null?
  • Any keys, tokens or secrets in the code or the logs?
  • Did it build what I asked for, or did it add extra features I never asked for?

The exception is anything to do with login, payments, permissions, deleting data or migrations. Those I read line by line, every time. After seeing Veracode's numbers I don't think that's paranoid.

3. I write the test cases, the agent writes the test code

When the same model writes a feature and its tests, the tests tend to check what the code does rather than what it should do. If the logic is wrong, the test happily confirms the wrong logic.

So I decide what needs testing from the requirement, and let the agent turn that list into test code. It's good at that part.

4. I use the feature on a real phone and try to break it

On mobile this catches more than you'd expect. Before I call something done I'll usually:

  • turn on airplane mode halfway through
  • deny the permission, then allow it later from Settings
  • send the app to the background mid-action and come back
  • rotate the screen and open the keyboard on a small phone
  • turn on large text and the screen reader
  • tap the same button five times quickly

Automated tests stop old bugs from coming back. Poking at the app by hand finds the ones nobody thought of. You need both.

5. A person approves the merge

I let AI review every pull request as a first pass, and it does catch real problems. But a human has to understand the change and say yes, because when it breaks at 2 a.m., a human gets the call.

Where I draw the lines

Every team should write its own version of this. Here's mine.

The agent can just do itI review the structureI decide, every time
Boilerplate, screens, stylingBusiness logic and stateLogin, sessions, permissions
Test code from my test casesDatabase queriesPayments
Refactors that already have testsReal-time sync and offline queuesDeleting data and migrations
Explaining unfamiliar codeError handling and retriesSecrets and infrastructure access
Docs and commit messagesEvery new dependencyApproving the merge

The right-hand column doesn't mean the agent can't help. It can draft all of it. It means I need to understand it and make the call before it goes out.

A few things I had wrong

When I started writing this I had some opinions that didn't survive the research.

I thought reviewing code with AI was pointless. It isn't. It's a good first filter, as long as a person makes the final call.

I thought reading line by line was always overkill. It is for boilerplate. For auth, payments and migrations it's the minimum.

And I thought manual testing mattered more than automated testing. Really they find different kinds of bugs, and you need both.

The short version

AI made writing code cheap. Understanding it is still expensive, and that's where the value is now. The developers I'd most want to work with aren't the fastest prompters. They're the ones who can open any file the agent wrote and tell you what it does and where it might break.

Use the agents. I use them all day. Just stay in the driver's seat.

If you have a rule or habit that works for your team, I'd love to hear it. You can find me on LinkedIn or send me a message through the contact page.

FAQ

Do AI coding agents make developers more productive in 2026?

+

Yes, according to the recent data. In a 2026 study of tens of thousands of Microsoft engineers, people who adopted Claude Code or GitHub Copilot CLI merged about 24% more pull requests. Google's 2025 DORA report also links AI adoption to higher delivery throughput, reversing its 2024 finding.

Is it safe to use AI-generated code in production?

+

Yes, as long as it's reviewed and tested like any other code. In Veracode's 2026 tests, AI code compiled almost every time but passed security checks only 56% of the time on average, so working code still needs a human check.

Should I review every line of AI-generated code?

+

Not every line of boilerplate. Always check the structure: which files changed, new dependencies, data flow and error handling. Read line by line anything that touches login, payments, permissions, deleting data or database migrations.

What should go in an AGENTS.md or CLAUDE.md file?

+

Your project's house rules: the stack, coding standards, how to run tests, which libraries are allowed, and what the agent must never do without asking, like adding dependencies or touching auth. A 2026 study of 441 repositories linked committed rules like these to much smaller growth in code complexity after teams adopted coding agents.

Do I still need manual testing if I have automated tests?

+

Yes. Automated tests stop old bugs from coming back. Manual testing on a real device finds the problems nobody thought to write a test for, especially on mobile with bad networks, permissions and small screens.

#AI#Coding Agents#Code Review#Testing#React Native