Wouter van Nierop

Are ‘Good’ Software Practices Dead, Dying, or Just Evolving?

Are ‘Good’ Software Practices Dead, Dying, or Just Evolving?

Apr 8, 2022

Blue Flower
Blue Flower
Blue Flower

Long-standing principles like SOLID have been celebrated as the gold standard for object-oriented programming (OOP), guiding developers in creating maintainable, scalable, and robust code. However, as software architectures evolve, especially with the shift from monolithic to distributed systems, questions arise: Are these principles still relevant, or are they becoming relics of a bygone era?

The Relevance of SOLID in Modern Architectures

The question of whether SOLID principles remain relevant in distributed systems was first posed to me by a mentor some years ago. Since then, I’ve had numerous discussions with colleagues and peers, exploring how these principles apply in the context of evolving software architectures. Recently, during a technical due diligence session, the topic came up again, with a colleague suggesting that SOLID principles might be more applicable to monolithic architectures and less relevant in, perhaps more modern, distributed systems.

This observation raises an important question: Should developers still adhere to SOLID principles when using OOP languages, or are there scenarios where these practices might no longer serve us well?

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, these principles help mitigate the complexities that arise as applications scale. However, in distributed systems, where services are more isolated and communicate over a network, strict adherence to SOLID can sometimes seem excessive.

Conversely, some services intended to be “microservices” end up becoming too large and complex, resembling traditional monolithic applications more than the lightweight, independent services they were meant to be. In these cases, applying SOLID principles is essential for maintaining the service’s manageability and ensuring it remains scalable and robust. The same principles that were vital in monolithic architectures become just as important in these larger, more complex services.

Python and the Flexibility of SOLID

Consider Python, a language that fully supports object-oriented programming (OOP) but often sees projects that don’t strictly adhere to SOLID principles. This isn’t due to Python itself, but rather how it’s often used in various contexts. Python’s design philosophy, encapsulated in "The Zen of Python", emphasises simplicity and readability, but does this lead to a more pragmatic approach to software design, sometimes at the expense of strict adherence to traditional principles?

Take FastAPI, a popular open-source web framework for building APIs with Python. FastAPI is a prime example of how “good” software practices, including clean code and SOLID principles, are being applied in the Python community. The project is meticulously organised, with a clear separation of concerns and strong adherence to object-oriented design principles. This adherence has, in part, contributed to its success and widespread adoption, showcasing that with the right approach, Python projects can maintain high standards of software engineering.

In some regions, like the Netherlands, the Python developer pool tends to have fewer senior developers compared to other programming languages like Java or C#. This disparity could be attributed to Python's relatively recent surge in popularity in fields like data science, its widespread adoption in universities, and the fact that historically, languages like C# and Java have been more dominant in the region. Many developers entering the workforce today have learned Python as their first language, often coming from non-traditional software engineering backgrounds. As a result, there might be less widespread application of traditional software practices like SOLID in Python projects, not because of the language itself, but due to the varying levels of experience within the developer community.

This presents a significant opportunity for seasoned developers who are proficient in other OOP languages to transition to Python. By doing so, they can bring their deep understanding of software engineering principles, including SOLID, to the Python ecosystem. Their experience could be invaluable in mentoring less experienced developers and in enhancing the quality and rigor of Python projects, contributing to a stronger Python ecosystem and community. Additionally, this transition would help them stay relevant in a landscape where Python is increasingly becoming one of the most popular programming languages globally.

The Double-Edged Sword of Emerging Development Tools

Emerging development tools make it easier than ever to write code quickly, but this speed often comes at a cost. When under pressure to deliver rapidly, some companies may prioritise speed over quality, essentially borrowing time from the future. This accumulation of tech debt, if not managed, can create significant challenges for those who eventually inherit the codebase, whether within the same team or by others down the line. By maintaining a focus on solid software practices, teams can avoid these pitfalls, ensuring that their code remains maintainable and scalable as it evolves.

Revisiting good software practices is not just about cleaning up a mess; it’s about building a strong foundation that can withstand future demands. Applying these principles later in the development process is far more challenging and costly than doing so early on. Without this early investment, teams may face the significant cost of repaying this tech debt by rebuilding components—or even entire applications—down the road. Ensuring the software remains a valuable and sustainable asset over time means laying the groundwork early, not just patching issues after they arise.

Evolving Practices: The Balance Between Speed and Sustainability

The relevance of these software practices isn’t a matter of them being alive or dead—they are evolving. In the early stages of a project, strict adherence might not seem necessary, but as projects mature and scale, embracing these practices becomes key to maintaining code integrity and manageability. The challenge lies in continuously assessing and refining the application of these practices to strike a balance between immediate delivery needs and long-term sustainability.

Conclusion

Success in software development doesn’t come from blindly following established principles but from the deliberate and thoughtful application of good practices. As tools evolve and project demands shift, the real challenge is continuously assessing and refining these practices to ensure they meet both current needs and future challenges. For developers transitioning to newer languages like Python, bringing a deep understanding of foundational principles can be invaluable in shaping a robust ecosystem. By investing in strong foundations early and avoiding the temptation to cut corners, developers can create robust, scalable solutions that deliver today and stand the test of time. The decisions made during development will ultimately determine whether a product becomes a lasting asset or a liability handed off to someone else.

Thank you for taking the time to read this blog post! If you found it helpful or have any questions, feel free to reach out.

Blog author
Blog author

Wouter van Nierop

Wouter van Nierop