solid_single_responsibility 50 Q&As

Solid Single Responsibility FAQ & Answers

50 expert Solid Single Responsibility answers researched from official documentation. Every answer cites authoritative sources you can verify.

unknown

50 questions
A

A module should be responsible to one, and only one, actor. Robert Martin's original definition: 'A class should have only one reason to change.' Clean Architecture (2017) refined this: 'A module should be responsible to one, and only one, actor' - where 'actor' represents a group of stakeholders who would request changes for the same reason. Example: UserController (HTTP actor), UserService (business rules actor), UserRepository (database actor) - three distinct actors. Violation: UserManager serves CFO (reporting), COO (business rules), CTO (infrastructure) - changes for one actor break another's needs. Benefits: Changes isolated to one stakeholder group, easier testing, clearer ownership. Historical note: 'Reason to change' caused confusion - actor-based definition is more precise and prevents unexpected side effects when one group's requirements change.

99% confidence
A

Ask: 'Does this class serve more than one actor?' or 'Does it have multiple reasons to change?' Red flags: (1) Class name with 'Manager', 'Utils', 'Helper', 'And', 'Or' - indicates unclear responsibility. (2) God class (>300 lines, 15+ methods). (3) Methods dealing with unrelated concerns (sendEmail + calculateTax). (4) Multiple import groups (HTTP + database + email libs). (5) Testing requires mocking 5+ dependencies. (6) Hard to name in one sentence without 'and'. Example: class Invoice {calculate(), formatForPrint(), saveToDB()} serves three actors: accounting (calculation), printing team (format), DBAs (schema). Refactor: InvoiceCalculator, InvoicePrinter, InvoiceRepository. Detection tools: SonarQube, ReSharper, Visual Studio Code Analysis flag high complexity and low cohesion. Warning sign: Everyone adds features to this class because it's 'easy to access' - leads to monster God class over time.

99% confidence
A

SRP is class-level (each class one actor), Separation of Concerns (SoC) is system-level (architecture organized by concern). Robert Martin: 'Reasons for change are people' - SRP is about organizational actors who request changes. SRP is Martin's practical refinement of the broader SoC principle, specifically focused on actors and their reasons for requesting changes. SoC (Dijkstra): Broad principle dividing system into distinct concerns (technical, architectural, or organizational). Example: Frontend ↔ Backend API ↔ Database (SoC at system level). SRP within SoC: Backend has UserController (HTTP actor), UserService (business actor), UserRepository (data actor) - each following SRP. Violation: Backend separates layers but UserService handles validation + business + caching (multiple actors). SRP asks 'which actor owns this change?' (people-based), SoC asks 'what is the concern?' (feature-based). Clean Architecture applies both: SoC for layers (presentation, business, data), SRP within each layer. TypeScript example: class EmployeeRepository (DBA actor) vs class EmployeeReportFormatter (CFO actor) vs class EmployeeSalaryCalculator (accounting actor) - three actors, three classes.

99% confidence
A

Functions should do one thing at one abstraction level. SRP for functions: Each performs one cohesive task. Good: function validateEmail(email) {return /regex/.test(email);} - one task, clear name. Bad: function processUser(user) {validateEmail(user.email); hashPassword(user.password); saveToDatabase(user); sendWelcomeEmail(user);} - four responsibilities at different abstraction levels. Refactor: Create four single-responsibility functions, processUser() orchestrates. Violations: (1) Name with 'and' (validateAndSave), (2) Multiple return types (data | error | null), (3) Vague name (handleUserStuff). Robert Martin: 'Functions should be small and do one thing.' Test: Describe in one sentence without 'and'? If no, split it. SRP applies recursively: system → modules → classes → methods → functions. Each level has single responsibility at its scope. Modern: React functional components follow this - small focused components, custom hooks extract reusable logic.

99% confidence
A

Violation 1: class User {login(), logout(), updateProfile(), sendEmail(), generateReport()} - mixes auth (IT actor), profiles (users actor), email (ops actor), reporting (business actor). Fix: AuthService, UserProfileService, EmailService, ReportGenerator - four actors, four classes. Violation 2: class OrderController {validateOrder(), calculateTax(), processPayment(), updateInventory(), sendConfirmation()} - handles HTTP, validation, tax rules, payment, inventory, notifications. Fix: OrderController (HTTP only), OrderValidator, TaxCalculator, PaymentProcessor, InventoryManager, NotificationService. Violation 3 (E-commerce): One class fetches data from API, processes it, saves to database, sends email. Fix: OrderAPIClient, OrderProcessor, OrderRepository, OrderNotifier. Mobile app: Split data fetching (network actor) from UI processing (UX actor) using UserDataProcessor and UIController. React: Component handles fetch + filter + display. Fix: useFetchData() hook, useFilteredData() hook, DisplayComponent. Result: Each actor's changes isolated.

99% confidence
A

SRP is crucial for testability - enables unit testing in isolation with mocked dependencies. Connection: To test class, identify and inject dependencies. To identify dependency = identify responsibility. Single responsibility = minimal inputs/outputs/decision points = easy tests. Pattern: class OrderService {constructor(private paymentProcessor: PaymentProcessor, private inventory: InventoryManager)} - two clear dependencies, easy to mock. Testing SRP-compliant code: Mock dependencies, verify they're called correctly, test single concern. Example: Test OrderService processes payment via PaymentProcessor without needing real payment gateway or database. Violation consequences: class OrderService handles payment + inventory + email → testing requires mocking 10+ dependencies, brittle tests, hard to isolate failures. TDD benefits: Test-Driven Development forces identifying responsibilities upfront - if class hard to test, likely violates SRP. Dependency injection enables both SRP and testability: inject PaymentProcessor interface, vary behavior without changing OrderService. Tools: Use DI frameworks (NestJS, InversifyJS) to manage dependencies.

99% confidence
A

React components should do one thing internally and be used for one purpose externally. Pattern: Break large components into focused pieces. Violation: Component fetches data, filters it, handles pagination, and renders UI (four responsibilities, four actors). Fix: Custom hooks for logic separation: useFetchUsers() (API actor), useFilteredUsers() (business actor), usePagination() (UX actor), UserListDisplay (presentation actor). When useState and useEffect are connected, extract into custom hook. Example: const {users, loading} = useFetchUsers(); const filtered = useFilteredUsers(users, filter); return . Benefits: Hooks are domain-specific (obvious purpose), components are presentational (easy to test), logic reusable across components. TypeScript: Type custom hooks properly for type safety. Modern practice: Functional components encourage small focused functions, hooks replace class lifecycle methods (which grouped unrelated logic). Tool: Split by actor - data fetching (backend actor), UI interactions (UX actor), business rules (product actor). React 2024: Server components, suspense boundaries further separate concerns by execution context.

99% confidence
A

Code analysis tools identify SRP violations through metrics, patterns, and coupling analysis. SonarQube (2025): Industry standard with 6,500+ rules across 35 languages, 7 million developers worldwide. Detects: (1) High complexity (cyclomatic complexity >15), (2) Low cohesion (LCOM metric), (3) High coupling (CBO metric), (4) Code smells ('Classes should not be coupled to too many other classes' rule targets SRP). Thresholds: Class >300 lines, >10 methods, >5 dependencies. Other tools: (2) ReSharper (C#/.NET) - suggests splitting classes, identifies coupling. (3) ESLint with complexity plugins (JavaScript/TypeScript). (4) CodeClimate - maintainability scores. (5) Visual Studio Code Analysis. Pattern detection: Classes named *Manager, *Utils, *Helper indicate unclear responsibility. TypeScript example: ESLint complexity rule flags class OrderService {validateOrder(), calculateTax(), processPayment(), sendEmail()} - four concerns detected. Limitations: Tools detect symptoms (size, complexity) not semantic violations (multiple actors). Require human judgment: Which actor owns this code? Best practice 2025: Combine SonarQube quality gates with actor-focused code reviews.

99% confidence
A

Don't apply SRP prematurely or rigidly - balance with YAGNI (You Aren't Gonna Need It) and pragmatism. YAGNI vs SRP tension: SRP adds extensibility, YAGNI avoids premature complexity. Rule of Three (2025 best practice): Tolerate coupling until pain becomes apparent (three variations), then refactor. When NOT to apply: (1) Trivial DTOs - interface UserDTO {id, name, email} doesn't need splitting. (2) Stable code - No changes in 2+ years, serves one actor clearly. (3) Prototypes - Requirements unclear, wait for patterns to emerge. (4) Responsibilities that always change together - If validation and business rules never change independently, combining them is pragmatic. (5) Over-fragmentation - 50 tiny classes harder to navigate than one cohesive class. Warning signs of over-engineering: Classes with single method, forced abstractions with no variation, complex navigation for simple tasks. 2025 guideline: Strict SOLID application without context leads to unnecessary complexity. Pragmatism over dogma: Apply SRP when benefits (easier testing, clearer ownership, reduced coupling) outweigh costs (more files, indirection). Context: SRP more valuable in large teams (clear actor ownership) than solo projects. Master knowing when to bend rules for the right reasons.

99% confidence
A

Dependency injection (DI) separates object creation responsibility from business logic - class focuses on its responsibility, DI container handles dependencies. Without DI: class UserService {private emailService = new EmailService(); private db = new Database();} - UserService responsible for creating dependencies (violates SRP). With DI: class UserService {constructor(private emailService: EmailService, private db: Database) {}} - dependencies injected, UserService only handles business logic. Benefits: (1) Single actor - UserService serves business requirements actor, not infrastructure actor. (2) Testability - inject mocks for testing. (3) Flexibility - swap implementations without changing UserService. DI containers (NestJS, InversifyJS, tsyringe) manage lifecycle and scope. Pattern: constructor injection for required dependencies, property injection for optional. TypeScript example: @injectable() class UserService {@inject(EmailService) emailService; processUser() {...}}. Warning: DI doesn't guarantee SRP - can still inject 10 dependencies (God class). Rule of thumb: >5 constructor parameters signals SRP violation. Refactor: split class into smaller services with fewer dependencies.

99% confidence
A

Each microservice should serve one business domain/actor (bounded context). Service boundaries aligned with organizational actors prevent coupling. Example: E-commerce - OrderService (sales actor), PaymentService (finance actor), InventoryService (warehouse actor), NotificationService (customer support actor). Each serves distinct stakeholder group with different change drivers. Violation: MonolithicService handles orders + payments + inventory - changes from finance actor affect warehouse operations. Granularity: Not too coarse (monolith), not too fine (nano-services). Sweet spot: Service per aggregate root (DDD) or business capability. Communication: Services communicate via events (loose coupling) or APIs. Testing: Each service testable in isolation, mock external dependencies. Deployment: Independent deployment per service (actor-driven changes isolated). Warning: Don't split by technical layer (APIService, DatabaseService) - split by business actor. 2025 trend: Team-per-service ownership reinforces actor alignment (team owns service serving their stakeholder). Conway's Law: System structure mirrors organization structure - microservices boundary IS actor boundary.

99% confidence
A

Database tables should represent single entities/actors, not mixed concerns. SRP in schema: Table serves one domain concept. Good: users (authentication actor), user_profiles (profile actor), user_preferences (UX actor) - three actors, three tables. Bad: users table with auth columns + profile columns + preferences columns + audit columns + billing columns - serves 5 actors, changes from billing team affect authentication. Normalization supports SRP: 3NF eliminates redundancy and mixed responsibilities. Example: orders table (sales actor) + order_items table (order details) + order_audit (compliance actor) - separate concerns. Violation: God table with 50+ columns serving multiple business units. Refactor: Vertical partitioning by actor - split into focused tables. Trade-offs: Over-normalization (100 tables for simple domain) vs under-normalization (one table for everything). Production: Use database views to aggregate data for specific actors without mixing storage responsibilities. Actor boundaries: Finance tables (invoices, payments), Operations tables (inventory, shipments), Customer tables (profiles, preferences). Warning: Don't confuse joins with SRP violations - joining user + profile for query is fine if stored separately.

99% confidence
A

Controllers should ONLY handle HTTP concerns - request/response parsing, validation, routing to services. Single responsibility: Translate HTTP to domain calls, domain results to HTTP. Pattern: @Controller() class UserController {@Post() createUser(@Body() dto: CreateUserDTO) {const user = await this.userService.create(dto); return {id: user.id, status: 201};}}. Controller handles: (1) HTTP routing (@Post, @Get), (2) DTO validation (@Body), (3) Response formatting (status codes, JSON). Does NOT handle: business logic, database access, external APIs. Those belong in services. Violation: Controller calculates tax, sends email, updates database directly - serves business actor, email actor, database actor. Fix: Move logic to UserService (business actor), EmailService (ops actor), UserRepository (data actor). Controller only coordinates. Actor: HTTP actor (API consumers who need REST interface). Changes from API versioning don't affect business logic. TypeScript example: Controller thin (10-30 lines per endpoint), services thick (business logic). Testing: Controller tests verify HTTP behavior (status codes, validation), service tests verify business logic. Modern frameworks (NestJS, Fastify) enforce this separation via decorators and DI.

99% confidence
A

Error handling should be separated by concern: error generation (business logic), error transformation (layer boundaries), error presentation (UI/API). Layered approach: (1) Domain layer throws domain exceptions - UserNotFoundException, InvalidEmailError. (2) Application layer catches and transforms - maps domain exceptions to application errors. (3) Infrastructure layer handles technical errors - DatabaseConnectionError, NetworkTimeout. (4) Presentation layer formats for display - HTTP status codes, user-friendly messages. Violation: Business logic catches database errors and returns HTTP responses - mixes three actors (business, infrastructure, API). TypeScript example: try {await userService.create()} catch (e) {if (e instanceof UserExistsError) return {status: 409}; throw e;}. Pattern: Use exception filters/middleware for cross-cutting error handling - @Catch() decorator in NestJS, Express error middleware. Single responsibility: ErrorLogger logs errors (ops actor), ErrorTransformer maps exceptions (API actor), ErrorNotifier sends alerts (on-call actor). Warning: Don't create God error handler that handles all errors everywhere - separate by actor. Production: Domain errors for business rules, technical errors for infrastructure, HTTP errors for API layer. Each layer responsible for its error concern.

99% confidence
A

Event handlers should handle one type of event for one actor. Pattern: UserCreatedHandler processes user creation (business actor), SendWelcomeEmailHandler sends email (ops actor), UpdateAnalyticsHandler tracks metrics (analytics actor) - three handlers for UserCreatedEvent, each serves one actor. Violation: Single handler does validation + email + analytics + audit logging - serves four actors. Event sourcing: Each event represents single fact (OrderPlaced, PaymentProcessed), aggregate applies events to update state. Responsibility: Event producer publishes facts, event consumers react independently. Actor alignment: Sales team owns OrderPlacedHandler (business logic), finance owns PaymentProcessedHandler (accounting), warehouse owns InventoryUpdatedHandler (operations). Benefits: Handlers independently deployable, one actor's changes don't affect others. TypeScript example: @EventHandler(UserCreatedEvent) class SendWelcomeEmailHandler {async handle(event) {await this.emailService.send(event.email);}}. Saga pattern: Orchestrator coordinates multi-step transactions but delegates work to specialized services (each SRP-compliant). Warning: Avoid God event handler that processes all events - separate by event type and actor.

99% confidence
A

Logging/monitoring should be separated from business logic via cross-cutting concerns (aspect-oriented programming, middleware). Pattern: Business classes focus on domain logic, logging handled by decorators/interceptors. Violation: class UserService {create(user) {this.logger.log('Creating user'); this.metrics.increment('user.created'); const result = this.db.save(user); this.logger.log('User created'); return result;}} - mixes business logic (database actor) with logging (ops actor) and metrics (monitoring actor). Fix: Use decorators - @Log() @Metrics('user.created') async create(user) {return this.db.save(user);}. Logging interceptor adds logs automatically. Separation: (1) Application logging (business events for audit) - separate logger. (2) Technical logging (errors, performance) - infrastructure concern. (3) Metrics (counters, timers) - monitoring service. (4) Tracing (distributed requests) - observability layer. TypeScript: NestJS interceptors, Winston logger, Prometheus client. Production: Structured logging with correlation IDs, log levels (DEBUG, INFO, ERROR), separate log storage per concern. Actor: Ops team owns logging infrastructure, developers own business event logging, SRE owns metrics. Warning: Over-logging violates SRP - log decisions should be configuration, not scattered in business code.

99% confidence
A

Interfaces should represent one role/contract for one actor. Good: interface Authenticator {login(), logout()} (auth actor), interface UserRepository {find(), save()} (database actor), interface EmailSender {send()} (notification actor) - focused contracts. Bad: interface IUser {login(), logout(), save(), find(), sendEmail(), generateReport()} - serves auth, database, email, reporting actors. Violation: Fat interface forces implementations to depend on methods they don't use (violates Interface Segregation Principle too). TypeScript example: interface PaymentProcessor {process(amount): Promise} - single responsibility, easy to mock. Implementation: class StripeProcessor implements PaymentProcessor {...}. Multiple interfaces: class UserService implements UserRepository, Authenticatable - composes multiple focused responsibilities. Actor-based design: interface CFOReportGenerator {generateFinancialReport()} (finance actor) separate from interface COOReportGenerator {generateOperationalReport()} (operations actor). Benefits: Small interfaces easier to implement, test, mock. Composition over fat interfaces. Warning: Don't create one interface per method (over-fragmentation) - group cohesive methods serving same actor. Production: TypeScript strict mode enforces interface contracts, prevents unintended coupling.

99% confidence
A

Key metrics: (1) Coupling Between Objects (CBO) - counts dependencies. CBO >10 suggests God class. (2) Cyclomatic Complexity - methods >10 often mix concerns. (3) Lines of Code (LOC) - classes >300 lines likely violate SRP. (4) Number of methods - >15 public methods suggests multiple responsibilities. (5) Fan-out - class uses >7 other classes indicates mixed concerns. (6) LCOM (Lack of Cohesion of Methods) - measures how related methods are (note: removed from SonarQube 4.1+ due to false positives, available via third-party plugins like CKMetrics). Low LCOM (0-0.3) = high cohesion, high LCOM (>0.6) = low cohesion. SonarQube 2025 thresholds: Class LOC >300, method complexity >15, dependencies >10. Ideal targets: CBO <5, complexity <10, LOC <300. TypeScript tools: ts-morph, madge (dependency analysis), complexity-report, ESLint complexity plugins. Production: Track metrics in CI/CD, fail builds on violations. Example: Class with CBO=12, LOC=500, complexity >15 → refactor into 3-4 focused classes. Caveat: Metrics detect symptoms (size, coupling, complexity), not root cause - require human review to identify actual actors. Use metrics as signals, not absolute rules.

99% confidence
A

State management should separate concerns by actor: UI state (UX actor), server state (API actor), form state (validation actor), app state (business actor). Pattern: Redux/Zustand slices per domain. Good: userSlice (user actor), cartSlice (e-commerce actor), notificationSlice (ops actor) - separate reducers. Bad: Single God store with mixed concerns - app state + UI toggles + API cache + form errors. Violation: One reducer handles authentication + shopping cart + notifications - three actors. React: useState for component-local UI state, React Query for server state, Formik for form state, Context for global app state. Separation: (1) Server state (react-query, SWR) - caching, background updates. (2) Form state (Formik, react-hook-form) - validation, submission. (3) UI state (local useState) - modals, toggles. (4) Global state (Context, Redux) - auth, theme. TypeScript: Type state by domain - UserState, CartState, UIState. Actor alignment: Product team owns cart state, backend team owns API state, UX team owns UI state. Warning: Global state for everything violates SRP - use local state where possible. Production: Separate state updates by feature (feature flags), deploy state changes independently.

99% confidence
A

Repository separates data access (database actor) from business logic (domain actor). Pattern: interface UserRepository {find(), save(), delete()} - single responsibility is data persistence. Business logic stays in UserService. Violation: class UserService {saveUser() {const sql = 'INSERT INTO users...'; db.execute(sql); this.sendEmail();}} - mixes business logic, SQL, and email (three actors). Fix: UserRepository handles SQL (DBA actor), UserService handles business rules (business actor), EmailService handles notifications (ops actor). TypeScript example: class TypeORMUserRepository implements UserRepository {async find(id) {return this.repo.findOne(id);}}. Benefits: (1) Business logic doesn't know about SQL/NoSQL. (2) Easy to mock repository for testing. (3) Switch databases without changing business code. Actor: DBA owns repository schema changes, business owns service logic. Generic repository anti-pattern: class Repository {find(), save()} - too abstract, lacks domain semantics. Better: Specific repositories - UserRepository, OrderRepository, PaymentRepository. Production: Repository per aggregate root (DDD), use query builders (TypeORM, Prisma) inside repository, never in services. Warning: Fat repositories with business logic violate SRP - keep repositories thin (data access only).

99% confidence
A

Service layer responsibility: Orchestrate business operations (business actor), coordinate domain objects and repositories, enforce transaction boundaries. Does: (1) Business workflows (multi-step operations). (2) Transaction management. (3) Call repositories for data. (4) Return DTOs to controllers. Does NOT: (1) HTTP parsing (controller responsibility). (2) SQL queries (repository responsibility). (3) Domain logic (domain model responsibility). (4) Presentation formatting (view responsibility). Example: class OrderService {async createOrder(dto) {const user = await this.userRepo.find(dto.userId); const order = Order.create(user, dto.items); await this.orderRepo.save(order); await this.emailService.send(order.confirmationEmail); return order;}}. Service orchestrates but delegates. Actor: Business stakeholders who define workflows. Changes to checkout process affect OrderService, not UserRepository or EmailController. Violation: Service contains SQL, HTTP responses, validation rules, formatting - four actors. Anti-pattern: Anemic service (only pass-through to repository) - lacks orchestration value. Transaction script pattern: Service per use case (CreateOrderService, CancelOrderService) vs one service per entity. Production: Service layer in application/use-case layer (Clean Architecture), domain logic in entities/value objects.

99% confidence
A

CQRS is SRP applied to read/write operations - separate models for commands (writes) and queries (reads). Pattern: CommandHandler modifies state (write actor), QueryHandler retrieves data (read actor) - different actors with different optimization needs. Example: CreateUserCommandHandler (business write operations), GetUserQueryHandler (read/reporting operations). Separation allows: (1) Write model optimized for transactions, validation, consistency. (2) Read model optimized for performance, denormalization, caching. (3) Different actors - CFO needs reports (queries), operations needs to create orders (commands). Violation: Single model handles create + update + complex reports - serves write actor and read actor with conflicting requirements (normalization vs denormalization). TypeScript: interface CommandHandler {execute(cmd: T): Promise}, interface QueryHandler<T, R> {execute(query: T): Promise}. Actor alignment: Write side serves business users making changes, read side serves analysts/reporting. Event sourcing often paired with CQRS - commands generate events, projections build read models. Production: Separate databases for read/write (eventual consistency), scale reads independently. Warning: CQRS adds complexity - only apply when read/write concerns genuinely conflict.

99% confidence
A

Refactoring strategy: (1) Identify actors - list stakeholders who request changes (CFO, COO, CTO, users). (2) Map methods to actors - group methods by which actor they serve. (3) Extract classes per actor - UserAuthenticator (IT actor), UserProfileManager (users actor), UserReportGenerator (CFO actor). (4) Update dependencies - inject new classes where needed. (5) Migrate incrementally - keep old class as facade initially, gradually migrate callers. TypeScript example: class User {login(), updateProfile(), generateReport()} → Extract: class Authenticator {login()}, class ProfileManager {update()}, class ReportGenerator {generate()}. Techniques: Extract Class, Extract Interface, Move Method. Tools: IDE refactoring (VS Code, WebStorm) automates extraction. Testing: Write tests before refactoring (characterization tests), ensure tests pass after each step. Strangler Fig pattern: Create new classes alongside old, migrate usage gradually, deprecate old class. Warning: Don't extract blindly - verify actors are truly distinct (different change drivers). Production: Refactor when adding features (Boy Scout Rule), not big-bang rewrites. Allocate 20% of sprint to refactoring. Metrics: Track class size and complexity over time, celebrate reductions.

99% confidence
A

SRP can hurt performance when excessive abstraction adds overhead, but usually negligible. Trade-offs: (1) Indirection cost - calling 5 small classes vs 1 method in God class adds function call overhead (nanoseconds in TypeScript/JavaScript). (2) Memory - more objects = higher memory usage. (3) Instantiation - creating multiple service instances slower than one. Real impact: Usually <1% performance difference in application code (I/O dominates). Example: Splitting calculateTax() into TaxRateProvider, TaxCalculator, TaxFormatter adds 3 method calls (0.0001ms) - irrelevant compared to database query (10ms). When it matters: (1) Hot paths - inner loops processing millions of items (game engines, image processing). (2) Embedded systems - memory constrained. (3) Real-time systems - microsecond latency requirements. Solution: Profile first, optimize hot paths only. Extract performance-critical code into optimized implementation while keeping SRP elsewhere. TypeScript: V8 JIT optimizes small functions - modern JS engines inline calls, eliminating overhead. Production: 99% of code is not performance-critical - favor SRP for maintainability. For 1% hot paths: optimize after measuring. Warning: Premature optimization citing performance to avoid SRP is anti-pattern - measure first.

99% confidence
A

DDD entities and value objects follow SRP - each represents one domain concept for one actor. Entity responsibility: Maintain identity and invariants for its aggregate. Example: Order entity enforces order rules (sales actor), Payment entity enforces payment rules (finance actor), Shipment entity enforces shipping rules (operations actor). Violation: Order entity handles payment processing + inventory updates + shipping - serves three actors. DDD layering: (1) Domain layer - pure business logic (business actor). (2) Application layer - use cases, orchestration (workflow actor). (3) Infrastructure layer - database, external APIs (technical actor). Aggregate root: Single entry point to aggregate, enforces business rules for its bounded context. Actor alignment: Bounded contexts map to actors - Sales context (sales team), Billing context (finance team), Inventory context (warehouse team). Value objects: Immutable, represent concepts (Email, Money, Address) - single responsibility is value equality and validation. TypeScript: class Order {addItem(), applyDiscount()} - business rules only, no database or HTTP code. Repository per aggregate root. Production: Ubiquitous language per context, each context serves distinct stakeholder group. DDD is SRP at architecture level.

99% confidence
A

A module should be responsible to one, and only one, actor. Robert Martin's original definition: 'A class should have only one reason to change.' Clean Architecture (2017) refined this: 'A module should be responsible to one, and only one, actor' - where 'actor' represents a group of stakeholders who would request changes for the same reason. Example: UserController (HTTP actor), UserService (business rules actor), UserRepository (database actor) - three distinct actors. Violation: UserManager serves CFO (reporting), COO (business rules), CTO (infrastructure) - changes for one actor break another's needs. Benefits: Changes isolated to one stakeholder group, easier testing, clearer ownership. Historical note: 'Reason to change' caused confusion - actor-based definition is more precise and prevents unexpected side effects when one group's requirements change.

99% confidence
A

Ask: 'Does this class serve more than one actor?' or 'Does it have multiple reasons to change?' Red flags: (1) Class name with 'Manager', 'Utils', 'Helper', 'And', 'Or' - indicates unclear responsibility. (2) God class (>300 lines, 15+ methods). (3) Methods dealing with unrelated concerns (sendEmail + calculateTax). (4) Multiple import groups (HTTP + database + email libs). (5) Testing requires mocking 5+ dependencies. (6) Hard to name in one sentence without 'and'. Example: class Invoice {calculate(), formatForPrint(), saveToDB()} serves three actors: accounting (calculation), printing team (format), DBAs (schema). Refactor: InvoiceCalculator, InvoicePrinter, InvoiceRepository. Detection tools: SonarQube, ReSharper, Visual Studio Code Analysis flag high complexity and low cohesion. Warning sign: Everyone adds features to this class because it's 'easy to access' - leads to monster God class over time.

99% confidence
A

SRP is class-level (each class one actor), Separation of Concerns (SoC) is system-level (architecture organized by concern). Robert Martin: 'Reasons for change are people' - SRP is about organizational actors who request changes. SRP is Martin's practical refinement of the broader SoC principle, specifically focused on actors and their reasons for requesting changes. SoC (Dijkstra): Broad principle dividing system into distinct concerns (technical, architectural, or organizational). Example: Frontend ↔ Backend API ↔ Database (SoC at system level). SRP within SoC: Backend has UserController (HTTP actor), UserService (business actor), UserRepository (data actor) - each following SRP. Violation: Backend separates layers but UserService handles validation + business + caching (multiple actors). SRP asks 'which actor owns this change?' (people-based), SoC asks 'what is the concern?' (feature-based). Clean Architecture applies both: SoC for layers (presentation, business, data), SRP within each layer. TypeScript example: class EmployeeRepository (DBA actor) vs class EmployeeReportFormatter (CFO actor) vs class EmployeeSalaryCalculator (accounting actor) - three actors, three classes.

99% confidence
A

Functions should do one thing at one abstraction level. SRP for functions: Each performs one cohesive task. Good: function validateEmail(email) {return /regex/.test(email);} - one task, clear name. Bad: function processUser(user) {validateEmail(user.email); hashPassword(user.password); saveToDatabase(user); sendWelcomeEmail(user);} - four responsibilities at different abstraction levels. Refactor: Create four single-responsibility functions, processUser() orchestrates. Violations: (1) Name with 'and' (validateAndSave), (2) Multiple return types (data | error | null), (3) Vague name (handleUserStuff). Robert Martin: 'Functions should be small and do one thing.' Test: Describe in one sentence without 'and'? If no, split it. SRP applies recursively: system → modules → classes → methods → functions. Each level has single responsibility at its scope. Modern: React functional components follow this - small focused components, custom hooks extract reusable logic.

99% confidence
A

Violation 1: class User {login(), logout(), updateProfile(), sendEmail(), generateReport()} - mixes auth (IT actor), profiles (users actor), email (ops actor), reporting (business actor). Fix: AuthService, UserProfileService, EmailService, ReportGenerator - four actors, four classes. Violation 2: class OrderController {validateOrder(), calculateTax(), processPayment(), updateInventory(), sendConfirmation()} - handles HTTP, validation, tax rules, payment, inventory, notifications. Fix: OrderController (HTTP only), OrderValidator, TaxCalculator, PaymentProcessor, InventoryManager, NotificationService. Violation 3 (E-commerce): One class fetches data from API, processes it, saves to database, sends email. Fix: OrderAPIClient, OrderProcessor, OrderRepository, OrderNotifier. Mobile app: Split data fetching (network actor) from UI processing (UX actor) using UserDataProcessor and UIController. React: Component handles fetch + filter + display. Fix: useFetchData() hook, useFilteredData() hook, DisplayComponent. Result: Each actor's changes isolated.

99% confidence
A

SRP is crucial for testability - enables unit testing in isolation with mocked dependencies. Connection: To test class, identify and inject dependencies. To identify dependency = identify responsibility. Single responsibility = minimal inputs/outputs/decision points = easy tests. Pattern: class OrderService {constructor(private paymentProcessor: PaymentProcessor, private inventory: InventoryManager)} - two clear dependencies, easy to mock. Testing SRP-compliant code: Mock dependencies, verify they're called correctly, test single concern. Example: Test OrderService processes payment via PaymentProcessor without needing real payment gateway or database. Violation consequences: class OrderService handles payment + inventory + email → testing requires mocking 10+ dependencies, brittle tests, hard to isolate failures. TDD benefits: Test-Driven Development forces identifying responsibilities upfront - if class hard to test, likely violates SRP. Dependency injection enables both SRP and testability: inject PaymentProcessor interface, vary behavior without changing OrderService. Tools: Use DI frameworks (NestJS, InversifyJS) to manage dependencies.

99% confidence
A

React components should do one thing internally and be used for one purpose externally. Pattern: Break large components into focused pieces. Violation: Component fetches data, filters it, handles pagination, and renders UI (four responsibilities, four actors). Fix: Custom hooks for logic separation: useFetchUsers() (API actor), useFilteredUsers() (business actor), usePagination() (UX actor), UserListDisplay (presentation actor). When useState and useEffect are connected, extract into custom hook. Example: const {users, loading} = useFetchUsers(); const filtered = useFilteredUsers(users, filter); return . Benefits: Hooks are domain-specific (obvious purpose), components are presentational (easy to test), logic reusable across components. TypeScript: Type custom hooks properly for type safety. Modern practice: Functional components encourage small focused functions, hooks replace class lifecycle methods (which grouped unrelated logic). Tool: Split by actor - data fetching (backend actor), UI interactions (UX actor), business rules (product actor). React 2024: Server components, suspense boundaries further separate concerns by execution context.

99% confidence
A

Code analysis tools identify SRP violations through metrics, patterns, and coupling analysis. SonarQube (2025): Industry standard with 6,500+ rules across 35 languages, 7 million developers worldwide. Detects: (1) High complexity (cyclomatic complexity >15), (2) Low cohesion (LCOM metric), (3) High coupling (CBO metric), (4) Code smells ('Classes should not be coupled to too many other classes' rule targets SRP). Thresholds: Class >300 lines, >10 methods, >5 dependencies. Other tools: (2) ReSharper (C#/.NET) - suggests splitting classes, identifies coupling. (3) ESLint with complexity plugins (JavaScript/TypeScript). (4) CodeClimate - maintainability scores. (5) Visual Studio Code Analysis. Pattern detection: Classes named *Manager, *Utils, *Helper indicate unclear responsibility. TypeScript example: ESLint complexity rule flags class OrderService {validateOrder(), calculateTax(), processPayment(), sendEmail()} - four concerns detected. Limitations: Tools detect symptoms (size, complexity) not semantic violations (multiple actors). Require human judgment: Which actor owns this code? Best practice 2025: Combine SonarQube quality gates with actor-focused code reviews.

99% confidence
A

Don't apply SRP prematurely or rigidly - balance with YAGNI (You Aren't Gonna Need It) and pragmatism. YAGNI vs SRP tension: SRP adds extensibility, YAGNI avoids premature complexity. Rule of Three (2025 best practice): Tolerate coupling until pain becomes apparent (three variations), then refactor. When NOT to apply: (1) Trivial DTOs - interface UserDTO {id, name, email} doesn't need splitting. (2) Stable code - No changes in 2+ years, serves one actor clearly. (3) Prototypes - Requirements unclear, wait for patterns to emerge. (4) Responsibilities that always change together - If validation and business rules never change independently, combining them is pragmatic. (5) Over-fragmentation - 50 tiny classes harder to navigate than one cohesive class. Warning signs of over-engineering: Classes with single method, forced abstractions with no variation, complex navigation for simple tasks. 2025 guideline: Strict SOLID application without context leads to unnecessary complexity. Pragmatism over dogma: Apply SRP when benefits (easier testing, clearer ownership, reduced coupling) outweigh costs (more files, indirection). Context: SRP more valuable in large teams (clear actor ownership) than solo projects. Master knowing when to bend rules for the right reasons.

99% confidence
A

Dependency injection (DI) separates object creation responsibility from business logic - class focuses on its responsibility, DI container handles dependencies. Without DI: class UserService {private emailService = new EmailService(); private db = new Database();} - UserService responsible for creating dependencies (violates SRP). With DI: class UserService {constructor(private emailService: EmailService, private db: Database) {}} - dependencies injected, UserService only handles business logic. Benefits: (1) Single actor - UserService serves business requirements actor, not infrastructure actor. (2) Testability - inject mocks for testing. (3) Flexibility - swap implementations without changing UserService. DI containers (NestJS, InversifyJS, tsyringe) manage lifecycle and scope. Pattern: constructor injection for required dependencies, property injection for optional. TypeScript example: @injectable() class UserService {@inject(EmailService) emailService; processUser() {...}}. Warning: DI doesn't guarantee SRP - can still inject 10 dependencies (God class). Rule of thumb: >5 constructor parameters signals SRP violation. Refactor: split class into smaller services with fewer dependencies.

99% confidence
A

Each microservice should serve one business domain/actor (bounded context). Service boundaries aligned with organizational actors prevent coupling. Example: E-commerce - OrderService (sales actor), PaymentService (finance actor), InventoryService (warehouse actor), NotificationService (customer support actor). Each serves distinct stakeholder group with different change drivers. Violation: MonolithicService handles orders + payments + inventory - changes from finance actor affect warehouse operations. Granularity: Not too coarse (monolith), not too fine (nano-services). Sweet spot: Service per aggregate root (DDD) or business capability. Communication: Services communicate via events (loose coupling) or APIs. Testing: Each service testable in isolation, mock external dependencies. Deployment: Independent deployment per service (actor-driven changes isolated). Warning: Don't split by technical layer (APIService, DatabaseService) - split by business actor. 2025 trend: Team-per-service ownership reinforces actor alignment (team owns service serving their stakeholder). Conway's Law: System structure mirrors organization structure - microservices boundary IS actor boundary.

99% confidence
A

Database tables should represent single entities/actors, not mixed concerns. SRP in schema: Table serves one domain concept. Good: users (authentication actor), user_profiles (profile actor), user_preferences (UX actor) - three actors, three tables. Bad: users table with auth columns + profile columns + preferences columns + audit columns + billing columns - serves 5 actors, changes from billing team affect authentication. Normalization supports SRP: 3NF eliminates redundancy and mixed responsibilities. Example: orders table (sales actor) + order_items table (order details) + order_audit (compliance actor) - separate concerns. Violation: God table with 50+ columns serving multiple business units. Refactor: Vertical partitioning by actor - split into focused tables. Trade-offs: Over-normalization (100 tables for simple domain) vs under-normalization (one table for everything). Production: Use database views to aggregate data for specific actors without mixing storage responsibilities. Actor boundaries: Finance tables (invoices, payments), Operations tables (inventory, shipments), Customer tables (profiles, preferences). Warning: Don't confuse joins with SRP violations - joining user + profile for query is fine if stored separately.

99% confidence
A

Controllers should ONLY handle HTTP concerns - request/response parsing, validation, routing to services. Single responsibility: Translate HTTP to domain calls, domain results to HTTP. Pattern: @Controller() class UserController {@Post() createUser(@Body() dto: CreateUserDTO) {const user = await this.userService.create(dto); return {id: user.id, status: 201};}}. Controller handles: (1) HTTP routing (@Post, @Get), (2) DTO validation (@Body), (3) Response formatting (status codes, JSON). Does NOT handle: business logic, database access, external APIs. Those belong in services. Violation: Controller calculates tax, sends email, updates database directly - serves business actor, email actor, database actor. Fix: Move logic to UserService (business actor), EmailService (ops actor), UserRepository (data actor). Controller only coordinates. Actor: HTTP actor (API consumers who need REST interface). Changes from API versioning don't affect business logic. TypeScript example: Controller thin (10-30 lines per endpoint), services thick (business logic). Testing: Controller tests verify HTTP behavior (status codes, validation), service tests verify business logic. Modern frameworks (NestJS, Fastify) enforce this separation via decorators and DI.

99% confidence
A

Error handling should be separated by concern: error generation (business logic), error transformation (layer boundaries), error presentation (UI/API). Layered approach: (1) Domain layer throws domain exceptions - UserNotFoundException, InvalidEmailError. (2) Application layer catches and transforms - maps domain exceptions to application errors. (3) Infrastructure layer handles technical errors - DatabaseConnectionError, NetworkTimeout. (4) Presentation layer formats for display - HTTP status codes, user-friendly messages. Violation: Business logic catches database errors and returns HTTP responses - mixes three actors (business, infrastructure, API). TypeScript example: try {await userService.create()} catch (e) {if (e instanceof UserExistsError) return {status: 409}; throw e;}. Pattern: Use exception filters/middleware for cross-cutting error handling - @Catch() decorator in NestJS, Express error middleware. Single responsibility: ErrorLogger logs errors (ops actor), ErrorTransformer maps exceptions (API actor), ErrorNotifier sends alerts (on-call actor). Warning: Don't create God error handler that handles all errors everywhere - separate by actor. Production: Domain errors for business rules, technical errors for infrastructure, HTTP errors for API layer. Each layer responsible for its error concern.

99% confidence
A

Event handlers should handle one type of event for one actor. Pattern: UserCreatedHandler processes user creation (business actor), SendWelcomeEmailHandler sends email (ops actor), UpdateAnalyticsHandler tracks metrics (analytics actor) - three handlers for UserCreatedEvent, each serves one actor. Violation: Single handler does validation + email + analytics + audit logging - serves four actors. Event sourcing: Each event represents single fact (OrderPlaced, PaymentProcessed), aggregate applies events to update state. Responsibility: Event producer publishes facts, event consumers react independently. Actor alignment: Sales team owns OrderPlacedHandler (business logic), finance owns PaymentProcessedHandler (accounting), warehouse owns InventoryUpdatedHandler (operations). Benefits: Handlers independently deployable, one actor's changes don't affect others. TypeScript example: @EventHandler(UserCreatedEvent) class SendWelcomeEmailHandler {async handle(event) {await this.emailService.send(event.email);}}. Saga pattern: Orchestrator coordinates multi-step transactions but delegates work to specialized services (each SRP-compliant). Warning: Avoid God event handler that processes all events - separate by event type and actor.

99% confidence
A

Logging/monitoring should be separated from business logic via cross-cutting concerns (aspect-oriented programming, middleware). Pattern: Business classes focus on domain logic, logging handled by decorators/interceptors. Violation: class UserService {create(user) {this.logger.log('Creating user'); this.metrics.increment('user.created'); const result = this.db.save(user); this.logger.log('User created'); return result;}} - mixes business logic (database actor) with logging (ops actor) and metrics (monitoring actor). Fix: Use decorators - @Log() @Metrics('user.created') async create(user) {return this.db.save(user);}. Logging interceptor adds logs automatically. Separation: (1) Application logging (business events for audit) - separate logger. (2) Technical logging (errors, performance) - infrastructure concern. (3) Metrics (counters, timers) - monitoring service. (4) Tracing (distributed requests) - observability layer. TypeScript: NestJS interceptors, Winston logger, Prometheus client. Production: Structured logging with correlation IDs, log levels (DEBUG, INFO, ERROR), separate log storage per concern. Actor: Ops team owns logging infrastructure, developers own business event logging, SRE owns metrics. Warning: Over-logging violates SRP - log decisions should be configuration, not scattered in business code.

99% confidence
A

Interfaces should represent one role/contract for one actor. Good: interface Authenticator {login(), logout()} (auth actor), interface UserRepository {find(), save()} (database actor), interface EmailSender {send()} (notification actor) - focused contracts. Bad: interface IUser {login(), logout(), save(), find(), sendEmail(), generateReport()} - serves auth, database, email, reporting actors. Violation: Fat interface forces implementations to depend on methods they don't use (violates Interface Segregation Principle too). TypeScript example: interface PaymentProcessor {process(amount): Promise} - single responsibility, easy to mock. Implementation: class StripeProcessor implements PaymentProcessor {...}. Multiple interfaces: class UserService implements UserRepository, Authenticatable - composes multiple focused responsibilities. Actor-based design: interface CFOReportGenerator {generateFinancialReport()} (finance actor) separate from interface COOReportGenerator {generateOperationalReport()} (operations actor). Benefits: Small interfaces easier to implement, test, mock. Composition over fat interfaces. Warning: Don't create one interface per method (over-fragmentation) - group cohesive methods serving same actor. Production: TypeScript strict mode enforces interface contracts, prevents unintended coupling.

99% confidence
A

Key metrics: (1) Coupling Between Objects (CBO) - counts dependencies. CBO >10 suggests God class. (2) Cyclomatic Complexity - methods >10 often mix concerns. (3) Lines of Code (LOC) - classes >300 lines likely violate SRP. (4) Number of methods - >15 public methods suggests multiple responsibilities. (5) Fan-out - class uses >7 other classes indicates mixed concerns. (6) LCOM (Lack of Cohesion of Methods) - measures how related methods are (note: removed from SonarQube 4.1+ due to false positives, available via third-party plugins like CKMetrics). Low LCOM (0-0.3) = high cohesion, high LCOM (>0.6) = low cohesion. SonarQube 2025 thresholds: Class LOC >300, method complexity >15, dependencies >10. Ideal targets: CBO <5, complexity <10, LOC <300. TypeScript tools: ts-morph, madge (dependency analysis), complexity-report, ESLint complexity plugins. Production: Track metrics in CI/CD, fail builds on violations. Example: Class with CBO=12, LOC=500, complexity >15 → refactor into 3-4 focused classes. Caveat: Metrics detect symptoms (size, coupling, complexity), not root cause - require human review to identify actual actors. Use metrics as signals, not absolute rules.

99% confidence
A

State management should separate concerns by actor: UI state (UX actor), server state (API actor), form state (validation actor), app state (business actor). Pattern: Redux/Zustand slices per domain. Good: userSlice (user actor), cartSlice (e-commerce actor), notificationSlice (ops actor) - separate reducers. Bad: Single God store with mixed concerns - app state + UI toggles + API cache + form errors. Violation: One reducer handles authentication + shopping cart + notifications - three actors. React: useState for component-local UI state, React Query for server state, Formik for form state, Context for global app state. Separation: (1) Server state (react-query, SWR) - caching, background updates. (2) Form state (Formik, react-hook-form) - validation, submission. (3) UI state (local useState) - modals, toggles. (4) Global state (Context, Redux) - auth, theme. TypeScript: Type state by domain - UserState, CartState, UIState. Actor alignment: Product team owns cart state, backend team owns API state, UX team owns UI state. Warning: Global state for everything violates SRP - use local state where possible. Production: Separate state updates by feature (feature flags), deploy state changes independently.

99% confidence
A

Repository separates data access (database actor) from business logic (domain actor). Pattern: interface UserRepository {find(), save(), delete()} - single responsibility is data persistence. Business logic stays in UserService. Violation: class UserService {saveUser() {const sql = 'INSERT INTO users...'; db.execute(sql); this.sendEmail();}} - mixes business logic, SQL, and email (three actors). Fix: UserRepository handles SQL (DBA actor), UserService handles business rules (business actor), EmailService handles notifications (ops actor). TypeScript example: class TypeORMUserRepository implements UserRepository {async find(id) {return this.repo.findOne(id);}}. Benefits: (1) Business logic doesn't know about SQL/NoSQL. (2) Easy to mock repository for testing. (3) Switch databases without changing business code. Actor: DBA owns repository schema changes, business owns service logic. Generic repository anti-pattern: class Repository {find(), save()} - too abstract, lacks domain semantics. Better: Specific repositories - UserRepository, OrderRepository, PaymentRepository. Production: Repository per aggregate root (DDD), use query builders (TypeORM, Prisma) inside repository, never in services. Warning: Fat repositories with business logic violate SRP - keep repositories thin (data access only).

99% confidence
A

Service layer responsibility: Orchestrate business operations (business actor), coordinate domain objects and repositories, enforce transaction boundaries. Does: (1) Business workflows (multi-step operations). (2) Transaction management. (3) Call repositories for data. (4) Return DTOs to controllers. Does NOT: (1) HTTP parsing (controller responsibility). (2) SQL queries (repository responsibility). (3) Domain logic (domain model responsibility). (4) Presentation formatting (view responsibility). Example: class OrderService {async createOrder(dto) {const user = await this.userRepo.find(dto.userId); const order = Order.create(user, dto.items); await this.orderRepo.save(order); await this.emailService.send(order.confirmationEmail); return order;}}. Service orchestrates but delegates. Actor: Business stakeholders who define workflows. Changes to checkout process affect OrderService, not UserRepository or EmailController. Violation: Service contains SQL, HTTP responses, validation rules, formatting - four actors. Anti-pattern: Anemic service (only pass-through to repository) - lacks orchestration value. Transaction script pattern: Service per use case (CreateOrderService, CancelOrderService) vs one service per entity. Production: Service layer in application/use-case layer (Clean Architecture), domain logic in entities/value objects.

99% confidence
A

CQRS is SRP applied to read/write operations - separate models for commands (writes) and queries (reads). Pattern: CommandHandler modifies state (write actor), QueryHandler retrieves data (read actor) - different actors with different optimization needs. Example: CreateUserCommandHandler (business write operations), GetUserQueryHandler (read/reporting operations). Separation allows: (1) Write model optimized for transactions, validation, consistency. (2) Read model optimized for performance, denormalization, caching. (3) Different actors - CFO needs reports (queries), operations needs to create orders (commands). Violation: Single model handles create + update + complex reports - serves write actor and read actor with conflicting requirements (normalization vs denormalization). TypeScript: interface CommandHandler {execute(cmd: T): Promise}, interface QueryHandler<T, R> {execute(query: T): Promise}. Actor alignment: Write side serves business users making changes, read side serves analysts/reporting. Event sourcing often paired with CQRS - commands generate events, projections build read models. Production: Separate databases for read/write (eventual consistency), scale reads independently. Warning: CQRS adds complexity - only apply when read/write concerns genuinely conflict.

99% confidence
A

Refactoring strategy: (1) Identify actors - list stakeholders who request changes (CFO, COO, CTO, users). (2) Map methods to actors - group methods by which actor they serve. (3) Extract classes per actor - UserAuthenticator (IT actor), UserProfileManager (users actor), UserReportGenerator (CFO actor). (4) Update dependencies - inject new classes where needed. (5) Migrate incrementally - keep old class as facade initially, gradually migrate callers. TypeScript example: class User {login(), updateProfile(), generateReport()} → Extract: class Authenticator {login()}, class ProfileManager {update()}, class ReportGenerator {generate()}. Techniques: Extract Class, Extract Interface, Move Method. Tools: IDE refactoring (VS Code, WebStorm) automates extraction. Testing: Write tests before refactoring (characterization tests), ensure tests pass after each step. Strangler Fig pattern: Create new classes alongside old, migrate usage gradually, deprecate old class. Warning: Don't extract blindly - verify actors are truly distinct (different change drivers). Production: Refactor when adding features (Boy Scout Rule), not big-bang rewrites. Allocate 20% of sprint to refactoring. Metrics: Track class size and complexity over time, celebrate reductions.

99% confidence
A

SRP can hurt performance when excessive abstraction adds overhead, but usually negligible. Trade-offs: (1) Indirection cost - calling 5 small classes vs 1 method in God class adds function call overhead (nanoseconds in TypeScript/JavaScript). (2) Memory - more objects = higher memory usage. (3) Instantiation - creating multiple service instances slower than one. Real impact: Usually <1% performance difference in application code (I/O dominates). Example: Splitting calculateTax() into TaxRateProvider, TaxCalculator, TaxFormatter adds 3 method calls (0.0001ms) - irrelevant compared to database query (10ms). When it matters: (1) Hot paths - inner loops processing millions of items (game engines, image processing). (2) Embedded systems - memory constrained. (3) Real-time systems - microsecond latency requirements. Solution: Profile first, optimize hot paths only. Extract performance-critical code into optimized implementation while keeping SRP elsewhere. TypeScript: V8 JIT optimizes small functions - modern JS engines inline calls, eliminating overhead. Production: 99% of code is not performance-critical - favor SRP for maintainability. For 1% hot paths: optimize after measuring. Warning: Premature optimization citing performance to avoid SRP is anti-pattern - measure first.

99% confidence
A

DDD entities and value objects follow SRP - each represents one domain concept for one actor. Entity responsibility: Maintain identity and invariants for its aggregate. Example: Order entity enforces order rules (sales actor), Payment entity enforces payment rules (finance actor), Shipment entity enforces shipping rules (operations actor). Violation: Order entity handles payment processing + inventory updates + shipping - serves three actors. DDD layering: (1) Domain layer - pure business logic (business actor). (2) Application layer - use cases, orchestration (workflow actor). (3) Infrastructure layer - database, external APIs (technical actor). Aggregate root: Single entry point to aggregate, enforces business rules for its bounded context. Actor alignment: Bounded contexts map to actors - Sales context (sales team), Billing context (finance team), Inventory context (warehouse team). Value objects: Immutable, represent concepts (Email, Money, Address) - single responsibility is value equality and validation. TypeScript: class Order {addItem(), applyDiscount()} - business rules only, no database or HTTP code. Repository per aggregate root. Production: Ubiquitous language per context, each context serves distinct stakeholder group. DDD is SRP at architecture level.

99% confidence