## DEV Community π©βπ»π¨βπ» is a community of 963,503 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Dale L. Jefferson

Posted on • Updated on • Originally published at dalejefferson.com

# The Most Convoluted JavaScript Fizz Buzz Solution

Finding the correct level of decomposition is sometimes challenging. In this article, I will take decomposition to the extreme creating a wildly convoluted Fizz Buzz solution and share my feelings on the correct level of decomposition.

## Fizz Buzz Test

Write a program that prints the numbers from 1 to 100. But for multiples of three print βFizzβ instead of the number and for the multiples of five print βBuzzβ. For numbers which are multiples of both three and five print βFizzBuzzβ.

``````const modulo = n => a => a % n;
const equals = a => b => a === b;
const compose = (...a) => x => a.reduceRight((p, fn) => fn(p), x);
const equalsZero = equals(0);
const isDivisibleBy = a =>
compose(
equalsZero,
modulo(a)
);
const isDivisibleBy3 = isDivisibleBy(3);
const isDivisibleBy5 = isDivisibleBy(5);
const isDivisibleBy15 = isDivisibleBy(15);
const ifElse = (condition, onTrue, onFalse) => n => {
return condition(n) ? onTrue(n) : onFalse(n);
};
const identity = x => x;
const always = value => () => value;

const fizzBuzz = ifElse(
isDivisibleBy15,
always("FizzBuzz"),
ifElse(
isDivisibleBy3,
always("Fizz"),
ifElse(isDivisibleBy5, always("Buzz"), identity)
)
);

for (let index = 1; index <= 100; index++) {
console.log(fizzBuzz(index)); // 1, 2, Fizz, 4, Buzz
}
``````

I would hope it is obvious to the reader that I have taken decomposition too far, the question is when should I have stopped and how would I know to stop.

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. - Antoine de Saint-Exupery

Let's take this step by step.

## Starting Point

``````const fizzBuzz = n => {
if (n % 15 === 0) {
return "FizzBuzz";
} else if (n % 3 === 0) {
return "Fizz";
} else if (n % 5 === 0) {
return "Buzz";
} else {
return n;
}
};
``````

Pretty concise code, we could make it a little easier to read and remove duplicated logic by extracting the divisible check into its own function.

## First Refactor

``````const isDivisibleBy = (a, n) => n % a === 0;

const fizzBuzz = n => {
if (isDivisibleBy(15, n)) {
return "FizzBuzz";
} else if (isDivisibleBy(3, n)) {
return "Fizz";
} else if (isDivisibleBy(5, n)) {
return "Buzz";
} else {
return n;
}
};
``````

Now we have the reusable isDivisibleBy function, but should we break it down further? Is isDivisibleBy doing two things finding the remainder and a comparison?

## Second Refactor

Extract till you Drop. - Uncle Bob

``````const equalsZero = n => n === 0;
const modulo = (a, n) => n % a;
const isDivisibleBy = (a, n) => equalsZero(modulo(a, n));

const fizzBuzz = n => {
if (isDivisibleBy(15, n)) {
return "FizzBuzz";
} else if (isDivisibleBy(3, n)) {
return "Fizz";
} else if (isDivisibleBy(5, n)) {
return "Buzz";
} else {
return n;
}
};
``````

Should we stop now or push on? How many of you have "dropped"?

## I know it when I see it

I shall not today attempt further to define the kinds of material I understand to be embraced within that shorthand description ["hard-core pornography"], and perhaps I could never succeed in intelligibly doing so. But I know it when I see it, and the motion picture involved in this case is not that. - Justice Potter Stewart

Personally, when I get to the point that the refactored code becomes more complex and harder to read than the original I stop or revert the refactor. I believe this happened in this simplified example at the second refactor. Like many things in software development, this is a personal preference, all versions of this code work as well as all the others, actually, I would imagine the starting point code is the fastest.

Let me know what you think in the comments, how far do you take decomposition?