Skip to content
Dev Dump

Design Principles - II

Beyond the foundational SOLID principles, software engineers rely on a secondary tier of heuristics and best practices to keep codebases readable, maintainable, and robust.

Also known as the Principle of Least Knowledge. An object should only talk to its immediate neighbors. It should not reach through objects to access other objects.

Avoid “train wrecks” or long chains of method calls (e.g., a.getB().getC().doSomething()).

Law of Demeter Visualizing LoD: Don’t talk to strangers. The Driver talks to the Car; the Car encapsulates the Engine.

A method inside an object should only invoke methods of:

  1. The object itself.
  2. Objects passed in as parameters.
  3. Objects instantiated within the method.
  4. Direct component objects (instance variables).

Violating LoD:

// The Driver reaches through the Car to touch the Engine directly.
// If the Engine API changes, the Driver breaks.
car.getEngine().start();

Following LoD:

// The Car provides an abstract start() method.
// How it starts the engine is encapsulated.
car.start();

“Always leave the code better than you found it.”

Software entropy is real; codebases naturally degrade over time. The Boy Scout Rule is a continuous refactoring mindset.

Boy Scout Rule Refactoring messy code as you touch it prevents technical debt accumulation.

When you open a file to add a feature or fix a bug, take a few extra minutes to:

  • Rename a poorly named variable.
  • Break down a massive function.
  • Add missing encapsulation (changing public fields to private getters/setters).
  • Delete commented-out “zombie” code.

“Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.” — John Woods

Write code that is readable, self-explanatory, and predictable. Code is read 10x more than it is written.

Bad Practice:

// Cryptic variable names and magic numbers
class C {
int x, y;
void p() {
x = 10;
y = 20;
System.out.println(x + y);
}
}

Good Practice:

class Calculator {
private int firstNumber;
private int secondNumber;
// Meaningful names and clear intent
public void printSum() {
System.out.println(firstNumber + secondNumber);
}
}

A component of a system should behave in a way that most users (or developers) will expect it to behave. It should minimize surprises.

Functions should do exactly what their name implies, and nothing more. Hidden side-effects are the root of many bugs.

Violating POLA:

class LightSwitch {
public void toggle() {
// Astonishing: Why does toggling a light turn on a fan?
System.out.println("Fan turned ON");
}
}

Following POLA:

class LightSwitch {
private boolean isOn = false;
public void toggle() {
isOn = !isOn;
System.out.println(isOn ? "Light ON" : "Light OFF");
}
}

Understanding the push-and-pull between Coupling and Cohesion is the hallmark of a senior engineer.

Coupling vs Cohesion Aim for Loose Coupling and High Cohesion.

Coupling refers to the degree of direct knowledge one element has of another.

  • Tight Coupling: Classes depend heavily on concrete implementations. A change in Class A breaks Class B.
  • Loose Coupling: Classes depend on interfaces/abstractions. They can be swapped out easily.