Ready to get started?

Check out the plugin on GitHub and start using it today.

I

Interface Segregation Principle in WordPress

Many specific interfaces are better than one general-purpose interface

The Interface Segregation Principle (ISP) states that no client should be forced to depend on methods it doesn’t use. Split large, “fat” interfaces into smaller, focused ones — each serving a specific need.

Focused Interfaces

No Forced Dependencies

Better Composition

What ISP Requires

Clients should not be forced to depend on interfaces they don’t use. Split fat interfaces into smaller, role-specific ones:

Each interface has a single, clear responsibility

Classes implement only what they actually need

No empty or throw-only method implementations

Composition over one-size-fits-all interfaces

ISP Violation Signs

Watch out for these red flags indicating a “fat” interface problem:

Methods implemented as empty or throwing exceptions

Classes that “accidentally” depend on unrelated methods

Interface changes break unrelated implementing classes

Interface has a name like Manager or Handler

The Classic Worker / Robot Problem

The most famous ISP example — a fat interface forces irrelevant methods on implementing classes

Violating ISP

Fat interface forces robot to “eat” and “sleep”

// ❌ Violating ISP - one fat interface forces all methods
interface Worker {
    public function work(): void;
    public function eat(): void;
    public function sleep(): void;
    public function takeBreak(): void;
}

// Robot is forced to implement methods it doesn't need
class Robot implements Worker {
    public function work(): void {
        echo 'Robot working...';
    }
    public function eat(): void {
        // Robot doesn't eat! Forced empty implementation
        throw new \Exception("Robots don't eat!");
    }
    public function sleep(): void {
        // Robot doesn't sleep either
        throw new \Exception("Robots don't sleep!");
    }
    public function takeBreak(): void {
        // Meaningless for a robot
    }
}

PROBLEMS:

Robot is forced to implement meaningless methods

Adding a new human method breaks Robot class too

Violates SRP — interface has multiple unrelated roles

Following ISP

Segregated interfaces — each class picks what it needs

// ✅ ISP compliant - segregated interfaces
interface Workable {
    public function work(): void;
}

interface Feedable {
    public function eat(): void;
}

interface Restable {
    public function sleep(): void;
    public function takeBreak(): void;
}

// Robot only implements what it needs
class Robot implements Workable {
    public function work(): void {
        echo 'Robot working at full speed!';
    }
}

// Human implements everything relevant
class Human implements Workable, Feedable, Restable {
    public function work(): void { echo 'Human working...'; }
    public function eat(): void { echo 'Having lunch...'; }
    public function sleep(): void { echo 'Sleeping...'; }
    public function takeBreak(): void { echo 'Coffee break!'; }
}

BENEFITS:

Robot only implements Workable

Changes to Feedable don’t affect Robot

Easy to add new capabilities without touching old classes

WordPress Examples: Violation vs Compliance

Real WordPress scenarios where ISP improves plugin architecture

One Massive Block Interface

All blocks forced to handle render, save, transform, variations, styles, deprecated…

// ❌ One massive block interface
interface Block_Interface {
    public function render(): string;
    public function save(): string;
    public function get_attributes(): array;
    public function validate(): bool;
    public function transform_to(): array;   // not all blocks transform
    public function get_variations(): array; // not all blocks have variations
    public function get_styles(): array;     // not all blocks have styles
    public function get_deprecated(): array; // new blocks have no deprecated
}

PROBLEMS:

New simple block must implement 8 methods

Most return empty arrays — noise in code

Breaks every block when interface changes

Role-Based Block Interfaces

Renderable_Block, Transformable_Block, Styleable_Block, Deprecated_Block — blocks pick what they need

// ✅ Segregated block interfaces
interface Renderable_Block {
    public function render(array $attributes, string $content): string;
    public function get_attributes(): array;
}

interface Transformable_Block {
    public function transform_to(string $block_name): array;
}

interface Styleable_Block {
    public function get_styles(): array;
}

interface Deprecated_Block {
    public function get_deprecated(): array;
}

// Simple block - only what it needs
class Quote_Block implements Renderable_Block {
    public function render(array $attributes, string $content): string {
        return '<blockquote>' . $content . '</blockquote>';
    }
    public function get_attributes(): array {
        return ['citation' => ['type' => 'string']];
    }
}

// Complex block - implements more interfaces
class Cover_Block implements Renderable_Block, Styleable_Block, Deprecated_Block {
    public function render(array $attributes, string $content): string { /* ... */ }
    public function get_attributes(): array { /* ... */ }
    public function get_styles(): array { return ['default', 'light-text', 'dark-text']; }
    public function get_deprecated(): array { /* backward compat */ }
}

BENEFITS:

Simple Quote block has only 2 methods

Cover opts-in to styles and deprecated

Each interface can be tested in isolation

Fat Plugin Interface

All plugins forced to implement everything

// ❌ Fat WordPress plugin interface forces too much
interface WP_Plugin_Interface {
    public function register_post_type(): void;
    public function register_taxonomy(): void;
    public function register_rest_route(): void;
    public function register_widget(): void;
    public function register_block(): void;
    public function enqueue_scripts(): void;
    public function add_admin_menu(): void;
    public function handle_cron(): void;
}

// Simple plugin forced to implement everything
class Simple_SEO_Plugin implements WP_Plugin_Interface {
    public function register_post_type(): void {} // not needed!
    public function register_taxonomy(): void {}   // not needed!
    public function register_rest_route(): void {}
    public function register_widget(): void {}     // not needed!
    public function register_block(): void {}      // not needed!
    public function enqueue_scripts(): void { /* actual logic */ }
    public function add_admin_menu(): void { /* actual logic */ }
    public function handle_cron(): void {}         // not needed!
}// ❌ Fat WordPress plugin interface forces too much
interface WP_Plugin_Interface {
    public function register_post_type(): void;
    public function register_taxonomy(): void;
    public function register_rest_route(): void;
    public function register_widget(): void;
    public function register_block(): void;
    public function enqueue_scripts(): void;
    public function add_admin_menu(): void;
    public function handle_cron(): void;
}

// Simple plugin forced to implement everything
class Simple_SEO_Plugin implements WP_Plugin_Interface {
    public function register_post_type(): void {} // not needed!
    public function register_taxonomy(): void {}   // not needed!
    public function register_rest_route(): void {}
    public function register_widget(): void {}     // not needed!
    public function register_block(): void {}      // not needed!
    public function enqueue_scripts(): void { /* actual logic */ }
    public function add_admin_menu(): void { /* actual logic */ }
    public function handle_cron(): void {}         // not needed!
}

PROBLEMS:

Simple SEO plugin has 6 empty methods

Adding a new method breaks every plugin

Hard to test — too many responsibilities

Segregated Interfaces

Each plugin picks only what it needs (Has_Scripts, Has_Admin_Menu, Has_Rest_Routes…)

// ✅ ISP compliant - small focused interfaces
interface Has_Scripts {
    public function enqueue_scripts(): void;
}

interface Has_Admin_Menu {
    public function add_admin_menu(): void;
}

interface Has_Rest_Routes {
    public function register_rest_route(): void;
}

interface Has_Cron {
    public function handle_cron(): void;
}

interface Has_Post_Types {
    public function register_post_type(): void;
}

// Each plugin implements only what it actually needs
class Simple_SEO_Plugin implements Has_Scripts, Has_Admin_Menu {
    public function enqueue_scripts(): void {
        wp_enqueue_style('seo-plugin', plugin_dir_url(__FILE__) . 'css/seo.css');
    }
    public function add_admin_menu(): void {
        add_options_page('SEO Settings', 'SEO', 'manage_options', 'seo-settings', [$this, 'render_settings']);
    }
}

class Advanced_Plugin implements Has_Scripts, Has_Admin_Menu, Has_Rest_Routes, Has_Cron {
    public function enqueue_scripts(): void { /* ... */ }
    public function add_admin_menu(): void { /* ... */ }
    public function register_rest_route(): void { /* ... */ }
    public function handle_cron(): void { /* ... */ }
}

BENEFITS:

Simple SEO plugin has zero unused methods

Adding Has_Cron doesn’t break SEO plugin

Easy to test each interface independently

Common ISP Violations in WordPress

Patterns that lead to fat interfaces and forced dependencies

The “God Interface” Anti-Pattern

One interface for everything — all plugins/classes implement one massive contract.

Fix: Split by role: Has_Scripts, Has_Admin, Has_REST. Use composition.

Empty Method Implementations

Classes implement methods with empty bodies return []; just to satisfy the contract.

Fix: If a class consistently returns empty, that method doesn’t belong in this class’s interface.

WP_Widget Misuse

Extending WP_Widget just for helpers — then leaving widget(), form(), update() empty.

Fix: Only extend WP_Widget when you need actual widget functionality. Otherwise extract helpers into a standalone class.

REST Controller Overload

One REST controller handling reads, writes, admin, bulk — all in one class.

Fix: Separate into Read_Controller, Write_Controller, Admin_Controller each with a focused interface.

Frequently Asked Questions

How small should an interface be?

A: An interface should represent a single role or capability. If you can name it with a single verb-based noun (Renderable, Cacheable, Hookable), it’s probably the right size. If you need “and” in the name — split it.

Doesn’t ISP lead to too many interfaces?

A: More interfaces is not a problem — it’s a feature. Many small interfaces are easier to understand, compose, and test than a few large ones. Name them clearly so the codebase stays readable.

How does ISP relate to WordPress hooks?

A: WordPress hooks are a natural ISP implementation. Focused hooks: wp_enqueue_scripts, init, admin_menu — each plugin only hooks into what it actually needs.

Is ISP the same as Single Responsibility?

A: They’re related but different. SRP is about classes having one reason to change. ISP is about interfaces not forcing clients to depend on methods they don’t use. Violating ISP often leads to SRP violations.

Official WordPress Resources

How ISP applies in the WordPress ecosystem

Plugin API / Hooks

WordPress hooks are a perfect example of ISP — subscribe to only the hooks you need

Block Editor Handbook

Block registration uses focused APIs — blocks implement only the features they support

PHP-FIG PSR Standards

PSR interfaces (PSR-3 Logger, PSR-7 HTTP) are excellent ISP examples

Focused interfaces mean your classes only depend on what they use. Changes to unrelated features never break your code. Testing becomes trivial.

Classes only implement what they actually need — no empty methods, no unused contracts

Adding a new interface doesn’t break existing implementations — they simply don’t implement it

Mix and match small interfaces to build exactly the capabilities each class needs