Concepts / Quality, testing and extensibility / The machine-coding rubric

The machine-coding rubric

What a 90-minute coding-plus-design round is graded on.

01The intuition

The problem it solves

You have 90 minutes, a laptop and a prompt: "build a parking lot that supports park, unpark and a per-floor status display; vehicle types and pricing will change." Many candidates open an editor and start typing classes. Ninety minutes later they have a hierarchy, no running code and no answer to "show me a car parking". The round is not graded on how much design was attempted; it is graded on what works, how it reads, and how it bends.

class Vehicle: ...
class Car(Vehicle): ...
class Bike(Vehicle): ...
class Truck(Vehicle): ...
class Spot: ...
class Floor: ...
class ParkingLot: ...
class Ticket: ...
class PricingStrategy: ...
class Display: ...
# 90 minutes later: nothing runs end to end

The rubric has four axes, and the order you work in should follow them: working code for the core flows, correctness on edge cases, readable structure with clear responsibilities, and extensibility shown by handling one change request cheaply. Tests and a driver are the evidence for the first two.

Build it one piece at a time

Step one, first 10 minutes: write the requirements as a list, pick the two or three flows that must work, and write a driver that exercises them before any class exists. The driver is your definition of done.

def main() -> None:
    lot = ParkingLot(floors=2, spots_per_floor={"car": 2, "bike": 1})
    t1 = lot.park("car", plate="KA01")
    t2 = lot.park("car", plate="KA02")
    assert lot.park("car", plate="KA03") is None       # floor 1 full, floor 2 has room? depends on spec
    print(lot.status())
    print(lot.unpark(t1))                               # fee

Step two, next 40 minutes: the thinnest vertical slice that makes the driver pass. Plain classes, obvious data structures, no patterns yet. Every method does one thing and the invariants are enforced where the data lives.

from dataclasses import dataclass, field
from itertools import count

@dataclass
class Ticket:
    id: int
    plate: str
    kind: str
    floor: int
    spot: int

class ParkingLot:
    def __init__(self, floors: int, spots_per_floor: dict[str, int]) -> None:
        self.free: dict[tuple[int, str], list[int]] = {}
        for f in range(floors):
            for kind, n in spots_per_floor.items():
                self.free[(f, kind)] = list(range(n))
        self.tickets: dict[int, Ticket] = {}
        self.ids = count(1)

    def park(self, kind: str, plate: str) -> Ticket | None:
        for (f, k), spots in sorted(self.free.items()):
            if k == kind and spots:
                t = Ticket(next(self.ids), plate, kind, f, spots.pop())
                self.tickets[t.id] = t
                return t
        return None

    def unpark(self, ticket: Ticket) -> int:
        self.tickets.pop(ticket.id)
        self.free[(ticket.floor, ticket.kind)].append(ticket.spot)
        return 20                                       # placeholder fee, replaced in step 3

    def status(self) -> dict[tuple[int, str], int]:
        return {key: len(spots) for key, spots in self.free.items()}

Step three, next 25 minutes: the edge cases the prompt implies (unknown vehicle type, unpark twice, full lot), then the one extension the prompt named. Pricing was called out as changing, so pricing becomes an injected policy now, not earlier. Tests go in as you go, three or four, each one sentence long.

from typing import Protocol

class FeePolicy(Protocol):
    def fee(self, ticket: Ticket, hours: int) -> int: ...

class FlatFee:
    RATES = {"car": 20, "bike": 10}
    def fee(self, ticket: Ticket, hours: int) -> int:
        return self.RATES[ticket.kind] * hours

def test_unpark_twice_is_an_error() -> None:
    lot = ParkingLot(1, {"car": 1})
    t = lot.park("car", "KA01")
    lot.unpark(t)
    try:
        lot.unpark(t)
        assert False, "second unpark must fail"
    except KeyError:
        pass

Last 15 minutes: run everything, read your own code top to bottom once, rename what confuses you, and prepare two sentences on what you would do with another hour. A time plan that matches the rubric:

MinutesDoRubric axis it serves
0–10requirements list, driverproblem understanding
10–50thin slice that runsworking code
50–75edge cases, tests, the named extensioncorrectness, extensibility
75–90run, read, rename, summaryreadability, communication

Why it works

Working code first is not a shortcut; it is the only axis that cannot be recovered later. A beautiful hierarchy with a broken park method scores below a plain class that works, because the reviewer cannot evaluate what does not run. Doing the extension last, and only the one named in the prompt, keeps the design honest: the pattern arrives with the requirement that justifies it, which is exactly the judgement the round measures.

Where it breaks: a driver that does not match the spec sends you confidently in the wrong direction, so read the prompt twice and write assumptions down where the reviewer will see them. Skipping tests to save time usually costs more time in the final demo. And silence is a failure mode: in a live round, say what you are doing at each phase boundary.

score ≈ working flows × (correctness on edge cases) × readability, with extensibility as the tie-breaker; a zero in the first term zeroes the product

The trade-off you must say out loud

You are trading completeness for a working core, and you should say so: "I am implementing park, unpark and status end to end first; display formatting and the second pricing rule are stubs I will fill if time allows." Reviewers reward a candidate who knows what they left out. The follow-up that usually comes next is a change request chosen to test your extension point; if it lands where you left the door, you pass the axis; if not, name where it would go.

Where it shows up in the 100 Google problems

  • Parking lot: the worked example above, the most common machine-coding prompt.
  • Restaurant waitlist: the Google version is a 45-minute coding round where the class design must run.
  • Rate limiter class: a driver that fires requests at known times is the test harness the reviewer expects.
  • Splitwise: a classic 90-minute prompt where a working equal split beats an unfinished percentage engine.
  • Task scheduler with dependencies: topological execution first, retries and priorities as the named extensions.

02Used in these problems