"Build a responsive web app with a basic CMS from scratch for the first time, with minimum understanding of user requirements, and have it production ready in two months? Sure!"
This was me sarcastically at the beginning of June. I was incredibly skeptical, but the four of us interns (one designer, one developer, two business analysts) at American Express got the damn thing completed, and to the original requirements as well.
I'd like to quickly go through the app and some of the tools I used along the way to really power through development.
PythonI love Python. I really do. Whilst it's not as fast as C# or even more low level languages like C or Rust, the language is ubiquitous, has first-class support for functions as objects (which I really like and basically expect from most languages), and is just really nice. Especially after having to write programs in VBA for the past six months, Python is a breath of fresh air.
FlaskI used to be somewhat skeptical of using Flask, the Python web microframework, for anything other than the most basic of proof of concept things, but I grew up a bit and did some research and found out that it's actually quite production ready.
It also has a small footprint, ready to fit nicely into whatever architecture scaffolding you choose for your source code. I sort of muddled through a few different guides, drawing inspiration from Digital Ocean's recommended guide and made something really quite nice out of it's MVC-like structure.
Flask-LoginAs aforementioned, Flask is very extensible, which is v nice for complex things like user persistence and user management. Flask-Login adds some hooks into the Flask lifecycle so you can make use of its higher order functions to enforce user login requirements like adding @login_required to a route and automatically protecting the route. It's also super customisable and lets you deal with as much or as little user persistence as you want. Please use it.
SQLAlchemy + AlembicTo be frank, I'm no database expert. I got the hang of data modelling and requirements elicitation throughout uni but to be honest I'm not going to become a database architect in my next life. Thus, these two Python libraries are a godsend to any fullstack developer.
SQLAlchemy allows you to declaratively create Python objects and create a database model around them, and handles initialising the database instance and connection pool for you as well. Of course, this is the role of any good ORM, but this is the first Python one I've used and I really really like it. Database engine independence (ability to move from, say, sqlite to MySQL) is another thing that comes for free by using this tool.
Alembic is the icing on the cake. I've never used a database schema revision tool, and it was incredible to be able to keep my changes in sync across my local, developer and production environments without any hassle. Every time you change your schema in your SQLAlchemy models, just update your tables using Alembic's revision generator (either by specifying column changes manually or using the autogeneration option which I never managed to get working), and save the revision to your source control tool.
Add an Alembic upgrade step (
alembic upgrade head) to your deployment chain, and you're literally done. Easy as, and perfect for agile development where your schema is likely to change multiple times as the problem domain is further defined down the track.
uWSGI + NginxI had never worked with Nginx before this project and it was a really interesting experience. According to the docs, you shouldn't be using Flask's built in web server for anything but development. Instead, in production environments, use the uWSGI gateway library for Python to run your Flask 'app', and have it sit behind an Nginx server.
Getting it working on Ubuntu was super easy, as there are multiple guides around, but OS X was a bit harder. Make sure you follow a guide that uses a unix socket to communicate instead of a HTTP relay, as it'll be faster and has less overhead. Here's a handy guide that outlines how to set up this stuff on Ubuntu. Then use an nginx rewrite to rewrite your /static url to point to the static files directory used by Flask so you have nice and fast file serving times.
ArrowThis is a small thing but also I didn't how how confusing Python's various datetime libraries were until I had to do some timeshifting across timezones . It hurt my head till @auscompgeek told me about an awesome Python library, Arrow, that deals with times for people who are human like myself. Then we lived all happily ever after.
Sass + Girder + Skeleton CSSEveryone likes Sass, and (mostly) everyone likes prebuilt component libraries like Bootstrap. But not everyone likes the weight of Bootstrap. For this project I utilised Skeleton, a simple but responsive CSS boilerplate. Honestly, I probably didn't even need all of it but it's nice to have some CSS that smooths out the rough edges of the browser implemented spec. Additionally, my designer created specs to a grid system so it was easy to translate into something vaguely responsive with a class-based grid system (think Bootstrap's "sm-12 md-8 lg-4" classes).
For all those edge cases of responsiveness where a HTML-specific grid system was too heavy handed, I used Girder, which I've made use of in previous projects. When I found Girder, I had grown somewhat tired of HTML class-based grid systems as they're so heavy, and its description completely made sense. Finally, you can start putting your presentation logic in CSS where it belongs. It has a learning curve which takes some time to get used to, but I found it far nicer.
CacheJust use a cache - Flask depends on Werkzeug, which has a cache API already. Something that I overlooked early on is that the default Werkzeug in-memory SimpleCache (the easiest cache to set up) doesn't scale well across multiple uWSGI worker threads, and will leave you with an inconsistent cache. This led to some embarrassing moments when demonstrating on the production environment with our client...oops.
When you actually deploy your app, use something like the FileSystemCache or MemCached Werkzeug caches, which fortunately use the same API as the implementation of SimpleCache.
The Final ProductWe ended up with a fully fledged and fluidly responsive web app that we were all proud of, and looks kinda nice as well. It's responsive, and can sit nicely on a cheap DigitalOcean instance too - project total cost including hosting for staging and production environments was under $400.