What You Need To Know To Refactor Strange Code
Practical techniques for safely improving legacy code without breaking everything.
Hey there,
I recently spent days untangling a class getting data from a database that seemed designed to confuse anyone who looked at it.
The code worked. Technically.
But "working" isn't the same as "maintainable."

All the checks in the code were weird negations.
Instead of just saying if this equals that it’d say if not this equals not that.
Like why do that? What purposes does it serve?
Things were named without enough detail as well.
Design choices were unexplained.
And due to the complexity of some of the queries, one of the main concerns we had was: “how do we ensure we don’t inadvertently break something when we touch this?”
We recently explored writing code that doesn't haunt you from the get-go.
Now, let's dive deeper into the art of transformation: refactoring.
Refactoring code means to take what you have and improve it.
It isn't about rewriting everything from scratch, but about the process of improving code while preserving its core functionality.
It’s a bit like buying an old house.

There’s so many things to change.
The living spaces are cramped, and you want to open them up to be more modern. The kitchen is dreadful. The bathroom is yellow for some reason.
You take what you’ve got and improve it.
And sometimes, people are still living in it, so you’ve gotta be extra careful.
You need a strategy and a careful approach.
The Psychology of Refactoring
Most junior developers approach refactoring with fear.
That's natural. You're touching working code. What if you break something?
Many experienced developers also approach it with fear.
It’s a delicate operation, and you never quite know what you might find that completely explodes your estimates.
But here's the truth: Not refactoring is more dangerous than refactoring carefully.

Technical debt is like interest on a loan.
The longer you wait, the more expensive it becomes.
Each quick fix adds complexity.
Eventually, the system becomes so tangled that even simple changes become risky.
So how do we tackle such a beast? Let me show you how I approached that class I was untangling recently.
Your Refactoring Strategy: 3 Critical Approaches
Refactoring isn't about wild changes.
It's about calculated, low-risk improvements.
Let’s consider the class I mentioned earlier.
It had to do with creating documents and templates for these.
The class handled way too many things, like:
Fetching documents
Fetching metadata
Setting up multiple queries
Logging details related to the document
Doing all kinds of things with many other entities
Instead of rewriting everything at once, you might:
Extract metadata fetching
Ensure queries are split in a way that makes sense
Implement a logging service
Ensure that it only affects entities that make sense
There is no single technique to do all of this.
Instead, you should apply a strategy combining multiple approaches:
Identifying opportunities
Applying Safe Transformation principles
Using the correct tooling
Identifying opportunities
The first step of any refactoring process is to identify potential candidate sections of code.
As you maintain a system, you likely end up doing a task that makes you stop.
It makes you think: “huh, that seemed a lot more tedious than it had any right to be.”
And that’s a key indicator that something there isn’t quite working.
It might be complex, or just hard to maintain.
Take the time to stop, think about the reason why, and try to come up with a way to change it.

Consider some of these questions:
Can I explain what this code does in one sentence?
Would another developer understand this quickly?
How difficult would it be to modify this code?
Are there multiple reasons this code might need to change?
If it’s a large undertaking: suggest it to your team lead.
Even if it ends up on the back burner it still shows expertise and sense of ownership.
Here are some common red flags to look out for:
Functions longer than 25 lines
More than 3 parameters in a function
Repeated code blocks
Complex conditional logic
Tight coupling between components
Lack of clear separation of concerns
Applying Safe Transformation principles
One way to ensure refactoring changes are calculated and low-risk is adhering to a set of principles for safe transformation:
Always have a comprehensive test suite before starting
Make small, incremental changes
Run tests after EVERY modification
Use version control to track changes
Have a rollback plan for each larger refactoring
It’s an iterative process.
I dearly hope your system has a usable, comprehensive test suite.
Otherwise, grab your trusty AI buddy and start developing one! And it’s not just the AI that will help you.
Using the correct tooling
Modern IDEs and tools can automate much of the refactoring process.
Fully utilizing the tools you have available is an essential part of a professional process.

You accomplish more, faster, at a higher quality.
Here are some tools you really should have, and make use of:
Static code analyzers
Automated refactoring tools in your IDE
Linters specific to your programming language
Code coverage tools
Continuous integration platforms
Remember: Refactoring is a skill.
You'll get better with practice.
And you won’t get there without feeling the pain.
I know I sure felt that pain the other day with the document fetching.
It never really goes away.
But that class now? Much more maintainable, clear and focused.
With a careful approach, you’ll handle it better each time.
Start small. Feel the pain. Learn from it. Keep pushing forward.
PS... Enjoying this newsletter? Consider referring it to a friend who's also navigating the start of their career! Each new subscriber helps us create more in-depth newsletters.