The article is very much worth the read in its entirety. There are a few key takeaways though I derived from it personally:
I like the definition of over-engineering (quoted from Jeff Sternal) as “Code that solves problems you don’t have.“.
Fagner’s conclusion is that what constitutes over-engineering depends on both context and the people involved.
Over-engineering is usually brought about by unclear requirements and engineers writing code before having truly understood what that code is supposed to do. Because requirements most of the times aren’t as clearly defined as they could be we tend to try and anticipate requirements and potential future use cases. Hence, knowing and questioning business requirements is quintessential when trying to develop quality software.
The 5 Whys technique – originally introduced at automaker Toyota to get to the bottom of manufacturing problems – can be helpful with this. Repeatedly asking why a feature needs to be implemented in a specific way or even why it needs to be implemented at all can be quite revealing and sometimes brings about surprising, simpler solutions we wouldn’t have thought about if we had accepted requirements as-is.
Another way of thinking about this is approaching challenges from a first-principles point of view that questions conventional thinking and focuses on what we actually want to achieve and what needs to be accomplished to get there.
Finally, abstraction is sometimes seen as something of an end to itself instead of just a means to solve a problem, which is why I usually argue for writing disposable instead of reusable code in order to combat complexity for complexity’s sake in software architecture.