SOLID Principles. Are they as important as they used to be 20 years ago?

Michael Choi
17 min readMay 9, 2020
Photo by Dino Reichmuth on Unsplash

As I coach a lot of my students, I also hear from them what type of interview questions they were asked and how to better prepare for these interviews.

One of the topics I’ve been hearing quite a bit is about hiring managers asking questions specific to SOLID principles for Object-Oriented Programming and whether my students understood how to apply these principles.

For those who are not familiar with SOLID principles, it refers to 5 design principles: S for Single Responsibility, O for Open/closed principle, L for Liskov substitution principle, I for Interface segregation principle, and D for dependency inversion principle.

As I reflect back on my software development career, I wondered if these principles are still as critical. Part of me was saying that these principles are still important, although another part of me was saying that these principles and design patterns are no longer as relevant.

In fact, when I reflect back on the recent web development projects I worked on, these design patterns rarely played a key factor in anything. So if that’s the case for me, would that be the case for other folks in the industry? And if that’s the case, why is it that these questions still get asked during interviews for web developers?

As I reflected on this question, I had a realization that helped me understand how the industry has shifted.

Going back 20+ years ago

Imagine that we went back in time and we were living around the time that Windows 95 just came out. Imagine you were a software engineer and you were part of a team that was making Microsoft Word.

How many engineers would be working on that single software? How many teams would there be? How many system architects would there be writing specs on how the system should work? How many versions would the company need to support?

As you think about these questions, you can imagine a giant team, say a team of hundreds of engineers working on a single software where what one person did could heavily impact how the whole software worked. There would probably be dozens of teams specializing in different areas of the software and the architect would have needed to design how the software would work together, what classes would need to be created, which class would inherit from which class, what methods each class should have, whether a particular class would be an abstract class or not, etc. To coordinate this, it would have required an enormous amount of work.

To better understand the scope and complexity of a project like this, imagine a screen like this:

Look at all the menu items above as well as buttons. As each of those buttons is an object, how would you organize classes for those objects? Would you have all of the 50+ buttons as 50 separate classes? Or would you put them into a single class? Putting them into a single class would be a disaster as each of these buttons would have dozens of features and putting 50+ buttons with dozens of features would make that class way too messy (it also violates the S principle in SOLID).

If we did follow the S principle in SOLID, where each class is supposed to do one responsibility, we can imagine that each button would be an instance of a specific class for that button.

So now imagine that you are going to have classes such as these:

  • UIFileNewButton
  • UIFileOpenButton
  • UIFileSaveButton
  • UIPrintButton
  • UIGrammarCheckButton
  • ….

Wouldn’t some of these buttons have common behaviors? Also, doesn’t the Microsoft Operating System already provide generic interfaces/classes that you could leverage to create toolbars/buttons/modals/etc? Instead of creating all of these from scratch, therefore, imagine that Windows 95 made classes/interfaces such as

  • UIMenuBar — use this to put a menu item on the top
  • UIMenuDropdown — use this to put sub-menu once the menu item is clicked
  • UIButton — generic button that listens for a mouse event with some generic behavior to make the button follow certain design

Not only you would have to study the Operating System and see what classes/interfaces are available and how to build a desktop application on top of the OS layer, but you would also need to better organize the 50 buttons so that the common functionalities don’t need to be written inside each of the class. You would probably need to create some base classes (where each base class extends another class or follows a specific interface) where the children classes can inherit.

Can you imagine how complex your application would get, even from having to figure out how to put the buttons below the menu items? Imagine how many more classes you would need to create for all the modals and other things that appear when each of the buttons is clicked, as well as saving the user’s data and remembering those settings!

If designing the buttons would take this much work, wouldn’t you think building this whole application would be at least 1000 times more complex than designing the classes for these buttons? If the complexity of the project correlates with the number of classes needed (which is often the case), this means say 50,000+ classes one would need to design to make this app work! That’s a lot of classes.

To make this more complex, imagine that your team also needed to support version 1.0, 1.1, 1.2, and so forth, where future versions as the features got added, the number of classes you need to support increase even further!

So imagine you were part of this team. As this is a giant monolithic application, you will probably work on a very small subset of this project. Your senior engineers or architects would follow specific specs and whenever you would write a feature, you would have to think hard whether you should modify an existing class or create an extension of that class to add that feature. You would also need to think about whether your class depends on other factors and if so, try to decouple that, so that as the app evolves, you don’t need to modify the previous class you already wrote. All these complexities made it important that all engineers understood good OOP design patterns and followed them.

Adhering to these OOP design patterns introduced fewer bugs, allowed engineers to support future features while the code still worked for older versions, etc.

The type of development I am mentioning still applies today if you’re building a native desktop or mobile application. To even create the visual appearance of the app, you would have to create multiple classes, read the documentation to understand what interfaces your class needs to follow to use some of the functionalities available within the OS development kit, etc. Understanding when to create abstract classes and how interfaces work is therefore extremely important for these types of applications.

With this complexity in mind, let’s go back to that scenario where say your team of 3 engineers was tasked to create a clickable prototype of Microsoft Word (the desktop version) where all the buttons, modals, and drop downs would need to be created from the right type of class (that adhered to SOLID principles as well as other design patterns).

How long would it take for you to design the front-end experience of the site? Imagine that the application doesn’t need to store any of the data yet (or remember any of the settings) but only needs to have the appearance of proper menus, drop-downs, buttons, etc, but would need to be instantiated from the right Class structure.

Would your team of 3 engineers be able to do it in a day? A few days? A few weeks? A few months?

The right answer, of course, depends on how competent your engineers were, but even if they were very competent, your team would have needed to create hundreds of classes to make this work.

To create hundreds of classes to make this appearance work and where they are organized well to allow the code to easily scale as well as support other future features for the application, you’re probably talking about weeks or maybe a few months of development.

Transporting you back to today

I hope you had fun with this imaginary exercise. Hopefully, you saw how important OOP design patterns were, especially as a lot of desktop applications were very complex, heavy with thousands/tens-of-thousands of features and good OOP design patterns were needed to organize these sets of features into neat buckets. Can you imagine how bad your software could get if your engineers didn’t follow some of these design patterns and either put lots of features into an existing class (breaking some of the previous versions) or also go too far and create for example 20 classes to support just a single feature?

This is why OOP design patterns were important and studied extensively by software engineers. Every single software engineer needed to know about these design patterns before being able to jump into an existing project and make meaningful contributions.

Now, come back to today’s world, particularly to the web development world, and imagine the same scenario but where you are now a software engineer for a web startup that’s making an online version of something like Microsoft Word.

You are given the same screenshot as above:

You are given a task to create a front-end appearance of this application. It doesn’t need to store any of the settings nor send any of the information to the database. It just needs to be enough to click around and see how the application would look visually. In other words, you’re creating a clickable prototype of this application.

If your team had 3 awesome front-end engineers who were Javascript, HTML, CSS stars, how long do you think it would take for you to create this clickable prototype? A few days, A few weeks, or a few months?

My guess is that a team of 3 awesome front-end engineers can create 80–90% of the clickable prototype functionalities in a few days.

Now, how long would it have taken if you were building this as a desktop application? Probably a few months!

This speed comparison is quite remarkable in fact. Because now with the web, you don’t need to create hundreds of OOP classes for the front-end experience of the user, you can do things much much faster. You would still create “classes” in HTML/CSS but these classes are extremely easy to create and manage than OOP classes. It’s interesting that they do have the same name and are used, at least on the front end, to do something very similar yet where one has hundreds of speed efficiencies than the other.

How about the database?

To zoom into this issue even further, note that if you were back in the Windows 95 era, there was no internet…. So your application needed to store everything locally. This meant that now you had to create your own database or create another way of storing data for the desktop application. Either option would require immense amount of work.

Each class would now need functionalities, including getters and setters, to retrieve, update, save settings, and would also need to have a fail-safe mechanism (in case the computer crashed and you lost everything). It also needed to manage all these efficiently and therefore understanding common data structures so that you could store things efficiently and also link information together efficiently was important. Knowing whether you should store that data as a singly linked list, doubly linked list, binary search tree, or hash table, or another form of data structure was critical because you wanted to make sure that the desktop application was running fast and smoothly. If you were using a linked list and you had a circular linked list, it also meant that your program may go on forever (in an infinite loop) and not go anywhere, crashing the entire application. Knowing how to detect therefore issues such as an accidental circular linked list was important.

Back then, memory was scarce and disk space short too. A computer back then only had say less than 100MB of disk space and say 8MB of RAM. A big bulk of this was occupied by the operating system, so whether your desktop application was using 2MB of RAM or 5MB of RAM was critical. Therefore, you needed to have a deep understanding of data structures so that you also understood the memory-usage of your algorithm and whether you could more efficiently do the computation you needed using a more appropriate data structure.

To help with this memory management, these software required developers to indicate whether you’re going to need memory to store integer, floats, x characters, etc, and if so how much of these you needed so that it could allocate this amount of memory in the system for your program to use.

You can see why Big O notation, as well as OOP design patterns, were critical because one wrong choice made by a single engineer could cripple the entire application and cause all of the hard work of other engineers to not shine. This was why during technical interviews these principles were drilled heavily.

Compare that with today when a lot of applications are going into Web. If an engineer said they will create their own database to store the information, you would probably think that engineer is crazy. You’re probably right in that regard.

Now, there are so many great databases available. Now, instead of your program only allowing to use say 2MB of RAM and 20MB of hard drive space, each computer that the user is using has giga-bytes of RAM and gigabytes of hard drive space. Whether your algorithm used 1MB of RAM or 2MB of RAM mattered a lot back in the early days, but now it doesn’t really play that significant of a role.

Also, with the databases readily available that can easily support tera-bytes of space and where information doesn’t need to be stored in the client’s computer but can be stored somewhere else in the cloud, you don’t really need to worry whether your application is storing 30MB of information per user or 40MB. After all, disk space is so cheap now that the cost difference between 30MB and 40MB is not that much at all.

Not only do you not have to worry about whether the application’s data is taking the computer’s valuable RAM and disk space, but you also don’t have to even build this data storage system at all.

With lots of options such as MySQL, Postgres, MariaDB, Cassandra, Redis, MongoDB, and numerous other services offered by each of the major cloud service providers, you don’t even have to worry about scaling up/down the database operations, as these services will do all of that for you, for very little money.

Now, you don’t need hundreds, thousands, or tens-of-thousands of classes to manage your data layer, you need significantly less as you can just send the data to the cloud and have a separate service manage that.

Micro-service movement

So now, I hope you can see my perspective which is that a lot of things have changed from the old world (in the 90’s or in the 80’s). Application development has really moved a lot to the web and there are lots of proven/reliable databases that one could leverage. With these two factors alone, you can imagine how the number of classes a software engineer needed to create would have easily gone down by a factor of 10–100. It’s still important for a web developer to understand good OOP design patterns but as the number of classes needed has gone down dramatically, the importance of really understanding these OOP design patterns has gone down too.

Now, with the application going into the web, there is another interesting phenomenon: “containerization”. Just as Virtual Machines (VM) had a huge impact in transforming how the computers are set up and managed (introducing the whole “cloud” movement), “containerization” is having a huge effect on how applications are built, managed, and deployed. Compared to traditional ways where launching a new server would have taken minutes, launching a new container can be done in seconds. “Containers” also don’t need their own Operating System in it, which saves even more RAM, CPU, and disk space. This makes not only containers very powerful, easy to deploy, but also significantly saves us cost as we don’t need to pay for the RAM/CPU/disk space of the operating system that traditionally needed to be installed/managed in each of the servers that was in the cloud.

With this movement also came cloud-native technologies. By using Git/Github and connecting certain events/hooks to tools such as Jenkin, as well as having test cases to ensure that the applications are working according to its design, developers can more easily integrate their features to staging, and if set up with continuous deployment, see their pull request get integrated automatically to the production server.

This made it even easier to create multiple services. For example, for creating a web version of Microsoft Word, no longer you needed to create that as single software. In fact, you could have broken it down to say 20 different “services” where each service could have been built in a different language and leveraged a different framework.

For example, you could have structured the online version of the Microsoft Word as the following services:

  • Web Interface Service: a service that provides HTML/CSS rendering of what the user sees. Back-end built using programming language X, and front-end leveraging React, Angular, Vue.js, or Ember.
  • Authentication Service: a service that authenticates the user before they are allowed to use the app
  • File Service: a service that allows the user to save the file in different formats; could be built using Python, etc.
  • Export Service: a service that allows the user to export the file as PDF, doc. docx or other types
  • Live Chat Service: a service that provides real-time support for the user using a web socket. Could have been built say in Node.js, Express, and Socket.io.
  • Drawing Service: a service that allows the user to draw in the document, saves its progress, and compresses the vector format, and then storing that information in a separate database.
  • Image Service: a service that allows the user to upload an image or make updates to the image. After the image is uploaded, the service to compress the image to save space.
  • Autocorrect Service: a service that checks for spelling or grammar errors. Returns its results in JSON that can be used by other services to format its results differently for the users.
  • Search service: a service that takes the user’s search topic and provides helpful search results that the user can easily access.
  • and so forth. You can easily imagine dozens of separate services.

Each of these services could be built in a different programming language. This gives you a lot more freedom.

Remember that in the 20+ years ago scenario, you needed someone who knew Java or C++, and everyone in your team needed to know that.

With this being built as a web application and dozens of “services”, the number of classes you would find in each “service” would be significantly less.

How much less? Let’s assume that the web applications and our ability to create the front-end with HTML, CSS, and Javascript reduced the number of classes we needed by a factor of 10. Also imagine that with us being able to save our data directly in the database, the number of classes/features needed for storing data in my application has gone down by a factor of 10. Imagine also that with specialized “services”, the number of classes needed per software/app can easily go down by a factor of 10 (with more micro-services, this can go down even further).

This means that the number of classes you would need for a web application, with the same functionalities can be1000 times less!

Remember back in the desktop version of Windows 95, we estimated that we needed 50,000+ classes? With this as a web application, as an order of magnitude calculation, you could imagine that the number of classes needed for each app would now be around 50.

When you are in a project that had 50,000+ classes, you can imagine why OOP design patterns mattered dramatically. It was critical.

With web application, with HTML/CSS/Javascript, databases, and micro services, the number of classes you will find in a single app, is significantly lower, even could be as low as 50.

Of course, the actual number of classes you may find in each micro-service could be significantly higher than 50 or could be even less, but you will not likely find 50,000+ classes.

Putting things into perspective

I hope this analogy helped you see how the software development space has changed over the years and the requirements for the software engineer has changed too.

Back in the old days, understanding of Data Structures and good OOP design patterns were critical, especially as everything needed to be built as a giant monolithic application and the amount of RAM/CPU available was very small.

In the current world, where a lot of things are going into the web, what’s more critical for the engineers is how they would architect a system, how they would break down the app to different micro services, what database they would choose for each micro-service, how they would reduce the number of queries to the database to optimize speed, and how to create efficient containers and manage these containers to have it go up/down in volume based on traffic needs.

Yes. It’s still important to understand OOP design patterns, particularly how to use an MVC framework, but principles such as SOLID and other OOP design patterns are not as critical for web applications. These principles are still good to know and have benefits in helping engineers better organize their code, but we need to understand how application development has evolved over the years and what this means when we’re training, interviewing, and hiring our engineers.

Other interesting insights

According to Google Trends, search volume for Java and C# has gone down by a whopping 80% and 70%, respectively, from Jan 2004 to May 2020. It’s also interesting that the most in-demand jobs for software engineers deal with Javascript, Python, and other open-source programming languages. In fact, only about 20–30% of jobs require knowledge of Java or C#. The other majority are with Javascript (about 40% of jobs), Python (about 20% of jobs), and where PHP, Ruby take a single-digit percentage(about 2–5% of jobs).

It’s also interesting that a lot of developers are moving away from Java and C# and how Microsoft is striving hard to make C# popular in the development community. When we look at the big picture, Java and C#, which were designed to optimize for scenarios when memory was so scarce, disk-space critical, it made sense that variable types needed to be specified, size of the array determined before-hand, and function return type specified. It also made sense that it needed strong OOP and support things like abstract classes and interfaces. It’s interesting however that the most popular programming languages do not concern them with these but take care of these behind the scene for the developers. Maybe that is one of the reasons why a lot of developers prefer to build things in Python, Ruby, Javascript/Node.js, PHP, etc.

Summary

In this article, I discussed at a very high-level and simplified version, how application development has evolved and how one must have felt if they were a software engineer 20+ years ago working on a giant desktop application vs now working on a web application.

I also did a high-level estimate on the number of classes a desktop application would have needed back 20+ years ago vs now how many classes would be needed for a micro-service in a web application.

With these things in perspective, my conclusion is that OOP design patterns are still good to know and important, but not as critical as it was 20+ years ago.

My wish is that recruiters and hiring managers also realize how the industry has shifted and how testing people on their knowledge of complex OOP design patterns may not be the best approach, especially if the company is working on web-application and not focused on native desktop or mobile application development.

My wish for the students is that they understand why OOP design patterns used to be even more important (say 1000 times more important) back 20+ years ago and to still study them so that when they are asked questions about OOP design patterns, they can still have an intelligent conversation with the hiring managers. However, it’s also important to understand topics such as database optimizations, how to architect different services for the web application, how to identify what language/framework to use, which database to use, how to create containers and manage them, what tools/services are available for you to easily scale up your app and manage different micro-services, how to use git to your advantage, and also how to connect things with other services for continuous integration, delivery, and deployment.

Other topics you may be interested in reading:

  1. HTTP request and response and how web applications work
  2. How to scale a web application to handle an infinite amount of traffic
  3. System Design — how to design a complex web application
  4. What I learned about screening coders: particularly about permanent knowledge vs transient knowledge.

--

--