A software backlog rarely stays organized for long.
A bug arrives from a customer. Someone adds a feature request. A regression appears after a release. Documentation needs updating. A support issue looks like a bug at first, but turns out to be configuration.
Meanwhile, developers are already fixing things every day.
The queue still grows.
Adding an AI coding agent can increase implementation speed, but that does not automatically make the maintenance workflow easier to operate. If every item goes directly from ticket to generated code, the team still has to work out what the issue is, whether it can be reproduced, how risky the change is, and what evidence the reviewer should trust.
A better approach is to treat AI-assisted software maintenance as a workflow with clear stages.
The AI can help with classification, investigation, reproduction, implementation, testing, and preliminary review.
Humans can keep ownership of the decisions that determine what actually ships.
This guide shows one practical way to structure that system.
1. Start with the backlog, not the coding agent
Before adding agents, define the types of work that enter the maintenance queue.
A simple starting model could be:
type MaintenanceItemType =
| "bug"
| "feature"
| "documentation"
| "support"
| "regression"
| "duplicate"
| "unknown";
The purpose is not to create perfect labels.
It is to stop every incoming item from entering the exact same engineering path.
A documentation correction does not need the same workflow as a regression affecting customer data. A feature request should not be treated like a confirmed bug. A duplicate issue may not need implementation at all.
The first job of the system is therefore:
Understand what entered the queue.
A classification result should also leave useful context behind.
type ClassificationResult = {
type: MaintenanceItemType;
confidence: number;
reasoning: string[];
suggestedNextStep:
| "reproduce"
| "analyze"
| "documentation"
| "support_review"
| "close_or_merge";
};
Now the next stage does not have to rediscover the same information.
2. Do not let every reported bug become a coding task
A bug report is evidence that someone experienced a problem.
It is not automatically proof that the problem can be reproduced from the current codebase.
Before asking an AI agent to edit production code, give another stage responsibility for reproduction.
Its job is narrow:
- Understand the reported behavior.
- Find the relevant area of the codebase.
- Create the smallest useful reproduction.
- Confirm whether the problem exists.
- Record what happened.
A useful result might look like this:
type ReproductionResult =
| {
reproduced: true;
environment: string;
steps: string[];
failingTest?: string;
observedBehavior: string;
expectedBehavior: string;
}
| {
reproduced: false;
attempts: string[];
missingContext?: string[];
notes: string;
};
This creates a strong boundary.
If the issue cannot be reproduced, the workflow can ask for more information instead of immediately producing a speculative patch.
If it can be reproduced, the implementation agent receives evidence rather than a vague ticket.
3. Add an analysis stage before implementation
Reproduction tells you that something is wrong.
It does not tell you what the safest change should be.
A separate analysis step can answer questions such as:
- Which package or service owns this behavior?
- Is a public interface affected?
- Could the change break existing consumers?
- Which tests should be updated?
- Does documentation need to change?
- Is the issue isolated or architectural?
- What level of review should this receive?
Represent that output explicitly.
type ChangeAnalysis = {
affectedAreas: string[];
proposedApproach: string;
requiredTests: string[];
documentationImpact: boolean;
compatibilityRisk: "low" | "moderate" | "high";
securitySensitive: boolean;
};
Now the implementation stage gets a scoped task.
That is much healthier than asking one agent to read a ticket, decide what it means, invent an architecture change, write the code, and validate its own assumptions in one uninterrupted run.
4. Pass evidence between stages
Avoid making the system depend on one giant hidden agent conversation.
Instead, let each stage produce an artifact that the next stage can inspect.
A simple workflow might look like:
Incoming item
↓
Classification
↓
classification.json
↓
Reproduction / validation
↓
reproduction.json + failing test
↓
Change analysis
↓
analysis.json
↓
Implementation
↓
patch + test results
↓
Independent review
↓
review.json
↓
Human decision
This gives the workflow memory without hiding all of that memory inside a prompt.
It also makes failures much easier to debug.
If the implementation is wrong, you can inspect whether the problem came from:
- bad classification
- weak reproduction
- incorrect analysis
- implementation failure
- insufficient tests
That is much more useful than receiving one failed agent run and trying to infer where its reasoning drifted.
5. Let the implementation agent implement
Once the workflow has:
- a classified issue
- reproduction evidence
- an analysis of the change
- expected tests
- known risk
the coding task becomes much more focused.
Its contract can be straightforward.
type ImplementationInput = {
issueId: string;
reproduction: ReproductionResult;
analysis: ChangeAnalysis;
};
type ImplementationResult = {
changedFiles: string[];
summary: string;
testsRun: string[];
testsPassed: boolean;
warnings: string[];
};
The implementation agent should produce a change that can be reviewed.
It should not decide by itself that the change deserves to ship.
Those are separate responsibilities.
6. Use an independent review stage
An agent reviewing its own work may repeat the assumptions that caused the original mistake.
A separate review stage gives the patch fresh context.
The reviewer can check:
- Does the change actually solve the reproduced problem?
- Did anything outside the intended scope change?
- Are tests sufficient?
- Is backward compatibility affected?
- Is there a security concern?
- Does documentation need to change?
- Does the implementation match the analysis?
A simple review output could be:
type AutomatedReview = {
correctness: "pass" | "concern";
testCoverage: "sufficient" | "insufficient";
sideEffectRisk: "low" | "moderate" | "high";
compatibilityRisk: "low" | "moderate" | "high";
concerns: string[];
};
Notice what this stage does not return:
ship: true
The review agent prepares evidence.
The shipping decision can remain human-owned.
7. Match human attention to the risk
Not every maintenance change deserves the same amount of review.
A small documentation correction and a change to authentication behavior should not consume identical attention.
Create a risk model that is simple enough to use consistently.
For example:
type ReviewTier =
| "routine"
| "focused"
| "deep";
Then route work accordingly.
Routine review
Examples:
- documentation changes
- typo fixes
- well-contained cleanup
- clearly isolated corrections
AI can prepare most of the work.
A human verifies the result.
Focused review
Examples:
- integration changes
- contained product fixes
- provider-specific behavior
- small features within an established pattern
The reviewer sees reproduction evidence, tests, implementation context, and risk notes.
Deep review
Examples:
- authentication
- authorization
- billing
- public APIs
- data boundaries
- core product behavior
- migrations with broad impact
AI can still perform investigation and preparation.
Human review becomes much deeper because the consequence of a mistake is larger.
A useful rule is:
Automate preparation aggressively. Scale human attention with consequence.
8. Give every agent only the permissions it needs
A classifier does not need the same access as an implementation agent.
A documentation agent does not need production credentials.
A reproduction agent may need to execute code, but it does not necessarily need access to internal systems outside its task.
That suggests a permission model like:
Classifier
Read issue
Read repository metadata
Reproducer
Read repository
Install dependencies
Run tests
Analyzer
Read repository
Read reproduction evidence
Implementer
Write inside isolated workspace
Run tests
Reviewer
Read patch
Read evidence
Run verification checks
Keep the boundary narrow.
AI-assisted maintenance often processes untrusted content from:
- issue descriptions
- comments
- pull requests
- links
- package dependencies
- code submitted by outside contributors
The execution environment should assume that some of that input may be hostile.
9. Run code-changing agents in isolated environments
Any agent that can install dependencies, execute code, or modify a repository should operate inside an isolated workspace.
A simple lifecycle looks like:
Maintenance task created
↓
Fresh sandbox starts
↓
Repository state loaded
↓
Task-specific secrets provided
↓
Agent performs its job
↓
Artifacts saved
↓
Sandbox destroyed
Network access should also be intentional.
If an agent does not need unrestricted outbound access, do not give it unrestricted outbound access.
The goal is not to make AI harmless.
The goal is to contain the possible impact of one failed or manipulated task.
10. Treat the workflow as a queue of observable jobs
Once several agents are involved, execution state should be visible.
A maintenance item can move through states such as:
received
↓
classified
↓
reproducing
↓
analyzing
↓
implementing
↓
automated_review
↓
human_review
↓
merged / rejected / deferred
Store that state.
type MaintenanceJob = {
id: string;
itemId: string;
stage:
| "received"
| "classified"
| "reproducing"
| "analyzing"
| "implementing"
| "automated_review"
| "human_review"
| "complete";
status:
| "queued"
| "working"
| "blocked"
| "failed"
| "complete";
};
Now the team can answer operational questions quickly.
What is currently being investigated?
Which tasks are blocked?
Which agents fail most often?
What is waiting for human review?
Which changes are high risk?
Where is the backlog actually spending time?
That visibility matters as much as raw coding speed.
11. Evaluate every agent against its own job
A specialized agent gives you a useful testing surface.
You can maintain an evaluation set for classification:
"Update retry documentation"
Expected: documentation
"Streaming duplicates the final event"
Expected: bug
"Add support for another provider"
Expected: feature
Then another evaluation set for reproduction.
Another for risk analysis.
Another for review.
A failed agent run can become a new evaluation case.
Over time, the workflow improves because recurring mistakes become tests rather than memories stored in somebody's head.
This is much harder when one general-purpose agent owns the entire lifecycle.
12. Start with one maintenance bottleneck
You do not need seven agents on day one.
Start where the team is already losing time.
For many teams, that may be bug triage.
A first version could be:
Incoming issue
↓
AI classification
↓
AI reproduction attempt
↓
Evidence attached to ticket
↓
Human decides next action
That already improves the quality of the queue.
Once that works reliably, add analysis.
Then implementation.
Then automated review.
Then backports or documentation automation.
Building incrementally has two benefits.
You learn where agents actually help.
And you discover the security, context, and evaluation requirements before giving the system broader responsibility.
13. A small SaaS team can use the same architecture
A large open-source repository and a five-person SaaS team operate at very different scales.
The workflow principles still transfer.
Imagine a SaaS product receiving:
Customer bug
Feature request
Support escalation
Regression
Documentation issue
A useful maintenance system could turn that into:
Incoming work
↓
Classify
↓
Reproduce or validate
↓
Estimate impact and risk
↓
Route appropriately
↓
AI-assisted implementation where suitable
↓
Tests + evidence
↓
Human review
↓
Release
That can help founders and engineering leads see something more useful than a ticket count.
They can see:
- what type of work is arriving
- how much is reproducible
- which areas create repeated failures
- which changes carry more risk
- what is waiting for review
- which maintenance work can be automated safely
- where human engineering time is still being consumed
The result is a clearer software-maintenance operation.
14. A practical rollout checklist
Before building an AI-assisted maintenance workflow, I would work through this list.
Backlog structure
- Define the main types of incoming work.
- Separate bugs from features, support issues, documentation, and duplicates.
- Decide what should happen after each classification.
Evidence
- Define what counts as a reproduced bug.
- Store reproduction steps and failing tests.
- Keep change analysis attached to the work.
- Preserve test and review results.
Agent boundaries
- Give each agent one clear responsibility.
- Define its expected input.
- Define its expected output.
- Avoid overlapping ownership.
Security
- Run code execution in isolated environments.
- Give agents task-specific secrets.
- Restrict unnecessary network access.
- Treat external issue and PR content as untrusted.
Review
- Define routine, focused, and deep review tiers.
- Route higher-impact changes to deeper human review.
- Keep the final shipping decision clearly owned.
Operations
- Track workflow state.
- Surface blocked and failed runs.
- Measure where maintenance work spends time.
- Turn recurring failures into evaluation cases.
Vercel's AI SDK factory is a useful production example
Vercel recently published the architecture behind the software factory it runs for AI SDK.
Its system uses specialized agents for work including classification, bug reproduction, fixes, reviews, backports, documentation, feature analysis, and feature implementation. Agents execute inside isolated sandboxes, and a human on the AI SDK team still reviews and merges every change.
Vercel also says the factory now authors 25–35% of the pull requests merged each week, closed more than 75% of the issues closed during July, and helped reduce the open backlog after its late-June peak.
The architecture is more useful than copying the exact scale.
The system does not ask one AI developer to own everything.
It breaks maintenance into reviewable jobs, passes evidence forward, limits permissions, and keeps human accountability attached to shipping.
Build the workflow before you chase autonomous coding
AI can write code faster than most software teams can comfortably review it.
That makes the structure around coding increasingly valuable.
A useful AI-assisted maintenance system should make it easier to answer:
- What entered the backlog?
- Can we prove the problem?
- What should change?
- How risky is that change?
- What evidence supports it?
- Who is responsible for deciding whether it ships?
Get those boundaries right first.
Then let AI accelerate the work between them.
Top comments (0)