A Retrospective on Refactoring a 3-Year-Old Spring Project (Including Adding Tests)
The first thing I was assigned after joining the company was a three-year-old Spring project. All the business logic was crammed into controllers, and test coverage was 4%. Here’s what I learned while refactoring it over six weeks.
1. If you change the structure first without tests, you’re bound to cause trouble. I started by using characterization tests to pin down the current behavior. This took the longest, but it was the most important step.
2. Don’t try to change everything at once. I split just the work of moving logic into the service layer into 20 PRs. It made things easier for reviewers and made rollbacks simpler too.
3. Just giving domain logic a name can change the code. Once I gave method names to meaningless chunks of if statements, bugs started to become visible.
The conclusion is that refactoring is more a matter of patience than technical skill. If you’re in a hurry, don’t do it. Seriously.
9 answers
Agreed, especially #1. Changing the structure without tests is like walking through a minefield blindfolded.
Do you have a source? Characterization tests are from Michael Feathers' book, right?
I agree with point 3. Giving a method name to a chunk of if statements is genuinely effective. While naming it, you often hit moments like, "Wait, these two are the same condition?" That's when I found two condition-inversion bugs while doing Extract Method.
Oh, I didn’t know this. This is the first time I’ve heard of the concept of characteristic testing, so I should look it up.
Same here. I'm in exactly that situation right now, so I'm starting cautiously—I'm curious how you decided on your criteria for splitting PRs. By feature? By layer?
20 PRs over 6 weeks is pretty well broken down—the teammates who reviewed them must have had a rough time too haha. How did the reviewers actually react? I did something similar before and got hit with "When is this going to be done?"
I don't agree. Framing the conclusion as a matter of patience feels like a vague hand-wave. Isn't agreeing on scope and having a rollback plan more important than patience? Without those, patience alone is just overtime lol
Well, I’m a bit skeptical. I agree we should start by locking down characterization tests first, but honestly, isn’t 6 weeks for a legacy codebase with 4% coverage a lucky case? At that scale, the tangled dependencies alone are hell for us—there’s no way it wraps up in 6 weeks.
Good post, but one quibble: “If you’re in a hurry, don’t do it” ultimately means that if the organization won’t give you time to refactor, you can’t do anything. But what I’m most curious about in this post is how you convinced them to give you that time, and that part is missing. I suspect that’s more central than the technical discussion.