←  work / case-studies
case study · the delivery standardv1.0

Taking Figma out of a design team.

The deliverable stopped being a file and became a running app, on a branch that auto-deploys to the client's review site. This is the standard that makes that safe — branching, gates, two pull requests, deploy verification — and what it took for a five-person team to run it without me in the room.

read the standard, v1.0 ↗ the actual document the team runs — seven pages, unredacted. Client names were never in it; that is part of the point.
design lead · I wrote it five designers run it US healthcare · 40+ sprints behind it v1.0 · 24 aug 2026

01The problem

A handoff file is a promise. The thing the client approved is a picture of the product, not the product — and every gap between the picture and the build gets discovered later, by somebody else, as a defect.

My team stopped making the picture. We design directly in code, in the client's repository, and the deliverable is a branch that builds and runs. The client reviews the actual thing. Nothing is lost in translation, because there is no translation.

That trade does not come free. It swaps the handoff problem for a sharper one: every habit a design team has is built around files. What counts as a review, when feedback is allowed, who may publish, what "done" means — all of it assumed the deliverable was inert. Ours now executes. A designer with merge rights, on a repo that deploys to a client, in US healthcare, is one habit away from publishing an accident.

So the real work was never the tool swap. It was writing down the process that makes the tool swap safe — and the rule underneath the whole thing came first: nothing is allowed to live only in someone's head. Not a decision, not a client correction, not the reason a feature was cut. If it is not in the thread, it did not happen.

02The fact that drives everything

The design branch auto-deploys to the client's review site. Which means: a merge is a publication.

Once that sentence is true, tidy code is the least of it. The standard has exactly two jobs beyond tidiness: make an accidental publication impossible, and make a bad deploy detectable rather than merely unlikely. Every rule in the document traces back to one of those two jobs — and the rules were not invented in advance.

Nothing here is theoretical. Every warning box is something that went wrong once and cost real time to find.

That line is from the standard's own preamble, and it is the honest description of how the document exists. It is a changelog of mistakes, organised so the next person does not have to repeat them.

03One thread, two carriers

The process is one continuous thread of context, from the first sales document to the dev handoff. It thickens every time something joins, changes carrier once, and never breaks.

A diagram titled 'One thread, day one to handoff'. A green thread labelled Claude Chat gathers streams — kickoff and discovery calls, internal calls, client emails and walkthrough feedback — and produces the feature list and workflow diagram. At the client-approval flag it crosses a yellow dashed bridge of .md files into a blue thread labelled Claude Code, which produces design themes, screens, and the dev handoff, stitched by session-handoff files.
fig · the threadThe diagram I drew for the team. Carrier one is a chat project — every call is recorded, transcribed and pasted in; the feature list and the workflow diagram are built from that one context, and the client signs both. That signature is the gate. Only then does the thread change carrier.

The crossing is the fragile part. The chat and the code tool share no memory, so context crosses on .md files, by handCLAUDE.md, the project brief, the design system, the constants, the feature spec, plus the convention files that travel with the boilerplate. Into every repo, on the right branch. What is not written down does not make it across — which is not a weakness of the tooling so much as an enforcement mechanism for the no-heads rule above.

On the far side, the work happens in waves.

04The wave loop

One feature, one branch, one commit, two pull requests. Ten steps, in order, and the order is the point.

  1. 1Cut a branch from the design branch, after pulling. feature/, fix/ or docs/ — the prefix says what kind of history this is.
  2. 2Read the specification section first. Section numbers are load-bearing.
  3. 3Read whatever prior art exists, report what ports and what does not — then wait. The report is the design proposal, even when there is no prior art at all.
  4. 4Build, and verify in the browser. Defects that pass every automated gate while being plainly wrong on screen are the normal case, not the exception.
  5. 5Stop and review it, uncommitted. In a browser, against the specification. Never commit mid-build — a half-built wave in the history is a wave nobody can review.
  6. 6Feedback rounds against the working tree, as many as it takes. This step rewrites waves; it is not a polish pass.
  7. 7Only once approved, update the documentation. Revise stale entries — never append beside them — or the docs accumulate two contradictory answers that both look current.
  8. 8One commit. Two only if the reasoning genuinely splits. The message carries the why and what was rejected — the diff already says what changed.
  9. 9Merge the design PR deliberately — it publishes. Leave the dev PR alone.
  10. 10Verify the deploy by matching asset hashes. Then move on.
Same branch, same body, different bases. The feature branch is cut from the design branch, so the review build always contains what the client already has.

And rather than describe what running one feels like — run one. The transcript is real: the commands, the gates, the deliberate stop, the publish warning, the hash check at the end.

one wave · simulated
$ git checkout design && git pull
$ git checkout -b feature/notify-panel
# spec §4.2 read — section numbers are load-bearing
# prior art read · report written · WAIT … approved
# build · verify in the browser, not the diff
$ npm run typecheck && npm run lint
✓ typecheck ✓ lint — 0 errors
$ npx prettier --write . && npm run build
✓ formatted ✓ built — 14 assets
# STOP. review it uncommitted, in a browser, against the spec
# two feedback rounds folded into the working tree
# docs updated — stale entries revised, never appended
$ git commit -m "feat(alerts): notify panel with read state"
$ gh pr create --base design → PR #41
$ gh pr create --base development → PR #42
$ gh pr merge 41 --merge
⚠ this publishes — deploy workflow firing…
$ diff local.txt deployed.txt
(empty — deployed exactly what was built · 14 assets)
→ wave shipped · dev PR #42 waits on engineering’s schedule

While the dev branch lags, its PR shows a superset of waves. The standard's answer is unglamorous and correct: say so at the top of the PR body, name the merge order, and do not "fix" a cosmetic problem by stacking branches — that trades it for a real one.

05The decision I'd defend

Every instinct says a review is one thing: someone senior looks at the work, once, and blesses it. Applied to a coded design deliverable, that instinct quietly reviews the wrong thing at the wrong moment.

the problem

One review cannot serve two masters

If the engineering review blocks the design branch, a comment about naming holds up a client demo. If the design review happens on the pull request diff, it reviews code — after the design has already settled, when changing it is expensive — and it reviews something a feedback round is about to rewrite anyway.

And underneath both: the person who faces the client and the person who takes the code to production are looking for different defects. Collapsing them into one review means one of them is skimming.

the reframe

Two reviews, at two different moments, blocking different things

The design reviewThe code review
whenSteps 5–6 — before a commit existsAfter the wave is pushed
againstThe working tree, in a browserThe dev PR's diff
byWhoever faces the clientThe engineers taking it to production
asksIs it the right thing? Is the copy true?Architecture, reuse, naming, the mock seam
blocksThe commitNothing on the design branch

The design review is the one that changes the product, and it happens while the work is still molten. The code review never blocks the client. Because the two are separated, neither is optional — skip the first and you ship the wrong thing quickly; skip the second and engineering inherits a deliverable nobody on their side has read.

The objection I get most: the designer is reviewing their own wave. The standard's answer, which I stand behind: the design is produced by a tool the designer is directing, so steps 5–6 are a review of that output — in a browser, against the specification — not someone marking their own homework. It is the same relationship any reviewer has with any author's work, compressed into one person and one sitting. And the feedback that arrives after a wave has shipped does not bend the rule: the dividing line is the commit, not the seniority of whoever spoke. Late feedback opens a fix/ branch and becomes the next wave, with its own review and its own deploy verification.

06Three rules with receipts

The standard's last section is titled "the rules that cost the most". These three earned their place.

shipped wrong, tick was green

The green tick is not the check

The deploy workflow has run green while shipping the wrong build. So every merge ends with the ritual in the instrument above: build locally, list your content-hashed assets, list the deployed ones from the build log, diff — empty means the deploy is exactly the build. On one project the asset count went 8 → 12 → 14 → 17 → 21 → 19, and the rule is not "the count must not fall" — a bundler may legitimately merge chunks. The rule is an unexplained count is the only signal you get, in either direction.

Same family, worse disguise: hashed assets are cached forever, so the HTML shell must be synced separately with no-cache headers — or a returning reviewer gets yesterday's shell pointing at asset names that no longer exist. A blank page, on a site that deployed successfully.

the record lied

An approval can outlive the code it approved

A platform approval is pinned to a commit, and by default it survives new commits — so a pull request can show an approval for code nobody has read. One checkbox (dismiss stale approvals) is the difference between a review and the appearance of one, and it has to be switched on per repository, at setup.

The deeper version: requesting a review does not produce one. Whole waves were discussed in detail in chat while the pull-request record stayed empty. The standard's position is to stop pretending — decide which channel the ask actually goes through, and treat the platform record as bookkeeping unless audit needs it to be more. The habit changes deliberately, or it is not assumed to have changed.

the docs drifted

Written down is not the same as true

A rule recorded in the design system has been cited to justify an exception — and never actually applied to the surface that needed it. Five blocked items once sat in a "do not start" table after the thing blocking them had been answered somewhere else. Interface copy described behaviour the product did not have, which is a defect where the fix is usually the missing behaviour, not softer words.

The standard's countermeasures are all small and procedural: audit against the specification rather than your own notes, re-read the blocked list whenever a decision lands, sweep the copy for capability claims, and treat seed data as copy — it is read by the same people and carries the same authority.

07Making it the team's, not mine

A standard one person follows is a habit. The test was whether it survived contact with four other designers, parallel waves, and a platform it was not written for.

Parallel work was designed in, not discovered. One branch per wave is what lets several designers run at once without colliding; the branch prefixes keep the history legible; the two-PR shape means nobody's client demo waits on anybody's code review.

The document knows what it does not know. Two things are deliberately blank — who holds the DevOps role on an engagement, and whether code review is a named person or the team — because those are organisational, and pretending they are technical would make the standard wrong somewhere. Each repository's own agent guide stays authoritative for its exact branch names, base paths and gate commands; the standard generalises them for starting the next project.

And it has already survived its first inversion. The mobile adaptation, written the same week, keeps the wave loop and the two-PR shape but flips the central rule: on mobile, publication is a build, not a merge — a binary someone installs cannot be un-shipped the way a deploy can be replaced. The adaptation records only what differs and why, down to "yarn, never npm" with the exact peer-dependency reason. A v1.0 that can absorb that inversion without rewriting its philosophy is the closest thing process work has to a passing test.

08What this is not

What I think is genuinely good: the one-thread rule, which makes the whole flow auditable; the two-review split; and the deploy-verification ritual, which converted "probably fine" into a thirty-second check with a yes/no answer.

What honestly limits it:

The paper trail, all of it mine to publish: the standard itself, v1.0 — seven pages, as the team runs it; the context-thread diagram above, drawn as the companion to the sprint-flow document; and the mobile adaptation quoted in section 07, which records the standard's first platform inversion. Client work stays unnamed throughout — the process is the publishable part, and it was written that way on purpose.

Screens are absent from this page by design: every screen this process has produced belongs to a client. The two case studies beside this one show the craft; this page shows the system that ships it.

←  back to selected work talk about this one →