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.
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