— Software Engineering, Productivity, Opinion — 7 min read
Products and services are getting complex exponentially. Expectations are sky-high. In such an environment, an engineer is often tempted to build things "for the future growth", "to scale well", "to be orders of magnitudes better than existing solutions", etc. Though all those intentions are healthy and good, it is often redundant to focus on it, right from the start. The reality is that, it is highly unlikely, that you will be able to predict the future and build your tech stack in a way that it will remain unchanged for years. What is more important is to provide value timely, because nobody needs useless but scalable, perfect solution for the problem of the past
In this post I will try to cover a danger of over-engineering and more importantly, how to recognize and avoid it.
According to Wikipedia, over-engineering is is the act of designing a product to be more robust or have more features than often necessary for its intended use, or for a process to be unnecessarily complex or inefficient. And this definition covers quite a lot of this concept. However, I would amend it a little bit so that we add "at the moment" to the end of the former sentence. Look, technically speaking there is nothing wrong in building robust and versatile things. Moreover, you should always aim for it. However, one has also to accept the reality and map your engineering efforts to your reality.
Let's imagine you are a small company, building a brand-new collaboration platform. Are you sure your very first version should support 1M users and daily ingestion of petabytes of data? It is highly unlikely. Of course, you should design your system with scalability in mind, but to be honest, this is not so hard to do if you stick to basic building blocks in your tech stack, and not the latest framework that promises to fix all your problems at the same time. See this post to learn more about this idea.
In my opinion, over-engineering is a type of the problem, which is easy to spot. Let's imagine, you received a pull request, adding a new and objectively trivial UI widget, or a basic CRUD API implementation. Now, I would do the following tests:
For those who are reading my blog for a while, this is not a surprise - fight for simplicity and aim for logic density. If you follow this simple lead, you will never be wrong. In the worst case, your solution will be naive, but it is better to be naive, than pretend being extremely smart. Also, it is very natural to go from simple to hard and extremely difficult to go from hard to simple. I can give couple of actionable advices here:
Truth to be told, over-engineering could also be a result of a bad product management. Like if your roadmap fluctuates constantly and you have to pivot your product focus every month, it is hard to build something sustainable. Sometimes this is just a reality of a new startup. In this situation this is fine to accept and communicate, that whatever you are building is a prototype, which will have to be rebuilt as soon focus will be more clear. At least it will set right expectations and will not diverge your attention from the main thing: finding something that works for your (potential) customers.
Another situation to be in is when you are building something truly innovative in terms of technology. Imagine, you are building a Netflix in its early days. So, you are literally first to do it. And you know, it will be tough. In such case, it is also probably fine to accept that your first product version will be messy. But you will learn a lot, so that you can improve and simplify your solution later. It is important to keep it in mind and be ok with an inevitable fact that you will likely have to delete most of your initial code later.
So, to sum it up, sometimes the product itself pushes us towards over-engineering - it is particularly important to identify such situation, accept it and decide how to move on and re-iterate later.
Frankly, this is hard. Let's say you just joined the company with an existing product. Couple of weeks later you see all the sins: legacy, code bloated with various framework(s) patterns, over-hyped team integrating every new hot library into the project every month, loads of dashboards so that it is hard to find something when you need it. You are all alone in the sea of sharks. It will be hard to convince people that some things should be done much simpler and that legacy code is eventually not required anymore.
My advice here would be: observe for a month or two, depending on project complexity. Could be you do not understand some things and it is just that the problem, project is solving, is tough. I saw people coming into teams and starting to advise how to build things from day 2 at the company. And believe me, this is tough situation to be in, if you are wrong. And if you will discover that project is indeed over-engineered, please make sure you convinced people that you are worth listening to. You can do so, by doing an excellent work even in the existing context. As soon you gained some trust, you can start looking around for trivial things that you can improve to make project code cleaner. Start simple. Believe me, if you will make a "Simplify whatever" pull request, removing 100 lines of code, without removing any features, it will be difficult to reject it. Simple is always better. Such approach will build up some reputation for you, so that you will be perceived as a pragmatic engineer, aiming for code excellence. Over time, people will start trusting your choices and you will be able to change more complex parts of the project. Just be prepared that it will take some time.
Hope you enjoyed the reading! Want to discuss the content? Feel free to reach out to me on social media!