Let's not make it complicated

Meta Info

Ammerse Values:
A: Mi: M: E: R: S: Ex:
Author: Stijn Dejongh
Published on: 2023-09-16 00:00:00 +0000 UTC

Problem Statement

Programmers tend to over-design simple things to make them more capable, clever, or beautiful than they need to be at that point in time. In doing so, they often end up spending much more time and mental effort on a piece of software than is needed.
Or worse: they build in functionality or capabilities that will never be used.

An image of a bicycle with a robotic rear wheel, looking highly complicated and over-engineered

Intent

  • Speed: Lower throughput time of changes
  • Stability: Less regression or bugs introduced during tasks
  • Lower function point count of changes
  • Reduced cyclic complexity
  • Improved readability

Contextual forces

What is this? These describe factors at play that influence the outcome of the practice. They are not necessarily good or bad, but they are important to be aware of. Some contextual forces are enablers, giving the practice a higher chance of being useful. Others are deterrents, making the practice less useful in your given context.

Enablers

  • the level of refinement of a codebase should make sense for the problem at hand
  • not all code will have a significant lifespan
  • unpredictability of future requirements
  • development time costs a LOT of money
  • readable code is easier to maintain

Deterrents

  • people like to show how clever they are
  • thinking about future problems can help mitigate them

Solution

  • write code that is as well-designed as it needs to be at this point in time.
  • make sure the code you write at this point in time adheres to the basic principles of clean code and design
  • when an idea for a more generic solution comes to mind during your implementation, take note of it and revisit it afterwards
  • iteratively enhance the codebase when it makes sense to do so: when tackling a new code challenge, look for reusable components or structural improvements.

Self-diagnosis

In order to avoid over-complicating your code, ask yourself:

  • “Is this code likely to be changed/expanded in the future?”
  • “Is my design solving an issue that is here NOW, or am I solving an issue that might never happen?”
  • “If this expected issue occurs in the future, can it be easily fixed at that time?”

Examples

How not to do it: Enterprise Quality FizzBuzz

FizzBuzz is a children’s game well known to software developers, as it is commonly used as a training exercise or interview question. The aim is to write a program that counts to a given number. When the current count is divisible by 3, the program should print out Fizz rather than the current count. When the current count is divisible by 5, it should print out Buzz. If the number divisible by both, we expect FizzBuzz. The FizzBuzz exercise aims to gauge basic programming aptitude, such as the use of loops and accumulators.

Usually, one can write a program to do this in a dozen lines of code. As satire to the tendency of programmers in big corporations to overcomplicate their code, people have co-created a version of this program that counts upwards of 10'000 lines of code. This marvel of over-engineering is available online on [github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition](https://github. com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition)

To give you an idea of how far the authors took it: the Enterprise FizzBuzz system uses Spring, and implements nearly all Go4 patterns.
The class below shows a strategy-injection visitor to determine which output formatter to use for a given number.

/**
 * Context for FizzBuzzOutputGeneration
 */
public final class FizzBuzzOutputGenerationContext implements OutputGenerationContext {

	private final DataPrinter printer;
	private final IsEvenlyDivisibleStrategy strategy;

	/**
	 * @param strategy IsEvenlyDivisibleStrategy
	 * @param printer DataPrinter
	 */
	public FizzBuzzOutputGenerationContext(final IsEvenlyDivisibleStrategy strategy,
			final DataPrinter printer) {
		super();
		this.strategy = strategy;
		this.printer = printer;
	}

	/**
	 * @return
	 */
	@Override
	public DataPrinter getPrinter() {
		return this.printer;
	}

	/**
	 * @return
	 */
	@Override
	public IsEvenlyDivisibleStrategy getStrategy() {
		return this.strategy;
	}

}

References