Coding with AI Assistants · Workflows That Work

Agentic Coding Tools: Plan, Act, Verify

Tools that edit files and run commands are powerful; permissions, plans and verification keep you in control.

Create a free account to save this page, take notes and track your progress. Sign in

The problem

You give an agentic coding tool a vague task in a repository with broad permissions: "Make the build pass." It deletes a flaky test, pins an old dependency version and adds # type: ignore in twelve places. The build passes; the problems are hidden, not fixed.

Real-world analogy

A new team member with admin rights and a vague goal will reach it the shortest way they can find. Clear goals, limited rights and a review of their plan lead to work you actually want.

The idea

Agentic coding tools (for example Claude Code, or agent modes in editors) read files, edit code and run commands in a loop. Use them deliberately:

  • Plan first: ask it to explore and propose a plan; approve or correct the plan before edits.
  • Define done precisely: "the failing test passes, no tests deleted or skipped, no new ignore comments".
  • Limit permissions: approve commands that change things; allow read-only commands freely; never give production credentials.
  • Verify: run tests, linters and type checks; read the diff before committing.
  • Work in a branch or worktree, commit in small steps, and keep changes reviewable.

The code

A verifier that checks an agent's changes against the definition of done, not just the build result:

Build passing is not the same as doneTry it yourself
diff = {
    "deleted_tests": ["tests/test_retry.py::test_backoff_jitter"],
    "added_lines": ["x = parse(data)  # type: ignore", "requests==2.19.0", "def retry(...):"],
    "build_passes": True,
}

RULES = [
    ("no deleted or skipped tests", lambda d: not d["deleted_tests"]),
    ("no new type: ignore", lambda d: not any("type: ignore" in l for l in d["added_lines"])),
    ("no dependency downgrades", lambda d: not any(l.startswith("requests==2.1") for l in d["added_lines"])),
    ("build passes", lambda d: d["build_passes"]),
]
for name, check in RULES:
    print(f"{'PASS' if check(diff) else 'FAIL'}  {name}")

Output

FAIL  no deleted or skipped tests
FAIL  no new type: ignore
FAIL  no dependency downgrades
PASS  build passes

Common mistakes

  • Vague goals ("make it work") that reward hiding problems.
  • Granting broad permissions or real credentials to save time.
  • Letting the agent make large, unreviewed changes in one go.
  • Judging success by "the build is green" without reading the diff.

Key points

  • Ask for a plan before edits, and define done precisely.
  • Limit permissions and keep secrets and production access away from agents.
  • Verify with tests, linters and a diff review, in a branch, in small steps.

Further reading: Claude Code overview · Claude Code security · Claude Code best practices

Practice questions

Why can 'make the build pass' be a dangerous instruction for a coding agent?

Answer

The agent may reach the goal by hiding problems (deleting tests, adding ignore comments, downgrading dependencies) rather than fixing them; done must be defined precisely.

How should permissions be set for agentic coding tools?

Answer

Allow read-only commands freely, require approval for commands that change things, and never give them production credentials or secrets.

What verification should follow an agent's changes?

Answer

Run tests, linters and type checks, and read the full diff in a branch before committing or merging.

Open in the app to mark lessons complete, take notes and track your progress.

Was this page helpful?