I’ve recently had an opportunity to build a small project from start to finish, and I thought it would be interesting to document the whole process, from laying the plans to initial release. Also, as much of my work is commercial development, my opportunity to document and share the whole process is limited. In this case, Matrix Decider is open source, so I can tell all
This project had a somewhat lighter plan than many projects I work on, as it was partly an experiment: I wanted to do a small end-to-end application using the Play framework, to gain experience and help cement my understanding of the framework itself. My goal was to have an application that provided an actual (if trivial) set of functionality (and no, I couldn’t bring myself to do another TODO-list application!), and to have it use all my normal processes and workflows, to see if that presented any issues that needed to be resolved.
I’ve used the workflow described here many times before, and most of the technologies as well, but there’s no substitute for going through the whole process.
Decision: A set of alternatives and criteria that can be combined in a logical way to determine an optimal choice from amongst the alternatives. An example might be “Choose a Car to Buy”
Alternative: One of the choices among which a decision is to be made. For example, Ford Focus Model XYZ, Honda Accord, Range Rover
Criteria: A way to evaluate the desirability of a specific alternative, for example: price, fuel efficiency, color. A criteria may be assigned a relative Importance, e.g. “trivial”, “critical”, “very important”, etc.
Ranking: The rating of a specific alternative with respect to a criteria.
User: An individual who may have zero or more decisions in progress at any given time.
Within this context we have several entities and one aggregate. User is an entity. Decision is the aggregate, and alternatives and criteria are accessed via the decision aggregate root.
As soon as the domain was designed, I began story breakdown in Pivotal Tracker, my tool of choice for planning and managing projects. I’ve used many others, but they are all much heavier in weight and provide me no additional value. I created a number of stories in a backlog, ordered by priority and dependency (e.g. where one story can’t be done until some other story is completed).
What I selected and why.
As part of the plan here was to give a good workout to the Play framework, it was the centrepiece of this application. Plenty has been written about it, so suffice it to say that it was a very efficient and pleasant development experience, and I’ll be using it a lot more in the future. I use the Scala version of Play, as Scala is my go-to language, for reasons that require another entire blog post to discuss.
I wanted a very lightweight persistence layer, and my choices ran between using MongoDB, and persisting on a database hosted at MongoHQ, or Hypersonic, both of which I’ve used many times before. Using Slick, the difference in implementation between the two was not that large, but HSQL did have the advantage of having an in-memory mode for easy testing. And yes, I know and have used Fongo, but it’s not quite as lightweight in most situations.
I’ve used Hypersonic for many years, and it’s gotten more capable and valuable to me over time. As I specifically wanted a relational model for Matrix-Decider, it was an obvious choice. My implementation allows me to run HSQL in “in-process/in-memory” mode for testing and simple deployments, and to easily turn on “file mode” without needing to deploy an external server. If I wanted multiple nodes of Matrix Decider working against a single data store (for high availability or to scale out), I could easily do so, although the deployment and monitoring gets a bit more complex, as then I’d have to deploy Hypersonic in server mode separate from the application itself.
The company Cloudbeees offers excellent PaaS for Java applications, not just for execution, but for development as well. I have been using them (as well as a local “backup” Jenkins server) for quite a while now, so they were a clear choice. I use Jenkins (the non-Oracle version of Hudson), for it’s lightweight yet capable support of continuous integration.
I use Monit, an excellent tool, to ensure all my applications are up and running as expected, and to restart them in the event of a failure, notifying me via email in that event. In this case, Monit is running on the same Linode instance where I deploy the application itself.
Who watches the watchers? It is of course quite possible for my entire Linode instance to go down – it doesn’t happen very often, but there was one brief interruption a while back. What good is my monit then, as it’s running on the same box? None, of course – so I have a periodic Jenkins job on my build server ping the box as a double-check. If it doesn’t get a reply from a known URL, it emails me, and again I know something is up. If alls well, then alls quiet.
Development Toolset and Environment
Some of the development toolset gets implied by the above technology choices: SBT as my build tool, as that’s what Play uses in any case, and I’d prefer it even if it didn’t.
IDE choice is a subject I’ve blogged quite a bit about lately, and fortunately the Scala world has plenty of good choices, from the Eclipse plugin to IntelliJ, and many others. I go back and forth a bit, alternating between using IntelliJ IDE and it’s excellent Scala plugin and using Emacs and Ensime. I’ve already blogged about my experiments between these two, but no matter which I chose, I used the same engine to drive them.
My specific hardware is a bit more complex, as it’s not one machine, it’s many, depending on where I am: When I’m mobile, I use a MacBook air, but whenever I can I add an external monitor and keyboard. At home I use a Mac Pro much of the time, but in neither case is that what I’m actually developing on – both are just used as a front-end to an Amazon EC2 instance. When I’m using Emacs I prefer console-based emacs in an iTerm2 terminal window, and using tmux to manage sessions, allowing me to easily work for a few minutes, drop the connection, switch machines, and resume exactly where I left off. Of course, when I’m using IntelliJ I can do much the same, using X11 and the excellent No-Machine Player for my client.
My EC2 instance is heavy on compute power (20ECUs), medium on memory (8GB), and medium on I/O performance (backed by an EBS instance when the instance is stopped). This gives me the equivilant of a pretty potent development box, but let’s me access it from anywhere – or anywhere with a network, which is almost anywhere. For the rare times when I’m offline entirely, I can still develop quite nicely on the Air standalone, I’m just not able to compile as fast as I’m able on the VM.
In either case, I commit very frequently and push to a development branch, ensuring that even if something nasty happens, I won’t lose more than a few minutes work.
I have an account with the excellent Cloudbees service, as described above, and although in this case my git repo is on Github, Cloudbees has no problem polling my git repo periodically for changes, and building my app. I build the application using the “dist” command in Play, which produces a ready-to-go .zip file. Then I have another job (triggered by the completion of the first) that deploys that zip to the proper location on my Linode server, and shutdown and restart the application.
This is a little more complex than it sounds: first the script must tell Monit to not restart the app for a moment, then bring up the new version. If this works, tell Monit to start watching again, and do the same thing to the other node. This guards against a bad deploy in that if the app won’t come up or otherwise fails to deploy, only one node is down, and I get an immediate alert.
I use a software load-balancer (Pound) to keep requests shared across multiple instances of the same application running on one host, in this case. In a full production situation, I’d use at least a couple of hosts – ideally in different data centers.
This deploy process happens just about immediately when I check in a change to the application, assuming all tests are still passing.
Speaking of tests, I always employ a test-first approach, ensuring that I write code that is easy to test, and that I don’t write code I don’t need. Play makes this quite easy.
Much as I believe that code should be self-documenting, that only covers the code itself, not the whole project. To this end, I’ve prepared s short writeup, in the form of a README for Github, that describes what Matrix Decider is all about, and how to get it and use it.
This post is another form of documentation, albeit not one I usually do for a project.
All in all, this was a typical project for me, and held no real surprises. I was able to work with some of the portions of Play that I hadn’t had the chance to before, and it turns out that the framework does indeed fit my typical project flow, including the ability to test extensively and to deploy continuously.