I had a conversation at work recently about making sure to have an easy-to-evolve design when exploring a new problem space. Which made me wonder, why does it seem nearly impossible to have an easy-to-evolve design in the first place? In my opinion, this is such a fundamental part of the discipline of Software Engineering and still, every company I worked at gets to a point where feature development grinds to a halt.
The problem
The root cause of this, at least in my opinion, is the result of sub-optimal decisions. Sub-optimal decisions can be made deliberately or accidentally. The focus will mostly be on the accidental type.
Deliberate sub-optimal decisions
Deliberate sub-optimal decisions are introducing technical debt. The conscious decision of not going for the optimal decision can be made for a multitude of reasons, such as reaching deadlines.
Accidental sub-optimal decisions
Although there is much to say about introducing technical debt, I think accidental sub-optimal decisions are much more interesting to think about.
I see two main sources, coming from a lack of:
- Understanding of the problem space
- Understanding future implications of current decisions
The reason I consider this type to be more interesting is because I am 100% convinced there is no way around this. Everyone will inevitably make sub-optimal decisions from time to time.
The solution
What I donโt consider the solution to this, is to start overanalyzing, because that is time-consuming and it wonโt uncover all problems anyway. The only way to truly get that understanding is by experience and simply doing the work.
The only way to truly solve the problem is by embracing the fact that sub-optimal decisions will be made and spending time thinking about how to handle them instead of trying everything to prevent them.
I see three aspects to solving the problem:
- Having the right abstractions in place: Bad abstractions will result in messiness spreading over the code base. The messiness wonโt grow out of control as long as abstractions are well-defined. Abstractions can be interpreted in a very broad sense: REST APIs, Interfaces, domain concepts,โฆ
- Prioritising solving bad abstractions over reaching deadlines and releasing new functionality: Itโs impossible to get it right on the first try, letโs embrace that fact and make sure the impact of sub-optimal decisions remains well-contained.
- Working in small iterations: Small iterations are the easiest way to get a better understanding of the problem space and to see the implications of decisions. Itโs the typical โfail fastโ approach. Failing fast means the impact is still limited and itโs relatively inexpensive to solve.
Final takeaway
Although this article was applied to software engineering, I think this applies to many aspects of life. Whatever you do, it will never be perfect on the first try. You might as well embrace failing and see it as a way to learn.