The WhisperGifts re-launch recently went very well! I promised a more technical follow-up with some details about what's new and what's changed, so if you want to know more about what makes WhisperGifts tick then you'll want to read on. Hint: It's a dash of Django, a pinch of Python, and a slathering of open-source software all around.
The primary technology behind WhisperGifts is Django, the "web framework for perfectionists with deadlines". My own deadline for this project was rather, ahem, flexible - the new design was a work in progress for 2 years (on-and-off, mostly off due to the birth of our baby) and the back-end re-write happened over a few months early in 2012.
Django allows us to iterate quickly. I find the language natural to use and the documentation is epic. There's a number of things that no framework does out of the box; I've tried to rely on open-source to fill as many gaps as possible rather than re-writing things from scratch like I did with the original WhisperGifts site - this is mostly because the open-source ecosystem around Django is now so much larger than it used to be.
As an example, originally I rolled my own authentication as the user management modules in early Django releases were rather inflexible. Building your own authentication is never a good idea, so I've migrated to using built-in Django logic. Similar changes are found throughout my codebase.
What I Use
Django, obviously. I use most of what comes with Django: The ORM and URL dispatcher, the included Admin, User and Cache apps, and more. Some might be interested to note that I don't use class-based views, simply because I don't see a need to change at this point.
Caching is done using Memcached and nested cache tags, as I've blogged about previously. I also use Django's site-wide caching middleware for anonymous users, which reduces load time for the marketing/static portions of the site.
Images are processed via the sorl-thumbnail package. I can generate thumbnails in any size on the fly. All of my images are stored locally - due to my current volume, the overhead of setting up a more formal CDN or even just using S3 isn't worthwhile.
Customer payments (for upgraded packages) are handled by PayPal. To interface with their IPN and to simplify the user-facing workflow as much as possible, I use django-paypal.
To track in-app metrics (such as number of signups, number of upgrades, number of new items) I use django-app-metrics and get a simple daily e-mail. I'm also testing out Mixpanel which although it isn't free lets me get much more detailed statistics for the same sorts of metrics. django-app-metrics even has a backend to automatically push data through to Mixpanel, so I might use that later on.
All e-mails are sent asynchronously (using django-celery) so they don't tie up front-end web serving. I deliver my e-mails via Postmark with the useful django-postmark library. All my outgoing e-mails include both HTML and plain-text components; I also embed a header image. In the geek world this is heavily frowned upon, but remember who my audience are: couples getting married and their wedding guests. Postmark makes these e-mails simple.
The front-end web server to all my websites is always nginx. It is small, easy to configure, does a wicked job of serving up any static files (both my own site static files and those customer images that have been uploaded) and integrates well with Django. To run Django for nginx I always use gunicorn managed by superisord.
My site-specific CSS and JavaScript files are hand-crafted during development then at runtime combined together effortlessly and minimised as much as possible using django-compressor.
To make sure that any gremlins are caught and dealt with, django-sentry catches any exceptions in my code and presents them in an interface that is incredibly useful: You can see which exceptions occur most often, what conditions trip them, and more.
In a similar vein I use django-timelog and occasionally review how long my views are taking to render in a live environment, django-debug-toolbar gives me similar data during development.
Bringing it all together
For much of the above all I need to do is pip install django-compressor
and add the relevant code to my settings.py
and templates. Very little of what I've mentioned above has changed the way I develop or deploy; they simply make life easier. The fact that I can pick up these bits of software (most of which weren't available 3-4 years ago) and use them off-the-shelf with some very minimal setup just makes me love Django development even more.
I therefore owe a big "thank you" to the Django community.
Previously I've manually written code to do many of the things I've mentioned above (and bad code, at that, given it's status as "helper code" rather than the main part of my projects). I owe a few people a beer or three.