Evolving with SOLID Principles: A Journey from Junior to Senior Engineer
From a junior to a senior software engineer, my journey highlights the transformative impact of evolving with SOLID principles.
As I reflect on my journey from a wide-eyed junior software engineer to a seasoned senior developer, one key factor stands out: my evolving relationship with SOLID principles. These principles have been my guiding light, transforming not only the code I write but also the way I approach software design. In this post, I want to share my personal experience with SOLID principles through the lens of my career progression, using examples in C# to illustrate their impact.
The Junior Engineer: A World of Chaos
When I first stepped into the realm of software development, I was like a kid in a candy store, eager to build, create, and leave my mark on the digital world. However, my early projects were, in hindsight, a chaotic mishmash of functionality, with code that was hard to understand, let alone maintain.
One specific project comes to mind. I was tasked with building a simple e-commerce platform in C#. It started off well, but as the codebase grew, so did the complexity. Classes were interconnected in ways that made changes a perilous undertaking. Adding a new feature often meant breaking existing functionality, and debugging was akin to navigating a maze blindfolded.
The Medior Engineer: Discovering SOLID Ground
As I transitioned to a mid-level engineer, I began to feel the weight of maintaining and extending the projects I had once created with so much enthusiasm. It was during this phase that I stumbled upon SOLID principles, a set of guidelines that promised to bring order to the chaos. Intrigued, I decided to dive deep and apply them to my ongoing projects.
S — Single Responsibility Principle (SRP):
I remember a significant shift occurred when I applied SRP to refactor a class responsible for both data validation and persistence in my e-commerce project. By breaking it into two classes, one handling validation and the other dealing with persistence, each class became more focused, making the code more readable and maintainable.
// Before SRP
public class OrderProcessor
{
public void ProcessOrder(Order order)
{
// Validation logic
// Persistence logic
}
}
// After SRP
public class OrderValidator
{
public bool ValidateOrder(Order order)
{
// Validation logic
}
}
public class OrderPersister
{
public void SaveOrder(Order order)
{
// Persistence logic
}
}
O — Open/Closed Principle (OCP):
As I embraced OCP, I found myself extending functionality without modifying existing code. For example, I introduced interfaces and abstract classes to enable future extensions without altering the core classes.
// Before OCP
public class PaymentProcessor
{
public void ProcessPayment(Order order)
{
// Payment processing logic
}
}
// After OCP
public interface IPaymentProcessor
{
void ProcessPayment(Order order);
}
public class CardPaymentProcessor : IPaymentProcessor
{
public void ProcessPayment(Order order)
{
// Card payment logic
}
}
public class PayPalPaymentProcessor : IPaymentProcessor
{
public void ProcessPayment(Order order)
{
// PayPal payment logic
}
}
The Senior Engineer: Embracing SOLID Wisdom
Now, as a senior engineer, SOLID principles have become second nature. They’ve not only influenced the way I write code but also how I approach software architecture and mentor junior developers.
L — Liskov Substitution Principle (LSP):
LSP has saved me from many headaches, especially when working with inheritance. Ensuring that derived classes can be substituted for their base classes without altering the correctness of the program has led to more robust and flexible systems.
// Before LSP
public class Rectangle
{
public int Width { get; set; }
public int Height { get; set; }
}
public class Square : Rectangle
{
public new int Width
{
set { base.Width = base.Height = value; }
}
public new int Height
{
set { base.Width = base.Height = value; }
}
}
// After LSP
public class Shape
{
public virtual int Area => 0;
}
public class Rectangle : Shape
{
public int Width { get; set; }
public int Height { get; set; }
public override int Area => Width * Height;
}
public class Square : Rectangle
{
public override int Area => Width * Width;
}
I — Interface Segregation Principle (ISP):
ISP has been instrumental in preventing ‘fat’ interfaces. By breaking them into smaller, more specific interfaces, I’ve ensured that classes only implement what is relevant to them.
// Before ISP
public interface IWorker
{
void Work();
void Eat();
void Sleep();
}
public class Robot : IWorker
{
public void Work()
{
// Robot working
}
public void Eat()
{
// Robot eating... wait, what?
}
public void Sleep()
{
// Robot sleeping... seriously?
}
}
// After ISP
public interface IWorkable
{
void Work();
}
public interface IEatable
{
void Eat();
}
public interface ISleepable
{
void Sleep();
}
public class Robot : IWorkable
{
public void Work()
{
// Robot working
}
}
D — Dependency Inversion Principle (DIP):
DIP has been a game-changer in terms of writing flexible and extensible code. By depending on abstractions rather than concrete implementations, I’ve built systems that can easily adapt to change.
// Before DIP
public class OrderProcessor
{
private readonly SqlDatabase _database;
public OrderProcessor()
{
_database = new SqlDatabase();
}
public void ProcessOrder(Order order)
{
// Processing order using SQL database
}
}
// After DIP
public interface IDatabase
{
void ProcessOrder(Order order);
}
public class SqlDatabase : IDatabase
{
public void ProcessOrder(Order order)
{
// Processing order using SQL database
}
}
public class OrderProcessor
{
private readonly IDatabase _database;
public OrderProcessor(IDatabase database)
{
_database = database;
}
public void ProcessOrder(Order order)
{
_database.ProcessOrder(order);
}
}
Conclusion: A Solid Foundation for Success
In my journey from a junior engineer grappling with complex, tangled code to a senior engineer building robust and maintainable systems, SOLID principles have been my constant companions. They have not only shaped the way I write code but have also become the cornerstone of my approach to software design.
By embracing the Single Responsibility Principle, Open/Closed Principle, Liskov Substitution Principle, Interface Segregation Principle, and Dependency Inversion Principle, I’ve not only improved the quality of my code but also enhanced my ability to collaborate with other developers and respond to evolving requirements.
As I continue to grow in my role as a senior software engineer, I find solace in knowing that the principles that guided me through the complexities of software development as a junior are the same principles that empower me to architect scalable, maintainable solutions today. SOLID principles are not just guidelines;
Lessons Learned
Navigating the Code Jungle
Early projects can feel like venturing into a jungle; it’s messy, confusing, but a necessary part of the learning journey. SOLID principles act as a compass, guiding you out.
Clarity Amidst Complexity
When my code resembled a puzzle missing pieces, the Single Responsibility Principle taught me that simplicity is a virtue. Clear code is like a well-told story; everyone can follow.
Change Isn’t the Enemy
The Open/Closed Principle is a friendly reminder that change is constant. Embrace it by designing for extension, not upheaval. Your codebase will thank you later.
Flexibility for Real-World Turbulence
Real-world scenarios aren’t textbook examples. The Liskov Substitution Principle became my ally, ensuring my code could handle the twists and turns without breaking.
Interfaces Speak a Universal Language
Like learning a language, Interface Segregation Principle made me appreciate that not everyone needs to know everything. Speak what’s necessary, nothing more.
Dependency Drama to Harmony
Dependency Inversion Principle transformed dependency relationships from drama to a symphony. Depending on abstractions allows for harmonious adjustments, even when the cast changes.
Beyond Code: A Journey of Growth
SOLID principles are not just coding guidelines; they are life lessons. They’ve been with me through the ups and downs, guiding my growth from a confused junior to a confident senior engineer.