Best way to learn software design

So you are thinking about learning software design. Excellent! I would say that it is one of the best moves you can make in order to become a better software engineer or programmer. But you don’t really know where to start or how to start.  A lot of people think it is pretty easy and straightforward. They hear fancy terms like SOLID, Clean architecture, TDD and what not and so they decide to take a look at what it is all about. Their journey into software design has begun, or so it seems. And while the intent is admirable, it is the same as learning how to sprint without learning to walk first.

You see, a major problem is that a lot of people don’t really understand what they are learning. They start in the middle of what software design is and expect magical results without making the effort to understand the why behind all of it. The problem with learning software design like that is that people don’t fully get what they are learning. They read about principles and best practices, but they don’t fully understand what it is about. Some of these techniques and principles seem very easy, but are actually way more complex. This causes a couple of problems:

    • People sometimes face a wall of confusion and don’t use these techniques and principles at all
    • People misunderstand the techniques and principles and apply them incorrectly
    • People don’t understand the why of the principle or technique and start using it everywhere.
scream when some tests work and but other tests don't

All of the above problems will end with spaghetti code of some kind. To be honest, understanding is the key to becoming really good in software design. Understanding and knowing why some techniques are valuable in a certain situation is what you need. Software design really is a game of trade offs. Not as much as architecture, but still. There is rarely only one good answer.

So what would be the best way to learn software design? 

The best way to learn software design would be to start from the most fundamental level. You have to start with the base concepts and build up from there. The more advanced techniques are going to make so much more sense if you understand the concepts on which these techniques are built upon. What follows is a personal suggestion for a learning path. If you would ask me what the best way is, the learning path I will describe here, would be my answer. 

Before we start, I would like to share these 3 points about software design. I consider them ‘laws’ which are set in stone and who will most likely never change (in my humble opinion):

    • Understand software design and its concepts. You need to know why we have software design, what it’s purpose is. You need to know the why of its concepts and techniques, what their purpose is and what their effect will be on code. 
    • Once you understand the why, the next law you need to know is that everything in software design is context related. There is no concept, principle or technique that will always be true or that always has to be used no matter what. You need to use your understanding of software design and its concepts in order to make the best possible decision in the current given context. This is very important to realise. 
    • You don’t design software just once. It is an ongoing process. With every change you need to make in a codebase, the context might no longer be the same. What was likely to be the best solution back then, is probably not the best solution anymore today. See what has changed and use your understanding of software design to create a better solution today.

The baseline of software design


    • What is software design and why do we have it?
    • Code dependency
    • Cohesion and coupling
    • Change in code
    • Abstraction

When you learn about software design, you should know what it is and why we have it. What is the driving factor for having software design? Knowing this will drive your understanding in general. You should learn about the qualities that we are looking for in a codebase and which ones we can do without.

The next topic to study would be code dependency or connascence. What exactly is it and what does it mean for your code? And very related to that subject is cohesion and coupling. I consider these concepts to be core design concepts. Understanding these will get you a long way already.

Something that is commonly overlooked when learning software design is change. And that is actually a bit ironic, since software design is all about change. To be effective with software design, you need to understand change in code: What can the effect of change be on code? What is likely to change and what is not? And where does change actually come from? Change is the driving factor in the software business, so it is important to understand it well.

The next core concept in software design according to me, would be abstraction. I think we all have an idea of what abstraction is. However, in the world of software design, it has a couple of different meanings. Learn them and understand how stakeholders are using abstraction without realising it. 

Level 1: Isolation as the baseline for writing new code


    • Encapsulation
    • Single Responsibility Principle
    • Interface Segregation Principle
    • GRASP
      • information expert
      • creator
      • controller
      • pure fabrication
    • DRY

After the fundamental concepts of software design, this is the next level of concepts and techniques to learn. They revolve around ‘Isolation of change’ to a codebase and they will help you write decent software from scratch. 

The first thing to understand is the Single Responsibility Principle. This is a very famous principle, part of the SOLID design principles. It is also one of the most misunderstood principles, especially when it comes to applying it. The roots of this principle lay in cohesion and coupling and on a deeper level in abstraction. This principle is important because it will help you with decomposing problems into code. 

The Interface Segregation Principle is another one of the SOLID design principles. It is one of the more straightforward principles but still valuable to know and to understand. In the context of isolation, this principle is a bit related to the Single Responsibility Principle. 

The next thing to look into would be some GRASP principles. GRASP  is a set of principles aiming to help you figure out the different responsibilities for your different units of code. The fundamentals of GRASP are in my opinion cohesion and coupling, which is probably why they are part of the set of principles. There is some common ground between GRASP and the Single Responsibility Principle. Grasp has 9 principles, but in this level, we only look at 4 of them, because those are all about assigning responsibility to units of code. 

Next up is encapsulation. Encapsulation is very important in the world of Object Oriented Programming as it is one of the Object oriented pillars. However, since we can choose whether or not to use this object oriented feature, encapsulation is also a technique. You should learn it properly. 

The last thing to look into is DRY, which stands for Don’t Repeat Yourself. This is also a concept that is often misunderstood. Learn what DRY really is about and why you shouldn’t vigorously eliminate all duplication you see.  

Level 2: changing without changing as a baseline for working with existing code


    • Polymorphism
    • Open/Closed Principle
    • Inheritance 
    • Liskov Substitution Principle
    • Behavioural design patterns
      • Strategy pattern
      • Command pattern 
      • Template method pattern
      • State pattern
      • Observer pattern

This level is all about introducing change without actually changing much existing code. By now you realise that the art of software design is actually dealing with change in code. The concepts and techniques in this level do just that.

The first concept is immediately a very important one. Polymorphism is a true superpower when it comes to making changes without actually changing code. The concepts that power polymorphism are abstraction and also cohesion. Learn why polymorphism is a super power, understand what it is and what it really can do for you. Because polymorphism is a part of grasp, together with GRASP principle called ‘protected variations’, you should look into that as well. It might also be important to gain some knowledge on how polymorphism works in different kinds of languages. For example: it is going to work differently in statically typed languages than in dynamically typed languages. Why is that?

The Open/Closed Principle is the next big topic on this level. It is part of the SOLID principles and it advocates change without actually changing it. This principle is powered by polymorphism and together with polymorphism, it is the base for a big number of behavioural design patterns.

Inheritance is the next thing to study. Inheritance is also one of the pillars of object oriented programming. Inheritance is very powerful and at the same time very misunderstood. It is like dynamite: very potent, but you wouldn’t let your kids play with it. Look at what inheritance is and what it is not. What can it do for you and what is the cost of doing it?

Closely related to inheritance and polymorphism is the Liskov Substitution Principle. Now this principle is not about inheritance, but there is a relation to it. Learn what this principle is all about and how it can help you avoid common mistakes. 

Then there are several behavioural design patterns. These are all powered by polymorphism and once implemented correctly, they can help to apply change without too much code changes. These are very powerful techniques which every software developer should know in my opinion.

Level 3: Dealing with outside dependencies as a baseline for designing the application


    • Dependency Inversion Principle
    • Application architecture types
      • Clean architecture
      • Hexagonal architecture

In the last level, you go one small step beyond plain software design and start looking at designing the application. How can you deal with outside dependencies, how to structure your application code, etc. There aren’t that many topics in this level, but those who are in there are more complex and bigger than the topics in the previous levels. . 

The first topic is the Dependency Inversion Principle. This is the one remaining SOLID principle that we need to discuss. Its root is also polymorphism and in itself it is the core principle behind clean architecture and hexagonal architecture. 

The different types of architecture. They are quite similar to each other, all heavily rely on the Dependency Inversion Principle and they are quite effective. Study these well, learn why you should aim to design your application in this way and then start thinking how you might be able to do this. Also look at other topics, like DTO objects, ports and adapters, etc

This is not a complete overview of all that is software design. But in my opinion, it covers a lot of topics that I have used often, if not daily in my career so far. 

Becoming an expert

So this is the theory. These are in my opinion the core concepts that every software developer should know and be proficient in. But it is only half the work of learning software design. The other half is actually doing it. Learning software design is in many ways like learning how to ride a bike. When you learn how to ride a bike, somebody will tell you that you need to sit on the saddle and grab the handles of the bike to steer. In order to move forward, people will tell you that you need to start pedalling. And that is it. Nobody can tell you how to keep balance. Nobody can really transfer that knowledge. You have to learn that yourself, you have to experience that yourself. The same is true for software design to become a true expert in my opinion.  So don’t be afraid to try out things. Don’t be afraid to make a mistake. As long as you keep an open mind to the result and learn from it.

Just remember those 3 ‘laws’ from the beginning. You will be tempted to go full blown software design in almost every situation. It is normal, everybody sees nails everywhere when they just acquired a new hammer. By that I mean that you will be tempted to anticipate every possible change that might pop up in the future. Don’t. Solve the problem at hand with code that is easy to maintain, don’t try to solve problems that don’t exist yet (and probably never will?). If you did a good job with the concepts and techniques of software design, you have also studied the why and what they are trying to solve. So understand the context of the problem, evaluate what is appropriate and then apply. And even though you might not have the intuition of an expert yet, knowing this will help you make better decisions!

I got 3 more tips that might help in getting better faster. They are a bit related to each other.

The first one is very stereotypical. But here goes anyway: keep an open mind. Learn as much as you can, but be sceptical about what you learn and what people tell you. Yes, this includes my content as well.

Secondly: talk to other people about software design. Ask questions and ask for opinions. Learn from the answers and the feedback, even though it might not make sense, even though it might go against what you have learned so far. When I used to hear an idea or a suggestion so contrary to my own beliefs, I start to wonder how this idea or suggestion could be true. And after figuring that out, my horizons have widened a bit more, my vision on the matter has expanded. Ok in all honesty, way back in time, when I heard a suggestion like that, I thought: this is nonsense. And I didn’t do anything with it. I realise now that this is just a missed opportunity.

And lastly: you can learn from other peoples experiences as well. This has a lot of overlap with the previous point, but there are differences. Ask them about their experiences in certain situations. Or sometimes you will get into discussions with other people. They will have a point or an opinion about a principle or a technique that is going to be wildly different from yours and at some point, they will bring in an experience. They will say: I have done it like this and it also worked. Don’t be closed to that experience. Don’t get on a high horse and disregard their experience as nonsense.

Instead, wonder about the why. Why did they do it that way? What was the context and what were the circumstances? Instead of going further into discussion, ask about the benefits but also the downsides. If you think something is not right or you wonder about a particular part of the experience, question it, discuss it and try to learn from it. The idea is to gather as many experiences as possible, as many insights and visions. They are of course not the same as your own experiences, but they can still help you deepen your own insights, strengthen your own intuïtion (even if it is the smallest amount). And it will help you make better design decisions in the long run.

I hope this was useful. Remember, this is my personal point of view on learning software design. It is based on my own experiences and on what I see on a daily basis in the industry.

I can already help you with the baseline of software design. Whenever you are ready to get a good understanding of the fundamental concepts, you can take my free course on the fundamentals of software design.

If you want to go to the next level, I have material that covers a big part of the first level as well. So whenever you are ready to learn how to use the Single Responsibility Principle, you can subscribe for my course ‘decomposing problems into code’.