There are many crucial aspects of designing software systems. An example is: beginning with a clean and well-defined set of components.
One needs to precisely define the functionality of each component, following the single responsibility principle. Then, the relationship between each other has to be clearly defined.
Understanding heuristics is an important part of not only many engineering and other domains, but of life itself.
A heuristic is some rule or way of thinking which is known to be very helpful in solving problems. It may not necessarily be 100% accurate (it actually is not meant to be 100% accurate) but it is nonetheless very helpful in thinking. A rule of thumb is one of the well known heuristics.
It also applies to software. For instance, one of the well known ones is the KISS (“Keep It Simple Stupid”) rule in designing software.
There are also several heuristics when it comes to software testing like even a poor test is better than no test.
Similarly there are many other heuristics which are very useful to learn for software programmers.
An important feature observed in systems built on FOSS like Bitcoin and Linux is that they evolve over time by a process, more or less, of trial and error.
Although there may have been a single architect or a group of architects behind the initiation of these projects, the software design has evolved over time by being touched upon by multiple contributors. In way, these systems have lost their fragility.
There is a saying formulated by Eric Raymond:
Given enough eyeballs, all bugs are shallow
Or more formally:
Given a large enough beta-tester and co-developer base, almost every problem will be characterized quickly and the fix obvious to someone.
Both the above statements point to the fact that problems which otherwise may escape a centralized team of architects and developers may be found and fixed when the code is exposed to multiple eyeballs.
So the design of the software evolves over time in a natural, organic fashion, which makes the system antifragile.
The factory pattern is one of the most commonly used design patterns.
The probable reason why it is so popular is because of it’s simplicity.
It takes care of the complex initialization of the objects it deals with and it does not store any state.
That such an elegant pattern is very popular is not really a wonder.
The principle of having a class or a function do only one thing is a very important and useful heuristic. This is known as the single responsibility principle.
Also, it is important to correctly name the classes and functions. The convention followed is to have class names be nouns and function names be verbs.
Doing these two things ensures that anyone looking at any class and function can quickly understand what it does and can plug in or remove or modify these without any issues.
For example, the below python code, which follows the above principles, is pretty easy to understand and follow along:
self.color = get_color()
One of the assumptions of the modern web and internet based software systems is the availability of a lot of resources like CPUs and memory. However, there are many systems which have limited resources like embedded devices, many of which have real-time performance requirements, which have limited resources like memory and CPU cycles. In these systems, management and optimization of resources becomes crucial. Examples include space rockets and your iPod.
One of the important requirements of any software today is that it has to be scalable. It is one of the major factors to be considered in system design.
For example, as a result of being more scalable, NoSQL databases like MongoDB have become more popular with the web companies than relational databases.
It is often said that “premature optimization is the root of all evil”. The full quote from Donald Knuth is:
We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%
What this indicates is that worrying about optimization in non-critical parts of the code is not worth the effort.
During the early part of the development process, there should be focus on having a good clean design and all code should be written following the clean code principles. At the same time, we need to look for efficient code in every possible place, especially in critical sections.
Bugs are an indication of issues with some part of a software system. It is much cheaper in most cases to keep fixing bugs. However, in case there are some missing features altogether or there are things which cannot be done because of the system architecture does not allow it, the dreaded rewrite may be called for.
A tiger team usually gets assembled to redesign and rewrite the whole system. This is one of the main reasons to have a good detailed design with a long-term perspective from the get-go and to ensure that clean coding practices are followed.
On many an occasion, the number of bugs are an indication of the quality of design of a software system. Many bugs are avoided by writing clean code which is easy to read. On many other occasions, bugs against a particular component could mean that the component is heavily used.