Skip to content
ML Academy

Core day 58 of 60 · Week 12 · Part II · evaluation depth

Production AI System Design

In a design review someone asks: "Why this architecture, and what would make you reverse it?" Using the assumption register and decision log you actually kept in Weeks 1-11, how do you defend one ML or GenAI design decision in six minutes without defending it past its evidence?

By the end: You choose one decision from your own log, list the assumptions it rests on, name the strongest alternative and the measurable triggers that would reopen it, and answer one challenge by checking the log rather than arguing.

At a glance

Show the design as a chain of decisions, link each decision to dated assumptions, and let written triggers, not the argument, decide when to reopen it.

  • A chain, not a model

    Decision, data, training, serving, gate, monitoring, cost; GenAI adds retrieval and a trust boundary.

  • Two linked logs

    Assumption register and decision records, joined by the assumptions each decision lists.

  • Queues from the log

    Failed assumption: reopen. Date passed: review. No assumption: untestable.

  • Triggers

    Metric, threshold, window, owner and the action they fire.

  • Six moves

    Decision, constraints, strongest alternative, weakest assumption, failure and rollback, triggers.

Recall · 5 min

From memory: (1) Name three differences between batch, online and shadow serving. (2) Which signal fires first when inputs drift, and which one arrives last? (3) Which control stopped the injected passage in your Week 11 incident? Then clear your due review cards.

Open due reviews

Sign in to record this block.

Concept · 10 min

At a senior level, system design is a chain of dependent decisions, not a model choice. Every link rests on assumptions: how often the consumer acts, how fast a feature changes, how a vendor behaves, whether held-out queries look like real ones. Defending a design means showing which assumption would break which link, and not defending it past that point.

A design choice is a point on a quality-cost frontier that moves when one assumption moves

Predict first

Raising one assumed input moves the point at which the cheapest option at your quality floor flips. What do you record?

Text description of the figure

A cost, latency and quality lab with editable, hypothetical price, routing share, cache hit rate and context length inputs. Changing an input moves the cheapest option that meets a quality floor, so the lab shows which assumption the choice depends on.

The design chain for a prediction service and for a GenAI service Comparison
CriterionPrediction servicecard-fraud scoring (banking)GenAI servicepolicy assistant (insurance)
Decision and consumerApprove, decline or step up each card authorisationClaims handler quoting cover to a policyholder
DataTransactions with a data contract and point-in-time featuresPolicy wordings with versions, effective dates and permissions
Model stepTrained model against the current rulesRetrieval, context assembly, generation mode
ServingOnline under a latency budget; shadow first (Day 19)Per request, with an untrusted-input boundary (Day 53)
Evaluation gateTime-ordered backtest in losses and false declinesGolden set with slices, attack suite (Days 51, 55)
Monitoring and rollbackDrift, skew, chargeback labels weeks late; previous model one switch awayAbstention rate, citation share, incidents; previous index or prompt
Cost unitCompute per 1,000 scored transactionsTokens per request and per good answer (Day 54)

Each cell rests on assumptions. The defence names the one most likely to be false.

The design as a blueprint, with its loops and today's audit Illustration
ConsumerServingModel / retrievalFeatures / indexDataPrediction serviceGenAI serviceMonitoringOverbooking runacts 4 times a dayBatch scoring4 runs a dayD1No-show modelretrained monthlyD4Point-in-time featuresas of each cutoffBookingswith a data contractdecisions change the dataAgent quoting a feereads the cited answerGenerate with citationsabstain below threshold; trust boundaryHostedembeddingsD2BM25fallbackD3Chunk indexversions and permissionsFare-rules corpusmanifest, checksums, datesDecision outcomesno-shows, complaintsDrift and skewlabels arrive laterLatency p95abstention, citationsTriggersmetric, threshold, windowtrigger routes to fallbackD1reviewA2 was due 2026-10-01D2reopenA3 failedD3okassumptions holdD4untestableno recorded assumption

The toy decision log and assumption register of the application (synthetic), audited on 2026-10-11 by the same rules as its code. Components are generic; no real organisation is described.

Audit on 2026-10-11: D1 review, D2 reopen, D3 ok, D4 untestable. Dashed lines are the loops that move assumptions: decisions change the data the next model learns from, and monitoring fires the fallback.

Each badge is a decision from the toy log, coloured by the audit the application code runs. The dashed lines are where assumptions move.

Two logs, one link, three queues Diagram
Assumptionsconfidence, owner,dateDecisionrecordslist theirassumptionsAssumptionstatus?Reopencompete again onevidenceReviewvalidate before relyingWatchlow confidenceUntestablenothing can ever faillisted byauditfaileddate passedlow confidencenone listed
  1. Assumptionsconfidence, owner, date
  2. Decision recordslist their assumptions
    • audit: to Assumption status?
  3. Untestablenothing can ever fail
  4. Assumption status?
    • date passed: to Review
    • low confidence: to Watch
  5. Reopencompete again on evidence
  6. Reviewvalidate before relying
  7. Watchlow confidence

Each decision lists the assumptions it rests on. Failed: reopen. Revisit date passed: review. Low confidence: watch.

Application output: the toy log audited on 2026-10-11 Table
DecisionRests onFlagNext step
D1 Batch no-show scores, 4 runs a dayA1, A2Review: A2 was due 2026-10-01Validate A2 before relying on D1
D2 Hosted embeddings for retrievalA3, A4Reopen: A3 (latency) failedCompare with the fallback on current evidence
D3 BM25 as fallback retrieverA4okSurvives the event that reopens D2
D4 Retrain monthly on a calendarnoneUntestableRecord what the calendar assumes
Six moves, about a minute each Sequence
  1. Decision and consumer

    What is decided, by whom, how often.

  2. Constraints and success

    Latency, cost, quality floor; the number that defines success.

  3. Strongest alternative

    Why it lost, with the evidence and its limits.

  4. Weakest assumption

    The one most likely to be false and how you would find out.

  5. Failure and rollback

    What breaks first and how you get back.

  6. Triggers

    Metric, threshold, window, owner: when the decision reopens.

Anatomy of a usable trigger Table
PartExampleMissing it means
Metricp95 latency of the embedding callNobody knows what to watch
Threshold800 msEvery wobble is an argument
Windowseven consecutive daysIt fires on noise
Owneron-call engineer for retrievalIt never fires
Actionroute to the fallback, open a superseding recordIt fires and nothing happens
  • The Assumption register (capability S19): statement, evidence, confidence, owner, impact if wrong, how to validate, revisit date. Revise entries; never overwrite them.
  • The Decision record (ADR) (S05, extending Nygard's short format of context, decision and consequences): context, options with evidence, decision, consequences, a measurable Revisit trigger.
  • The link: each decision lists its assumptions. A failed assumption reopens its decisions, a passed date queues a review, a decision with none is untestable.
  • Six moves fill six minutes: decision and consumer; constraints and success criterion; strongest alternative; weakest assumption; failure mode and rollback; triggers.
  • Under challenge, check the register, apply the trigger, and supersede with a new record if the evidence moved. Scenarios 008, 011 and 012 practise this.

Assumptions

  • You kept some entries in the Decision log and Assumption register in Weeks 1-11; entries you write today are labelled "recorded late" with today's date.

  • Numbers in a decision come from your own runs or from inputs labelled hypothetical; neither is presented as a measurement of a real organisation.

  • A decision is defended against the evidence available now; evidence that arrives later may legitimately change it.

Sign in to record this block.

Application and workplace decision · 10 min

  • Pick one decision from your Decision log, or write one now marked "recorded late".
  • (a) List its two to four assumptions with confidence, owner and revisit date.
  • (b) Write the strongest alternative and the evidence for and against it.
  • (c) Write the triggers as metric, threshold, window and owner.
  • (d) Say the six moves aloud against a timer, then answer from the register: "your key assumption failed; what do you do?"
  • Then run the code: it audits a toy log of four decisions and four assumptions and prints what to reopen, review, watch or distrust.

Python

from datetime import date

TODAY = date(2026, 10, 11)
# Toy log (synthetic). Assumption id -> (statement, confidence, revisit date, status)
assumptions = {
    "A1": ("Revenue-management run happens 4 times a day", "high", date(2026, 12, 1), "holds"),
    "A2": ("The strongest feature changes slower than 6 hours", "medium", date(2026, 10, 1), "holds"),
    "A3": ("Hosted embedding API p95 latency stays under 800 ms", "low", date(2026, 11, 1), "failed"),
    "A4": ("Held-out queries match how agents phrase questions", "medium", date(2026, 11, 15), "holds"),
}
# Decision id -> (title, assumption ids it rests on)
decisions = {
    "D1": ("Serve no-show scores in batch, 4 runs a day", ["A1", "A2"]),
    "D2": ("Use hosted embeddings for retrieval", ["A3", "A4"]),
    "D3": ("Keep BM25 as the fallback retriever", ["A4"]),
    "D4": ("Retrain monthly on a fixed calendar", []),
}
reopen = []
for did, (title, ids) in decisions.items():
    flags = ["UNTESTABLE: no recorded assumption"] if not ids else []
    for a in ids:
        _, confidence, due, status = assumptions[a]
        if status == "failed":
            flags.append(f"REOPEN: {a} failed")
            reopen.append(did)
        elif due <= TODAY:
            flags.append(f"REVIEW: {a} was due {due}")
        elif confidence == "low":
            flags.append(f"WATCH: {a} has low confidence")
    print(f"{did} {title}: {'; '.join(flags) or 'ok'}")
print("decisions to reopen:", reopen)

This code is executed in the repository's checks (pnpm lesson:py / lesson:sql). It is not run on this site; copy it into your notebook.

Sign in to record this block.

Mini-assessment · 8 min

Your first answer is your evidence. You can re-answer later in practice mode; that is labelled practice.

Question 1 · easy

Which element makes a decision record something you can revisit, rather than a description of what was decided?
Options

How confident are you? (optional, used for calibration)

Question 2 · medium

Your log has five decisions and four assumptions. D1 relies on A1 and A2, D2 on A2, D3 on A3, D4 on A2 and A4, and D5 on A4. Assumption A2 is marked failed today; the others hold. How many decisions go to the reopen queue?
decisions

How confident are you? (optional, used for calibration)

Question 3 · hard

Your ADR chose batch scoring four times a day because the consumer acts four times a day, with the trigger "reconsider when the consuming system becomes event-driven or the hourly-versus-batch replay gap exceeds 0.010 log loss". In review, a product manager says the consuming system "will probably go event-driven in the first quarter". What is the best response?
Options

How confident are you? (optional, used for calibration)

Question 4 · medium

Your log has decision D4, "retrain monthly on a fixed calendar", with no assumption attached. A reviewer asks, "What would make you retrain earlier, or later?" What is the defect in the log, and what do you do?
Options

How confident are you? (optional, used for calibration)

Question 5 · hard

Your ADR chose hosted embeddings with the trigger "p95 above 800 ms for seven consecutive days". The vendor's p95 was above 800 ms on five of the last seven days; on the other two (a public holiday and a weekend) traffic was low and p95 was 700 ms. The assumption is registered with low confidence and a revisit date three weeks away. What is the best action?
Options

How confident are you? (optional, used for calibration)

Sign in to complete lessons and keep your progress.

Technical depth · optional · 36 min in total

Open only what you need. Nothing here is required to complete the day; the assessment covers the core above.

The method: availability of a chain of components6 minSeries and parallel availability derived from the multiplication rule, a four-link service worked by hand, and where independence breaks.
Availability of a chain: one component, in series, in parallel Formula
A=MTBFMTBF+MTTR,Aseries=∏iAi,Aparallel=1−∏i(1−Ai)A = \frac{\text{MTBF}}{\text{MTBF} + \text{MTTR}}, \qquad A_{\text{series}} = \prod_{i} A_i, \qquad A_{\text{parallel}} = 1 - \prod_{i} (1 - A_i)
AA
share of time a component works; 99.9% over 30 days (43,200 minutes) means 43.2 minutes down
MTBF, MTTR\text{MTBF},\ \text{MTTR}
mean time between failures and mean time to repair: faster repair raises A as much as rarer failure
∏iAi\prod_i A_i
series: a request needs every link, so the probabilities that each works multiply; the chain is worse than its worst link
1−∏i(1−Ai)1 - \prod_i (1 - A_i)
parallel: the step works if any copy works, so the probabilities that each fails multiply
independence\text{independence}
both products assume components fail independently; a shared cluster, region or vendor breaks that

Derived from the multiplication rule for independent events. The request path of an ML or GenAI service is a series chain; a fallback is a parallel branch.

The request path is a series chain; a fallback adds one parallel branch Diagram
API gateway99.95%Embedding APIhosted; 99.5%: theweakest linkVector index99.9%LLM API99.7%Answer99.05% in series; 99.65%with fallbackBM25 fallback99.9%: a parallel branchembedsearchcontextgenerateon failurepassages
  1. API gateway99.95%
    • on failure: to BM25 fallback
  2. Embedding APIhosted; 99.5%: the weakest link
    • search: to Vector index
  3. BM25 fallback99.9%: a parallel branch
    • passages: to LLM API
  4. Vector index99.9%
  5. LLM API99.7%
  6. Answer99.05% in series; 99.65% with fallback

A request needs every link, so availabilities multiply: 409 minutes down per 30 days in series, 151 with the fallback around retrieval. The same arithmetic covers a card-authorisation path, a telecom fault-detection pipeline or a hospital triage-score service; log each link's assumed availability.

Worked by hand: an insurer's policy assistant (hypothetical availabilities) Table
Part of the request pathAvailabilityDown per 30 daysHow it is computed
API gateway99.95%21.6 min0.0005 x 43,200
Hosted embedding API99.5%216 min0.005 x 43,200: the weakest link
Vector index99.9%43.2 min0.001 x 43,200
LLM API99.7%129.6 min0.003 x 43,200
Whole chain in series99.05%409 min (6.8 h)0.9995 x 0.995 x 0.999 x 0.997
Retrieval with a BM25 fallback (99.9%) in parallel99.9994%0.3 min1 - (1 - 0.995 x 0.999)(1 - 0.999)
Whole chain with the fallback99.65%151 min0.9995 x 0.999994 x 0.997: now the LLM API dominates
Fallback on the vector index's own cluster99.55%194 minone cluster outage takes both branches: retrieval = 0.999

Redundancy on the weakest link cut downtime from 409 to 151 minutes, and the binding constraint moved to the next link. A fallback that shares a dependency with what it backs up adds less than the formula promises. Recomputed in Python.

The fallback cuts 258 minutes; sharing a cluster gives 43 back Bridge
  1. Whole chain in series409 min
  2. BM25 fallback around retrievalindependent: retrieval 99.9994%−258 min
  3. Fallback on the index's clusterone outage takes both branches+43 min
  4. Shared-cluster fallback194 min

Downtime per 30 days from the worked table. The independent fallback's 151 minutes is the running total after the first step; now the LLM API dominates.

A fallback that shares a dependency adds less than the formula promises Comparison
CriterionIndependent fallbackFallback on the index's cluster
Runs onIts own cluster, region and vendor accountThe vector index's own cluster
Retrieval availability99.9994%99.9%: one cluster outage takes both branches
Chain downtime per 30 days151 min194 min
How you find outList each branch's dependencies side by sideA game day that takes the shared cluster down

Shared clusters, regions, vendor accounts or config pushes make failures correlated, and the parallel formula then overstates the fallback. Any shared line in the dependency list is a common-cause failure: test the fallback for real, not on paper.

Error budgets: deciding in advance how much failure is acceptable5 minThe budget and burn-rate formulas, the multi-window alert arithmetic, and how a burn-rate alert is a pre-registered trigger.
Error budget and burn rate: a trigger with arithmetic behind it Formula
B=(1−SLO)⋅T,r=bad fraction observed1−SLO,share of B used=r⋅tTB = (1 - \text{SLO})\cdot T, \qquad r = \frac{\text{bad fraction observed}}{1 - \text{SLO}}, \qquad \text{share of } B \text{ used} = r \cdot \frac{t}{T}
SLO\text{SLO}
service-level objective: the share of good events promised, e.g. 99.9% of requests answered under the latency limit
BB
error budget: the bad events allowed in the window T; 0.1% of 30 days is 43.2 minutes, or 1 in 1,000 requests
rr
burn rate: how many times faster than allowed the budget is being spent; r = 1 spends exactly B over T
t/Tt / T
the alert window as a share of the SLO window: one hour of a 720-hour month is 1/720

A budget turns "is this bad enough to act?" into a number agreed in advance, the same discipline as a written reconsideration trigger in the decision log.

Burn-rate alerts for a 99.9% SLO over 30 days (SRE workbook example) Table
AlertBurn rate and windowBudget used when it firesBudget gone at that rate in
Page14.4x over 1 hour14.4 x 1 / 720 = 2%720 / 14.4 = 50 hours
Page6x over 6 hours6 x 6 / 720 = 5%120 hours (5 days)
Ticket1x over 3 days1 x 72 / 720 = 10%30 days: on track to just meet the SLO

Each alert also requires a short confirmation window of about 1/12 of its long window to exceed the same rate, so it stops firing soon after recovery. Example values from the Google SRE workbook; the arithmetic is the method.

A page, computed: fraud scoring at 99.9% under 150 ms (hypothetical) Sequence
  1. The budget

    1 - 0.999 = 0.1% of authorisation calls may be slow over 30 days (720 hours).

  2. One bad hour

    1.44% of calls were slow in the last hour.

  3. Burn rate

    1.44 / 0.1 = 14.4 times faster than allowed.

  4. Budget used

    14.4 x 1 / 720 = 2% of the month's budget in one hour: the page fires.

Why a burn-rate alert requires two windows Comparison
CriterionLong window onlyShort window onlyBoth required
A one-minute blipIgnoredPages someone on noiseIgnored
After the service recoversKeeps firing for hoursStops quicklyStops within the short window

The same trade-off as the trigger window in the decision log, with the numbers made explicit.

The budget also orders the work Diagram
Budget leftthis window?Ship model changesthe risk is affordableReliability workfirstuntil the burn raterecoversyesno: spent
  1. Budget left this window?
    • yes: to Ship model changes
  2. Reliability work firstuntil the burn rate recovers
  3. Ship model changesthe risk is affordable

A decision rule agreed before the incident, not during it.

Little's law: why latency triggers are capacity assumptionsadvanced6 minLittle's law, the single-queue latency formulas, a worked utilisation table, a sizing example and a simulation that checks both.
Little's law, and what a single queue adds to it Formula
L=λWM/M/1:ρ=λμ,W=1μ−λ,L=ρ1−ρ,W95=ln⁡20μ−λL = \lambda W \qquad \text{M/M/1:}\quad \rho = \frac{\lambda}{\mu},\quad W = \frac{1}{\mu - \lambda},\quad L = \frac{\rho}{1 - \rho},\quad W_{95} = \frac{\ln 20}{\mu - \lambda}
L=λWL = \lambda W
mean requests in the system = arrival rate x mean time in system; any stable system, any distributions, long-run averages
μ\mu
service rate of one server or replica (requests per second it can finish)
ρ\rho
utilisation: the share of time the server is busy; must stay below 1 or the queue grows without bound
W=1/(μ−λ)W = 1/(\mu - \lambda)
M/M/1 (Poisson arrivals, exponential service, one FIFO server): latency depends on the spare capacity, not on load alone
W95W_{95}
time in an M/M/1 system is exponential with rate mu - lambda, so its 95th percentile is ln 20 / (mu - lambda), about 3 / (mu - lambda)

Little's law is an identity about averages; use it for sizing. The M/M/1 formulas are a model of the latency curve's shape: several replicas, batching, long-tailed prompts and retries (extra arrivals exactly when the system is slow) push the real tail above them, while Little's law still holds.

Worked by hand: one replica finishing 10 requests per second Table
Arrivals per secondUtilisationMean time in systemIn system: lambda x Wp95 time in system
50.501 / 5 = 0.20 s1.03.00 / 5 = 0.60 s
70.701 / 3 = 0.33 s2.31.00 s
80.801 / 2 = 0.50 s4.01.50 s
90.901 / 1 = 1.00 s9.03.00 s
9.50.951 / 0.5 = 2.00 s19.05.99 s

Check: rho / (1 - rho) gives the same L as lambda x W in every row. From 80% to 90% busy the mean doubles. A p95 under 800 ms needs mu - lambda above ln 20 / 0.8 = 3.74, so arrivals under 6.26 per second: utilisation below 63%.

A latency trigger is a capacity assumption: one queue, simulated Illustration
Latency explodes long before 100% busy0 s1 s2 s3 s30%50%70%90%p95 trigger 800 ms63%utilisation rho = lambda / mu (mu = 10 requests/s)
  • mean: simulated (dots), 1/(μ − λ) (line)
  • p95: simulated (squares), ln 20/(μ − λ) (line)

Simulated mean time in system: 0.50 s at 80% busy, 1.01 s at 90% (formula 0.50 and 1.00). With this service rate a p95 of 800 ms only holds below about 63% utilisation.

Little's law on the same runs: L = λW0044881212L = λW↑ requests found in the system (measured)λ x mean time in system (measured)

At 90% busy the arrivals found 9.1 requests in the system on average; λ x W gives 9.1. Little's law needs no distributional assumption, only a stable system.

Seeded simulation of one server with Poisson arrivals and exponential service times (an M/M/1 queue), 40,000 requests per utilisation after a warm-up of 2,000. Real services have several replicas and heavier tails; the shape, not the numbers, carries over.

Left, simulated mean and p95 latency against the queue formulas as utilisation rises. Right, Little's law checked on the same simulated runs.

Sizing by hand: a claims-triage LLM endpoint (hypothetical) Sequence
  1. Little's law

    12 requests per second x 2.5 s in system = 30 requests in flight.

  2. At 100% busy

    Replicas with 4 concurrent slots: 30 / 4 = 7.5 replicas, with no headroom at all.

  3. At a 70% target

    30 / (4 x 0.7) = 10.7, rounded up to 11 replicas.

  4. Register the inputs

    Arrival rate and time in system are dated assumptions; a new traffic forecast reruns this arithmetic.

A latency assumption silently rests on a traffic assumption Diagram
Trafficforecast λan assumption witha dateUtilisation ρ = λ /μμ = 10 requests/s perreplicap95 = ln 20 / (μ −λ)the M/M/1 shapep95 above 800ms?Reopen the decisionbefore users feel itKeep; date the checkdivide by μM/M/1compareyesno
  1. Traffic forecast λan assumption with a date
  2. Utilisation ρ = λ / μμ = 10 requests/s per replica
    • M/M/1: to p95 = ln 20 / (μ − λ)
  3. Reopen the decisionbefore users feel it
  4. p95 = ln 20 / (μ − λ)the M/M/1 shape
  5. p95 above 800 ms?
    • yes: back to Reopen the decision
  6. Keep; date the check

Register both assumptions, with the utilisation at which the latency one fails (63% for one replica finishing 10 requests per second), so a traffic forecast can reopen the decision before users feel it.

Answering a challenge with the trigger4 minThe decision path when someone says your key assumption is failing, and how the window length trades false alarms for delay.
Answering "your key assumption is failing" Diagram
Written triggermet?Act and supersedefallback now; new datedrecordEvidencenearing it?Update registerlower confidence,earlier datePrepare cheaplyshadow-test the fallbackKeep and note ityesnoyesthenno
  1. Written trigger met?
    • yes: to Act and supersede
  2. Evidence nearing it?
    • yes: to Update register
  3. Keep and note it
  4. Act and supersedefallback now; new dated record
  5. Update registerlower confidence, earlier date
  6. Prepare cheaplyshadow-test the fallback

The trigger was agreed before the data arrived. Evidence that approaches it changes confidence, dates and preparation, not the criterion.

The window is what separates a trigger from an alarm on noise Illustration
Daily p95 latency (ms) and two triggers6008001000real shift1-day window fires7-day window holds (circle) →060120180240day

A 1-day window fires 15 times before anything changed. A 7-day window never fires on noise and holds 16 days after the real shift.

Window length: false firings against delay05101515162331415678910consecutive days above 800 ms required
  • false firings before the shift (count)
  • days from the shift until the trigger holds

The window is a dial between two costs: arguments and cut-overs on noise, or days of a real breach before anyone acts. Choose it from the cost of each, then write it down before the data arrive.

Seeded synthetic series: 240 days, level 720 ms then 860 ms from day 170, autocorrelated noise. Shows the trade-off, not any vendor's behaviour.

Same threshold, ten window lengths, computed on one synthetic latency series with a real shift.

What to register, and why it drifts3 minAssumptions worth tracking and the ML-specific debts that move them.
Which assumptions deserve a register entry Overview
  • Under several decisions

    One failure reopens many; monitor it first.

  • Cheap to check

    A query or a dashboard settles it; no reason not to.

  • Expensive if wrong

    Serving mode, vendor, data contract: costly to reverse.

  • Feedback loops

    Once a model changes what is observed next, retraining data describe its own effect.

Why assumptions drift in ML systems Overview
  • Entanglement

    Changing one input changes everything downstream of it.

  • Hidden feedback loops

    Model outputs shape the next training data.

  • Configuration debt

    Thresholds and settings scattered without owners.

Categories from Sculley et al. (2015). Each is a reason to register and date the assumption it threatens.

Hidden feedback loopsadvanced4 minHow a model's own policy corrupts its next training set, and the fixes that must exist before the loop starts.
A hidden feedback loop, simulated Illustration
Round 0: before reminders0.00.20.4risk bucket, low to high

5 of 8 buckets reminded.

Round 1: retrained0.00.20.4risk bucket, low to high

2 of 8 buckets reminded.

Round 2: retrained0.00.20.4risk bucket, low to high

4 of 8 buckets reminded.

9.0%11.0%13.0%15.0%012345Realised no-show rate per round, same customers every roundround 0, own outcomes: 9.9%round 1, own outcomes: 13.1%round 2, own outcomes: 10.7%round 3, own outcomes: 14.9%round 4, own outcomes: 10.7%round 5, own outcomes: 14.9%round 0, holdout: 12.0%round 1, holdout: 12.6%round 2, holdout: 12.6%round 3, holdout: 12.6%round 4, holdout: 13.4%round 5, holdout: 12.6%own outcomesholdoutretraining roundno-show rate

Retraining on its own outcomes, the realised no-show rate swings by 5.0 points between rounds (9.9% to 14.9%). Retraining on a never-reminded random holdout keeps it near 12.6%.

Seeded simulation: 8 risk buckets, 300 customers each per round, a reminder at learned risk 0.15 or above, and an assumed reminder effect of 50%; the fix keeps 30% of each bucket as a never-reminded holdout. Bars: learned risk (orange = reminded); black ticks: true risk; dashed: the threshold.

Reminders lower no-shows, as for clinic appointments or service bookings: the model's own policy changes the outcomes it is retrained on. Nothing about the customers changes between rounds.

Breaking a feedback loop: four fixes, what each costs and buys Comparison
CriterionUntouched random holdoutthe policy never acts on itLog action and its probabilitywith every predictionAction as a model inputAssumption with a trigger
What it costsSome value lost on the holdoutLogging and storage disciplineNeeds variation in who received itOne register entry
What it buysUnbiased outcomes to retrain on and to measure the policy effectOff-policy estimates of what another policy would have doneSeparates risk from the effect of the interventionSomeone notices when the training data start describing the model

The first two need to exist before the loop starts; outcomes not logged under a known policy cannot be recovered later.

The same chain for a GenAI service2 minOne testable assumption per link, from corpus freshness to price.
Assumptions per link of a GenAI service Stack
  1. CorpusDocuments are current; versions and permissions are tracked.
  2. QueriesHeld-out queries look like how agents actually ask.
  3. RetrievalThe embedding provider's latency and model stay within bounds.
  4. GenerationThe pinned model version exists until its announced retirement.
  5. CostPrices, cache hit rate and escalation share stay near the measured values.

Write each one as a statement a number could contradict, with a validation method and a date.

Which decisions deserve the full treatment2 minCost to reverse against uncertainty, and where a design review should spend its time.
Which decisions earn a record, a register entry and a trigger Map

Experiment

Try it behind a flag and measure; a short note is enough.

Full record and triggers

Serving mode, vendor, data contract: assumptions, alternatives, a measured fallback.

Just decide

Library minor versions, plot styles; no record.

Record once

Expensive but well understood: write the decision and revisit on a date.

Practitioner heuristic. The top-right cell is where a design review spends its six minutes: serving mode, vendor, data contract, evaluation design. A removed component gets a record too, with the trigger that would bring it back.

Superseding a record2 minHow a decision changes without rewriting history.
Superseding a decision record Sequence
  1. Keep the old record

    Status becomes superseded, with a link to its successor.

  2. State the new evidence

    What moved, when, measured how.

  3. Compare again

    The same alternatives on current evidence, plus any new one.

  4. New triggers

    Fresh metric, threshold, window and owner; a better criterion applies from now on.

What a staff-level defence addsadvanced2 minBlast radius, a priced reversal path, named loops, a pre-mortem and what was deliberately not built.
What a staff-level design defence sounds like Overview
  • Blast radius

    "A2 sits under two decisions, so it is monitored first."

  • Reversal path priced

    "Falling back costs the measured quality gap; staying costs p95 above 800 ms."

  • Loops named

    "The collections model decides who gets a call; the next model learns from that, so we keep a holdout."

  • Pre-mortem

    "If this fails in six months, the likeliest cause is the vendor latency assumption."

  • What we chose not to build

    "No online scoring yet; the trigger that would bring it back is written down."

Memory anchor

Defend the process, not the verdict: every decision names its assumptions and the number that would reopen it.

Useful for

  • Preparing a design review, an architecture-board presentation or a senior system-design interview.

  • Turning a pile of project notes into a log that tells the next owner when to reopen a choice.

  • Deciding which assumptions deserve monitoring because they sit under several decisions.

Not suitable for

  • Replacing evaluation; a well-kept log records that a decision was made on evidence but does not create the evidence.

  • Small, reversible choices; a full record for every library version produces noise and hides the decisions that matter.

Traps

Defending the decision instead of the process

Under challenge you argue that the original choice was right and ignore that its assumption has moved, so the reviewer concludes you cannot revise.

How to avoid it

Open the register, state what changed, apply the trigger and supersede the record if the evidence says so.

Assumptions that cannot be tested

"Users will like it" or "traffic will be moderate" sit in the log with no metric, owner or date, so nothing can ever mark them failed.

How to avoid it

Write each assumption as a statement a number could contradict, with a validation method and a date.

Senior reasoning

A senior designs for reversal. They keep a measured fallback so one failed assumption does not take down the system, they write triggers as metric, threshold, window and owner, and they treat a review challenge as a free validation of the weakest assumption. They also prune: a log of every choice is as useless as no log, so they record the decisions that cost most to reverse.

Interview question

"Describe an architecture decision you made, what you gave up, and what would make you reverse it." How would you structure the answer?

Recall cards scheduled after completion

  • What are the elements of an assumption register entry and of a decision record?
  • How do the two logs tell you what to reopen?

Reviewed on a 1 · 3 · 7 · 14 · 30-day schedule; a miss returns the card to 1 day.

References

Optional sections

Deep dive · 10 min

Writing a trigger. A usable trigger names four things: the metric (p95 latency of the embedding call), the threshold (800 ms), the window (seven consecutive days) and the owner (the on-call engineer for retrieval). Add the action it fires (route to fallback and open a superseding record). Triggers without a window fire on noise; triggers without an owner never fire.

Which assumptions to register. Prefer assumptions that sit under several decisions (A2 in question q-058-2), that are cheap to check, or that are expensive to be wrong about. For learned systems add the hidden feedback loop: once a model's output changes what is observed next, the data it retrains on describe the model's own effect.

The same chain for a GenAI service. Decision and consumer, corpus and its manifest, retrieval and its held-out evaluation, context assembly, untrusted-input boundary, generation mode with an evidence label, per-request cost and latency, monitoring (abstention rate, citation share, incidents), rollback to the previous index or prompt version. Each link has its own assumptions: corpus freshness, query distribution, provider behaviour and price.

Interview practice · 10 min

You are told in review that your design is over-engineered for a team of two. How do you respond?

Content version 6 · last verified 2026-10-18