“A good interface is like a good menu: it shows only what you need — nothing more, nothing less.”
If you’ve ever had to implement an interface just to throw NotImplementedException() in half the methods…
You’ve already violated the Interface Segregation Principle (ISP) — the "I" in SOLID.
What does ISP say?
“Clients should not be forced to depend on methods they do not use.”
In practice:
Avoid bloated, one-size-fits-all interfaces
Prefer small, focused contracts
Split responsibilities — one purpose per interface
The problem with fat interfaces
Here’s a common scenario:
public interface IEmployee {
void Work();
void ManageTeam();
void ApproveBudget();
}
Now you need to create a Developer:
public class Developer : IEmployee {
public void Work() { /* okay */ }
public void ManageTeam() { throw new NotImplementedException(); }
public void ApproveBudget() { throw new NotImplementedException(); }
}
This violates ISP:
The class is forced to depend on methods it doesn’t use
It makes testing and maintenance harder
It leads to awkward design choices and code smells
Applying ISP the right way (with C#)
The solution? Split the interface into role-specific contracts.
public interface IWorker {
void Work();
}
public interface IManager {
void ManageTeam();
void ApproveBudget();
}
Now:
public class Developer : IWorker {
public void Work() { /* code */ }
}
public class Manager : IWorker, IManager {
public void Work() { /* code / }
public void ManageTeam() { / code / }
public void ApproveBudget() { / code */ }
}
Each class depends only on what it actually needs.
A simple analogy: the remote control
A TV remote should give you exactly what you need: volume, channels, power.
Now imagine it also had buttons for:
Opening your garage
Launching Spotify
Turning on the coffee machine
Cool? Maybe.
Useful? Only if you need it.
For most users? Just noise.
That’s what happens when we design interfaces that try to do too much.
Signs you’re violating ISP
Your class throws NotImplementedException() in multiple methods
You’re mocking unrelated behavior in unit tests
You constantly comment: // TODO: implement later
You’ve reused an interface just because it’s “already there”
These are classic code smells that your interface is doing too much.
Tips to apply ISP in real projects
Split large interfaces into multiple role-based ones
Name them clearly: ISavable, IDownloadable, ISharable
Let each class implement only the capabilities it actually provides
Combine ISP with SRP (Single Responsibility Principle) for laser-focused design
Real-world example: file services
public interface IFileService {
void Upload();
void Download();
void ConvertToPdf();
void ShareViaEmail();
}
Now you need a ReadOnlyFile. It only needs Download().
Your options?
Implement the full interface and throw exceptions
Violate ISP
Refactor
Split the interface:
public interface IReadable {
void Download();
}
public interface IWritable {
void Upload();
void ConvertToPdf();
void ShareViaEmail();
}
Now:
public class ReadOnlyFile : IReadable { ... }
public class FullFile : IReadable, IWritable { ... }
- Clean, flexible, and scalable.
Conclusion: Smaller interfaces make larger systems healthier
The Interface Segregation Principle seems small, but its impact is huge.
It helps you:
Reduce unnecessary dependencies
Improve testability
Make intentions explicit
Design modular, maintainable and adaptive software
“Great systems aren’t built from massive interfaces — they’re built from small, well-crafted pieces.”
Top comments (0)