Fat interfaces — what they are and why they violate ISP
Fast recall
Quick revision
Core answer
Interview-ready guide
Interview answer
A fat interface is a contract that combines too many unrelated capabilities. A reporting component may need only customer reads but still depend on methods for deletion, notification, and export. A read-only implementation may then need empty methods or runtime exceptions for operations it cannot support.
For example, CustomerService can be separated into CustomerReader, CustomerWriter, and CustomerReporter when those capabilities have different consumers or implementations. A report controller receives only CustomerReader, while an administration use case can receive the write contract as well.
Adding an abstract method to a broad interface can force many implementers to change. Java default methods can preserve compatibility in some cases, but they do not make unrelated methods cohesive. The fix is a contract that accurately describes what each client needs.
Fat interfaces — what they are and why they violate ISP
Direct Answer
A fat interface combines too many unrelated capabilities. Clients depend on methods they never call, and implementations may need meaningless stubs or unsupported operations. Adding an abstract method can also force relevant implementers to change. Split the contract by real client capabilities, while avoiding arbitrary one-method fragmentation.
Code Example
// Fat interface: covers persistence, validation, notifications, and reporting
interface CustomerService {
void saveCustomer(Customer customer);
void deleteCustomer(Long id);
Customer findById(Long id);
List<Customer> findAll();
boolean validateEmail(String email);
boolean validatePhoneNumber(String phone);
void sendWelcomeEmail(Customer customer);
void sendPromotionalEmail(Customer customer);
List<Customer> generateMonthlyReport();
byte[] exportCustomersToExcel();
}
// A read-only service is forced to implement write and notification methods
class ReadOnlyCustomerService implements CustomerService {
public Customer findById(Long id) { /* actual logic */ }
public List<Customer> findAll() { /* actual logic */ }
// Forced to implement methods it does not need
public void saveCustomer(Customer c) { throw new UnsupportedOperationException(); }
public void deleteCustomer(Long id) { throw new UnsupportedOperationException(); }
public boolean validateEmail(String e) { throw new UnsupportedOperationException(); }
public boolean validatePhoneNumber(String p) { throw new UnsupportedOperationException(); }
public void sendWelcomeEmail(Customer c) { throw new UnsupportedOperationException(); }
public void sendPromotionalEmail(Customer c) { throw new UnsupportedOperationException(); }
public List<Customer> generateMonthlyReport() { throw new UnsupportedOperationException(); }
public byte[] exportCustomersToExcel() { throw new UnsupportedOperationException(); }
}
Common Mistakes & Pitfalls
- Judging only by size: ten cohesive methods used by the same clients can be better than five unrelated ones.
- Treating every UnsupportedOperationException as proof: first check whether the operation is guaranteed or documented as optional.
- Splitting without a client: a new interface should represent a real capability or usage boundary.
- Leaving consumers typed to the old fat interface: the narrower contract must be used at dependency boundaries.
Check your understanding
Answer these without looking back at the article.
- How do you refactor a fat interface?
- What is the God Object antipattern?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.