The myth of refactoring!
You don't always need tests to refactor code. You just don't. It's a myth.
... traditional developers and projects managers everywhere suddenly have heart palpitations.
I get it, it's a scary subject, especially when talking about product development. But it's also one of the biggest problems I see too, particularly in traditionally trained development. The inability to refactor without tests. This age-old misconception that you can't change code without first having something to test it against.
In my experience, tests are usually a luxury. Especially in the startup and agency world. I'd always advocate factoring in time to create tests, both unit and automation for products. But what if you don't have any, then how can you refactor code?!
It's a fear instilled in developers, product owners, and managers early on in their careers. A myth that holds back everyone, not just in the short term, but over the long term.
How to take a ball of mud and make it shiny. That's called Dorodango, it's definitely possible without breaking it.
And because of this myth, code actually gets worse over time. Developers end up making the smallest amount of changes to the code possible. Instead of cleaning up mistakes as they go. They're afraid to refactor in case they break something. And that's what turns well-meaning code into a spaghetti of crap. Code rot of duplicate functions, patches from multiple teams, bad dependencies, and other programming craziness.
Someone said to me a while back, that it didn't matter that the code was messy, it was just the way it was. In some ways I agree with this, I get the fact that deadlines need to be met. That can be managed. But blindly accepting that your product is a mess that it will one day be fixed, is a dream. It won't. It's more likely that you'll end up rewriting the whole application if you go too far down that route, and even worse, half of your business logic will be lost along with that process.
But apart from inexperience or lack of time, it's fear that stops us refactoring, which comes from inexperience and a perceived lack of time.
I'm empathetic to why this might seem scary. I've seen a lot of nightmare applications and projects, but I've never come across one that can't be refactored, that can't be tidied up with a few straightforward actions.
Step by step.
If you ever went to coding school, you've probably heard of all the traditional methods of refactoring, like preparatory refactoring. Probably coupled with the fact that you must have tests. Lots of different terms and analogies, but the process and ideas are usually the same. Step by step and methodical.
First test. And test again. Knowing what's broken already is especially important before you get started. Having a baseline to work from. Making sure that any changes won't introduce more defects. From functional tests to automation tests, try to build a picture of the current state of your product.
Don't forget to test at the end. That's a given, but sometimes for forgotten. Your refactor skills might be legendary in your world, but in everyone else's, you've just destroyed the code. And until proven otherwise, go with the presumption that it doesn't work.
Use the automated refactoring with your IDE. Essentially linting with auto-formatting. I haven't come across, yet, a case when this is a bad idea. It only ever seems a good idea and has saved me heaps of time over the years. From highlighting obvious mistakes in code to helping me see how everything is put together. Formatting using lint tools is a good way to see what needs to be done first. Especially if you're working with legacy code that didn't have any idea of consistency.
Plan and go forward methodically. Making sweeping changes without some kind of method to the madness is plain madness. So having a thoughtful plan in your head about how you're going to do it is important. If they are radically wide changes, then you need to break the tasks down. Understanding how the thing is put together is that first step. Move things, break things. Have some fun with the code in silo first, then make the changes required.
Always clean, not always better. This might be a confusing statement, but it's important to remember. We sometimes get sucked into complexity, as I've mentioned in the past. There is a myriad of ways to do things in code. React for example you can do petty much anything a hundred different ways, half the reason it usually goes wrong without good planning. You might have a clever approach or a new way of doing things, but ask yourself does it make it clean and simple. Which in turn you can argue makes it justifiably better.
And finally, commit often. Into a separate branch obviously. Not so you can backtrack if things go wrong, but so others can see what you're doing. Even if it's just you sense checking your own work.
All that in mind. This is just a quick opinion article and very light on details. There's so much more I can add here and different techniques. Many approaches dictated by factors such as system design and product life cycle.
What I'm trying to get across though, is that if you're methodical about what you do and how you do it, you're less likely to run into problems later down the line. If you have time and budget to create tests first, or create tests as you go, then please do. That's what makes refactoring truly easy and safe.
It's not about not worrying, but about speaking up and making thoughtful progress. Changes that can save money and time for everyone. It's about making products that are fun to work on, not complete nightmares. Products that work.
That are simple and effective.