Index Ai Academy
Phase 11 · Professional practice · Working in a team

Working with AI coding tools

Give agents good context, keep tasks small, and check every change they make.

About 35 minutes · A free account opens the whole lesson

Create a free account Try part of it here

Already have an account? Sign in to open this lesson

Why it matters

AI coding tools now write a large share of new code. Used well, they make good engineers much faster. Used carelessly, they produce plausible code that's subtly wrong, insecure, or quietly deletes the test that would have caught it. You remain responsible for every line you merge. This lesson is about getting the speed without giving up that responsibility. Having built a coding agent in Phase 10, you know exactly what's going on inside these tools.

What you'll be able to do

  • Use a reliable workflow with coding agents: explore, plan, write tests, code, verify, commit
  • Give an agent the context it needs: instructions files, the right files and exact errors
  • Spot the common failure modes: invented packages, weakened tests, oversized changes
  • Write a checker that flags risky patterns in an AI-written patch

What it covers

  1. A workflow that works
  2. Give it context
  3. How AI-written code goes wrong
  4. Catching a weakened test
  5. Safety habits
  6. Is this change too big to review?
  7. Spotting new imports
  8. Green tests aren't enough

A workflow that works

Think of a very fast, very eager new hire. You'd ask them to look around first, tell you their plan, take one clear task at a time, and show you the result. A coding agent needs the same handling:

  1. Explore. Ask the agent to read the relevant code and explain it, without changing anything. Plan mode, which is read-only, is made for this.
  2. Plan. Ask for a plan for the change. Read it. Fix the plan before any code exists; it's much cheaper than fixing the code.
  3. Write or name the tests. A failing test is the clearest possible spec. "Make test_discount_applies_once pass without changing it" leaves little room to go wrong.
  4. Code in small steps. One focused change at a time, not "rebuild checkout".
  5. Verify. Run the tests and the app yourself. Read every line of the diff, as you would a teammate's pull request.
  6. Commit each working step, so you can always go back.
Try it now

You stay responsible for what ships, however it got written.

Give it context

A contractor who wasn't at the meeting only knows what's in your note. The agent knows only what's in its context window, as you saw when building one:

  • An instructions file (CLAUDE.md, AGENTS.md) with build and test commands, conventions and things to avoid. Every session loads it.
  • Point to files ("follow the pattern in orders/service.py") rather than hoping it finds them.
  • Paste exact errors and failing test output, not "it doesn't work".
  • Say what not to do: "don't change the public API", "no new dependencies".
  • Start fresh for a new task, so old context doesn't steer it wrong.
Quick check
Which request is most likely to get a correct change from a coding agent?
  1. "Fix the billing bugs."
  2. "Rewrite billing in a cleaner way."
  3. "Make refunds better."
  4. "test_refund_rounding fails with assert 10.0 == 9.99. Fix the rounding in billing/refunds.py so it passes; don't change the test."
Show the answer

"test_refund_rounding fails with assert 10.0 == 9.99. Fix the rounding in billing/refunds.py so it passes; don't change the test."

It names the failure, the file, the goal and a limit. The agent has a target it can check.

How AI-written code goes wrong

A confident tour guide can make up a street name and never blink. AI-written code fails in similar, predictable ways, so you can learn what to look for:

failurewhat it looks likedefense
invented APIsa function or option that doesn't existrun it; check the docs
invented packagespip install of a package that doesn't exist, or a malicious lookalikecheck every new dependency is real and maintained
weakened testsa test deleted, or an assert loosened, so the suite goes greenread test diffs closely; make tests read-only for the agent
oversized changes30 files touched for a 3-line fixask for smaller steps; reject unrelated edits
security holesSQL built from strings, missing permission checksreview with a security eye; run scanners
stale knowledgepatterns from an old version of a librarygive it the current docs or examples
confident wrong explanationsa clear story that isn't truecheck claims in the code

Invented packages deserve special mention. Attackers register package names that models tend to invent, so a careless install runs their code. It's like a fake shop opening under a name people often misremember. This is sometimes called slopsquatting.

Worked example

Catching a weakened test

An agent told to get the tests to pass may loosen them. When a diff makes a failing test pass by weakening the assertion instead of fixing the bug, reject it. A small check on the test diff finds assertions that vanished.

def weakened(diff):
    removed = [l for l in diff.splitlines() if l.startswith("-") and "assert" in l]
    added = [l for l in diff.splitlines() if l.startswith("+") and "assert" in l]
    return removed, added

diff = """--- a/tests/test_cart.py
+++ b/tests/test_cart.py
-    assert total(cart) == 42.50
-    assert len(cart) == 3
+    assert total(cart) > 0"""

removed, added = weakened(diff)
print("assertions removed:", len(removed), " added:", len(added))
print("look closely" if len(removed) > len(added) else "fine")
It runs right here in your browser, with nothing to install.

The suite is green, and the test now accepts almost anything. Comparing counts is only a rough signal, since assert x > 0 replaced an exact check here. So read test diffs line by line, because that's where a false green comes from.

Safety habits

  • No secrets in prompts. Use placeholders; keep keys in the environment.
  • Untrusted repositories can carry prompt injection in files and comments. Use plan mode or a sandbox, and approve commands.
  • Keep your skills sharp. Pilots who fly on autopilot still train by hand. Use AI to explain and to check, but write enough code yourself that you can judge its output. If you can't explain a change, don't merge it.

In plain words: the tool writes it, but you're the one who answers for it.

Worked example

Is this change too big to review?

A change you can't review is a change you can't trust. Set a limit, and split anything over it into smaller steps.

def review_size(files, limit_lines=400, limit_files=10):
    lines = sum(added + removed for added, removed in files.values())
    if lines > limit_lines or len(files) > limit_files:
        return f"too big: {lines} lines in {len(files)} files. Split it."
    return f"ok: {lines} lines in {len(files)} files"

print(review_size({"cart.py": (12, 3), "tests/test_cart.py": (20, 0)}))
print(review_size({f"module_{n}.py": (40, 25) for n in range(30)}))
In the lesson you can change this and run it.

Agents can write much faster than you can read. Small steps keep you in charge of what ships, and a limit like this is an easy rule to put in CI. The right limit depends on your team, so the numbers here are examples.

Quick check
You want a coding agent to explore a repository you downloaded from a stranger. What's the safest setup?
  1. Let it push changes directly
  2. Bypass mode, to save time
  3. Give it your production credentials
  4. Plan mode or a sandbox, approving any commands, because files can contain prompt injection
Show the answer

Plan mode or a sandbox, approving any commands, because files can contain prompt injection

Untrusted files can carry instructions for the model. Read-only exploration and approvals keep you in control.

Worked example

Spotting new imports

Compare the imports before and after a change, and ignore the standard library.

import ast, sys

def imports(source):
    names = set()
    for node in ast.walk(ast.parse(source)):
        if isinstance(node, ast.Import):
            names |= {a.name.split(".")[0] for a in node.names}
        elif isinstance(node, ast.ImportFrom) and node.level == 0:
            names.add(node.module.split(".")[0])
    return names

before = "import json\nimport requests\n"
after = "import json, hashlib\nimport requests\nfrom fastjsonx import loads\n"
new = imports(after) - imports(before)
print(sorted(new))
print(sorted(n for n in new if n not in sys.stdlib_module_names))
In the lesson you can change this and run it.

hashlib is in the standard library. fastjsonx isn't, so a reviewer should check it's a real, trusted package.

Quick check
Is it from the standard library? What does this print?
import sys

print("tomllib" in sys.stdlib_module_names, "numpy" in sys.stdlib_module_names)
  1. True False
  2. False False
  3. True True
  4. False True
Show the answer

True False

tomllib ships with Python 3.11 and later, so the first is True. numpy is a third-party package, so the second is False, and a new import of it deserves a check.

Green tests aren't enough

An agent's code can pass the tests and still be wrong in ways the tests don't check. It may have changed behavior elsewhere, added a slow query, or quietly weakened the test itself. You're responsible for what you merge, whoever typed it. So read the diff as if a new teammate wrote it, and ask the same questions you'd ask them.

Small asks get better results

"Rebuild the checkout" invites a huge change that you can't review and the agent can't get right in one go. "Add a test for empty carts, then make it pass" is something you can check in two minutes. Large work is fine, as long as it's broken into steps that each leave the code working. You stay in control, and a mistake only costs one small step.

Check every new package

Models sometimes suggest packages that don't exist, because the name sounds right. Attackers have started registering those names with malware inside, and this has a name: slopsquatting. Before you install anything new, check that it's real, that it's maintained, and that it's the one you meant.

You still have to understand it

If you can't explain what a change does, you can't review it, debug it or tell when it's wrong. The more code you generate, the more this skill matters. A fair test is to ask yourself whether you could fix this at 3 a.m. if it broke.

Practice what you just read

  • 3 code examples you can edit and run
  • a practice exercise that checks your code
  • a 5-question quiz
  • 5 review cards for spaced repetition

Create a free account

Try a quiz question

Why ask a coding agent for a plan before it writes code?

  1. Fixing a wrong plan is much cheaper than fixing wrong code
  2. Agents can't code without a plan, and will stop
  3. Plans make the code shorter, by cutting extra steps
  4. It uses fewer tokens in total, as a plan replaces code

Answer it at the end of the lesson to see if you're right, and why.

Before this lesson