Skip to main content

Why Robustness Matters

Introduction

A new software project typically begins with a burst of rapid progress. Features can be added quickly, and changes are easy to implement. In the early stages, the codebase is small and manageable, making development feel fast and efficient. However, as the project grows in size and scope, the pace of development often slows. Adding new functionality becomes increasingly difficult, and even small changes can require significant effort. The codebase becomes more complex, harder to reason about, and more fragile. This slowdown is a common pattern in software development, and a major reason why many projects struggle or ultimately fail.

This phenomenon is especially pronounced in startups. Early on, the engineering team is often just one or two developers, and the entire system fits easily into their heads. The code is simple, direct, and unencumbered by legacy concerns. Everyone understands everything. But as the company grows and new engineers join the team, the dynamics shift. The codebase expands, and with more contributors, inconsistencies start to appear. The design becomes harder to follow, and institutional knowledge becomes fragmented. As complexity rises, development slows, bugs become more frequent, and onboarding new team members gets harder. Ironically, adding more developers can sometimes make the problem worse, each additional person increases coordination costs and introduces new opportunities for misalignment.

This negative feedback loop, where complexity breeds inefficiency, is the result of unmanaged system growth. When a software system is allowed to evolve without a clear architectural vision or strong design principles, it inevitably becomes more complex than necessary. Organic growth alone does not produce good design. Without intentional structure, even the most promising codebase can devolve into a tangled mess, slowing progress and undermining the team’s ability to deliver value.

If a system needs to stand the test of time, it must be designed with care. This requires a deep understanding of the system's requirements, constraints, and trade-offs. It also requires a willingness to make difficult decisions and to invest in the long-term health of the system. This is not easy, but it is essential for building robust software systems that can evolve over time.

Robustness, Reliability, and Resilience

In addition to good design, a system that is intended to be used and maintained over many years needs to be robust, reliable, and resilient. These traits ensure that the system can continue to deliver value despite changes, failures, and the passage of time.

Robustness means the system can handle unexpected inputs or situations without crashing or producing incorrect results. In a long-lived system, it’s inevitable that someone will misuse an interface, misconfigure a setting, or encounter a scenario the original developers didn’t anticipate. A robust system handles these gracefully, failing safely, logging useful errors, or recovering automatically when possible. This reduces firefighting and keeps teams focused on development instead of damage control.

Reliability is about correctness and consistency over time. A reliable system does what it's supposed to do, even as the environment, usage patterns, or underlying dependencies evolve. This is especially important for systems that form the backbone of critical processes. Financial software, healthcare tools, and infrastructure services are systems where small errors can have costly or dangerous consequences. Without reliability, trust erodes, and so does long-term usability.

Resilience focuses on how well a system can recover from failures. No matter how well you design it, things will go wrong: network outages, hardware failures, bad deployments, or unexpected user behavior. A resilient system can absorb these shocks, restarting components, rerouting traffic, or degrading functionality gracefully without complete breakdown. Resilience doesn't eliminate failure, but it makes failure manageable.

Together, these qualities help software age well. They reduce the maintenance burden, extend the system’s useful lifespan, and give teams confidence to evolve the system without fear. Systems that lack these traits often suffer from brittleness: over time, they become risky to touch, fragile under pressure, and expensive to maintain. In contrast, systems built with robustness, reliability, and resilience in mind remain stable, usable, and valuable long after their original creators have moved on.

Software Architecture and Design

A well designed system is easier to build, easier to maintain, and easier to extend. It is also easier to understand and more fun to work with. A poorly designed system, on the other hand, can be a nightmare to work with. It's frustrating and difficult to add new features, fix bugs, or even understand how the system works.

It's common for new engineers to view the system design to be the responsibility of the architect. However, this is not the case. Everyone on the team is responsible for the design of the system. The architect is responsible for the overall architecture, but everyone else is responsible for the design of their own code, and for ensuring that their new work fits into the existing system. This means that you need to think about design when you are writing code, not just during some initial planning phase.

Getting the design right is important, but it's also important to recognize that design is an iterative process. You will not get it right the first time, and that's okay. The key is to be willing to change your design as you learn more about the system and as the requirements change.

Being able to design a system in a way that will allow it to evolve over time is one of the most important skills you can develop as a software engineer. This is what separates good engineers from great engineers. Great engineers are able to see the big picture and design systems that are flexible and adaptable to change. They can communicate their design decisions clearly and effectively, and they can work with others to build a system that meets the needs of the users.

It's sometimes ok to skip design

If you are building a small system that will only be used by a few people, or if you are building a prototype, then it may be ok to skip design. Sometimes short term speed matters more than long term maintainability. In these cases, you can get away with a quick and dirty design. However, you should still be aware of the design principles and best practices, and you should be willing to change your design if it becomes clear that it is not working.

Designing Unique Systems

Every system you ever build will be unique. It will have its own set of requirements, constraints, and trade-offs. This is what makes software engineering so challenging and so rewarding. You will never be able to find a one-size-fits-all solution to a problem. Instead, you will need to use your knowledge and experience to design a system that meets the specific needs of your users.

This also is what makes assessing the quality of a design so difficult. We never really get timely feedback on a design, which makes it hard to tell when we've made a mistake until it's too late.

There is a common saying that it takes 10,000 hours to become an expert at something. This is a myth. Spending 10,000 hours doing something does not make you an expert. Instead, it takes 10,000 hours of deliberate practice to become an expert. This requires timely feedback and the ability to learn from your mistakes. Without feedback, you will never be able to learn from your mistakes. You need to be able to see the results of your design decisions in order to learn from them.

Unfortunately, there are no hard and fast rules for what makes a good design. Instead, you will need to use your judgment and experience to evaluate the design of a system. Our goal is to help you develop the skills and knowledge you need to make these judgments effectively.

You should be able to:

  • Recognize which design decisions are important and which ones are not
  • Explain the trade-offs involved in different design decisions
  • Recognize design decisions that are likely to cause problems in the future
  • Evaluate the quality of a design

Software Architecture vs. Software Design

The terms "software architecture" and "software design" are often used interchangeably, but they really shouldn't be. This course is focused on software and system design, which is a subset of software architecture. Software architecture is a much broader topic that includes not only the design of the system, but also the design of the development process, the team structures, and the communication structures inside the organization. Software architecture is about making high-level decisions that affect the entire system, while software design is about making low-level decisions that affect individual components.

All software design is architecture, but not all architecture is software design.

Our focus is on designing maintainable and extensible software systems. This might be a single application, a set of applications, or even a system of systems. We will not be focusing on the design of the development process or the team structures. However, these are important topics that you should be aware of as you move forward in your career. The development process and the team structures can have a significant impact on the design of the system, and you should be aware of these factors when making design decisions.

The Importance of Building Software Design Skills

The single biggest difference between a highly productive software engineer and a mediocre one is the ability to design software. Being able to design software is what separates the best engineers from the rest. They can build system more quickly, with fewer bugs, and with less effort. They can also build systems that are easier to maintain and extend. A software engineer skilled in design is much more valuable than one who is just focused on writing code. They can take a rough list of requirements and turn it into a working system. They can also take an existing system and make it better. They can see the big picture and understand how all the pieces fit together.

Building these skills as a junior engineer will help you become a better engineer and a more valuable member of your team. You will be able to take on more responsibility and work on more complex projects, which will help you grow in your career. You will also be able to help your team and your organization build better software systems, which will make you a more valuable member of the team.

These skills will also help you to be more mobile in your career. Every company will have their own tech stack and their own way of doing things. However, the principles of software design are universal. If you can design software well, you will be able to pick up new technologies and new frameworks quickly. You will be able to adapt to new environments and new teams more easily.

The Impact of AI

AI coding tools have only made this problem worse. They can help you write code more quickly, but they do not help you design software. In fact, they can make it harder to design software because they can give you the illusion that you are being productive when you are really just writing code. You need to be able to step back and think about the design of the system, not just the code. You need to be able to see the big picture and understand how all the pieces fit together.

Building good design skills will allow you to get more out of these tools. Having a clear design in mind will help you to use these tools more effectively. You will be able to use them to implement your design, rather than just writing code. You will be able to use them to automate the boring parts of the design process, rather than relying on them to do all the work for you.

Without Good Design Skills

Lacking strong design skills as a software engineer is like being a painter with no understanding of color theory. You might still produce something that technically works, but it will lack depth, coherence, and lasting impact. You can get by, but you won’t create anything truly exceptional.

The same holds true for software development. You can write functional code without a solid grasp of design principles, but your systems will likely be harder to maintain, less adaptable to change, and more prone to breaking under pressure. You'll be building software that works, for now, but not systems that endure, scale, or inspire confidence.

Poor design skills also limit your career. Without them, you're likely to be confined to narrow roles: writing isolated pieces of code, fixing bugs, and relying on others to make the big architectural decisions. You’ll struggle to take on larger responsibilities, contribute meaningfully to team-wide improvements, or lead projects with complexity and scope. In short, your ability to grow and to help others grow will be capped.

If you want to thrive as a software engineer, you must be able to design software. This isn’t optional. It’s what allows you to take an ambiguous set of requirements and turn it into a coherent, maintainable, and robust system. One that others can understand, extend, and rely on for years to come.

Conclusion

Building long-lived software systems is a complex and evolving challenge, one that demands thoughtful design, deliberate planning, and a deep commitment to robustness, reliability, and resilience. To succeed, developers and architects must understand not only the technical intricacies of software development, but also the broader dynamics that shape how systems grow, adapt, and endure over time. By prioritizing these essential qualities from the start, you can build systems that are not only capable of meeting today's requirements, but also resilient enough to thrive amid tomorrow’s uncertainties and change.

Your software design skills are the foundation of your career as a software engineer. Strong design capabilities enable you to build better systems, boost your productivity, and contribute more effectively to your team and organization. As you develop these skills, you'll be prepared to take on greater responsibility, tackle more complex projects, and advance professionally. You'll also play a key role in helping your team deliver higher-quality software—making you an indispensable part of the organization.