Courses › Low-Level Design › Foundations

Handling Extension Follow-Ups

Classifying "now add X" changes, where each one plugs in, and refactoring calmly when it doesn't.

Almost every LLD round has a second half: once your core design works, the interviewer changes the requirements. "Now add electric-vehicle charging spots." "Now notify users when their order ships." "Now make it thread-safe." These extension follow-ups are where interviewers find out whether your design was actually extensible, or only looked that way.

Why interviewers ask them

A design that handles the original prompt tells them little. Real software changes constantly, so they want to see:

  • whether the change fits into an existing seam or forces a rewrite;
  • whether you can say where the change goes before typing;
  • whether you notice when the design is missing an abstraction, and refactor calmly.

A four-step response

  • Restate and clarify. "So some spots have chargers, and charging is billed separately? Can a non-electric car park in a charging spot?"
  • Classify the change. Most follow-ups fall into a handful of kinds (table below), and each kind has a usual home in the design.
  • Say the plan out loud: which classes change, which are new, and which stay untouched. "Charging is a new spot feature plus a new pricing rule. ParkingLot and Ticket don't change."
  • Make the change and re-run your tests or demo. If the change touched more than expected, say why and what abstraction would have prevented it.

The common kinds of follow-up

A worked example: pricing a parking lot

Suppose your first version computed the fee inside Ticket:

class Ticket:
    def __init__(self, vehicle_type, entry_hour):
        self.vehicle_type, self.entry_hour = vehicle_type, entry_hour

    def fee(self, exit_hour):
        hours = max(1, exit_hour - self.entry_hour)
        rate = 5 if self.vehicle_type == "truck" else 2
        return hours * rate

The interviewer asks for weekend rates and an electric-vehicle charging surcharge. Adding more if branches here would mix every pricing rule into Ticket. Instead, say: "Pricing varies independently of tickets, so I'll move it behind a strategy, and make the surcharges composable."

from abc import ABC, abstractmethod


class PricingRule(ABC):
    @abstractmethod
    def price(self, ticket, exit_hour, base): ...


class HourlyRate(PricingRule):
    RATES = {"car": 2, "truck": 5, "ev": 2}

    def price(self, ticket, exit_hour, base):
        hours = max(1, exit_hour - ticket.entry_hour)
        return base + hours * self.RATES[ticket.vehicle_type]


class WeekendMultiplier(PricingRule):
    def __init__(self, factor):
        self.factor = factor

    def price(self, ticket, exit_hour, base):
        return base * self.factor if ticket.weekend else base


class ChargingSurcharge(PricingRule):
    def __init__(self, per_hour):
        self.per_hour = per_hour

    def price(self, ticket, exit_hour, base):
        if not ticket.charging:
            return base
        return base + (exit_hour - ticket.entry_hour) * self.per_hour


class Ticket:
    def __init__(self, vehicle_type, entry_hour, weekend=False, charging=False):
        self.vehicle_type, self.entry_hour = vehicle_type, entry_hour
        self.weekend, self.charging = weekend, charging


class PricingPolicy:
    def __init__(self, rules):
        self.rules = rules                     # applied in order

    def fee(self, ticket, exit_hour):
        total = 0
        for rule in self.rules:
            total = rule.price(ticket, exit_hour, total)
        return total


policy = PricingPolicy([HourlyRate(), WeekendMultiplier(1.5), ChargingSurcharge(3)])
print(policy.fee(Ticket("car", 9), 12))                              # 6
print(policy.fee(Ticket("car", 9, weekend=True), 12))                # 9.0
print(policy.fee(Ticket("ev", 9, weekend=True, charging=True), 12))  # 18.0

What to say while doing this:

  • "Ticket is now plain data, and PricingPolicy doesn't know any specific rule."
  • "Order matters: the weekend multiplier applies to the base rate but not to charging. I made that explicit by putting it before the surcharge. I'd confirm that with you."
  • "A new rule, say a loyalty discount, is one new class and one line of configuration."

This is the same move as in the Discount Engine walkthrough, at a smaller scale.

Designing for follow-ups you can't predict

You can't know which extension is coming, and building every abstraction up front is over-engineering. Use these defaults instead:

  • Inject dependencies such as the clock, strategies, the storage and notification senders through the constructor. Every follow-up about time, persistence or notifications then becomes easy.
  • Keep the main flow in one place (for example, ParkingLot.park and ParkingLot.leave) so you have an obvious spot to raise events or add checks.
  • Use enums and small value objects rather than raw strings and tuples, so adding a field doesn't break callers.
  • Put each rule in one place. If the same condition appears in three methods, a follow-up will make you change all three.

[!INTERVIEW] Refactoring is not a failure If the follow-up doesn't fit, say so plainly: "This doesn't fit my design, because pricing lives in Ticket. I'll pull it out into a strategy first, which takes about two minutes, then add the rule." Interviewers rate that well. Forcing the change in with a pile of if statements, or pretending it fits, rates badly.

Thread-safety follow-ups

"Now make it thread-safe" is the single most common follow-up. A good answer:

  • Find the shared, changing state: the spot map, seat status, account balances.
  • Find the operations that must be atomic: "check that the spot is free, then assign it".
  • Choose the lock granularity: one lock for the whole object is simple and correct; per-item locks scale better but need a consistent lock order to avoid deadlocks.
  • Keep slow work outside the lock, such as sending notifications or calling a payment provider.

See Concurrency & Thread Safety and the Movie Ticket Booking walkthrough.

[!TIP] Practice Parking Lot · Car Rental System · Notification Service · Logging Framework