Single Responsibility in Practice
Single Responsibility sounds obvious and is the most violated principle in practice.
Introduction
Single Responsibility sounds obvious and is the most violated principle in practice. A useful definition: one reason to change driven by one stakeholder. Pricing changes when the pricing team asks; persistence when DBAs ask; notifications when marketing asks. If one class serves three stakeholders, three teams fight over one file.
The story
A retail platform's Order class had 4,000 lines, edited 200 times in a year by twelve engineers across four teams. Every release had merge conflicts. After Extract Class along stakeholder lines — Pricing, Fulfilment, Notification, Audit — conflicts fell to near zero.
The business problem
SRP violations scale painfully with team size:
- Merge conflict clusters in god-files touched by every team.
- Coupled tests — exercising pricing requires spinning up email mocks.
- Ownership blur — no team can safely refactor "shared" modules.
- Release coupling — one team's change blocks another's deploy.
The problem teams faced
Symptoms of multi-stakeholder modules:
- One class edited by product, platform, and compliance in the same sprint.
- Tests require unrelated setup to test one behavior.
- Documentation impossible — module "does everything."
- Fear of touching the file — workarounds multiply around it.
Understanding the topic
SRP test: finish this sentence with one role — "This module changes when ___ asks."
- Cohesion — methods operate on the same data for the same reason.
- Stakeholder — team or role requesting changes.
- Refactor moves — Extract Class, Move Method, module split.
Internal architecture
Stakeholder → module mapping:
Pricing team → PricingService / PricingStrategyOps team → FulfilmentServiceMarketing → NotificationServiceCompliance → AuditLoggerOrchestrator → OrderService (thin — coordinates only)
Visual explanation
Three diagrams for SRP in practice:
Informative example
Before / after — order handling split by stakeholder:
// ❌ SRP violation — three stakeholders, one classclass Order {calculateTotal() { /* pricing team changes */ }save() { /* DBA changes */ }sendConfirmationEmail() { /* marketing changes */ }}// ✅ One stakeholder per classclass OrderPricing { total(cart: Cart): Money { /* ... */ } }class OrderRepository { save(order: Order): Promise<void> { /* ... */ } }class OrderNotifier { confirm(order: Order): Promise<void> { /* ... */ } }class PlaceOrderHandler {constructor(private pricing: OrderPricing,private repo: OrderRepository,private notifier: OrderNotifier,) {}async execute(cart: Cart) {const order = { total: this.pricing.total(cart) };await this.repo.save(order);await this.notifier.confirm(order);}}
Execution workflow
List stakeholders
Who requests changes to this file? By team/role.
Real-world use
Spring stereotypes encode SRP: @Repository, @Service, @Controller. DDD bounded contexts are SRP at architecture scale. Microservices often emerge when SRP violations between teams become unbearable — but in-process SRP split is cheaper when teams share a repo.
Production case study
The 4,000-line Order refactor (above):
- Before: 200 edits/year, 12 engineers, merge conflicts every release.
- After: four focused modules, team-owned boundaries, near-zero conflicts.
- Metric: p95 PR review time on order flow dropped 45%.
- Lesson: SRP payoff multiplies when team size exceeds five.
Trade-offs
- Cohesive modules are easier to test, own, and document.
- Risk of Lazy Class over-splitting — one-method classes that add noise.
- Naming "the one responsibility" is harder than it sounds.
Decision framework
- Split when 3+ stakeholders edit the same module regularly.
- Split when files dominate merge conflict reports.
- Keep together when one stakeholder owns all behavior and data.
- Skip on stable 30-line utilities.
Best practices
- Use git shortlog + blame to prove multi-stakeholder pain before splitting.
- Thin orchestrators coordinate; fat logic lives in focused services.
- Apply SRP to functions — if a function parses AND persists, split it.
Anti-patterns to avoid
- One class per method "for SRP" — Lazy Class smell.
- Splitting by technical layer only (all SQL in one god Repository).
Common mistakes
- Extracting without tests — SRP refactor becomes incident generator.
- Splitting classes that always change together — artificial boundaries.
Debugging tips
- Merge conflict heatmap → top files → stakeholder audit.
- Test setup lines count → coupled responsibilities signal.
Optimization strategies
- Schedule SRP refactors before adding headcount to a god module.
Common misconceptions
- SRP ≠ one method per class. Cohesive related methods serving one stakeholder belong together.
Advanced interview questions
Interview Prep
Practice concise answers, then expand each card for the explanation.
1IntermediateQuestionHow do you define 'one responsibility'?+
Answer
Follow-up
2AdvancedQuestionCost of SRP violations?+
Answer
Follow-up
3AdvancedQuestionCan SRP go too far?+
Answer
Follow-up
Summary
You can diagnose SRP violations by stakeholder, split god classes with Extract Class, and avoid lazy over-splitting. Tell the 4,000-line Order story with metrics.