LLD Dojo

Syllabus · F5

Clock management and finishing

The idea

Clock management — finishing what you start

You walk in with one hour and one blank page. In 50 minutes you have a design sketch and code that's half-there. At 55 minutes you're testing it. At 58 minutes you realize the tests won't compile and your main method doesn't exist. That's exactly where the round ends.

The interview judges three things it can measure: does it compile, does it have a runnable main, and do the tests pass. A missing main is an automatic 0 on functionality — interviewers run it first. A clock running out before all tests pass is a failure tag on its own. These aren't harsh; they're the floor.

The constraint forces a choice: ship a thin, working slice or ship a thick slice that doesn't work. Interviewers would rather see a parking lot that parks one type of vehicle with a fully-passing test suite than one that handles five types but crashes on startup.

You manage the clock by committing to a scope early and protecting the last ten minutes for compilation and a walkthrough. Write a main method that demonstrates the happy path. Get the base suite fully green before you try the curveball. If the curveball drags and time closes, you step back — that's fine. Code that compiles and runs scores higher than clever code that doesn't.

The three failure tags that land here all point to execution discipline, not design. You designed correctly but didn't get to the finish line before the round ended.


Worked walkthrough

Clock Management: Execution Discipline in an Interview

Run it first

Open the reference solution for the rate-limiter problem. Look at Main.java. Read the entire main method. That is your target: a method that is runnable, clear, and shows the core flow without being overly simple.

Why the three failures route here

All three defect tags point to finishing problems, not design problems:

What the grader actually checks

Phase 3 (grade-phase3.mjs, line 22 onwards) checks exactly three things:

const main = hasMain(source)           // Line 32: does a main method exist?
const withinClock = elapsedMs <= 30*60*1000  // Did you finish within 30 minutes?
const level = functionalityLevel({ hasMain: main, ... })  // Line 34

A missing main zeros functionality. You cannot recover that. The grader never overrides it. Line 90 of the standard states it plainly: "A missing driver caps this dimension at 0 — interviewers run it first."

The clock: Phase 2 vs Phase 3

You get one clock per phase. Running out in phase 3 doesn't matter if you passed phase 2. But it shows poor pacing — you either took too long on design or didn't ruthlessly scope the implementation.

The threshold

You're done when:

  1. Your code compiles with zero errors
  2. A public static void main(String[] args) method exists and can run
  3. All base-suite tests pass (look at summarise() in the test framework)
  4. You still have time left on the clock (ideally 3-5 minutes)

Common moves that fail the clock

What a main method shows

It's not about cleverness. It's about demonstrating that you understand what you built and that the code runs. For a parking lot, main might:

ParkingLot lot = new ParkingLot(10);
lot.park(new Vehicle("KA01", VehicleType.CAR));
System.out.println("Fee: " + lot.fee(VehicleType.CAR));

That's it. It shows that instantiation works, a method call works, and the result makes sense. Tests show correctness; main shows that the thing boots.

If you're running out of time

At minute 28:

  1. Ask yourself: does my code compile? Is main there? Do the base tests pass?
  2. If yes to all three, you've earned level 2 on functionality. Done.
  3. If no, use these last minutes to fix ONE critical issue in this order: compile → main → test.
  4. Compilation is mandatory. A missing main is the next priority. Failing tests are last.
  5. Do not start a new feature. Do not refactor. Do not try the curveball.

This is not giving up. This is pacing like an engineer.


Worked source

The 1 file of the worked design

Every file below is the one the app opens, verbatim. This is the part worth reading slowly: the prose above argues for a shape, and these are the lines that have it.

worked/Main.java147 lines

/**
 * Clock Management: Finishing What You Start
 *
 * This main method walks through the rate-limiter implementation to show:
 *   1. How to design for time constraints
 *   2. What a complete main method looks like
 *   3. When to step back and accept the scope
 *
 * See NOTES.md for the strategy of clock management in a timed interview.
 */

import java.time.Clock;
import java.util.HashMap;
import java.util.Map;

public class Main {

  /**
   * A token-bucket rate limiter. The core design: one class, one interface.
   * This is a scope choice — not handling distribution, persistence, or
   * background refill. All of these reshape the design; we explicitly forbade
   * them to finish on time.
   */
  interface TokenBucket {
    boolean allowRequest(String clientId);
  }

  /**
   * Simple token-bucket implementation with demand-driven refill.
   * No background thread, no persistence. Single-process only.
   *
   * Why this design within the clock:
   *   - Refill happens on-demand when a request comes in
   *   - No scheduler thread to manage
   *   - No state to persist
   *   - One file, under 100 lines
   */
  static class RateLimiter implements TokenBucket {
    private final Map<String, ClientState> clients = new HashMap<>();
    private final int tokensPerWindow;
    private final long windowMs;
    private final Clock clock;

    RateLimiter(int tokensPerWindow, long windowMs, Clock clock) {
      this.tokensPerWindow = tokensPerWindow;
      this.windowMs = windowMs;
      this.clock = clock;
    }

    @Override
    public boolean allowRequest(String clientId) {
      long now = clock.millis();
      ClientState state = clients.get(clientId);

      // New client or window expired.
      if (state == null || now >= state.windowEnd) {
        state = new ClientState(now + windowMs, tokensPerWindow);
        clients.put(clientId, state);
      }

      // Consume a token if available.
      if (state.tokensRemaining > 0) {
        state.tokensRemaining--;
        return true;
      }
      return false;
    }

    /**
     * Why this is a static inner class, not a separate file:
     * Scope decision. We're shipping on time. If the interview
     * demanded distributed state, we'd extract this. It doesn't.
     */
    private static class ClientState {
      long windowEnd;
      int tokensRemaining;

      ClientState(long windowEnd, int tokensRemaining) {
        this.windowEnd = windowEnd;
        this.tokensRemaining = tokensRemaining;
      }
    }
  }

  // Entry point — the mandatory part. Never skip this.
  public static void main(String[] args) {
    System.out.println("Rate Limiter: Clock Management");
    System.out.println("=".repeat(60));

    // Scenario 1: Single client, within quota.
    System.out.println("\n=== Scenario 1: Within Quota ===");
    Clock fixedClock = Clock.fixed(
      java.time.Instant.parse("2025-01-01T00:00:00Z"),
      java.time.ZoneId.of("UTC")
    );
    TokenBucket limiter = new RateLimiter(5, 60_000, fixedClock);

    String client = "client-001";
    for (int i = 1; i <= 6; i++) {
      boolean allowed = limiter.allowRequest(client);
      System.out.println("Request " + i + ": " + (allowed ? "ALLOWED" : "DENIED"));
    }
    // Expect: first 5 allowed, 6th denied.

    // Scenario 2: Multiple clients, independent quotas.
    System.out.println("\n=== Scenario 2: Multiple Clients ===");
    limiter = new RateLimiter(3, 60_000, fixedClock);
    String client1 = "user-a";
    String client2 = "user-b";

    System.out.println("User A, request 1: " + limiter.allowRequest(client1)); // true
    System.out.println("User B, request 1: " + limiter.allowRequest(client2)); // true
    System.out.println("User A, request 2: " + limiter.allowRequest(client1)); // true
    System.out.println("User A, request 3: " + limiter.allowRequest(client1)); // true
    System.out.println("User A, request 4: " + limiter.allowRequest(client1)); // false
    System.out.println("User B, request 2: " + limiter.allowRequest(client2)); // true
    System.out.println("User B, request 3: " + limiter.allowRequest(client2)); // true
    System.out.println("User B, request 4: " + limiter.allowRequest(client2)); // false

    // Scenario 3: Window expiry (simplified).
    System.out.println("\n=== Scenario 3: Window Expiry ===");
    System.out.println("At design time, you decided: 'windows don't overlap, only one client.'");
    System.out.println("This scope choice let us finish the implementation in time.");
    System.out.println("We didn't add concurrency, persistence, or background threads.");

    System.out.println("\n" + "=".repeat(60));
    System.out.println("The Clock Test: What to Ship");
    System.out.println("=".repeat(60));

    System.out.println("\nYou have 30 minutes. At minute 28:");
    System.out.println("  ✓ Code compiles? Check.");
    System.out.println("  ✓ main method exists and runs? Check.");
    System.out.println("  ✓ Base suite 100% green? Check.");
    System.out.println("  → Level 2 earned. Clock still running, level 3 within reach.");

    System.out.println("\nIf the curveball (add metrics, handle concurrency, etc.) drags:");
    System.out.println("  1. Stop at minute 28. You've already won.");
    System.out.println("  2. Commit the green state.");
    System.out.println("  3. Do not rewrite half your code chasing level 3.");

    System.out.println("\nWhy this matters in real rounds:");
    System.out.println("  - Interviewers run main first. No main, no score.");
    System.out.println("  - A full test suite beating the clock shows execution discipline.");
    System.out.println("  - Rushed, broken code costs more than careful, finished code.");
    System.out.println("  - You'll interview again. Prove you can ship.");
  }
}

The faded stage is not here, on purpose

In the app, the third stage of this lesson is a written completion: a prompt with blanks, answered in prose and then graded against what the reference extraction expects. The grading needs the app, so this page stops at the worked walkthrough.

Run the app for the drill: it is the download in the header, and it works offline once unpacked.

← F4 · Decision defence and trade-off articulation

← all lessons