AmouAI Hub/Courses/Software Engineering for AI Engineers/Chapter 1
The Tradeoffs You Are Already Making
A coding agent will produce a working implementation of almost anything you describe. What it will not do is tell you that the implementation just traded away your ability to add a second region, or that it chose consistency over availability on a screen where nobody would have noticed either way. Those decisions got made. The only question is whether you made them.
By the end of this chapter you can
- Name the seven properties every system design trades between, and give an example of each in conflict
- Read a product requirement as a ranking of those properties rather than a list of features
- State what a technical decision cost, not only what it bought
- Compute the availability of a system from its parts, in series and in parallel
- Recognise the defaults a coding agent applies when your prompt is silent
- Write a constraint ranking short enough that a colleague, or an agent, will actually read it
1Seven properties, one system
Andrew Ng lists them almost in passing: latency, availability, consistency, reliability, maintainability, simplicity and cost. They are not a checklist. They are seven forces pulling in different directions, and every line of infrastructure you add moves several of them at once.
Here is the uncomfortable part. None of these properties is a thing you can simply have more of. Each one is bought with something else, and usually the currency is one of the other six.
| Property | What it means concretely | What it is usually bought with |
|---|---|---|
| Latency | How long one operation takes, measured at p95 or p99 rather than the mean | Cost (more caches, more regions) and consistency (serving a copy that may be stale) |
| Availability | The share of requests that get an answer at all | Cost (redundancy) and consistency (accepting writes you cannot immediately reconcile) |
| Consistency | Whether every reader sees the same value at the same moment | Latency (coordination takes round trips) and availability (refusing to answer beats answering wrongly) |
| Reliability | Whether the system does the right thing under failure, not just under load | Simplicity (retries, breakers, fallbacks are all extra machinery) and cost |
| Maintainability | How cheaply the next person can change it correctly | Latency and cost (abstraction layers are not free) and sometimes raw performance |
| Simplicity | How much of the system fits in one person’s head | Almost everything else, which is why it is the first thing quietly spent |
| Cost | Money per month, and engineer-hours per month, which is the larger number | All six of the above, in both directions |
A design decision is never “better”. It is better on some axes and worse on others. An engineer who can only say what a choice improves has not finished thinking about it. The sentence you are learning to finish in this course is: this buys us X, and it costs us Y, and Y is acceptable because of Z.
The instrument below is the course in miniature. Five ordinary decisions, seven meters. Move anything and watch what else moves. There is no configuration where all seven read green, and looking for one is the specific mistake this chapter exists to prevent.
Notice what happens when you pick the strongest option in every row. Reliability and availability go up, and simplicity collapses. You have built something that survives almost anything, that nobody on a four-person team can hold in their head, and that costs more than the revenue of the catalogue it serves. That configuration is not wrong in the abstract. It is wrong for this scenario, and knowing which is the skill.
2What the agent optimises when you do not say
A coding agent is not neutral. Asked for a feature with no constraints attached, it produces the shape that dominates its training data: the tutorial shape. That shape has a consistent bias, and once you can name the bias you can correct for it in one sentence of prompt.
The tutorial shape optimises for plausibility on first read. It is simple, synchronous, unpaginated, unversioned, single-region, and it assumes the happy path. That is genuinely the right default for a prototype, which is why it is so hard to notice when it is wrong. It becomes wrong the moment the thing goes in front of users, and by then it is load-bearing.
Reviewing generated code by reading it top to bottom and asking “does this look right?”. It always looks right; that is what the model is good at. The review that finds things asks a different question: what decision does each line encode, and did anyone decide it? An unpaginated list is not a missing feature, it is an availability decision made silently.
This is what Ng means when he writes that a novice who vibe codes “did not know such tradeoffs even existed and therefore did not steer the agent”. The failure is not that the agent chose badly. The agent chose the way a reasonable stranger would choose with no information. The failure is that nobody supplied the information.
3A requirement is a ranking, not a list
Product requirements arrive as features. Architectures are decided by the properties nobody wrote down. The translation between the two is a skill, and it is mostly a matter of asking three questions of every requirement.
Take a real sentence: “Users should be able to see their order history.” As written, this constrains nothing. Now ask:
- What is the cost of being slow? A shopper waiting three seconds on an order history page is irritated. A trader waiting three seconds on a position page has lost money. Same feature, two orders of magnitude between the latency budgets.
- What is the cost of being wrong? Showing an order that was cancelled two seconds ago is a support ticket. Showing a bank balance that is two seconds stale is a regulatory problem. This question is the one that decides your consistency model, and almost nobody asks it explicitly.
- What is the cost of being down? If the page is unavailable for ten minutes on a Tuesday, does anyone lose anything? For a lot of internal tooling the honest answer is no, and that answer should save you a five-figure infrastructure bill.
The answers give you a ranking. The ranking gives you an architecture. Skipping to the architecture is how teams end up running multi-region active-active for an admin dashboard used by eleven people.
Before asking an agent to build anything non-trivial, give it the ranking: “Constraints in priority order: (1) … (2) … (3) …. The following are explicitly not constraints: …”. The second half matters as much as the first. Telling an agent that cost does not matter at this scale stops it inventing an optimisation you will have to maintain.
4The arithmetic of availability
Availability is the one property in the list with real arithmetic behind it, and the arithmetic is unintuitive enough that people get it wrong in both directions. Ten minutes here will save you from two expensive mistakes.
An availability figure is a probability: the share of requests that get a successful answer over some window. Write 99.9% as 0.999 and the arithmetic works out like this.
Components in series multiply. If your request must pass through a load balancer, an application server and a database, and each is independently available 99.9% of the time, the path is available 0.999 × 0.999 × 0.999 = 99.7% of the time. You did not build a 99.9% system. You built a 99.7% system out of 99.9% parts, and you tripled your downtime budget without adding a single feature.
// three dependencies, each "three nines"
const path = [0.999, 0.999, 0.999];
const available = path.reduce((a, b) => a * b, 1);
console.log(available); // 0.997002999 -> 99.70%
console.log((1 - available) * 43200); // 129 minutes down per 30-day month
Redundant components in parallel add nines. Two instances of a 99% service, where either one can serve the request, fail only when both fail: 1 − (0.01 × 0.01) = 99.99%. This is why redundancy is such a powerful lever, and also why it is oversold — the arithmetic assumes the failures are independent. Two instances in the same rack, on the same deploy, running the same bug are not independent at all, and the formula quietly becomes a lie.
Independence is an assumption, not a property. Correlated failure is the normal case: a bad deploy hits every replica, a certificate expires everywhere at once, a poison message crashes every consumer in the pool. When you claim four nines from redundancy, the honest question is what single event takes out both copies — and there is almost always one.
The last piece of arithmetic is the one that governs behaviour rather than design. Convert the target into a downtime budget and it stops being an abstraction.
| Target | Down per month | Down per year | What that buys in practice |
|---|---|---|---|
| 99% | 7h 18m | 3.65 days | An internal tool. One person can restart it in the morning. |
| 99.9% | 43m | 8h 46m | A normal web product. Single region, good pipeline, someone on call. |
| 99.95% | 21m | 4h 23m | Automated failover is now mandatory; a human cannot respond fast enough. |
| 99.99% | 4m 19s | 52m | Multi-region, no manual steps in recovery, and a real on-call rota. |
| 99.999% | 26s | 5m 15s | Rarely justified outside infrastructure. A single bad deploy exceeds the annual budget. |
Read the bottom row again. Five nines allows five minutes of unavailability per year. One botched release spends the whole budget. If your product does not require that, asking for it is not ambition; it is an expensive way to make every future deploy frightening.
5One feature, three products
The clearest way to feel that there is no universal right answer is to build the same thing three times under three different rankings. Here is one feature — a counter of how many people are currently viewing an item — in three products.
A news site
Ranking: latency, then cost, then everything else. Nobody is harmed by a wrong number.
Design: increment a counter in a shared cache, read it from the edge, refresh every 30 seconds. No database involvement at all. If the cache is lost, the number resets and nothing bad happens.
A ticketing site
Ranking: consistency, then availability, then latency. The number drives a scarcity message that has legal consequences if it is false.
Design: a real count against a transactional store, with the reservation and the count in one transaction. Slower, more expensive, and correct — because “only 2 left!” when there are 40 is a regulator’s problem, not an engineer’s.
An internal dashboard
Ranking: simplicity, then cost. Eleven users, all colleagues.
Design: a COUNT(*) against the database on
every page load. It is the wrong answer at scale and exactly the right answer here, because the
alternative costs a week of somebody’s life to save 40ms for eleven people.
Three defensible designs, three rankings, one feature. An engineer who has memorised “use a cache for counters” gets the ticketing site wrong. An engineer who has memorised “always be correct” gets the news site wrong and the dashboard very wrong.
6Writing the ranking down
Everything above collapses into one short artefact. It goes at the top of the design document, in the pull request description, and in the prompt. It is four to six lines long, and if it is longer than that nobody will read it, including the agent.
The shape that works:
# Order history page
Ranked constraints
1. A customer must never see another customer's data. (non-negotiable)
2. Refund status must be current within 5s. (regulatory)
3. p95 under 400ms for the shopper path. (measured, not felt)
4. Correct under concurrent inserts. (no skipped rows)
Explicitly not constraints
- Availability beyond 99.9%. Down is a support cost, not a revenue loss.
- Cost at current scale. Under $200/month either way.
- The admin view's latency. Staff will wait.
Three properties of this document make it work. It is ranked, so conflicts have an answer. It has numbers, so “fast” cannot drift. And it says what is not a constraint, which is the half that stops both humans and agents from gold-plating.
You are not being asked to predict the future. You are being asked to make the decisions visible, so that when the situation changes — and it will — somebody can find the sentence that is no longer true and change it deliberately. An architecture without a written ranking cannot be revised; it can only be rewritten.
The playground below is the exercise you will repeat in most chapters of this course: a scenario, its stated constraints, and a set of decisions that have to answer to them. Nothing scores you. The board only reflects your own ranking back at you, which turns out to be enough.
Take something you have built or are building and write its constraints.md. Four ranked
lines, three non-constraints, numbers where you can. Then read the code and find one place where it
contradicts the document. There is always one, and it is the most useful thing you will learn about that
system this week.
✓Checkpoint
Five questions. Answer before you scroll — the explanation is worth more when you have committed to something.
▶Playground
A real brief with a real ranking. Configure it, then open the defensible answer and see where you differ — and, more importantly, whether your reason was better than the one written down.
✓Exercise set
Twelve problems. The JavaScript ones run in a sandboxed frame and are checked against what your code actually does, so there is usually more than one right answer. The design ones are graded against the scenario’s stated constraints, and they require a written justification — naming the tradeoff out loud is the skill being assessed. Your work is saved in this browser.
Chapter 2 — The Request Path, End to End
Every performance argument you will ever have is really an argument about which hop owns the milliseconds. Next chapter you draw the path and attribute the time.