> no matter how small a component already is, the single-responsibility principle can still be applied: every line of code can be assigned its own responsibility
The definition of SRP is to have each class (or module) to have a single reason to change. I don't see how that has anything to do with having each line be assigned a responsibility. If the line changes for the same reasons as it surrounding lines, then, they are part of the same component (to use the author's wording). My guess is that the principle is being taken literally from its name/acronym.
"Reason" is an "in the eye of the beholder" type human thing. They're taking it in the most tortured sense, because under sufficient pressure that's "exactly" what happens anyways. It sounds silly until everything you touch is 20 indirections away.
More to the point the definition of responsibility is ambiguous and rarely shared.
The SRP is a bit like the original agile principles: the intent in writing them down was good and they definitely alluded to something real and valuable but the actual wording is vague enough to allow almost anything - including the exact opposite of the original intent.
SRP doesnt need to be tossed away, just redefined more tightly.
My evaluation on this is always to ask "are these two things intrinsically the same or coincidentally the same?"
Answering that question requires domain interrogation. If after that I'm still unsure, then I err on the side of having two copies with maybe a comment on both pointing to the other.
There’s some art to identifying what the single responsibility should be at a particular abstraction layer and use that. It’s not meant to be taken literally.
there’s an excellent talk in cppcon on this https://youtu.be/Ntraj80qN2k?si=-jsMAccDMKMFQPo8
Worth noting that in each well defined domain area, this can be replied recursively.
I.e. within the domain are the (O) basic structures, relations and operations.
Then (T) practical supporting algorithms, tuned for performance in specific cases, serialize, visualize, or whatever.
Then (A) the code that uses O & A to implement the details of specific solutions or manage specific processes.
Wherever there is a well defined broad class of problems with shared structure, this approach has merit.
• Minimize AT -> A, T, O or X.
• Minimize A -> T, O or X.
• Minimize T -> O or X.
• Minimize O -> X.
Where Blood Type X is... Well its just code that doesn't need to exist. Now all possible code has a blood type. And highest productivity is to increase code in X.
I'd say people more often try to extract a generic component "Group 0" way too early, when no all the differences are obvious. And de-generalizing it (aka introducing copypasta) actually makes everything clearer, simpler, easier to change and reason about.
> Unfortunately, it is often used as a killer argument to chop up a software system beyond all recognition.
In all my years, all the companies and codebases I have ever seen, I have literally never seen a system chopped up this way by the SRP. The number of monoliths, though, are uncountable. The number of modules where some function is trying to juggle 8 different tasks, all conflicting … this is the constant state of affairs of actual code in the industry.
The SRP is not about chopping code up into (literal!) single lines; as I (and many others — this isn't some unique thought of mine) the SRP is about semantic, not syntactic — responsibility. It is fine, within the SRP, to have syntactically identical functions, if they serve semantically different purposes. "Are these two functions/sections of code bound to be the same by the law of physics?" If "no" … it's fine if there's a little copy paste here and there. Copy B's requirements might change down the line, and coalescing them into a single copy would be pain later down the line.
Drink with moderation.
> The big advantage of a group 0 component is that you can consume it within components from any other group (like blood type 0 (sic) can be received by any other type).
sigh. The blood type is "type O". Though, I do like the A/T separation, and yeah, generic-ish things become away of specific logic is usually a smell. (Though I'd love, like, some thoughtful reasoning. It resonates with me … but maybe a "why?".)
> When it comes to deciding where to place which code, I found a particular mantra very helpful: minimize code and maximize use cases. The idea is to have as many reusable software components as possible and to minimize the overall code within an organization.
You'll want to do exactly the opposite. This just leads to maximizing dependencies and grinding everything to halt as everything you could fix in 2 mins will take 6 months of waiting instead. Also literally anything can be "reused" with enough configuration and parameters, with them becoming the source of complexity and programming itself and at the end of the day it was a huge waste to effectively create a new programming language.
"I've seen many teams building distributed monoliths and spaghetti code in pursuit of the SRP."
I've worked with probably 6 or 7 different "microservice" based systems at this point, essentially all of them are just distributed monoliths. I don't think I've met a single person at my company who actually know what the hell a "microservice" is.
A relatively small service that should be deployable independent of the rest of your system. They're usually event driven.
But 99% if the time people think "it's in a container" means it's a microservice. So instead of getting reasonably sized, fairly well isolated, independently deployable services, you get 150 glorified function calls spread out across a Kubernetes cluster.
Nothing spoils the fun of software development like obsessive commitment to things like TDD and SOLID, like you’re some sort of Thoughtworks consultant.
It didn't even get out of the introduction really. They described the problem as they see it, started to layout a solution, and then just fucked off. There should have been a whole example implementation showing each piece for the article to be useful at all.
This would have been more useful if it didn't rely on the reader being familiar with those js libraries. I haven't even heard of a single one of them, let alone used them.
I also don't really see the challenge here. Is it just that people can take things too far? No shit. Extreme adherence to any principle or rule of thumb is rarely a good thing.
> no matter how small a component already is, the single-responsibility principle can still be applied: every line of code can be assigned its own responsibility
The definition of SRP is to have each class (or module) to have a single reason to change. I don't see how that has anything to do with having each line be assigned a responsibility. If the line changes for the same reasons as it surrounding lines, then, they are part of the same component (to use the author's wording). My guess is that the principle is being taken literally from its name/acronym.
"Reason" is an "in the eye of the beholder" type human thing. They're taking it in the most tortured sense, because under sufficient pressure that's "exactly" what happens anyways. It sounds silly until everything you touch is 20 indirections away.
> It sounds silly until everything you touch is 20 indirections away.
Which is how your standard Uncle Bob inspired codebase winds up looking
More to the point the definition of responsibility is ambiguous and rarely shared.
The SRP is a bit like the original agile principles: the intent in writing them down was good and they definitely alluded to something real and valuable but the actual wording is vague enough to allow almost anything - including the exact opposite of the original intent.
SRP doesnt need to be tossed away, just redefined more tightly.
[dead]
My evaluation on this is always to ask "are these two things intrinsically the same or coincidentally the same?"
Answering that question requires domain interrogation. If after that I'm still unsure, then I err on the side of having two copies with maybe a comment on both pointing to the other.
There’s some art to identifying what the single responsibility should be at a particular abstraction layer and use that. It’s not meant to be taken literally. there’s an excellent talk in cppcon on this https://youtu.be/Ntraj80qN2k?si=-jsMAccDMKMFQPo8
Some parts are hard to follow for a non-cpp programmer but overall it's a great video!
Worth noting that in each well defined domain area, this can be replied recursively.
I.e. within the domain are the (O) basic structures, relations and operations.
Then (T) practical supporting algorithms, tuned for performance in specific cases, serialize, visualize, or whatever.
Then (A) the code that uses O & A to implement the details of specific solutions or manage specific processes.
Wherever there is a well defined broad class of problems with shared structure, this approach has merit.
• Minimize AT -> A, T, O or X.
• Minimize A -> T, O or X.
• Minimize T -> O or X.
• Minimize O -> X.
Where Blood Type X is... Well its just code that doesn't need to exist. Now all possible code has a blood type. And highest productivity is to increase code in X.
As they say, less (OTA), is more! (X)
Premature generalization is also a sickness.
I'd say people more often try to extract a generic component "Group 0" way too early, when no all the differences are obvious. And de-generalizing it (aka introducing copypasta) actually makes everything clearer, simpler, easier to change and reason about.
So as in every such blog post -- "it depends".
> Unfortunately, it is often used as a killer argument to chop up a software system beyond all recognition.
In all my years, all the companies and codebases I have ever seen, I have literally never seen a system chopped up this way by the SRP. The number of monoliths, though, are uncountable. The number of modules where some function is trying to juggle 8 different tasks, all conflicting … this is the constant state of affairs of actual code in the industry.
The SRP is not about chopping code up into (literal!) single lines; as I (and many others — this isn't some unique thought of mine) the SRP is about semantic, not syntactic — responsibility. It is fine, within the SRP, to have syntactically identical functions, if they serve semantically different purposes. "Are these two functions/sections of code bound to be the same by the law of physics?" If "no" … it's fine if there's a little copy paste here and there. Copy B's requirements might change down the line, and coalescing them into a single copy would be pain later down the line.
Drink with moderation.
> The big advantage of a group 0 component is that you can consume it within components from any other group (like blood type 0 (sic) can be received by any other type).
sigh. The blood type is "type O". Though, I do like the A/T separation, and yeah, generic-ish things become away of specific logic is usually a smell. (Though I'd love, like, some thoughtful reasoning. It resonates with me … but maybe a "why?".)
KISS my DRY SOLID goodbye.
> When it comes to deciding where to place which code, I found a particular mantra very helpful: minimize code and maximize use cases. The idea is to have as many reusable software components as possible and to minimize the overall code within an organization.
You'll want to do exactly the opposite. This just leads to maximizing dependencies and grinding everything to halt as everything you could fix in 2 mins will take 6 months of waiting instead. Also literally anything can be "reused" with enough configuration and parameters, with them becoming the source of complexity and programming itself and at the end of the day it was a huge waste to effectively create a new programming language.
I actually lean towards copy and pasting the same code into many files just so that they are self-contained and have no dependencies.
"I've seen many teams building distributed monoliths and spaghetti code in pursuit of the SRP."
I've worked with probably 6 or 7 different "microservice" based systems at this point, essentially all of them are just distributed monoliths. I don't think I've met a single person at my company who actually know what the hell a "microservice" is.
What the hell is a "microservice"?
A relatively small service that should be deployable independent of the rest of your system. They're usually event driven.
But 99% if the time people think "it's in a container" means it's a microservice. So instead of getting reasonably sized, fairly well isolated, independently deployable services, you get 150 glorified function calls spread out across a Kubernetes cluster.
Nothing spoils the fun of software development like obsessive commitment to things like TDD and SOLID, like you’re some sort of Thoughtworks consultant.
Much like Unbreakable, I felt like this ended just as it was starting to get good.
It didn't even get out of the introduction really. They described the problem as they see it, started to layout a solution, and then just fucked off. There should have been a whole example implementation showing each piece for the article to be useful at all.
I just rewatches deadwood and i read your comment in the voice of al swearengen. I also tossed in a few "hooplehead" references for good measure.
I've never seen Deadwood so I had to look up some scenes with that character. I'm gonna take this as a compliment lol
The highest of compliments.
This would have been more useful if it didn't rely on the reader being familiar with those js libraries. I haven't even heard of a single one of them, let alone used them.
I also don't really see the challenge here. Is it just that people can take things too far? No shit. Extreme adherence to any principle or rule of thumb is rarely a good thing.