Skip to content
Low Level Design Mastery Logo
LowLevelDesign Mastery

Contract and API Definitions

Master contract and API definitions - the bridge between design and implementation!

Contracts define how classes interact - they’re the agreements between components. Well-defined contracts lead to:

  • Testable code - Clear interfaces to mock
  • Flexible design - Can swap implementations
  • Clear documentation - Self-documenting code
  • Error prevention - Compile-time checks
Diagram

A contract defines:

  • What a class/method does
  • Inputs - Parameters and types
  • Outputs - Return types
  • Exceptions - What can go wrong
  • Preconditions - What must be true before
  • Postconditions - What will be true after
  1. Interface Contracts - Abstract interfaces
  2. Method Contracts - Method signatures
  3. API Contracts - REST/HTTP APIs
  4. Class Contracts - Class responsibilities
Diagram

Interface contracts define what a class must implement without specifying how.

payment_processor.py
from abc import ABC, abstractmethod
from typing import Dict, Any
class PaymentProcessor(ABC):
"""
Interface for payment processing.
Contract:
- Must process payments
- Must handle refunds
- Must validate payment methods
"""
@abstractmethod
def process_payment(self, amount: float, payment_method: str,
payment_data: Dict[str, Any]) -> Dict[str, Any]:
"""
Process a payment.
Args:
amount: Payment amount (must be > 0)
payment_method: Payment method (e.g., "credit_card", "paypal")
payment_data: Payment-specific data (card token, email, etc.)
Returns:
Dict containing:
- status: "success" or "failed"
- transaction_id: Unique transaction ID
- message: Optional message
Raises:
ValueError: If amount <= 0 or invalid payment_data
PaymentFailedException: If payment processing fails
"""
pass
@abstractmethod
def refund_payment(self, transaction_id: str, amount: float) -> Dict[str, Any]:
"""
Refund a payment.
Args:
transaction_id: Original transaction ID
amount: Refund amount (must be > 0 and <= original amount)
Returns:
Dict containing:
- status: "success" or "failed"
- refund_id: Unique refund ID
Raises:
ValueError: If amount <= 0 or transaction_id invalid
RefundFailedException: If refund fails
"""
pass
@abstractmethod
def validate_payment_method(self, payment_method: str) -> bool:
"""
Validate if payment method is supported.
Args:
payment_method: Payment method to validate
Returns:
True if supported, False otherwise
"""
pass
Diagram
  1. Method signatures - What methods exist
  2. Parameter types - What inputs are expected
  3. Return types - What outputs are guaranteed
  4. Exceptions - What can go wrong
  5. Documentation - Clear description of behavior

Method contracts define the behavior of individual methods - inputs, outputs, and exceptions.

parking_lot_methods.py
from typing import Optional
from datetime import datetime
class ParkingLot:
"""
Manages parking operations.
"""
def park_vehicle(self, vehicle: Vehicle) -> Ticket:
"""
Parks a vehicle and returns a ticket.
Contract:
- Precondition: Vehicle must be valid and parking lot must have available spots
- Postcondition: Vehicle is parked, ticket is issued, spot is occupied
Args:
vehicle: Vehicle to park (must not be None)
Returns:
Ticket: Parking ticket with entry details
Raises:
ValueError: If vehicle is None or invalid
ParkingLotFullException: If no spots available for vehicle type
InvalidVehicleException: If vehicle type not supported
Example:
>>> vehicle = Car("ABC123")
>>> ticket = parking_lot.park_vehicle(vehicle)
>>> print(ticket.ticket_id)
"TICKET_12345"
"""
if vehicle is None:
raise ValueError("Vehicle cannot be None")
if not self._is_vehicle_type_supported(vehicle.get_vehicle_type()):
raise InvalidVehicleException(f"Vehicle type {vehicle.get_vehicle_type()} not supported")
spot = self._find_available_spot(vehicle.get_vehicle_type())
if spot is None:
raise ParkingLotFullException("No available spots")
spot.park_vehicle(vehicle)
ticket = Ticket(vehicle, spot, datetime.now())
self._tickets[ticket.ticket_id] = ticket
return ticket
def unpark_vehicle(self, ticket_id: str) -> Payment:
"""
Unparks a vehicle and processes payment.
Contract:
- Precondition: Ticket must be valid and vehicle must be parked
- Postcondition: Vehicle is unparked, spot is available, payment is processed
Args:
ticket_id: Parking ticket ID (must not be None or empty)
Returns:
Payment: Payment transaction details
Raises:
ValueError: If ticket_id is None or empty
InvalidTicketException: If ticket is invalid or already used
VehicleNotParkedException: If vehicle is not currently parked
Example:
>>> payment = parking_lot.unpark_vehicle("TICKET_12345")
>>> print(payment.amount)
25.50
"""
if not ticket_id or ticket_id.strip() == "":
raise ValueError("Ticket ID cannot be None or empty")
if ticket_id not in self._tickets:
raise InvalidTicketException(f"Ticket {ticket_id} not found")
ticket = self._tickets[ticket_id]
if ticket.is_returned():
raise InvalidTicketException(f"Ticket {ticket_id} already used")
spot = ticket.spot
vehicle = spot.unpark_vehicle()
duration = ticket.calculate_duration()
amount = self._calculate_payment(duration, vehicle.get_vehicle_type())
payment = Payment(ticket, amount, datetime.now())
ticket.mark_as_returned()
return payment
def find_available_spots(self, vehicle_type: VehicleType) -> List[ParkingSpot]:
"""
Finds all available spots for a vehicle type.
Contract:
- Precondition: Vehicle type must be valid
- Postcondition: Returns list of available spots (may be empty)
Args:
vehicle_type: Type of vehicle (must not be None)
Returns:
List[ParkingSpot]: List of available spots (empty if none available)
Raises:
ValueError: If vehicle_type is None
Example:
>>> spots = parking_lot.find_available_spots(VehicleType.CAR)
>>> print(len(spots))
5
"""
if vehicle_type is None:
raise ValueError("Vehicle type cannot be None")
return [spot for spot in self._spots
if spot.is_available() and spot.get_type() == vehicle_type]
  1. Method signature - Name, parameters, return type
  2. Preconditions - What must be true before calling
  3. Postconditions - What will be true after calling
  4. Exceptions - What can go wrong
  5. Documentation - Clear description
Diagram

API contracts define how external systems interact with your system via HTTP/REST APIs.

api_spec.yaml
# OpenAPI/Swagger Specification
paths:
/api/v1/parking/park:
post:
summary: Park a vehicle
description: Parks a vehicle and returns a parking ticket
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- vehicleType
- licensePlate
properties:
vehicleType:
type: string
enum: [CAR, MOTORCYCLE, TRUCK]
description: Type of vehicle
licensePlate:
type: string
minLength: 1
maxLength: 20
description: Vehicle license plate
responses:
'200':
description: Vehicle parked successfully
content:
application/json:
schema:
type: object
properties:
ticketId:
type: string
example: "TICKET_12345"
entryTime:
type: string
format: date-time
spotId:
type: string
example: "SPOT_A1"
'400':
description: Bad request (invalid input)
content:
application/json:
schema:
type: object
properties:
error:
type: string
example: "Invalid vehicle type"
'404':
description: No available spots
content:
application/json:
schema:
type: object
properties:
error:
type: string
example: "Parking lot is full"
/api/v1/parking/unpark:
post:
summary: Unpark a vehicle
description: Unparks a vehicle and processes payment
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- ticketId
properties:
ticketId:
type: string
description: Parking ticket ID
responses:
'200':
description: Vehicle unparked successfully
content:
application/json:
schema:
type: object
properties:
paymentId:
type: string
example: "PAYMENT_67890"
amount:
type: number
format: float
example: 25.50
duration:
type: number
format: float
example: 2.5
'400':
description: Bad request
'404':
description: Ticket not found
/api/v1/parking/spots:
get:
summary: Get available spots
description: Returns available parking spots for a vehicle type
parameters:
- name: vehicleType
in: query
required: true
schema:
type: string
enum: [CAR, MOTORCYCLE, TRUCK]
responses:
'200':
description: List of available spots
content:
application/json:
schema:
type: object
properties:
spots:
type: array
items:
type: object
properties:
spotId:
type: string
spotType:
type: string
location:
type: string
  1. Endpoint - URL and HTTP method
  2. Request - Body, parameters, headers
  3. Response - Status codes, body format
  4. Error handling - Error codes and messages
  5. Authentication - How to authenticate
Diagram

Exceptions are part of the contract - they define what can go wrong and how to handle it.

  1. Validation Exceptions - Invalid input
  2. Business Logic Exceptions - Business rule violations
  3. System Exceptions - System failures
  4. Not Found Exceptions - Resource not found
exceptions.py
class ParkingLotException(Exception):
"""Base exception for parking lot operations"""
pass
class ParkingLotFullException(ParkingLotException):
"""Raised when parking lot is full"""
pass
class InvalidVehicleException(ParkingLotException):
"""Raised when vehicle type is invalid"""
pass
class InvalidTicketException(ParkingLotException):
"""Raised when ticket is invalid"""
pass
class VehicleNotParkedException(ParkingLotException):
"""Raised when vehicle is not currently parked"""
pass
# Usage in contract
def park_vehicle(self, vehicle: Vehicle) -> Ticket:
"""
Parks a vehicle.
Raises:
ValueError: If vehicle is None
InvalidVehicleException: If vehicle type not supported
ParkingLotFullException: If no spots available
"""
if vehicle is None:
raise ValueError("Vehicle cannot be None")
if not self._is_vehicle_type_supported(vehicle.get_vehicle_type()):
raise InvalidVehicleException(f"Vehicle type {vehicle.get_vehicle_type()} not supported")
spot = self._find_available_spot(vehicle.get_vehicle_type())
if spot is None:
raise ParkingLotFullException("No available spots")
# ... rest of implementation

Be explicit - Clear parameter and return types
Document exceptions - What can go wrong?
Use meaningful names - Self-documenting code
Define preconditions - What must be true before?
Define postconditions - What will be true after?
Handle edge cases - Null checks, validation
Use interfaces - For abstraction and flexibility

Don’t be vague - Unclear contracts lead to bugs
Don’t forget exceptions - Document what can fail
Don’t expose implementation - Hide internal details
Don’t break contracts - Once defined, maintain them
Don’t over-complicate - Keep contracts simple

Diagram

Contracts define agreements - What classes/methods will do
Interfaces - Abstract contracts for flexibility
Method contracts - Preconditions, postconditions, exceptions
API contracts - REST/HTTP API specifications
Exception handling - Part of the contract
Documentation - Clear and comprehensive
Best practices - Explicit, documented, maintainable

When defining contracts, ensure:

  • Clear signatures - Parameters and return types
  • Documented exceptions - What can go wrong?
  • Preconditions - What must be true before?
  • Postconditions - What will be true after?
  • Well documented - Clear descriptions
  • Edge cases handled - Null checks, validation
  • Consistent - Follow same patterns
Diagram

Congratulations! You’ve now mastered all the key steps of LLD interviews:

  1. Understanding LLD Interviews - What they are and why they matter
  2. Steps in LLD Interview - The systematic approach
  3. Identifying Actors & Entities - Foundation of design
  4. Assign Responsibilities - Single Responsibility Principle
  5. Class Diagrams - Visualize your design
  6. Contract and API Definitions - Define interfaces

Before your LLD interview, make sure you can:

  • Understand the problem - Ask clarifying questions
  • Identify actors - Who uses the system?
  • Identify entities - What are the core objects?
  • Assign responsibilities - One per class
  • Create class diagrams - Visualize relationships
  • Define contracts - Clear interfaces
  • Handle edge cases - Think about errors
  • Implement cleanly - Follow your design