Skip to content
← Back to writing

Agents Fail Quietly

Seven apps in four months, and the failure mode I had already spent years learning to fear.

August 2026

·

5 min read

There's a bug in Everywhen that never happened, and I think about it more than most of the ones that did.

Everywhen is a geography-and-history learning game I shipped to the App Store. It tracks how many times you've recalled a place. The obvious way to store that is a counter — a column called attempts, incremented on every answer. It's one integer. It's fine.

It isn't fine. Answer three questions on your phone in airplane mode, answer one more on your iPad, then reconnect. Last write wins. The count that syncs last overwrites the other rather than summing with it, and three attempts vanish. Nothing errors. Nothing logs. The app looks like it's working, because by every check it makes, it is. The user notices weeks later that their numbers feel wrong, and by then there's no record of what was lost.

So Everywhen doesn't store counters. It stores an append-only log of attempts and derives the count from it, which makes every sync an idempotent upsert with no merge algorithm to get wrong. The row-level security is built the same way: an event trigger makes a forgotten policy impossible rather than merely unlikely, because I would rather a migration refuse to run than have a table quietly start serving someone else's rows.

I bring this up because it's the shape of nearly everything I believe about building software. The dangerous failure is the quiet one. A crash tells you it crashed. A 500 tells you it's a 500. The bugs that actually hurt people are the ones where the system reports success and is wrong.

That's the lens I brought to working with AI. It turned out to be the whole game.

Writing stopped being the bottleneck

In the last four months I've built seven products as a one-person studio — four live on the App Store, three in review. Games, edtech, a journaling app, native utilities. Most of that code was written by agents.

I want to be precise about what that did and didn't change, because the discourse tends to skip this part. It did not turn me into a faster typist with extra steps. It changed which half of the job is scarce.

It used to be that producing code was expensive and reading it was cheap — I'd just written the thing, it was still warm, I knew where the bodies were. Now producing code is nearly free and reading it is the entire cost. My actual working day is specification and review. The agent proposes. I decide.

And the thing about an agent's output is that it is confident. It compiles. It has tests. The tests pass. The naming is good, the structure is plausible, and it will explain its reasoning to you in fluent, well-organized paragraphs. Nothing in the artifact distinguishes the parts that were carefully derived from the parts that were pattern-matched off something adjacent and almost right.

Which is to say: an agent's characteristic failure mode is silence. It's the exact failure mode I'd spent years learning to design against, arriving now at volume, with excellent grammar.

The blank page

The clearest case I've hit: I was building a writing tool — you speak a revision out loud, the model returns a diff, and nothing touches your words until you accept it.

Somewhere in the save path there was a branch where, under one particular sequence of events, the editor's contents resolved to empty before the write. The app would save. It would report success. It would write a blank page over a chapter.

Every test passed. Of course they did — the tests were written against the same understanding that produced the bug. I found it reading the write path out loud, the way I read a schema before a migration, asking the only question I've learned to trust: what does this do when it's wrong?

I was on a deadline. I nearly didn't look. That's the part I keep sitting with — not that I caught it, but how completely ordinary it would have been to ship it.

What I actually do now

Four practices came out of this, and they're all downstream of the same idea.

I review write paths first and everything else second.

A read that's wrong is annoying. A write that's wrong is unrecoverable. I spend my review budget where the damage is permanent: saves, deletes, migrations, auth, anything touching a user's data or their money. I will skim a view layer. I will not skim a save.

I specify the failure case, not the feature.

The prompt that produces good code isn't “add autosave.” It's “add autosave; an empty buffer must never overwrite a non-empty stored document; if buffer state is indeterminate, refuse the write and surface it.” Agents are remarkably good at satisfying constraints you actually state, and they will not invent your paranoia on your behalf.

I prefer designs with no quiet failure mode over designs that are merely correct.

Append-only over counters. Natural composite keys over merge logic. Constraints in the database rather than conventions in the code. This was already my taste — AI made it load-bearing, because a system where mistakes are loud is a system where a fast, careless collaborator can't do much lasting harm.

I read tests as evidence, not as coverage.

A generated suite tells you the code does what its author believed. It cannot tell you the author was thinking about the right thing. I read a handful closely and ignore the number.

The part I'd argue about

The usual framing here is trust — how much do you trust the model, when do you check its work, what's your confidence threshold. I think trust is the wrong axis entirely.

I don't trust a compiler either. I just know precisely what it guarantees and what it doesn't.

The useful question isn't how much do I trust this? It's what is this thing's failure mode, and have I built somewhere for that failure to be loud? You can work very fast with a collaborator who is reckless, as long as you know the exact shape of how they go wrong and you've made that shape visible. That isn't a compromise with AI. That's most of engineering, and it always was.

Seven apps in four months is a velocity number, and velocity numbers are boring. The number I care about is zero — the number of times a user has written to me about lost work.

That one I earned by reading the save path.

Timothy Yang designs and ships products end to end — see the work or get in touch.