The Single Responsibility Principle, the most pervasive idea in software engineering and simplest way to improve your code

Intro

The Single Responsibility Principle (SRP) is one of those simple ideas that enlight developers and scientists as well.

When correctly following the Single Responsibility Principle, your code will be faster to develop, simpler to debug and other people will find it easy to navigate it. Hence, it will be simpler for you to share it with your colleagues and for them to enhance it or to modify it.

The SRP introduces a simple rule so that you can have a very quick and easy to check test to immediately see if your code is well written or if something should change.

If you violate the Single Responsibility Principle, most likely you should re-think about your code, however, if you don’t violate it, it does not mean that your code is perfect.

The Single Responsibility Principle

The Single Responsibility Principle roughly states that:

A module should have only one reason to change

Here a “module” can be interpreted as a class, or as a set of classes, or as a function or even as a part of your single file.

Making it concrete

While the SRP is well-known between software engineers, it can sound strange at first to scientist or junior alike. A more useful view of the SRP simple will state that each part of your system should do one thing and one thing only.

Being responsible for only a single functionality.

Suppose your software needs to read data from a file, and then it needs to do some calculation and finally print the results.

Following the SRP, there will be three part in your software.

The first part, the input-handler will be responsible to read the files and package all the information in a suitable data structure (list, arrays, dictionary, map, etc…) for the calculation.

The second part, the compute will be responsible for reading the data structure coming from the input-handler, do the necessary computation, and return the result.

The third part, the output-handler will be responsible to read the data from the compute and print them to the screen.

If the format of the file changes, then, only the input-handler will need to change. Crucially the new input-handler will need to create a data structure that the compute know how to handle. Most likely, the new input-handler will need to create the same data structure of the old.

The new input-handler will need to respect the same interface, of the old input-handler.

If instead of reading from a file we start to read from a database, similarly, as long as we pass a suitable data-structure to the compute part, the input-handler is free to change to fit the needs of your analysis.

Same approach to the compute part, as long as it can read the data that comes from the input-handler and pass suitable data to the output-handler it can internally change to fit your needs. Maybe you don’t need anymore to compute the average, but you need the median.

Finally, the output-handler, as long as it can read the data from the compute part, it does not really matter what it does. When you are starting your analysis it could be sufficient to just print to screen the results, but if your analysis get more complex you may want to start creating plots.

Deeper than it sound

While our example seems very simple and easy to implement, the SRP is a driving force in software engineering.

It can be applied on both the architecture of the code, but also in smaller parts. If your simulation or analysis is composed of several steps, is vital to correctly applying the SRP. Both for speeding up the developing time, to make it simpler to find error, and to make it simpler to share it with your colleagues.