Tutorial 2: a change that needs a design
Tutorial 1 skipped Understand almost entirely — the bug
was small and unambiguous. This tutorial covers the other case: a change that touches
something other code depends on, where getting it wrong is expensive to unwind later. By
the end you’ll know exactly which properties of a change trigger design-solution, what
it produces, and how that feeds into Build.
Who this is for
Section titled “Who this is for”You’ve already done Tutorial 1 or know the four-step shape from the Onboarding Guide, and you want to see what changes when a fix isn’t small.
The change
Section titled “The change”Continuing the descriptor example: product wants a second descriptor computed through the
same pipeline — TPSA (topological polar surface area) alongside the existing molecular
weight — and other code already calls compute_molecular_weight(mol, exclude_salts) in
three places. This isn’t a bug fix; it’s a new shape for an existing function every caller
depends on. That’s the signal.
Why this triggers Design
Section titled “Why this triggers Design”From the Onboarding Guide: Design is conditional depth inside Understand, triggered by real properties of the change — not by size. This change qualifies because:
- It changes a shared contract —
compute_molecular_weight’s signature, called from three other places. - Getting the descriptor representation wrong (a bare float? which units for which kind? what happens to a caller that doesn’t know about TPSA yet?) is exactly the kind of decision that’s cheap to get right once and expensive to unwind after three call sites depend on the wrong shape.
A one-line salt-stripping fix didn’t have either property. This one has both.
Step 1: describe the change
Section titled “Step 1: describe the change”State the outcome to your assistant – there is no separate entry point to switch to
for design work, on any platform
(ADR-0044).
The same conversation reads the situation and invokes design-solution itself:
We need to compute TPSA alongside the existing molecular weight in the screeningpipeline. Three call sites use compute_molecular_weight today.The assistant investigates the actual call sites first — not just the function
definition — and, because this is a shared-contract change, proposes a brief before
touching design: outcome, who’s affected, what’s explicitly out of scope (e.g. “no change
to how salts are excluded” if that’s true), and success criteria. For a feature this size,
that’s docs/codev/features/descriptor-support/brief.md. Review it, correct anything wrong
about the framing, and accept it.
Step 2: the design
Section titled “Step 2: the design”With the brief accepted, design-solution drafts
docs/codev/features/descriptor-support/design.md, grounded in the actual current code
(not an idealized rewrite). For a change this size, expect it to settle:
- A unified entry point —
compute_descriptor(mol, kind, exclude_salts=True)returning aDescriptor(kind, value, units)result, instead of one hardcoded function per property. - Every call site, named explicitly, with what changes at each one — not “update callers as needed.”
- What happens at the boundary values — an unknown
kind, whetherexclude_saltseven applies the same way to TPSA as it does to molecular weight. These are exactly the questions worth settling on paper before three call sites each guess differently. - Test strategy — one test per call site’s actual usage, not just the new function in isolation.
This is where a real decision belongs to you, not the AI: which representation to use, and whether existing callers get migrated in this change or a follow-up. The assistant proposes an option with trade-offs; you decide. Once you accept the design, it becomes the authority the builder works against — matching Tutorial 1’s rule that later documents link to earlier ones rather than repeating them.
Step 3: build against the accepted design
Section titled “Step 3: build against the accepted design”From here it’s the same loop as Tutorial 1 — codev task start, codev git branch, build
in bounded rounds, codev task record, codev task check — except the builder now treats
the accepted design as authority it doesn’t get to redesign to make coding easier. If the
implementation reveals the design missed a case (a fourth call site nobody remembered),
that’s a stop condition, not something to quietly patch around: the builder returns
BLOCKED with exact evidence, and the design gets corrected before implementation
continues.
The rule this tutorial is really about
Section titled “The rule this tutorial is really about”Risk and coordination decide how much of Understand you need — not the size of the diff. A five-line change to a permission check gets exactly this same design treatment even though the diff is tiny, because the property that matters (a security boundary) is present regardless of line count. A three-hundred-line change that’s purely additive, with no shared contract and no risk property, can skip straight to Build. Check the properties, not the size.
Where to go next
Section titled “Where to go next”- Taking the resulting PR through outer-loop review: Tutorial 3
- Coordinating this kind of change across two developers: Tutorial 4