This week one of the readers of my blog asked me this question:
“Do SOLID design principles make code slow?”
I think this is a very interesting question, which I have already encountered several times during my 20 years as a professional software developer.
This question became even more common with the popularization of Agile methods, that in general focus on simplicity following principles such as KISS and YAGNI. While I agree that we should try to keep things simple, I think that over-simplification is extremely dangerous.
The SOLID principles of object-oriented design are:
– Single Responsibility principle
– Open/Closed principle
– Liskov Substitution principle
– Interface Segregation principle
– Dependency Inversion principle
The goal of these design principles is to improve the Separation of Concerns, through weaker Coupling and stronger Cohesion. The main consequence should be software systems that are easier to maintain and to extend. Thus the application of these principles may be seen as an investment in the quality of the software design that has future benefits for the system’s evolution.
However, some potentially undesirable consequences are:
– The proliferation of relatively small concrete classes.
– The proliferation of abstract classes and interfaces.
– A high number of levels of indirection.
– Too many abstraction layers.
– Increase in the depth of the inheritance tree.
– Yo-yo problem.
Thus the indiscriminate application of SOLID principles may in practice increase too much the complexity of software systems, with negative consequences for maintainability and extensibility. This is the reason we need experienced software developers in a team, who have the wisdom to foresee the consequences of their design decisions.
Back to the original question: “Do the SOLID principles make code slow?” As we have seen, this is one possibility, because of the potential increase in the levels of indirection and in the number of abstraction layers. In other words, we may be forced to make more method calls and aggregate the data we need from several small objects.
My advice is to choose a design that is clean, focusing on the separation of concerns, but still as simple as possible. If there are performance concerns, this should be measured using runtime tools such as a profiler. Only if a clear bottleneck is detected should the initial design be modified to make the code faster. In any case, the definition of “slow” and “fast” depends on specific system requirements.
To end with a quote by Donald Knuth: “Programmers waste enormous amounts of time thinking about, or worrying about, the speed of non-critical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.”
Do you agree? Please share your experience in the comments below.
Pick your poison. I’ve worked in a number of companies and while many do not follow SOLID necessarily, all the good ones incorporate some sort of upfront design.
I’d heard complaints from many junior and even mid-level developers that doing design upfront rather than as-you-go slows them down. Except in any case where people start coding without a plan we always eat it on the back-end, usually right before or during QA. Plus it seems like most of those systems were more maintenance intensive, and thus higher cost in the long-run.
My question would be, at what point does everyone realize that a design phase in your project may mean a slower start but you’ll achieve greater velocity and lower cost in the long run?
I agree with you that a design phase allows “greater velocity and lower cost in the long run”. However it seems that most people following Agile practices were convinced that Big Design Up Front (BDUF) is extremely bad, but do not know what is the alternative. My proposal is called Adaptable Design Up Front (ADUF):
https://effectivesoftwaredesign.com/adaptable-design-up-front/
Interesting. I’ll have to digest that some more. Our process is best described as Agile with a Waterfall front-end, so my current job would be best described by you as BDUF.
The obvious problem is that if our iterations employ agile, it doesn’t take much for the initial design documents to become out-of-date. Furthermore, the software designers are often pressured to begin their design docs before requirements are fully gathered.
The one issue I see out of the gate for us with ADUF is that our issue is more about how our PMO operates than our architecture team.
Still worth pointing out to my boss though.
They do know an alternative to BDUF. It’s called TDD (aka: Red, Green, Refactror). Using TDD, a thoroughly tested architecture (aside from performance) emerges from proven tests and not just theory.
Upfront design is a wasteful illusion of people that believe they have all the answers before they build a system. They make decisions upfront that can be deferred until later and ends up costing the client more money based on realized design conflicts that could’ve been avoided.
Upfront design locks the business into a decision prematurely with limited information. I wrote a blog post an hour ago on why software architects are irrelevant.
Thanks for your comment. I really like when you write “aside from performance”. Perhaps the reason you think that “software architects are irrelevant” is that you are working on systems in which performance is an afterthought. In complex software systems, the non-functional attributes such as performance are as important as the functional requirements.
Hi Hayim. Thank you for your feedback.
Performance is a relative constraint in my view. Does performance really need to be dealt with up front when coding business rules?
In regards to algorithms, performance matters as long as the code is maintainable afterwards (in most cases).
However, I’m not so sure that designing for performance before coding the actual business rules is really necessary. The business rules are a much higher priority than performance or security when starting a project. Developers do need to be conscious of Big O’ notation as they are managing collections. But they should not be distracted with external dependencies while they’re focused on implementing the business rules.
SDETs can stress test the software and then report their findings to “the architect” so that more hardware can be thrown at the bottleneck after performing root cause analysis and eliminating algorithms from the analysis performed.
Who knows, maybe the client doesn’t really need to shell out as much money as the architect thinks for servers to manage the workload. An alternative is the cloud to respond to workload. Again, these decisions can be deferred to a later time even though an architect wants to protect their “relevance” to the client.
Couldn’t agree more with you, Hakim, especially with regards to profiling the app in order to determine where the bottlenecks are.
One trivia fact about the famous phrase against premature optimization, popularized by Donald Knuth: it was first pronounced by British CS Sir Anthony Richard Hoare.
to say it with the words of my former teacher at the university: a wrong program can be quite fast in comparison and it can be optimized even more.
Is a CUSTOMER really paying us for SOLID programming?
All software design comes down to choices and the reasons for your choices. SOLID is a great way to build software- except when it isn’t. Problem is that SOLID/GRASP/DRY/YAGNI aren’t absolute rules- they’re guidelines you use to protect your code from future changes. But if you don’t have some sort of context for those guidelines, you can’t make reasonable decisions on what change to protect from and what changes to ignore.
I’m starting to see more and more on this from other corners of the Architecture world- Gene Hughson, for instance. Also something I’ll be talking about at dev.Objective() in Minneapolis in May. http://www.devobjective.com/sessions/software-architecture-it-s-about-marcus-aurelius-not-microservices/
Pingback: The SOLID Principles Illustrated by Design Patterns | Effective Software Design
Pingback: Agila utvecklare måste även skriva sin kod på ett agilt sätt för att var helt agila – Robert Forsström's Blogg
Pingback: Agila utvecklare måste även skriva sin kod på ett agilt sätt för att vara helt agila – Robert Forsström's Blogg