The Single Responsibility Principle. It’s my desert island principle. If I had my brain cleansed of all software wisdom except for one thing – it would be the good ol’ SRP. Every software creator should seek to have a tight grasp of this principle. It provides a huge amount of clarity in exchange for the simple task of thinking about things in this particular way.
And that is, of course, that every thing (method, function, class, package, etc..) should have only one reason to change – it should have a single responsibility. I won’t re-hash the entire principle write-up that I linked to above – but it creates clarity when you read the code because your brain doesn’t have to shift contexts as it looks at a single thing, and when you go to change code, you don’t risk changing a responsibility you’re not interested in changing.
The SOLID principles were originally, frequently referred to as Principles of Object Oriented Development. And they are that. But they are actually deeper than that – they are Principles of Software Development that apply equally to any development paradigm. Of particular interest, in addition to OOP, because of its impact on the current state-of-the-art is Functional Programming.
These two paradigms, OOP and FP, are often pitched as being radically different. They aren’t – they stem from the same need, take two different paths, but arrive ultimately in the same place.
I often say that there are two audiences that you write your software for; the user, and the reader. Making a computer do something useful these days is relatively straightforward. The art – the thing that distinguishes the true Craftsman from the amateur – is how you treat the second audience – the reader of the code. And how one reads and understands code is as subjective, if not more so than how one uses the functioning system. It’s an exercise in aesthetics. It is understanding vocabulary and idioms, and it’s making intent and reasoning clear. When all these pieces come together, the reader has a positive emotional response and is able to understand and modify the code quickly.
Making the intent of the reasoning clear means making concepts crisp and logic obvious. And using abstraction to eliminate unnecessary details (because we’re limited in our ability to store information and reason about it). If we didn’t have to worry about humans understanding and reasoning about our code, we could write it all in binary – there would be no reason to bother with higher level languages. They’re an accommodation for our inherent weakness.
Human reasoning happens on a number of levels – from levels that we are explicitly aware of, and can consciously arrange syllogisms to get to the result we want – to levels that are so unconscious, that we barely even know we’re doing (though we might be able to feel ourselves doing). We romanticize the unconscious reasoning – because can look and feel like magic – because we’re pulling answers out of a hat without being able to describe how we got to them. We tend to think of the more conscious versions of our reasoning as more scientific, more rational. The truth is they’re both very powerful and useful in different ways – some of us balance between the two, some of us have a severe tendency toward one or the other.
In software development – these two styles are approached with either of these two high-level paradigms we’ve mentioned: OOP with its mantras about nouns that match the real world, having things that do things, is an approach that appeals to the unconscious style of reasoning – more purely to intuition. And FP, with its mantras about equational reasoning and functional purity appeal to explicit rationale – to the conscious style of reasoning.
The great part is that our consciousness of the reasoning we’re performing doesn’t change the nature of the reasoning. We abstract and then draw causal connection and then deduce the outcome. Subconscious reasoning, with its speed, may sacrifice accuracy. And conscious reasoning, with its accuracy, may sacrifice speed. But the ultimate reality is it’s the same thing, just done differently by us.
The SOLID principles circumvent our mechanisms and are connected to the underlying reality of reasoning. Since this is the case, they apply directly to any kind of programming we may do. And further, the more we follow SOLID principles, the more our code is inherently rational. So whether we start from a FP perspective and follow SOLID Principles or start from an OOP perspective and follow SOLID Principles – we end up in the same place. Code that is inherently more rational, which will on the surface mean, OO code that starts to look like good FP code and FP code that starts to look like good OO code.
Anyway – back to SRP – step with me into the shoes of an OO programmer. One of the articles of common wisdom held by OO programmers is that you’re supposed to “hide data behind behavior”. That is, if you believe the textbooks on the topic, you might think it’s mandatory if you have a class, it should have state *and* behavior in that class.
But if we look closely at the SRP – and think about what it means to “have a single reason to change”. We realize that a class having state and behavior, *always* has two reasons to change. A class that has a Single Responsibility really only represents one or the other.
And further – a good class that’s doing only one thing, shouldn’t really have more than a method or two. And two is pushing it. Unless, maybe you’re using the class as a kind of package, in which case you might have related methods that do independent things.
And both of these apply across the board. There are always edge case things, where the flexibility is nice to have – but if we’re really being honest with ourselves, we’re not really SRP if we don’t do these two things.
And if you look at it – if you follow SRP closely like this – immediately the code is easier to reason about. But it also is surprisingly close to good FP code. There’s no state, so the method’s result (barring depending on state elsewhere) is always only the product of it’s parameters.
FP and OO are converging – simply because they are both appeals to human rationality. But they are inspired by different phases of the same reality. SOLID pokes at the root of this and asks us – how do we really make great code that clearly displays intent, and gives the reader the best possible experience.