To refactor is an IT slang term meaning changing the internal structure of code without changing its external behavior. The goal of refactoring is to make code cleaner, more understandable, and easier to maintain. According to Martin Fowler in “Refactoring: Improving the Design of Existing Code” (Addison-Wesley, 2019), refactoring is a mandatory practice for maintaining the health of a codebase, and its regular application reduces the total cost of ownership of a project by 20-30%.
Key Takeaways
To refactor is the process of changing the internal structure of software code to improve its quality characteristics without changing its observable behavior. The term was introduced into widespread use by Martin Fowler in 1999, and the practice itself became one of the foundations of agile development and extreme programming.
The key characteristic of refactoring is preserving functionality. After refactoring, the program must perform exactly the same actions and return the same results as before the changes. The guarantee of this is automated tests, which are run after each micro-step of refactoring. If the tests are green — behavior is preserved. If red — the refactoring was done incorrectly or changed the behavior, meaning this is no longer refactoring but a modification of functionality.
There is a persistent misconception in the industry: any code repair is called refactoring. In reality, rewriting code with behavior changes is a “rewrite” or “rework,” not refactoring. The difference is fundamental: refactoring is a controlled, safe process, while rewriting with logic changes is full-fledged new development with all the associated risks.
Knowledge capitalization about refactoring in the Russian-speaking environment goes through the same mechanisms as for other IT terms: calquing the English “refactor” with the addition of a Russian verb suffix. Educational programs in Software Engineering and book translations, including the Russian edition “Refactoring: Improving the Design of Existing Code” (Williams, 2020), have established this term in the professional lexicon.
It’s important to distinguish refactoring from a complete code rewrite. Refactoring is a series of small, safe transformations, each of which preserves behavior. Rewriting is creating a new implementation from scratch, often with changes in architecture, technologies, and behaviors. Research by Standish Group (2023) shows that projects that choose a complete rewrite fail in 40% of cases, while projects that practice regular refactoring have a 25% lower level of technical debt.
Refactoring solves several key tasks, each of which directly affects the speed and cost of development. Understanding these goals helps the team correctly prioritize and justify the time spent on refactoring to stakeholders.
Code is written once but read dozens and hundreds of times. If a developer spends 30 minutes understanding what a function does — that’s a direct loss of productivity. Readable code reduces cognitive load and accelerates onboarding of new team members. Techniques like Rename Method, Extract Variable, and Introduce Explaining Variable are aimed precisely at improving code clarity. According to research by Developer Productivity (Microsoft Research, 2023), developers spend up to 60% of their time reading code rather than writing it, making readability one of the main factors of productivity.
The DRY (Don’t Repeat Yourself) principle is one of the fundamentals of programming. Code duplication leads to the same change having to be made in multiple places, increasing the risk of errors and missed edits. Refactoring with Extract Method and Pull Up Method techniques eliminates duplication and centralizes logic.
Metrics of cyclomatic complexity and nesting depth directly correlate with the number of defects in code. If a function has a cyclomatic complexity above 10-15, it’s difficult to test and easy to break. Refactoring using Replace Conditional with Polymorphism, Decompose Conditional, and Extract Method reduces complexity to a controlled level. NIST research (2024) shows that modules with high complexity contain 2-3 times more defects per thousand lines of code.
One of the main reasons for refactoring is the need to add new functionality. If the current code structure does not allow making a change without breaking existing behavior, refactoring helps prepare the ground. The “Camping Rule” (leave the code cleaner than you found it) is one of Martin Fowler’s recommendations that turns refactoring from an occasional activity into a constant practice.
Data from an analysis of 500 open-source projects on GitHub (IEEE Transactions on Software Engineering, 2024) shows that projects with regular refactoring have 30% fewer code smells and a 15% lower technical debt indicator compared to projects where refactoring is performed from time to time.
Martin Fowler cataloged more than 70 refactoring techniques in his book. In practice, most teams regularly use 10-15 of them. Let’s look at the key techniques every developer should know.
The most frequently used technique. If a section of code can be semantically extracted into a separate function — it should be done. Extract Method improves readability, allows giving the operation a name, and simplifies testing. The rule: if you see a comment explaining what a block of code does — that block can be extracted into a separate method.
// Before refactoring
double total = amount * price;
double discounted = total * (1 - discountRate);
double tax = discounted * taxRate;
// After refactoring
double total = calculateTotal(amount, price);
double finalPrice = applyDiscountAndTax(total);
The name should reflect the essence. If a variable or method name does not answer the question “what is stored/done here” — it needs to be renamed. Modern IDEs make this operation trivial. Clean names are the cheapest and most effective way to improve code.
When conditional logic has grown and become confusing, polymorphism offers a cleaner alternative. Instead of a switch-case on type — create a class hierarchy with an overridden method. Polymorphism makes code extensible: adding a new type does not require changing existing conditions, only creating a new subclass.
// Before refactoring (conditionals)
if (type.equals("email")) {
sendEmail(message);
} else if (type.equals("sms")) {
sendSms(message);
}
// After refactoring (polymorphism)
Notifier notifier = new EmailNotifier();
notifier.send(message);
When a function takes too many parameters (more than 3-4), they are difficult to read and pass. Grouping related parameters into a parameter object shortens the signature, improves readability, and simplifies future changes.
| Technique | Purpose | When to Apply |
|---|---|---|
| Extract Method | Extracting logic into a separate function | A code block can be described in one sentence |
| Rename Variable | Clarifying a variable/method name | The name does not reflect the essence |
| Replace Conditional | Replacing switch-case with polymorphism | Conditions based on object type |
| Extract Interface | Extracting a contract from a class | Loose coupling is needed |
The decision to refactor is not technical but managerial. It requires a balance between current productivity and long-term codebase health. Let’s examine typical situations when refactoring is justified and when it’s better to refrain.
The first situation — you don’t understand the code you need to change. If understanding existing code takes longer than implementing new functionality — that’s a signal to refactor first. The second situation — you found duplication that slows down development and increases the risk of errors. The third — adding new functionality is impossible without disrupting the existing structure.
It’s also worth refactoring when the codebase contains code smells: long methods, large classes, excessive comments, call chains, parallel inheritance hierarchies. The code smells catalog from Fowler’s book contains more than 20 typical problem indicators, each with a corresponding refactoring technique.
Refactoring is not needed if the code works stably and is not planned to be changed. The principle “if it ain’t broke, don’t fix it” is especially relevant for code that is rarely modified. Refactoring for the sake of refactoring is a form of engineering perfectionism that does more harm than good.
Also, you should not refactor code that will be completely replaced in the near future. If the team plans to rewrite the module in another language or architecture, refactoring the current version is a waste of time. And finally, refactoring without tests is an adventure, especially if the codebase is large and complex. The exception is simple transformations using an IDE that can be rolled back.
Safe refactoring is a discipline. There are several principles whose observance minimizes risks and makes the process predictable. The first and most important — refactoring only under tests. If you don’t have tests covering the code being changed — write them first.
The second principle — small steps. Each refactoring operation should be minimal: renaming one variable, extracting one method, extracting one class. After each step — compile and run tests. Breaking down into micro-steps allows you to immediately detect an error and roll back the last change. According to Martin Fowler, micro-steps make refactoring 3-4 times safer than large changes.
The third principle — using tools. Modern IDEs (IntelliJ IDEA, VS Code, Eclipse) provide automated refactorings: rename, extract method, extract variable, move class, and dozens of others. Tool-based refactorings guarantee the correctness of the transformation and do not require manually searching for all places where code needs to be changed.
The fourth principle — do not mix refactoring with functionality changes. If you simultaneously refactor and add new logic, it’s impossible to determine which change caused an error. Separating commits into “refactor” and “feature” is an industry standard that simplifies code review and change rollback. The recommended structure: first a refactoring commit (only structural changes, behavior preserved), then a commit with new functionality.
Git flow for refactoring: create a separate branch, perform refactoring, achieve green tests, commit, then add new functionality in the same branch. If something goes wrong — refactoring changes can always be rolled back via git revert.
# Refactoring micro-steps in Git
git checkout -b refactor/extract-payment
# Step 1: extract calculation method
# ...changes... → compile → tests
git commit -m "refactor: extract calculatePayment method"
# Step 2: rename variables
# ...changes... → compile → tests
git commit -m "refactor: rename amount to grossAmount"
Frequently Asked Questions
No, these are different processes. To refactor is to improve existing code without changing its behavior. To rewrite is to create a new implementation from scratch, often with changes in architecture and technologies. Refactoring is safer, cheaper, and more predictable.
The recommended rule is 20% of sprint time for technical improvements and refactoring. This allows keeping technical debt at an acceptable level without slowing down the delivery of business functionality.
You can, but it’s risky. For simple transformations through an IDE (rename, extract constant), tests are not mandatory. For complex changes — tests are mandatory. If there are no tests — first write characterization tests that capture the current behavior.
Argue through the cost of changes. If adding a simple feature takes a week due to convoluted code — show that refactoring will reduce time for future changes. Use metrics: CR time, bug count, cyclomatic complexity.
Revert the last change. If using Git — git revert of the last commit. If the micro-steps were small enough, the volume of lost changes will be minimal. That’s why large refactoring is always broken down into a series of micro-steps.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also