In the middle of a major change to the Leaf type system I encountered the refactoring demon. It reared its ugly little head chanting, “you’re doing it wrong, all wrong!” It was probably right, but there was no way to know until I was done. Seeing my determination did not sway the demon. Instead it started pointing out all the other changes I should be making.
Avoiding the big refactor
First off, problems like these are why I avoid refactoring that requires changing a lot at once. I much prefer coming up with small steps, where the test suite can be run after each change.
Alas, sometimes a big change is required. My change in Leaf was about how parametric types are handled. Trying to implement a feature I hit a block. There was just no reasonable way to introduce the feature without either making a mess or making a large change to the type system. I deliberated until I convinced myself the change was the right thing to do.
Just a few hours into these changes I noticed other problems with the code. A lot of things started to crop up, things that could be done better. Things that would make everything easier. I even started doubting whether my approach was correct at all. Already demotivated, I came to another dissatisfying realization: my type identifier code was too hard to read and highly redundant as well.
Stop, think
I had a few basic options. I could carry on and come back to the issues later. Or I could roll back my current refactoring and do the other reductions first.
Carrying on with the parametric refactoring had these considerations:
- I had already invested a lot of time in this refactoring. It was something that had to be done anyway.
- It was a clear goal to work towards and would implement a concrete feature.
- A lot of the work would be discarded by the next refactoring.
- I was putting in lots of strange hacks to work around issues the other refactoring would fix.
- Some of the tests had to be commented out since a proper solution could not be found.
- There was technically nothing wrong with this refactoring.
Stopping this effort and doing the reductions first had these considerations:
- It would significantly reduce the work I needed to implement the parametric feature.
- The type identifier code would be a lot easier to understand, and could produce cleaner rules for the type system.
- The code would be significantly reduced.
- Though smaller it was also a rather large change.
- It didn’t include a semantic change to the system, thus would involve fewer tricky decisions.
At this point it wasn’t a matter of whether either refactoring should be done; I was convinced that both of them were needed. It was just very problematic that the work involved in one would significantly alter the other. Indeed, the parametric refactoring would be a whole lot easier if the reduction was done first.
Punching through
Neither solution was great. These frustrating realizations can be depressing and lead to a code paralysis where nothing gets done. I took a step away from the machine for a while. I built a small shelving unit for my desk; wood presents a much simpler set of challenges than compiler code.
After more reflection, I decided to continue with the parametric refactoring. It offered a real new feature in the system, as opposed to a pure refactoring. This was my primary consideration. I did not wish to abandon the work done so far to go traipsing about in refactor land.
To avoid losing too much time on code that would become redundant, I decided to simplify my effort. I’d take the questionable approach of hacking it to completion. I just turned a blind eye to any other issues I saw in the code. I used a few too many if statements. I commented out a few unit tests. I left the type identifier code in a worse state than when I started.
I managed to complete the change. The new feature even functioned as desired without further work. That made me quite happy. Sure, I left a bunch of holes in the code, but the high-level language tests all worked.
Now I just need to do the reduction refactoring. I presume the little demon is waiting for me.





