Concept · SOLID principles
Single responsibility principle
A module should have one reason to change — it should answer to one actor — so changes requested by different people don't collide.
The single responsibility principle (SRP), the S in SOLID, says a module should have one reason to change. Robert Martin later sharpened the wording: a module should be responsible to one actor — one person or group who might request changes to it.
Reasons to change, not "one thing"
SRP is often misread as "a type should do only one thing" or "functions should be tiny". It's about who drives change. Consider:
type Employee struct{ /* ... */ }
func (e Employee) CalculatePay() Money { /* rules owned by finance */ }
func (e Employee) ReportHours() string { /* format owned by operations */ }
func (e Employee) Save() error { /* schema owned by the database team */ }Three actors share one type. Finance changes overtime rules and the change lands in a file operations also depends on. If CalculatePay and ReportHours share a helper like regularHours(), finance's change can silently alter operations' report — a bug nobody asked for and nobody tests for.
Split along the actors: a pay calculator, an hours reporter and a repository, each changing only when its owner asks. They can share data types without sharing logic.
Recognising violations
- A file appears in pull requests for unrelated features.
- A type's methods use disjoint subsets of its fields.
- Describing what a module does needs "and": "it validates orders and renders invoices and syncs to the ERP".
- Tests for one behaviour require setting up dependencies for another.
How far to take it
A cohesive type with many methods that change for the same reason is fine; splitting it into ten one-method types just scatters related code. Apply SRP where different actors genuinely pull on the same code. It's the class-level face of cohesion, and at module level it becomes package by feature. The SOLID lesson covers all five principles together.