A Philosophy of Software Design
Rate it:
Started reading October 29, 2020
5%
Flag icon
David Parnas’ classic paper “On the Criteria to be used in Decomposing Systems into Modules” appeared in 1971, but the state of the art in software design has not progressed much beyond that paper in the ensuing 45 years.
6%
Flag icon
I have talked with several people I consider to be great programmers, but most of them had difficulty articulating specific techniques that give them their advantage.
6%
Flag icon
However, there is quite a bit of scientific evidence that outstanding performance in many fields is related more to high-quality practice than innate ability (see, for example, Talent is Overrated by Geoff Colvin).
7%
Flag icon
All programming requires is a creative mind and the ability to organize your thoughts.
8%
Flag icon
This book is about how to use complexity to guide the design of software throughout its lifetime.
8%
Flag icon
there isn’t a simple recipe that will guarantee great software designs. Instead, I will present a collection of higher-level concepts that border on the philosophical, such as “classes should be deep” or “define errors out of existence.” These concepts may not immediately identify the best design, but you can use them to compare design alternatives and guide your exploration of the design space.
Lalit Kale
“Exploration of design space” is most important aspect of software design.
8%
Flag icon
The best way to use this book is in conjunction with code reviews.
9%
Flag icon
Complexity is anything related to the structure of a software system that makes it hard to understand and modify the system.
10%
Flag icon
Isolating complexity in a place where it will never be seen is almost as good as eliminating the complexity entirely. Complexity is more apparent to readers than writers. If you write a piece of code and it seems simple to you, but other people think
10%
Flag icon
it is complex,
10%
Flag icon
then it is c...
This highlight has been truncated due to consecutive passage length restrictions.
10%
Flag icon
Your job as a developer is not just to create code that you can work with easily, but to create code that others can also work with easily.
10%
Flag icon
Cognitive load: The second symptom of complexity is cognitive load, which refers to how much a developer needs to know in order to complete a task.
10%
Flag icon
Sometimes an approach that requires more lines of code is actually simpler, because it reduces cognitive load.
11%
Flag icon
Unknown unknowns: The third symptom of complexity is that it is not obvious which pieces of code must be modified to complete a task, or what
11%
Flag icon
information a developer must have to carry out the task successfully.
11%
Flag icon
Complexity is caused by two things: dependencies and obscurity.
12%
Flag icon
If a system has a clean and obvious design, then it will need less documentation. The need for extensive documentation is often a red flag that the design isn’t quite right. The best way to reduce obscurity is by simplifying the system design.
13%
Flag icon
The tactical tornado is a prolific programmer who pumps out code far faster than others but works in a totally tactical fashion. When it comes to implementing a quick feature, nobody gets it done faster than the tactical tornado.
13%
Flag icon
In some organizations, management treats tactical tornadoes as heroes. However, tactical
13%
Flag icon
tornadoes leave behind a wake of ...
This highlight has been truncated due to consecutive passage length restrictions.
Lalit Kale
Tactical Tornado programmer is anti pattern for engineering. Finishing faster does not mean they have designed system well.
13%
Flag icon
The first step towards becoming a good software designer is to realize that working code isn’t enough.
13%
Flag icon
Your primary goal must be to produce a great design, which also happens to work. This is strategic programming.
13%
Flag icon
Some of the investments will be proactive. For example, it’s worth taking a little extra time to find a simple design for each new class; rather than implementing the first idea that comes to mind, try a couple of alternative designs and pick the cleanest one. Try to imagine a few ways in which the system might need to be changed in the future and make sure that will be easy with your design. Writing good documentation is another example of a proactive investment.
14%
Flag icon
complexity accumulates more rapidly under the tactical approach, which reduces productivity.
15%
Flag icon
In order to manage dependencies, we think of each module in two parts: an interface and an implementation. The interface consists of everything that a developer working in a different module must know in order to use the given module.
16%
Flag icon
A developer should not need to understand the implementations of modules other than the one he or she is working in.
16%
Flag icon
Much of the discussion about modular design in this book focuses on designing classes, but the techniques and concepts apply to other kinds of modules as well.
16%
Flag icon
The interface to a module contains two kinds of information: formal and informal.
16%
Flag icon
In general, if a developer needs to know a particular piece of information in order to use a module, then that information is part of the module’s interface.
16%
Flag icon
The informal aspects of an interface can only be described using comments, and the programming language cannot ensure that the description is complete or accurate1. For most interfaces the informal aspects are larger and more complex than the formal aspects.
16%
Flag icon
An abstraction is a simplified view of an entity, which omits unimportant details.
17%
Flag icon
An abstraction that omits important details is a false abstraction:
17%
Flag icon
The mechanism for file I/O provided by the Unix operating system and its descendants, such as Linux, is a beautiful example of a deep interface. There are only five basic system calls for I/O, with simple signatures:
18%
Flag icon
On the other hand, a shallow module is one whose interface is relatively complex in comparison to the functionality that it provides. For example, a class that implements linked lists is shallow.
18%
Flag icon
Shallow classes are sometimes unavoidable, but they don’t provide help much in managing complexity.
19%
Flag icon
Unfortunately, the value of deep classes is not widely appreciated today. The conventional wisdom in programming is that classes should be small, not deep. Students are often taught that the most important thing in class design is to break up larger classes into smaller ones. The same advice is often given about methods: “Any method longer than N lines should be divided into multiple methods” (N can be as low as 10). This approach results in large numbers of shallow classes and methods, which add to overall system complexity.
19%
Flag icon
Classitis may result in classes that are individually simple, but it increases the complexity of the overall system. Small classes don’t contribute much functionality, so there have to be a lot of them, each with its own interface. These interfaces accumulate to create tremendous complexity at the system level.
Lalit Kale
This applies to Microservices or service oriented architectures as well.
19%
Flag icon
Providing choice is good, but interfaces should be designed to make the common case as simple as possible
20%
Flag icon
The basic idea is that each module should encapsulate a few pieces of knowledge, which represent design decisions.
21%
Flag icon
Information leakage occurs when a design decision is reflected in multiple modules.
21%
Flag icon
Suppose two classes both have knowledge of a particular file format (perhaps one class reads files in that format and the other class writes them).
21%
Flag icon
Even if neither class exposes that information in its interface, they both depend on the file format: if the format changes, both classes will need to be modified. Back-door leakage like this is more pernicious than leakage through an interface, because it isn’t obvious.
22%
Flag icon
When designing modules, focus on the knowledge that’s needed to perform each task, not the order in which tasks occur.
22%
Flag icon
information hiding can often be improved by making a class slightly larger.
24%
Flag icon
Buffering in file I/O is so universally desirable that noone should ever have to ask explicitly for it, or even be aware of its existence; the I/O classes should do the right thing and provide it automatically. The best features are the ones you get without even knowing they exist.
25%
Flag icon
As a software designer, your goal should be to minimize the amount of information needed outside a module; for example, if a module can automatically adjust its configuration, that is better than exposing configuration parameters. But, it’s important to recognize which information is needed outside a module and make sure it is exposed.
25%
Flag icon
The phrase “somewhat general-purpose” means that the module’s functionality should reflect your current needs, but its interface should not. Instead, the interface should be general enough to support multiple uses. The interface should be easy to use for today’s needs without being tied specifically to them.
25%
Flag icon
The word “somewhat” is important: don’t get carried away and build something so general-purpose that it is difficult to use for your current needs.
27%
Flag icon
One of the most important elements of software design is determining who needs to know what, and when.
« Prev 1