Software design [P1] – It all revolves around complexity

Tram Ho


It can be said that software development is one of the most innovative jobs in human history. Indeed, programmers are not bound by real-life limitations like the laws of physics; we can master amazing fantasies and perform actions that are not possible in real life. Programming does not require physical aptitude or coordination such as ballet and rugby, but all that is required for programming is a creative mind and the ability to organize thoughts. If you can visualize a system, you can probably deploy it on a computer program.
This means that the biggest limitation when writing software is its ability to understand the systems we’re working on. As a program is developed and features added, it becomes complex, with insignificant constraints between its components. Over time, complexity accumulates and makes it difficult for programmers to modify the system because all necessary information must be kept in mind. This slows down the software development process and leads to bugs, which in turn slow development and cost more money. In the life cycle of any program an increase in complexity is inevitable. The larger the program, the more participants working with it, the more difficult it is to manage complexity.
Today there are many good tools available to help us deal with the complexity of developing software, however, using the tools alone is not enough. If we want to make it easier to write software, to be able to build powerful systems at less cost, we have to find ways to make software simpler. Complexity will still increase over time, despite our best efforts, simpler design allows us to build stronger and larger systems, before complexity becomes difficult to obtain. control.
All of the above is an introduction to John Ousterhout’s Philosophy of Software Design . This book was released in April 2018, and has become one of the recommended books for every programmer with a lot of appreciation from veteran programmers. The author himself is currently a professor of Computer Science at Stanford University. He is also the author of the TCL programming language and is known for his research on distributed operating systems and storage systems. The book Philosophy of Software Design is evaluated by John as a reflection of his personal experience, and he wants to convey it with the goal of helping the reader to acquire the skills. Engineering reduces the complexity of building software systems.
I have personally read this book and would like to introduce it to everyone in the Vietnamese programming community, especially the students, fresher or junior. I think even senior should not be ignored, because when the author introduces the contents of the book to software engineers at Google, there are still a lot of people to admire. This article is my summary notes during and after reading Philosophy of Software Design by John Ousterhout.

The nature of complexity

We are dealing with complexity, and we must first understand our enemy by answering the following questions:

  • What exactly is “complexity”?
  • How can one say a system is unnecessarily complex?
  • What makes the system complicated?

In this section, we will answer the above question on the level of abstraction, while the specific level will be in the following sections.


Complexity, which is everything related to the architecture of a software system, makes it difficult to understand or change that system.

For example: It is difficult to understand how a piece of code works, or unclear which parts of the code must be fixed when changing a feature.
In other words, a software system that is difficult to understand or to modify is considered complex. Conversely, if it is easy to understand and change, it is considered simple. You can also understand complexity in terms of cost and profitability: in a complex system implementing a small improvement takes a lot of work and time; while in a simple system, larger improvements can be implemented with less cost and effort. People often use the word “complex” to describe large systems with lots of features, but if it is possible to work on them easily, for the purposes of this book and this article, it is not considered complicated.
The reader will see the complexity more clearly than the writer himself. If you write a piece of code and it looks simple to you but other people think it’s complicated, then it’s complicated. In this situation, it is better to find out what makes the code complicated for the reader. A programmer’s job is not only to write code to complete tasks and to do things for himself, but also to write code so that others can work together easily.

Manifestation of complexity

A complex system has the following symptoms:

  • Change amplification : A seemingly simple change requires editing in many different places.
    Example: A website consists of many different pages, all pages have the same background color, and the color code is specified on each page. When you want to change the background color of the website, the developer must change the color code on all existing pages. Instead of this approach, the programmer can create a variable, at a certain central location, containing the value of the background color code and pointing to that variable on each page. Thus, only one change in the other background color variable is that all the background color on all pages will be changed.
  • Cognitive load : The volume of knowledge indicates the amount of information a programmer needs to know to complete a task. The huge amount of knowledge means that a programmer has to spend a lot of time learning the required knowledge, and the risk of creating bugs is enormous if one misses something important.
    Example: C’s memory allocation function returns a pointer, which points to the allocated memory, and the function caller needs to release that memory after it is no longer in use, otherwise a memory leak error will occur. . This increases the amount of knowledge that the programmer needs to use the function.
    The volume of knowledge increases in many ways, such as APIs with multiple methods, global variables, inconsistencies, and binding between modules. System designers sometimes assume that complexity is measured by lines of code, that a shorter implementation is considered simpler, and that only a few lines of code can produce an alternative. change, the change must be simple. However, this view ignores the cost associated with the amount of knowledge. Sometimes, an approach that requires more lines of code is actually simpler, because it reduces the amount of knowledge.
  • Unknown unknowns means unknown, which code needs to be changed to complete a task, or what information a programmer needs. is required to perform the task successfully.
    Example: Go back to website example. When the programmer uses a central variable to specify the background color code, it’s simple to change the background color across entire pages. However, some of the pages use a contrast color for the font, and this font color is clearly indicated on each page. If the background color changes, the font color must also change. Unfortunately the programmer doesn’t realize this, so he only changes the background color. And even when he realizes the problem, he has to search each page to find out which pages use contrasting font colors.

Of the three symptoms above, unknown unknown is the worst, because it is something you need to know, but there is no way to find out what it is or to know if it is the problem or not. You will not find it until bugs appear after the change is made. Large-scale changes are annoying, too, but as long as it’s clear what code needs to be changed, the system goes live once all changes are made. Similarly, a large amount of knowledge increases the cost of changes to the system, but as long as it is clear what information needs to be known, the change will most likely remain accurate.
One of the key goals of a good design is transparency and clarity. This is in contrast to the vast amount of knowledge and unknown unknowns. The following sections cover techniques to make the code more transparent.

The source of complexity

The complexity of a system is caused by two factors:

  • Dependencies : For the purposes of books and articles, a constraint exists when it is not possible to independently understand or modify a piece of code. That is, code A links to code B in some way, and code B must be considered or modified when code A is changed. Constraints are fundamental in software and cannot be completely eliminated. In practice, we intentionally create constraints as part of the software design process. However, one of the purposes of software design is to reduce the number of constraints and make them as simple and clear as possible.
  • Obscurity : Ambiguity occurs when important information is not clearly shown. A simple example is that the name of the variable is too broad and does not convey useful information, such as: time. Ambiguity is also often associated with constraints, when a constraint exists transparently. Inconsistency is also a driver of ambiguity: if a variable name is used for many different purposes, it is difficult for a programmer to determine what the purpose of the variable is.

Complex growth gradually

The complexity is not caused by a fatal error, it accumulates from many small pieces. A constraint, or an ambiguity, does not seem to greatly affect the maintainability of the software system. Complexity arises when hundreds, or even thousands, of constraints and obscurity accumulate over time. In the end, there are so many small problems that every change in the system is affected by them.
This gradual growth nature makes it difficult to manage complexity. The little complexity caused by the current change doesn’t matter, but if all programmers work that way, the complexity will build up quickly. Then, it would be very difficult to remove the complexity of the system, because just changing a constraint or removing an ambiguity would make no significant difference.

Code to work is not enough

One of the key elements of good software design is the mindset you apply when approaching a problem. Many organizations encourage short-range thinking, focusing on features that make them work as fast as possible. However, if you want a good design, you must follow the strategic thinking approach, i.e. invest the time to fix bugs and create good designs.

Tactical programming

Most programmers approach the problem with a short-term mindset. In this approach, the goal is to make something work, like creating a new feature or fixing a bug. If you do short-range programming, you strive to get the task done as quickly as possible and don’t spend a lot of time searching for the best design. Then you accept to add a little complexity, as long as the task is completed quickly. And this is exactly how a system becomes complicated, because as we said in the previous section: complexity grows gradually.

Strategic programming

The first step to becoming a good programmer is to realize that working code isn’t enough . Adding unnecessary complexity to quickly complete the task is unacceptable, since the most important thing is the long-term structure of the system. Therefore, you should not see working code as your primary goal, but rather about creating good design, and of course working. This is tactical programming.
Strategy programming takes an investment of time and thinking. These investments will be a bit slower at first, but will speed you up over the long haul, as shown in the animation below:

How much to invest?

Investing some time and thinking big initially for the whole system will not be effective, because this is the waterfall method. Instead, the ideal design of a system tends to show up over time, as you gain more experience working with that system. Therefore, the best approach is to make many small investments in design, on an ongoing basis. John Ousterhout thinks the right amount is to spend 10-20% of your entire software development time.


In this article, we have learned about the abstractions related to the complexity of software design. In the following sections, we will learn about techniques to reduce system complexity.

Share the news now

Source : Viblo