A clojure cup retrospective
Update: Voting is live! Vote for my app at https://clojurecup.com/#/apps/booker, then check out some more deserving entries at https://clojurecup.com/#/apps and vote for them too!
I’ve just spent about 30 hours this weekend coding up Booker, my entry to the 2014 Clojure Cup, in which I participated as a one-man team. It was pretty exhausting, and the app doesn’t do quite as much as I was hoping it might, but I’m still quite happy with how it all turned out. Here’s how my weekend went.
On Friday evening, my wife and I landed in Istanbul, where we’ll be staying for a few weeks. Sensibly, I had arranged to spend the first weekend shut in our apartment writing code. I made a nighttime run for supplies – bottled water, some pasta, and some instant coffee – and went to sleep still wondering what I would make.
The schedule for the cup is the 48 hours between 00:00 UTC Saturday and 24:00 UTC Sunday – 03:00 - 03:00 in Istanbul. On Saturday, I woke up around 10:00am with still little idea what I would write. I know how I would probably host it, though, so I went over to CloudSigma to claim my free server. I set up the pre-installed Ubuntu, got ssh up and running, and installed nginx and git.
We went out to grab some breakfast, and I came up with the idea to write an app to help transient workers and other travellers meet up; anyone who has ever travelled substantially knows that there’s a lot of camraderie to be found in being mutual strangers in a strange land (Side note: Istanbul is strange, but in an amazing way. No hard feelings).
We got back around noon thirty and I set to work. First things first, I used Middleman to make a mockup of the splash page. Just a header, a big photo (I got the photos from unsplash.com) and some form fields.
Tip: Putting some decent typography over someone else’s gorgeous photograph is a good cheap design trick. Scan through the entries to the cup if you don’t believe me; most of the lookers do just that.
I think I’ve finally got a handle on a good way to arrange projects like this. I plopped
middleman into the
resources/site directory, and configured it to dump its build files
resources/public, which is where Ring expects to find them anyhow. After I got a
mockup up, I made my first push to the repo, pulled it onto the server, and
pointed nginx directly at the
resources/public directory, where it happily served
up the static content and pages while I got to development.
I originally intended to write a client/server cljs/clj app backed by kioo and om, so I spent a few hours getting figwheel up and templates sorted, and writing some data storage methods on the server side. However, as afternoon crept into evening, I decided to scrap the one-page-app approach in favor of the more familiar enlive/ring/compojure strategy. Happily, the work I had done in Kioo could be ported to Enlive easily.
After that, I spend many hours rearranging things and making some depth-first bugfix journeys. Around 22:30 I made my second commit, which added more pages to the app, included some js libraries, and added all the clojure code – persistance, routing, ring handlers, even some preliminary (i.e. broken) facebook oauth support.
The ui-first approach sort of defined my schedule; I’d write some UI mockups, then I’d have to write the code to implement them. Then, repeat. Since my mockup now included all the pages in the final app (search, profile, profile edit), this meant that my work was cut out for me for a while. All in all, I stayed up until 3am on Saturday before hitting the hay.
On Sunday, I got to work implementing all that my UI promised again. I worked on the facebook oauth (what a pain!), put in some error handling, and in general just implemented all the things. I made my third commit at around 19:00 on Sunday, 8 hours before the end of the contest, implementing permanent persistence with Redis, a bunch of UI nicities, and all the other stuff you’d expect to see from almost 16 hours of work.
A side note on persistance: Until this commit, the only persistance the app had
AtomStore that forgot everything it knew every time the process was restarted.
I used the process I outlined before
to set up a persistence protocol for each data model, so I could
add implementations easily. I decided to use Redis for the session store, because
Redis makes everything better, and after doing that I realized that I could
abuse Carmine’s serialization to use Redis as my datastore without putting too
much effort or adding infrastructure into it. This is probably not a good decision
for an app with aspirations of long-term viability, but hey, at least I can
just write new implementations for my storage protocols.
the very last thing I implemented before putting this commit live.
Tip: If you’re using Carmine’s serialization on records, go ahead and define your own serialization and deserialization methods. Apparently my type-ids got out of sync and I ran into major trouble deserializing older versions of objects, even if their fields hadn’t changed. Better to save some pain and debugging and do it yourself, it’s not hard.
At this point, I spent until around midnight fixing bugs and polishing
up the extant features. At midnight, with three hours to go,
I started seriously working to fix bugs in the deployment. I had to switch deployment
lein run on account of some bugs with resources,
and make the aforementioned fixes for Nippy’s benefit.
At 2am, with one hour to go, I committed the final feature: integration with Postmark to send emails. Without this the whole app is kind of useless, so this was an important one. Thankfully, this turned out to be not so bad. I debugged it quickly, fixed up my app page on Clojurecup’s site, did one last check, and got to bed 14 minutes before the contest ended.
So that’s how that went. If I could do this again, I would have done a few things differently.
Firstly, I'dve put even the slightest effort into planning my app out. Just knowing what the UI and backend would look like at the start would have saved a lot of time and wasted effort.
I would have chosen fewer libraries and services that I had never used. In particular, kioo/reagent, friend, and facebook’s API were entirely new to me. I’m a big proponent of using projects as an excuse to try new things, but perhaps my app suffered for it this time.
I would have probably scheduled some meals. I kept forgetting to eat until I was starving. I'dve also tracked down some better coffee, but I guess that’s what I get for being in an unfamiliar city for the contest. Maybe if it’d been a week later I'dve had proper groceries.