← Insights

Frontend Projects Don't Fail. They Degrade.

There's a specific kind of meeting I've been in more than once. Something in the codebase has become difficult enough that it's started affecting delivery. Someone has noticed. The proposed solution is either a major refactor, a framework migration, or both. There's energy in the room. People are ready to fix things.

The problem is that the decision being made is about tooling. The actual problem is organizational.

Frontend projects rarely fail because of a bad technical decision. They degrade because of a governance problem that compounds over months, sometimes years, and stays invisible until it can't anymore.

It starts with how the frontend is categorized

In a lot of organizations, frontend is still understood as the UI layer. The place where designs become reality. Valuable, but not a system in the same sense that a backend service or a data platform is a system.

This categorization has consequences that aren't obvious at first. When frontend is "just UI," technical decisions don't get roadmap time. Architecture planning doesn't happen because it's not clear what there is to plan. Engineering conversations happen at the feature level: can we build this, how long will it take, when will it ship.

Nobody is being negligent. This is how the organization has framed the problem. But framing the frontend as a rendering layer rather than a platform means the questions that matter at the platform level never get asked. How does this codebase need to change over the next eighteen months to support what product is planning? What are the load-bearing assumptions we're making right now, and are they still valid? Where are the seams we'll need to break if the team doubles in size?

These questions have answers. Getting to them requires that someone in the room has the authority and the mandate to ask them, and that the answers actually feed into planning. That's an organizational question, not a technical one.

The accumulation phase

For a while, nothing obviously goes wrong. Features ship. The UI is stable. Metrics look fine. From the outside, and often from the inside, there's no visible problem.

Underneath, the codebase is changing in a specific way. Temporary solutions stop being temporary. Not because anyone decided to make them permanent, but because the conditions that were supposed to allow for cleanup never materialized. The ticket gets created. It stays in the backlog. Priorities shift. Six months later, the workaround is load-bearing and too risky to touch.

This happens with individual components. It happens with data fetching patterns. It happens with state management decisions made in week two of a project that are now implicit contracts between a dozen different parts of the app. Each one seemed like a reasonable call at the time. Accumulated, they are the architecture.

The signal that something is wrong at this stage isn't bugs. It's friction. Things that should be straightforward take longer than they should. New engineers take six weeks to become productive rather than two. Small changes require touching more files than anyone expected. None of this shows up in a sprint report. Nobody escalates friction.

Why it stays invisible

Part of what makes this pattern hard to interrupt is that the people who see it clearly are often not the people making decisions about it.

The engineers closest to the codebase know where the problems are. They've felt the friction. They know which areas are fragile and which changes carry hidden risk. But in organizations where frontend is treated as a delivery layer, the signal from those engineers gets filtered through a frame of "that's a technical concern, we'll address it when there's time." There's never time because time is allocated to things that appear on the roadmap, and technical concerns that haven't caused a visible incident yet don't appear on the roadmap.

So the people making resource allocation decisions are operating on incomplete information. Things look fine because nothing has broken visibly. The codebase is getting harder to work with, but that's treated as an engineering complaint rather than a product risk. The two framings lead to very different responses.

The misdiagnosis

When the problems do become visible, the conversation that follows usually focuses on the wrong thing.

"This code is too messy to work with" is a real problem, but it's a symptom. The cause is that the codebase has been accumulating changes without a considered architecture for long enough that there's no longer a clear model of how it's supposed to work.

"We should refactor" is a reasonable instinct. But a refactor without changed governance is expensive work that buys you maybe eighteen months before you're back in the same position. If the conditions that produced the problem haven't changed, the problem will reproduce.

"We should migrate to a different framework" is the version that concerns me most. It's not that migrations are never warranted. It's that a framework migration is a large, disruptive undertaking that addresses the technology while leaving the organizational conditions untouched. The new framework inherits the same planning gaps, the same missing ownership, the same absence of a technical roadmap. Two years later, the problems are familiar and the migration is cited as having failed to deliver on its promise.

The thing worth fixing is the decision-making structure: who has the authority to make architectural decisions, whether those decisions are tracked and revisited, whether there is a technical direction that maps to the product direction and someone who reviews it regularly. These are uncomfortable questions because they implicate processes and sometimes people. That's why the framework conversation is easier to have.

What the numbers actually show

The costs that accumulate during the degradation phase are real but diffuse. Regression rates increase gradually. Delivery slows in ways that are easy to attribute to scope or complexity rather than to structural issues. Engineers who are worn down by codebases that resist them leave, taking context with them. Onboarding takes longer every quarter. Each of these is individually explainable away. Together they are the cost of the accumulated debt.

None of it shows up in a single incident. There is no outage, no missed launch you can point to and say this was the moment it broke. It just gets harder, and slower, and more expensive, until someone is in a meeting proposing a framework migration and calling it a fresh start.

It isn't a fresh start. It's the same problem with new tooling on top of it.


Frontend systems don't collapse under the weight of bad code. They degrade under the weight of decisions, and the absence of decisions, that treat them as something less than systems. The fix isn't technical. It's getting the right questions into the right rooms before the degradation becomes the only thing anyone can talk about.