Enterprise Architecture Review Checklist
TechLearningPro's campus-import feature shipped after a review that was a slide reading. The deck had twelve services, a bus, and a green "scalable" badge. Two weeks later a regist
Enterprise Architecture Review Checklist
Advanced System Design & Software Architecture · Lesson 69 · Architecture Practice, Interviews and Capstone
Lesson Overview
TechLearningPro's campus-import feature shipped after a review that was a slide reading. The deck had twelve services, a bus, and a green "scalable" badge. Two weeks later a registrar at Northwind uploaded a roster whose tenant_id field was Eastbridge's identifier, because the body was trusted. The job wrote Eastbridge learners into a shared table that had no row-level security. The same request path sent a welcome email synchronously, so a slow mail provider made the upload time out, the registrar retried, and a second welcome went out. Support logs contained full date-of-birth fields. Finance had no estimate of the object storage the import dumps would keep. The architecture was not missing a tool. It was missing a review that asked for evidence.
This lesson is that review. Lessons 61 through 65 taught decision records, SLOs, threat modeling, disaster recovery, and capacity. Lessons 66 through 68 taught you to produce a design under a clock. A design review is the meeting where that packet is accepted, accepted with conditions, or sent back. The checklist covers requirements traceability, boundaries and data ownership, security, reliability, cost, operations, compliance, and tenancy. Each item asks for evidence in the packet, not for a feeling about the diagram.
You will run the checklist against a deliberately weak campus-import design and against a cohort-analytics cache that dropped the tenant from the key. Lesson 70's capstone is graded with this list. A packet that cannot answer a blocker item is not ready for a review meeting, and a review meeting that cannot say "blocker" is a status update.
Learning Objectives
By the end of this lesson you will be able to:
- Explain what a design review decides, who must be in the room, and what packet is required before the meeting starts.
- Classify findings as blocker, major, minor, or question, and attach each finding to evidence.
- Trace a functional requirement and a quality scenario to a component, a data owner, and a test.
- Check service boundaries for a single writer, a named protocol, and a failure the caller can see.
- Apply a security pass that includes trust boundaries, sensitive data, authz, and abuse cases.
- Check reliability against the SLO: timeouts, retries, idempotency, degradation, and the dependency that can break the target.
- Check cost against a labeled capacity model, including the line item the design forgot.
- Check operations: the symptom you would page on, the trace a request carries, and the rollback.
- Check compliance as engineering controls: retention, access logs, export, and what the service must not log.
- Check tenancy: identity source, predicate on every read and write, cache keys, jobs, and noisy neighbors.
- Facilitate a sixty-to-ninety-minute review that ends in approve, approve with conditions, or rework.
- Use the same list on your own capstone before anyone else sees it.
Prerequisites
- Lesson 10, ADRs. The review reads decisions. It does not invent a new documentation system.
- Lesson 60, multi-tenant SaaS. Pool, bridge, and silo, and the rule that a body-supplied tenant id is not authority. The checklist asks you to show that rule.
- Lesson 61, staff decision-making. The review is one of the forums that lesson describes.
- Lesson 62, SLOs. A review without a user-facing target cannot judge reliability.
- Lesson 63, threat modeling. This lesson asks whether a model exists and whether the design reflects it. It does not re-teach the method.
- Lesson 64, disaster recovery, and lesson 65, capacity review. RPO, RTO, and the bottleneck sentence should already be in the packet.
- Lesson 44, service security, and lesson 48, migration. The checklist points at those packets when the finding is about tokens or cutover.
- Comfort with PostgreSQL row-level security and with HTTP status codes. The worked review uses both as evidence, not as new theory.
Detailed Core Concepts
What the meeting is for
A design review accepts risk on purpose. The output is one of three decisions.
- Approve. The packet is sufficient and the residual risks are written down.
- Approve with conditions. Specific findings must be closed before build, or before production, and each condition has an owner and a date.
- Rework. A blocker is open. Building would encode the blocker into schema or contracts that are expensive to undo.
A review that ends in "looks good" with no residual-risk list has not accepted risk. It has declined to look. A review that redesigns the system in the room has also failed. The facilitator parks redesigns as follow-ups. The packet's author owns the next draft.
Roles
| Role | Responsibility in the room | What they do not do |
|---|---|---|
| Author | Walks the packet, answers from evidence | Argue a finding down without a quote from the packet |
| Facilitator | Keeps the checklist order and the clock | Secretly be the approver |
| Reviewer | Files findings with severity and evidence | Design a replacement in the meeting |
| Scribe | Writes findings in the author's words plus the reviewer's | Polish away a blocker |
| Decision owner | States approve, conditions, or rework | Delegate the word to the quietest person after the meeting |
On a small team one person may facilitate and review. They may not also be the only author. Someone who did not write the packet must be able to say "blocker."
The packet that earns a meeting
Refuse the meeting if these are missing. A slide with boxes is not a packet.
- Users, workflows in scope, and an exclusion list.
- At least one quality scenario with a target, in the form lesson 62 uses.
- A capacity sketch with labeled assumptions and a bottleneck sentence, in the form lesson 65 uses.
- A context or container diagram with owners and protocols.
- The API or event contracts the diagram depends on, including the failure status.
- Data ownership: system of record, derived views, retention.
- ADRs for the decisions that would hurt if reversed, using lesson 10's shape.
- A threat sketch: assets, trust boundaries, and the abuses you designed against. Lesson 63's depth for anything that holds grades, payments, or tokens.
- Rollout and rollback, even if the rollout is "new service, no traffic yet."
- Tenancy statement when the product is TechLearningPro Cloud: pool, bridge, or silo, and where
tenant_idcomes from.
If item 7 is missing, the review can still happen as a working session to discover decisions. It cannot approve.
Severity
| Severity | Meaning | Example |
|---|---|---|
| Blocker | Shipping encodes a data leak, a double charge, an unbounded outage, or an irreversible migration | Body-trusted tenant id on a shared table |
| Major | The design misses its own SLO, cost ceiling, or operability bar, and the fix changes the design | Synchronous mail provider on the upload path; no timeout |
| Minor | A real gap with a local fix that does not change boundaries | A diagram arrow without a protocol, once the protocol is agreed |
| Question | The reviewer does not understand, and the author may close it with a sentence | "Which service owns the roster row?" |
Severity is about consequence and reversibility, not about how embarrassed the author should feel. A missing trace id is often minor until the SLO is already burning and nobody can see why, in which case the same gap is major. Write the consequence in the finding.
Checklist: requirements traceability
- Every in-scope workflow names a caller, a success result, and one failure result.
- Every exclusion is written, so a later diagram cannot quietly add it.
- Each quality scenario names a stimulus, an environment, a response, and a measure. "Fast" and "highly available" do not pass.
- Each scenario points at the component that provides the response.
- A test or a production signal will show the measure. A target with no signal is a wish.
- Conflicting scenarios are explicit. A synchronous welcome mail and a two-second upload SLO are a conflict, not two bullets.
Checklist: boundaries and data ownership
- Each stored fact has one writer. Caches and projections are marked derived and have a staleness promise or a rebuild.
- Each arrow has a protocol: HTTPS, SQL, a named event, a file.
- Sync calls have a timeout, a client budget, and the user-visible result when the budget is spent.
- Retries are idempotent or they are not retries. Lesson 37 is the test.
- No service reads another service's tables. If it must during a migration, the ADR has an end date.
- The system of record during a migration is named. Two writers of the same roster are a blocker unless the ADR explains the reconciliation window.
Checklist: security
Use the threat sketch as the index. A useful pass, aligned with the spirit of STRIDE and with lesson 63, asks concrete questions:
- Spoofing. How does the service know the tenant and the user? A host and a token that must match, as lesson 60 requires, pass. A JSON field passes only as a bug.
- Tampering. Where are integrity checks: signed webhooks, checksums on import files, conditional updates for state?
- Repudiation. Which actions append an audit record that the actor cannot edit: grade change, roster delete, allowlist edit?
- Information disclosure. What is logged, what is cached, what crosses a tenant, what is in a URL?
- Denial of service. What is the limit on upload size, row count, and request rate? What sheds first?
- Elevation. Which role may import, which role may read grades, and where is that check enforced? The UI is not the enforcement point.
Secrets stay in a secret store. The packet says which system. It does not paste a sample key. Provider calls use the provider's published API and a configured endpoint. The review rejects any design that depends on an undocumented private API you cannot link.
Checklist: reliability
- The SLO's error budget owner is named, even if the name is a team.
- For each dependency, the packet says: timeout, retry, circuit or bulkhead if a retry storm is plausible, and whether the user path fails or degrades. Lesson 41 is the vocabulary.
- The failure of the largest dependency is walked once. "The database is down" must have a user-visible sentence.
- Data recovery: RPO and RTO from lesson 64, and whether a restore violates an idempotency assumption (a replayed capture, a replayed import).
- Backpressure exists for bulk work. An import does not share an unbounded thread pool with sign-in.
- There is one drill or test that would have caught the worst bug: a second import, a killed worker mid-batch, a duplicate webhook.
Checklist: cost
- The capacity model has units and assumptions. Peak factor is labeled as a guess or a measurement.
- The model names the first saturated resource and the second.
- Cost lines follow the model: compute, database, storage, egress, provider fees, log retention. A line with no driver is optional. A driver with no line is a finding.
- Per-tenant cost is visible when tenants share a pool. One campus must not be an unbounded storage or support cost.
- The design says what you turn off or sample if the bill crosses a threshold: raw click logs, debug traces, unused renditions.
- Numbers that claim to be current prices are suspect. The review wants the formula. Plug in the provider's price list at build time.
Checklist: operations
- One page-worthy symptom per critical workflow, with the query or metric name you would actually run.
- Logs are structured, correlated by a trace id, and free of secrets and of excess personal data. OpenTelemetry, or an equivalent trace context you can name, crosses the import API and the worker.
- Dashboards exist for the SLO, the queue age, and the dependency timeouts. A dashboard of CPU alone fails this item.
- Rollback is a flag, a previous deploy, or a compensating job, and it is tested in the sense that someone has written the steps.
- A runbook says who is paged and what they do in the first fifteen minutes. "Restart it" is not a runbook for a double import.
- Config and feature flags have an owner. A tenant allowlist on one engineer's laptop is an operational and a security finding.
Checklist: compliance
This list is an engineering control list. It is not legal advice. Counsel names the regime. Engineering shows the mechanism.
- Data classes are labeled: public catalog, account data, education records such as roster and grades, payment tokens, staff audit data.
- Each class has a retention period and a deletion mechanism. A nightly job that is "best effort" needs a correctness story the way expiry did for links in lesson 49: read-time enforcement, or a proven sweep.
- Access to education records and payment data is authorized and audited.
- Export and correction paths exist if the product promised them. A manual SQL script only the DBA knows is a finding.
- Logs, analytics events, and error reports are checked against the data class. A stack trace that includes a roster row fails.
- Subprocessors, such as the mail provider and the payment provider, are named. The packet says what data they receive. They receive a destination address or a token, not the whole learner row, unless the ADR says why.
- Residency: if a tenant requires a region, the design is bridge or silo for that tenant's data, not a comment in the contract.
Checklist: tenancy
Lesson 60 is the baseline. The review asks you to show it.
- Tenant identity comes from the host and the token. The body is not authority.
- Every table that holds tenant data has a tenant predicate enforced in the database, such as PostgreSQL row-level security with
SET LOCAL, not only in a repository you hope everyone calls. - Cache keys, search documents, object prefixes, and queue payloads include the tenant.
- A job that iterates tenants sets the tenant context per unit of work and cannot carry it across tenants by accident.
- Quotas and fair scheduling exist for imports, exports, and notifications. Sign-in for other tenants has a separate budget.
- A test fails if a query without a tenant predicate returns a row. The test is in the packet or the finding is major.
- The isolation tier, pool or bridge or silo, matches the contract. A silent upgrade to a dedicated database is a cost and ops finding, not a favor.
How a finding is written
textID: IMP-04Severity: BlockerChecklist: TenancyEvidence: API spec section 3 accepts tenantId in the JSON body and thesequence diagram uses that value in the INSERT. No RLS policyis attached to roster_row.Consequence: A caller who can reach the endpoint can write anothertenant's roster.Condition to close: Tenant comes only from the authenticated context.RLS is enabled. A test inserts as tenant A andshows tenant B's row is invisible and not writable.
The author may disagree by pointing at a page the reviewer missed. The author may not close a blocker by promising to "be careful in code review."
Architecture and Design Diagrams
The review is a funnel. Missing packet items stop the meeting. Findings are classified. The decision owner speaks the outcome. Conditions return to the author as tracked work.
mermaidflowchart TDPacket[Packet complete?] -->|no| Hold[Hold the meeting]Packet -->|yes| Walk[Walk checklist in order]Walk --> Find[Write findings with evidence]Find --> Sev{Any blocker?}Sev -->|yes| Rework[Decision: rework]Sev -->|no| Maj{Majors that change the design before build?}Maj -->|yes| Cond[Decision: approve with conditions]Maj -->|no| Ok[Decision: approve with residual risks]Rework --> Author[Author updates packet]Cond --> AuthorAuthor --> Packet
The sample under review is Campus Import, a worker beside the existing learning platform. The diagram is what the author brought. The flaws are the point of the walkthrough.
mermaidflowchart LRRegistrar[Registrar browser]API[Import API]DB[(Shared roster tables)]Mail[Mail provider]Obj[(Import files)]Registrar -->|JSON includes tenantId| APIAPI --> DBAPI -->|sync send welcome| MailAPI --> Obj
The review's target shape, after conditions, moves mail off the request and takes tenant id from the token. Row-level security sits in the database, which the container diagram should show as a policy, not as a hope.
mermaidflowchart LRRegistrar[Registrar browser]Edge[Edge: host plus token]API[Import API]DB[(Roster tables with RLS)]Q[Welcome queue]Worker[Mail worker]Mail[Mail provider]Registrar --> EdgeEdge -->|tenant from token| APIAPI -->|SET LOCAL tenant| DBAPI --> QQ --> WorkerWorker --> Mail
A second sequence is the one the checklist forces you to draw: the retry. If it is absent, file a finding before debating the logo on the boxes.
mermaidsequenceDiagramparticipant Reg as Registrarparticipant API as Import APIparticipant DB as Roster DBparticipant Mail as Mail providerReg->>API: POST /imports Idempotency-KeyAPI->>DB: insert rows for token tenantAPI->>Mail: send welcomeMail-->>API: timeoutAPI-->>Reg: 503Reg->>API: POST same Idempotency-KeyAPI-->>Reg: original result, no second insert
That sequence is the corrected design. The author's original sequence timed out inside the mail call and inserted again on retry. The difference is the review.
Technical Explanation
Facilitation in ninety minutes
A ninety-minute review of a single feature, not of the whole platform, fits. A review of "the entire microservices rewrite" does not. Split the rewrite into slices with their own packets.
Suggested clock:
| Minutes | Activity |
|---|---|
| 0–5 | Decision owner states the decision wanted today and the slice in scope |
| 5–15 | Author reads the scope sentence, the SLO, the bottleneck, and the ADR titles |
| 15–30 | Boundaries, data ownership, contracts |
| 30–45 | Security and tenancy |
| 45–60 | Reliability and disaster recovery |
| 60–75 | Cost and operations |
| 75–85 | Compliance, if the data class needs it |
| 85–90 | Read findings back. Decision owner speaks the outcome |
Stop a section when the findings are written, even if people have more design ideas. Ideas go to the scribe as questions. The author may adopt them in the next packet. The room does not vote on a new architecture.
Pre-read is mandatory. Reviewers who arrive without the packet get the first ten minutes in silence to read the scope, the SLO, and the ADRs. They do not get to filibuster.
Evidence, not taste
"I would have used events" is taste until it is tied to a missed SLO, a coupling that blocks the migration, or a failure the sync call cannot express. The facilitator asks, "Which checklist row does that violate, and where is it in the packet?" If the answer is "nowhere, I prefer events," the scribe records a question, not a major.
Framework choice is in scope only when it changes operability, cost, or hiring in a way the ADR ignored. A review that becomes a language argument has lost the packet.
Tenancy evidence you can check quickly
For a pooled schema, the packet should include the policy and the session pattern. This is ordinary PostgreSQL, the same mechanism as lesson 60.
sqlALTER TABLE roster_row ENABLE ROW LEVEL SECURITY;CREATE POLICY roster_tenant ON roster_rowUSING (tenant_id = current_setting('app.tenant_id', true))WITH CHECK (tenant_id = current_setting('app.tenant_id', true));
The application sets the value inside the transaction.
sqlBEGIN;SELECT set_config('app.tenant_id', :tenantFromToken, true);-- inserts and selects for this transaction onlyCOMMIT;
set_config with the local flag, or SET LOCAL, lasts for the transaction. A session-level SET on a pooled connection leaks the previous tenant to the next borrower. If the packet says SET without LOCAL, the tenancy finding is a blocker, not a style note. If the packet has no policy at all, the finding is the one in IMP-04.
Cache keys look like roster:{tenantId}:{importId}. A key of roster:{importId} is a major or a blocker depending on whether import ids are globally unguessable and whether the cache sits in front of another tenant's read. Prefer the tenant in the key anyway. Unguessable ids are not an authorization model.
Reliability evidence
Map each sync dependency to a number. Import API calls the database with a statement timeout. It does not call the mail provider. The worker calls the mail provider with a timeout, a bounded retry, and an idempotency key so a retried welcome is one welcome. The provider's published API supplies that key's behavior. You specify the header or body field the provider documents. You do not invent a field they do not have. If their public API has no idempotency, the worker stores a send ledger keyed by import row and does not call again after ACCEPTED or UNKNOWN. That is the dispatcher rule from lesson 67, applied to a review.
The SLO might be: 95% of roster imports under 50,000 rows complete within ten minutes, excluding the mail provider's delivery time. Mail is outside the SLO because it was removed from the path. If mail stays on the path, the SLO must include the provider, and the provider's outage burns the error budget. That is often the argument that makes the queue a checklist finding rather than a preference.
Cost evidence
Worked example, assumptions labeled, prices deliberately rounded so you replace them with a current price list before anyone puts them in a forecast.
Assumptions: 2,000 tenants, 50 imports a day on a peak campus, average file 20 MB, retained 30 days, other campuses quiet. Peak campus storage is 50 * 20 MB * 30 ≈ 30 GB. Fleet-wide, if 5% of tenants are peak-like, storage is tens of terabytes only if you have mis-estimated the long tail. Write the long tail down. A model that multiplies every tenant by the peak campus will "prove" a cost crisis and then lose credibility.
The forgotten line is usually logs. A debug log of every roster row at a few kilobytes each, retained 30 days, can exceed the file storage. The checklist asks for the log line. If the author says "logging is free," file a major under cost and a major under compliance, because those rows are education records.
Operations evidence
Name the traces. A request carries W3C traceparent, which OpenTelemetry propagates, from the edge to the import transaction and onto the queue message. The worker continues the trace. The page is "import jobs older than 15 minutes" and "RLS denial spikes," not "CPU high." Rollback of a bad import is a compensating job that deletes rows by import_id inside the tenant context, and it is idempotent. A rollback that is "restore the whole database" is a major finding if a single campus import can force it.
Compliance evidence for a roster
Label the file as education-record data. Retention 30 days for the raw file, longer for the roster rows that are the live system of record, with the longer period coming from the product's records policy. Access is the registrar role for that tenant, checked in the service and reinforced by RLS. The audit row records actor, tenant, import id, and counts. It does not record every learner's date of birth a second time. Logs include import id and counts, not the CSV body. The mail provider receives an email address and a template id for a welcome the tenant opted into. It does not receive the CSV.
If counsel has said a tenant's data must remain in a specific region, this feature cannot be the first one to copy that tenant's roster into the shared region's bucket. That is a blocker under compliance and tenancy together.
Conditions that are actually closable
A condition has a test. "Add security" is not a condition. "Reject a body tenant id that differs from the token, with a test that would fail today" is a condition. "Improve scalability" is not a condition. "Remove the mail call from the request path and show the upload SLO still holds when the provider is black-holed" is a condition. The decision owner reads conditions aloud. The author repeats the test. If they cannot, the condition is not written yet.
Reviewing a review
Once a quarter, sample three packets. Count findings that were taste, findings that were real and missed, and conditions that closed without the test. The checklist is working when blockers found in production decrease and when authors bring RLS and idempotency keys without being asked. The checklist is failing when every review approves, or when every review reworks on preference. Publish the counts. Lesson 61's decision practice includes this kind of operational honesty.
Practical Implementation or Design Walkthrough
The packet under review is Campus Import, brought by the enrollment team. You are the reviewer. The facilitator is keeping the clock. The decision owner is the director of engineering for the learning platform.
The packet, short enough to fail
Scope: a registrar uploads a CSV. The API creates or updates learners and enrollments and sends a welcome email. Scale: "Northwind has 40,000 students." SLO: none. Diagram: the first mermaid in the previous section, which they drew in a slide tool. API: POST /imports with tenantId, file URL, and no idempotency key. Data: inserts into roster_row in the shared database. ADR: none. Threat model: "we use HTTPS." Tenancy: "the client sends the tenant." Migration: "replace the old CSV email-to-script next Monday for all campuses."
You refuse nothing of the meeting only because leadership already booked it. You start by listing missing packet items as findings so the room sees the tax.
Walk the list
Traceability. Workflow "upload roster" has a success path and no failure path. Quality scenario absent. Finding IMP-01, major: no SLO, so reliability cannot be judged. Condition: write one scenario, including what the registrar sees when the file is invalid.
Boundaries. The API writes the database and calls mail. Timeout on mail unspecified. Retry behavior unspecified. Finding IMP-02, major: sync mail couples the provider's outage to the import SLO you have not written, and a retry can duplicate learners if the insert is not idempotent. You will split this into IMP-02a blocker once you confirm duplicates, which the author confirms: "we insert and if it times out they run it again." Blocker IMP-02a: duplicate learners on retry. Condition: Idempotency-Key and a unique natural key per tenant, such as (tenant_id, external_student_id).
Security. HTTPS is transport, not a threat model. The file URL is caller-supplied. Finding IMP-03, blocker: the API will fetch a URL the registrar supplies, which is server-side request forgery against cloud metadata and internal hosts. Condition: the browser uploads to a short-lived signed PUT you issued, or you accept only object keys in your bucket under that tenant's prefix. No arbitrary fetch.
Tenancy. IMP-04, blocker, as written in the finding template above.
Reliability. No statement timeout. A 40,000-row import holds a request thread. Finding IMP-05, major: move the parse to a worker; the API returns 202 and an import id. RPO: the author says "the database is replicated." They cannot say lag. Finding IMP-06, minor if the platform standard from lesson 64 already covers this database, question if it does not. You record a question, then promote it if nobody can point at the platform standard.
Cost. No model. You do the napkin math in the room only to show the method, then assign the model as a condition. IMP-07, major: capacity and log volume unknown. The author mentioned debug logging of rejected rows "for a while."
Operations. No symptom, no trace, rollback is "restore." IMP-08, major: compensating delete by import id, and a page on queue age. Rollback by full restore is rejected for a shared database.
Compliance. CSV in logs is likely. IMP-09, blocker: education-record contents must not be written to application logs or to a third-party error tracker. Condition: an explicit denylist in the logger test, plus retention for the raw file.
Decision. Rework. Four blockers. The facilitator does not let the room redesign the worker framework. The scribe reads IMP-02a, IMP-03, IMP-04, and IMP-09. The decision owner says rework and names a new review when those four tests exist. Majors remain mandatory before production and may ride along in the same packet.
What the next packet must contain
A scope sentence, one SLO, the corrected diagram, the idempotency rule, the signed upload, the RLS snippet and the cross-tenant test name, the log denylist test name, and an ADR titled "Roster import is asynchronous and tenant-scoped." That ADR's rejected option is the synchronous mail call, and the reopen condition is "never," because the SLO and the provider outage are incompatible. Reopen conditions can be "do not reopen" when the alternative violates an invariant. Say that plainly.
Time spent
The walk took seventy minutes because the packet was thin. A complete packet moves faster, because you are checking evidence instead of discovering that the evidence is absent. Thin packets are the expensive ones. Holding the meeting until the packet exists is cheaper. The director's mistake was booking the review to "create urgency." The urgency created a slide, not a design.
Enterprise Real-World Scenarios
Scenario 1: the import review as a production postmortem
Imagine the review above did not happen and the feature shipped. The incident is the one in the overview: Eastbridge rows written from a Northwind session, duplicate welcomes, dates of birth in a log archive. The postmortem's corrective actions are exactly the conditions IMP-02a, IMP-03, IMP-04, and IMP-09. The cost is a tenant notification, a purge job, a mail-provider cleanup, and a trust problem with registrars.
The checklist's value is that those actions were knowable from the packet. A postmortem that discovers a new class of bug is a learning. A postmortem that discovers a blocker the checklist already names is a skipped review. TechLearningPro's engineering staff meeting treats a skipped blocker as a process incident of its own. The facilitator who said "they seem senior, approve it" is in that meeting. Seniority is not evidence. RLS is evidence.
The same scenario scales down. A team of four still has an author and a reviewer. They can run the checklist in forty-five minutes on a one-page packet. They cannot skip tenancy because the team is small. A small team sharing a database with the rest of the platform can leak a large tenant. Size of team is not size of blast radius.
Scenario 2: cohort analytics cache
A second review, six months later, looks at a read model for cohort dashboards. The packet is better. SLO: dashboard p95 under 400 ms. Capacity: the heavy campus, 200 dashboards open, is named. ADR: a cache in front of a nightly aggregate. Threat model: a short one, identifying the aggregate as an education record. The room is ready to approve.
A reviewer asks to see a cache key. The key is cohort-dashboard:{cohortId}. Cohort ids are UUIDs. The author says they are unguessable. The reviewer asks what happens when a support tool or a bug writes a key, and what happens when a cohort is moved between tenants during a merger of two campuses, which the product has done once. The aggregate payload includes grades. IMP-14, major, promoted to blocker because the data class is grades: the key must include tenantId, and the read must check that the token's tenant owns that cohort even on a hit. A cache hit is not an authorization decision.
The second finding is noisy neighbors. The nightly job rebuilds every tenant in one queue. Northwind's rebuild runs for two hours and delays Eastbridge's morning. IMP-15, major: fair per-tenant scheduling, or a reserved concurrency for small tenants. This is lesson 60's queue rule. The packet cited the lesson and did not apply it. Citing the lesson is not evidence.
Decision: approve with conditions. No other blockers. Ship the API behind a flag to one tenant after IMP-14's test is green. IMP-15 closes before the nightly job is enabled for all tenants. Residual risk, written down: the aggregate is up to a day stale, and the product copy says so. That residual is an accepted product decision, which is what residual risk should look like.
What both scenarios demand of the reviewer
In the import review the author was unprepared and the reviewer had to be willing to file four blockers in front of a director. In the cache review the author was prepared and the reviewer had to be willing to file a blocker against a packet everyone liked. The checklist supports both postures. It does not support "we need to ship" as a severity discount. Shipping changes the condition's due date only when the decision owner accepts a written residual risk. A leak is not a residual risk. A stale dashboard can be.
Best Practices
- Require the packet before the meeting. A missing SLO or a missing owner list is a reason to hold, not a reason to improvise.
- Walk the checklist in a stable order so authors learn what to prepare.
- Write findings with an id, a severity, a checklist row, evidence, a consequence, and a test that closes them.
- Separate taste from evidence. Preference without a violated row is a question.
- Treat body-supplied tenant identity, missing idempotency on unsafe retries, arbitrary URL fetches, and education records in logs as default blockers until the packet shows otherwise.
- Put tenant id in every cache key and queue payload. Check authorization on the hit.
- Ask for one user-visible failure per dependency and one page-worthy symptom per workflow.
- Tie cost findings to a formula. Reject "logging is free."
- End with an outcome spoken by the decision owner. Send the finding list the same day.
- Sample past reviews. Count taste findings and missed blockers.
- Use this list on the capstone before the faculty review. Self-filed blockers are a sign of quality, not of weakness.
- Keep the slice small. Review an import. Do not review the platform's entire future.
Common Mistakes and Anti-Patterns
The slide reading. The author narrates boxes for an hour. No findings are written. It happens because conflict feels impolite. The consequence is the import incident. The facilitator who does not ask for a failure mode is part of the design.
Severity inflation. Every nit is a blocker, so authors stop bringing reviews early. It happens when reviewers are scored on finding count. The consequence is hidden work. Reserve blocker for leaks, duplicate money or duplicate records, unsafe fetches, and irreversible cuts. Put the rest at major or minor.
Severity deflation. Real blockers become "follow-ups" because the date is close. It happens under launch pressure. The consequence is a production tenant leak with a ticket number. The decision owner may accept residual risk only in writing and only when the consequence is not a privacy or correctness breach.
The redesign salon. The room invents a new event backbone. It happens because reviewers are architects and drawing is pleasant. The consequence is no decision and a packet that mirrors the loudest person. Park alternatives as questions. Let the author return with an ADR if the alternative still matters next week.
Checklist theater. Every box is ticked "yes" with no pointer into the packet. It happens when the list becomes a form. The consequence is false confidence. A tick requires a section id or a test name. Empty evidence is an open finding.
Tenancy by UUID. Unguessable identifiers replace authorization. It happens because it works in demos. The consequence is a cache or a log that becomes a cross-tenant read the moment ids leak or staff tools accept an id. Put the tenant in the key and check the token.
The free log. Debug retention is unbounded and contains the CSV. It happens because logging is someone else's budget. The consequence is a compliance incident and a storage bill. Give logs a data class and a retention period in the same table as the database.
Citing the course instead of the mechanism. "We follow lesson 60" with a body-supplied tenant id. The citation is not the control. The control is the token source, SET LOCAL, and the test.
Troubleshooting and Design Review
This section is the checklist applied to the review process itself.
| Symptom | Likely cause | Corrective action |
|---|---|---|
| Every review approves in one sitting | Findings are being softened, or packets are trivial | Sample blockers from incidents and ask which review saw the packet |
| Reviews always rework | Blockers include taste, or packets are being accepted incomplete | Re-read severity definitions; hold meetings until the packet list is met |
| Authors are surprised by tenancy findings | The checklist was not shared | Publish this list as the pre-read, not as a trap |
| The same major appears in three reviews | Conditions are not gated in the tracker | A condition is a ticket with the test name in the title, closed by the reviewer |
| Meetings run two hours over | The slice is "the platform" | Split the packet; review one workflow |
| Production leaks a tenant the review approved | Evidence was a tick mark | Require the test name for IMP-style tenancy items |
| Cost surprises a month later | The model ignored logs or egress | Add the forgotten line to the required packet |
| On-call cannot follow a failure | The symptom was "high CPU" | Require one symptom per workflow that names the user-visible break |
| Two writers after a migration | The end of the dual-write window was undated | ADR condition: end date and the reader that still uses the old store |
When a review itself is the system under review, the facilitator writes a short retro: three findings, whether the outcome matched the evidence, and one checklist row the room skipped. Skipping compliance because "it is just an internal tool" is the usual miss. Internal tools hold the same roster.
Interview Questions and Answers
Q1. What is the decision a design review is allowed to make?
Approve, approve with conditions, or rework. Approve means the residual risks are written and accepted by the decision owner. Conditions are tests with owners, not themes. Rework means at least one blocker is open, and building would bake that blocker into a contract or a schema. A review that ends in encouragement has not decided. A review that ends in a new architecture drawn by the room has also not decided. The author leaves with a finding list and a spoken outcome.
Q2. What do you refuse to review?
A packet with no scope, no quality target, no data owner, and no decision record. You may hold a working session to discover those, and you label it as a working session so nobody calls the result an approval. Slides of boxes are not a packet. A diagram is one required item among ten. The expensive failure mode is reviewing a thin packet because the calendar said to, then discovering in production that tenant identity was a JSON field.
Q3. How do you tell a blocker from a strong preference?
A blocker has a consequence you can say in one sentence about data leaking across tenants, money or records duplicating, a credential or an internal URL being fetched, or a migration you cannot undo. A preference has a different shape that also works, such as a queue versus a worker table, once timeouts, idempotency, and the SLO are satisfied. If both shapes meet the checklist, record a question or a minor, and let the ADR stand. Reviewers who cannot describe the consequence are holding a preference.
Q4. How do you review tenancy on a pooled database?
Ask where the tenant id comes from. The acceptable answer is the host and the token, matched. Ask how a query is constrained if a developer forgets a WHERE clause. The acceptable answer is row-level security with a transaction-local setting, plus a test that connects as tenant A and fails to read tenant B. Ask about cache keys, object prefixes, and jobs. Each must carry the tenant. A UUID cohort id without a tenant prefix is not enough for grade data. Lesson 60 is the design. The review is the evidence that this feature uses it.
Q5. What reliability evidence do you expect in the packet?
For each synchronous dependency: a timeout, the user-visible result when it fires, and whether a retry can double-apply. For the SLO: a number, an exclusion list, and the signal that measures it. For bulk work: a queue or a worker with backpressure, so sign-in does not share an unbounded pool with a 40,000-row file. For recovery: how a partial import is identified and compensated without restoring every tenant's database. If the author cannot walk the mail provider's timeout, the reliability section is not done, even if replication is described.
Q6. How should cost be reviewed if prices change every quarter?
Review the formula and the drivers, not a screenshot of a price calculator. Drivers include request rate, storage, egress, log volume, and third-party fees. Assumptions are labeled. The first saturated resource is named. The reviewer checks that a data class which is logged has a retention driver in the model. Plug current prices in when the team forecasts. A review that argues about a stale unit price and ignores an unbounded debug log has reviewed the wrong number.
Q7. What belongs in the operations section of a review?
One symptom per critical workflow that a human can page on, correlated traces from the edge to the worker, logs that respect the data class, a rollback that does not restore the world, and a named owner for flags and allowlists. OpenTelemetry's trace context is a sufficient concrete mechanism if the packet says the import message carries it. A vendor dashboard without a symptom is not. The reviewer asks, "It is 02:00 and imports are stuck. What do you open first?" The answer is the section.
Q8. How do you handle compliance without pretending to be counsel?
Ask counsel which regimes and which data classes apply, then show engineering controls: retention, deletion, audit of access, export, log redaction, and the fields a subprocessor receives. For TechLearningPro, roster and grades are treated as education records. The review checks that those fields are not in logs and that a tenant-scoped role is required to read them. The finding is about the missing control, not about a legal conclusion. If a tenant contract requires residency, the design must place that tenant's data in-region. A footnote does not place data.
Q9. How do you facilitate when a senior author disagrees with a blocker?
Return to the evidence line. Read the API field and the insert. Ask what a caller can do with that field. If the author shows a control the reviewer missed, downgrade or withdraw the finding in the scribe's notes, live. If the author says the client would never send the wrong tenant, the blocker stands, because clients are not a control. Disagreement about taste is parked. Disagreement about a leak is not a vote. The decision owner chooses rework or a written acceptance, and acceptance of a cross-tenant write should be rare enough to be memorable.
Q10. How does this checklist relate to a system-design interview?
The interview in lessons 66 and 68 produces a scope sentence, a bottleneck, a contract, an owner, a failure, and a reopen condition in forty-five minutes. The review asks for the same artifacts plus security, cost, operations, compliance, and tenancy, with tests. A candidate who can do the interview and cannot say where tenant id comes from is not yet ready to author a packet. A candidate who files IMP-04 against their own mock is practicing this lesson. Do not try to speak the whole checklist in an interview. Do speak the trust boundary and the single writer. Those two rows are the bridge.
Q11. What does approve-with-conditions look like a week later?
Each condition is a ticket whose title is the test. The reviewer who filed it is the person who closes it, or a delegate who runs the test. The flag stays off, or the traffic stays at one tenant, until the blocker-grade conditions are closed. Majors that were deferred past launch have a date and a residual-risk note the decision owner signed. A week later the scribe's notes match the tracker. If they do not, the review was theater and the next retro starts there.
Q12. How do you review a migration slice differently from a new service?
Add three questions. What is the system of record this week? What still reads the old store, and when does that stop? How does one tenant or one campus roll back without rolling back everyone? Dual-write without an end date is a major. Two writers of roster status with no reconciliation is a blocker. Lesson 48 is the method. The checklist does not need a new section so much as it needs those questions under boundaries and operations. A big-bang cutover of every campus on Monday, with rollback equal to a full restore, fails the migration questions even if the new service is well drawn.
Hands-On Exercises
Exercise 1: review Campus Import
Problem. Using only the weak packet in the walkthrough, write findings IMP-01 through IMP-09 in the finding format. Then write the decision and the list of tests that reopen the review.
Tasks.
- Classify each finding's severity and checklist row.
- Promote at least the four blockers from the walkthrough, with consequences.
- Separate one item you personally dislike that is only a question.
- Draft the ADR title and the rejected alternative for async import.
- Name the cross-tenant test and the log denylist test.
Done when. A classmate can implement the tests without asking you what the finding meant.
Exercise 2: review a cache you liked
Problem. Take a design you have written earlier in the course, preferably the feed timeline or the shortener cache. Run the tenancy, security, and reliability rows. File at least one finding against your own packet.
Tasks.
- Write the cache key and say whether a hit authorizes the caller.
- State the staleness promise the way lesson 68 required for interviews.
- State the log data class.
- Decide approve, conditions, or rework, and record one residual risk.
Done when. The finding would have been fair if someone else had written it about your work.
Evaluation rubric
| Assessment area | Weight | Score 3 means |
|---|---|---|
| Packet judgment | 15% | Missing SLO, owner, or ADR is called out before detailed design argument |
| Finding quality | 25% | Each finding has evidence, consequence, and a closing test |
| Severity discipline | 15% | Blockers are leaks, duplication, unsafe fetch, or irreversible cuts |
| Checklist coverage | 20% | Security, reliability, cost, operations, compliance, and tenancy are each touched |
| Facilitation | 10% | Taste is parked as a question; the outcome is explicit |
| Tenancy and authz | 15% | Token source, RLS or equivalent, and cache keys are checked |
A review that approves the weak import packet caps finding quality at 1. A review that marks every item blocker caps severity discipline at 2.
Lesson Summary
A design review accepts risk in public. The packet must already contain scope, a quality target, capacity, owners, contracts, decisions, a threat sketch, rollout, and a tenancy statement. The meeting walks security, reliability, cost, operations, compliance, and tenancy, and it files findings with evidence and a test. Blockers include a body-supplied tenant on a shared table, a retry that duplicates records, an arbitrary URL fetch, and education records in logs. The outcome is approve, approve with conditions, or rework.
Campus Import fails that bar on purpose, so you can practice writing the findings. Cohort analytics shows that a polished cache still fails if the key omits the tenant and a hit skips authorization. The same list grades the capstone. Bring a packet that can survive your own review before you bring it to anyone else.
Further Reading
- PostgreSQL row security policies — the database control the tenancy row of this checklist expects for a pool.
- W3C Trace Context —
traceparent, the correlation the operations row asks for. - OpenTelemetry documentation — propagation across the API and the worker.
- OWASP Threat Modeling Cheat Sheet — a concise public checklist; lesson 63 remains the course method.
- AWS Well-Architected Framework — a second lens on reliability, cost, and operations. Map it to evidence. Do not tick its pillars from memory.
- Microsoft Azure Architecture Center, multi-tenant guidance — silo, bridge, and pool, as used in lesson 60.
- RFC 9110 — status codes for the corrected import contract, including
202and409. - Lessons 10, 60, 62, 63, 64, and 65 — the packet this review reads and does not replace.