Software Engineering Principles (By Analogy)

Welcome to the debut of the Low Level Bunker! Your input matters, so don't hold back—tell me what you think. In this post, we will talk take a look of some of the most important software engineering principles. 

My Website



1. Single Responsibility Principle: Each class should have only one responsibility.

For the Single Responsibility Principle, think of a Swiss Army knife. Each tool serves a specific purpose, like cutting, screwing, or opening bottles.


2. Open-Closed Principle: Classes should be open for extension, but not modification.

Like a fridge, it's open for putting food in it but not for modifying it: you don't change its door or paint it all over. Open for extension, not for modification. 

3. Liskov Substitution: Subtypes must be replaceable for their supertypes. 

In a pizza, you can replace any slice with another type of pizza slice and still have a complete pizza. Similarly, in code, a subclass should be able to replace its parent class without affecting the behavior of the program, just like swapping pizza slices without changing the overall meal.



4. Interface Segregation Principle: Split interfaces into specific ones, ensuring clients only access relevant methods.

Picture a supermarket with clearly labeled sections: one for sugar cookies, another for round cookies, and yet another for star-shaped cookies. Each section caters to specific preferences, ensuring that customers only access the types of cookies they're interested in, in a quick way.

5. Dependency Inversion: High-level modules should not depend on low-level modules. Both should depend on abstractions.

Consider a corporate hierarchy, where junior employees shouldn't rely directly on senior executives for guidance. Instead, both juniors and seniors should depend on the CEO, the ultimate authority and we say the CEO is abstract because it's rarely involved in day-to-day operations. 


  • Law of Demeter: Limit coupling between objects.

Imagine you're at a coffee shop. Instead of asking the cashier to pass a note to the barista detailing your specific coffee order, you simply tell the cashier directly. This way, there's less back-and-forth communication and everyone can focus on their respective tasks efficiently.

  • Abstraction: Hide implementation details.
Abstraction is like driving a car without knowing how the engine works. You focus on using the steering wheel, pedals, and gears, abstracting away the complexities of combustion, pistons, and gears under the hood. Similarly, in software, users interact with simplified interfaces, unaware of the details running behind the scenes, making all more intuitive.


  • Consistency: Maintain uniformity across systems.

Picture a symphony orchestra where each musician plays their part in perfect harmony, following the conductor's lead. Just as consistency ensures uniformity in music, it maintains coherence and standardization across systems.

  • Encapsulation: Restrict access to certain components.

Imagine a safe deposit box: it securely holds valuable items, and only authorized individuals with the right key can access its contents.


  • Principle of least astonishment: Keep things intuitive and obvious.

Picture walking up to an elevator button expecting it to be on the right, as usual. But today, it's on the left! Avoid surprises by ensuring your code behaves and looks as expected, following the Principle of Least Astonishment.

A Sandwich.
Ensure that your code doesn't resemble this apple! 

  • Refactoring: Restructure code without changing its behavior.

Think of refactoring as renovating a house. You update its structure, improve its layout, and fix any flaws, all without changing it's purpose : being a shelter.

This house got refactored!
  • Optimization: Avoid optimizing prematurely, avoid pessimizing prematurely.

Imagine yourself building a house. You don't start by painting the walls before laying the foundation or by throwing random materials together. Premature optimization is like painting those walls too soon—it ties you to a pattern that may not be suitable in the long run. Pessimizing prematurely leads to a structure that's unmaintainable, ultimately forcing you to start over.

  • Incremental Development: Build and refine software incrementally.

Think of it like building a LEGO set, piece by piece. You start with the foundation, adding bricks one by one until the structure takes shape. Similarly, in software development, you gradually add and refine features, building upon each other to create a robust system. Because if you don't: it's harder to identify issues, you end up with a monolithic inflexible unmaintainable codebase and you can't change it ...

MVP is incremental development: at each stage we deliver something reasonable, despite not being the actual product expected by the user (truck), the important thing is that we get feedback at every stage, while in the above we don't, and the consumer is dissatisfied in all the stages except the last; instead in MVP the user get not exactly what he asked but something that satisfy the same need : movement.

  • Polymorphism: Use a single interface to represent multiple types.
It's like a universal remote control that can operate various devices. Just as the remote has different buttons for different functions, polymorphism allows a single interface to handle multiple types of objects, making it versatile and efficient.



We take something abstract and implement it where we need it. Highly reusable!

  • Single Source of Truth: Store data in a clear place.
Imagine a library where all books are cataloged in one index. This index serves as the single source of truth for finding any book in the library. Similarly, in software, storing data in one central location ensures consistency and reliability.

  • Pareto Principle: 80% of outcomes results from 20% of causes
Picture a garden where 20% of the plants consume 80% of the water and nutrients. Similarly, in software development, 20% of the features often account for 80% of the usage. The Pareto Principle reminds us to focus on the vital few rather than the trivial many.


  • Separation of Concerns: Modularize + Encapsulate. Divide the program into logical units.
Think of it as organizing a toolbox. You wouldn't store your hammers with your screwdrivers; each tool has its designated compartment. Likewise, separating concerns in software keeps different aspects of functionality isolated, making it easier to maintain and update.


Questionnaire!


1. Single Responsibility Principle:

Which analogy best represents the Single Responsibility Principle?





Open-Closed Principle:

What does the Open-Closed Principle emphasize?





Liskov Substitution:

Which analogy illustrates the Liskov Substitution Principle?





Interface Segregation Principle:

How should interfaces be organized according to the Interface Segregation Principle?





Dependency Inversion:

What does the Dependency Inversion Principle recommend?




Miscellaneous Tips:

  • Use version control, always.
  • Understandability comes first.
  • Write const by default. Make it an habit.
  • Prefer composition than inheritance, to avoid coupling.



Related Links:


As we conclude this post in the Low Level Bunker, feel free to stay by the cozy fire. The bunker's always open for your return. Happy coding, and until next time!

Comments

Popular Posts