Introduction

Imagine asking an agent to add a new field to a subscription renewal screen. It finds the controller, adds the field, and updates a test. The test passes. In production, the renewal email still uses the old value because the email is built by a queued job the agent never found.

The problem was not that the agent could not write PHP. It did not have a reliable way to discover the full change path or check the result.

This is where Agent Experience (AX) engineering comes in. We already think about developer experience when we name things, document workflows, and make tests easy to run. AX applies that same care to the environment in which a software agent works. The goal is not to make a repository look nice to a model. The goal is to make the right work discoverable, the unsafe work difficult, and the result verifiable.

In this article, we will use a Laravel subscription application to see what good AX looks like, how to improve it, and where the agent's responsibility ends.

What Is Agent Experience Engineering?

An agent can inspect files, run tools, edit code, and report its findings. Unlike a developer who has spent months on a project, it usually begins each task with little local history. If important knowledge lives only in someone's head or in a chat thread, the agent has to guess.

AX engineering is the practice of designing a working environment that lets agents find the right context, act within clear boundaries, and get useful feedback about their changes. It includes repository structure, concise instructions, tool access, tests, and review rules. It is not a new framework or a guarantee that an agent will be correct.

There is a useful distinction from the agentic engineering workflow I wrote about before. That workflow describes how I plan, implement, and review work with agents. AX asks whether the project itself supports that workflow. A good plan is less useful if nobody can tell which job sends the renewal email or how to run the relevant tests.

Think about the information an agent needs for our renewal change:

Change request
    │
    ▼
Find the owning module ──▶ Find the HTTP, database, and queue paths
    │                                  │
    ▼                                  ▼
Read local rules               Identify behavior that must not change
    │                                  │
    └──────────────┬───────────────────┘
                   ▼
          Make a bounded change
                   │
                   ▼
         Run focused tests and review the diff
                   │
                   ▼
          Human reviews and decides to ship

Each step needs a reliable answer. More instructions will not help if the source of truth is wrong, and a passing test will not help if it misses the queued email.

Make the Change Path Discoverable

Start with the question an agent will ask first: where does this behavior live?

Suppose our application has a Billing module that owns subscriptions and renewal emails. We could give the agent three small files under .memory/ instead of one long document. Each file answers a different question:

.memory/
├── PROJECT_STRUCTURE.md  Where does the behavior live?
├── STANDARDS.md          How do we work in this project?
└── MEMORY.md             What durable decisions and pitfalls should we remember?

For our subscription change, the contents might be as simple as these examples:

<!-- .memory/PROJECT_STRUCTURE.md -->
# Project Structure

Billing owns subscriptions, payment attempts, and renewal notifications.
For renewal changes, trace the HTTP entry point, renewal action, queued
notification job, and email template. Reporting reads subscription state
but does not change it.
<!-- .memory/STANDARDS.md -->
# Standards

Keep Billing changes within the owning module and its public interfaces.
Run the focused Billing feature tests and check queued notification behavior
before requesting review.
<!-- .memory/MEMORY.md -->
# Project Memory

Renewal emails are built by a queued job, not by the HTTP request. A test
that checks only the response will miss changes to the email content.

PROJECT_STRUCTURE.md maps responsibilities and the change path. STANDARDS.md records how the team expects changes to be made and checked. MEMORY.md holds the durable detail that is easy to miss when starting a new task. This example assumes the queue behavior was confirmed in the code. The files do not replace reading the implementation, and we should correct them when the code changes.

Names help too. RetrySubscriptionPayment tells a reader more than ProcessService. Clear module ownership helps an agent trace the code, but it also helps the next developer. AX is often ordinary maintainability work viewed through a new constraint: the reader may have no memory of yesterday's conversation.

Do not dump the whole architecture into these files. Long documents make it harder to find the few facts that matter to a task, and they grow stale. Keep each file short enough to read before work. Record current structure and lasting decisions, not a running log of every task. The code and tests remain the source of truth when a note is out of date.

Give the Agent a Contract, Not a Pep Talk

An agent needs to know what it may change and what requires a human decision. An AGENTS.md file is one way to provide repository instructions when your coding tool reads it. The format is less important than whether the instructions are short, accurate, and usable.

AGENTS.md can point to the three files and tell the agent when to check them. For our Laravel application, a small example could be:

# Working in this repository

Before changing code, read `.memory/PROJECT_STRUCTURE.md`,
  `.memory/STANDARDS.md`, and `.memory/MEMORY.md`.
Check those notes against the code. If they disagree, follow the code and
  correct the stale note.
Keep edits within the requested behavior and its tests.
Run the focused Billing tests, then report what ran and what did not.
Before finishing, update the relevant memory file if you learned a lasting
  fact or changed the project's structure or standards. Skip temporary notes.
Do not modify `.env`, production credentials, or deployment settings.
Ask before migrations that change existing data or external payment behavior.
Never use customer data in fixtures, prompts, or logs.

Now the agent has an entry point instead of having to discover three files by chance. It reads the map, the rules, and the durable notes before starting. At the end, it updates only what changed. If the renewal job moves to a different module, the structure file should change. If a test revealed an important queue pitfall, that belongs in memory. A note saying "I updated the controller today" does not help the next task.

These rules also identify scope, verification, and decisions the agent cannot make alone. They are more useful than "always write perfect code." The last rule matters when an agent has access to logs or a database. Tool access should be limited to what the task needs. A read only view of local test data is not the same thing as access to production customer records.

Instructions are not a security boundary. A malicious comment in a source file or an external web page can tell an agent to ignore its task. Treat retrieved text as data, not authority. Permissions on files, services, and deployment credentials still need to be enforced outside the prompt. For high impact operations, require an explicit human approval step.

Build a Feedback Loop That Catches the Real Failure

An agent that can run php artisan test has a feedback loop, but a green suite only proves what the suite covers. In our renewal example, a controller test that asserts a successful response does not prove the queued job uses the new field.

Give the task an observable success condition. For example:

When a renewal succeeds, the saved subscription and the queued email both contain the updated renewal date. When payment fails, neither claims the renewal succeeded.

Then test the relevant behavior at its boundaries. One test can check the database state after a successful renewal. Another can check what the queued job sends. A failure case can prove that no success email is queued on a declined payment. The agent still needs to inspect the existing application flow to choose the right test level.

The loop should look like this:

  1. Write down the expected behavior and the failure case.
  2. Locate the owning code and tests before editing.
  3. Change the smallest part of the flow that satisfies the request.
  4. Run focused tests and static checks used by the project.
  5. Review the diff for unrelated edits, missing paths, and unsafe data handling.
  6. Report the checks that ran and any gaps that remain.

It is tempting to make the test suite run faster by skipping slow integration tests. That may be reasonable for the first feedback cycle. It is not reasonable to report the change as verified if the queue behavior was never checked. Fast feedback and complete evidence are different things.

Improve AX Without Building an Agent Platform

You do not need to add a new service to start. Pick one change that agents regularly get wrong and ask where the missing information should live.

If an agent keeps editing Reporting for a Billing feature, document module ownership and enforce the dependency in code or tests where possible. If it cannot find the right command, add a short task oriented command list. If it repeatedly misses queued behavior, add a focused test. If it asks for production credentials to reproduce a bug, provide a safe local fixture instead.

Measure the result with real tasks, not with the size of your instruction file. A few useful signals are the number of corrections needed before review, the time spent locating the relevant code, and the failures found after a change was called complete. Review a small sample of tasks before and after each improvement. The point is to identify repeated friction, not to assign a score to an agent.

There is also a limit to what AX can solve. Clear context and good tests reduce avoidable mistakes, but they do not decide whether a customer should receive a second charge or whether a schema change is safe to deploy. Those are product and engineering decisions. The agent can collect evidence and suggest options. A person still owns the decision and the release.

Conclusion

AX engineering makes a repository easier for an agent to work in without giving the agent more authority than it needs. Start with a change path that can be discovered, a small set of reliable rules, and tests that reflect the behavior you care about. Then watch where agents still get stuck and improve that part of the environment.

The same changes usually make the project easier for your team too. That is a good sign. We are not optimizing for an agent to produce more code. We are making it easier to produce a change we can understand, check, and trust.