DEV Community

Cover image for Design too much, build just enough
Munachimso Nwugo
Munachimso Nwugo

Posted on

Design too much, build just enough

"Overengineering is the hallmark of an inexperienced engineer"

This is the narrative pushed in computer science classrooms and work places accross the world. The simplest solution is always the best solution. Complicated solutions are bad. I believed it.

I sank hours into redesigning systems to make them as simple as possible, desperate to show others that I know what I'm doing, despite being a student. Then spent hours redesigning again once I realised I had gone too far. At the end of the day, I would spend more time redesigning the solution I had already made than implementing it.

But conversations with senior engineers during an internship showed me that overengineering can actually be an asset. Maybe it's simply the nature of robotics, but I stopped seeing overengineering as a vice to be squashed and started seeing it as a skill to be embraced. 
The truth is, simple implementations are better than overengineered ones. Overengineered designs are a great tool to make sure the simple implementations are the right ones.

That being said, there are some limitations to this philosophy. 


Ground Rules

Overengineering is dangerous. There's a reason that people are told not to do it. So to make sure we aren't causing more harm than good, we need to abide by strict ground rules. 

Overengineered solutions should never be fully implemented

The idea is to design a wide solution that considers as many edge cases as possible, and then ruthlessly simplify during the implementation phase. It's easier to think through possible failure modes when designing, and easier to see what actually won't be an issue during implementation. Think of overengineering as a mental stress test, not an implementable solution. 

Solution complexity should match problem complexity

There is no reason that a to-do list app should have an architecture that's comparable to a Virtual Machine. Overengineering a complex problem is risk management. Overengineering a simple problem is just procrastination.

Clarity over cleverness

The final solution should be clear. Nobody cares about your clever one-liner or 10x engineer for loop initialisation (yes, I've seen this; no, it made no difference). Your code should be robust and easy for others to understand. Including yourself from the future. Be sure to document the why behind the what. 
Now that we've gotten the bad parts under control, it's time to start looking at the good stuff.


Understanding the problem space
All I know is that I know nothing. 
-Socrates

Overengineering shouldn't be about showing off. It should be about exploration. One person can't know everything about every problem they encounter, and it can often be quite difficult to figure out what you need to know and what you don't. 

Think of it as a counterbalance to naivety. Overengineering forces you to ask questions. Should my simulator use spatial hashing, or will that add too much overhead? Should my wear-levelling algorithm include bit masking, or are we storing too much information? 

There is no need to _force _something into a design within which it doesn't belong, but the bar for exclusion should be high. Knowing exactly why this particular problem _doesn't _need something is still important when it comes to understanding the problem space.

Increasing the surface area of our design also has another hidden benefit.


Failure coverage

"The best thing you can do is figure out everything that can go wrong before you start writing code. It's much better to filter out non-issues than add to fix issues."

A throwaway comment my tech lead made about my wear-levelling solution during one of our meetings. That's all it was, but it stuck with me. 

I had started the project with a way to deal with every single error that I thought of. What happened when a page was worn? Can we get the information back? What if the robot turns off mid-write? What if radiation flips a bit somewhere? Can we tell the difference between that and a normal error? 

This was - of course - way too much to implement at one go. But at the same time, it's one of the biggest strengths of overengineering. Coming up with a way to deal with everything means that when you decide not to cover an edge case, it's because you don't need to. Not because you can't.
When you overengineer during the design phase, you make it easier for your future self. Implementation is difficult enough as-is; having to come up with new parts of the design to cover failure modes you hadn't thought about is just a recipe for decision fatigue.


The benefits don't always have to be pragmatic, though. Honestly, overengineering designs is just fun! Let's call it explorative play. Enjoyment has always been a great motivator, so if you have a tendency to overengineer, have at it! Just make sure you keep it reined in.

Top comments (0)