Skip to content

Chapter 8 of 206 min readBy Deepak GuptaPublished

Claude Code for Software Engineers

The gap between engineers who get several times more output and engineers who get marginal gains is process, not prompts. Same tool, different workflow.

The five-step loop

  1. Spec. Describe the outcome in plain language, with constraints and a definition of done.
  2. Plan. Have Claude explore and propose an approach in plan mode. Push back before any code exists.
  3. Implement. Execute the plan in small steps, verifying and committing between them.
  4. Verify. Tests, typecheck, lint, and a review pass by fresh eyes.
  5. Refine. Once it works, ask for the cleaner version.

Skipping step 2 is the most common and most expensive shortcut. A wrong plan costs a minute to fix. The same mistake found two hundred lines into the implementation costs an hour.

Writing specs that work

A weak request:

Add rate limiting to the API.

A strong one:

Add per-user rate limiting to all routes under /api/v2. Limit to 100 requests per minute per API key, 20 per minute for unauthenticated requests by IP. Return 429 with a Retry-After header. Use the existing Redis client in src/cache. Do not limit /api/v2/health. Done means: unit tests for the limiter, an integration test proving a 429 on request 101, and no changes to existing route signatures.

The strong version answers the questions Claude would otherwise guess at: scope, numbers, behavior on failure, which existing code to reuse, exclusions, and what proof of completion looks like.

Working the plan

Ask for the plan explicitly: "Explore the relevant code and propose a plan. Do not edit anything yet." Or press Shift+Tab into plan mode.

Then read it like a reviewer. Look for:

  • Files it plans to touch that it should not
  • New dependencies where existing code would do
  • Missing steps for migrations, configuration, or docs
  • Tests that check implementation details instead of behavior

For anything larger than an afternoon, ask Claude to write the plan to docs/plans/<feature>.md. That file becomes the contract and the handoff document from Chapter 3.

Implementing in steps

Tell Claude to work through the plan one step at a time, run the relevant tests after each, and commit when a step passes. Small commits make rewinds cheap and reviews readable. If a step fails twice, stop and rethink the step rather than letting Claude thrash.

When you need to steer mid-task, press Esc and say what to change. Interrupting early is cheaper than undoing late.

Give it real evidence

Claude reasons best from the actual artifact, not your summary of it.

  • Paste the real stack trace, not "it crashes on login."
  • Pipe logs in: cat build.log | claude -p "why did this build fail?"
  • Point at the failing test by path and name.
  • For UI work, give a screenshot or mockup.

Test-first with Claude

Test-driven development works unusually well with an agent because the tests give it an objective target.

  1. Ask Claude to write tests for the spec and confirm they fail. Tell it explicitly not to write implementation yet.
  2. Review the tests. This is where your judgment matters most.
  3. Commit the tests.
  4. Ask Claude to implement until the tests pass, without modifying the tests.
  5. Have a reviewer subagent check that the implementation is general, not tailored to the test cases.

Step 5 exists because agents under pressure to make tests pass will sometimes special-case the test inputs. Fresh eyes catch it.

The screenshot loop for frontend

Give Claude a design mockup. Let it implement. Have it capture a screenshot of the result through a browser MCP server, compare against the mock, and list the differences. Then fix and repeat. The first version is usually decent; two or three iterations usually get you to good.

The refine pass

Once a feature works, ask: "Knowing everything you learned building this, what would the cleaner implementation look like?" The first working version carries the scars of exploration. The second pass often deletes a third of the code.

Legacy code

Claude is excellent at explaining unfamiliar code. Start there: "Explain how a request flows from the router to the database in this service." Then build a safety net before changing anything: ask for characterization tests that capture current behavior, including its bugs. Refactor only once those tests pass on the original code.

Large migrations

For mechanical changes across hundreds of files, use headless mode in a loop rather than one giant session.

for f in $(cat files-to-migrate.txt); do
  claude -p "Migrate $f from the v1 logger to the v2 logger per docs/logger-migration.md. Run its tests." \
    --allowedTools "Read,Edit,Bash(npm test:*)"
done

Each file gets a fresh context, a narrow tool allowlist, and the same written instructions. Chapter 12 covers parallelizing this.

Debugging production issues

With error tracking and logs connected through MCP, start with: "Pull the top error for the checkout service from the last 24 hours, find the code path, and propose a root cause with evidence." Insist on evidence. Ask Claude to reproduce the bug in a test before fixing it. A fix without a failing test first is a guess.

Warning

Pitfall: Accepting "I fixed it" without proof. Ask Claude to show the test that failed before and passes now, or the command output that demonstrates the fix.

Tip

What to do this week

  • Write your next task as a full spec with a definition of done.
  • Use plan mode and change at least one step before approving.
  • Try the test-first loop on one small feature.

Key takeaways

  • Process beats prompts: spec, plan, implement, verify, refine.
  • A strong spec states scope, numbers, failure behavior, code to reuse, exclusions, and what done means.
  • Test-first gives the agent an objective target; review the tests before any implementation.
  • Never accept "I fixed it" without a failing test that now passes.