Skip to content

Refactor, not rewrite

Last October I wrote TypingMania, a type-along game on the 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 recent years. If you review the code you will see many old style codes, 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 of any game – or application, really – the most fragile part of this piece of code is audio processing. It should be able to show a 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 the browser.

At first, I used SoundJS and PreloadJS to handle all my sound needs, and from the same maker, PreloadJS can preload sound with a progress indicator, and it seems to do reasonably well. Well, reasonably well. Turns out that with sound preloading it doesn’t report any progress (and I came to the realisation much later that it is just a <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 an old browser (and all browsers now come 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 don’t really need jQuery and such any more. (Well, you might still want underscore.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 a 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 a bad idea because you are throwing away years of bug fixes. But this code is so small that it doesn’t really apply.

The real problem is that, in the old project, I started with a simple hardcoded song and typing text to test if the idea worked, then built wrappers around wrappers and utilities and graphics until it became this little game. Actually, if you didn’t really build something this way, you are doing it wrong. Doing it this way ensures 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 a 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 the old code.

I have successfully removed PreloadJS and SoundJS and replaced them with simple libraries I created. Even though the new preloader system is based on Promise, I still wrap it into callback-based code, so I can drop into the old code and confirm that my new code is working correctly. Of course, debugging code in this mess is painful, but it’s still much more pleasant than doing a full rewrite.

Of course, choose the right tools. I have somewhat modularised the old monolithic codebase into multiple AMD JS modules. The problem is, that I know no editor that is AMD-aware, so none of them can do this automatically. Doing it by hand is quite painful as you would have to replace all existing occurrences manually. Of course, the fact that Promise tends to swallow exceptions doesn’t help.

But at least it’s done, and it’s still usable (in fact, it does nothing different from old code, only more manageable this time!). I only spend a few hours on it (not counting the time I write the Promise library myself), and it’s much more productive than doing a rewrite.