OOP & Design PrinciplesSOLID PrinciplesMedium⏱️ ~2 min

Interface Segregation Principle (ISP): Focused Interfaces for Minimal Dependencies

The Interface Segregation Principle states that clients should not be forced to depend on methods they do not use. Instead of one wide interface with dozens of methods, prefer multiple narrow interfaces grouped by cohesive capabilities. When a read only cache client must implement write(), delete(), and flush() methods it never calls, it creates unnecessary coupling and testing burden. ISP says split into ReadOnlyCache and WriteableCache so clients depend only on what they need. At scale, ISP reduces binary bloat, stabilizes the change surface, and improves startup latency. Meta's modular mobile architecture isolates features behind narrow interfaces so the main app depends only on capability contracts. Each feature module declares a small interface (e.g., ProfileDisplay, PaymentFlow), and the app shell loads only the modules it needs for a given user session. This reduces cold start initialization time; feature modules have single digit millisecond initialization budgets to keep p50 and p90 cold start times competitive on billions of devices. Without ISP, every feature would pull in all dependencies, inflating binary size and startup overhead. Microsoft Visual Studio applies ISP to extension points. An extension that provides code completion does not implement unrelated interfaces for debugging or refactoring. This keeps extension initialization lightweight and allows lazy loading; only the capabilities the user activates get loaded into the process. With over 4,000 extensions in the marketplace, a monolithic extension interface would force every extension to implement irrelevant methods, increasing memory and startup cost. The tradeoff is potential interface proliferation. Too many granular interfaces create conceptual complexity, making discoverability harder and forcing clients to manage multiple dependencies. When five separate interfaces must be wired together for a common workflow, the developer experience degrades. Balance ISP with pragmatism: group methods that truly vary together into one interface, but separate capabilities that evolve independently or are used by distinct clients. Amazon's storage abstraction might have ReadOnlyStore, WriteOnlyQueue, and TransactionalStore interfaces instead of one giant Store interface with 30 methods. Measure the impact: if splitting an interface reduces object count at runtime or shortens startup, it is a win; if it just adds boilerplate without measurable benefit, keep it unified.
💡 Key Takeaways
Reduce dependency coupling: A read only cache client depends on get() and contains() methods, not write(), flush(), and evict(). Splitting into ReadOnlyCache and WriteableCache isolates changes; updating cache eviction logic does not recompile read only clients.
Improve startup and memory: Meta mobile features expose narrow interfaces (ProfileDisplay, PaymentFlow). Main app loads only needed modules, keeping cold start initialization under single digit milliseconds per module across billions of devices.
Enable lazy loading: Microsoft Visual Studio loads extension capabilities on demand. A code completion extension does not pull in debugging or refactoring interfaces, reducing memory footprint and shortening startup time with 4,000+ available extensions.
Avoid interface explosion: Too many granular interfaces (one method each) increase conceptual overhead and force clients to wire many dependencies. Group cohesive methods; split only when clients use disjoint subsets or capabilities evolve independently.
Stabilize change surface: When a CacheWriter interface changes flush() semantics, only writers recompile. Readers depending on ReadOnlyCache are unaffected, reducing blast radius and deployment coordination.
Measure before splitting: Track binary size, startup time, and dependency graph complexity. If splitting an interface reduces object count or initialization latency measurably (e.g., 5+ ms saved), proceed; otherwise, defer until variation pressure appears.
📌 Examples
Meta mobile modularization: Feature modules declare narrow interfaces (ProfileDisplay with showProfile(), PaymentFlow with initiatePurchase()). Main app depends only on active features, keeping cold start p90 competitive with single digit millisecond per module initialization budgets.
Microsoft Visual Studio extensions: Code completion extension implements ICompletionProvider, not IDebugger or IRefactoring. Extensions load lazily, reducing memory and startup overhead even with 4,000+ extensions available.
Amazon storage abstraction: ReadOnlyStore (get, list), WriteOnlyQueue (enqueue, batch), and TransactionalStore (begin, commit, rollback) as separate interfaces. Analytics pipelines depend only on ReadOnlyStore, avoiding coupling to transactional logic.
← Back to SOLID Principles Overview