- read

The refactoring of the “Very efficient Code” from Dutch DigiD App

Juntao Qiu 3

I found a very interesting code snippet today on a programmer hummer channel on Reddit (Very efficient Code).

And that code snippet kinda goes viral, and you might have already seen it on different platforms as well — people love it.

The original code snippet

Many arguments are going on about the topic. Some believe there are much shorter (maybe also better) versions that can do the same job.

For example, I asked ChatGPT to rewrite it with a shorter version and got this:

Answer from ChatGPT

Is shorter better?

To be honest, my first reaction to the original version is like, Whaaat? I was laughing internally and thought I could refactor it a bit with a map or similar clever techniques in 5 minutes. However, after a coffee, I looked at the code snippets again. I found the intention pretty clear, and ironically the map version requires more time to read.

The shorter version, to an experienced developer, may need several seconds to figure out what is going on. And if the code was written a couple of weeks ago, hmm, it might take a few more minutes to understand.

What is the problem with the original code?

If the application only has one progress bar, like the blue-white dots, we don’t need to make any changes. The current implementation works perfectly well.

However, if we need to maintain two different progress bar generation logic. For example, we need red-white dots in another place, or we need more granularity about the progress — instead of drawing ten dots, we want 20.

With the new requirements, the initial approach is not adaptive enough, and we need to change it a bit.

Though appearing straightforward and uncomplicated, the first version of the code has a drawback of combining the presentation and the business logic. Software is designed to be flexible and adaptable, and this version of the code makes it more difficult to make changes in the future.

By saying it mixes presentation and logic, I mean that if tomorrow we want to show a red dot (instead of the blue one), we have to modify quite a few places.

Apart from that, I would like to address a tiny issue with the logic leak first. You might have noticed that it repeats percentage > x && percentage <= y several times and I would extract a function to make it slightly more readable:

const isPercentageInRange = (number: number, low: number, high: number) =>
number > low && number <= high;

And if I split the percentage check and draw the blue and white dots into two functions, and arrange the result in the new getPercentageRounds, the code will be something like this:

const getBandByPercentage = (percentage: number) => {
if (percentage === 0) return 0;

if (isPercentageInRange(percentage, 0.0, 0.1)) return 1;
if (isPercentageInRange(percentage, 0.1, 0.2)) return 2;
if (isPercentageInRange(percentage, 0.2, 0.3)) return 3;
if (isPercentageInRange(percentage, 0.3, 0.4)) return 4;
if (isPercentageInRange(percentage, 0.4, 0.5)) return 5;
if (isPercentageInRange(percentage, 0.5, 0.6)) return 6;
if (isPercentageInRange(percentage, 0.6, 0.7)) return 7;
if (isPercentageInRange(percentage, 0.7, 0.8)) return 8;
if (isPercentageInRange(percentage, 0.8, 0.9)) return 9;
return 10;
};

const drawProgress = (percentage: number) => {
const band = getBandByPercentage(percentage);
return new Array(10).fill("🔵", 0, band).fill("⚪", band, 10);
};

const getPercentageRounds = (percentage: number) => {
return drawProgress(percentage).join("")
}

Function getBandByPercentage maps the percentage into a band (or a level), while drawProgress draws the dots based on the band.

Make the presentation more flexible.

We can make the progress bar more flexible by extracting the blue and white dots as parameters. Also, to keep the current behaviour, we can use the current value as default:

const drawProgress = (
percentage: number,
done: string = "🔵",
doing: string = "⚪"
) => {
const band = getBandByPercentage(percentage);
return new Array(10).fill(done, 0, band).fill(doing, band, 10);
};

We can then make a progress bar in the command line like this:

const getPercentageRounds = (percentage: number) => {
return drawProgress(0.3, '#', '=').join("")
}

If we want the progress bar wider, we can pass a longer version string to indicating done and doing:

const getPercentageRounds = (percentage: number) => {
return drawProgress(0.3, '##', '==').join("")
}

So we can have different variations of the progress bar, short and long, blue and red.

Different variations of the progress bar

I also published a short video if you prefer the video format.

A short video about the refactoring to this point

Code or Config

After the refactoring, the presentation and logic are split. I’m not a big fan of such a huge block of if-else:

const getBandByPercentage = (percentage: number) => {
if (percentage === 0) return 0;

if (isPercentageInRange(percentage, 0.0, 0.1)) return 1;
if (isPercentageInRange(percentage, 0.1, 0.2)) return 2;
if (isPercentageInRange(percentage, 0.2, 0.3)) return 3;
if (isPercentageInRange(percentage, 0.3, 0.4)) return 4;
if (isPercentageInRange(percentage, 0.4, 0.5)) return 5;
if (isPercentageInRange(percentage, 0.5, 0.6)) return 6;
if (isPercentageInRange(percentage, 0.6, 0.7)) return 7;
if (isPercentageInRange(percentage, 0.7, 0.8)) return 8;
if (isPercentageInRange(percentage, 0.8, 0.9)) return 9;

return 10;
};

As Martin Fowler’s article discussed, splitting the “code” into a configured file is beneficial in some cases.

We can move this percentage to band mapping into a single place like (or even we can move the bandConfig into a JSON file):

const bandConfig: BandConfig[] = [
{
range: [-Infinity, 0.0],
band: 0,
},
{
range: [0.0, 0.1],
band: 1,
},
//...
];

And then getBandByPercentage can be simplified as

const getBandByPercentage = (percentage: number) => {
const config = bandConfig.find((c) => {
const [low, high] = c.range;
return isPercentageInRange(percentage, low, high)
});

return config?.band;
};

As the complicity is moved to the config file, the getBandByPercentage function has only a few remaining lines.

Reuse the logic?

Let me demonstrate one more use case to show what the split can bring. Now imagine we want to use the progress bar in Web UI — a ProgressBar component, for example.

It’s pretty effortless to import the drawProgress function:

const ProgressBar = ({
percentage,
}: {
percentage: number;
done?: string;
doing?: string;
}) => {
return (
<>
{drawProgress(percentage).map((character) => (
<span>{character}</span>
))}
</>
);
};

And on the page, we could see something like:

React version of the loading

We can easily change the dot character in the component and make the progress bar easier to adopt the new UI requirements.

Conclusion

The final result might not be as “efficient” as the original one. Still, with the clear separation of concerns (the presentation and business logic, also the logic and configuration), it can be more responsive to new requirements.

Further reading:

Want to gain hands-on experience with clean code and refactoring techniques? Sign up for my free 7-Day React Clean Code Challenge, where I’ll guide you through building an application using Test-Driven Development and teach you how to refactor it to a professional standard.

If you like the reading, please Sign up for my mailing list. I share Clean Code and Refactoring techniques weekly via blogs, books and videos.