Coding agents for controlled legacy-system changes
Use repository navigation, behavior-preservation tests, isolation and review to make bounded changes to established business systems.
A warehouse team wants a hold-reason field on a shipment. The visible change is small, but shipment eligibility also runs in an old service, a nightly job and a customer export. An agent can produce a convincing UI patch while missing the actual release rule. The useful job is to trace the business behavior, make a bounded change and produce evidence that the important behavior still holds.
Map the business path before editing#
Establish the baseline revision, working-tree status, build and test commands, deployment topology and owners. Preserve other people’s uncommitted work. Record known failures separately rather than silently calling an already-broken baseline healthy. Identify the data classes and credentials a local command might access before running the project’s setup scripts.
Start from the user-visible hold control and follow its request to the service, domain rule, storage, jobs and exports. Combine exact searches for field names and API routes with symbol references and dependency maps. Aider’s repository-map documentation illustrates compact symbol context that can guide later reads. It is a navigation aid, not proof that every runtime dependency has been found.
| Change-impact node | Question to answer | Evidence to inspect |
|---|---|---|
| Shipment UI | Who may view and edit the new reason? | Form, route and authorization checks |
| Shipment service | Which rule decides release eligibility? | Domain function and callers |
| Database | How are holds represented and migrated? | Schema, constraints and existing rows |
| Nightly job | Does background processing respect the hold? | Worker entry point and service identity |
| Customer export | Does the new field affect a contract or file format? | Serializer, versioning and consumer assumptions |
| Tests and operations | What detects a broken release rule? | Regression fixtures, monitoring and rollback procedure |
Static navigation can miss generated code, configuration-driven dispatch, reflection and external consumers. Follow those boundaries explicitly. Read the affected code instead of asking the model to infer its behavior from filenames. If two implementations of the release rule exist, determine whether that is intentional before unifying them as an incidental refactor.
Write the intended behavior and invariants#
The requested change is to expose a hold reason while preserving existing release rules. Define what happens when the reason is absent, who can change it, whether old clients may omit it and whether exports add a column or use a new version. Do not let the implementation silently convert a missing reason into a cleared hold.
| Condition | Before | Required after the illustrative change |
|---|---|---|
| Shipment has an active hold | Release blocked | Release remains blocked regardless of reason text |
| Authorized user edits the reason | No reason field exists | Reason can be edited under existing hold permissions |
| Unprivileged user sends the API request directly | Cannot modify a hold | Cannot modify the reason or bypass the hold |
| Old client omits the new field | Existing update behavior | Compatible behavior explicitly preserved |
| Nightly job processes held shipment | Release blocked | Same rule remains enforced |
| Export consumer reads old format | Known contract | Compatible format or an explicitly versioned migration |
Test the boundary the business depends on#
Characterization tests capture relevant existing behavior before the change. Use synthetic shipments with an active hold, no hold, missing reason and restricted users. Exercise the domain rule directly, then its API and job callers. A screenshot that shows a new input does not prove the background process still blocks shipment release.
Preserve intended behavior, not every accidental defect. If inspection reveals a security bug, separate its correction from the requested feature and document the behavior change. Avoid broad refactors that make the reviewer evaluate new architecture and a business requirement at the same time. A small coherent patch is easier to verify and easier to recover from.
Use deterministic clocks for expiry-sensitive rules and isolated fixtures for mutable state. Check the actual negative case: a direct unauthorized API call should fail even when the UI hides the button. Verify that omitted, empty and explicitly cleared fields behave according to the contract. Include export and nightly-job integration tests where those paths own independent logic.
Separate edit isolation from execution security#
A Git branch names a line of development; a linked worktree supplies a separate working directory and index for review. Git’s manual documents shared repository relationships. Neither a branch nor a worktree provides security isolation for commands, network access, credentials or a shared database. Running a migration from a separate checkout can still affect the same production database if its environment points there.
Use an actual restricted execution environment with non-production credentials, an isolated test database, bounded network access and controlled commands. Inspect setup and test scripts before granting broad execution. Avoid copying production secrets into a convenient local environment. Keep generated logs and fixtures free of real customer data unless an authorized minimized dataset is specifically required.
Repository comments, issue descriptions and dependency output can contain instructions aimed at an agent. Treat them as project material with provenance, not authorization to expose credentials, disable tests or deploy. The trusted task and execution policy remain separate from text the agent discovers. GitHub’s responsible-use guidance reinforces the need to validate generated changes in their actual context; product-specific protections do not automatically apply to every tool.
Make a compatible, reviewable patch#
An additive optional field may be a sensible first step if old application versions continue running during deployment. Update readers and writers deliberately, constrain maximum length and normalize only what the contract permits. Keep authorization on the service boundary. Do not add a client-only check and assume the server has become safe.
For data changes, consider old and new code running simultaneously. A migration may add the field, deploy compatible code, backfill with explicit limits, then switch behavior after validation. Destructive cleanup belongs in a separate reviewed step with backup and restore evidence. Rolling back application code does not necessarily reverse a data migration or an already-sent export.
For a larger legacy replacement, Fowler’s Strangler Fig pattern suggests introducing a boundary and replacing behavior incrementally. An agent can help inspect and implement a bounded slice, but the migration pattern predates AI and still requires coexistence, routing and operational planning. Rewriting the whole service is not the default answer to a new field.
Keep long tasks tied to observed progress#
Long-running agent work benefits from a concise progress record: requirement, touched boundaries, completed checks, known failures and remaining decisions. Anthropic’s harness account describes progress artifacts and incremental checks in its coding experiment. Apply the idea by recording verifiable facts and command outcomes, not just a model’s confidence that the task is almost complete.
When multiple workers help, assign separate responsibilities such as tracing the export contract, reviewing authorization and inspecting regression coverage. Avoid concurrent edits to the same migration or shared fixture unless coordination is explicit. Independent review requires checking evidence and assumptions, not merely having several agents agree with the first summary.
Review the diff and the behavior together#
Run the checks appropriate to the change: types and static analysis, focused regression tests, integration contracts and the actual UI, job and export flow. Retain the commands, results and tested revision. Passing tests establishes evidence for those tests; it does not prove the absence of every defect. SWE-bench similarly evaluates issue-solving in defined repositories and environments, not every enterprise deployment risk.
Inspect the diff for unrelated files, broadened permissions, sensitive logging, dependency additions, generated artifacts and altered tests. A test removed or weakened by the agent is not a passing check. Confirm that failure messages and empty states remain useful. Review both the normal path and the direct API path that bypasses the interface.
| Release evidence for the hold-reason change | Question a reviewer should resolve |
|---|---|
| Requirement and impact map | Are UI, service, job and export boundaries accounted for? |
| Authorization evidence | Can only the intended actor read or edit the reason? |
| Regression evidence | Does every relevant path still block active holds? |
| Migration evidence | Can old and new versions coexist without losing data? |
| Operational evidence | What is monitored, who can stop rollout and how is recovery performed? |
| Remaining uncertainty | Which assumptions or untested external consumers need explicit ownership? |
Release through the normal ownership process#
Use the repository’s review, CI and authorized deployment path. A model should not approve its own change or infer permission to merge because tests passed. A feature flag or staged rollout can limit exposure when it fits the architecture. Monitor release-rule violations, job failures, export errors and user correction reports rather than only application uptime.
Retain a known-good code configuration and a plan for data compatibility. If a new export format has already reached customers, recovery may require a forward fix or coordinated consumer change. NIST SSDF provides a secure-development framework across the lifecycle; citing it does not certify a generated patch. The team still owns concrete controls, evidence and vulnerability response.
Measure accepted changes, not impressive activity#
Track accepted-change lead time, reviewer rework, escaped defects, unintended edits, regression failures and deployment recovery. Navigation and context cost are useful diagnostics, but a lower token count is not automatically better if the agent missed the nightly job. Compare with a meaningful baseline and retain the scope of the comparison instead of copying a vendor’s percentage claim.