<arch.design/>
Principles/Interface Segregation Principle
{ }CodeArchitectureintermediate1994solidmartinfat-interfacerole-interface

Interface Segregation Principle

Prefer many small, focused interfaces over one large general-purpose one — clients should not be forced to depend on methods they don't use.

3/5
{ }
Operates at: Code level

Inside a codebase — classes, modules, files

How it works

The Interface Segregation Principle (ISP), part of SOLID (Robert C. Martin, 1994), says that no client should be forced to depend on methods it does not use. Fat interfaces force implementors to stub out methods that don't apply to them — a sign the interface models the wrong abstraction.

The canonical violation: a Worker interface with work(), eat(), and sleep(). A Robot implements Worker but must throw NotImplemented on eat() and sleep() — methods that make no sense for it.

The fix: split into Workable, Feedable, and Restable. Human implements all three. Robot implements only Workable. No stubs, no surprises.

ISP also applies to clients: if a caller only needs read operations, it shouldn't depend on an interface that also declares write operations — because any change to the write signature forces a recompile/retest of the reader.

Small interfaces are easier to implement, easier to mock in tests, and easier to compose. They also age better: adding a method to a small, focused interface affects fewer implementors.

Implementation

TypeScript · Go · Rust
// ❌ Fat interface — Robot must stub methods it can never use
interface Worker {
  work(): void;
  eat(): void;
  sleep(): void;
}

class HumanWorker implements Worker {
  work()  { console.log("working"); }
  eat()   { console.log("eating"); }
  sleep() { console.log("sleeping"); }
}

class Robot implements Worker {
  work()  { console.log("working"); }
  eat()   { throw new Error("Robots do not eat"); }  // ❌ forced stub
  sleep() { throw new Error("Robots do not sleep"); }
}

// ✓ Segregated interfaces — each client depends only on what it uses
interface Workable { work(): void; }
interface Feedable  { eat(): void; }
interface Restable  { sleep(): void; }

class HumanWorker implements Workable, Feedable, Restable {
  work()  { console.log("working"); }
  eat()   { console.log("eating"); }
  sleep() { console.log("sleeping"); }
}

class Robot implements Workable {
  work() { console.log("working"); } // ✓ no stubs, no surprises
}

Why it matters

Fat interfaces create unnecessary coupling. Classes implementing methods they don't use, test doubles mocking operations they'll never call, and callers recompiling because an unrelated method changed — all signs of ISP violation.

When to use

  • When an interface has methods that some implementors must stub with NotImplemented
  • When a client imports an interface but only uses two of its ten methods
  • When splitting an interface would let different teams evolve their parts independently
  • When designing public library APIs — lean interfaces are more stable

When NOT to use

  • Over-segregation creates too many tiny interfaces that must be composed manually
  • A cohesive group of methods that always travel together belongs in one interface

Trade-offs

+

Clients depend only on what they use — changes elsewhere don't affect them

Too many fine-grained interfaces become hard to discover and compose

+

Implementors only implement what's relevant — no forced stubs

Splitting existing interfaces is a breaking change for existing implementors

+

Interfaces are more stable — a method change affects fewer clients

Finding the right granularity requires domain knowledge and experience

In production

Go standard library

io.Reader (one method), io.Writer (one method), io.ReadWriter (both) — compose interfaces rather than one large one

Spring Data

CrudRepository, PagingAndSortingRepository, JpaRepository — progressively richer interfaces; use the smallest that fits

React hooks

useState, useEffect, useRef — each hook is a tiny, focused 'interface' to a single React capability

Industry adoption

3/5Common in specific contexts — used when the problem fits.

Related principles