Requirements and Estimation
In one line: the estimation gets everything right and then discards its own peak-traffic figure at the last step. It is also the chapter where the standard sizing question is the wrong question.
Requirements
| Functional | Detail |
|---|---|
| Registration and authentication | Create accounts, log in securely, reset passwords |
| Payment processing | Initiate payment on purchase; accept credit and debit cards |
| Transaction history | View history with dates, amounts, descriptions |
| Balance management | Check balances, including available funds and pending transactions |
| Mobile accessibility | Mobile apps or responsive web |
Non-functional: data integrity and security (industry standards, encryption, secure storage, prevent corruption) · availability · reliability · scalability (peak loads during promotions) · performance.
'Pending transactions' is the requirement that admits the two-phase model
It is a single clause in the balance requirement and it is the only place the functional requirements acknowledge that authorization and settlement are separate.
A pending transaction is one that has been authorized and not yet captured — the hold from Lesson 1. It reduces available funds without having moved anything.
That means a correct balance is not one number:
Ledger balance what has actually settled Available balance ledger balance MINUS outstanding holds Pending authorized, awaiting capture or expiry
Show a customer only the ledger balance and they will overspend against holds they cannot see. Show only available and their statement will not reconcile.
A system with a delay between commitment and transfer needs at least two balances, and the requirement quietly says so while the design that follows tracks a single balance column.
'Data integrity' is doing far more work here than in any other chapter
Every chapter in this module lists security. Here the first non-functional requirement is "data integrity and security… maintain consistency, and prevent data corruption" — and in a payment system that phrase has a specific, testable meaning that "be consistent" does not.
Integrity in a financial system means the books balance. Not "the replicas agree" — a stronger property: for every transaction, the money debited equals the money credited, and the sum over all accounts of all entries is zero. That invariant is checkable at any moment, on any subset of the data.
It is what makes reconciliation possible at all, and it is why financial systems use double-entry bookkeeping — a technique this chapter names nowhere despite having a ledger component. Lesson 5 covers it.
In most systems "integrity" is a quality; in a financial system it is an arithmetic invariant you can assert on. That difference is worth stating out loud in an interview.
Storage
Each transaction is 100 bytes, each user record 200 bytes, at 50 million transactions per day.
50M × 100 bytes = 5 GB/day
Correct, and small enough that the interesting constraint is retention rather than volume
The arithmetic checks out and the number is tiny by this module's standards — that building block moved 60 TB a day, that building block 32 TB. 5 GB/day is a rounding error.
Which means volume is not the constraint here. Retention is, and it is set by regulation rather than by engineering:
5 GB/day x 365 = 1.8 TB/year x 7 years (typical financial retention) = ~13 TB
Still small. But note what that changes: you cannot delete anything. Every other system in this module could expire old data — the crawler re-fetches, the deployment system prunes artifacts, feeds age out. A payment system's records are evidence: needed for disputes (chargebacks can arrive months later), for tax, for anti-money-laundering, and for audits.
When retention is set by regulation rather than by cost, the design decision is not what to delete but how to tier — recent data on fast storage, older data on cheap storage, all of it immutable and queryable.
Two things make the raw 100 bytes optimistic, though. The ledger is append-only and, done properly, double-entry — so one payment produces the payment event, two or more ledger entries, a wallet update, and later a settlement record. The real multiplier is several times the transaction count. And a transaction row carrying ID, amount, currency, both party IDs, status, timestamp, and a payment method is comfortably more than 100 bytes.
Neither changes the order of magnitude, which is the useful thing about being this far from any limit.
Bandwidth
Peak: 5M transactions/hour / 3600 = ~1,400 TPS In: 1,400 x 100 bytes x 8 = 1.12 Mbps Out: 1,400 x 130 bytes x 8 = 1.46 Mbps
All three reproduce exactly.
Three megabits, and the number worth carrying forward is 1,400
This is the smallest bandwidth figure in the entire module. A payment is a few hundred bytes of structured fields — no media, no documents, no binaries. Bandwidth is not a design consideration here at any scale you will be asked about.
But 1,400 transactions per second is the figure that matters, because it is the system's actual peak request rate, computed correctly from the peak hourly load. Hold onto it. The next section is about to throw it away.
Worth noting the peak-to-average ratio while you have both numbers:
Average: 50M / 86,400 = 579 TPS
Peak: 5M / 3,600 = 1,400 TPS
---------
Peak is ~2.4x average
That is a mild ratio — payments spread across a day more evenly than, say, a build fleet — and it is the ratio you would size against, plus headroom for the promotions the scalability requirement calls out.
Servers
To estimate the server count, we use the peak traffic load. Given our assumption that daily active users serve as a proxy for requests per second, we get 50 million requests per second.
50,000,000 / 64,000 ≈ 782 servers
The chapter says it will use the peak load, then substitutes a number that is neither peak nor a rate
Two sentences, and they contradict each other.
The first says "we use the peak traffic load." The peak traffic load was computed two sections earlier: 1,400 TPS. The second sentence substitutes 50 million, and the substitution is wrong twice over:
Wrong UNIT: 50 million is per DAY, used as per SECOND
Wrong QUANTITY: the "DAU as a proxy" convention is about USERS;
50 million is TRANSACTIONS. Not the same thing at all.
Follow the correct number through:
Published: 50,000,000 / 64,000 = 782 servers
Peak actual: 1,400 / 64,000 = 0.02 servers
-----------
Factor: ~36,000x
Even the chapter's own quiz figure of 3,000 TPS gives 0.05 servers. Every path leads to one machine, with capacity to spare.
This is the eighth appearance of DAU-as-RPS in the module, and the worst instance, because unlike the others the correct figure was already on the page and explicitly promised. The Yelp, Newsfeed, Instagram, and TinyURL chapters at least applied the convention consistently; that building block flagged its own estimate as unrealistic; that building block used a coherent unit and simply changed the quantity's scope between sections. Here the chapter states the right method, computes the right input, and then uses neither.
When a section announces its method, check that the next line follows it.
But RPS is the wrong sizing question for a payment system entirely
Correcting 782 to 0.02 is right and it is not the useful answer, because request throughput is not what constrains a payment system. Three other things are.
External call latency dominates. Every authorization makes a synchronous round trip out to a gateway, a card network, and an issuer bank. That is hundreds of milliseconds you do not control:
1,400 TPS x 500 ms in flight = ~700 concurrent in-flight authorizations
So the sizing question is not "how many requests per second can a CPU handle" but "how many concurrent outbound calls must I hold open, and what happens when the issuer slows down?" That is a connection-pool, thread-model, and timeout-budget question — and it is why Lesson 8's retry and timeout strategies matter more than any server count.
Write durability, not read throughput. Every payment is a durable, ordered, auditable write — to the payment store, the wallet, and the ledger. Payments are write-heavy with almost no read amplification, which is the inverse of every other system in this module. The database is the capacity limit, and 1,400 durable writes/second with strong guarantees is a real engineering problem where 1,400 HTTP requests/second is not.
Peak concentration during promotions. The scalability requirement names it — a flash sale concentrates a day's volume into minutes, and the constraint that binds first is the issuer's rate limit, not yours.
Size a payment system by concurrent external calls and durable writes, not by requests per second. That is the answer that shows you understand what the system spends its time doing.
| Quantity | Published | Assessment |
|---|---|---|
| Peak TPS | 1,400 | ✅ Correct, and then discarded |
| Storage/day | 5 GB | ✅ Correct — the real constraint is regulatory retention, not volume |
| Incoming bandwidth | 1.12 Mbps | ✅ Correct |
| Outgoing bandwidth | 1.46 Mbps | ✅ Correct |
| Servers | 782 | 🔴 ~36,000x too high — the peak figure gives 0.02; and RPS is the wrong metric for this system |
The building blocks, and the one that is conspicuously short
Databases (user payment events and transaction data) · load balancers · pub-sub ("decouple services like payment processing, wallets, and ledgers").
Three entries — the shortest block list in the module, and for a system with the strictest correctness requirements in it.
What is missing is telling. There is no cache (correct — you do not serve stale balances), no CDN (correct — no media), and no blob storage (correct). Those omissions are right.
But there is also no mention of an idempotency store, a secrets/key-management service, or a tokenization vault — three components this design genuinely needs, the first for the duplicate-charge protection the chapter later describes, and the last two for the card data it later stores. Lessons 3 and 6 return to both.
The pub-sub entry is the interesting one: it decouples payment processing from wallet and ledger updates, which is what makes the flow asynchronous after authorization. Lesson 7 shows that this decoupling is where the design's hardest correctness bug lives.
The ordering matters and is worth stating: in most systems you trade correctness for availability or latency. Here you do not. A payment system that is fast and occasionally double-charges is worse than a slow one, and that inversion is what makes the design choices unusual.
Key takeaway
Storage (5 GB/day) and bandwidth (1.12 / 1.46 Mbps) are correct and both are trivially small — for storage, the constraint is regulatory retention rather than volume, and nothing can be deleted. The peak rate of 1,400 TPS is computed correctly and then discarded: the server section promises to use the peak load and instead treats 50 million transactions per day as requests per second, wrong in both unit and quantity, for a 36,000× error where every correct path gives one machine. When a section announces its method, check that the next line follows it. But the deeper point is that RPS is the wrong metric here — a payment system is bound by concurrent external calls to opaque third parties and by durable ordered writes, not by request throughput. And "data integrity" carries unusual weight: in a financial system it is an arithmetic invariant you can assert on, not a quality.
Next: the APIs and a schema that stores the card number in plaintext.