SwadhyAI Notes

All Low-Level Design notes

Low-level design

How to run the forty-five minutes

An LLD round is a timed conversation that ends in working object code. You clarify use cases and scope first, agree a tiny public API with real types, name the model, then implement. Candidates who freeze try to recall a perfect class diagram. Candidates who finish talk before they type.

We already know this parking lot is in-process design, not a distributed system. Now you need a talking order so the last thirty minutes are coding, not panic. Ask what is out of scope early — refunds, a mobile app, multiple cities — then set up the API and code.

Working order

1

Clarify

Use cases, one program, what is out.

2

API

Commands and queries with real types.

3

Model

Entities, values, who owns whom.

4

Implement

Happy path, then one bad ticket.

Visible artifacts

By each time marker, something exists on the board

This prevents long silent thinking. The diagram can stay messy. The cannot.

  1. 5 minutes

    Clarify use cases and what is out

  2. 10 minutes

    Agree the public API with real types

  3. 20 minutes

    Name types, values, who owns whom

  4. 35 minutes

    Happy path plus one illegal case

  5. 45 minutes

    Show the seam for the extra they add

Talk before you type

Say the use cases out loud. Confirm who acts: driver, admin, two gates. Confirm : what you will not build today. Write the public API next. park takes a vehicle and returns a ticket. exit takes a ticket id and returns a receipt. That API is the contract you will implement.

Then list the things that exist and the one rule that must not break: a spot is held by at most one vehicle. Only then mention a plan for “which free spot?” or “how much to charge.” Mention it only if they hinted those rules will change. Strategy and Factory are tools. They come after you know who allocates and who prices. Opening with a pattern name wastes the first ten minutes.

A working order is: types and enums, the API, the , one (lot full, unknown ticket), then the they always add. Even a crude test keeps you honest when you split a Manager apart. Do not wait for a perfect picture. Talk, agree the API, then code.

Conversation map

Drive, then invite depth

Treat the round as checkpoints. At each checkpoint, say what you are assuming and ask whether they want to adjust scope. This keeps you from over-building the wrong lot.

Clarify actors and out-of-scope
Speak the API
Assign verbs to types
Where will change land?
Code happy path
One illegal case + seam

Agree this API before you type the rest

If park and exit are still fuzzy, you will rewrite the core. Speak the contract. Get a nod. Then implement. process(Object) is not an API. Real types force you to name Ticket, Vehicle, and Receipt early, which is half the model.

java
class ParkingService {
  private final ParkingLot lot;
  private final SpotChooser chooser;
  private final TicketBook tickets;
  private final Pricing pricing;
  private final Clock clock;

  Ticket park(Vehicle vehicle) {
    ParkingSpot spot = chooser.choose(vehicle)
        .orElseThrow(LotFullException::new);
    spot.occupy(vehicle);
    return tickets.issue(vehicle, spot, clock.instant());
  }

  Receipt exit(TicketId ticketId) {
    Ticket ticket = tickets.require(ticketId);
    Money fee = pricing.fee(ticket, clock.instant());
    ticket.spot().vacate();
    tickets.close(ticket);
    return new Receipt(ticket.id(), fee);
  }
}

You can sketch this on a whiteboard as signatures only for the first ten minutes. Empty method bodies are fine. The point is agreement. The requirements-to-classes page is how you fill the types behind these signatures without a Manager.

A transcript of a round that stays on the rails

You

I will design an in-process parking lot. Use cases: park and exit. Out of scope: payments company, phone app, multiple cities. Two gates means two threads sharing spots. Is that the right altitude?

Interviewer

Yes. Start with cars and bikes. We may add EV later.

You

Public API: Ticket park(Vehicle), Receipt exit(TicketId). Invariant: a spot holds at most one vehicle. I will put occupy and vacate on the spot. Pricing and spot choice will be small types so EV can land later without editing the lot.

Interviewer

How do you pick a free spot?

You

SpotChooser. Nearest-empty today. If you want EV-only stalls, that is a second implementation, not a new if in park.

You

I will code the happy path next, then lot-full, then we can add hourly EV pricing.

Notice the candidate never opened with “Factory plus Strategy.” They placed jobs, named the seam, and asked permission before going deeper. That is the round.

Implement in this order, not in pattern order

  1. Enums and value types: VehicleType, Money, TicketId. They make the API readable.
  2. Entities with one job: ParkingSpot.occupy, Ticket as a record of a stay.
  3. Collaborator interfaces: SpotChooser, Pricing, TicketBook.
  4. ParkingService walking those collaborators on the happy path.
  5. LotFullException and UnknownTicketException as types, not strings.
  6. If time remains, a second Pricing or a synchronized occupy for two gates.

If you feel lost

Return to the contract. Say park and exit again. Ask which verb has no home. Assign that verb. Then type. Silent staring at a blank class list is how the Manager comes back — every leftover method dumped into one file so something compiles.

One illegal case, written as a type

Interviewers watch whether failure is designed or shrugged. A dedicated exception (or a Result type) shows the API has edges. A string message inside a generic runtime exception does not.

java
class LotFullException extends RuntimeException {
  LotFullException(VehicleType type) {
    super("no free spot for " + type);
  }
}

class UnknownTicketException extends RuntimeException {
  UnknownTicketException(TicketId id) {
    super("unknown ticket " + id.value());
  }
}

Optional<ParkingSpot> choose(Vehicle vehicle); // empty means full

Ticket require(TicketId id) {
  Ticket ticket = byId.get(id);
  if (ticket == null || ticket.closed()) {
    throw new UnknownTicketException(id);
  }
  return ticket;
}

Checkpoints you can reuse

Clarify

Who acts? One program or many machines? What is out of scope? Two gates or one? Payments today or later?

API

Commands and queries with real types. Not process(Object). Write signatures on the board and wait for a nod.

Model

Entities, values, who owns whom. One invariant said out loud: at most one vehicle per spot.

Assign

Each verb gets a home: entity, service, store, strategy, controller, factory. If every home is the service, keep splitting.

Implement

Happy path, then a bad ticket, then a race if two gates share a spot.

Extend

Show the gap: new vehicle type, new fee rule, new handler. A new type, not a new if.

Two mistakes that steal the first ten minutes

Jumping to a pattern

Strategy and Factory are tools. They come after you know who allocates and who prices. Opening with a pattern name wastes the first ten minutes and hides the missing owner.

Coding before the API is agreed

If park and exit are still fuzzy, you will rewrite the core. Speak the contract. Get a nod. Then implement. Quiet typing looks like guessing, which the scorecard punishes.

Continue from here

Takeaway

Talk first. Agree scope and a tiny API. Name types. Assign verbs. Code the happy path, then one illegal case, then show where the follow-up lands. The diagram can stay messy. The contract cannot. Next we take a messy requirements paragraph — vehicles enter, get a ticket, leave, pay by time — and turn it into a class list that does not include ParkingLotManager.