Refactor, not rewrite
Last October I wrote TypingMania, a type-along game on web based on TypingMania Odyssey of SightSeeker studio. It’s not quite polished (the menu screen could use some serious work), but I am happy with it (well, I wrote it because I want to use it).
That was my first Javascript project in a very long time (~6 years), and of course the web changed a lot in the recent years. If you reviewed the code you will see many old style code, callback spaghetti and many synchronising boolean that look like some bad shot at thread safety, of which Javascript doesn’t have one.
Even though UI is always the most time-consuming part in any game – or application, really – the most fragile part of this piece of code is audio processing. It should be able to show loading indicator to tell how much has it buffered, it must have perfect time synchronising to the type-along part, and it must run in browser.
At first I use SoundJS and PreloadJS to handle all my sound need, and it bundled together, PreloadJS can preload sound with progress indicator, and it seem to do reasonably well. Well, reasonably well. Turns out that with sound preloading it doesn’t report any progress (and I came to realisation much later that it just <audio>
tag preloading mechanism which doesn’t report progress at all)
As far as I know, Javascript has been standardised recently, and its API is not necessarily bad. In fact, if you don’t need to support old browser (and all browser now comes with auto updater, even IE, so there’s not really any reason to do so), you are only going to need JS libraries that do sophisticated things like Single-page app. You aren’t really needing jQuery and such any more. (Well, you might want underscode.js though)
So I want to pluck the SoundJS and PreloadJS out and write my own tiny wrapper around XHR2 and Web Audio API/HTMLMediaElement directly. But the problem is that the most complicated part of the code is actually the Viewport (scene graph of DOM element) and the loader/sound callback spaghetti. So I think I should do complete rewrite.
And I did. And it is totally wrong. Here’s why:
You might have read on Joel on Software blog or anywhere else that throwing away old code is bad idea because you are throwing away years of bug fixes. But this code is very small that it doesn’t really apply.
The real problem is that, in old project I start with simple hardcoded song and typing text to test if the idea work, and build wrappers around wrappers and utilities and graphics until it become this little game. Actually, if you didn’t really build something this way, you are doing it wrong. Doing it this way ensure that, at every step in your development, you have a working code that you can see that is still working!
This is very important. I tried full rewrite, and I want to abandon it. I was thinking of starting another rewrite when it hit me: If you don’t even finish a rewrite, another one would face the same fate! So instead I refactor old code.
I have successfully remove PreloadJS and SoundJS and replace with simple libraries I created. Even though the new preloader system is based on Promise, I still wrap it into callback-based so I can drop into old code and confirmed that my new code is working correctly. Of course, debugging code this mess is painful, but it’s still much more pleasant than doing full rewrite.
Of course, choose the right tools. I have somewhat modularised the old monolithic codebase into multiple AMD JS module. The problem is, I know no editor that is AMD-aware, so none of them can do this automatically. Doing by hand is quite painful as you would have to replace all existing occurrences manually. Of course, the fact that Promise tends to swallow exception doesn’t help.
But at least it’s done, and it’s still usable (in fact, it does nothing different than old code, only more manageable this time!). I only spend a few hour on it (not counting the time I write the Promise library myself), and it’s much more productive than doing rewrite.