Before You Refactor, Answer These 5 Questions
Before we start
Before You Refactor, Answer These 5 Questions

Before You Refactor, Answer These 5 Questions
Before we start
I share short daily software engineering lessons on Telegram: https://t.me/ShahkhajehCodeAcademy
I also share shorter thoughts and discussions on LinkedIn: https://www.linkedin.com/in/mohamad-shahkhajeh-7675541b3/
One common mistake developers make is this:
They think every refactor automatically makes the code better.
But that is not always true.
Sometimes refactoring really improves the code.
It makes the code easier to read.
Easier to test.
Easier to change.
Easier to maintain.
But sometimes refactoring only makes the code look cleaner while making the system more risky.
That difference matters a lot.
Because refactoring is not just cleaning up code.
Refactoring means changing the structure of code without changing its expected behavior.
And if you do not understand the behavior, you may accidentally break it.
Clean-looking code can still be dangerous
I have seen this happen many times.
A developer looks at a piece of code and says:
“This is messy. We should refactor it.”
And sometimes they are right.
The code may be hard to read.
It may have duplication.
It may be tightly coupled.
It may be doing too many things.
But then the developer starts changing it too quickly.
They move logic around.
Create new classes.
Rename methods.
Remove conditions.
Simplify flows.
Change boundaries.
Extract abstractions.
From the outside, the code may look better.
But then something strange happens.
A bug appears.
A rare edge case breaks.
A business rule disappears.
A production behavior changes.
A test fails.
Or worse, no test fails, but users notice the problem later.
Why?
Because the system was not fully understood before the refactor.
That is the risk.
Refactoring without understanding can turn messy working code into clean broken code.
Question 1: What behavior must this code preserve?
Before changing the structure, you need to understand the behavior.
Not just the obvious behavior.
The real behavior.
What should happen when the input is valid?
What should happen when the input is invalid?
What happens when data is missing?
What happens when an external service fails?
What happens for old users, existing records, or legacy flows?
What business rule is hidden inside this condition?
What weird edge case does this code currently handle?
A lot of old code looks strange because it grew around real production cases.
Some of it is bad.
Some of it is accidental.
But some of it exists because something broke in the past.
If you remove that without understanding it, you may reintroduce an old bug.
So before you refactor, ask:
What behavior must stay exactly the same?
Question 2: Is there a test that proves nothing important broke?
Refactoring without tests is not always impossible.
But it is riskier.
A test gives you a safety net.
It lets you change structure with more confidence.
It tells you whether the important behavior still works.
Before refactoring, ask:
Is there a test that protects this behavior?
If not, maybe the first step is not refactoring.
Maybe the first step is adding a characterization test.
A test that describes how the system currently behaves.
Not how you wish it behaved.
How it actually behaves today.
That kind of test can protect you while you improve the structure.
Because without tests, you are often relying on memory, confidence, and manual checking.
That is not enough for risky code.
Question 3: What is the real problem here?
Not all messy-looking code has the same problem.
Sometimes the problem is readability.
Sometimes it is duplication.
Sometimes it is coupling.
Sometimes it is too many responsibilities in one place.
Sometimes it is performance.
Sometimes it is unclear naming.
Sometimes it is lack of tests.
Sometimes it is just personal taste.
This matters because different problems need different fixes.
If the issue is naming, you may not need a new architecture.
If the issue is duplication, you may not need five new abstractions.
If the issue is coupling, moving code around without changing boundaries may not help.
If the issue is missing tests, refactoring first may make things worse.
Before refactoring, ask:
What problem am I actually solving?
Because if you cannot name the problem clearly, you may create a solution that only feels better.
Question 4: What risk does this change create?
Every refactor has risk.
Even small ones.
Maybe another part of the system depends on this behavior.
Maybe an old flow uses this function in a way you did not expect.
Maybe changing the order of operations changes the result.
Maybe a cache, queue, event, or database transaction depends on a detail that is not obvious.
Maybe a frontend screen, API consumer, or background job expects the old behavior.
This is why senior developers do not only ask:
How can I make this cleaner?
They also ask:
What can this break?
Who depends on this?
How will we know if it breaks?
Can we ship this safely?
Can we split this into smaller steps?
Do we need logs, tests, or a feature flag?
A refactor is still a change.
And every change has a blast radius.
Question 5: Will this make the next change easier?
This is one of the best questions before refactoring.
Will this actually make the next change easier?
Or will it only create a new structure that the team needs to understand later?
Sometimes developers refactor for beauty.
But good refactoring should reduce future cost.
It should make the next bug easier to find.
The next feature easier to add.
The next test easier to write.
The next developer’s job easier.
If the refactor does not make future work easier, it may not be worth the risk.
Clean code is not about looking elegant.
It is about making useful change safer.
Junior, mid-level, and senior thinking
Junior developers often fear messy code.
They see something hard to read and feel stuck.
Mid-level developers sometimes rush to clean everything.
They have learned patterns, structure, design principles, and refactoring techniques.
That is useful.
But it can also create a new problem:
Changing code before understanding it deeply enough.
Senior developers usually move differently.
They first try to understand:
What behavior exists?
What risk exists?
What cost exists?
What change is actually needed?
What should be protected before touching the structure?
That is the difference.
Not more confidence.
Better judgment.
Sometimes the best refactor is no refactor yet
This is important.
Sometimes the right move is to refactor.
Sometimes the right move is to add tests first.
Sometimes the right move is to improve names.
Sometimes the right move is to isolate one small behavior.
Sometimes the right move is to document a business rule.
Sometimes the right move is to leave the code alone until you understand it better.
Not every ugly piece of code needs immediate refactoring.
Some code is ugly and dangerous.
Some code is ugly but stable.
Some code is ugly because the business process is ugly.
Some code is ugly because nobody has yet understood the right boundary.
The job is not to make every file look nice.
The job is to make the system easier and safer to change.
Final thought
Refactoring is a powerful tool.
But it is not magic.
It does not automatically improve the system.
Used well, it can reduce complexity, improve clarity, and make future changes safer.
Used carelessly, it can turn hidden behavior into hidden bugs.
Before you refactor, ask:
What behavior must stay the same?
What test protects it?
What problem am I solving?
What risk does this create?
Will this make the next change easier?
Because refactoring without understanding is not engineering maturity.
It is just a cleaner version of risk.
메타데이터
- post_id
- 05dfde3a8599
- slug
- before-you-refactor-answer-these-5-questions-05dfde3a8599
- url
- https://medium.com/@mohamadshahkhajeh/before-you-refactor-answer-these-5-questions-05dfde3a8599
- canonical_url
- https://medium.com/@mohamadshahkhajeh/before-you-refactor-answer-these-5-questions-05dfde3a8599
- author_url
- https://medium.com/@mohamadshahkhajeh
- status
- ok
- fetched_at
- 2026-07-22 15:40:29