Recently, some like-minded engineers at Pivotal NYC started to get together to discuss software design. Our topics so far have included agile architecture, emergent design, and other related topics. This month, we’re focusing on a topic that comes up often among Pivots. The SOLID principles of object-oriented programming and design. We started a series of discussions with the Single Responsibility Principle (SRP).
While working as Pivots, we often introduce SOLID to our clients and new co-workers. This month’s software design lunch was an opportunity for us to deeply discuss the Single Responsibility Principle (SRP) to better understand of how it can be used in our designs and how we can better teach it to others.
What is it?
A class or module should only have one reason to change.
Like any tool in our software design toolbox, we want to make sure that we’re using the right tool for the job. Our group brought up these design smells that can let you know that you might need to use SRP:
- Large classes with high churn: These classes will probably change often because they has many reasons to change. These classes often appear as “God” objects in your system.
- Many private methods: these classes hold lots of details that could be reasons for change
- Public methods with clearly different responsibilities: let’s say you have ‘send_report_via_email’ and ‘get_employees’ as public methods on your class. They clearly have different responsibilities, and therefore reasons to change.
- Conjunctions in method names: let’s say you have ‘send_email_and_log’ as a method name. Your class knows about emailing and logging which are both reasons to change.
There are many other smells that might lead you toward Single Responsibility Principle, but these are the smells that we discussed.
Why should we, as engineers, care to avoid these smells? Why should we be concerned about reasons to change?
When talking about software design, we should be driven by a motivation to reduce the cost of developing software. Techniques like Refactoring and Design Patterns are great, especially in the context of reducing the cost of creating and maintaining software. The SOLID principles also fit in this context.
The Single Responsibility Principle can help to create a codebase that is:
- Easy to change: because of reduced coupling between objects
- Easy to parallelize work: teams aren’t often changing the same class at the same time so there are less merge conflicts. Teams can move faster
- Easy to Re-use: SRP objects have a single purpose, and if that purpose is what another class needs, then it can be re-used without also depending on unwanted behavior
- Easier to read and comprehend: much time can be wasted while trying to understand a complex object. SRP objects are often very easy to understand, which reduces the cost of writing software
- Easy to unit test: SRP objects often have few dependencies and are not complex to set up in test. These objects are easy to design and verify behavior
- Easier to pay attention to quality: because there is less code in one class, it is easier to pay attention to quality. Are you following your team style guidelines? Are your methods well named? Are your private methods truly private? These are easier to achieve with SRP objects.
- Easier to refactor: the internal design of a SRP object is often easy to improve
With all of these benefits, we can apply SRP to reduce the cost of creating and maintaining software.
With any software design pattern or principle, there are things that we should take under consideration when introducing them to our teams:
- SRP can often create many small pieces of code that need to be combined together, which can sometimes be hard to comprehend.
- Team experience and maturity. Is your team good at understanding trade-offs in software design. Do they know these principles already? Do they have other related design skills already?
- Introducing SRP can also lead to an inconsistent code style throughout a system. Legacy code might stand out as very inconsistent with new design.
- SRP might be too much design when you don’t have the Smells/symptoms/warning signs. Maybe your classes wont change, and therefore there is no need to defend against the cost of change.
Introducing SRP usually comes with a need to introduce other design patterns and principles to reduce these concerns. Pick up a Design Patterns book for some inspiration.
What are some ways to introduce SRP to your team?
Our teams find outside-in test-driven development to be a useful technique to create SRP classes. Introducing mocks and stubs into your testing practice can alert you to multiple reasons for change. Programming by wishful thinking is a useful technique, to ‘wish’ for a new class that has the exact responsibility you need, instead of putting the new behavior in an existing class.
Another useful technique for spreading SRP throughout your team is to have a team discussion about it. Demo an example and show off its benefits. This can make it easier for your team members to apply the pattern to other parts of the codebase. You can discuss tradeoffs and when it might be a good time to introduce SRP to a particularly gnarly part of your codebase. This helps to gauge how prepared your team to try new strategies for designing software.