The question that keeps coming back
Long-standing principles like SOLID have been celebrated as the gold standard for object-oriented programming, guiding developers in creating maintainable, scalable, and robust code. But as software architectures evolve — especially with the shift from monolithic to distributed systems — a question keeps surfacing: are these principles still relevant, or are they becoming relics of a bygone era?
The question was first posed to me by a mentor some years ago. Since then, I've had dozens of discussions with colleagues, peers, and clients. Recently, during a technical review session, a colleague suggested that SOLID principles might be more applicable to monolithic architectures and less relevant in distributed systems.
It's a reasonable hypothesis. It's also wrong — but in an interesting way.
SOLID in distributed systems
SOLID principles — single responsibility, open-closed, Liskov substitution, interface segregation, and dependency inversion — were crafted to make code more modular and easier to manage. In traditional monolithic architectures, they help mitigate the complexity that arises as applications scale.
In distributed systems, where services are isolated and communicate over a network, strict adherence to SOLID can sometimes seem excessive. Each microservice is already scoped to a single responsibility. Interface segregation is handled at the API boundary. The architectural pattern itself enforces some of the principles that SOLID was designed to achieve.
But here's the catch: many services intended to be "microservices" end up becoming too large and complex — resembling traditional monoliths more than the lightweight, independent services they were meant to be. In these cases, SOLID principles are just as essential as they ever were for maintaining manageability and ensuring scalability.
The architecture doesn't eliminate the need for principles. It changes where you apply them.
Python and the pragmatism question
Consider Python — a language that fully supports OOP but often sees projects that don't strictly adhere to SOLID. This isn't Python's fault. It's how the language is used in practice.
Python's design philosophy, captured in The Zen of Python, emphasises simplicity and readability. This sometimes leads to a pragmatic approach to software design — prioritising "working now" over structural rigor.
Take FastAPI, a popular web framework for building APIs. FastAPI is a strong example of how clean code and SOLID principles can be applied effectively in the Python ecosystem. The project is meticulously organised, with clear separation of concerns and strong adherence to object-oriented design. Its success isn't coincidental — the architectural discipline contributed directly to adoption and maintainability.
In some regions, like the Netherlands, the Python developer pool tends to have fewer senior developers compared to languages like Java or C#. This disparity reflects Python's more recent surge in popularity, its adoption in universities, and the influx of developers from non-traditional software engineering backgrounds. Many entering the workforce today learned Python as their first language, often without deep exposure to traditional software engineering principles.
This creates an opportunity. Seasoned developers proficient in other OOP languages can bring their understanding of SOLID and clean architecture to the Python ecosystem — mentoring less experienced developers and elevating the quality of Python projects. The principles are language-agnostic. The experience translates.
The speed trap
Emerging development tools make it easier than ever to write code quickly. But speed and quality exist in tension.
When under pressure to deliver rapidly, teams often prioritise speed over structure. This is borrowing time from the future. The accumulation of technical debt, if not managed, creates significant challenges for whoever inherits the codebase — whether within the same team or by others down the line.
Revisiting good software practices isn't about cleaning up a mess. It's about building a foundation that can withstand future demands.
Applying these principles later in the development process is far more expensive than doing so early. Without that early investment, teams face the compounding cost of rebuilding components — or entire applications — when the debt becomes unmanageable.
Evolution, not extinction
The relevance of software practices isn't binary. They aren't alive or dead — they're evolving.
In the early stages of a project, strict adherence might not seem necessary. A prototype doesn't need dependency inversion. An MVP doesn't need interface segregation. But as projects mature and scale, embracing these practices becomes essential for maintaining code integrity.
The challenge is continuous assessment: knowing when to apply rigor, when to defer it, and when the cost of deferring has exceeded the cost of implementing. That judgment — not the principles themselves — is what separates experienced engineers from those still developing their craft.
Success in software development doesn't come from blindly following established principles. It comes from the deliberate and thoughtful application of good practices — adapted to the context, the constraints, and the long-term trajectory of the product.
The decisions made during development ultimately determine whether a product becomes a lasting asset or a liability handed off to someone else.