Responsibility Map
Pick a unit and read what it knows, what it does, what it depends on — and every distinct reason it has to change. The last list is the one that matters: two reasons is a design, seven is a queue of future merge conflicts wearing one class name.
Single responsibility is usually taught as “a class should do one thing”, which is unfalsifiable — any behaviour can be described as one thing at a high enough altitude. The version that does work is a question about change: how many separate events in the world would force you to edit this file? Pricing changing is one. Tax law changing is another. The email wording, the schema, the payment provider are three more. Count them, and the design problem stops being a matter of taste. Nothing on this page is graded; the count is the reading, and you decide what it is worth.
- — How a price is calculated
- — Which table orders live in
- — The email template for confirmations
- — Which payment provider is configured
- — What an order status string means
- — Validates input
- — Computes totals
- — Writes to the database
- — Calls the payment provider
- — Sends email
- — Writes an audit record
- — Database
- — Payment SDK
- — SMTP client
- — Template engine
- — Clock
- — Config
- — Pricing rules change
- — The schema changes
- — The email copy changes
- — The payment provider changes
- — A new order status is added
- — The audit format changes
- — Anything about validation changes
Seven distinct reasons to change is the finding. Any one of them forces a redeploy and a full regression of the other six, and every one of those seven teams now queues behind the same file. The fix is not "split it into seven classes" — it is to notice that pricing, persistence and notification are three different rates of change, and to give the two most volatile ones their own address first.