Every Feature Opens a Maintenance Account
Coding agents have developed a dangerous little habit. You ask for one change, and somewhere near the end they offer to add three more.
Would you like a configuration flag? A JSON output mode? A retry option? Maybe a dashboard while we’re here?
The agent can build it. The tests and relevant files are already in the context. So why not?
Just one more feature, one more prompt. You know what I’m talking about.
Then six months later the flag is still there. Somebody relies on the JSON shape. The retry behavior conflicts with a new timeout. The dashboard has a dependency that needs a security update. The agent that offered to build all of it has moved on with its life, mostly because it never had one.
You still own the feature.
That’s the part we need to consider. Every feature is another record in a maintenance ledger.
A Feature Is a Standing Promise
We tend to think of a feature as the code that implements it. Add the function, connect the interface, write the tests, merge the change. Feature complete.
That’s the construction phase. The feature itself is a promise that begins after the merge.
If you add a CLI flag, you’re promising that scripts can keep using it. If you expose a JSON response, you’re promising something about its shape. If you store a new piece of data, you’re promising to preserve, migrate, export, and eventually delete it correctly. If you connect another service, you’re promising to notice when its API changes.
The code might be thirty lines. The promise has no line count.
This is why tiny features get expensive. The implementation fits in one file, but the behavior crosses documentation, tests, support, deployment, security, and every future change near that boundary. Agents are very good at showing us the thirty lines. They’re much less likely to show us the next three years.
The Opening Balance Looks Great
Before coding agents, implementation cost acted as a filter. Not always a good one, but a filter.
Someone had to estimate the work, find time in a sprint, write the code, and get it reviewed. A small convenience feature might lose simply because nobody wanted to spend two days on it. Frustrating, sure, but it forced the question: is this worth building?
Now the estimate is twenty minutes. The agent has already inspected the codebase. It can update the model, add the command, generate the tests, and fix the type errors without needing another meeting. The old cost-benefit calculation collapses, because almost everything looks worth building when you only count the first implementation.
So we say yes more often.
That’s not automatically bad. Plenty of useful software never existed because construction cost too much. Cheaper implementation lets small teams solve problems that used to require a real budget. Good for us, but the maintenance math didn’t collapse along with it.
The feature still adds another path through the system. It still creates behavior that can regress. It still has users, even if the only user is you on a Sunday afternoon six months from now.
The opening balance is cheap. The account stays open.
What Accumulates
Maintenance is easy to wave away because no single piece sounds overwhelming. It’s just one more test. One more paragraph in the docs. One more migration. Then the interest starts adding up:
- Compatibility: Existing callers depend on behavior you considered an implementation detail.
- Testing: Every supported path needs coverage, fixtures, and updates when neighboring code changes.
- Documentation: The feature needs to be discoverable, accurate, and removed from the docs if it goes away.
- Dependencies: A tiny feature can introduce a library that now participates in every upgrade and security review.
- Operations: New jobs, tables, queues, or API calls need logs, failure handling, and a recovery story.
- Support: Someone has to answer why it behaved differently on another machine.
- Removal: Deleting it later means finding its users, migrating their data, and deciding how long compatibility lasts.
None of these costs are unique to generated code. We’ve always paid them. The difference is volume. Agents let us open maintenance accounts much faster than we close them.
A Ten-Minute Flag Is Still an Interface
Let me give you an example.
You have a command that prints a human-readable table. An agent offers to add --json. That sounds great. It probably is great. The code serializes the existing records, the tests compare a sample payload, and the whole change lands before lunch.
Then someone pipes that output into another script.
Now field names matter. Null behavior matters. Ordering might matter even though you never promised it. A renamed internal property breaks an external workflow. Adding a timestamp creates noisy diffs. Removing a field requires a compatibility decision.
The flag didn’t add another display format. It created an API.
Would you still build it? Probably. I like useful CLI tools, and machine-readable output is usually worth supporting. The point isn’t to reject the feature. The point is to recognize the account you’re opening. Once you see it as an interface instead of a ten-minute patch, you define the schema deliberately, document what’s stable, avoid exposing fields that should stay internal, and decide whether versioning matters before somebody’s automation answers that question for you.
Same code. Better ownership.
Backlogs Hide the Statements
One reason maintenance gets away from us is that backlogs are organized around changes, not promises.
The issue says “add export support.” It rarely says:
Maintain this export format for as long as anyone depends on it, update it whenever the underlying model changes, keep its documentation accurate, and provide a safe way to retire it later.
That would look ridiculous in an issue title. It’s still what the issue means.
Agents make backlogs disappear quickly, which feels fantastic. I’ve watched them knock out work that would have sat around for months. But a closed issue can become an open obligation. A project with fifty completed features isn’t necessarily healthier than one with twenty. It might just have thirty more things that can break.
Price the Account Before You Open It
I don’t want a meeting for every CLI flag. The whole advantage of these tools is that we can move fast.
We can still take thirty seconds to ask better questions before accepting the extra code:
- Who will depend on this? A person clicking a button creates a different promise than a script parsing output.
- What new state or interface does it introduce? Stored data and public schemas are much harder to remove than local calculations.
- What has to stay compatible? Name the stable boundary instead of letting users guess.
- How will we know it broke? Tests help, but logs, validation, and recovery may matter more.
- What ongoing work does it create? Dependencies, docs, migrations, provider changes.
- What would cause us to close the account? Decide now whether it’s experimental, permanent, or removable.
If the answers are cheap too, build it. If the feature creates a permanent public contract for a minor convenience, nope, not going in.
Cheap Construction Needs Better Restraint
I’m not interested in making software expensive again. Faster implementation is good. More people turning an idea into a working tool is good. Small teams getting leverage that used to belong to large companies is very good.
We just need to stop treating features as free.
I’d appreciate a follow. You can subscribe with your email below. The emails go out once a week, or you can find me on Mastodon at @[email protected].
AI Programming Software-development Coding-agents Maintenance