Too Long; Did not Read
True Story Follows
Back in 2K9 when I was off fighting the Great War in Afghanistan, I had a side project going as I was first learning Django where I could dynamically generate workouts for people. The inputs were a user’s available equipment, fitness goal, fitness level, and their schedule. A friend of mine who is a very successful personal trainer developed the algorithm while I wrote all of the code.
The plan was to become a billionaire, but that plan never quite materialized. The site’s algorithm and the concept was cool, but that’s about it. At this point it’s actually embarrassing for me if people saw the site and know that I wrote it.
The front end actually belied the complexity beneath. It was hard to get users to believe that the site was trustworthy when the whole look and feel was terrible. Or get them to understand how in-depth the training algorithms were.
But, perhaps some distrust of the application was quite warranted. The code underneath from my perspective looking at it 5 years after I wrote it is that it’s AWFUL and painful to look at.
With all that said though, I’m ranked #1 on Google for “Workout Generator” and there’s still quite a lot of traffic. So I wanted to re-write the application, and Adam Burke, who I originally wrote the application with, happened to want to get involved with the idea as well.
After working with a good software company for almost 2 years now, I was able to quickly apply much better skills on nights and weekends.
Before and After Screenshots
Before getting to any technical details, here’s what the revamped version of the site looks like comparitively:
Landing Page Before
Landing Page After
Sign Up Page Before
Sign Up Page After
(More Sign Up Page After)
Workout Page Before
Workout Page After
Cardio View Before
Cardio View After
Between the new and the old site, the basic algorithm is the same, but the entire thing was re-written. Some of the immediately noticeable changes in the new application:
- The entire site is a single page web application; Only one web request is made that returns HTML
- The entire site is drastically faster
- Everything is built on top of a RESTful API
- The front is responsive and mobile friendly
- Video demonstrations use HTML5 video instead of YouTube
Main Reasons for Faster Site
Making things fast is fun for me, so this is one of the coolest things about re-writing the application. There are a number of things that happened to make the site way faster.
First of all, from a user perspective, the site simply feels faster because it’s a single page web application, and requests are all done through AJAX. Clicking a button will cause a spinner to appear until the request complete. When you change views, there’s a sleek animation which takes the user’s attention from a delay caused by code execution.
But on top of the user experience, it actually is faster. The biggest and most obvious speed up is because of drastically fewer database transactions. That was done in a number of ways.
The first reason for fewer database transactions is simply because I’m better than I was 5 years ago. The past Scott from 2009, the Scott that we all hate, would simply fetch from the database whenever data was needed. The new Scott, the one that everyone loves, made the code much more maintainable and decomposed code in such a way where data didn’t need to be fetched so frequently. In general, for a single request I could batch a few fetches from the database and then batch saves as necessary.
The second reason for fewer database transactions was documented in a recent blog post where I represented a lot of the previous database rows in memory instead of on the hard drive. The long story short: much of the data in the database wasn’t going to grow anymore because of its particular use case, so I offloaded that into a python file that stores data in dictionaries based on query patterns.
The Advantages of a Single Page Web Application
The benefits of a single page web application are now probably widely accepted, but I’ll write a little bit about it anyway. The new version of the site is using Backbone.js and Underscore templates and then fetches data from a very simple REST API. Here’s what’s awesome about it:
To keep talking about speed a little more, the REST API is faster. Instead of rendering an entire HTML page we only need to render JSON text. It’s a minor speed increase, but beneficial nonetheless.
The front end is entirely interchangeable. If I want to write an iPhone application or an Android application, it would be incredibly simple. Or if I want to re-make the front end entirely, it’s still pretty easy.
I could even integrate my service with someone’s else’s now. For example, I have 700 exercises stored with video demonstrations labelled by muscle group and all sorts of things. In the future I might find that I can benefit from making that publicly available.
Server side code now hardly gets mangled with code corresponding to a view. This could be done without a RESTful API, but the whole architecture discourages sloppy coding practices.
I’m not sure how much I’ll continue to work on this project, but as it stands, the groundwork has been laid to make for service-oriented-architecture.
I used to write code like this:
- Write some code
- Open a web browser, set stuff up as necessary (maybe I need to login and select a particular option)
- Verify that my change works
Now my workflow is more like:
- Write some code
- Write an automated test with appropriate pre-conditions
- Validate that tests pass
- Re-Factor initial code
- If tests still pass, in 99% of the cases I can safely assume that the feature works for the web
The second workflow seems like it’s longer, but it actually isn’t. In my experience thus far, setting up a solid foundation is always faster in the long run (and by long run, I’m talking days, not weeks or even months). With the automated tests, I can make a small change or a huge change and remain confident that nothing broke. I can test all possible permutations of pre-conditions at no cost if I want. If I’m working on something tricky where the code is a work in progress and is throwing exceptions, it’s way faster to write a test once and run many times vs. re-creating the full production environment manually. With tests, it’s also way easier to troubleshoot problems. It’s too easy to jump directly into the code from an automated test. That’s a huge pain to setup with a full blown web server.
Probably the coolest thing to compare (and also the most embarrassing) is to compare coding style from 2009 to coding style now.
Code Sample Before
Code Sample After
If you look closely at the first screenshot, you’ll notice that I’m on line 6,000 or so. My old project was a single views.py file and a single models.py file. It was horrible
Now code is decomposed pretty hardcore. It’s almost a game to me to see how well code can be decomposed: how short can I make a function and have it still be meaningful? How can I write things in such a way where a function does one and ONLY ONE thing? How can I make it so that I can avoid repeating logic? How can I make it so that a module knows as little as possible about anything else? How can I make it so that I can avoid special cases at all costs?
If you look closely, another horrible trait of my old code is that the variable names are horrible. I used names like “iterator” and “ret.” The style looks horrible, I didn’t use PEP8 or anything. I didn’t use a linter, so there’s unused variables all over the place.
Offload Some Problems to 3rd Party Services
Django has some recommended some solutions for login. There’s also some functions you can use to configure and send emails from your web server. You can follow some tutorials on how to setup Facebook login.
I didn’t do any of that.
I let Parse handle user authentication and management. I also let them handle Facebook login. This made my whole sign up / login / change password page that I hate building a breeze to set up.
Payment gateways suck, and managing security for payments also sucks. Stripe takes care of all of that. It took less than an hour to setup recurring payments and the user interface to create and cancel a subscription.
Email is also not very fun to set up. I’m using Mailgun, and they make it incredibly easy.
I used to SSH into a web server and set up a django application manually. Now I just use Heroku and Git, and I can just mirror my local development environment.
I’m not very good at UI. I downloaded a responsive UI template that was already good to go.
I wanted some snazzy buttons and stuff. Bootstrap has everything you need.
On one last note, one thing that really makes the site feel better is to use HTML5 videos. Previously I had a bunch of Youtube embeds for video demonstrations, but with HTML5 all videos are queued up and loaded by the browser immediately. So from the user’s perspective, there is 0 wait time to play a video, and this plays out really nicely on mobile as well.
In conclusion, we all hate the old Scott. It took me longer to read and understand the old code than it did for me to write the new version. The sample code from the old Django book is by no means a best practice.