Solution Architecture  /  Front of House  /  Delivery Retrospective

The front desk ran on six tabs.
One screen replaced them.

Seven weeks after the first commit, a cultural venue's front desk opens one screen in the morning: the day's programme, live visitor numbers, the runsheet, VIP visits, checklists and the answers staff keep being asked for, on the desk tablets and on staff phones. This is how it was built, the one rule that shaped all of it, the day it broke, and what is not proven yet.

BEFORE · SIX PLACES TO LOOK AFTER · ONE SCREEN public programme site ops board · runsheet ops board · school groups sensor vendor console service desk · facilities FAQs, contacts, VIP list read server-side, at request time · the app stores none of it FRONT DESK · 9:41 · 24° Events Visitors Runsheet VIPs Map FAQs Checklist Collection +5 more

A visitor asks what is on at two o'clock. The person behind the desk opens the public website on a shared tablet, scrolls to today, and gets an answer. The next visitor asks whether the school group in the main hall will be gone by eleven. That answer is on an operations board, in a different app, behind a different sign-in. Then the duty manager wants to know how full the building is, which lives in a vendor console nobody on the desk has open. None of these questions is hard. Every one of them costs a tab switch while someone waits.

The front-of-house app exists to remove that cost. It is a single web app, installed on the desk tablets as a full-screen app and available on staff phones, that puts every one of those answers behind one home screen of large tiles. This is a look back at its first seven weeks, from an empty repository to a production app the desk runs a day on.


01 · THE PROBLEMEvery answer existed. None of them were at the desk.

The important thing about the front desk's problem is that nothing was missing. The programme was published. The runsheet was maintained, every day, by the people who run the building. School group arrivals, duty managers, facility issues, opening hours: all of it was written down somewhere by someone whose job it was. Visitor counts were measured by sensors at every entry. The data was good. It was simply in six places, and the desk is the one place in the building where nobody has time to look in six places.

The second thing is who uses it and how. The desk is a standing job. The device is a shared tablet, touched by fingers, often mid-conversation. Anything designed for a mouse, a dropdown, a date picker, a small link, fails there in a way it never fails at a desk with a keyboard. And the desk has no patience for a login screen two minutes before the doors open on a Saturday.

02 · THE WRONG TURNThe two obvious designs, and why neither survives the floor

There are two designs everyone reaches for first, and both are reasonable for about a day.

The first is a portal of frames: a home screen of tiles where each tile opens the existing tool inside the app. It is cheap and it ships in a week. It also breaks on contact with reality. A service desk that sends a frame-ancestors policy renders blank when embedded, and redirects to its own login anyway. Several tools only make sense signed in. The ones that do embed still look like six different products wearing one frame, with six different idioms for touch. The app does embed where it genuinely works, the wayfinding map and the collection site, and opens the rest in a new tab. But a portal is a bookmark bar, not an answer.

The second is a front-of-house database: copy the programme, the runsheet and the counts into a store the app owns, and render from that. It feels robust. In practice it creates a second source of truth for every field, a sync job per source to keep it honest, and a new question nobody had before, which is whose copy is right when a duty manager edits the runsheet at 8:50 and the desk still shows the old one at 9:05.

The trap in both

Both designs make the app responsible for something it should not own. The portal makes it own the look of other people's products. The database makes it own other people's data. A front-of-house screen has no business being the system of record for anything. The moment it is, the desk is reading a rumour about the runsheet rather than the runsheet.

03 · THE KEY INSIGHTOwn nothing. Read the source. Decide at the door.

The design that held is a short set of refusals.

The app stores no business data. Every screen reads its source of record through a small route on the app's server, at request time, and shapes the answer for a finger. The runsheet is the operations board. What's On is the content API. Visitors are the sensors. When a duty manager edits the runsheet, the desk sees it on the next read, because there is nothing in between to go stale. Credentials for those sources live on the server and never reach the tablet.

Who gets in is decided once, at the door, per request. On the venue's own network, the desk walks straight in: no login, no sign-in library even loaded. Anywhere else, a staff member signs in with their work account and the server verifies that identity on every request. There is no client secret anywhere in the design to leak or rotate. And if the door is misconfigured, it stays shut: in test and production, nobody gets in until the rules are set.

The UI is designed for fingers first, always. Every control is at least 44 pixels. There is not one native dropdown or date picker in the app; both were replaced with chips, sheets and a touch calendar. Every screen is checked at phone width and tablet width, the way a finger would use it.

The desk does not need another system. It needs every other system to be one tap away and never wrong.


04 · THE ARCHITECTUREA door, a thin app, and the systems that already knew the answers

Read the picture left to right. Two kinds of device arrive at one door. Behind the door is a thin Next.js app on Vercel whose only job is to read and shape. To its right are the systems that own the data, reached through the integration gateway where one exists and directly where it does not. Nothing on the right knows the app exists, which is the point.

WHO THE DOOR + THE APP SOURCES OF RECORD DESK TABLETS venue network · installed app STAFF PHONES anywhere · work sign-in THE DOOR · every request on the venue network → straight in elsewhere → verified identity token otherwise → sign-in page or 401 · fails closed THE APP · Next.js on Vercel 13 touch screens · installed as an app server routes hold every credential typed contracts on every response last answer drawn at once on reload no database · no copy of anyone's data WARMER · scheduled every 10 min through opening hours INTEGRATION GATEWAY managed API gateway · one facade → serverless function → ops boards runsheet · FAQs · school groups previews read dev · production reads prod CONTENT API events · programmes · collection · VIPs PEOPLE-COUNTING SENSORS read directly · counters found by name WEATHER API STAFF IDENTITY PROVIDER OBSERVABILITY · errors, traces signature, issuer, audience, expiry THE APP OWNS NO DATA · EVERY SCREEN READS ITS SOURCE · THE DOOR DECIDES ONCE
The settled architecture. The left half is the app, the right half is everyone else. No arrow on the right points back at the app: the sources are read, never written, and never asked to change for it. The dashed identity line is the only coupling to the identity provider, and it is a signature check, not a session the app has to keep. The warmer exists only so the first person at the desk in the morning never pays for a cold gateway.

Thirteen screens, and where each one reads from

This is the controlling table for the app. Every screen has exactly one source, and when a new screen is proposed, the first question is which system already owns its data.

Events · What's OnThe chosen day as a two-row board on the tablet, a list on the phone. Six months searchable, with quick picks for the busiest dates ahead.
CONTENT API
VisitorsLive occupancy for each entry and the whole building.
SENSORS
RunsheetOpening hours, duty managers, school groups side by side, security and facility issues below.
OPS BOARDS
VIPsToday's VIP visits.
CONTENT API
FAQsApproved answers, only those active today.
OPS BOARDS
CollectionBrowse objects to answer a visitor's question.
CONTENT API
MapWayfinding and a space finder.
EMBEDDED
Checklist · Contacts · Forms · QR codesOpening and closing checklists, tap-to-call contacts, staff forms, codes visitors can scan.
APP CONFIG
Residents & StudentsAn occupancy view shared with the analytics app.
EMBEDDED
FacilitiesReport an issue. The service desk refuses framing, so this one opens in a new tab.
NEW TAB

Two rules came out of building that table. A tile that cannot be embedded properly opens in a new tab rather than being embedded badly. And the home screen itself carries the weather, the time and a "Did you know" fact from the daily runsheet, because the desk glances at it far more often than it taps it.


05 · THE DETAIL THAT SEPARATES REAL FROM DEMOWhat has to be true before the desk can trust it

A demo of this app takes an afternoon. What took the seven weeks were four things that never appear in a demo.

The first is the door. The desk tablets must never stop for a login, and a staff phone on the train must never get in without one. The hosting platform sees the address traffic leaves the venue from, after the network translates it, so the trusted network is defined by the venue's outbound addresses, not the ones inside the building. Off the network, the work sign-in runs in the browser, the resulting token is posted once to the server, checked for the provider's signature, the right issuer, this app as its audience and its expiry, and kept in a cookie the page's scripts cannot read. While the tab is open it is renewed before it expires. Close the tab on a shared device and the sign-in ends with it.

A desk tablet on the venue network STRAIGHT IN

The common path, and it should be invisible. The request comes from the venue's network, the door opens, and the sign-in library is never even loaded.

door: trusted network · no sign-in · no token

A staff phone anywhere else VERIFIED

One work sign-in, then every request carries a token the server checks on its own. No client secret exists to leak, because the app is registered as a single-page app and only verifies what the provider signed.

door: signature · issuer · audience · expiry

Nothing configured, or anything wrong SHUT

The path that protects the venue. In test and production, an unset or broken rule means nobody gets in, rather than everybody. A page sends the visitor to sign in; an API answers 401, or 403 for a signed-in person outside the allowed groups.

door: fails closed · never fails open

The second is names, not numbers, for the sensors. The obvious way to read the visitor counters is by their numeric IDs. The trouble with an ID is that it silently changes when someone replaces a sensor or rebuilds a zone in the vendor console, and the desk would show a confident wrong number. The app asks the vendor for its list of counters, finds each one by the name the building team gives it, caches that list for ten minutes, and falls back to the known IDs only if the lookup fails. A renamed sensor is a configuration change, not a deploy.

The third is the cold morning. The first person at the desk at opening is the person least able to wait for a gateway that has been idle all night. A scheduled warmer reads every data route every ten minutes through opening hours. A reload draws the last answer instantly and refreshes behind it. A tapped tile shows that it landed even before its data arrives. None of that is visible when it works, which is the only acceptable state for it.

The fourth is contracts that fail the build instead of the desk. The content API client is generated from its published spec, and every response from every source is checked against a shared schema before a screen sees it. When an upstream shape changes, a test goes red in a pull request, not a tile at 10am.

06 · THE DAY IT BROKEEvery call failing in under a tenth of a second

Two weeks before release, every data call the app made began to fail at once, and fast. A fast failure is a useful clue: it means nothing is being slow, something is missing.

Incident · integration gateway

The serverless function behind the gateway had been replaced by a new one during an infrastructure change. The gateway still pointed at the old host, and that host no longer existed. The app's own code had not changed; a separate build that had not been touched in days failed identically, which settled it. We traced the chain end to end, wrote down the exact fix for the infrastructure owner, and the API was rebuilt on the new function that afternoon with its own key. Every endpoint was back the same day.

The lasting change was to how we test. The team now replays the exact upstream call a screen makes before trusting the screen, and each environment's gateway is written down in one place: local development and previews read the dev gateway, production reads the prod gateway, and the two are never crossed. The next time the chain breaks, it is a five-minute diagnosis rather than an afternoon.

A fast failure is a gift. Something slow is struggling. Something fast is gone.


07 · THE SEVEN WEEKSThree engineers, six releases

43pull requests merged
6production releases
13screens for the desk
54automated test files
~47klines added, 462 files
milestoneproduction releaseincident
  1. WEEK 1Repository created. First detail pages and overview.
  2. WEEK 3Workspace, error reporting and the first promotion to production.
  3. WEEK 3Dark tablet redesign, the content API client, weather, live visitors and the phone layout. Then the gateway outage, found and fixed the same day, and the runsheet and FAQs go live.
  4. WEEK 4Icon set, cache warming, instant reloads, app icon, UI theme. Promoted to production.
  5. WEEK 5VIPs from the content API, previews pinned to dev and production to prod, configurable home tiles, production hardening.
  6. WEEK 6Event filters and a content API update.
  7. WEEK 7Facility issue groups, form titles, then staff sign-in and sensors by name. Released after review.

All of it went through the same path: a test branch, a reviewed pull request, then production. Nobody pushes straight to production. Type checks, the full test suite and a build run locally in Docker before a change reaches CI, which keeps the hosted build bill small without skipping a single gate.

Three people did this. One engineer started the project and laid the visual groundwork: the first pages, the type, the icon set. Another led the content API integration, events, VIPs, facilities and forms, and owned every promotion to production. I led the kiosk experience, the gateway integration and the incident, the runsheet, caching, the visitor sensors and staff sign-in.

08 · THE PROOFWhat the design guarantees, and what is not proven

What the app commits to, in a form that can be checked rather than asserted.

And the edges, because a write-up that does not name its unknowns should not be trusted.

← All writing rummanahmed.com Share on LinkedIn