Skip to content

SE 11 API Design Principles

Dr M H B Ariyaratne edited this page Jun 8, 2026 · 1 revision

SE-11: API Design Principles

Part of the Software Engineering Principles series


What Is an API?

An API (Application Programming Interface) is a defined contract that allows software components to communicate. An API specifies what operations are available, what inputs they expect, and what outputs they produce — without exposing how they work internally.

APIs exist at many levels:

  • Class-level — the public methods of a Java class
  • Library-level — the public surface of a JAR file
  • Service-level — HTTP endpoints a service exposes to other systems

This article focuses primarily on HTTP/REST APIs, which are the most common form in modern web applications, but the principles apply broadly.


REST Principles

REST (Representational State Transfer) is an architectural style for web APIs defined by Roy Fielding in 2000. A REST API models resources and uses HTTP methods to manipulate them.

The Six Constraints of REST

Constraint Meaning
Stateless Each request contains all information needed; the server stores no session state per client
Client-Server UI concerns and data concerns are separated
Cacheable Responses must declare whether they can be cached
Uniform Interface Resources are identified by URIs; manipulation is through representations
Layered System Clients do not know whether they are connected directly or through intermediaries
Code on Demand (optional) Servers may send executable code to clients

Resource Design

Think in Resources, Not Actions

REST models things (resources), not operations. URLs should be nouns, not verbs.

// Bad — RPC-style, action-oriented
POST /createPatient
POST /getPatientById?id=42
POST /deletePatient?id=42

// Good — REST-style, resource-oriented
POST   /patients          (create)
GET    /patients/42       (read)
PUT    /patients/42       (replace)
PATCH  /patients/42       (partial update)
DELETE /patients/42       (delete)

Use Plural Nouns for Collections

/patients          — collection of all patients
/patients/42       — a specific patient
/patients/42/bills — bills belonging to patient 42

Hierarchical URIs Reflect Relationships

/departments/5/stock              — stock in department 5
/departments/5/stock/item-12      — a specific stock item in department 5
/prescriptions/99/items           — items in prescription 99

Avoid going deeper than 2–3 levels — deeply nested URIs become unwieldy.


HTTP Methods

Method Purpose Safe? Idempotent?
GET Retrieve a resource Yes Yes
POST Create a new resource No No
PUT Replace a resource entirely No Yes
PATCH Partially update a resource No No
DELETE Remove a resource No Yes

Safe: The operation has no side effects (no data modification).

Idempotent: Calling the operation multiple times produces the same result as calling it once. PUT and DELETE are idempotent: deleting an already-deleted resource should return success (or 404), not an error.


HTTP Status Codes

Status codes tell the caller exactly what happened. Use them precisely.

Success (2xx)

Code Meaning When to use
200 OK Success GET, PUT, PATCH responses
201 Created Resource created POST that creates a resource
204 No Content Success with no body DELETE, or PUT/PATCH with no response needed

Client Errors (4xx)

Code Meaning When to use
400 Bad Request Invalid request syntax or data Validation failures
401 Unauthorized Not authenticated Missing or invalid credentials
403 Forbidden Authenticated but not authorised Valid user, insufficient privileges
404 Not Found Resource does not exist Patient 42 does not exist
409 Conflict Request conflicts with current state Duplicate patient, stock already finalised
422 Unprocessable Entity Valid syntax but semantic errors Business rule violation

Server Errors (5xx)

Code Meaning When to use
500 Internal Server Error Unexpected failure Unhandled exception
503 Service Unavailable Temporarily unable to handle requests During maintenance or overload

Request and Response Design

Consistent JSON Structure

Adopt a consistent envelope for all responses:

// Success
{
  "success": true,
  "data": {
    "id": 42,
    "name": "Alice Perera",
    "dateOfBirth": "1985-03-12"
  }
}

// Error
{
  "success": false,
  "error": {
    "code": "PATIENT_NOT_FOUND",
    "message": "No patient found with id 42"
  }
}

Naming Conventions

Use camelCase for JSON field names (consistent with JavaScript conventions):

{
  "patientId": 42,
  "dateOfBirth": "1985-03-12",
  "isActive": true,
  "admissionCount": 3
}

Dates and Times

Always use ISO 8601 format. Always include the timezone offset.

{
  "admittedAt": "2025-06-08T14:30:00+05:30",
  "dateOfBirth": "1985-03-12"
}

Versioning

APIs change over time. Versioning allows existing clients to continue working while new clients use the improved version.

URI Versioning (most common)

/api/v1/patients
/api/v2/patients

Header Versioning

GET /api/patients
Accept: application/vnd.hmis.v2+json

Versioning Rules

  • Never make breaking changes to an existing version
  • A breaking change is anything that causes existing clients to fail: removing a field, changing a field type, changing a status code, removing an endpoint
  • Adding new optional fields is backward-compatible (not a breaking change)
  • Deprecate old versions before removing them; communicate timelines clearly

Documentation

An undocumented API is unusable. Good API documentation includes:

  • Overview — what the API does and who it is for
  • Authentication — how to obtain and use credentials
  • Endpoints — URL, method, parameters, request body, responses, error codes
  • Examples — real request/response pairs

OpenAPI / Swagger

The OpenAPI Specification is the standard for documenting REST APIs. It produces machine-readable documentation that can generate client SDKs, server stubs, and interactive documentation (Swagger UI).

paths:
  /patients/{id}:
    get:
      summary: Get patient by ID
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Patient found
        '404':
          description: Patient not found

API Design Principles Summary

Principle Practice
Design for the caller Make the API easy to understand and use; hide internal complexity
Be consistent Same patterns for naming, errors, and formats throughout
Be explicit Use precise HTTP methods and status codes
Fail clearly Error messages must tell the caller what went wrong and how to fix it
Version from day one Assume the API will change; plan for it
Document everything If it is not documented, it does not exist for consumers
Validate early Reject bad input at the boundary with a clear error, not deep inside processing
Make it predictable Callers should be able to infer how untried endpoints work from the patterns they know

Previous: SE-10: Version Control and Git Workflows
Next: SE-12: Security Principles

Back to Software Engineering Principles

Clone this wiki locally