OOP & Design Principles • SOLID PrinciplesEasy⏱️ ~2 min
Single Responsibility Principle (SRP): One Component, One Reason to Change
The Single Responsibility Principle states that each component should own one cohesive responsibility, meaning it has exactly one primary reason to change. When a class handles both user authentication and email notification, changing email providers forces you to touch authentication code, increasing risk and testing scope. SRP says split these into AuthenticationService and NotificationService so each can evolve independently.
At production scale, SRP dramatically reduces change amplification across teams. Amazon's two pizza team model embodies SRP at the service boundary: address normalization, tax calculation, and inventory management live in separate services owned by distinct teams. When tax rules change for a new jurisdiction, only the tax service team deploys. During Prime Day peaks pushing tens of thousands of checkouts per second globally, this isolation allows independent scaling; the address service might handle 5,000 requests per second while tax runs at 8,000 requests per second based on their distinct load profiles.
The principle applies at multiple layers. A UserRepository class should handle data access, not validation logic or audit logging. A microservice should own one bounded context, not sprawl across payments, recommendations, and analytics. Microsoft's Azure services follow this: Azure Storage handles blobs, tables, and queues but doesn't mix in identity management or billing calculations. Each responsibility gets its own deployment pipeline, on call rotation, and performance budget.
The tradeoff is coordination cost. Splitting too early creates network hops that add 0.5 to 5 milliseconds per call within a datacenter, inflating tail latency. Start with SRP within a single process using separate classes and modules. Extract to separate services only when scaling profiles diverge or team ownership boundaries demand it. Measure before splitting: if two responsibilities always scale together and change together, keeping them unified may be simpler.
💡 Key Takeaways
•One reason to change: A component should have exactly one primary responsibility and one axis of variation. Authentication logic should not mix with email delivery or audit logging.
•Reduces change amplification: At Amazon, splitting address normalization (5,000 RPS) from tax calculation (8,000 RPS) into separate services lets teams deploy independently without coordinating every release.
•Shortens pipelines: A focused component has smaller test suites and faster builds. Microsoft's Azure Storage services deploy storage logic without retesting unrelated identity or billing code.
•Premature service splitting backfires: Network hops add 0.5 to 5 ms latency per call. Keep SRP within process boundaries using modules/classes until scaling or team ownership truly diverges.
•Applies at multiple layers: Classes (UserRepository vs UserValidator), modules (auth module vs notification module), services (tax service vs inventory service), and data models (User table vs AuditLog table).
•Ownership clarity: Each component maps to one team's on call rotation. When a tax calculation bug fires at 3 AM during peak traffic, the tax team owns it without dragging in address or payment engineers.
📌 Examples
Amazon two pizza teams: Address normalization service (one team, 5K RPS) separate from tax calculation service (different team, 8K RPS). Tax rule changes for new jurisdictions deploy without touching address code.
Azure Storage: Blob storage, table storage, and queue storage live in one service boundary (cohesive storage responsibility) but don't include identity management, billing, or analytics (different responsibilities).
Bad example: UserService with authenticate(), sendWelcomeEmail(), logSecurityAudit(), and validatePasswordStrength(). Changing email templates requires editing and testing authentication code paths.