Extreme Makeover Code Edition

Too Long; Did not Read

I re-wrote the website now residing at Old.WorkoutGenerator.net and replaced it with WorkoutGenerator.net. Check it out.

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

Screen Shot 2015-01-23 at 2.48.35 PM

Landing Page After

Screen Shot 2015-01-23 at 2.48.48 PM

Sign Up Page Before

Screen Shot 2015-01-23 at 2.50.26 PM

Sign Up Page After

Screen Shot 2015-01-23 at 2.51.03 PM

(More Sign Up Page After)

Screen Shot 2015-01-23 at 2.51.24 PM

Workout Page Before

Screen Shot 2015-01-23 at 2.58.51 PM

Workout Page After

Screen Shot 2015-01-23 at 2.59.27 PM

Cardio View Before

Screen Shot 2015-01-23 at 3.01.33 PM

Cardio View After

Screen Shot 2015-01-23 at 3.01.48 PM

Technical Changes

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.

Security was easy to implement. I used to rely on Django’s session management for login purposes, but over the long haul this ended up getting pretty hairy. Now I just use an access token system where a user signs up with Parse (Parse is awesome by the way), and I set an access token on the Parse User object in javascript on signup. When a request is made for a particular user, an access token must be passed as well, and that access token must be able to have access to the particular resource. It’s simple, secure, and easy to expand upon.

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.

Automated Tests

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.

Code Style

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

Screen Shot 2015-01-23 at 3.09.45 PM

Code Sample After

Screen Shot 2015-01-23 at 3.11.46 PM

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.

HTML5 Videos

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.

Conclusion

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.