Rethinking Software Engineering: Why It Has Failed at Maintainability

Published: (December 14, 2025 at 04:20 PM EST)
6 min read
Source: Dev.to

Source: Dev.to

I have a strong opinion about the current state of software engineering for building maintainable applications: it has failed, and it’s time to change. Not to throw everything away and start over, but to evolve. There are many good things in today’s software engineering that we can preserve. However, before we can separate the wheat from the chaff, we must first understand why current approaches aren’t working.

The problem of Modern Software Engineering

Since the 2000s, several works have been highly influential in the field: Refactoring¹, Design Patterns², Domain‑Driven Design³, Clean Code⁴, Clean Architecture⁵, SOLID Principles⁶, and architecture styles such as Hexagonal⁷ and Microservices⁸. This doesn’t represent all of software engineering applied to maintainability, but it includes some of the most influential works, giving us a good enough view of the current state. Despite this impressive intellectual foundation, something isn’t working in practice.

Some time ago, I watched a talk entitled “Prioritizing Technical Debt as if Time & Money Matters”⁹ by Adam Tornhill. He described a case where a company team he was consulting for complained about a specific service being hard to maintain. When he, as an external consultant, analyzed the codebase and compared it to other projects the team was working on, he couldn’t identify any objective difference in code quality. However, the project the team complained about hadn’t been developed by the team itself—it had been inherited after the original team was dismantled.

This case reveals something crucial: the problem wasn’t the codebase itself; rather, the codebase was expressing deeper socio‑organizational problems. Building software professionally is fundamentally a social act. We construct software with other people, and our code inevitably reflects the dysfunction or health of those relationships, as Conway’s Law¹⁰ has suggested since the sixties. This realization led me to reconsider how we’ve approached even our best‑intentioned practices.

The Gap Between Principles and Practice

Take Domain‑Driven Design, for example. While it did great work introducing the concept of Ubiquitous Language¹¹—recognizing that a domain language emerges from social interactions, just like the language we speak—it stops short of addressing the practical challenges of implementation. We have Ubiquitous Language as a conceptual solution for communication issues between tech and business people, but we lack practical guidance on implementation:

  • How can we convince business stakeholders to participate in that construction?
  • Does a regular individual contributor in an average‑sized organization have the power to change how an entire team, or multiple teams, builds software?
  • What are the limitations?
  • What about the legacy non‑ubiquitous language that has been written into the current codebase? What do we do with it? What are the risks?

These aren’t rhetorical complaints—they’re genuine blockers that practicing engineers face daily. Yet our influential works rarely address them. The knowledge we have separates principles from their premises and from the real‑world constraints that affect their application.

The same problem appears in our most fundamental principles. If we take the clean‑code principle¹² of doing only “one thing” in a function—very similar to the Single Responsibility Principle¹³ from SOLID—we also hit challenges in applicability. What is a responsibility? What is a “thing”? What is a “reason to change”? Even philosophers debate these questions; there is a dedicated field in philosophy called ontology¹⁴ that has been wrestling with them for millennia.

Consider two different objects to illustrate why achieving a “single” responsibility, thing, or reason to change is not straightforward. Compare a desktop with a laptop from a maintenance perspective:

  • Desktop – If the keyboard breaks, we simply unplug the broken keyboard and replace it. The desktop has fewer responsibilities compared to a laptop.
  • Laptop – The keyboard is embedded; fixing it requires opening the entire device, which is far more challenging.

Even the desktop example raises questions: Is a keyboard a single “thing”? If only the “A” key is broken, do we replace the whole keyboard or just that key? And is a key itself a single “thing,” or is it composed of a switch and other components? Zooming in further leads us to sub‑atomic levels. In software, our objects and modules face analogous dilemmas. We can zoom in on abstractions until we reach the bit level, yet the Single Responsibility Principle does not tell us what level of abstraction we need or which level we can handle given socio‑organizational constraints.

A similar issue occurs when we try to define how “micro” a microservice should be. The goal is not “single” or “micro” per se; it is to adjust the number of responsibilities and the size of the “thing” to achieve specific objectives. Reaching a single “thing” cannot be a principle or a goal in itself. Many experienced practitioners already know this, but theory lacks formalization and can mislead inexperienced engineers.

The Missing Foundation: Empirical Evidence

Software engineering is a relatively recent and rapidly growing field compared to other domains of knowledge. We do not yet have many of the answers we need, and as professionals we must make the best decisions we can with the guidance of our experience. However, we could be more organized and meticulous.

The lack of data about the effectiveness and challenges of software‑engineering principles and practices for maintainability is a core part of the problem. We need more studies on clean code, SOLID, DDD, refactoring, and other mainstream techniques. Publishing new techniques that “worked for me” is valuable, but we must understand that they worked under certain premises and conditions. Without objective, empirical data about large‑scale adoption, such claims remain hypotheses. A technique that works for one team does not guarantee it will work for another; we need to rationally understand why it would or wouldn’t work in a given context.

My own experience illustrates this gap. In a decade working on enterprise web applications—spanning ERP, agriculture, and logistics—I have never encountered a delivery delay caused by a “Shotgun Surgery”¹⁵ change in the code. While spreading changes across multiple places is annoying, slow, and fault‑prone, it is not a blocker. Conversely, I have been stuck for days trying to contribute to a codebase that no one in the company remembered how to work with because the original contributors had left. The deep dive required to make a small contribution was a significant impediment.

I have also seen projects delayed because a project leader attempted DDD adoption despite the team’s lack of readiness. In my opinion, the fault lies with the leader’s mismatch between the technique and the team’s context. This underscores the need for empirical grounding: we must assess readiness, organizational health, and other constraints before prescribing a methodology.

Footnotes

  1. Martin Fowler, Refactoring (1999)
  2. Erich Gamma et al., Design Patterns (1994)
  3. Eric Evans, Domain‑Driven Design (2003)
  4. Robert C. Martin, Clean Code (2008)
  5. Robert C. Martin, Clean Architecture (2017)
  6. SOLID Principles (Robert C. Martin)
  7. Alistair Cockburn, Hexagonal Architecture (2005)
  8. Microservices Architecture (various sources)
  9. Adam Tornhill, “Prioritizing Technical Debt as if Time & Money Matters” (talk)
  10. Melvin Conway, Conway’s Law (1968)
  11. Eric Evans, Ubiquitous Language (DDD)
  12. Robert C. Martin, Clean Code (2008) – “one thing per function”
  13. Robert C. Martin, SOLID – Single Responsibility Principle
  14. Ontology (philosophical discipline)
  15. Martin Fowler, “Shotgun Surgery” (code smell)
Back to Blog

Related posts

Read more »

You Don't Hate Abstractions

It’s an hour until you’re free for the weekend, and you’re trying to knock out one last ticket before you escape into whatever action‑packed plans await you. Yo...

SOLID Principles + Design Patterns

!Forem Logohttps://media2.dev.to/dynamic/image/width=65,height=,fit=scale-down,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%...