Graphite Reviewer is now Diamond

Refactoring vs rewriting code: How to decide

Greg Foster
Greg Foster
Graphite software engineer
Try Graphite

Table of contents

Software teams frequently face a critical question: should we refactor existing code or rewrite it entirely? Making the right choice is key to maintaining a healthy, scalable codebase in the long run. Refactoring and rewriting are both valuable approaches for dealing with aging code, but they involve different levels of effort, risk, and reward. This guide explains what refactoring and rewriting mean, when to choose each approach, and how to balance factors like maintainability, technical debt, performance, and scalability. It also highlights modern best practices and tools – including Graphite's code review and collaboration platform – that support codebase health in either scenario. By the end, junior developers, senior engineers, and engineering managers alike will have a clearer framework for deciding when to refactor vs. rewrite code.

Refactoring means changing the internal structure of code without altering its external behavior. The goal is to make existing code cleaner, simpler, and easier to maintain (for example, breaking up complex functions or improving naming) without adding new features. In contrast, rewriting means discarding the old code (either a portion or the entire application) and developing a new codebase from scratch that fulfills the same requirements. Rewriting is considered when the current code is too outdated or tangled to salvage via incremental changes. Generally, refactoring is a lower-risk, iterative approach since it works within the known system, whereas rewriting is a higher-risk, big bang approach that can incorporate fundamental changes to technology or architecture.

The decision to refactor or rewrite often comes down to issues of code quality, maintainability, and technical debt. Over time, a codebase that has been patched and extended many times can become increasingly brittle and hard to work with. Quick fixes and outdated code accumulate as technical debt, eventually slowing development to a halt or causing frequent bugs. At that point, teams must invest either in refactoring (to gradually clean and improve the code) or in a rewrite (to replace it outright) to restore the codebase's health.

Performance and scalability concerns are also factors. Legacy architectures (for example, a tightly coupled monolith) might not scale well to new requirements. If the software can't meet current performance or load demands, it prompts an evaluation of deeper changes – sometimes a targeted refactor will suffice, but other times a new architecture via a rewrite is needed.

Refactoring is generally a safer, incremental approach to improving a codebase. Key benefits include:

  • Incremental with lower risk: Because refactoring works with the existing code and stack, it avoids the unknowns of a new system. Changes can be done in small chunks over time without major interruption.

  • Improves code quality and maintainability: Refactoring reduces technical debt by cleaning up code, improving readability, and updating outdated components, making future changes easier.

Despite its benefits, refactoring has limitations and risks:

  • Limited impact on deep issues: Refactoring can't easily resolve fundamental problems in architecture or tech stack. If the underlying platform or design is obsolete or poorly structured, those constraints remain.

  • Challenging and requires discipline: Refactoring large systems requires careful planning and testing. Without careful planning, refactoring can introduce bugs.

  • No immediate new features: Refactoring by definition doesn't add new functionality, which can be hard to justify since it produces no new user-visible features.

In some scenarios, a complete rewrite of the codebase can be the more effective route. Potential advantages include:

  • Modernize architecture and tech stack: A rewrite lets you choose better frameworks, languages, and architectures optimized for future needs. You can design for scalability and performance from scratch without being tied to old decisions.

  • Eliminate legacy problems: Starting fresh means you can wipe out accumulated technical debt, fix longstanding issues, and build in better security and best practices. None of the old "hacks" or deprecated code need to be carried over into the new system.

  • Flexibility for new features: With a blank slate, you can implement the current product vision without being constrained by previous design choices. A rewrite can accommodate major changes or new features (even new business models) more easily than patching the old code.

Rewriting an entire codebase is a high-stakes, high-cost undertaking. Drawbacks include:

  • High cost and lengthy timeline: Rewriting an application is a slow, resource-intensive process. During this time you likely need to maintain the old system (bug fixes only) in parallel while the new one is built, often requiring a feature freeze on the old code. Rewrites also tend to take much longer than anticipated, increasing budget and schedule risk.

  • Risk of regressions and lost knowledge: The new system starts without the years of bug fixes and refinements that the old system accrued. Issues that were already solved in the old code can reappear, and new bugs will be introduced as well. Valuable domain knowledge embedded in the old code might be lost in a rewrite.

  • Uncertain payoff and competitive risk: Until the rewrite is complete (which might be a long time), users get no new benefits – meanwhile competitors or market needs may advance. If the project fails or misses its goals, the effort can end up worse than if you had iteratively improved the existing code.

Choosing between refactoring and rewriting is seldom a black-and-white decision. The right choice depends on your specific context. Key questions to ask include:

  • Current codebase health: Evaluate how bad the existing code is. If it's mostly sound with isolated trouble spots, incremental refactoring is likely sufficient. If the code is riddled with issues across the board or nearly unmaintainable, a complete rewrite might be the only way forward.

  • Technology and scalability needs: Evaluate if the current stack and architecture can support future requirements. An outdated platform or unscalable design might necessitate a rewrite, whereas a still-viable stack can often be improved with refactoring and incremental upgrades.

  • Timeline and resources: If time, budget, or manpower are limited, refactoring is usually the safer bet given a rewrite's high cost and longer timeline. Also consider your organization's risk tolerance – some prefer steady improvement over a risky big bang.

Keep in mind you can also combine strategies – for instance, rewrite one component at a time while refactoring others (an approach known as the Strangler Fig pattern). This incremental rewrite technique offers a compromise, allowing you to modernize parts of the system step by step without the risk of a full restart.

Modern development tools can greatly assist whether you refactor or rewrite. Graphite is one such tool: a code review and collaboration platform that works with GitHub to streamline how teams manage changes. Graphite uses a stacked pull request model, allowing developers to break large code changes into a series of smaller PRs that can be reviewed and merged one by one. This approach encourages continuous, incremental improvements – ideal for refactoring efforts. By reviewing bite-sized changes, developers stay unblocked and catch issues early, and any given update is easy to roll back if needed.

Beyond code review, teams also leverage automated quality tools to keep a codebase healthy. Static analysis platforms (like SonarQube) automatically scan for bugs and code smells, highlighting areas that need refactoring. Automated tests and continuous integration (CI) are equally crucial – they help verify that you haven't broken existing functionality. Practices such as feature flags or canary releases can mitigate risk during a rewrite by allowing gradual rollout of the new system. Using modern tooling and best practices reduces the risks of both refactoring and rewriting, enabling your team to manage technical debt proactively.

In the end, choosing refactoring vs. rewriting is about balancing trade-offs. Refactoring offers a gradual, safer path – it improves the codebase step by step with minimal disruption. Rewriting is a bold, transformative move – it can solve deep-rooted problems at the cost of time and risk. There is no one-size-fits-all answer; each team must weigh its goals, constraints, and the state of the code. Often a mix of both approaches – refactoring where possible, rewriting where necessary – yields the best result. With careful planning and the right practices, you can steer your software toward a cleaner, more maintainable future.

Built for the world's fastest engineering teams, now available for everyone