DEV Community

Cover image for SOLID SRP - 30 years later
Christopher Mortenson
Christopher Mortenson

Posted on

SOLID SRP - 30 years later

This is part 1 of a 5 part series, going through each of the principles in SOLID, how they have aged, and investigating if we can achieve a deeper more comprehensive understanding of what each principle is trying to achieve.

SRP - The Single Responsibility Principle

A common misunderstanding

To Uncle Bob's own words, SRP is commonly misunderstood to mean

A module should do one thing and one thing only.

In reality, SRP is meant as a corollary to Conway's law, which in simple terms state that software will naturally be segregated to mirror the topology of the teams building the software.

Microservices is probably the most extreme example of this law. If you have one team dealing with user notifications and another dealing with user analytics you can bet that these two features will end up being split into separate services. This is because the communication overhead required to merge it into a single service is high, so there will be a natural resistance to it.

With this in mind, SRP is really supposed to be

A module should only belong to a single team.

Now, Uncle Bob assumes that the team topology matches the target user group of the software, so he goes one further and states that

A module should be responsible to one, and only one, actor.

As such, this is an important thing to note:

SRP only applies if your team topology is a one-to-one match with your user groups.

I personally believe that product-aligned teams are a good idea, which naturally implies user-group-aligned teams. But not all organisations are structures this way (for example a company might have DB team, and an AI team, but they each support many different products), so in those cases this distinction is important to note.

I never found clarity in which Uncle Bob belives is more important; the segregation on team or segregation on user group.

How does SRP help?

Now that we've cleared that up, let's go into the arguments for SRP.
Uncle Bob brings up the inherent issues in code shared by multiple user groups. In Clean Architecture, he gives two examples:

1. Relying on shared functionality

The example has two teams relying on a single function to calculate the payroll of employees.
One team decides to change the logic of that function, causing a bug to occur for the other team's users.

2. Merge conflicts

Two teams decide to mutate a single function in different ways, causing merge conflicts where, if a bug were to pop up, now risks impacting 2 user groups instead of just one.

Now, I want to argue that these issues are all inherent issues in code that is shared, i.e. relied upon in more than one circumstance. They have nothing to do with team topology, user group topology, or even the software topology.
If you decide to use a shared piece of code, you should be prepared for the consequence of the fact that the semantics of that code is also shared, i.e. all users of that code will all do the same thing.

In example 1, if multiple teams are using a single function to calculate payroll, the purpose for sharing that function should be that they all calculate the function in the exact same way. If they do not, they should not be sharing code - each team should be doing their own payroll calculations!

In example 2, if you have a merge conflict in a shared function, it is because you inherently want all involved parties to change their behaviour in that particular way, otherwise you would have your own version of it. This needs to be resolved if you want the behaviour to be shared. It's a real-life business logic conflict that needs to be solved.

The underlying lesson here should rather be that unless you explicitly want the shared code for its inherent property of being shared, shared code is a liability, not a virtue.

My version

With all that in mind, I'd like to reshape SRP into this:

Use shared modules if you
a) Are also willing to take on the liability of it being shared
OR
b) Want to explicitly ensure that the behaviour of your program is shared with other users of that module.
Otherwise, you should copy/freeze the logic you are relying on.

(Side note: Before all the DRY people come at me, "copying" doesn't necessarily mean literally copying code. It could be freezing a version of a dependency, using a library instead of a service, etc. It's just any code that is not automatically updated by someone else than you)

SRP is now a really poor name for this principle. Maybe we can change the abbreviation to ShaRe Purposefully? Comment your suggestions below!
Also, note that this no longer tells you how to split up your code. That's a big loss! Have no fear, we will have better heuristics for that when we look at the other principles in later posts!

Next time we'll looking at the O in SOLID - the Open-Closed Principle. Stay tuned!

Top comments (0)