A step-by-step look at what actually goes into an agent that can plan, write, run and fix its own code — and the guardrails that keep it from going rogue.

If you’ve used an AI code assistant, you’ve seen it suggest a line of code while you type. An autonomous coding agent is a different animal: instead of suggesting, it acts — it can open a file, run a test, read the error, and try again, on its own, in a loop, until the job is done. This guide walks through what’s actually happening under the hood, in plain language, with a small example you can reason about even if you’ve never built one.

What makes a coding agent different from autocomplete

Autocomplete predicts the next few words. An agent is given a goal — “make this test pass” — and decides, step by step, what to do next: read a file, edit a file, run a command, check the result.

The key difference is the loop. An agent doesn’t just produce one answer; it observes what happened after each action and adjusts its next move accordingly, the same way a person debugging code would.

The four things every agent needs

Every coding agent, no matter how fancy the model behind it, is built from the same four ingredients:

  • A goal — a clear description of what “done” looks like, e.g. “all tests pass.”
  • Tools — ways to act on the world: read a file, write a file, run a shell command, search the web.
  • Memory — a running record of what it has tried and what happened, so it doesn’t repeat mistakes.
  • A stopping condition — a rule for when to stop: success, a turn limit, or a human steps in.

Miss any one of these and the agent either can’t act, forgets what it just did, or never stops.

A simple example: fixing a failing test

Say you tell an agent: “Fix the failing test in math_utils.test.js.” A typical loop looks like this:

  • Run the test suite and read the failure message.
  • Open the file the error points to.
  • Propose a small code change.
  • Apply the change and re-run the tests.
  • If tests pass, stop. If not, go back to step 1 with the new error message.
while not done and attempts < MAX_ATTEMPTS:
    result = run_tests()
    if result.passed:
        done = True
    else:
        fix = propose_fix(result.error, read_file(result.file))
        write_file(result.file, fix)
    attempts += 1

This loop is the whole trick. Everything else — which model, which tools, how it’s prompted — is detail on top of this simple shape.

Guardrails: how to keep it from going rogue

An agent that can run shell commands can also delete files, install packages, or push to production if you let it. Sensible limits matter more than a clever prompt:

  • Run it in a sandbox or container, not your main machine.
  • Give it a small, explicit list of allowed commands instead of “run anything.”
  • Cap the number of attempts, so it can’t loop forever burning time and money.
  • Require a human review before anything touches production or a real database.

None of this is exotic — it’s the same discipline you’d want from a very fast, very literal junior engineer who never gets tired.