more > less
Creating small modular single page apps
Let me introduce myself. My name is Max Degterev.
I work at moviepilot.com. I write code and I love nodejs.
Why more > less?
Means more small single page apps is better than a large one.
It's better in terms of performance and it's easier to maintain.
What to expect?
- Continuing the talk of Tiffany Conroy
- Defining the problem
- Proposing a solution
This talk is intended to continue previous presentation by Tiffany Conroy "Cutting the fat".
I will give you more of a developer perspective, rather than a designer one as Tiffany did.
I will also include a real life example from moviepilot.com.
AJAX vs Reload
Why do we use it and what does it solve?
A short reminder what the original talk was about. And it was about AJAX vs full page reload.
What are the pros and cons of each approach. How do we create simple page apps and why.
AJAX
PROS
- No need to send html/css/js over the pipe every time
- No need to re-render the whole page
- Doesn't interrupt user interactions with the page
CONS
- Lose state unless you use frontend router
- Serverside computations moved to the client (sorting, filtering, etc.)
- Causes JS and CSS bloat
Behind every single page app there are tons of AJAX calls. What are the advantages.
What are the disadvantages.
PROS: Decrease the amount of traffic significantly. Increase the page render speed.
Server calls are asynchronous, user presented with working interface before the resources are loaded.
CONS:
You have to maintain routing history in the browser somehow. Most of the frameworks already
solved it for you, yet it's worth mentioning. Especially when sometimes yo have to have
routing on both serverside and clientside.
Since everything is rendered on the frontend you have to render somehow separately for the search crawlers.
Depending on what you do, large datasets filtering and sorting might be slow.
Causes megabytes of JS (haha parsing time). (not talking about lazy loading like yahoo/google)
Frameworks tend to encourage creating billions of event handlers. (Collection Views)
And if you're overzealous with your asyncronousness you might end up with a page like that.
Page reload
PROS
- Memory leaks aren't a problem
- You can control your environment
- SEO and browser history aren't a problem
CONS
- Page rendering slowed down by Server (SQL, etc.)
- Every page reload interrupts whatever user was doing
- Very 1999
The oldschool page reload approach with some javascript.
What are the advantages of going oldschool. What do we lose here and what do we gain.
PROS: Since pages are refreshed all the time there is no concern about leaky
event handlers and collections. You can change layout on every page completely, with single page approach it might be a bit challenging (e.g. when you want to have header on every page, but then change layout). All the computations are done on the server, just add more RAM and CPU if its slow.
Parsing works like a champ.
CONS: page rendering is slowed down by database and template rendering etc.
Uses loses context when he writes a comment or adds items to the shopping cart.
And it's for the old people, we want to be the cool kids after all.
So if there is an operation that failed or timed out you see an error page
where you would've got at least partially working interface.
Also some operations are synchronous in nature, resulting in a very slow
page rendering. For example conditional loading of subsets of information based on
main model etc. (not talking about caching here)
The question
How do we get the best parts of single page apps, but avoid the problems?
- Group pages by context: sign up, profile and settings, article, etc.
- Create small single page apps – one per context
How do we strike a balance between full single page app and using page reloads.
We use them both! We group contexts into small single page apps.
Notable examples
- GitHub
- Foursquare
- Moviepilot ♔
We're not pioneers here, lots of people have done it before.
- Ruby on Rails
- Backbone, Chaplin, Coffeescript
- Sass
- Handlebars
Let's talk about Moviepilot for a bit.
In case you didn't know, moviepilot is a website that covers information about the
upcoming movies: trailers, interviews, writes articles etc. and it's also a
blogging platform, allowing contributors to write their own posts. The app
we're working with was build some years ago in we found that maintaining it
started to become more and more problematic
Here is out stack.
The problem we had
- 700KB app + 400KB templates = 1.1MB of JS (without 3rd party scripts)
- Slow on low-end machines
- Takes minutes to compile assets with Rails on first run
- Even longer to deploy
- Huge app, seemingly small changes can have drastic consequences
Here are the problems we ended up with, when code was piling up, creating a
one huge single page app. That took us several years, but we've reached this goal. Yaay.
Codename "mp.author"
- A separate Rails app
- Not using any parts of the main app
- Mounted to a specific /path
- Need an easy way to share code
- Some of the previous problems remained
So on moviepilot we have a post form where contributors can write their articles
for the website. In the beginning it was relying on some parts of the main application,
sometimes some bugs were introduced, and it seemed that the main application just tried to
do too much at once. We deciced to try and abstract it from the application and see if
we can make a new clean post form app.
It doesnt use any parts of the main moviepilot code, although it build using similar
stack. It is running under /post so when you hit that URL nginx actually proxying you
away from the main application to that sub-app
We saw that it worked quite well, it's really fast and we thought that we can
split the main app even further. But we saw two problems with that. First was that
we're using the same stack, so we might end up in the same place where we were.
Second is that we can't reuse any code and it there must be a way to easily share code between sub-apps
Codename "mobilepilot"
- ExpressJS
- Backbone, Coffeescript everywhere
- Stylus
- Jade
- GruntJS
Simultaniously we worked on a new mobile website.
When just I started working at Moviepilot my first task was to make mobile website
better. About 40% of all traffic is from mobile devices, mostly iPhones. The original
mobile website was a separate RoR application with parts of chaplin and backbone,
stitched together like a frankenstein. I already built a few mobile websites before,
so I proposed to build it anew with nodejs and a bit different stack. Here's what we ended up with.
Fun facts
- Runs smoothly even on the old iPhones
- Assets compilation takes milliseconds
- Deployment takes about 15 seconds
- CPU usage dropped significantly
- Shares code between front-end and back-end
- Serverside pre-rendering for faster pageload and SEO
So, stack is a little bit different. And most importantly the website is quite fast.
We saw that assets compilation took ms instead of seconds with rails. Even for deployment.
CPU usage on the server dropped and the process of working with the app is much more enjoyable.
There definitely are things to learn from here and adapt to the desktop website to make it better.
For these small apps node felt just like home. RoR might be nice with all the spinners and
chrome, but also an overkill for such small projects.
It gives you a lot of power you dont even need, and it's assets pipeline is a bit rusty.
So we stepped on the slippery slope of using Nodejs
The solution we used
- Create a boilerplate app with the common modules
- Create small context-based apps
- Those apps are cloned from the boilerplate
We decided to have another experiment and see if we can split our main website into
smaller chunks and have those built on top of the same foundation.
That means that as long as you have a good boilerplate app with all of the common modules
in place. and maintain it regularly, you can create a small army of clones. Each of these
clones can be different in a way, yet they all share the same core. And it's very easy to jump
between those and share code, as they all have the same architecture.
Results
PROS
- If anything breaks, it breaks locally
- Server and client can share code and templates
- Can pre-render templates (SEO)
- Assets compilation is much faster
- Memory leaks are not that important
CONS
- You'll need a proxy for that army of clones (Nginx)
- Maintaining boilerplate app is not that comfortable (add as a remote to your app repo)
What were the results of that experiment.
PROS:
This way your apps are encapsulated, and if one of them goes down, the rest of
your website remains working. That is unless you break something in the boilerplate.
Please dont do that. With Jade we were able to easily share templates
and have some of the pages pre-rendered on the serverside so it didnt rely on the JS
to display the initial content. Your users are in no-js mode until JS is downloaded
and parsed after all.
CONS:
To run your app in it's fullest, you will have to have all of the sub-apps installed
and running as well. It's quite easy to do with nginx, but you have to do it.
You'll need to define port ranges for the apps you run so they dont collide.
Dont forget that your subapps need to be mounted to a specific /path too
when you cofigure your routing.
You also have to maintain the core boilerplate app and in the beginning it's a
bit hard, because you don't have a concrete common modules yet.
But as soon as you have it, changes are rare and small. You will
also face some challenges with routing and mounting under specific /path. And you'll
have to know what are routes that are local (within the single page app) and what are
external (leading to the other sub-app)
So, you might say "good for you", rite. "But what can I take out of this talk? Nothing.
I want those 10 minutes of my life back."
Takeaway
Forged in the fires of Mount Doom: https://github.com/suprMax/express-boilerplate
By default
- ExpressJS server
- Grunt tasks setup
- Cakefile configuration
- Coffescript everywhere
- Jade, Stylus
Included
- Snockets (default)
- Browserify
- RequireJS
- Chaplin
Turns out this thing is available on github, and you can poke around with it.
It is a bit opinionated in terms of the stack, but otherwise quite clean and ready to go.
If you want to use stack similar to ours (means Chaplin), there are some of the
frontend pipeline options available.
THE END
That's it. Have fun.