Overview
API and interface design is choosing the public methods, parameters, return types, and error behavior that other code will depend on. A good API is easy to use correctly and hard to use incorrectly: it uses domain vocabulary, small focused methods, clear types instead of primitives, predictable errors, and stable contracts.
In LLD interviews, the public API of your main classes is often what the interviewer evaluates first: park(vehicle) returning a Ticket, not assign(int floor, int row, int col, String plate, int type). Designing the API before internals keeps the solution centered on use cases.
Good machines offer 'Espresso', 'Latte', 'Hot water'. Bad ones ask you to set water temperature, pressure, and grind size separately for every cup. Both work, but only one is hard to misuse.
When to use it
- Defining the entry points of any class or module.
- Library and SDK design.
- The first coding step in an LLD interview.
Where it shows up in interviews
Recognize it when: 'what methods would your class expose?'
- Design a parking lot API
- Design a rate limiter interface
- Design a cache interface
Recognize it when: reusable component for other teams.
- Design a logging framework
- Design a task scheduler
Where it is used in real software
java.time replaced error-prone Date/Calendar APIs with immutable, well-named types.
Praised for consistent naming, idempotency keys, and expandable objects.
'APIs should be easy to use and hard to misuse' is a widely cited principle.
Key terms
- Contract
- Inputs, outputs, errors, and side effects callers can rely on.
- Primitive obsession
- Using raw strings and ints for domain concepts.
- Command-query separation
- Methods either change state or return data, not both.
- Backward compatibility
- Changes that do not break existing callers.
- Fail fast
- Reject invalid input immediately with a clear error.
How it works, step by step
- 1Start from use cases
Each use case needs an entry point.
- 2Name with domain verbs
park, exit, reserve, cancel.
- 3Use rich types
Vehicle, Money, TicketId instead of strings and ints.
- 4Define errors
Domain exceptions or result types for expected failures.
- 5Minimize surface
Expose only what callers need; keep the rest private.
Weak vs strong API
Parking lot entry
| Aspect | Weak | Strong |
|---|---|---|
| Signature | assign(int, int, String, int) | park(vehicle: Vehicle): Ticket |
| Meaning of arguments | Which int is floor? | Types document themselves |
| Failure | Returns -1 | Throws LotFullException / returns Result |
| Future change (add EV spots) | New int flag | Vehicle subtype or spot feature |
NOWAspect: Signature | Weak: assign(int, int, String, int) | Strong: park(vehicle: Vehicle): Ticket
Strong types and domain names make incorrect calls hard to write.
Implementation
type TicketId = string & { readonly brand: "TicketId" };type Money = { cents: number; currency: "USD" }; type ParkResult = | { ok: true; ticketId: TicketId; spot: string } | { ok: false; reason: "LOT_FULL" | "VEHICLE_ALREADY_PARKED" }; export interface ParkingLot { park(vehicle: Vehicle): ParkResult; // command with an explicit outcome exit(ticketId: TicketId, at: Date): Money; // throws UnknownTicket for programmer errors availability(): ReadonlyMap<VehicleSize, number>; // query, no side effects} // Callers must handle both outcomes; the compiler enforces itconst result = lot.park(new Car("KA-01"));if (!result.ok) console.log("Cannot park:", result.reason);Complexity and performance
Small surface.
Design carefully.
Trade-offs
Exceptions keep happy paths clean; result types force callers to handle expected failures.
Many optional parameters make APIs powerful but confusing; use builders or separate methods.
Variants and related techniques
Chainable methods for configuration and queries.
REST and gRPC follow the same principles plus versioning and idempotency.
Common mistakes
- Boolean flag parameters (book(seat, true)).
Fix: Use separate methods or an enum.
- Returning null for 'not found'.
Fix: Return Optional or a result type.
- Leaking internal types.
Fix: Return domain types or read-only views.
Interview questions
What makes an API easy to use correctly?
Domain names, rich types instead of primitives, few parameters, predictable errors, no hidden side effects in queries, and sensible defaults.
Exceptions or return codes for 'lot full'?
It is an expected business outcome, so a result type or a specific domain exception both work; the key is that callers are forced or clearly guided to handle it, unlike magic values like -1.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| Design the public API of an LRU cache | Easy | Contracts. |
| Design the API of a task scheduler library | Medium | Builders and errors. |