</> MAANG.io
system design · 301

Advanced

Master system design interviews with scalable architecture patterns, distributed systems, and real-world design challenges.

0/480 solved 0% complete

Section 12: Doors Left Unlocked - Follow-up Feature Evolution

Executive Summary: The interviewer's last move is a product roadmap: eight features, one quarter. Almost none hurt, and the reason is specific rather than lucky. Each walks through a door an earlier section left unlocked: reserve prices, Buy It Now and soft close are new triggers inside the same accept transaction; retraction and shill detection exist only because every maximum sits in an append-only ledger; the ending-soonest feed reads the closer's own index; sniping tools are shaped by the gateway and the cache; multi-currency was settled when the API spoke in minor units. Each is run as what changes, what does not, and which decision made it cheap, with #7731 re-resolved by hand once Marcus's bid is removed.

Seventy minutes in, the interviewer closes the laptop. "Product has a list: reserve prices, Buy It Now, anti-sniping, retractions, an ending-soonest page, shill bidders, and finance wants euros. Which of these make you rebuild?"

The honest answer is none. Three beats per feature say why: what changes, what does not, which door made it cheap.

Follow-up features mapped to the existing mechanism each plugs into: accept transaction and row lock, status and end_time index, append-only ledger, gateway and cache, minor units

Reserve Price and Buy It Now: New Triggers, Same Transition

Reserve price. What changes: a reserve_amount column on the auction row, as secret as high_max_amount and never returned by any read. Section 7's resolve gains one line: when the high maximum clears the reserve and the computed price sits below it, the price jumps to the reserve. Had Priya set a $5,000 reserve on #7731, Marcus's Tuesday bid would have landed at $5,000 instead of $4,350. Section 9's closer already had the branch: high maximum below reserve at close means CLOSED_RESERVE_NOT_MET, winner null. The API gains only an optional field and a reserve_met boolean. The rejected alternative, a hidden reserve as a seller's own bid, is shill bidding by design and poisons the auditor's ledger.

Buy It Now. What changes: a bin_amount column and POST /auctions/{id}/buy, a conditional close:

BEGIN;
UPDATE auctions
   SET status = 'CLOSED', winner_id = :buyer, final_price = bin_amount,
       closed_at = now(), version = version + 1
 WHERE auction_id = :id AND status = 'OPEN' AND bid_count = 0;
-- rows affected must be 1; otherwise roll back and return BUY_UNAVAILABLE
INSERT INTO outbox (event, payload) VALUES ('auction.closed', :payload);
COMMIT;

That update takes the same row lock a bid takes, so a buy and a bid in the same millisecond serialise: whichever commits first wins. Either the bid flips bid_count to 1 and the button disappears, or the buy closes the row and the bid gets REJECTED with AUCTION_ENDED, Theo's verdict at 19:00:00.050. The outbox row, written in the same transaction, emits the same auction.closed the closer would have; the closer never meets a row that is already CLOSED. For a reserve auction, swap the predicate to reserve_met = false.

Key Insight: A new trigger into an existing transition is a column and a predicate. A new state touches the closer, the cache, the stream and the notifications. Section 2's state machine was drawn so that Buy It Now is the first kind.

Quick check before moving on:


Sign in to continue reading

The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.

Sign in to read the full lesson

Sign in to MAANG.io

Use your Google or Microsoft account — no password to remember.

Continue with Google Continue with Microsoft

Please accept the terms above to continue.