Just Enough Engineering (JEE)

Positively occupy the term ‘JEE’: a simple anti-Over-Engineering Checklist

Let’s start with the Wikipedia definition of Over-Engineering:

Overengineering (or over-engineering) is the designing of a product to be more robust or complicated than necessary for its application…

Sounds familiar? Over-Engineered solutions generally violate two time-proven principles: KISS and YAGNI.

The Agile Manifesto authors have a clear stance on the matter :

Simplicity — the art of maximizing the amount of work not done — is essential.

There are many causes for over-engineering. More often than not it is the result of a noble drive: as engineers we want to build things properly. We want our code to be generic, to be extensible, to perform well, to be robust, to be well documented. We want to apply the fancy patterns we learned reading the Gang of Four. But are these qualities and characteristics necessary for good code?

No. Good code is fundamentally code that works, and code that can be worked with, that is, code that is readable, tested and extendable, so in other words maintainable.

There is always the risk of focusing too much on our own microcosm that we forget to ask ourselves: do we really need this? Does this code solve problems we don’t have? What might the tradeoff be?

Sometimes we think to know better than the user of the application. One instance of such hubris occurred in our team while discussing a feature for the software we are building. The result of the brainstorming session included the automation of a task that would otherwise be done manually by the user, and it would only cost the client approximately ten developer days.

The response of our client was not just interesting, but also rather eye-opening:

“We are only going to use this functionality maybe once per year, and as such the cost of automating it isn’t worth it.”

Was our idea interesting from a technical standpoint? Yes. 
Was our idea worth the client’s money? Absolutely not.

The takeaway : Don’t write code that solves problems you don’t have.

The following (incomplete) list is the outcome of a cross-project discussion on this very topic. Hopefully it can be beneficial when making a design decision.

Considerations when making a design decision:

  • YAGNI: Do we really need this? Do we really need this now? This is especially important from a business perspective: Remember that you only get a return on your time (= money) invested in a feature as soon it is actually used. Meanwhile, maintaining the additional code costs money.
  • KISS: Is the complexity of the solution needed? Is there a way to make it easier/less complex?
  • Can its usefulness be explained to a non-developer? Who needs this? Why is this needed?
  • Non-functional Requirements: how precisely are they defined (if at all)? Is this a case of premature optimization (according to Donald Knuth the root of (almost) all evil in programming)?
  • Do we really need to make this architectural decision right now? Can’t we wait any longer gathering more knowledge? Is it already the last responsible moment?
  • Is the invested time and effort in accordance to what the client needs? Could I justify my decision, were I asked to by the client? Would the client pay for this, were he explicitly asked? Is it worth his money?
  • Refactoring: could this be done in smaller steps? (avoid big bang refactoring and use baby steps (German blog post) instead)
  • Not invented here syndrome: is there an existing product that solves the same issue/problem I am facing?
  • Shiny new framework: sure it is sexy and new, but is it mature? Is the new dependency worth it or does the integration cause more work than it saves?

Do you agree with the above list? If you think of anything that should be added to this checklist or have any feedback, please let me know. Thanks!