Badoo’s MobileWeb project started in early 2012. Due to the initial pace of development, coding conventions and modularisation weren’t given priority. Most of the ‘modules’ lived inside a global object. As the project grew it became difficult to maintain and bugs became harder to track down. So after much internal discussion we found an opportunity to convert our codebase to use AMD modules (RequireJS). I’m here to explain how we used the power of regular expressions to speed up our migration process.
Below is a snippet of the kind of code we were working with.
As you can see, everything was ‘global’, called directly and had no tracking of dependencies. Fortunately, we had a couple of good things going for us:
- The project structure was modular (e.g
- In most cases each file corresponded with its object name.
Initially we attempted to do this manually, but this had the following drawbacks:
- Merge Conflicts - We can’t stop adding features to the original project, so merge conflicts every time there is an update.
- Bugs - The process was error prone. Humans are bad at repetitive tasks and we often missed objects or made typos which made debugging frustrating, because a huge chunk of the project needed to be converted before we could boot up the app.
- Time - It took us time to convert each file, and we had 400+ files to do.
I realised that this is something that must be automated. First I tried to experiment with Esprima, but that turned out to be time-consuming and reminded me of this XKCD comic:
What I needed was a quick solution that semi-automated the process.
So I set out to make a tool which does the following:
- Uses CodeMirror as the editor.
- Auto-AMDfies the project and lets us know of any errors using JSHint.
- Requires minors tweaks to get the pasteable result.
And I’m happy to present the solution!
Breakdown of the solution
First we need to have a method which lets us generate
define() blocks, given the dependencies.
Fairly straightforward, so next we need an array mapping search terms to their replacements.
So that makes up all the rules we need to follow for a global search/replace.
Note: The order of these things is important (e.g. replace
B.Viewsfirst followed by
And now for the magic:
This will iterate over the script, doing replaces one by one. Then we concatenate it with
defineHelper.getBlock() to get the result.
It took me a day to code up this tool and it made our conversion process an order of magnitude faster. Using this we migrated two projects and their unit tests within a few weeks. It now allows us to have proper modules in the code, manage circular dependencies and generate subsets of the application. This all adds up to make development much easier.
If you have any feedback please drop it in the comments below.