Syllabus · F5
Clock management and finishing
- Tier 3
- Process
- 6 min read
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:
no-main— You designed well, but there's no entry point. Interviewers runmainfirst; a missing driver caps functionality at 0 regardless of how clean your code is.ran-out-of-clock— You ran out of time. Phase 3 gives 30 minutes. If you hit that wall before all tests pass, you get tagged.suite-not-green— Your code compiles and has a main, but the test suite doesn't fully pass. You stopped writing tests or ran out of time before fixing failures.
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
- Phase 2 (skeleton): 12 minutes. Rough, proves you thought about the types.
- Phase 3 (core build): 30 minutes. Full implementation, tests, finished code.
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:
- Your code compiles with zero errors
- A
public static void main(String[] args)method exists and can run - All base-suite tests pass (look at
summarise()in the test framework) - You still have time left on the clock (ideally 3-5 minutes)
Common moves that fail the clock
- Writing tests for the curveball before the base suite is green. Green the base first. Always.
- Starting the curveball and then panicking halfway. If the curveball doesn't land by minute 28, step back. You don't lose points for not attempting it; you lose them for abandoning code halfway.
- Polishing code late. At minute 25, if the suite is green and main exists, stop refactoring. That's done.
- No main method at all. Some engineers jump straight to tests. A trivial main that instantiates a service and calls one method is better than nothing.
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:
- Ask yourself: does my code compile? Is main there? Do the base tests pass?
- If yes to all three, you've earned level 2 on functionality. Done.
- If no, use these last minutes to fix ONE critical issue in this order: compile → main → test.
- Compilation is mandatory. A missing main is the next priority. Failing tests are last.
- 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
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.