Give an Example of Single Responsibility Principle Violation
SRP violation creates implicit dependencies in the system. Changing one small detail (e.g., date format in logs) can break a critical business process (e.g., order saving), beca...
🟢 Junior Level
Single Responsibility Principle (SRP) violation occurs when a class handles multiple unrelated things simultaneously. Such a class is often called a God Object — it knows and does too much.
Simple analogy: Imagine a person who works as a doctor, driver, and cook at the same time. They might do everything, but the quality will be low, and if they get sick — everything collapses.
SRP violation example:
// Bad: OrderProcessor does too much
public class OrderProcessor {
public void processOrder(Order order) {
// 1. Order validation
if (order.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Invalid amount");
}
// 2. Save to database
saveToDatabase(order);
// 3. Send email to client
sendEmail(order);
// 4. Logging
logToSystem(order);
}
private void saveToDatabase(Order order) { /* 50 lines of code */ }
private void sendEmail(Order order) { /* 50 lines of code */ }
private void logToSystem(Order order) { /* 50 lines of code */ }
}
Why this is bad:
- If the email sending method changes — you have to modify the class responsible for orders
- Hard to test: to verify validation, you need to mock the database and email
- Cannot reuse: you can’t use
saveToDatabaseseparately
How to fix:
// Good: each responsibility in its own class
public class OrderProcessor {
private final OrderValidator validator;
private final OrderRepository repository;
private final EmailService emailService;
private final Logger logger;
public void processOrder(Order order) {
validator.validate(order);
repository.save(order);
emailService.sendOrderConfirmation(order);
logger.log("Order processed: " + order.getId());
}
}
🟡 Middle Level
Anatomy of SRP Violation
SRP violation creates implicit dependencies in the system. Changing one small detail (e.g., date format in logs) can break a critical business process (e.g., order saving), because all of this lives in one file.
Typical SRP Violation Examples
1. Spring @Service — “Dump”
A common anti-pattern in Spring projects: one service with dozens of repositories.
@Service
public class GeneralService {
@Autowired private UserRepository userRepo;
@Autowired private OrderRepository orderRepo;
@Autowired private ProductRepository productRepo;
@Autowired private PromoRepository promoRepo;
@Autowired private AuditRepository auditRepo;
// ... 15 more repositories
// Each @Autowired creates a proxy object. 20+ dependencies = 20+ proxies
// = complex call chain. During testing, each of the 20 needs to be mocked.
// This method violates SRP: combines logic of 5 different domains
public void checkout(Long userId, Long cartId) {
// 200 lines of logic
}
}
Consequence: Any Git merge causes conflicts, as 10 developers edit this file daily.
2. God Object in Business Logic
public class EmployeeManager {
// HR responsibility
public void hireEmployee(Employee emp) { }
public void fireEmployee(Long id) { }
// Accounting
public BigDecimal calculateSalary(Employee emp) { }
public BigDecimal calculateTax(Employee emp) { }
// IT infrastructure
public void createEmailAccount(Employee emp) { }
public void assignLaptop(Employee emp) { }
// Reporting
public void generateDepartmentReport() { }
}
Problem: Different stakeholders (HR, accounting, IT) demand changes in one class.
SRP Violation Diagnostics
| Sign | What It Means |
|---|---|
| “And” Rule | If the class description contains “and” (“it does X AND Y AND Z”) |
| Mocks Overload | More than 5 mocks in a unit test |
| Import List Size | Import list takes >20 lines |
| Cyclomatic Complexity | Many branches if/else for different scenarios. Cyclomatic Complexity — number of independent execution paths in a method. Counted as number of if/else/switch branches + 1. CC = 10 means 10+ tests for full coverage. Higher = harder to test. |
| Merge Conflicts | Frequent Git conflicts when editing the file |
Common Mistakes
-
Mistake: Creating
Utils,Helper,Managerclasses Solution: Break by domain areas (PasswordHasher,DateFormatter). In DDD,Manager/Orchestratorare legitimate coordination patterns. The problem is not the name, but that the class mixes business logic with infrastructure. -
Mistake: Combining CRUD operations into one service Solution: Separate by business capabilities (
OrderCreator,OrderCanceler) -
Mistake: Mixing business logic and infrastructure Solution: Separate DB, HTTP, queue operations into distinct layers
Refactoring: from God Object to Orchestrator
Step 1: Identify different responsibilities Step 2: Create separate classes for each Step 3: Use composition for delegation Step 4: Original class becomes a coordinator (Orchestrator)
🔴 Senior Level
Internal Implementation: Why God Object is “Poison” for a Project
SRP violation creates fundamental problems at the architectural level:
1. Fragility
When we replace one dependency (e.g., Splunk → ELK), we have to retest the entire class logic, because all methods live in one file and may share common fields or state.
// Fragile God Object
public class OrderProcessor {
private final HttpClient httpClient; // Used for email AND logging
private final Connection dbConnection; // Used for orders AND audit
public void process(Order order) {
// If httpClient breaks — both email and logging fail
// Impossible to update logging without risking breaking email
}
}
2. Immobility
A God Object cannot be reused without dragging all its dependencies along. This violates the Mobility principle — the ability of components to work in different contexts.
3. Low Testability
// Test for God Object — nightmare
@Test
void testProcessOrder() {
// Need to mock: DB, email, logging, analytics, inventory...
when(mockDb.save(any())).thenReturn(true);
when(mockEmail.send(any())).thenReturn(true);
when(mockLog.log(any())).thenReturn(true);
when(mockAnalytics.track(any())).thenReturn(true);
when(mockInventory.reserve(any())).thenReturn(true);
// 20 lines of mocks for 5 lines of real logic
}
Architectural Trade-offs
Strict decomposition:
- ✅ Pros: Ideal testability, independent deploys, clear boundaries
- ❌ Cons: Many classes, navigation complexity, communication overhead
Moderate decomposition:
- ✅ Pros: Balance of readability and flexibility
- ❌ Cons: Requires mature judgment, gradual degradation
Edge Cases
- Transaction Boundaries: When one transaction spans multiple domains
- Solution: Use
@Transactionalat the orchestrator level, but business logic in separate services
- Solution: Use
- CQRS Pattern: Separation of Command and Query responsibilities
- Example:
OrderCommandService(create/delete) vsOrderQueryService(read)
- Example:
- Event-Driven Architecture: Separation through events
- Example: OrderCreatedEvent → separate handlers for Email, Analytics, Inventory
Performance
- Method call overhead: Delegation adds method calls. JIT performs inlining, overhead ~0
- Memory: More objects → more references. GC handles this efficiently (generational GC)
- ClassLoader: More classes → more metadata in Metaspace (a few KB per class)
Production Experience
Real story from an enterprise project:
In a banking project, there was a PaymentProcessor with 5000+ lines and 40 dependencies. Problems:
- Any change took 3-4 days (analysis + tests)
- 30% of all production bugs were due to side effects
- Merge conflicts every day
- New developers took 2-3 months to onboard
Solution: Gradual refactoring over 4 sprints:
- Extracted
PaymentValidator,PaymentRepository,NotificationService - Created
FraudDetectionService,ComplianceChecker PaymentOrchestratorcoordinates calls- Event-driven communication via Spring Events
Result:
- Bug count dropped by 70%
- Code review time reduced from 2 hours to 30 minutes
- New features added in 1-2 days instead of a week
Monitoring and Diagnostics
Static analysis:
// ArchUnit test for SRP verification
@ArchTest
static void services_should_have_single_responsibility = classes()
.that().resideInAPackage("..service..")
.should().haveSimpleNameNotContaining("Manager")
.andShould().haveSimpleNameNotContaining("Utils")
.andShould().haveLessThanNDependencies(7);
Metrics to track:
- Lines per class (< 300)
- Number of methods (< 10)
- Cyclomatic complexity (< 15)
- Number of dependencies (< 7)
- Number of mocks in tests (< 5)
Best Practices for Highload
- Domain-Driven Design: Aggregate Root has one responsibility within the Aggregate
- Package-by-Feature: Group by business capabilities, not technical layers
- Hexagonal Architecture: Separation of domain, application and infrastructure layers
- Event Sourcing: State separation through event model
Relationship with Other Patterns
- SRP + Decorator: Adding responsibility without violating SRP
- SRP + Chain of Responsibility: Each handler — one responsibility
- SRP + Strategy: Each strategy — one responsibility
Summary for Senior
- SRP violation turns the system into a “house of cards” — changing one detail breaks everything
- Look for God Object signs: huge methods, dozens of dependencies, frequent merge conflicts
- Use decomposition by business meaning, not by technical functions
- Remember: SRP makes code clean, not small — balance between cohesion and file count
- ArchUnit is your friend for automatic verification of architectural boundaries
- SRP is an investment in maintainability, not “bureaucracy”
🎯 Interview Cheat Sheet
Must know:
- God Object — a class that knows and does too much, violates SRP
- “And” Rule: if class description contains “and” — SRP is violated
- Spring
@Servicewith 20+ repositories — a typical God Object in enterprise - Refactoring: identify responsibilities → create separate classes → delegation
- SRP violation creates fragility, immobility, and low testability
- Characterization Tests — first step of refactoring legacy without tests
- Event-driven architecture helps separate responsibilities through events
Common follow-up questions:
- How to diagnose a God Object? — >300 lines, >10 methods, >7 dependencies, >5 mocks in tests
- What is an Orchestrator? — A coordinator that delegates work to separate services, contains no business logic
- How to refactor without tests? — Characterization Tests / Golden Master: capture current behavior, then split
- CQRS and SRP? — Separation of Command and Query is an example of SRP at the service level
Red flags (DO NOT say):
- “Manager is a normal class name” (most often it’s a God Object)
- “If the code works — SRP is not needed” (works today, but unmaintainable tomorrow)
- “20 dependencies in a service is normal” (clear sign of SRP violation)
Related topics:
- [[1. What is Single Responsibility principle and how to apply it]]
- [[14. What happens if a class has multiple reasons to change]]
- [[18. How to refactor God Object]]
- [[22. Which anti-patterns contradict SOLID principles]]