Architecture IV: Deployment

Reminders
- Office hours: on Discord, or by appointment
- DUE TONIGHT, Thu Sep 17: Project Bids
- TODAY: the
cs4535-first-ticketpool is published - DUE Thu Sep 24: Onboarding: Gradebook Column Groups
What Do You Do in the Next 20 Minutes?
January 2023. Two Alaska Airlines jets take off underpowered and scrape the runway on rotation.
A software update to the tool that computes aircraft weight is reporting planes 20,000 to 30,000 lb lighter than they are, so the crews are setting thrust and speed for a plane that doesn't exist. The update had been tested for weeks. Flights are departing right now.
A. Ground the fleet until it's fixed
B. Push a fix to the weight tool
C. Turn off the automatic weight feed; crews request weights by hand
D. Write a load test and re-run the whole suite
They Turned It Off
C, in 20 minutes. Operations switched off the automatic uplink. Crews requested weights manually. "We didn't have the bug anymore."
B, five hours later. The software was permanently repaired that afternoon.
D, afterwards. The update had been tested for weeks. It only failed when many aircraft used the system at once, so a high-demand test was written after the fact.
The best long-term answer was the wrong 20-minute answer. Today is about having a 20-minute answer.
CS 4535: Software Design & Delivery
Architecture IV: Deployment
©2026 Jonathan Bell, CC-BY-SA
Learning Objectives
After this session, you'll be able to:
- Name the down path for a change, and say what it costs to take
- Say what a release is, and where code lives before users get it
- Say why a schema change can't be made atomic against a running client
- Apply the four-step pattern for a safe database migration
- Say what your change does to data that was written while it was live
What Went Right
The bug was real, it was in production, and it had been through weeks of testing. None of that is the interesting part.
There was a way to run the airline without that software.
Crews could request weights by hand. Somebody had built that path, it still worked, and operations could reach for it in twenty minutes.
They didn't need to fix the bug. They needed to make it stop mattering.
45 Minutes, $460 Million
Knight Capital, August 1 2012. New trading code deployed to eight servers.
- Seven took the new code. The eighth failed silently.
- The new code reused an old feature flag. On seven servers it meant the new logic. On the eighth it woke
Power Peg, dead test code left in production since 2003. - Market opened. 45 minutes. 4 million executions, 154 stocks, 397 million shares.
- Roughly $460 million. The firm never recovered.
There was no manual mode. No switch. No way to run the business with that software turned off. So it ran for 45 minutes.
Three Prices for a Down Path
Free. Flip a switch, everything else keeps working. Seconds. A feature flag.
Not free, but survivable. Degraded mode. Slower, more manual, somebody's day is worse, and the business keeps running. Alaska's manual weights.
There isn't one. Nothing to reach for. You fix it while it burns. Knight Capital. CrowdStrike, where the machines couldn't boot to receive the rollback.
You don't pick which one you have at 4pm. You picked it when you built the thing.
Which of These Can You Take Back?
| The change | Undo it? |
|---|---|
Add a nullable column to gradebook_columns | |
Create the gradebook_column_groups table | |
| Turn a feature on for one course | |
| Backfill: set a group on every column in ten courses | |
| Drop a column | |
| Rename a column | |
| Loosen an RLS policy for an hour |
3 minutes, in pairs. Put each one in a box from the last slide: free · costs something · there isn't one.
I'll take the ones you disagreed about.
What Is a Release?
Software as a thing
A version number. A disc. A date. You built it, tested it, shipped it, and then you could not change it.
A bug waited for the next one.
Software as a website
No version number the user can see. No disc. Users get whatever is on the server the moment they load the page.
You can change it in a minute. So you do.
A release is the moment a user's experience changes. Everything today is about controlling that moment.
Where Code Lives Before Users See It

Promotion is moving the same build forward, not rebuilding it. What you tested is what ships.
Dogfooding is using your own product. This course runs on Pawtograder, so you are your own first users.
Your migration has to survive the right-hand box. That's what the rest of today is about.
The System You've Spent Two Weeks Inside
Three sessions, three snapshots, all of them the system standing still. Today is the same picture while it's changing.
You Don't Have to Own Any of That
Same architecture. The pods, the autoscaler and the rolling deploy all still exist. You just aren't the one responsible for them, and what you get back is a rollout story that's already good: a preview URL per pull request, and rollback as a button.
That buys you the free box for your app code. Nobody sells you the free box for your schema.
The Other Way to Build This
A monolith is a system deployed as a single unit. One codebase, one build, one artifact, one deploy.
Bottlenose, the course tool most of you used before Pawtograder, is one of these.
Fix a typo in the grading page, redeploy the whole thing. A broken test in course management blocks your grading fix. Deployment frequency is set by the slowest-moving part of the app.
And everything in it arrives at the same instant. There is exactly one thing to roll back.
Deployability: What We Bought, and What It Cost
Bottlenose, the monolith
Deployability: poor. Every deploy is all-or-nothing, and a bug anywhere blocks everything.
Release speeds: one. Nothing in it can disagree with anything else in it about what version it is.
Pawtograder
Deployability: good. Ship the web tier without touching edge functions. Scale the functions without touching the database.
Release speeds: four, fast to slow: database, edge functions, app server, browser.
Deployability is a quality attribute you trade for, like performance. We bought it, and we paid in version skew and operational complexity.
Not Everything Releases at the Same Speed
| Part | When a user gets the new version | Undo? |
|---|---|---|
| Database migration | instantly, everyone, at once | only with another migration |
| Edge function | next time it's called | redeploy the old one |
| App server | rolling restart, 2 replicas, both live for a minute | redeploy the old image |
| Browser bundle | when they reload. Could be never | you can't. They have it |
The database is the fastest and least reversible. The browser is the slowest and you don't control it. Every migration has to survive both ends at once.
It Applied Cleanly. CI Is Green.
alter table gradebook_columns
rename column sort_order to display_order;
It's 2pm on a Tuesday. Four hundred students have the gradebook open right now.
7 minutes, in pairs. What happens, and to whom?
Write one sentence. I'll take one from every pair.
What Actually Happens
New page loads are fine. Fresh client, fresh query, new column name.
Every open tab is broken. It holds rows with sort_order and sorts by a field that no longer arrives. Nobody reloads a gradebook they're already looking at.
The deployed client's types disagree with the database. One of them is lying, and it isn't the database.
Rolling back is a second migration, and tabs opened in between break the other way.
There is no instant where the client and the database agree. You can't make a schema change atomic against a client you don't control.
The Four-Step Migration

The Rename, Staged
1. add display_order, nullable migration nobody notices
2. write both on every update deploy old tabs still fine
3. read display_order ?? sort_order deploy+flag ramp it, watch the rate
4. drop sort_order migration only after 100% + a wait
Four changes over weeks, instead of one that broke four hundred tabs at 2pm on a Tuesday.
Your column-groups migration is this shape: a new table, a backfill, and a client that reads from it.
The Down Path
Not "does my migration have a down script."
What happens to the data that was written while it was live?
For column groups, concretely:
- You created group rows and set
group_idon existing columns. - Undo drops the groups. Do the columns keep a dangling
group_id? - Does the gradebook render ungrouped, or does it crash?
- A course added a group after your migration ran. Where does it go?
Deliverable 6, due Thu Sep 24. What you tested against, and what happens if this has to be undone after it has already run once.
Where a Defect Gets Caught
| Layer | Catches | Costs | The hole |
|---|---|---|---|
| Your editor | type errors, typos | seconds | only what types can express |
lint, deno-unit-tests | style, unit-level logic | minutes | passes on code that's wrong |
gradebook-e2e | migration won't apply, types drift, nothing groups | minutes | one browser, one seeded class |
| Code review | design, missing constraint, RLS hole | hours | a tired human on a Tuesday |
| Staging | it doesn't work against real shapes | days | fake data, no load |
| Sentry in production | what everything above missed | live | you find out from users |
| The down path | nothing. It makes it stop mattering | 20 min, or nothing | you have to have built one |
Every layer has a hole. Alaska's testing was real and its hole was concurrency. You don't get to skip a layer because you did the one before it well.
What You Don't Have Yet
Software process · Wed Sep 23. Today you turned a flag by hand. Wednesday is the machine: pipelines, trunk-based development, feature flags and ramps, and why Facebook can't have somebody pressing a button.
Testing · Thu Sep 24. gradebook-e2e is already running on your PRs. What it catches, what it can't, and why Alaska's test suite was competent and still had a hole.
Monitoring and observability · Thu Oct 1. A dashboard is a thing somebody built. Logs, metrics, traces, and what you actually do at 4:15pm when the graph moves.
All three help you find out sooner. None of them replaces a down path.
Key Takeaways
A release is the moment a user's experience changes. Deploying is a different event, and the flag is what holds them apart. You can't make a schema change atomic against a client you don't control, so you stage it instead: add nullable, write both, read the new one, drop the old one, weeks apart.
Every change needs a down path, and its price is free, survivable, or nonexistent. You choose which when you build it, not at 4pm when you need it.
The architecture decides the price. The database being the application, authorization living in the rows, and the browser holding a live cache are what make this system good, and they're why its parts change at four different speeds and why a schema change can't be atomic.
Testing for weeks didn't save Alaska. Having a down path did.
Up Next
Wed Sep 23: Software Processes & Continuous Delivery
Today was getting one change into a live system without hurting anybody, and the architecture that makes that hard. Wednesday is the machine that does it every day: pipelines, trunk-based development, feature flags, dark launch and ramps.
Thu Oct 1: Operations. Today you read numbers off a dashboard. That session is where the dashboard comes from: logs, metrics, traces, and what you actually do during an incident.
Before then:
- Project Bids, tonight 23:59
- Onboarding: Gradebook Column Groups, due Thu Sep 24. Seven days