One of the interesting challenges working on Everyone’s Timetable is that it’s a live application with a server backend. That means that any one of the following can cause a very serious problem:
- A change to the Android app that’s not compatible with the PHP server code
- A change to the PHP server code that’s not compatible withe the Android app
- A change to the PHP server code that’s not compatible with the MySQL database schema
- A change to the MySQL database schema that’s not compatible with the PHP server code
- A loss of real user data in the MySQL database.
It’s hard enough to promote such a limited-reach mobile app. If on top of that the app stopped working all of a sudden, or the users found their accounts deleted or their data missing – they would probably leave complaining and never give the app another shot.
But I need to do development, which means I need to touch all of the points listed above – the app, the server code, the database schema, and the data in the database. How do I do it without risking a catastrophic bug?
This may be something that web people do all the time, I imagine it’s quite a common problem, but it’s the first time I’ve run into it so I will write up my experience. Since I am a beginner with online services – perhaps my experience will serve other beginners as well.
1. Version Control
First of all you need version control. I chose to use Git for this project mainly because it’s time for me to learn it. For many reasons it’s a dumb system for small developers who aren’t already Git experts but its popularity is undeniable, so might as well get used to it. Whatever I’ve done with it you should be able do in SVN just fine.
1.1 Release Tags
I have one repository for my Android code, and one repository for my server PHP code. At first I’ve done all the development in master. Then I got to the first release, 1.0. For this I created a tag, since a branch seemed unnecessary.
Now with the release properly versioned I had to set up the development branches. I chose to create a “devel” branch in both repositories.
You will find a thousand pieces of advice online about how to do branching “properly”. I am following neither. You can pick and choose what advice you want if you’re so inclined, in this guide I’ll only explain how to make the simplest development setup.
The idea is this: after the first release:
- All development work will be done on the devel branches, unless some major emergency happens in master and then I’ll deal with it as a one-off.
- The code in the client devel branch will only talk to the code in the server devel branch.
- The code in the server devel branch will not touch the production database.
- When it’s time for a new release (and not earlier) a merge will be done from devel to master.
Read on to see how I actually managed to accomplish that.
2. Development database
Bad code can cause not only cosmetic problems (a crash, a failed request) but more serious data corruption problems. The database could get corrupted because of bad client requests, or a mismatch between the client and server, or bad server code. All of that is fair game during development and none of it is an acceptable risk for production.
So just as we need separate branches for the code – we need a separate database for the data. Not a separate server or anything, just a separate database on the same server.
I didn’t have a script to create the original database so I had to relearn how I created it, and do the same for the new one. It wasn’t very hard, just a CREATE DATABASE and a couple of GRANTs. Then to populate the devel database (with the schema and data) I did something like:
mysqldump -u root -p et > et.dump mysql -u et -pMyPassword etdevel < et.dump
Notice that I am using the same user (et), I don’t see a problem with that.
If in the future I decided I need more test data or more current data – I could simply rerun those two commands.
3. Server: two copies & post-checkout hook
The best idea I could come up with for the code on the server was to have two copies of the server repository in two directories. The release code in the master branch checked out in the et directory and the devel code in the devel branch checked out in the et-devel directory.
That way I could have both exist at the same time and not step on each other’s toes, except they both access the same database. To make sure each branch uses the correct database I set up a git hook that generated a PHP file with a variable definition in it. My .git/hooks/post-checkout looks like:
#!/bin/bash BRANCH=`git rev-parse --abbrev-ref HEAD` echo -n '<?php $branch = "' > branch.php echo -n $BRANCH >> branch.php echo '"; ?>' >> branch.php
Which generates a very handy branch.php with just this in it:
<?php $branch = "master"; ?>
And with that variable defined I can now make sure that when I connect to the database I connect to the correct one, for example:
if ($branch === "master") $db = @mysqli_connect("localhost:3306", "user", "pass", "et"); else $db = @mysqli_connect("localhost:3306", "user", "pass", "etdevel");
4. Client: pre-build hook
I did not want to have multiple copies of the client repository on my laptop. I wanted an easy way to switch between devel and master, and an easy way to merge devel into master. Creating a branch was the easy part – the hard part was making sure that the code in the devel branch would only use the server devel branch and the code in the master branch would only use the server master branch.
It so happened that I was already only referencing the URL of the web service in a single place in my client (java) code. A single string that looked like this:
public static final String wsURL = "https://littlesvr.ca/et/et.php";
If I had references to my server production code (et/) in multiple places – that would make the process slightly more complicated but not so much.
I replaced that one line with this:
public static final String wsURL = MainActivity.context.getString(R.string.et_php_url);
I won’t bother explaining the static MainActivity.context, you can find your own way to deal with that java shit. The interesting part is R.string.et_php_url. Where does it come from? It comes from the XML file res/values/auto.xml. Where does that come from? It is automatically generated by my pre-build script, something like this:
$ cat pre-build.sh #!/bin/bash BRANCH=`git rev-parse --abbrev-ref HEAD` AUTOXML=res/values/auto.xml echo '' > $AUTOXML echo '
' >> $AUTOXML echo -n '' >> $AUTOXML ' >> $AUTOXML if [ $BRANCH = 'master' ] then echo -n 'https://littlesvr.ca/et/et.php' >> $AUTOXML else echo -n 'https://littlesvr.ca/et-devel/et.php' >> $AUTOXML fi echo '' >> $AUTOXML echo '
Note that this auto.xml is not versioned, in fact it’s in the .gitignore. The whole point was to have the server string switch seamlessly when I checkout master/devel, without merging anything.
To make sure the pre-build.sh script gets called before a build I did this in Eclipse: right-clicked the project -> Properties -> Builders -> New -> Program. Filled that in and moved the new builder all the way to the top (above Android Resource Manager).
Could I have called the script from a post-checkout hook? Yeah probably, but I’ve done this before I learned about that hook, and might as well show you two ways to do it :)
There’s more I could write on the topic but this post is already way too long, so there you go, I hope it was helpful.