What Does Good Design Look Like?
Several previous articles have discussed design, whether for products, machine-learning models, code, or architecture.
What we haven't done yet is define what Good Design even means.
Simply put, your system's design is the current arrangement of its components. What, then, makes such an arrangement good?
Properties of a Good Design
The overall system must serve its purpose. If it doesn't, it's designed poorly.
It must be comprehensible. The reason things are arranged the way they are should be evident in light of the system's purpose.
It must be easy to change.
The first two points are about the present. The last point is about the future. You spend much more time changing any relevant piece of software than on its initial creation, maybe because requirements change or enthusiastic customers demand new features.
It's important to stress that this doesn't mean that your system, in its current state, must anticipate all possible future requirements. That leads to bloated, overdesigned systems. Remember YAGNI: You Aren't Gonna Need It.
This requirement means that once a particular future requirement becomes present, the effort to change the system is reasonable: You don't have to rewrite the whole app or touch dozens of files across the codebase just because of a small tweak.
Several good practices fall immediately out of this requirement:
Your system must be easy to test. That way, you can make changes without fretting that you might accidentally break an unrelated part.
The system should be decomposed into units with clear and single responsibilities so that it's immediately clear where a change must be made.
Dependencies between these units should be managed and minimized so that changes to one part of the system don't ripple through the entire system.
Similarly, every "fact" about the system should have one unique source of truth, so you can't accidentally change only one of two sources for the same fact.
While many of these ideas come directly from software design, you can also lift them up to higher levels. The same is true for architecture (e.g., make sure your microservices architecture isn't a distributed ball of spaghetti) and even your company communications (small functional units with minimal dependencies between them for high organizational velocity.)
Happy designing!