Working with AI coding tools
Give agents good context, keep tasks small, and check every change they make.
Create a free account Try part of it here
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
- A workflow that works
- Give it context
- How AI-written code goes wrong
- Catching a weakened test
- Safety habits
- Is this change too big to review?
- Spotting new imports
- 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:
- 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.
- 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.
- Write or name the tests. A failing test is the clearest possible spec. "Make
test_discount_applies_oncepass without changing it" leaves little room to go wrong. - Code in small steps. One focused change at a time, not "rebuild checkout".
- Verify. Run the tests and the app yourself. Read every line of the diff, as you would a teammate's pull request.
- Commit each working step, so you can always go back.
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.
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:
| failure | what it looks like | defense |
|---|---|---|
| invented APIs | a function or option that doesn't exist | run it; check the docs |
| invented packages | pip install of a package that doesn't exist, or a malicious lookalike | check every new dependency is real and maintained |
| weakened tests | a test deleted, or an assert loosened, so the suite goes green | read test diffs closely; make tests read-only for the agent |
| oversized changes | 30 files touched for a 3-line fix | ask for smaller steps; reject unrelated edits |
| security holes | SQL built from strings, missing permission checks | review with a security eye; run scanners |
| stale knowledge | patterns from an old version of a library | give it the current docs or examples |
| confident wrong explanations | a clear story that isn't true | check 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.
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")
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.
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)}))
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.
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.
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))
hashlib is in the standard library. fastjsonx isn't, so a reviewer should check it's a real, trusted package.
import sys print("tomllib" in sys.stdlib_module_names, "numpy" in sys.stdlib_module_names)
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
Try a quiz question
Why ask a coding agent for a plan before it writes code?