Coupled code

First lets start by showing how de-coupled code looks like: (how you should write it)

function bodyMassIndex(weightKilo, heightMeter) {
	// Bodyweight in kilograms divided by height in meters squared
	var bmi = weightKilo / (heightMeter * heightMeter);
	
	/*
	Normal weight 	19–24,9
	Overweight 	25–29,9
	Obesity level I 	30–34,9
	Obesity level II 	35–39,9
	Obesity level III 	≥ 40
	*/
	
	var status = "Underweight";
	
	if(bmi < 19) status = "Underweight";
	else if(bmi < 25) status = "Normal weight";
	else if(bmi < 35) status = "Obesity level I";
	else if(bmi < 40) status = "Obesity level II";
	else if(bmi >= 40) status = "Obesity level III";
	
	return status;
}

var rl = require('readline').createInterface({input: process.stdin, output: process.stdout});

rl.question("Enter weight (kg): ", function enterWight(weightKilo) {
	rl.question("Enter height (meter): ", function enterHeight(heightMeter) {
		console.log("Your weight according to BMI: " + bodyMassIndex(weightKilo, heightMeter));
		rl.close();
	});
});

This is very simple, but even simple code tend to get coupled.
Here's how a coupled version could look like:

function bodyMassIndex(weightKilo, heightMeter) {
	return whoStatus(bmi(weightKilo, heightMeter));
}

function bmi(weightKilo, heightMeter) {
	return = weightKilo / (heightMeter * heightMeter);
}
	
function whoStatus(bmi) {
	var status = "Underweight";
	if(bmi < 19) status = "Underweight";
	else if(bmi < 25) status = "Normal weight";
	else if(bmi < 35) status = "Obesity level I";
	else if(bmi < 40) status = "Obesity level II";
	else if(bmi >= 40) status = "Obesity level III";
	
	return status;
}

Not that bad right? right? So why is it bad ? We now have the illusion of the code being more simple because we have split one function into three small and simpler functions. Yet the big one we had before is just as easy to test and manage as these three smaller ones. By splitting it up we added complexity by making couplings.

So what is coupled code ?

Coupled code is basically code that is coupled with other code, like bodyMassIndex in the example above is coupled with the bmi function and the whoStatus function.
Also note that the bmi function and the whoStatus function would be kinda useless without each other, so even if the bmi function doesn't call the whoStatus function they are still coupled.
If you where to reuse the bodyMassIndex function in another program, you would also have to include the bmi and whoStatus functions.

Why is coupled code bad ?

Note that the example above is small and easy to manage, but bigger code-bases usually have hundreds of coupled code pieces, and in order to reuse just one tiny piece you need to copy all those hundred code pieces. And changing something sometimes also requires you to change stuff in hundreds of other places.
This is usually one of the reasons why productivity slows down when the code-base grows bigger.

Modules

Modules work best when the abstraction is not application specific and thus can be reused in other applications.
It shouldn't need the rest of your application to function. It's ok for a module to take arguments though.

I've been managing code bases with global scope and include files within include files many levels deep for 15 years before finally seeing the light with NodeJS and the CommonJS modules. Bad habits are hard to get rid of so people are still using these old anti-patterns in NodeJS and JavaScript, disguising them as modules.

Coupled modules

Now lets create the illusion of simplicity by coupling using modules.

import * from "who-enterprise";

export function bodyMassIndex(weightKilo, heightMeter) {
	return whoStatus(bmi(weightKilo, heightMeter));
}

I've deliberately added even more complexity by using the import * anti pattern. Say you want to change the body weight definitions in whoStatus, now hope your IDE is clever enough to find it by "go to definition" or search for whoStatus using "find in files". This only takes a few seconds, but it all adds up if your code base is large.

CommonJS modules are better because you can contain the couplings in the lexical/function scope. It's also much harder to use the "include file"-pattern with CommonJS modules.

export function bodyMassIndex(weightKilo, heightMeter) {
	var bmi = require("bmi")(weightKilo, heightMeter);
	var whoStatus = require("whoStatus");
	
	return whoStatus(bmi);
}
... more code here that dont use whoStatus or bmi ...

But there are still couplings ... Lets say I still want to change the body weight definitions in whoStatus. I open the code containing whoStatus or "who-enterprise" and find this:

var calc = require("who-enterprise-calc");
... ten more module imports here ...

export {whoStatus: calc.whoDefinitions};

Then I find "who-enterprise-calc" and it looks just the same ...
And when I finally find the actual whoStatus function, it looks like this:

export function whoStatus(bmi, language, neededItToDoOneMoreThing) {
	return indexGive(bmi).translations(GLOBAL_LANG).moreThing(neededItToDoOneMoreThing);
}

... But they have made the whole who-enterprise into a packaged module !.. I can just npm install who-enterprise and treat it like a black box. But next week after running npm update there are 10,000 files touched! I'm not going to review that so security bugs sneak in ...

So yeh, don't couple your code!

Coupled code can often be avoided by using the plugin pattern


Written by Nov 16, 2016.


Follow me via RSS:   RSS https://zäta.com/rss_en.xml (copy to feed-reader)
or Github:   Github https://github.com/Z3TA