Creating simple solutions is one of the most underrated design principles in software design. Complicated problems seem to make it even more reasonable to create not only complex but complicated solutions. Building simple solutions is much more challenging than expected.
While looking into Python, I came across The Zen of Python and the first two lines stuck with me.
Simple is better than complex.
Complex is better than complicated.
For me, simple software is boring to read. Everything is predictable, obvious, and clear. There are no surprises and no magic between the lines. And most importantly, simple software is easy to modify.
Although identifying simple software is pretty straightforward, why is writing it so hard to achieve? And which steps can we take to make simple and boring software?
Understanding the problem
The best products solve a particular problem in the most elegant way. The key to simple solutions is to understand the root cause of the problem. Dig deep to understand why and for whom you are building the software, try to become an expert in the problem domain before writing a single line of code, and remember that the obvious might not be the best way to solve the problem. Your job is not to write code. Your job is to find a simple solution for a tricky problem. The most elegant solution is to solve a problem by writing no code at all.
Finding the right abstractions
Start with the user in mind, get an idea of how they will use the solution, and identify the core functionality. Determine critical pieces early in the design process and investigate possible solutions. Avoid getting distracted by wishful thinking and gut feelings. Build your solution step by step, and don't let sloppiness introduce leaky abstractions and coupling of independent parts. On the other hand, avoid overengineering by introducing sophisticated architecture patterns which do not bring any value. Focus on decent design for the small pieces is an excellent foundation for a great design in the big picture. Therefore, take an evolutionary approach and enhance the architecture as you go. A common principle of extreme programming is YAGNI (You Aren't Gonna Need I), which helps you focus on the important things. A smart abstraction and architecture of today is an investment for the future.
Choosing the right tools
Aim for boring languages, frameworks, libraries, and existing software with a proven track record. Building on existing technology with a vibrant community makes potential bugs googleable and Stackoverflowable. Developing your software with inclusivity in mind, in the sense that you include frameworks and languages with large and supportive communities, is a non-functional that is oftentimes skipped. If you need to create libraries and frameworks, be thorough when making your API design, documentation, and SDKs. Your colleagues will be thankful, and you will be happier as you avoid drowning in support requests. As your solution is growing, it might come the time to replace existing technologies. Changing along the way is fine, but avoid premature optimization.
Talking about the solution
Even the best engineers working in isolation come up with bad ideas. Gather feedback before you start coding. Listen to what your peers and customers think of your solution. It almost always gives you new insights, even if you don't incorporate them into your solution. A good approach is to write down the future API specification, customer-facing FAQ, or documentation for your product. These are approaches taken by Amazon, Stripe, and other companies. It is easy to share and gives potential customers a better idea of using the potential solution. If people have a hard time understanding your approach, it's a strong indicator to rethink the solution. It's not that the people are dumb; it is more likely that your solution has flaws.
Don't try to please people - the biggest issue with software is that you make nearly everything. It is not automatically a good solution because it is technically possible. Every decision comes with a trade-off. Build features that benefit the majority of your customers. Keep in mind to build what people need and not what people want. Be strict and challenge the requirements before you start building. Keep building solutions that solve the problems of today and tomorrow but not the ones which might appear next year.
To build simple solutions, keep the focus on the relevant pieces. Deprecate unused features and the related code.
Now we have a rough idea of how to approach problems and build simple solutions. But when should you start building complex solutions?
At no time, always aim for simple solutions as complexity will automatically come when the product becomes bigger; it is unavoidable. Building simple solutions is challenging, but it is worth it because simple will always outperform complex and complicated on in the long run.
Thanks to draft reader Brian