Software design is all about decisions. What language or platform is best suited to solve the problem? What pattern(s) will we adopt? What components need to be built? What layers are required and what will each layer be responsible for?
In a “good” software design, decisions are made efficiently. That means that we make decisions as little as possible. Stated differently, we try not to make the same decision over and over for every situation, but rather to generalize our decisions, forming guiding principles for the remainder of our design.
In order to generalize decisions, and to decide efficiently, we have to have some method for “prioritizing” or “sequencing” our decisions. We need a way to reflect the relative impact of a decision (the cost of changing our mind). We also need a way to reflect dependencies between decisions. I like to use two measures to accomplish this:
- Scope – this is a measure of how much of the application is affected by this decisions. So the scope might have the following values: Component, Feature, Layer, Subsystem, System, beyond. The purpose of this is to understand “potential impact zone” for changing this decision.
- Time Span – This is a measure of how much work or time it will take to unwind a decision once made. It correlates to cost. The higher cost of the decision, the more certainty with which I want to make it. Of course these are estimates, but if it takes me 3 months to build something, and I have to build over because of a bad decision, the time span could be 3 months… It could be more, it could be less. The zone of impact tells me that even things that aren’t directly affected could be affected.
When I look at my design, one of the things that I do is to try to reduce the scope and time span of my decisions through layering, and encapsulation. Basically, designing in ways that minimize the scope of each design decision. Why? Because it reduces the risk of bad decisions! All those design principles that we learned (separation of responsibility, etc) are really isolating design risk in as small an impact zone as possible. For the most part, it reduces the cost of screwing up. Is it the fastest way to produce software? No – In most cases, it increases overall software complexity, in order to simplify and remove risk from the implementation of individual components.
Many design decisions are influenced by the familiarity of the resources tasked with the design. Most people would not design something that they had no familiarity with, unless compelled by requirements. Many design decisions are influenced by the designer’s understanding of non-functional constraints and requirements. Many design decisions are influenced by the availability of tools and resources to construct the software.
If you do not explicitly set guiding principles for a design exercise, these types of influences are hard to mitigate, and can lead to unexpected consequences. So before you “make” decisions, define the decisions, put them in an appropriate sequence, look for themes or principles that emerge, and then promote the principles to the front of the list. Start making decisions.