System Design / Article

Requirements and constraints: define an honest reservation

Write a design brief for NightLens, including one-owner slot allocation, explicit SLO denominators, maintenance closures, and an offline-booking trade-off.

Shared case study: NightLens telescope reservations

A reservation system can be quick, available, and wrong. If two astronomy clubs receive confirmations for the same telescope at the same time, a beautiful dashboard does not rescue the evening. System design begins by deciding which outcomes the system must protect, which delays people can tolerate, and which promises the team can afford to operate.

This lesson turns a short request into a usable design brief for NightLens, a fictional service for reserving fixed, thirty-minute telescope sessions at community observatories. All workloads, budgets, and targets in this series are teaching assumptions, not measurements or claims about a deployed service.

Start with a decision the system must make

The initial request says: “Let members find a telescope, book a session, and receive a reminder.” That is a feature list. It does not tell us whether a booking can wait for email, whether an observatory can accept bookings while disconnected, or whether a search result is a promise of availability.

Write one concrete interaction instead. At 18:00, Ada and Ben both select telescope T7 for Saturday at 21:00. Both press Reserve. Exactly one may receive a confirmed booking. The other receives a clear conflict and can choose another session. A delayed email must not change who owns the session.

This gives the first invariant: at most one active reservation may occupy a telescope's predefined session slot. It also gives a boundary: a search result is an invitation to try, while a committed reservation is the authority. “Never disappoint a member” is impossible; “never confirm conflicting reservations for the same fixed slot” is a testable rule.

Fixed slots matter. Two arbitrary intervals can overlap without sharing a start time. This series deliberately uses predefined slots, so one slot key can identify the contested resource. If the product later accepts arbitrary start times and durations, the storage rule must change rather than quietly reusing the fixed-slot assumption.

Turn adjectives into a small requirements ledger

For the first release, assume twelve observatories, twenty-four telescopes, and one operations team. Members browse a rolling fourteen-day schedule. New sessions open together every Monday. The service coordinates reservations; it never operates telescope motors, shutters, or other physical safety equipment.

  • Correctness: one active reservation per slot; a maintenance closure cannot race past a new booking without a defined outcome.
  • Booking responsiveness: at least 99% of eligible booking attempts receive a committed booking or an expected business rejection within 800 ms at the API boundary over a rolling thirty-day window.
  • Discovery responsiveness: at least 99% of eligible schedule reads return a successful response within 300 ms over the same window.
  • Freshness: public availability is advisory and may lag a committed change by up to five seconds during healthy operation; once that age cannot be established, label it unverified or hide availability.
  • Delivery: confirmation is immediately visible in the account; email normally follows within two minutes and is monitored separately.
  • Operation: begin with one write region and a single application deployment, plus a worker process. The team cannot support several independent regional writers yet.

These are proposed objectives, not warranties. Before launch, the owner must agree on the workload envelope, measurement points, and what happens outside them. Google's Implementing SLOs recommends choosing indicators that reflect user experience and using objectives to guide engineering priorities. Here, that means measuring whether a member gets an honest booking outcome, not merely whether the process is running.

Define the denominator before choosing a target

An “eligible booking attempt” is an authenticated, syntactically valid request for a known session from a member allowed to reserve. Count an expected slot conflict as a valid outcome if it is returned within the deadline. Count a timeout, an internal error, and a service overload rejection as unsuccessful for this user-facing indicator. Count client mistakes and authorization failures separately, with their own abuse and usability metrics.

Otherwise the service could appear healthy by returning immediate overload errors to everybody. A fast answer is useful only if its meaning matches the user's task. Also record confirmed bookings, ordinary conflicts, and overload rejections separately: a healthy correctness mechanism may produce many conflicts during a popular release.

The API timing begins when the gateway receives the request and ends when it finishes the response. Browser rendering and the member's network need an additional end-to-end measure. Do not call an API objective a page-load guarantee. For the fictional 800 ms objective, write a latency histogram with outcome labels and record requests that time out at the gateway; missing slow observations would bias the report.

Make the failure preference explicit

Suppose the database cannot be reached but a cache still says T7 is free. There are two tempting options: confirm from the cache and reconcile later, or refuse the booking until the authoritative write path recovers. NightLens chooses refusal. A lost reservation opportunity is easier to explain than two confirmed groups arriving at one telescope.

That choice permits degraded discovery from sufficiently recent public data, but it does not permit an offline component to invent booking ownership. The user interface must say “availability may have changed” when appropriate and only display “confirmed” after a durable commit. Email is downstream of that decision.

A separate policy handles maintenance. Staff may close a slot even if a booking already exists; the existing reservation remains recorded and is flagged for staff resolution. Closure prevents new reservations. This preserves history and avoids pretending that a safety-related closure can always be rejected merely because somebody booked earlier.

Notice the compromise: members lose write availability during a database outage. That is intentional. We have chosen a failure preference for this operation, rather than assigning a vague “high availability” label to the whole system.

Separate constraints, assumptions, and open questions

A constraint is a condition the design must respect: the current team operates one write region. An assumption is something we use provisionally: six thousand visitors may arrive during the first ninety seconds of a release. An open question is unresolved: how should a booking affected by a weather closure be rescheduled?

Keep these categories visible in the decision record. Attach an owner and a verification action to each assumption. A release traffic forecast belongs to the product owner and becomes a load-test profile. A five-second freshness allowance belongs to the scheduling team and needs a usability check. A retention period belongs to the data owner and must be agreed before storing member history indefinitely.

For now, exclude payments, a waiting-list lottery, offline booking, arbitrary intervals, and automatic equipment control. These are deliberate scope decisions, not claims that the system could never support them. Each would introduce different invariants or operational obligations.

Let the requirements shape the first component map

Start with a browser, an API application, a transactional database, and a delivery worker. The API owns booking decisions. The database owns slot state, active reservations, request deduplication, and durable notification work. The worker delivers messages after commits. A public availability cache is an optimization introduced later; it never owns a reservation.

Keep these responsibilities distinct even if they share one repository and deployment. A box on a diagram is a responsibility boundary, not automatically a separate service. Fieldnote's module contracts inside one backend show how to make that distinction concrete. Splitting delivery from the synchronous booking path prevents an email provider from becoming part of the booking confirmation deadline. It also introduces delayed delivery, retries, and a backlog that somebody must monitor.

The map distinguishes synchronous booking calls from the worker's later polling of committed outbox work. If a booking sequence waits for an email call before the confirmation response, it contradicts the brief. That contradiction is useful: the model has exposed a decision before anyone wrote an integration.

Live model · Architecture view

Find the authority behind a confirmed reservation

The NightLens model separates public discovery from authoritative booking. Trace the browser through the API to the slot transaction, then follow committed outbox work to delivery. Email cannot determine whether a reservation exists.

Trace two members asking for the same slot, then remove the email provider from the success path. Explain why only one member can own the slot and why delayed email does not reverse that outcome. Open the public diagram ↗
Explore scenarios in this model

These links open the same public model in a selected view. They do not change its source.

Open this viewer on its own ↗ if the frame is unavailable. The explanation remains readable without the viewer.

Exploration is local to your viewer. Reload or Reset view returns to the source layout.

Practice: change one constraint and defend the consequence

A partner observatory now asks for reservations during a six-hour network outage. Write a three-sentence decision before adding an “offline” box:

  1. State whether offline staff may allocate sessions that the central service could also allocate.
  2. Describe the member-visible result if connectivity returns with conflicting claims.
  3. Name the invariant or scope boundary you must revise.

A defensible first answer is to preassign a disjoint block of slots to the observatory before disconnection and remove them from central availability. Offline staff can allocate only that block. This trades flexibility for exclusive ownership. It also requires a transfer protocol: central booking must stop before the block is handed over, and returning the block must reconcile its allocations before central booking resumes.

If that transfer is too expensive for the first release, decline offline booking while allowing staff to view a downloaded schedule. “We will merge later” is incomplete until it states what happens to the losing reservation. The exercise succeeds when you can identify the sacrificed capability and explain it in user terms.

Your next design artifact

Produce a one-page brief containing the booking invariant, two measured objectives with explicit denominators, a failure preference, a scope exclusion, and three assumptions with owners. Review it with someone who can change product scope. Then use Latency, throughput, and capacity: size the Monday release to translate the assumed release into resource demand.

The shared read-only model lets you trace the ownership decision. Select One slot, two members, and a staff closure to compare the competing booking and closure branches, then identify why delayed email does not reverse a committed booking. The text and exercise remain usable without the viewer.

Live model · Sequence view

Test one slot against competing decisions

This saved scenario turns the fixed-slot ownership promise into behavior: two members and a staff closure compete around the authoritative reservation decision. A committed booking and its outbox work are separate from eventual email delivery.

Follow the competing decisions for the same slot. Find the authoritative commit and explain why an email delay does not transfer or revoke ownership. Open the public diagram ↗

Open this viewer on its own ↗ if the frame is unavailable. The explanation remains readable without the viewer.