Building with Claude

Training series

Building with Claude

Practices, guardrails and habits for shipping faster without lowering the bar.

Part 1Guardrails and mindset
Part 2The Claude Code workflow
Part 3Beyond code
Part 4Multiply what works
Fig. 0Each dot stands for a Claude Code session my team ran; about 1 in 65 became a pull request (blue). This series is about turning more grey into blue.
A field of grey dots in which about 1 in 65 is blue

How this series works

Four short sessions. Each one ends with something you can use the same day.

Part 1

Guardrails and mindset

Everyone, 45 min

What never goes into a prompt, and who owns the output.

Part 2

The Claude Code workflow

Engineers and QA, 60 min, hands-on

Plan, build, test and review with Claude on a real repo.

Part 3

Beyond code

Everyone, 45 min

Deciding what to build, plus chat and Cowork for specs, docs and analysis.

Part 4

Multiply what works

Everyone, 30 min, then every two weeks

Share, turn mistakes into rules, and measure progress.

Part 1

Guardrails and mindset

Everyone, 45 minutes

  • Where we are today
  • You are the engineer of record
  • AI amplifies your process
  • Data, secrets and tenant isolation
  • Merging is accountability
Fig. 1Each chamber is one tenant's data, and nothing ever crosses a wall: 25 chambers, zero crossings. The blue chamber is the only one a query should ever touch.
A grid of 25 sealed chambers, one highlighted

Where we are today

My team's Claude usage in one month.

~92%
of the team uses Claude Code
1 in 65
sessions became a pull request
~43%
use Claude beyond code

We use Claude a lot. The gap is turning sessions into reviewed, shipped changes, and using it beyond code.

Claude drafts. You are the engineer of record.

Claude drafts code, tests, docs, plans You review and decide every line, every test Your name on the PR accountable for what ships

It lacks our context

It doesn't know our tenants, clients or history unless you tell it.

It can be confidently wrong

Especially about methods and APIs that look plausible but don't exist.

No review defence

"Claude wrote it" never explains a bug in your PR.

AI amplifies whatever process you already have

Clear process plan, tests, review AI Better work, faster quality compounds Messy process no plan, no tests AI Tech debt, faster problems compound too
  • Speed isn't free. If you ship faster, you also need to catch problems faster.
  • The fix is better structure and clearer constraints, not more AI.
  • That's why the rest of this series is about process, not prompts.

Describe the shape of the data, never the data

Never goes into a prompt
  • Client or tenant data, including exports and screenshots
  • Credentials, API keys, tokens, .env files
  • Personal data about customers or colleagues
  • Production dumps and query results
  • Contracts and commercial terms
Unsafe
Here's last week's sales export for the client.
Why is sell-through wrong? [pastes 2,000 rows]
Safe
Here's the schema of the sales table (column names
and types) and 5 synthetic rows. Sell-through comes
out above 100% for some styles. Write a query to
find the rows that cause it and explain likely causes.

Tenant isolation is a hard constraint

Query tenant_id = A scoped cross-tenant Tenant A data Tenant B data critical defect
def get_stock(tenant_id: str, style_id: str):
    if not tenant_id:
        raise TenantContextMissing(
            "tenant_id is required"
        )
    return stock_repo.find(
        tenant_id=tenant_id,
        style_id=style_id,
    )

Ask Claude: "List every data path this change touches and show where each one is scoped to a tenant."

Merging is accountability, not automation.

Explain every line

Never merge code you can't explain to a reviewer.

Watch for rubber stamps

As speed goes up, review depth tends to go down. Don't let it.

A human approves

Every merge and every deploy is a human decision.

Part 2

The Claude Code workflow

Engineers and QA, 60 minutes, hands-on

  • The work moves to design and review
  • The loop: plan, slice, test, review
  • CLAUDE.md and our 20 rules
  • Code patterns we accept and reject
  • Verification and context hygiene
Fig. 2The six stations on the outer ring are our workflow steps, from plan to PR. Each inner ring is another pass through the loop, tightening toward a clean result.
Concentric loops converging, with six stations on the outer ring

The work moves to design and review

Claude writes code quickly, but it can't fix a bad design. Spend the time you save on the plan and the review.

Before AI
DesignWriting codeReview
With AI
Design and planningCodeReview and testing

Illustrative split. Your numbers will vary; the direction won't.

The loop, every time

iterate: each loop is cheap now 1 2 3 4 5 6 Planagree the approach Small sliceone concern Testwith the code Run locallysee it pass Reviewyour own diff first PRwhy, tests, risks

AI doesn't reduce iterations. It makes each one cheaper, so do more of them. The first output is a draft, never the deliverable.

Step 1: plan before any code

Prompt
Read src/allocation/ and its tests.
Do not write or edit any code yet.

Propose a plan to add a minimum display
quantity check to allocation:
- files you will change, and why
- functions you will call (open each file
  and confirm it exists)
- tests you will add
- open questions and assumptions

Wait for my approval before implementing.
  • Use plan mode, so nothing is edited while you agree on the approach.
  • Rule: never propose changes to code you haven't read.
  • Challenge the plan. Do we need this table? What happens when this fails? It's cheaper to fix a plan than a diff.
  • If the plan touches many files, split the task.

For bigger work, let Claude interview you first

  • Instead of writing a spec from scratch, let Claude question you until the gaps are gone.
  • Save the spec in the repo. It becomes the brief for implementation and an example for the next feature.
  • Break the plan into thin vertical slices, each one testable and shippable on its own.
Prompt
I want to add [feature]. Before writing anything,
interview me. Ask one question at a time about:
users, edge cases, failure modes, tenant
boundaries and what's out of scope.

When you have enough, write a spec with:
goal, non-goals, API shape, data changes,
tests, and open questions.

Then split it into small slices, each one
shippable and testable on its own.

CLAUDE.md: our standards in every repo

  • Claude reads it at the start of every session, so everyone's Claude follows the same rules.
  • Keep it short and specific to the project: commands, rules, definition of done.
  • Every time you repeat an instruction, add it here instead.
  • Commit it, and review changes to it like code.
# CLAUDE.md
## Commands
Tests: pytest -q        UI: pnpm test

## Non-negotiable
- Every query is tenant-scoped. No cross-tenant paths.
- Read a file before referencing anything in it.
- Smallest change that solves the task.
  Don't touch unrelated code. Flag it instead.
- Tests in the same change. Never edit a test to pass.
- Ask before adding a dependency.
- No magic numbers. Named constants or config.
- Never swallow exceptions. Fail loudly and early.
- Safe to run twice. Log state changes and calls.

## Definition of done
- Tests run locally. Paste the output.
- Docs updated if behaviour changed.
- List every file changed and why.

Our 20 rules, grouped

Write it clearly

  1. 1Explicitness over cleverness
  2. 2Boring over clever
  3. 3DRY: one home for each piece of business logic
  4. 15No hardcoded values or magic numbers
  5. 18No dead code, unused imports or unreachable branches

Structure it well

  1. 4Changes don't ripple across layers
  2. 5Side effects at the edges, pure business logic
  3. 10Can't test it in isolation? Fix the structure
  4. 16Smallest change that solves the problem
  5. 17Public interfaces stay backward compatible

Keep it safe

  1. 6Fail loudly, fail early
  2. 8Tenant isolation is a hard constraint
  3. 14Never assume a method, field or service exists
  4. 19Idempotent by default
  5. 20Log state changes and external calls

Ship it properly

  1. 7Don't assume, prove it
  2. 9Tests aren't optional under deadlines
  3. 11Local testing before every PR
  4. 12Docs are part of the work
  5. 13Don't change what you weren't asked to

The most common failure: things that don't exist

Looks right, isn't
reserved = inventory_service.get_reserved_qty(
    tenant_id, style_id
)
# get_reserved_qty() was never defined.
# The name was a plausible guess.

Invented methods, fields, config keys and CLI flags read fine in review and fail at runtime.

Put this in the prompt
Prompt
Before you use any function, class, field
or config key:
1. Open the file where it is defined.
2. Quote its signature in your reply.
3. If it doesn't exist, stop and tell me.
   Don't create it silently.

In review, search the codebase for every new symbol in the diff.

Small slices, and only what was asked

Task: fix rounding in allocate_units()

allocation/core.py   +4  -2    the fix
allocation/core.py   +38 -41   renamed variables
utils/dates.py       +12 -3    unrelated cleanup
pyproject.toml       +1  -1    bumped a dependency

One line of value, three sources of risk, and a diff nobody can review properly.

Prompt
Change only what's needed to fix this.
If you notice other problems, list them
under "Flagged, not fixed" at the end.
Don't reformat, rename or upgrade
anything else.

Flagged issues become their own tickets. Public interface changes need explicit approval.

Tests that actually prove something

  • For a bug, ask for a failing test first, then the fix.
  • Every bug fix leaves a regression test behind.
  • Never change a test to make it pass. Claude asks before modifying existing tests.
  • Check the tests test behaviour, not that a mock returns what the mock was told to.
  • Claude shows the test output. "Tests pass" without output doesn't count.
def test_reorder_qty_is_never_negative():
    assert reorder_qty(on_hand=50, target=30) == 0


def test_on_hand_is_tenant_scoped(repo):
    repo.seed(tenant_id="t1", style_id="S1", on_hand=5)
    repo.seed(tenant_id="t2", style_id="S1", on_hand=500)

    assert get_on_hand(
        repo, tenant_id="t1", style_id="S1"
    ) == 5

Fail loudly. Don't let Claude hide errors.

Defensive code that hides bugs
try:
    plan = load_plan(tenant_id, plan_id)
except Exception:
    plan = {}  # carry on with empty plan

AI often adds broad try/except blocks to look robust. The result is wrong output and no error.

Explicit and early
plan = load_plan(tenant_id, plan_id)
if plan is None:
    raise PlanNotFound(
        f"plan {plan_id} not found "
        f"for tenant {tenant_id}"
    )

Catch only what you can handle. Never return a default just to keep going.

Pure core, I/O at the edges

  • Business logic takes values and returns values. Easy to test, no mocks.
  • Thresholds come from config, not literals.
  • Writes use upserts or keys, so a retry doesn't double-apply.
  • State changes and external calls are logged with tenant context.
# Pure: no I/O, no hidden state
def compute_replenishment(stock, policy) -> int:
    target = policy.target_cover_days * stock.daily_rate
    return max(target - stock.on_hand, 0)


# Edge: reads, logs, writes
def run_replenishment(tenant_id, style_id) -> None:
    stock = stock_repo.get(tenant_id, style_id)
    policy = policy_repo.get(tenant_id)
    qty = compute_replenishment(stock, policy)
    logger.info("replenishment computed", extra={
        "tenant_id": tenant_id, "qty": qty,
    })
    order_repo.upsert(tenant_id, style_id, qty)

Separate writing from checking

The session that wrote the code is the worst one to review it. Review in a fresh context, one lens at a time.

Writer session

Plans and implements one slice. Runs the tests.

→

Reviewer session, fresh context

Reads the diff cold and reports issues with severity and location.

CorrectnessTenant isolationSecurityPerformanceSimplicity
Review prompt
Review this diff as a senior engineer on this team. Check, in order: correctness, tenant isolation,
security, performance, simplicity. For each issue give severity, file and line, and a suggested fix.

Keep the context clean

Fresh
one task, only the relevant files
Working
plan agreed, implementing a slice
Cluttered
old tasks, long logs, stale assumptions
  • One task per session. Clear the context when you switch.
  • Clear between planning and building. The plan file carries over, the noise doesn't.
  • Two failed attempts? Stop, re-plan or narrow the task.
  • Read every command before approving it, especially migrations, force-push, deletes and CI changes.

The difference is discipline, not tooling

Rigorous design+ AI implementation+ Aggressive review+ Many iterations= Quality at speed
No review+ First output accepted= Tech debt at speed

Before you raise the PR

  • I can explain every line in this diff.
  • Every new method, field and config key exists. I checked.
  • Every data path is tenant-scoped.
  • Tests were added and I saw them pass locally.
  • Only the requested change is in the diff.
  • No swallowed exceptions, magic numbers or dead code.
  • Public interfaces are backward compatible, or the break is approved.
  • Re-running the operation is safe.
  • State changes and external calls are logged.
  • Docs and the PR description are updated: why, how tested, risks, revert plan.
Part 3

Beyond code

Everyone, 45 minutes

  • Build the right thing first
  • Simple, boring, proven
  • Chat and Cowork in daily work
  • QA with Claude Code
Fig. 3Every grey line is something we could build, and AI makes all of them cheap to start. The blue line is the one we chose on purpose: deciding what to build is now the scarce skill.
Many faint paths from one origin, with a single blue path chosen

Building got cheap. Deciding what to build didn't.

Code written Value delivered time conceptual
  • When anyone can generate code, features alone don't stand out. Knowing what to build and why does.
  • Ask "Why are we building this?" before the first prompt.
  • First build the right thing. Then build it right.

Pragmatic beats theoretically perfect

Simplest first

What's the simplest thing we could ship?

Ship that. Optimise after it's delivered and used, not before.

Boring tech

Proven over shiny

Claude makes adding a new library easy. That doesn't make it a good idea.

Prototype to decide

A PoC now takes hours

Use one to test feasibility before you promise a date or a design.

Every decision is a trade-off. Bring the business the pros and cons in their language, not a flat yes or no.

Chat and Cowork in daily work

Same data rules apply: redact first, and treat every output as a draft.

Design docs and ADRs

"Turn these notes into an ADR: context, options, decision, consequences. List what's missing."

Spec review

"Find gaps, edge cases and conflicting requirements. Ask me questions instead of assuming."

Ticket breakdown

"Split this spec into tickets with acceptance criteria, boundaries and test cases."

Log and incident analysis

"From these redacted logs, build a timeline and the most likely causes, ranked."

Test plans

"From this spec, write a test plan: happy path, boundaries, failures, tenant isolation."

PR descriptions

"From this diff, write a PR description: why, how it was tested, risks, revert plan."

QA with Claude Code

  • Automation lives in the same repos, under the same review rules as production code.
  • Synthetic data only. Fixtures come from factories, never from client exports.
  • Every feature gets a tenant isolation test as standard.
  • Every bug report comes with a failing test that reproduces it.
  • An AI first-look review on each PR catches the basics: committed secrets, missing tests, unlinked tickets.
Prompt
From this spec, write test cases for
allocation caps:
- happy path
- boundaries: 0, max, max + 1
- invalid and missing input
- one test proving tenant A cannot
  read or change tenant B's data

Use only synthetic data from
tests/factories.py.
Run the suite and show me the output.
Part 4

Multiply what works

Everyone, 30 minutes, then every two weeks

  • Why multiplying beats heroics
  • Turn mistakes into rules
  • Share in the open
  • Measure what matters
  • Do and don't
Fig. 4One person's good habit spreads to five people, then twenty-five, then a hundred and twenty-five. Sharing what works beats being the hero who does it alone.
One point branching to five, twenty-five and one hundred twenty-five

Make five people 20% better

Your best workflow is worth more shared than kept. That goes for prompts, CLAUDE.md lines and review habits.

You improve: +20%
Five people improve: +100% team capacity

Turn every mistake into a rule

Somethinggoes wrong Fix the process,not the person Could Claude catchthis next time? Encode itCLAUDE.md, test, lint rule
  • No blame. An AI-introduced bug passed through a prompt, a review and our tests. Fix the step that let it through.
  • Give feedback on mistakes privately. Share the lesson publicly.
  • Each encoded rule means one less thing a human has to catch.

Create a space and seed it

Don't wait for someone to assign it. Start sharing useful things and let it compound.

Daily

Team AI channel

Post one prompt, skill or workflow that saved you time this week.

Every two weeks

Show-and-tell

15 minutes. One person, one real workflow, one lesson.

Ongoing

Shared library

Reusable prompts, review workflows and CLAUDE.md snippets in one repo.

Don't just flag a problem. Bring the data, the cost, and an offer to help fix it.

Measure what matters, then work from red

Each metric gets thresholds we agree as a team. Every month we review the trend and start with whatever is red.

MetricWhat it tells usThresholds
Sessions that end in a merged PRAre we shipping, or just exploring?
People using more than one surfaceIs Claude helping beyond code?
Regression tests per bug fixAre we learning from mistakes?
Review comments on AI-introduced issuesIs our process catching problems early?
Rules added to CLAUDE.mdAre lessons being encoded?

Do and don't

Do

  • Plan first and keep slices small
  • Write tests with the code and run them locally
  • Review in a fresh context before the PR
  • Scope every query to a tenant
  • Encode every repeated instruction
  • Share what works

Don't

  • Paste client data, secrets or personal data
  • Ship the first output or code you can't explain
  • Let it change things you didn't ask for
  • Accept methods or fields you haven't verified
  • Change a test to make it pass
  • Blame a person for a process gap

This week

1

Add CLAUDE.md

To every repo you own, starting from the template in Part 2.

2

Plan first

Use the plan-first prompt on your next task, then review in a fresh session.

3

Share one thing

Post one prompt or workflow that worked in the team channel.

Credit to the Engineering Leadership newsletter, whose writing on AI-assisted engineering, pragmatic engineering and multiplier leadership informed much of this series.

Arrow keys to move, F for full screen