Engineering practice

How I Use Coding Agents Without Giving Up Architectural Control

How I use repository law, bounded tasks, executable checks, evidence, and review to let coding agents work quickly without making architecture by accident.

François Guéguen 13 min read

A coding agent can produce a correct local change and still make the project worse. It can add a second abstraction for an existing job, turn an experiment into an accidental product contract, or make tests pass while weakening the boundary the tests were meant to protect.

My short answer: I let coding agents explore, implement, test, document, and review. I do not let a chat thread—or an agent’s most convenient implementation—quietly decide what the product is. Durable authority lives in the repository, tasks stay bounded, and important rules become executable checks.

This is a description of my working method, not a claim that every repository needs the same documents. The useful principle is smaller: separate authority from execution, then make both legible enough that a person or agent can verify the result.

The problem is not only incorrect code

In my projects, prompt quality is rarely the only reason architecture drifts. The more persistent problem is that the repository does not say which facts are authoritative, which decisions are still open, or which boundaries must survive a locally reasonable change.

When project context exists only in a developer’s memory or an old conversation, I have seen agents fill gaps from nearby code. The result may look plausible while reproducing an obsolete pattern, inferring a public API from an experiment, or solving a narrow task by creating a second system beside the first.

OpenAI describes a related lesson from its agent-first engineering work: repository-local knowledge, strict architectural boundaries, and mechanical enforcement made larger agent-driven changes possible. The important qualification is that this depended on substantial repository structure and tooling; it is not a property of the model alone.

1. Put authority in the repository

I use a small hierarchy of documents with deliberately different jobs. The filenames can change, but their authority should not overlap.

Example: a product contract owns the three allowed support queues; the current task owns whether this week’s work adds timeout handling. The task may not invent a fourth queue.

Repository authority layers for coding-agent work
Artifact Owns Does not own
Invariants or product contract Non-negotiable product, architecture, security, and evidence boundaries Temporary implementation sequencing
Decision records Accepted choices, rationale, consequences, and reversal path Current progress
Specifications and traceability Current intended behavior and the evidence that verifies it Unapproved future ideas
Roadmap and current task The active slice, blockers, and exit criteria Durable architectural rationale
Risk, gate, and deferral ledgers What must stop, wait, or be reconsidered Implemented behavior

The root AGENTS.md is the entry point, not a duplicate encyclopedia. It tells the agent what to read, which source wins when documents disagree, which commands establish completion, and where specialized rules live. Codex supports layered AGENTS.md instructions from global through repository and nested directory scopes, so a narrow module can add rules without making every task carry them.

2. Give the agent one bounded outcome

“Improve the architecture” is not an executable task. I define a slice with five fields:

Example: “Route model timeouts to the existing human-review queue” is bounded; “make support routing more reliable” leaves the agent to define the product change.

  1. Outcome: one observable behavior, decision, or evidence result.
  2. Authority: the requirement IDs, contract values, and accepted decisions that govern it.
  3. Boundary: what the change must not add, rename, publish, or weaken.
  4. Evidence: the tests, fixtures, browser checks, or review needed to accept it.
  5. Reversal: how to remove or supersede the change without guessing.

This does not mean dictating every function before work starts. The agent can choose a local implementation inside the boundary. The control comes from making the outcome and the forbidden shortcuts explicit.

Worked example: make support-ticket routing fail safely

This is a small hypothetical example, not client work. Imagine an application with a routeTicket service. It asks a model to route an incoming support ticket to billing, product, or human-review. The public return type and the human-review queue already exist, but a model timeout currently causes the request to fail.

A bounded task for a coding agent could be:

  • Outcome: model timeouts and invalid model output go to human-review.
  • Authority: the existing TicketRoute type and support-routing requirements.
  • Boundary: do not change the public API, authorization, or storage behavior.
  • Evidence: test success, timeout, invalid output, and unrelated failures.
  • Reversal: remove the adapter fallback without migrating stored data.

A plausible first patch catches every exception in the HTTP controller and returns human-review. The timeout test passes, but the patch also converts authorization failures and database errors into successful routing responses. It duplicates model-specific behavior at the transport layer and makes operational failures harder to see.

The corrected change keeps the fallback inside the model adapter and handles only the failures the task authorized. In this hypothetical contract, confidence: null is already valid whenever the queue is human-review. The following is pseudocode; its predicates represent narrow, typed error classifications rather than catch-all string matching:

try {
  return await classifier.classify(ticket);
} catch (error) {
  if (isModelTimeout(error) || isInvalidModelOutput(error)) {
    return { queue: "human-review", confidence: null };
  }
  throw error;
}

In a real implementation, those predicates need focused positive and negative tests, and the fallback should emit a bounded operational event without recording ticket content. The public service contract stays unchanged. Authorization and storage errors still fail visibly. Review can now answer a specific question: did this patch contain model failure handling without quietly redefining the rest of the system?

If the agent discovers that the existing return type cannot represent human review, that is no longer a local implementation detail. The task pauses so the owner can decide whether to change the public contract. If the same fallback procedure recurs across several projects, its review checklist may become a skill; the product-specific queue names and error policy stay in the repository.

3. Record durable decisions before code depends on them

A coding agent can turn an unexamined assumption into a working implementation before anyone notices that a decision was made. That speed is useful only when the assumption is already authorized.

Example: renaming a private helper needs no decision record; adding a new public queue value does because callers, stored data, and operations may depend on it.

If a task needs a new public API, dependency direction, data owner, security boundary, or scientific interpretation, I stop the implementation path and record the decision first. A decision record should state the context, the selected option, consequences, validation, and rollback. The Architecture Decision Records community maintains background, examples, templates, and tools if a repository does not already have a decision format. The code then implements a visible choice instead of making the choice by accident.

Plans are different. A plan can change as evidence arrives. Treating a plan as project law makes exploration expensive; treating an architectural decision as a disposable plan makes the architecture unstable.

4. Turn important prose into executable checks

Documentation makes a rule discoverable. A check makes drift expensive and visible.

Example: if provider SDK types must stay behind an adapter, add a dependency or export check that fails when one reaches the public service boundary.

I add ordinary unit and integration tests, but architectural checks cover different failures: forbidden dependency edges, duplicate public routes, mismatched canonical values, unapproved exports, stale generated documentation, unsupported claims, or examples that are no longer produced by tested source.

For model-backed behavior, a handful of representative cases is more useful than one polished demo. OpenAI’s evaluation guide is a practical provider-specific introduction to task-specific tests, representative data, metrics, comparison, and continuous evaluation. Lucent Lab’s AI demo-to-production checklist places those evaluations beside failure handling, permissions, cost, rollout, and operational ownership.

The most useful failure message explains the boundary and points back to its authority. “Import not allowed” is weaker than “UI may depend on the service boundary, not the repository implementation; see ADR 0012.” The check becomes contextual feedback for both humans and agents.

Prose explains the rule. Tests enforce the behavior. Traceability shows why both exist.

5. Keep evidence separate from narrative

Because a fluent summary is not evidence, I keep raw observations, generated artifacts, and decision status separate from the prose that explains them.

Example: commit the timeout fixture and test result separately from the note claiming that fallback behavior works, so a reviewer can reproduce the conclusion.

For a product feature, that may mean a committed fixture, a browser trace, and a check that reconstructs the example. For research or performance work, it may require a frozen environment, raw measurements, an immutable manifest, and a result ledger that cannot be rewritten when the outcome is inconvenient.

This separation also improves review. A reviewer can ask whether the evidence supports the conclusion instead of reviewing only the fluency of the conclusion.

6. Use a fresh challenge pass, not a ritual

I ask the implementing agent to inspect its own diff and run the relevant checks. For consequential work, I also use a fresh review pass focused on a named risk: architecture, security, type design, complexity, scientific validity, or public claims.

Example: ask the reviewer “Can this catch block hide authorization or storage failures?” rather than “Review this thoroughly.”

A fresh pass can expose assumptions that the implementation pass did not question, but it is not independent authority merely because it uses a different model or thread. Review findings still need evidence and disposition. A human remains responsible for product scope, consequential trade-offs, external publication, and what ultimately ships.

Changes to invariants, public contracts, security boundaries, enforcement checks, or review gates require explicit owner review. An agent should not weaken the evidence used to approve its own work.

More reviewers are not automatically better. A focused review question with the correct contract and diff is usually more valuable than several general requests to “be thorough.” Repository controls can make ownership mechanical too: GitHub’s CODEOWNERS documentation shows how paths can identify responsible people or teams and participate in required review rules.

7. Convert repeated work into skills

Repository instructions and agent skills solve different problems. AGENTS.md says how work in this repository must be governed. A Codex skill packages a reusable task-specific workflow, with a required SKILL.md and optional scripts, references, and assets.

Example: package the repeated claim-verification procedure as a skill, but keep this project’s approved claims, sources, and publication rules in the repository.

I use a skill when a procedure repeats across tasks or repositories: checking a technical claim, performing a security scan, reviewing asymptotic complexity, creating a handoff, or producing a document with a fixed validation loop. I keep project-specific law in the project.

Good skills have a narrow trigger, explicit non-goals, and progressive disclosure. The short name and description help the agent decide when the skill applies; the full instructions load only when selected. Scripts are useful when a deterministic operation is safer than asking the model to reconstruct it each time.

A skill is still code-adjacent supply chain. I review its instructions and scripts, scope its permissions, version material changes, and test representative success and refusal cases before trusting it with consequential work.

Two projects, two versions of the same control system

Fadeno and SeaWeave are private, first-party side projects—not client work or independent validation. I include only their governance shape here; the examples do not make their repositories or unpublished evidence publicly inspectable.

Fadeno: product behavior, compatibility, and release evidence

Fadeno is a web-native TypeScript and JSX framework. Its repository separates project invariants, accepted architectural decisions, current specifications, feature traceability, roadmap state, experiments, release intent, and executable evidence.

The separation matters because framework work creates accidental contracts easily. An internal analyzer, private protocol, experiment, or example must not become a supported public surface merely because an agent finds it convenient. Public examples are generated from or executed against tested source, package changes carry explicit version intent, and the final gate binds validation to an unchanged commit.

SeaWeave: scientific claims and a gate that can stop the product

SeaWeave is a research-first browser laboratory for finite-depth linear ocean waves. Here architectural control is not mainly about package boundaries. It is about preventing an attractive visualization or promising experiment from becoming a scientific claim.

The repository separates the mathematical model contract, preregistered validation criteria, implementation, immutable evidence, scientific result records, and presentation. A failed feasibility result remains recorded. Dependent product work stays frozen until a new, separately versioned formulation passes its own gate and is explicitly selected.

That is the same control principle under a stricter evidence regime: an agent may implement the experiment and report the result, but it may not change the meaning of success after seeing the data.

What I do when the agent gets something wrong

I first fix the immediate change. Then I ask which layer failed:

  • Missing context: add or clarify the authoritative source.
  • Ambiguous authority: resolve the conflict instead of adding another explanation.
  • Repeated procedure: create or improve a skill.
  • Drift-prone rule: add an executable check.
  • Oversized task: reduce the slice and restore a decision point.
  • Missing judgment: keep the decision with the human owner.

Repeatedly correcting the generated code without improving the environment wastes the most valuable part of the failure: evidence about what the system could not see or enforce.

Do not copy the paperwork; copy the separation of concerns

A small application does not need a scientific ledger and dozens of decision records. It may need only a concise AGENTS.md, one architecture note, a short current plan, and a reliable check command.

Add structure when it owns a real conflict, risk, or repeated failure. Remove it when two artifacts own the same fact. The objective is not maximum documentation. It is a repository where the next contributor can find the current truth, identify who may change it, and verify the result.

Frequently asked questions

Can a coding agent make architecture decisions?

It can research options, expose consequences, draft a decision record, and implement an accepted choice. I do not treat the generated implementation itself as approval of a durable product or architecture decision.

Do skills replace project instructions?

No. Skills package reusable workflows. Project instructions establish repository-specific authority, reading order, boundaries, and completion commands. A skill should respect that local authority rather than override it.

Does every change need an ADR?

No. Use a durable record when the choice changes a public contract, consequential boundary, or long-lived rationale. Local implementation decisions belong in code, tests, or a short task note.

Is agent review enough to ship?

No universal rule fits every risk level. Automated and agent review can improve coverage, but the project owner remains accountable for scope, evidence, permissions, and release. High-impact changes need proportionally stronger verification and human judgment.

Practical resources and sources

All articles