Software can never be fully decoupled!
Decoupled with intentional coupling is the goal.

Just a guy who loves to write code and watch anime.
Search for a command to run...
Decoupled with intentional coupling is the goal.

Just a guy who loves to write code and watch anime.
Nice thought process
Qualities that make outstanding builders.

Bipedal vs quadrupedal: how many legs This is about the number of legs the creature walks on. Bipedal. Two legs. Humans, ostriches, T-rex, kangaroos, most fantasy humanoids. Quadrupedal. Four legs. Do

Intro You hear "lerp" and "smoothstep" everywhere in game dev. They sound like math jargon. They're not. Both are small tools that do the same job: smoothly move from one value to another. The problem

What is Kinematics Kinematics is the math field. It's the study of how things move without worrying about forces (which would be dynamics). FK and IK are the two branches: forward kinematics and inver

Intro Textures are usually the biggest cost in a 3D scene. Memory, bandwidth, and load time all get eaten by them. Resizing them is the obvious lever. There's more. This post is about the less obvious

We often talk about how good software is decoupled.
In this post, I want to dive into coupling, the dangers of coupled software and why software can never be 100% decoupled.
Things being coupled means they are connected in a pair or pairs.
In software engineering, parts of the system being coupled means they depend on each other.
So if part X changes, it will affect how part Y behaves.

In software engineering, when parts of the system are coupled, they affect each other.
The stronger the coupling, the harder to maintain and change the software.
If we change something in one place, we may have to change it in another part of the system. It's a dangerous cycle. Because of one change we might have to change 4 other places due to strong coupling. Complexity that could've been avoided.
This is why we want to aim for low coupling.
We aim for low coupling, not NO coupling.
No coupling doesn't make sense. Then our software wouldn't work. The pieces of the system will be connected. They form the entire software. Coupling will always exist.
Low coupling is the goal!
It's important to note separation of concerns and coupling are related but different things. We can separate e.g. a piece that processes orders and a piece that stores the orders. They might be tightly coupled despite being separated.
That's fine.
Let's take a look at a classic JavaScript example where we manage the shopping cart:
// Global state for cart (demonstration purposes)
let cart = [];
// Function to add item to the cart
function addItemToCart(item) {
cart.push(item);
updateCartDisplay();
saveCartToLocalStorage();
}
// Function to update the cart display
function updateCartDisplay() {
const cartElement = document.getElementById('cart');
cartElement.innerHTML = ''; // Clear cart element
cart.forEach(item => {
const itemElement = document.createElement('div');
itemElement.innerText = `${item.name}: $${item.price}`;
cartElement.appendChild(itemElement);
});
}
// Function to save the cart to localStorage
function saveCartToLocalStorage() {
localStorage.setItem('cart', JSON.stringify(cart));
}
UI changes: If we decide to change the way the cart is displayed, we would need to rewrite the updateCartDisplay function. Since addItemToCart directly calls updateCartDisplay, it would also need to be updated.
Storage changes: If we move from using localStorage to a different method of storage, like a backend API, the saveCartToLocalStorage function will need to change, which means addItemToCart will also need updating.
addItemToCart is doing too much.
We can use dependency inject (aka inversion of control) to loose the coupling.
// Global state for cart (demonstration purposes)
let cart = [];
// Function to add item to cart (manipulates data only)
function addItemToCart(item, callback) {
cart.push(item);
if (callback) {
callback(cart);
}
}
// Independent function to update the cart display
function updateCartDisplay(cart) {
const cartElement = document.getElementById('cart');
cartElement.innerHTML = ''; // Clear cart element
cart.forEach(item => {
const itemElement = document.createElement('div');
itemElement.innerText = `${item.name}: $${item.price}`;
cartElement.appendChild(itemElement);
});
}
// Independent function to save the cart to localStorage
function saveCartToLocalStorage(cart) {
localStorage.setItem('cart', JSON.stringify(cart));
}
// Usage
addItemToCart({ name: 'Apple', price: 0.99 }, cart => {
updateCartDisplay(cart);
saveCartToLocalStorage(cart);
});
I could go on for days talking about how to reduce coupling.
It's extremely important to be pragmatic.
Don't be dogmatic and take everything I mention below religiously:
Single Responsibility principle
Interface Segregation Principle
Dependency Inversion Principle
DRY
YAGNI
Avoid global state
Favor composition over inheritance
Dependency injection
Law of Demeter
Design patterns
If things are unrelated, pull them apart.
Microservices
Let's build loosely coupled software.
I had a lot of fun writing this haha :D