How to Learn Software Design and Architecture | The Full-stack Software Design & Architecture Map

Last updated Oct 5th, 2019
Software Design and Architecture is pretty much its own field of study within the realm of computing, like DevOps or UX Design. Here's a map describing the breadth of software design and architecture, from clean code to microkernels.

This topic is taken from Solid Book - The Software Architecture & Design Handbook w/ TypeScript + Node.js. Check it out if you like this post.

Translated by readers to: Japanese (日本語)

You ever think about what it took for some of the world's most skilled developers to learn how to build systems within companies like Uber, YouTube, Facebook, or Github?

It's crazy to me to consider the fact that Facebook was once an empty text file on someone's computer, and now it's this gargantuan company that has dipped it's toes into just about everything, and has personally impacted over 1.59 billion people worldwide.

As a junior, self-taught developer or even intermediate developer, the roadmap to continued growth towards actually learning how to design clean and scalable systems seems kind of daunting.

For a lot of us, our projects die after one or two iterations because the code turns into an unmaintainable mess.

So where do we even start in order to learn how to improve our designs?

The truth is:

Software design and architecture is a huge topic

Understanding how to:

  • Architect a system to serve the needs of its users
  • Write code that's easy to change
  • Write code that's easy to maintain
  • Write code that's easy to test

... is very hard. The breadth of learning required is just so large.

And even though you know how to write code to make things work at least once, the bigger challenge is to figure out how to write code that makes it easy to change in order to keep up with the current requirements.

But again, where to start?...

Anytime I'm faced with a complex problem, I go back to first principles.

First Principles

First principles is the most effective way to break down problems.

It works by deconstructing a problem all the way down to the atomic level where we can't deconstruct it anymore, and then reconstructing a solution from the parts that we're absolutely sure are true.

So let's apply it to software by first stating the goal.

What is the primary goal of software?

The goal of software is to continually produce something that satisfies the needs of its users, while minimizing the effort it takes to do so.

I fought with coming up with the best definition for a long time, and I'm prepared to argue with you about why I think that's accurate.

Software that doesn't serve the needs of its users, simply isn't good software.

And since the needs of our users changes often, it's important to make sure that software was designed in order to be changed.

If software cannot be changed (easily), that makes it bad software, because it prevents us from satisfying the current needs of our users.

We've established that design matters, and it's important to learn how to produce well-designed software, but it can be a long road.

In this article, I'd like to present to you what I believe are the concrete pillars of software design and architecture.

The stack

Before I show you the map, let me show you the stack.

Similar to something like the OSI Model, each layer builds on top of the foundation of the previous one.

The software design and architecture stack shows all of the layers of software design, from the most high-level concepts to the most low-level details.

In the stack, I've included examples to some of the most important concepts at that layer, but not all (because there are way too many).

Now, check out the map. While I think the stack is good to see the bigger picture, the map is a little bit more detailed, and as a result, I think it's more useful.

The map

To avoid running up my bandwidth, I reduced the quality of the map shown on site. If you'd like to get a high-quality png, you can find that up on my GitHub.

Below is the map for software design and architecture.

Stage 1: Clean code

The very first step towards creating long-lasting software is figuring out how to write clean code.

If you ask anyone what they think constitutes clean code, you'll probably get a different answer every time. A lot of times, you'll hear that clean code is code that is easy to understand and change. At the low-level, this manifests in a few design choices like:

  • being consistent
  • preferring meaningful variable, method and class names over writing comments
  • ensuring code is indented and spaced properly
  • ensuring all of the tests can run
  • writing pure functions with no side effects
  • not passing null

These may seem like small things, but think of it like a game of Jenga. In order to keep the structure of our project stable over time, things like indentation, small classes and methods, and meaningful names, pay off a lot in the long run.

If you ask me, this aspect of clean code is about having good coding conventions and following them.

I belive that's only one aspect of writing clean code.

My definitive explanation of clean code consists of:

  • 🧠 Your developer mindset (empathy, craftsmanship, growth mindset, design thinking)
  • ⚙️ Your coding conventions (naming things, refactoring, testing, etc)
  • 🤹🏼 Your skills & knowledge (of patterns, principles, and how to avoid code smells and anti-patterns)

Getting into the right mindset is incredibly important if you want to write clean code. One requirement is that you should care enough to learn about the business you're writing code within. If we don't care about the domain enough to understand it, then how can we be sure we're using good names to represent domain concepts? How can we be sure that we've accurately captured the functional requirements?

If we don't care about the code that we're writing, it's a lot less likely that we're going to implement essential coding conventions, have meaningful discussions, and ask for feedback on our solutions.

We often think that code is solely written to serve the needs of the end user, but we forget the other people we write code for: us, our teammates, and the project's future maintainers. Having an understanding of the principles of design and how human psychology decides what is good and bad design, will help us write better code.

So essentially, the best word that describes this step of your journey? Empathy.

Once we've got that down, learn the tricks of the trade and continue to improve them them over time by improving your knowledge of the essential software development patterns and principles.

Learning resources

  • Clean Code, by Robert C. Martin
  • Refactoring, by Martin Fowler (2nd edition)
  • The Pragmatic Programmer, by Andy Hunt and Dave Thomas
  • The Design of Everyday Things, by Don Norman

The best resource to learn how to write clean code is Uncle Bob's book, "Clean Code".

Stage 2: Programming Paradigms

Now that we're writing readable code that's easy to maintain, it would be a good idea to really understand the 3 major programming paradigms and the way they influence how we write code.

In Uncle Bob's book, "Clean Architecture", he brings attention to the fact that:

  • Object-Oriented Programming is the tool best suited for defining how we cross architectural boundaries with polymorphism and plugins
  • Functional programming is the tool we use to push data to the boundaries of our applications
  • and Structured programming is the tool we use to write algorithms

This implies that effective software uses a hybrid all 3 programming paradigms styles at different times.

While you could take a strictly functional or strictly object-oriented approach to writing code, understanding where each excels will improve the quality of your designs.

If all you have is a hammer, everything seems like a nail.

Learning resources

  • Clean Architecture, by Robert C. Martin
  • Domain Modeling Made Functional, by Scott Wlaschin
  • Concepts of Programming Languages, Robert W. Sebesta (10th edition)

Stage 3: Object-Oriented Programming

It's important to know how each of the paradigms work and how they urge you to structure the code within them, but with respect to architecture, Object-Oriented Programming is the clear tool for the job.

Not only does Object-Oriented programming enable us to create a plugin architecture and build flexibility into our projects; OOP comes with the 4 principles of OOP (encapsulation, inheritance, polymorhism, and abstraction) that help us create rich domain models.

Most developers learning Object-Oriented Programming never get to this part: learning how to create a software implementation of the problem domain, and locating it in the center of a layered web app.

Functional programming can seem like the means to all ends in this scenario, but I'd recommend getting acquainted with model-driven design and Domain-Driven Design to understand the bigger picture on how object-modelers are able to encapsulate an entire business in a zero-dependency domain model.

Why is that a huge deal?

It's huge because if you can create a mental-model of a business, you can create a software implementation of that business.

Learning resources

  • Object-Design Style Guide, by Matthias Noback
  • Clean Architecture, by Robert C. Martin
  • Domain-Driven Design, by Eric Evans

Stage 4: Design Principles

At this point, you're understanding that Object-Oriented Programming is very useful for encapsulating rich domain models and solving the 3rd type of "Hard Software Problems"- Complex Domains.

But OOP can introduce some design challenges.

When should I use composition?

When should I use inheritance?

When should I use an abstract class?

Design principles are really well-established and battle-tested object-oriented best practices that you use as railguards.

Some examples of common design principles you should familiarize yourself with are:

Make sure to come to your own conclusions, though. Don't just follow what someone else says you should do. Make sure that it makes sense to you.

Learning resources

  • Head First Design Patterns, by various authors
  • GoF Design Patterns, by various authors

Stage 5: Design Patterns

Just about every problem in software has been categorized and solved already. We call these patterns: design patterns, actually.

There are 3 categories of design patterns: creational, structural, and behaviour.


Creational patterns are patterns that control how objects are created.

Examples of creational patterns include:

  • The Singleton pattern, for ensuring only a single instance of a class can exist
  • The Abstract Factory pattern, for creating an instance of several families of classes
  • The Prototype pattern, for starting out with an instance that is cloned from an existing one


Structural patterns are patterns that simplify how we define relationships between components.

Examples of structural design patterns include:

  • The Adapter pattern, for creating an interface to enable classes that normally can't work together, to work together.
  • The Bridge pattern, for splitting a class that should actually be one or more, into a set of classes that belong to a hierarchy, enabling the implementations to be developed independently of each other.
  • The Decorator pattern, for adding responsibilities to objects dynamically.


Behavioural patterns are common patterns for facilitating elegant communication between objects.

Examples of behavioural patterns are:

  • The Template pattern, for deferring the exact steps of an algorithm to a subclass.
  • The Mediator pattern, for defining the exact communication channels allowed between classes.
  • The Observer pattern, for enabling classes to subscribe to something of interest, and to be notified when a change occurred.

Design pattern criticisms

Design patterns are great and all, but sometimes they can an additional complexity to our designs. It's important to remember YAGNI and attempt to keep our designs as simple as possible. Only use design patterns when you're really sure you need them. You'll know when you will.

If we know what each of these patterns are, when to use them, and when to not even bother using them, we're in good shape to begin to understand how to architect larger systems.

The reason behind that is because architectural patterns are just design patterns blown-up in scale to the high-level, where design patterns are low-level implementations (closer to classes and functions).

Learning resources

  • Head First Design Patterns, by various authors

Stage 6: Architectural Principles

Now we're at a higher level of thinking beyond the class level.

We now understand that the decisions we make towards organzing and building relationships between components at the high-level and the low-level, will have a significant impact on the maintainability, flexibility, and testability of our project.

Learn the guiding principles that helps you build in the flexibility that your codebase needs in order to be able to react to new features and requirements, with as little effort as possible.

Here's what I'd recommend learning right off the bat:

  • Component design principles: The Stable Abstraction Principle, The Stable Dependency Principle, and The Acyclic Dependency Principle, for how to organize components, their dependencies, when to couple them, and the implications of accidentally creating dependency cycles and relying on unstable components.
  • Policy vs. Detail, for understanding how to separate the rules of your application from the implementation details.
  • Boundaries, and how to identify the subdomains that the features of your application belongs within.

Uncle Bob discovered and originally documented many of these principles, so the best resource to learn about this is again, "Clean Architecture".

Learning resources

  • Clean Architecture, by Robert C. Martin

Stage 7: Architectural Styles

Architecture is about the stuff that matters.

It's about identifying what a system needs in order for it to be successful, and then stacking the odds of success by choosing the architecture that best fits the requirements.

For example, a system that has a lot of business logic complexity would benefit from using a layered architecture to encapsulate that complexity.

A system like Uber needs to be able to handle a lot of real time-events at once and update drivers' locations, so publish-subscribe style architecture might be most effective.

I'll repeat myself here because it's important to note that the 3 categories of architectural styles are similar to the 3 categories of design patterns, because architectural styles are design patterns at the high-level.


Projects with varying levels of components and wide-ranging functionality will either benefit or suffer from adopting a structural architecture.

Here are a few examples:

  • Component-based architectures emphasize separation of concerns between the individual components within a system. Think Google for a sec. Consider how many applications they have within their enterprise (Google Docs, Google Drive, Google Maps, etc). For platforms with lots of functionality, component-based architectures divide the concerns into loosely coupled independent components. This is a horizontal separation.
  • Monolithic means that the application is combined into a single platform or program, deployed altogether. Note: You can have a component-based AND monolithic architecture if you separate your applications properly, yet deploy it all as one piece.
  • Layered architectures separate the concerns vertically by cutting software into infrastructure, application, and domain layers.

Clean Architecture

An example of cutting the concerns of an application vertically by using a layered architecture. Read here for more information on how to do this.


Depending on your project, messaging might be a really important component to the success of the system. For projects like this, message-based architectures build on top of functional programming principles and behavioural design patterns like the observer pattern.

Here are a few examples of message-based architectural styles:

  • Event-Driven architectures view all signficant changes to state as events. For example, within a vinyl-trading app, a offer's state might change from "pending" to "accepted" when both parties agreee on the trade.
  • Publish-subscribe architectures build on top of the Observer design pattern by making it the primary communication method between the system itself, end-users / clients, and others systems and components.


A distributed architecture simply means that the components of the system are deployed separately and operate by communicating over a network protocol. Distributed systems can be very effective for scaling throughput, scaling teams, and delegating (potentially expensive tasks or) responsibility to other components.

A few examples of distributed architectural styles are:

  • Client-server architecture. One of the most common architectures, where we divide the work to be done between the client (presentation) and the server (business logic).
  • Peer-to-peer architectures distribute application-layer tasks between equally-privileged participants, forming a peer-to-peer network.

Learning resources

  • Clean Architecture, by Robert C. Martin
  • Software Architect's Handbook, by Joseph Ingeno

Stage 8: Architectural Patterns

Architectural patterns explain in greater tactical detail how to actually implement one of those architectural styles.

Here are a couple of examples of architectural patterns and the styles that they inherit from:

  • Domain-Driven Design is an approach to software development against really complex problem domains. For DDD to be most successful, we need to implement a layered architecture in order to separate the concerns of a domain model from the infrastrural details that makes the application actually run, like databases, webservers, caches, etc.
  • Model-View Controller is probably the most well-known architectural pattern for developing user interface-based applications. It works by dividing the app into 3 components: model, view, and controller. MVC is incredibly useful when you're first starting out, and it helps you piggyback towards other architectures, but there hit's a point when we realize MVC isn't enough for problems with lots of business logic.
  • Event sourcing is a functional approach where we store only the transactions, and never the state. If we ever need the state, we can apply all the transactions from the beginning of time.

Learning resource

  • Domain-Driven Design, by Eric Evans
  • Implementing Domain-Driven Design, by Vaughn Vernon

Stage 9: Enterprise patterns

Any architectural pattern you choose will introduce a number of constructs and technical jargon to familiarize yourself with and decide on whether it's worth the effort to use or not.

Taking an example that many of us know, in MVC, the view holds all the presentation layer code, the controller is translates commands and queries from the view into requests that are handled by the model and returned by the controller.

Where in the Model (M) do we handle these things?:

  • validation logic
  • invariant rules
  • domain events
  • use cases
  • complex queries
  • and business logic

If we simply use an ORM (object-relational mapper) like Sequelize or TypeORM as the model, all that important stuff to gets left to interpretation on where it should go, and it finds itself in some unspecified layer between (what should be a rich) model and the controller.


Taken from "3.1 - Slim (Logic-less) models" in

If there's something I've learned so far in my journey going beyond MVC, it's that there is a construct for everything.

For each of those things that MVC fails to address, in Domain-Driven Design specifically, there exist several enterprise patterns to solve them. For example:

  • Entities describe models that have an identity.
  • Value Objects are models that have no identity, and can be used in order to encapsulate validation logic.
  • Domain Events are events that signify some relevant business event occurring, and can be subscribed to from other components.

Depending on the architectural style you've chosen, there are going to be a ton of other enterprise patterns for you to learn in order to implement that pattern to it's fullest potential.

Learning resources

These are just a few different learning resources mostly focused on Domain-Driven Design and Enteprise Application Architecture. But this is where there is the most to learn, and where you can dive the deepest in your learning, because it builds ontop of everything we've learned thus far.

  • Patterns of Enterprise Application Architecture, by Martin Fowler
  • Enterprise Integration Patterns, by Gregor Hohpe
  • Domain Driven Design, by Eric Evans
  • Implementing Domain-Driven Design, by Vaughn Vernon

Resources & Conclusion

We talk a lot about Domain-Driven Design on this blog, but there's a lot readers would benefit from knowing first (like layered architectures, oop, model-driven design, design principles and patterns) before we dive deep on building rich domain models with TypeScript.

If you're looking for a one-stop resource, I just prelaunched - The Software Design & Architecture Handbook. I teach readers the things that I think they really need to know at each stage in this map in order to produce good software like we discussed in this article. It's currently on sale until it's fully complete, but I'm also happy to recommend a couple of other excellent resources that I personally used when I was learning software design and architecture.



Liked this? Sing it loud and proud 👨‍🎤.


Commenting has been disabled for now. To ask questions and discuss this post, join the community.

4 years ago

I love the way you put an structure to learning, thank you.

4 years ago

Hello Khalil, nicely structured content and I like OSI model idea. I think Architectural style is commonly a larger concept than just architectural pattern and those should be switched places IMHO.

Sergio Bernal
4 years ago

This is an awesome article. Thanks a lot Khalil you are doing an amazing job with your blog.

4 years ago

Please remove when read.

Suggested replacement:

needs of it's users -> needs of its users

Rajesh Malakar
4 years ago

Best ways to give ideas of thinking process for a software developer.

4 years ago

Really great article, bringing it together like this is helping to connect some dots in my head.

3 years ago

Nicely organized material! Thank you.

Henrique Costa
3 years ago

Man, your job is amazing! Please, continue posting and keep working on your book and course. It's unique on the web. It's been months since I started searched for a guide or course that would completely give me a really practical vision and hands-on experience on software design and this article and your book is the best resource I found to connect the dots.

Every software engineer should read this article and your book. Please keep up the good work and bring more content for us.

Khalil Stemmler
3 years ago

Thanks for saying that, Henrique. I'm working hard on it :')

Ahmed Mudhar
3 years ago

priceless article

3 years ago

"Software that doesn't serve the needs of it's users, simply isn't good software"

That doesn't mean that it won't make money for the insiders when they flip the company. Financing determines what we deliver. What we have learned about "Infinitely augmenting human capability with software" rarely has anything to do with it. It was driving me nuts trying to make sense of what I've seen until I came to that realization. I haven't found it the easiest thing to accept morally.

Thanks for a great site. I can see many new and valuable things for me to learn here.

3 years ago

I am very grateful for so many interesting articles with which I have learned so much to redirect my ideas. Congratulations on your enormous work

2 years ago

Khalil, you're amazing! Keep writing, keep sharing. Looking forward to your course - any ETA?

Vinicius Dias
2 years ago

That's an awesome article. Thanks for sharing.

But DDD isn't an architectural pattern. It's more like a way of thinking. A philosophy, maybe. That's way a layered architecture is suggested. Because DDD itself doesn't enforce an architectural pattern.


Khalil Stemmler
2 years ago

Absolutely correct!

2 years ago

I have searched my whole life for this roadmap


2 years ago

What an incredible didactic! Thank you so much for that! You rocks :)

2 years ago

For many times I found myself lost about what direction got for learn about design and architecture, this article really showed me the way.

Amazing article!

2 years ago

What tools do you use to make these illustrations in the article?

2 years ago

Thank you very much for useful article

Mohamed Lkhl
9 months ago

You're So Amazing, So much Appreciation and respect. such a wonderful Job.

Stay in touch!

About the author

Khalil Stemmler,
Software Essentialist ⚡

I'm Khalil. I turn code-first developers into confident crafters without having to buy, read & digest hundreds of complex programming books. Using Software Essentialism, my philosophy of software design, I coach developers through boredom, impostor syndrome, and a lack of direction to master software design and architecture. Mastery though, is not the end goal. It is merely a step towards your Inward Pull.

View more in Software Design

You may also enjoy...

A few more related articles

How I Write Testable Code | Khalil's Simple Methodology
The single biggest thing that improved the quality of my designs was understanding how dependencies influence my ability to write ...
Organizing App Logic with the Clean Architecture [with Examples]
In this article, you'll learn about the Clean Architecture, why we should separate the concerns of large applications into layers,...
Domain Knowledge & Interpretation of the Single Responsibility Principle | SOLID Node.js + TypeScript
The Single Responsibility Principle specifies that a class or function should only have one reason to change. Admittedly, that's n...
SOLID Principles: The Software Developer's Framework to Robust & Maintainable Code [with Examples]
The SOLID principles are a set of software design principles that teach us how we can structure our functions and classes to be as...

Want to be notified when new content comes out?

Join 15000+ other Software Essentialists learning how to master The Essentials of software design and architecture.

Get updates