I'm not a professional, but you seemed to be using refactor and rewrite interchangeably. I had the impression they were different and you seem to be applying all the bad ideas of rewrites to refactors.
“rewrite” is in sense of “I don’t even want to understand it, going to just delete it, and implement anew”,
VS
“refactor” is in sense of “I had to spend quite a while trying to understand this code, and I think it can be improved, so that the next developer doesn’t have to spend this time.”
I like to refactor in the big and to rewrite in the small.
In the big, the n-th rewrite will be just as constrained by how much we can keep in our head at the same time as the original attempt. Refactoring essentially means offloading to the computer what you already have in a computer-readable format. What an opportunity!
What I mean with rewriting in the small is taking an existing logic-wart and, while trying to decipher it, taking notes in the form of a rewrite. After finishing the first approximation you take a long, skeptical look at both versions, "is that simple, elegant thing really doing the same?" and then you see the additional corner cases (and/or bugs, sometimes you'll never know) in a clarity that would be impossible to achieve just looking at the original code. It's mostly a reading tool. Sometimes I will trash the rewrite, but often the new version, with the non-obvious bits added will be far more readable than the original because it was written from the perspective of a reader with little knowledge of what the code should do, not by someone deeply immersed in the problem space.
Refactoring/Rewrites is the MOST rewarding task that can be achieved with both legacy and new code. I do it from the start of my career (when I don't even know the name of it).
And that include, regulary, rewriting to different langs (like, for example FoxPro -> C#, C# -> Python -> F# -> Swift (aborted) -> Rust (noob) -> Rust (now I know rust, almost!) <-- yes, the same project, years on it.
Exits a lot of FUD against this. Is risky? Is hard? Can be messed up? Everything is staked against you? Yep LIKE ANY OTHER CODE ACTIVITY.
----
Things that help in make this a success (assuming, of course, an average, half-decent developer. But I have done it when I not even know anything, so who knows if it work?):
CRITICAL:
- Have source control
- Have a task manager (mine: pivotal)
- Split stuff in small tasks (couple hours max)
- Priorize to know the data(schemas), the business REAL priorities (because all the time, everything will be declared of imperative importance). The code? Less so. If the code is good then you are not considering this at all.
- This can be done in the small or in the large. So, a full rewrite can be totally done in hours. If all is so tangled this is not true, then the code is beyond salvation, REWRITE.
Nice:
- If possible, deployable code in one go (of the old code)
- The database (or data in general) is kind of easy to grasp (if not, all is a mess, REWRITE). With a good database, rewrite is far more profitable than refactoring messy logic.
- Have good test that can be used to validate it? Great. I rarely have that luxury.
To become good:
- Rewrite/Refactor is a muscle. The more you do it, the better you become.
- Do A LOT of MICRO side-projects or experiments. Trash them, rewrite them, explore them. Make your mind used to it (note: this is not "ruin my life, only code yo!", is to start a major idea with a micro side project before commit bigly)
Everyone know the second time your code is better. Imagine if you do 5 rewrite in successions. That code will be celestial!
- Be confident in being able to read code. Read code. And read more. Much more if is a new lang.
- Ask question, and ask the same question, differently.
- Pick a project that you can do in your sleep (mine: Pseudo micro ORMS) and start with that when new lang or framework. In a side project. Must be of small size (few files, at most)
- If change lang, framework, be aware that some are BETTER than others. Learn to know why and when start using it.
- Dedicate a few minutes learning useful stuff.
- Do the HARDEST stuff first, at least, a significant piece of it. If the hard is done, the rest is easier!
- Have a solid selection of companion tools that you RARELY need to change (mine: Sqlite, Postgres, nginx, ubuntu (deploy))
The MORE messed up, the BIGGER is the payoff:
- Not have tests or are all useless? Start writing test and all is tangled? Rewrite
- Like a stupid idiot you are 4 months into a refactoring (doing stuff properly and all) and all paths lead to doom? Rewrite (you probably feel it 3 months ago. Idiot me)
- You testing (manually, auto, whatever) take X times than write code? Rewrite
- The customer is on fire, nothing work, all things are delayed? Rewrite
- Everyone in the path of requirements are even more lost than you? Rewrite
---
Surely exist some stuff that is conveniently ignored in this list: Have a customer/team that accept the rewrite.
Look, if the customers/boss/team is THAT bad, then NOT MATTER what you do, you ARE SCREWD.
But I'm lucky, and have worked in situations with serious trouble (but decent humans around, even if can't help much and I must figure a lot on my own) have tell me that people are more accepting of rewrites than some could think. Even delayed projects can be tolerated if exist a good flow of improvements.
P.D: This is my experience in my sector, and the few I have done in a mid-size startup...
When a developer says "I am going to rewrite ..." you have to question why? If you were not doing this "rewrite" then what else would you be doing? Refactor is usually a term used to minimally modify legacy code to allow new functionality to be added.
Rewriting implies rewriting legacy code (regressions) and wasting time. If a developer says "it's the only way" then you need to be sure you can trust their judgement.
I'm of a similar mind but tend towards avoiding rewrites, mostly because I find refactoring more efficient.
Interesting choice of phrase. What would you say is the difference between refactoring and rewriting, assuming we’re talking about keeping the externally observable behaviour of the affected part of the system the same in each case?
This shouldn't be happening in a sufficiently tested or sufficiently understood system
Perhaps, but as systems and the teams that develop them evolve over time, it is all but inevitable that some knowledge and understanding are lost. You’ll never have perfect documentation. You’ll never have tests for every possible case. You’ll never have an unambiguous and readily located comment for every subtle detail in the code that someone seven years ago spent a couple of hours thinking about before they put finger to keyboard, when three other people have since refactored that code, generalised its interface, and fixed a performance glitch in a dependency so some aspects of the original design became unnecessary. Preserving knowledge and understanding as well as possible is one of the challenges facing any team with a long-lived system.
Those losses are a form of technical debt, and as it accumulates, two things happen. First, the original code becomes harder to maintain and develop. Second, the original code becomes more risky to rewrite. So unfortunately, there’s a fairly high probability in practice that a part of a system that has become sufficiently difficult that a large-scale rewrite is being considered will also be a part of the system where the current development team have incomplete understanding of the current implementation.
If you can't figure out a peicemeal way to refactor what you have into what you want you want, you have no business discussing what a rewrite should entail.
But how do you do a piecemeal refactoring exercise if, say, you’re moving to a new programming language? I had to rewrite a large part of a relatively complicated web UI a few years ago, like for like. Although it had been working reasonably well to that point and the design was quite clean and had stood the test of time, it was built around a Java applet. That design made sense when we were first implementing that system, at a time when JS was slow and many features we take for granted in browsers today weren’t yet widely supported. It was less helpful when the great and the good of the browser world decided that Java applets were a liability and they were going to make them harder and harder to use until eventually they killed them off completely. Now, we did have a clean, careful interface between the applet and the various other parts of the page that were written in JS, so we only had to redo that part of the system. However, we had no choice but to reimplement everything the applet did, using alternatives like JS and SVG, starting from scratch. This is the sort of scenario I was talking about before when I mentioned changes in the foundations of a system.
I would argue that rewrite is slightly more difficult then refactor. Refactor means, after all, making the current system better while rewrite would mean no update/change/feature on the current system and more pressure to get it done.
I would herby say that a good team with experienced people will succeed in both cases while an less experienced team will probably have a higher chance of doing a refactoring then a rewrite.
I like refactorings better. It allows me to optimize a system slow and steady.
I like rewriting better if you don't need to support the old env while doing the rewrite and having expierence in the old system.
reply