September 12, 2026
PLAN THE INTERFACES BEFORE PARALLEL WORKERS BUILD THEM

Lesson 43.1 ordered the work. This lesson solves the next risk: separate AI workers implement pieces that look reasonable individually but do not fit together. The frontend renders a date the backend never sends. The API returns null where the UI assumed a string. Both sides pass their own checks. Integration fails.
The fix is unglamorous and decisive: agree on the interface before parallel work starts.
What an interface contract actually is
An interface contract is a precise agreement between components about what one side sends, what the other receives, who owns it, what happens when input is missing or invalid, and how the agreement is tested.
Say it as a checklist every time:
- What is sent?
- What is received?
- Who owns the definition?
- What happens when input is missing or invalid?
- Which test proves both sides agree?
A contract is not a vague description ("the API returns source info"). It is exact enough that two workers in two separate sessions — or two separate branches — can build against it without asking each other questions.
This connects directly to earlier lessons. JSON from Part II expresses the data shape. APIs and MCP from Part V expose tools and data across a boundary. Branches and worktrees from Class 41 separate the work. The interface contract is what keeps those independently moving pieces compatible.
A concrete contract: the source record
Take the research dashboard's source record. Here is the agreed shape:
{
"source": {
"title": "Example filing",
"url": "https://example.com/source",
"publishedAt": "2026-09-12T00:00:00Z",
"publishedAtStatus": "known"
}
}
The JSON syntax is the easy part. The contract decisions are the real teaching:
| Decision | Choice | Why it matters |
|---|---|---|
| Required vs. nullable | title and url required; publishedAt nullable | Provider dates are inconsistent; the UI must handle absence by design |
| Missing-data UI | publishedAtStatus: "unknown" renders "Date unavailable" | No blank space, no invented date, no crash |
| Who can change it | Data/API owner only | UI may read it, never redefine it |
| Version + supplier | v1, from approved provider mapping | A new provider version cannot silently change the shape |
| Agreement test | Contract test: response matches shape; UI renders known + unknown states | Proof both sides honor the same truth |
Notice how many of these answers are decisions, not code. "Required or nullable?" "What does the UI show when it is missing?" "Who is allowed to change it?" If those answers live only in one worker's chat, the other worker will invent them. That invention is the integration bug, arriving early and wearing a disguise.
Review the boundaries before coding
Before any implementation task starts, review the boundaries the feature touches. You do not need to solve every line. You need to name every surface where a "small" feature could get bigger:
- Frontend. Which component renders the data, and what are its empty, loading, and error states?
- Backend / API. Which endpoint or tool exposes the data, and what shape does it guarantee?
- Database. Does anything need storing, migrating, or retaining — or is this read-only?
- External provider. What does the supplier actually return, including nulls, rate limits, and version changes?
- Auth / security. Who may read or change this, and where is that rule enforced?
- Background job. Does anything refresh, retry, or run on a clock?
- Deployment. Does preview behave like production for this feature — data, permissions, settings?
For the source-date feature, this review is short but decisive: provider (dates sometimes absent) → API (nullable field + status flag) → UI (designed missing state) → no auth, no migration, no job. For the watchlist, the same review lights up: identity, access rules, user-data retention, and privacy — a far heavier plan. Same template, different answer. That difference is the point.
Contract tests: proof, not hope
A contract test is a repeatable check that a provider, endpoint, or component returns or accepts the agreed shape. If the backend renames publishedAt or starts sending an empty string instead of null, the test fails before the UI breaks in front of a user. If the UI drops the missing-date state, its side of the test fails before merge.
Full testing mechanics — runners, fixtures, mocks, CI — arrive in Part XI, Build Software. Here you learn the planning concept: every interface needs proof, and the plan must name which test provides it and who runs it. "We will check it manually later" is not a contract test. It is a wish.
The parallel sequence that actually works
Parallel work is safe only in one order:
planner approves contract
→ data/API owner implements and tests it
→ UI builds against the agreed mock/contract
→ reviewer checks integration
→ human runs the end-to-end flow
Three rules enforce it:
1. One contract, one owner. The data/API owner writes and changes the definition. Everyone else reads it. 2. Mock against the contract, not against imagination. The UI's mock uses the exact agreed shape, including the nullable case. 3. Never let both sides invent the contract in separate chats. If the contract moves — the provider really has no dates — stop, update the note and the decision log (Class 42.3), re-approve, then resume. Code written against yesterday's guess is rework.
This is why Lesson 43.1 sequenced data proof before brief UI. The UI lane can start early against a frozen mock, but it may not redefine the shape to suit itself.
The INTERFACE-NOTE.md habit
For every connection between components, write one short note in the repo:
# INTERFACE-NOTE: brief source record
- Owner:
- Input:
- Output (with example payload):
- Errors / empty state:
- Access rule:
- Agreement test:
- Version / supplier:
A filled example for this lesson:
# INTERFACE-NOTE: brief source record
- Owner: data/API owner (Maya); UI reads only
- Input: company ticker, e.g. `ACME`
- Output: source object with title (required), url (required),
publishedAt (nullable ISO-8601), publishedAtStatus (known|unknown)
- Errors / empty state: unknown date → "Date unavailable"; missing
source → empty-state card, no invented citation
- Access rule: public read; no auth required for brief sources
- Agreement test: contract test on endpoint shape + UI render
check for known and unknown dates
- Version / supplier: v1, approved provider mapping dated 2026-09-12
- Example: {"source": {"title": "Example filing",
"url": "https://example.com/source",
"publishedAt": null, "publishedAtStatus": "unknown"}}
If two separate workers can each build their side from this note alone, without messaging each other, the note is done. If they need to guess, it is not.
Exercises
1. Write an INTERFACE-NOTE.md. Pick one connection in your project — an API response, a tool result, a database read, a job payload. Fill in owner, input, output, errors/empty state, access rule, and one example payload. Include the nullable-vs-required decision explicitly.
Finish line: an INTERFACE-NOTE.md that two separate workers could use without guessing.
Verify it: hand it (mentally or literally) to someone who has not seen your code. Ask what the UI shows when the key field is missing, who may change the shape, and which test proves agreement. If they can answer all three from the note, it passes. If they guess, revise.
Common failure: documenting only the happy path. Recovery: add one missing-field example and one error example before calling the note complete.
Check your understanding
- What five questions must every interface contract answer?
- Why is "is
publishedAtrequired or nullable?" a more important contract decision than JSON syntax? - Which boundaries should you review before coding, even briefly?
- What is the correct parallel sequence, and why must the UI build against the agreed mock rather than its own invention?
In the next lesson you will learn where to spend your scarcest resource — attention. Interfaces show what must fit; risk-based planning determines which work needs more design, review, and proof before it moves.
ARTICLE DISCUSSION
JOIN THE
CONVERSATION.
Got a question, a take, or a better way to do this? Log in and leave a comment.
