The Single Responsibility of Software Development

Do you know what your single responsibility as a software developer is? Consider this exchange from the movie City Slickers:

Curly: Do you know what the secret of life is? [points index finger skyward] This. Mitch: Your finger? Curly: One thing. Just one thing. You stick to that and the rest don’t mean [expletive]. Mitch: That’s great, but what is the “one thing?” Curly: That’s what you have to find out.

This scene has resonated with many as a great philosophy for approaching life.  However, it can also be interpreted as a philosophy for software development.

Single Responsibility

The “S” of the SOLID design principles is the Single Responsibility Principle. This principle states that, in object oriented design, a class should have one and only one responsibility. That is, a class should do one thing.

Consider the Unix philosophy, as stated by Doug McIlroy, “This is the Unix philosophy: Write programs that do one thing and do it well. Write programs to work together.”

Sound familiar?

The idea of simplifying code down to it’s most essential function is not a new one.  Unix dates back to the early 1970s.  Robert C Martin started talking about the principles that would become SOLID back in 1995.

As you can see, this philosophy of doing “one thing” can be applied to all levels of software.

Classes

This is the most intuitive for people with object oriented design backgrounds.  The idea is that a class is responsible for one and only one thing.  If you find yourself having more than one reason to modify a class, it is probably doing too many things.

For example, suppose we have a class, DataObject, that has two methods: read() and process().  The read() method reads some data off disk and the process() method does something interesting with that data.

DataObject with subclasses has multiple responsibilities

Suppose we want to support multiple data formats: CSV and XML, to start with.  We could extend the DataObject class, overriding the read() method, but the flaw with this design is that the DataObject class is responsible for two things, reading AND processing.

Now, you might argue that the XML and CSV logic lives in separate sub-classes, thus having a single responsibility.  That’s true, but the fundamental problem with this design is that the DataObject hierarchy does two things: read and process.

Using the Single Responsibility Principle, we can separate these concerns and come up with a much cleaner, more extensible design.

Separate reading hierarchy gives each class single responsibility

In this design, each class is responsible for only one thing.  The DataObject processes the data, the CsvDataReader reads CSV data, and the XmlDataReader reads XML data.

Methods and Functions

This notion of a single responsibility can even apply within a single class. The public facing method of a class may need to look up a configuration value, run a database query and call a web service endpoint.  Each of these would be encapsulated in separate methods.

Libraries

Have you ever worked on a project that had a library that was just a miscellaneous grab bag of classes?  This library tends to grow and, before you know it, you find yourself pulling in a huge library because you wanted one utility method to perform some date related math.

Just as classes have a single responsibility, libraries should also have a well defined responsibility.  For example, you might have a library for common string manipulations, one for date utilities, another that contains the logic for calling a specific web service endpoint.

Applications

The applications we write should also have a single responsibility. As mentioned earlier, this is not new.  Unix was based on this idea. In recent years, this idea has returned in the form of microservices.

What is the single responsibility of an application?  This is where things tend to get a bit fuzzy.  It could be to process items in a queue, or service a collection of related REST API endpoints.  Or is your application’s single responsibility to serve the consumer facing website?  It all depends on your definition.  The definition I like tends to focus on the set of data an application manages and the business processes that operate on that data.

Whether you’re building an application, a library, a single class or even a single method within that class, find that one thing, just one thing, that your code does well.  Stick to that and the rest is just integration.

But what is the “one thing”, you ask?

That’s what you have to figure out.