A Philosophy of Software Design
Rate it:
Read between April 17 - June 20, 2020
13%
Flag icon
This is how systems become complicated. As discussed in the previous chapter, complexity is incremental.
Brad Balderson
Indeed it is; 'Do not unnecessarily complicate unless there is evidence to suggest the complication is necessary.'
Farhan Khalid liked this
13%
Flag icon
The first step towards becoming a good software designer is to realize that working code isn’t enough.
Brad Balderson
We need code that will last, is easily understandable, and thereby testable and maintainable.
13%
Flag icon
Your primary goal must be to produce a great design, which also happens to work. This is strategic programming.
Brad Balderson
I.e. The simplest AND the best solution.
13%
Flag icon
Strategic programming requires an investment mindset. Rather than taking the fastest path to finish your current project, you must invest time to improve the design of the system. These investments will slow you down a bit in the short term, but they will speed you up in the long term, as illustrated in Figure 3.1.
Brad Balderson
Yes, i needed this. The key thing is having practical strategies on how to design good code with minimal complexities as quickly as possible.
14%
Flag icon
Other investments will be reactive. No matter how much you invest up front, there will inevitably be mistakes in your design decisions. Over time, these mistakes will become obvious. When you discover a design problem, don’t just ignore it or patch around it; take a little extra time to fix it. If you program strategically, you will continually make small improvements to the system design.
Brad Balderson
This will drastically decrease chance of project failure since the mental load of understanding the different components will decrease, thus decreasing anxiety and freeing mental resources for other important parts of the project.
14%
Flag icon
If your code base is a wreck, word will get out, and this will make it harder for you to recruit. As a result, you are likely to end up with mediocre engineers. This will increase your future costs and probably cause the system structure to degrade even more.
Brad Balderson
This is important to keep in mind; as Peter Thiel said, a company fouled up at the outset is destined for failure.
15%
Flag icon
Good design doesn’t come for free. It has to be something you invest in continually, so that small problems don’t accumulate into big ones. Fortunately, good design eventually pays for itself, and sooner than you might think.
Brad Balderson
Invest in good design with 10-20% of your time. This is not only true of designing code; but also in designing one's life. It might free more hours in the day now to not read, or continously educate yourself, but in the long term it is better to invest this time because you will be happier and have a lower mental load sinced you have reduced the complexity in your life; allowing you the freedom to really be productive. Invest the time to design and cultivate your image of the future, and in so doing cultivating your future. This needs to be done frequently to ensure the actions today lead to the result you desire.
15%
Flag icon
In modular design, a software system is decomposed into a collection of modules that are relatively independent. Modules can take many forms, such as classes, subsystems, or services. In an ideal world, each module would be completely independent of the others: a developer could work in any of the modules without knowing anything about any of the other modules. In this world, the complexity of a system would be the complexity of its worst module.
Brad Balderson
In designing our life and future, we also need to think in terms of modules. We need to think in terms of different levels of our future. What do we do today, to ensire that tomorrow, so that next week, so that next year, so that ten years from now. If we think about the above, we will be over-whelmed with existential dread at the complexity of the endeavour. We should therefore only dedicate certain times to think at certain levels. Each week we should think about what needs to be done that week. Each day what needs to be done on the day. Each month, each year.
16%
Flag icon
First, a simple interface minimizes the complexity that a module imposes on the rest of the system. Second, if a module is modified in a way that does not change its interface, then no other module will be affected by the modification.
Brad Balderson
Interfaces act as an important separation between the code implementation and how that code would be invoked by other systems. The interface should be as simple as possible, only requiring the minimum input. The goal is two-fold: 1) Hide compexity from the module user, and 2) Dettach the implementation as maximally as plossible from the interface to reduce the chance changes in the implementation will need to result in changes in the interface.
19%
Flag icon
Flag: Shallow Module A shallow module is one whose interface is complicated relative to the functionality it provides. Shallow modules don’t help much in the battle against complexity, because the benefit they provide (not having to learn about how they work internally) is negated by the cost of learning and using their interfaces. Small modules tend to be shallow.
Brad Balderson
This is very good to know, need to aim for hiding complexity, layering it hierarchically so that don't need to deal with different layers of complexity.
21%
Flag icon
Information leakage is one of the most important red flags in software design. One of the best skills you can learn as a software designer is a high level of sensitivity to information leakage. If you encounter information leakage between classes, ask yourself “How can I reorganize these classes so that this particular piece of knowledge only affects a single class?” If the affected classes are relatively small and closely tied to the leaked information, it may make sense to merge them into a single class. Another possible approach is to pull the information out of all of the affected classes ...more
Brad Balderson
Very good thing to keep in mind to reduce information complexity.
21%
Flag icon
Information leakage is one of the most important red flags in software design. One of the best skills you can learn as a software designer is a high level of sensitivity to information leakage. If you encounter information leakage between classes, ask yourself “How can I reorganize these classes so that this particular piece of knowledge only affects a single class?” If the affected classes are relatively small and closely tied to the leaked information, it may make sense to merge them into a single class. Another possible approach is to pull the information out of all of the affected classes ...more
Brad Balderson
Yes, i really needed this. Good way is to have an object which gets manipulated, as opposed to carrying around different components of that object.
21%
Flag icon
Red Flag: Information Leakage Information leakage occurs when the same knowledge is used in multiple places, such as two different classes that both understand the format of a particular type of file.
21%
Flag icon
temporal decomposition, the structure of a system corresponds to the time order in which operations will occur.
21%
Flag icon
temporal decomposition often results in information leakage.
22%
Flag icon
When designing modules, focus on the knowledge that’s needed to perform each task, not the order in which tasks occur.
Brad Balderson
When focussing on that information, try to bundle the information in a way which hides the complexity, i.e. may need to implement a data structure. Other thing to consider is that if there is a temporal order of the classess, these should not be implemented by parsing objects between methods. Instead, you should write another class which sequentially calls the methods in their required order. This increases the overall depth of the applications, and reduces the shallowness.
22%
Flag icon
Red Flag: Temporal Decomposition In temporal decomposition, execution order is reflected in the code structure: operations that happen at different times are in different methods or classes. If the same knowledge is used at different points in execution, it gets encoded in multiple places, resulting in information leakage.
22%
Flag icon
information hiding can often be improved by making a class slightly larger.
27%
Flag icon
Questions to ask yourself
27%
Flag icon
What is the simplest interface that will cover all my current needs?
Brad Balderson
What method can i implement that will provide required functionality in the most general purpose way. I.e. Something which can be used in alot of different scenarios, and so might be useful for future projects.
28%
Flag icon
In how many situations will this method be used?
28%
Flag icon
Is this API easy to use for my current needs?
28%
Flag icon
General-purpose interfaces have many advantages over special-purpose ones. They tend to be simpler, with fewer methods that are deeper. They also provide a cleaner separation between classes, whereas special-purpose interfaces tend to leak information between classes. Making your modules somewhat general-purpose is one of the best ways to reduce overall system complexity.
Brad Balderson
This is what i need to do; i need to recognise some of the useful inherent functionality that might be represented by groupings of variables, or functions, and use these to create more separate, stand alone classes that are general purpose and so could be very useful for future research.
30%
Flag icon
The solution is to refactor the classes so that each class has a distinct and coherent set of responsibilities.
30%
Flag icon
One example where it’s useful for a method to call another method with the same signature is a dispatcher. A dispatcher is a method that uses its arguments to select one of several other methods to invoke; then it passes most or all of its arguments to the chosen method.
Brad Balderson
This was my intention for the evolvable program; have a dispatcher thay might randomly select one of several possible implimentations, return the result and the name of the implimentation used. Could use this with evolutionary algorithm to select best combination of implimentations, with some penalty for methods which require more parameters. Would be useful to adapt the approach depending on the input data with this kind of approach.
32%
Flag icon
Pass-through variables add complexity because they force all of the intermediate methods to be aware of their existence, even though the methods have no use for the variables.
32%
Flag icon
store the information in a global variable,
Brad Balderson
One approach to overcome pass-through variables.
32%
Flag icon
A context stores all of the application’s global state (anything that would otherwise be a pass-through variable or global variable).
Brad Balderson
Context object; stores all of the parameters required by the chain of methods; allowing flexibility to add new variables to the context without needing to update the input needed by each method. Must be only one context object that is meant to represent the whole system; that way methods can access the whole system the whole time.
33%
Flag icon
Contexts may also create thread-safety issues; the best way to avoid problems is for variables in a context to be immutable. Unfortunately, I haven’t found a better solution than contexts.
Brad Balderson
Good to know; this problem i have had significant problems with.
33%
Flag icon
it is more important for a module to have a simple interface than a simple implementation.
Brad Balderson
Keras is great example of this.
37%
Flag icon
If the same piece of code (or code that is almost the same) appears over and over again, that’s a red flag that you haven’t found the right abstractions.
Brad Balderson
Abstractions meaning good way to encode the information that maximises the number and independence of the components.
37%
Flag icon
The way to separate special-purpose code from general-purpose code is to pull the special-purpose code upwards, into the higher layers, leaving the lower layers general-purpose.
Brad Balderson
Biology works this way too.
37%
Flag icon
Red Flag: Special-General Mixture This red flag occurs when a general-purpose mechanism also contains code specialized for a particular use of that mechanism.
Brad Balderson
In other words, you have not properly separated general functions from specific applications of those functions in a certain class. For instance, a general purpose mechanism might be to subset a matrix. It dosn't make sense to impliment this with a class that stores matrices, instead one might need to alter this mechanism to change different kinds of subsetting. If you instead separate this matrix subset mechanism from the class, could use it in other classes as well, and would simplify the original class.
41%
Flag icon
Long methods aren’t always bad. For example, suppose a method contains five 20-line blocks of code that are executed in order. If the blocks are relatively independent, then the method can be read and understood one block at a time; there’s not much benefit in moving each of the blocks into a separate method. If the blocks have complex interactions, it’s even more important to keep them together so readers can see all of the code at once; if each block is in a separate method, readers will have to flip back and forth between these spread-out methods in order to understand how they work ...more
Brad Balderson
Great point, suggests that in alot of cases grouping our code within the method with relevant comments is more useful than actually separating that code into helper methods.
41%
Flag icon
When designing methods, the most important goal is to provide clean and simple abstractions. Each method should do one thing and do it completely. The method should have a clean and simple interface, so that users don’t need to have much information in their heads in order to use it correctly. The method should be deep: its interface should be much simpler than its implementation.
46%
Flag icon
Overall, the best way to reduce bugs is to make software simpler.
Brad Balderson
Beautifully said and absolutely true.
48%
Flag icon
a system processes a series of requests, it’s useful to define an exception that aborts the current request, cleans up the system’s state, and continues with the next request. The exception is caught in a single place near the top of the system’s request-handling loop.
Brad Balderson
Good thing to keep in mind.
50%
Flag icon
With exceptions, as with many other areas in software design, you must determine what is important and what is not important. Things that are not important should be hidden, and the more of them the better. But when something is important, it must be exposed.
Brad Balderson
Absolute fundamental principle of life.
50%
Flag icon
Designing software is hard, so it’s unlikely that your first thoughts about how to structure a module or system will produce the best design.
Brad Balderson
Echoing george polya here (in How to Solve It) about when to start working on the problem; and even when you do find solution, you should review it and attempt to simplify the steps you needed to take to solve it.
51%
Flag icon
rather than picking the first idea that comes to mind, consider several possibilities.
Brad Balderson
Absolutely the same as Polya suggests; one of the key idea about considering multiple possibilities is that it informs you on the different implementation variations you might try. This helps inform abstraction at the interface level so that can easily swap out different implementations when they become redundant.
51%
Flag icon
After you have roughed out the designs for the alternatives, make a list of the pros and cons of each one.
Brad Balderson
Helps inform which one you should select.
51%
Flag icon
Designing it twice does not need to take a lot of extra time. For a smaller module such as a class, you may not need more than an hour or two to consider alternatives. This is a small amount of time compared to the days or weeks you will spend implementing the class. The initial design experiments will probably result in a significantly better design, which will more than pay for the time spent designing it twice.
Brad Balderson
This is absolutely and completely true; glad to hear someone concur this; not that it's neccesary, i know it to be true.
51%
Flag icon
I have noticed that the design-it-twice principle is sometimes hard for really smart people to embrace. When they are growing up, smart people discover that their first quick idea about any problem is sufficient for a good grade; there is no need to consider a second or third possibility. This makes it easy to develop bad work habits. However, as these people get older, they get promoted into environments with harder and harder problems. Eventually, everyone reaches a point where your first ideas are no longer good enough; if you want to get really great results, you have to consider a second ...more
Brad Balderson
Hard problems are humbling.
52%
Flag icon
It isn’t that you aren’t smart; it’s that the problems are really hard! Furthermore, that’s a good thing: it’s much more fun to work on a difficult problem where you have to think carefully, rather than an easy problem where you don’t have to think at all.
Brad Balderson
I seriously love and completely agree with this attitude.
52%
Flag icon
Inadequate documentation creates a huge and unnecessary drag on software development.
Brad Balderson
Same with everything that one does; documenting helps with repeatability for any procedure which will be performed at a later date.
52%
Flag icon
I hope these chapters will convince you of three things: good comments can make a big difference in the overall quality of software; it isn’t hard to write good comments; and (this may be hard to believe) writing comments can actually be fun.
Brad Balderson
I agree especially with the last point!
52%
Flag icon
The informal aspects of an interface, such as a high-level description of what each method does or the meaning of its result, can only be described in comments. There are many other examples of things that can’t be described in the code, such as the rationale for a particular design decision, or the conditions under which it makes sense to call a particular method.
Brad Balderson
These are great points; an additional point would be that writing comments makes one think more deeply about the philosophy of the method and what it is trying to achieve.
53%
Flag icon
If users must read the code of a method in order to use it, then there is no abstraction: all of the complexity of the method is exposed.
Brad Balderson
I.e. Writing the comments helps to clearly specify the method abstraction; the idea behind what the method is trying to do independent of the implimentation.
53%
Flag icon
If you want to use abstractions to hide complexity, comments are essential.
54%
Flag icon
The overall idea behind comments is to capture information that was in the mind of the designer but couldn’t be represented in the code.
Brad Balderson
This is a neat philosophical idea.
« Prev 1 3