The go-to resource for learning PHP, Laravel, Symfony, and your dependencies.

Framework Upgrade: When to Refactor vs. Rewrite


In architecture, there’s a fundamental choice when a building outlives its original purpose: do we renovate the existing structure, preserving its character while upgrading its systems, or do we demolish and build anew? The same dilemma faces PHP development teams when their application’s framework reaches end-of-life. Consider a Laravel 5 application running on PHP 7.1—both versions lost official security support, yet the application handles critical business operations. Or imagine a Symfony 3 codebase with 200,000 lines of custom business logic that cannot simply be discarded. When we face such situations, we must choose between refactoring—incrementally upgrading the existing code—and rewriting—starting completely fresh. This isn’t merely a technical decision; it’s a strategic one with implications for your budget, timeline, and the team’s morale.

Of course, the answer isn’t universal. What works for a three-person startup maintaining a CodeIgniter application won’t necessarily suit an enterprise with a 15-year-old Zend Framework monolith. This is why we need to examine both approaches carefully—their trade-offs and practical considerations—so you can make an informed decision for your specific context.

The Case for Refactoring: Improving What You Have

Refactoring is the process of restructuring existing code—changing its internal organization—without altering its external behavior. In a framework upgrade, this means incrementally modernizing your application’s structure, addressing technical debt, and adapting code to the new version’s conventions, all while preserving the core business logic that powers your operations.

Consider a concrete example: you’re running a Laravel 5.8 application with 150 custom service classes. Rather than rebuilding from scratch, you could upgrade to Laravel 10 incrementally—first updating Composer dependencies, then addressing deprecation warnings using PHPStan, then refactoring one service at a time to use the updated container syntax. You might use tools like Rector to automate much of the mechanical work, as discussed in our article on using Rector to automate PHP version upgrades. This approach keeps your application running throughout the process.

When to Choose Refactoring:

  • The Core Architecture is Solid: Your application’s fundamental design remains sound. The issues are outdated dependencies, inconsistent coding standards, or missing test coverage—not a fundamentally flawed foundation. For instance, a well-structured Yii2 application may need only dependency updates and minor API adjustments to run on PHP 8.2.

  • Budget and Time are Limited: Refactoring typically costs 30-50% of a full rewrite and delivers value incrementally. Of course, these percentages vary by codebase, but the pattern holds: you can often allocate two developers part-time for six months rather than dedicating a full team for eighteen months. The return on investment begins immediately, not after the rewrite completes.

  • You Can’t Pause Feature Development: A phased approach lets your team continue shipping features while upgrading in the background. Using the legacy-code or strangler fig pattern, you can replace modules one by one. Our guide to the legacy code strangler pattern for PHP upgrades demonstrates this technique in detail.

  • Business Logic is Complex and Undocumented: Applications with years of embedded business rules—think decade-old tax calculation logic or proprietary inventory algorithms—risk losing critical knowledge during a rewrite. Refactoring lets you extract and document that logic while preserving it. As you work, you can write characterization tests to capture existing behavior before making changes.

  • Your Team Has Deep Domain Knowledge: Your developers understand the application’s quirks, edge cases, and historical decisions. That knowledge is difficult to transfer to a new team or rebuild from scratch. Though a rewrite might seem appealing, losing this institutional knowledge often proves costly.

  • Testing Coverage Exists or Can Be Built: If your application has at least moderate test coverage—say 50% or more of critical paths—you can refactor with confidence. If not, you can build tests incrementally as you work, focusing first on the most complex business logic. One effective strategy is to write characterization tests that capture existing behavior before making changes—this creates a safety net that prevents regressions. The testing framework upgrades in staging environments article covers establishing safe upgrade pathways with proper testing, including how to set up parallel environments for validation.

The Case for a Rewrite: Starting with a Clean Slate

Refactoring isn’t always the answer, though. There are situations where the existing codebase has accumulated so much technical debt, or the architecture has become so compromised, that incremental improvements cannot deliver the transformation needed. In these cases, a rewrite—discarding the existing codebase and building anew on a modern foundation—becomes the rational choice. This is a high-investment strategy with significant risks, and it should be considered only when the current system actively hinders your organization’s goals. A rewrite recognizes that the existing application has become a liability rather than an asset.

Many successful PHP applications began as rewrites of earlier systems—for instance, when an organization moved from a legacy Laravel 4 application with global state and facade abuse to a fresh Laravel 11 application with proper service container architecture, they gained better maintainability and developer productivity. But rewrites also have a well-documented failure rate; the second-system effect, where a rewrite inherits the complexity of the original while adding new problems, is common.

When to Choose a Rewrite:

  • The Core Architecture is Fundamentally Flawed: The application has become a “big ball of mud”—classes with 3,000 lines each, database queries scattered throughout views, and no discernible separation of concerns. Making any change triggers unexpected bugs in unrelated features. Refactoring such a system can cost nearly as much as a rewrite while delivering far less improvement. In these cases, starting fresh often makes better financial sense.

  • The Technology is Obsolete Beyond Practical Recovery: You’re running PHP 5.6 with CodeIgniter 2.x on an unmaintained server. Critical dependencies have no PHP 8 compatible versions. Security vulnerabilities remain unpatched because the framework maintainers dissolved years ago. Moreover, you cannot hire developers willing to work with such outdated technology. When the upgrade path requires through multiple major versions—Laravel 5 to 6 to 7 to 8 to 9 to 11—the integration effort approaches a rewrite in scope.

  • The Business Has Pivoted Significantly: Your application was built for e-commerce but now serves as a content management system. The database schema, user workflows, and core entities reflect the old model. Patching the old system to support the new business model creates unsustainable complexity. A rewrite aligned with current needs serves the business better than forcing an obsolete structure to accommodate new requirements.

  • You Need Transformational Capabilities: The current architecture cannot support required capabilities—horizontal scaling, real-time processing, microservices, or integration with modern frontend frameworks. Incremental improvements won’t bridge the gap. For instance, upgrading from a monolithic Laravel 6 application to a full API-driven architecture with Laravel Sanctum, event broadcasting, and queue workers may be more efficiently achieved through a rewrite.

  • The Cost of Maintenance Exceeds the Cost of Rewriting: If your team spends 80% of their time fixing issues introduced by the old framework’s quirks rather than building features, the math may favor a rewrite. Our article on cost-benefit analysis of PHP version upgrades provides a framework for quantifying these costs.

A Decision Framework: Key Factors to Consider

Before committing to either path, evaluate these four factors systematically. One may wonder: how do we weigh competing priorities when these factors pull in different directions? Let’s examine each in turn, and then we’ll bring them together in a decision matrix.

1. Business Goals

What does the business fundamentally need? Consider these questions:

Does your application need to support new business models or market opportunities? If your company is pivoting from B2B to B2C, for instance, your existing user management and billing systems may be structured for wholesale rather than retail. A rewrite can align the architecture with new workflows more cleanly than patching an obsolete design.

Are you trying to reduce operational costs or accelerate feature velocity? Refactoring typically improves maintainability faster, allowing your team to ship features sooner. A rewrite postpones most new development until the new system is complete—which may be 12 to 24 months away. If feature development has slowed due to technical debt, refactoring offers faster relief.

Do you need to modernize while maintaining service continuity? For e-commerce or SaaS applications, downtime equals lost revenue. Refactoring supports blue-green deployments and feature flags to upgrade with minimal disruption, as discussed in our zero-downtime framework upgrades guide. A rewrite typically requires a cutover event, though careful planning can minimize downtime.

2. Budget and Timeline

Be rigorously honest about resources. Industry data suggests rewrites take 2-3 times longer than original estimates—the “second-system effect” is real. Consider these realities:

Tip: When planning budgets, use the “multiply by three” rule for rewrites. If your initial estimate is 6 months, plan for 18 months of runway.

What is your actual budget? A typical Laravel to Laravel rewrite for a medium application (50,000-100,000 lines) might require 6-12 months of 2-3 developer salaries. Refactoring the same system might take 3-6 months part-time. Can your organization sustain either timeline? Of course, these numbers vary widely based on complexity, but they establish a useful baseline for planning.

Can you tolerate delayed ROI? A rewrite delivers no visible improvement until it goes live. Refactoring shows progress continuously—each upgraded component is immediately more maintainable. Stakeholders see intermediate results, which can help maintain organizational support for the project.

Do you have contingency for overruns? Though no project should go over budget, rewrites are particularly prone to scope creep and integration surprises. We recommend adding a 30-50% contingency buffer for rewrites, versus 15-20% for refactoring. The difference reflects the higher uncertainty inherent in building from scratch.

3. Team Skills and Knowledge

Does your team know the current codebase? Developers who have maintained the application for years understand its edge cases, workarounds, and undocumented behaviors. That knowledge is difficult to transfer. A refactoring leverages this expertise; a rewrite requires rediscovering these nuances through testing and bug reports.

Does your team have expertise in the target technology? Moving from Symfony 3 to Symfony 6 introduces new patterns: autowiring, service containers, PHP 8 features. If your team hasn’t worked with these, learning curves affect both refactoring and rewriting—but a rewrite amplifies the risk because every decision is new. Of course, you can hire contractors or consultants to fill knowledge gaps, but that adds cost and may create knowledge transfer challenges. Consider targeted training first; our article on team training for new PHP version features addresses this.

Can you staff the project? Though PHP remains widely used, finding developers with deep framework-specific expertise varies by version. Laravel developers are plentiful; experienced developers familiar with Zend Framework 1 or 2 are rare. If you can’t hire for the skills needed either to maintain the old system or build the new, both paths become riskier.

4. Risk Tolerance

What happens if the project overruns or fails? A failed refactoring typically leaves you with a slightly improved but still functional system. A failed rewrite can leave you with nothing usable after investing significant resources. Assess your organization’s tolerance for this risk profile.

How complex is your domain logic? The more complex your business rules—regulatory compliance, financial calculations, specialized workflows—the more dangerous a rewrite becomes. Business logic embedded in a working system, however messy, represents proven correctness. Reimplementing it accurately requires comprehensive test suites or extensive user acceptance testing.

What is your testing coverage? If you have comprehensive integration and end-to-end tests—say, covering 70% of critical paths—a rewrite becomes less risky because you can verify the new implementation matches the old. If you have minimal testing, a refactoring approach lets you build tests incrementally while preserving functionality. Our guide to testing before upgrades helps establish this safety net.

5. Technical Constraints

Let’s also consider technical constraints that often get overlooked:

Note: These constraints frequently determine the viable options regardless of your preferences. Acknowledge them early rather than discovering them mid-project.

What dependencies constrain your choices? Third-party libraries, APIs, and database systems may have version requirements that limit your options. For instance, if you depend on a library that only supports PHP 7.4, neither refactoring nor rewriting to PHP 8.3 is feasible until the library updates or you replace it. In practice, we’ve seen teams spend months evaluating dependency compatibility before making any framework decision.

What infrastructure changes are required? Moving to a new framework might necessitate changes to your hosting environment, build pipeline, or deployment processes. These infrastructure changes often have their own timelines and dependencies. Our articles on Docker strategy for testing PHP version upgrades and upgrading PHP on shared hosting environments cover these considerations in detail. We recommend evaluating these infrastructure constraints before committing to a particular upgrade path.

Are there security or compliance deadlines? If your PHP 7.4 application runs on a framework no longer receiving security patches, and compliance requires supported software, your timeline may be dictated by external factors. One may wonder: can a rewrite be fast enough? This pressures the decision toward the faster option—usually refactoring, unless the codebase is too compromised to upgrade safely.

Conclusion

The choice between refactoring and rewriting is rarely straightforward. Most teams find themselves somewhere between these poles, considering hybrid approaches that combine elements of both.

Beyond the Binary

Though this article frames the decision as two options, we should acknowledge alternatives that blend both strategies:

The Incremental Rewrite (Strangler Fig Pattern): You rewrite specific modules or services while the old system remains operational, gradually “strangling” the legacy code until it can be retired. This approach combines the architectural benefits of a rewrite with the risk mitigation of incremental delivery. Our guide to the legacy code strangler pattern for PHP upgrades details this technique.

Phased Migration with Parallel Systems: Some teams run both old and new systems in parallel for an extended period, routing traffic gradually from one to the other. This requires duplication of effort but provides an emergency rollback capability. The incremental vs big bang PHP upgrades article compares these approaches.

Refactor Toward Rewrite: Start with extensive refactoring to improve test coverage and modularity, then proceed to rewrite specific components once the foundation is solid. This reduces rewrite risk by first clarifying the domain.

A Word of Caution

Before we conclude, two important warnings:

Don’t underestimate the unknowns. A rewrite appears simpler because you don’t yet understand all the edge cases and implicit requirements embedded in the existing system. Those surprises often emerge during acceptance testing, inflating timelines. One experienced CTO described this as “the iceberg problem”—you see the user-facing features, but beneath the surface lie thousands of tiny business rules, data validations, and error-handling paths you forgot were there.

Don’t overestimate refactoring’s ease. Refactoring a deeply coupled system can feel like changing a car’s engine while driving on the highway. Legacy dependencies, untested code, and architectural constraints can make incremental progress agonizingly slow. Some teams refactor for years without escaping the legacy quagmire—the “never-ending refactor” trap.

Making Your Decision

To summarize:

FactorFavors RefactoringFavors Rewrite
Architecture qualitySolid foundationFundamentally flawed
Budget constraintsLimited resourcesAvailable investment
Timeline pressureNeed immediate ROICan wait 12-24 months
Team knowledgeDeep domain expertiseCan learn new domain
Business continuityMust keep shippingCan tolerate disruption
Testing coverageModerate to highLow, but can rebuild
Technology viabilityUpgrade path existsDead or dying stack

Ultimately, this decision requires collaboration between engineering, product, and business stakeholders. There is no universally correct choice—only the choice that best fits your organization’s context, constraints, and goals.

Next Steps

If you decide to proceed with refactoring, consider these resources from our repository:

If a rewrite is the chosen path:

Whichever path you choose, document your decision process, assumptions, and success criteria. That documentation itself becomes part of your institutional knowledge—useful whether this is your last major upgrade or the first of many.

Final Thought

To paraphrase the software engineer who famously noted that “there are only two hard things in computer science: cache invalidation and naming,” we might add a third: knowing when to rebuild versus when to repair. Both paths have their place. The mark of a skilled technical leader is not choosing one approach exclusively, but understanding the trade-offs deeply enough to choose appropriately for the situation at hand.

Sponsored by Durable Programming

Need help with your PHP application? Durable Programming specializes in maintaining, upgrading, and securing PHP applications.

Hire Durable Programming