There is a growing complaint in software teams right now:
“AI is great at building new features, but it struggles to maintain existing code, especially in large projects.”
At first glance, that sounds reasonable. Many developers have seen AI do a decent job scaffolding something new, only to produce weak or risky results when asked to change mature code in a large system.
But I think this conclusion is wrong.
In many cases, AI is not failing because maintenance is inherently beyond it. It is failing because teams are approaching maintenance badly. They are giving the agent poor inputs, missing context, no clear boundaries, and no process — then blaming the output.
They are treating the coding agent like a black box that should magically understand the entire project, the business intent, the architecture, the technical debt, the hidden side effects, the existing conventions, and the correct implementation path.
That is not how real maintenance has ever worked.
If you gave a junior developer a one-line instruction like “change the planning logic” and expected them to safely update a complex codebase without a feature specification, technical notes, architecture guidance, acceptance criteria, or a review process, you would not call that good engineering. You would call it poor management and poor process.
Yet that is exactly how many teams are using AI.
The real issue is not that AI is bad at maintaining code. The issue is that maintenance has always required structure, and AI makes that painfully obvious.
Maintenance is a context problem, not just a coding problem
Greenfield work is often easier for AI because it has fewer constraints.
If you ask an agent to create a new component, scaffold a service, or generate a prototype, the task is often additive. The boundaries are narrower. Even when the output is imperfect, the damage is usually limited.
Maintenance is different.
When you maintain an existing project, you are not just adding code. You are entering a living system. That system has history, assumptions, edge cases, inconsistencies, and trade-offs. It often contains behavior that only makes sense once you understand how multiple parts fit together.
A maintenance task is rarely just “make this one change.” It is usually some combination of:
- understanding the current behavior
- understanding why the behavior exists
- locating the correct place to make the change
- preserving existing intent
- avoiding regressions
- keeping architectural consistency
- updating documentation
- updating tests
- validating that the feature as a whole still makes sense
This is why maintenance feels harder. It is not because the code edit itself is harder. It is because the surrounding context matters far more.
And that is exactly where vague prompting falls apart.
The black-box mistake
A very common maintenance prompt looks something like this:
“Please update the scheduler so urgent jobs are handled better.”
That sounds like a request, but it is actually a bundle of missing information.
What does “urgent” mean in this system?
Where is priority currently calculated?
Is priority derived in the UI, the API, or the domain layer?
What existing business rules already affect scheduling?
Are there reports or dashboards that depend on the current behavior?
Should the agent refactor the logic, patch it, or redesign it?
Are there technical constraints it must preserve?
What behavior must not change?
Without those answers, the agent is forced to guess.
Then the same team says, “AI is bad at maintenance.”
No. The task was badly framed.
AI is not failing because code maintenance is impossible. It is failing because developers are often skipping the same setup they would normally need to give a human engineer.
The right mental model: set the agent up like you would a real developer
The most useful way to think about AI in maintenance is not as an all-knowing senior engineer.
Think of it as a very fast implementation partner that still needs structured guidance.
If you want it to succeed, give it the same kinds of things you would give a real developer entering the task:
- the feature intent
- the requested change
- the existing feature specification
- the technical specification
- the relevant files or modules
- known risks and constraints
- what must remain unchanged
- edge cases
- examples
- acceptance criteria
- documentation expectations
That is not overkill. That is responsible maintenance.
In fact, AI often performs best when the team becomes more disciplined, not less.
Why large projects expose bad AI usage faster
In a small toy project, an agent can sometimes get away with inference. In a large codebase, that approach breaks down quickly.
Large projects usually contain:
- multiple generations of architecture
- old and new coding styles mixed together
- incomplete abstractions
- undocumented assumptions
- legacy workarounds
- domain knowledge spread across many files
- naming inconsistencies
- integration points that are easy to miss
No agent can safely infer all of that from a vague prompt.
The larger the project, the more it needs a map.
That map comes from documentation, process, review, and explicit instructions. If those are missing, the problem is not just an AI problem. It is a maintainability problem in the team itself.
AI simply reveals it faster.
“We don’t have time to write all that documentation”
This is the objection that usually comes next.
A team agrees that specifications and documentation would help, but then says:
“We don’t have time to go write feature specs and technical specs for this huge existing codebase.”
That sounds practical, but it misses the opportunity completely.
This is not actually a hard problem to solve.
If the codebase already exists, then AI can be used to help generate the missing documentation from the existing system.
In other words, one of the first useful AI investments in a large legacy codebase is not code generation at all. It is documentation generation and refinement.
You can create custom agents that specialize in:
- identifying existing features from the codebase
- generating draft feature specifications
- generating draft functional specifications
- generating technical summaries of how a feature works
- identifying dependencies and affected modules
- describing current flows and responsibilities
- extracting architectural patterns from the implementation
- highlighting inconsistencies or undocumented behavior
You can also give those agents specific instructions about:
- coding standards
- architectural conventions
- module responsibilities
- naming expectations
- UI patterns
- layering rules
- documentation format
- domain terminology
That gives the agent a much better chance of understanding a massive codebase in a structured way instead of treating it like a pile of disconnected files.
So when people say, “We do not have time to document everything,” the answer should not be, “Then let’s skip that step.”
The answer should be:
Step one is to get the existing features up to date with documentation — not just for AI, but for the humans working in the codebase too.
Because this is not only an AI enablement problem. It is a team productivity problem.
If you want a junior developer to work on an existing feature, you need to give that person a chance to understand what the thing actually is, how it works, and why it exists. Proper documentation helps enormously with that.
The same is true for AI.
Documentation is not overhead. It is operational infrastructure.
Teams often talk about feature specifications and technical specifications as if they are nice-to-have documents that slow development down.
That is the wrong way to think about them.
In a large existing system, documentation is part of the operating infrastructure of the codebase.
It is what allows people to answer questions like:
- What does this feature do?
- Why does it exist?
- What is the user-facing behavior?
- What business rules apply?
- Which modules are involved?
- Where are the extension points?
- What should never be changed accidentally?
- What technical design decisions are intentional?
- What assumptions does this feature depend on?
Without that, every maintenance task begins with archaeology.
And archaeology is slow, error-prone, and expensive.
The point is not to create giant documents for the sake of process. The point is to reduce ambiguity so that humans and AI can work effectively.
The correct maintenance workflow for AI-assisted development
If you want AI to maintain an existing project well, the process should not begin with “go change the code.”
It should begin with understanding.
A mature workflow looks something like this.
1. Start with the feature, not the code edit
Before any implementation begins, define the change in terms of feature intent.
What problem is being solved?
What user or business need is changing?
What behavior should change?
What behavior must remain intact?
A vague implementation instruction leads to vague implementation choices. A clear feature-level change request gives the work meaning and boundaries.
2. Review the existing feature and technical documentation
If the feature already exists, the first step should be to review its current documentation.
That includes:
- the main feature specification
- the technical design or technical specification
- architecture notes
- existing acceptance criteria
- known constraints or rules
- previous decisions that shaped the implementation
This is critical. Maintenance should not start from scratch every time. It should start from the current source of truth.
If developers skip this and go straight to code, they are forcing the agent to reverse-engineer intent from implementation details alone. That is both slower and riskier.
3. If the docs do not exist, generate them
This is the missing step most teams overlook.
If the codebase lacks good feature specifications and technical specifications, do not use that as an excuse to continue working blindly.
Use AI to help create them.
That might mean:
- generating draft feature specs from code and UI flows
- generating draft functional descriptions from existing behavior
- generating technical summaries from the implementation
- building a catalog of major features and affected modules
- creating internal documentation agents specialized for the project
These drafts will still need human review, but that is far easier than starting from nothing.
The important shift is this:
You do not need perfect documentation before using AI. You can use AI to help build the documentation baseline that makes future AI usage much stronger.
4. Create a change feature specification
When maintaining an existing feature, there should be a temporary working document for the change itself.
This can be called a change feature specification.
Its purpose is to describe the maintenance request in a focused way:
- what is changing
- why it is changing
- what part of the feature is affected
- what constraints apply
- what acceptance criteria define success
- what technical areas are likely involved
- what risks or dependencies should be considered
This is not meant to replace the main feature specification. It acts as a working artifact for the specific maintenance task.
That matters because maintenance work often starts as a narrow change request, and you need a place to define that request clearly before it is merged back into the full feature story.
5. Ask the agent to research before implementing
One of the best ways to improve AI performance is to separate research from implementation.
Instead of saying:
“Make the change.”
Say something like:
“Review the current feature documentation, inspect the codebase, identify the modules involved, explain the current behavior, list risks and assumptions, and propose an implementation plan. Do not change code yet.”
This changes the entire quality of the interaction.
Now the agent is not improvising. It is building a model of the system first.
That is how experienced engineers work too. They do not rush into edits before understanding the shape of the problem.
6. Create a technical change plan
Once the current state has been analyzed, the next step is to produce a technical plan.
This plan should include:
- the files or modules to touch
- where the real source of behavior lives
- whether the change is additive, corrective, or refactoring-related
- how the current architecture should be respected
- what tests need to change
- what documentation needs to be updated
- what risks exist
- what assumptions still need verification
This step matters because it prevents maintenance from becoming random code surgery.
7. Implement the change incrementally
Once the plan is understood and reviewed, implementation can begin.
This should ideally happen in small, reviewable steps rather than giant one-shot edits.
That gives the team a chance to verify:
- that the right layer is being changed
- that no duplication is being introduced
- that naming and patterns remain consistent
- that tests reflect the intended behavior
- that side effects are controlled
AI is usually much safer when directed through staged work than when told to perform a large maintenance change in one jump.
8. Update the main feature and technical documentation
This is one of the most important parts, and one of the most neglected.
If a feature already has documentation, then maintaining the feature means maintaining that documentation too.
This should happen in two stages:
First, use the temporary change feature specification during the task.
Then, once the implementation is complete, update the main feature specification and main technical documentation so they remain the authoritative and current description of the feature.
This is essential.
If the change spec stays separate and the main spec is not updated, the project slowly accumulates fragmented truth:
- the old feature spec says one thing
- a ticket says another
- a technical note says something else
- the code now behaves differently again
That makes future maintenance worse for both humans and AI.
A healthy system has one up-to-date main feature definition, not a trail of partially overlapping change history that developers have to mentally reconstruct.
Maintenance is also about preserving the truth of the system
One of the biggest mistakes teams make is acting as if maintenance is complete once the code works.
It is not.
A feature is not just code. It is the combination of:
- business intent
- user behavior
- technical design
- implementation
- tests
- documentation
If one of those changes and the rest do not, the system drifts.
That drift is one of the main reasons mature codebases become difficult to work in. Over time, the implementation and the documentation stop matching. The feature’s original intent becomes unclear. Future developers, and future agents, are left to infer too much.
So proper maintenance is not just “make the code pass.” It is “make the system truthful again.”
Every maintenance task should end with a whole-feature review
This is another area where teams often stop too early.
They verify the changed line, the changed screen, or the changed output. But that is not enough.
Once a maintenance change has been implemented, the entire feature should be reviewed as a whole.
This is important because local correctness does not guarantee feature-level coherence.
A change can be technically correct and still weaken the feature.
The final question should not just be:
“Did we make the requested change?”
It should be:
“Does the feature, as a whole, still match its intended purpose after this change?”
That is a much higher bar — and it is the right one.
Best practices for AI-assisted maintenance
If teams want better outcomes, these are the habits that matter most.
1. Never start with only a code instruction
Start with intent, scope, and feature context.
2. Review existing documentation first
Do not force the agent to reconstruct what the feature already knows on paper.
3. If documentation is weak, use AI to help generate it
Do not let missing documentation become the excuse for continuing blind.
4. Create a change feature specification
Use a temporary spec to capture the exact maintenance task before code changes begin.
5. Keep the main feature spec authoritative
After implementation, merge the change back into the primary feature and technical docs.
6. Separate research from implementation
Ask the agent to understand the system before asking it to edit it.
7. Ask for risks and assumptions
Make uncertainty visible.
8. Define technical guardrails
Be explicit about what patterns to follow, what layers to avoid, and what must stay stable.
9. Work incrementally
Small, reviewable changes are safer than giant AI-generated rewrites.
10. Require documentation and tests as part of done
Do not treat them as optional follow-up.
11. Review the feature holistically after the change
Do not only review the diff. Review the feature.
A practical example
Here is the difference between weak AI usage and strong AI usage on a maintenance task.
Weak request
“Update the work order filter so it supports team and date.”
That sounds reasonable, but it leaves far too much unsaid.
Strong request
We are maintaining the existing work order list feature.
Review the current feature specification and technical documentation first. If they are incomplete, generate draft versions from the existing code flow and current UI behavior.
We need a maintenance change so planners can filter by assigned team and planned date range.
Create a change feature specification that defines:
- the business intent
- expected UX behavior
- acceptance criteria
- constraints
- impacted areas
Then inspect the codebase and identify:
- the current filter flow
- relevant components and hooks
- where filter state is stored
- whether the API already supports the new parameters
- risks or architectural concerns
Produce an implementation plan before changing code.
After implementation:
- update the main feature specification
- update the technical documentation
- review the feature end-to-end to confirm the entire filter experience still matches the intended design
That request is dramatically stronger.
It frames the task as maintenance of a feature, not just a code patch.
It tells the agent to use existing documentation.
It includes a fallback for generating missing documentation.
It introduces a change-spec workflow.
It requires planning before editing.
It includes the requirement to update the authoritative docs.
And it requires a feature-level review at the end.
That is how AI should be used in serious projects.
Guardrails are not bureaucracy
Some people hear this kind of process and think it sounds heavy.
It is not heavy. It is controlled.
The truth is that many developers have been using AI in a very sloppy way because the interface feels conversational. They assume that because they can ask casually, they can work casually.
But software maintenance has consequences.
The bigger the project, the more dangerous vague instructions become.
The more important the feature, the less acceptable guesswork is.
Guardrails are how you turn AI from a risky generator into a reliable collaborator.
Useful guardrails include things like:
- do not introduce a second source of truth
- prefer extending existing patterns over inventing new ones
- preserve backward compatibility
- list assumptions before implementation if behavior is unclear
- update docs if feature behavior changes
- add tests for changed business rules
- refactor only where necessary to support the change
- review the feature holistically after implementation
That is not bureaucracy. That is engineering discipline.
AI should support engineering discipline, not replace it
One of the worst habits teams can develop is using AI to bypass thinking.
AI should not replace the need for structured understanding. It should accelerate it.
Its real value in maintenance is not just typing code faster. It is helping with:
- feature analysis
- technical summarization
- impact analysis
- planning
- documentation updates
- test drafting
- identifying inconsistencies
- surfacing assumptions
- reviewing whether the implementation matches the stated intent
The teams that will get the most out of AI are not the ones who ask it to “just do the work.”
They are the ones who use it to strengthen their process.
The uncomfortable truth behind the complaint
A lot of “AI is bad at maintenance” complaints are actually exposing something else:
the team’s maintenance process is weak.
If the project has poor documentation, vague ownership, weak specs, inconsistent architecture, and unclear acceptance criteria, AI will struggle.
But so will humans.
The difference is that human developers often compensate with tribal knowledge, hallway conversations, memory, intuition, and instinct. AI cannot rely on those things. So the weakness becomes visible much faster.
That can make it feel like the AI is the problem.
Often, it is just making the process problem impossible to ignore.
Final thought
AI is not inherently bad at maintaining existing projects.
What it is bad at is reading minds, inferring missing intent, and safely navigating large systems without guidance.
If you want AI to be effective in maintenance, you must stop treating it like an all-knowing black box and start treating it like a highly capable development partner that still needs context, structure, documentation, and guardrails.
That means:
- using existing feature and technical documentation
- generating missing documentation where necessary
- creating a change feature specification for maintenance work
- researching before implementing
- planning before editing
- updating the main specifications after the change
- reviewing the entire feature after implementation to ensure it still matches its true intent
That is how you make AI useful in large, existing projects.
Not by asking it to magically know everything.
But by giving it the same conditions any good engineer would need to succeed.
And in the long run, that is probably the real lesson here:
AI is not lowering the importance of good engineering process.
It is making it impossible to ignore.