L2 · 07 · Agentic Coding Workflows in Depth

Level 1 introduced agents. This page is about using them as a senior engineer would on real codebases: spec-driven development, test-first loops, large refactors and migrations, parallel agents, and reviewing at speed without lowering the bar.

Level 255 minTracks 5, 7Last verified 2026-10

By the end of this page you can

The senior workflow

1. Specyou + AI 2. Planagent proposes 3. Testsfail first 4. Implementsmall steps 5. Verifybuild, lint, tests 6. Reviewhuman + AI loop until green human gates: approve spec & plan human gate: merge
Humans own the gates (spec, plan, merge). Agents do the loop in between. Most failures trace back to a weak spec or skipped plan review.

Spec-driven development

A spec is a short Markdown file that states what to build and how you'll know it's right. It is the single best lever for agent quality, and it doubles as documentation and PR description. Draft it with the AI (ask it to interview you), then edit it yourself.

# Spec: Pickup reminder notifications

## Goal
Reduce uncollected pickup orders. Customers get a reminder 48h before their hold expires.

## Behaviour
1. A daily job finds orders with status READY_FOR_PICKUP whose hold expires in 44-52h.
2. For each, publish a `PickupReminderRequested(orderId, customerId, expiresAt)` event.
3. Never send more than one reminder per order (idempotent; store `reminderSentAt`).
4. Skip orders whose customer opted out of notifications.

## Out of scope
Sending the actual email/push (the Notifications service consumes the event).

## Constraints
- Scala 3, Cats Effect, existing `OrderRepository` and `EventPublisher` traits.
- No new dependencies. No schema change except nullable `reminder_sent_at` column (migration V42).

## Acceptance tests (must exist and pass)
- Expiring in 48h -> one event; running the job twice -> still one event.
- Opted-out customer -> no event.
- Expiring in 10h or 4 days -> no event.

## Verification
`sbt scalafmtCheckAll "core/test" "db/test"`
Read specs/pickup-reminders.md and AGENTS.md. Propose an implementation plan as a numbered list:
files to create/change, the order of steps, the tests you'll write first, and risks/open questions.
Do NOT write code yet. Wait for my approval.

Test-first loops agents can't game

  1. Write or approve the tests first. Either write them yourself, or let the agent write them in a separate step that you review carefully.
  2. Commit the tests. Now they're a contract.
  3. Instruct: "Make these tests pass. Do not modify files under src/test/. If a test seems wrong, stop and explain why."
  4. Enforce it: a hook or CI check that fails if test files changed in the implementation commit (L1 page 07).
  5. Check test strength: ask the agent to break the implementation in 3 plausible ways and confirm the tests catch each one (mutation testing by hand; tools in page 10).
Watch for these "cheats" Special-casing test inputs, catching and ignoring exceptions, weakening assertions, marking tests as ignored, mocking the thing under test. They are rarely malicious, just the shortest path to "green". Review test diffs first.

Large refactors

Agents are excellent at repetitive, wide changes, if you keep each step small and verifiable.

StepWhatWhy
1. Safety netHave the agent add characterisation tests that pin current behaviourRefactors must not change behaviour
2. Inventory"List every call site of LegacyPriceCalc, grouped by module, with a risk note"Size the work; find surprises early
3. Pattern firstDo one representative change together; save it as the reference exampleThe agent copies a reviewed pattern
4. BatchesApply module by module, one commit/PR each, tests green every timeSmall diffs are reviewable and revertible
5. Mechanical → toolsFor purely syntactic changes, ask the agent to write a scalafix rule or script instead of editing by handDeterministic, repeatable, reviewable once
6. Clean-upRemove the old code path once all call sites are migratedAvoid two ways of doing things

Playbook: Scala 2.13 → Scala 3 migration with agents

  1. Assess. Ask the agent to list dependencies without Scala 3 artefacts, macro usage, and compiler plugins (the usual blockers). Check the official Scala 3 migration guide.
  2. Cross-build. Add Scala 3 to crossScalaVersions and get ++3.x compile running module by module, leaves first.
  3. Let the compiler rewrite. The Scala 3 compiler can rewrite much old syntax automatically with -source:3.0-migration -rewrite. The Scala Center's migration tooling (e.g. the sbt-scala3-migrate plugin) helps with types and scalac options. Use these before asking an agent to edit by hand.
  4. Agent for the rest. "Fix the remaining Scala 3 compile errors in module core only. Keep behaviour identical. Prefer given/using for new code but don't rewrite working implicits unless required."
  5. Tests on both versions until you drop 2.13.
  6. Idiomatic pass later (enums, opaque types, new syntax) as separate, optional PRs. Don't mix it with the migration.
Why this order matters Compiler rewrites and migration tools are deterministic and cheap. Agents are best for the judgement calls left over. Using an agent to do what -rewrite can do wastes time and adds review burden.

Parallel agents with git worktrees

Several agents can work at once if each has its own working directory and branch. Git worktrees give you that without cloning the repo again.

git worktree add ../store-reminders   -b feat/pickup-reminders
git worktree add ../store-price-fix   -b fix/rounding-vat
git worktree list
# open each folder in its own IDE window / terminal agent session
# ...when merged:
git worktree remove ../store-reminders

Keeping long tasks on track

Reviewing agent output fast (without lowering the bar)

  1. Read the summary and plan first, then the tests, then the code. Tests tell you what was actually built.
  2. Ask for a guided tour: "Walk me through this diff file by file. Flag anything risky, any behaviour change, and anything you weren't sure about."
  3. Second-opinion review: run a reviewer agent (L1 page 07) with a fresh context. It isn't anchored on the implementation.
  4. Check the usual suspects: error handling, concurrency, time zones, money, null/empty inputs, logging of personal data, new dependencies.
  5. Own it: if you can't explain a line, don't merge it.

Measuring whether it helps

Feeling faster isn't the same as being faster. Track a few honest signals for your team:

Compare similar tasks with and without agents over a few weeks. Expect big wins on boilerplate, tests, migrations and unfamiliar code, and smaller ones on novel design work.

Try it (60 min)
  1. Pick a small real feature. Have the AI interview you, then write a spec in the format above.
  2. Get a plan, edit it, then write or approve tests first and commit them.
  3. Let the agent implement with "don't touch tests". Do the 3-ways-to-break check.
  4. Run a fresh reviewer agent on the diff. Note what it found that you missed, and vice versa.

Key takeaways