6 steps to improve your web app’s performance

We’ve created a set of guidelines that will help you improve your Web App’s performance from the get-go.

When you start building a modern Web App, one of the first things that hit you in the head like a brick is the fact that not only are most phones a lot slower than your computer, but the 3G/4G connections most users have are not strictly speaking awesome.

In fact, the differences between the experiences on localhost and the actual production servers can be so dramatic that if you don’t take a few precautions, the second you release your app you are going to realize that your product performs incredibly poorly for most users, and they’ll probably hate you for it.

To prevent this from happening to us, we created a set of guidelines with the most effective steps you can take to improve your web app’s performance from the get go, as well as preventing performance rot when you start piling on new features on top of that beautiful first release.

1) Set a Performance Budget, and stick to it

A Performance Budget is a clear objective of how fast your web app is expected to be. The goal is to prevent your team from turning the product into a mess of caked-on features that takes an eternity to load, which is pretty much inevitable in long-term projects. As you can imagine, there are two main components in this strategy:

Setting the Performance Budget itself

Actually following through with it (which is, incidentally, where everyone fails)

The best way to implement this is to get the Product Owner to clearly set performance as a priority. This will lead the team (designers and developers) to focus on mobile, where speed is one of the most important features.

Once that’s done, the next best step is to plug a rule into your CI Pipeline that blocks all pull requests that don’t fit the bill. And no, you can’t make exceptions (that’s kind of the point of a CI rule).

These are the two main rules we’re using at Aerolab. Feel free to copy them:

Don’t take more than 1 second to show the user what they want to see

(1 second Time To First Meaningful Paint on good WiFi on our standard issue Macbook Pros)

The web app has to be interactive in 1.5 seconds

(1.5 second Time To Interactive on good WiFi on our standard issue Macbook Pros)

Having a clear performance objective will touch everything in your stack, from the design and the Javascript libraries that you pick, all the way down to how your servers are being set up.

Implementing a performance budget -and defending it-, is a great way make your entire team work together on making the product fast.

Once everyone understands how their work impacts on this metric, you’ll be able to stop performance degradation right in its tracks, and that means you’ll keep the great speed from that pristine first release across the entire life of the product.

2) Keep your core as light as possible

This seems strangely relevant

There’s one thing that you should keep in mind when starting a new project:

The core frameworks and libraries you use to build your product are usually going to stick around for its entire life, making those decisions pretty much irreversible.

So, if you pick an impossibly large enterprise framework that somehow needs to run a Linux VM in the browser, you can say goodbye to your product’s performance until the next big refactor. Which is, of course, never going to happen because removing things is much, much, much more expensive than adding them back in.

The best piece of advice I can give you here is that you should pick the lightest possible solution that helps you do the job. If you are building a product that’s very close to what a browser can do on its own (like a news site, a blog, an ecommerce, or anything that mainly shows text, images and videos), you usually only need the bare minimum to help you have an organized codebase and try to keep the peace when merging pull requests.

Exercise your professional judgement here. If you are building a product for millions of mobile users, picking a insanely heavy enterprise stack can be catastrophic from both the UX and revenue side of things. If instead you are working on some intranet app that few people use once a year (or just trying out a new framework), you can just throw in all the crap you want because nobody’s really going to care about it.

If you want some reference here, our go-to libraries for web development are Preact (a fully compatible React clone in 3kb) and the Next.JS framework, which provides server side rendering, offline support, prefetching, great docs, and a fantastic community.

This hits the sweet spot for most projects and it’s very easy to add features on top of that tiny codebase. Combining this approach with the next tip we are able to load our latest web apps comfortably under the one second mark, which is fantastic from a UX standpoint.

3) Design a Loading Strategy

If you’ve been working on this field for a while you know that eventually you’re going to need to shove an impossibly heavy component into your product, destroying all of those performance gains from the first release. What’s worse, this will demotivate your team and make them feel that all their efforts on making the product fast were pointless.

But don’t despair! There’s a way to cheat when that happens, and it’s called Designing a Loading Strategy, which is a shared effort between designers and developers.

The main principle behind a good Loading Strategy is to never let our users know when our app is loading. Or if we simply have to (like when the app is starting up), make the process as fast and as pleasant as possible, loading the important things first and everything else either late or never.

The best way for trying this out is to grab the heaviest component in your app (you know, the one that requires a ton of JS but is used like twice in the entire site) and split the loading process into two subcomponents:

A placeholder version of the component , that requires just the bare minimum amount of code, but will otherwise fit neatly into the page and look just like the real thing. For example, if the component is a complex video player that loads dozens of ads, you typically just need to show the thumbnail and a play button. This is what you’ll show the user when the web app first loads . You get extra brownie points if you make it seem like it actually works.

, that requires just the bare minimum amount of code, but will otherwise fit neatly into the page and look just like the real thing. For example, if the component is a complex video player that loads dozens of ads, you typically just need to show the thumbnail and a play button. . You get extra brownie points if you make it seem like it actually works. The real version of the component, which you’ll load only after everything that’s actually important in the site loads properly. The best way to do this is to defer the loading of that component’s dependencies and other stuff after the window load event, and when you finally get all that code, you can transparently replace the placeholder component with the real, working one.

One of the most popular examples of this technique is the progressive image loading effect used by Medium, Quartz and a few others, which embeds a very low resolution image with the HTML (to act as a placeholder) which then transitions to the full image once it loads:

If you do both steps correctly, this new heavy component won’t interfere with your initial load times (letting users start using the product just as quickly as before). Everything will still load and function properly, and that component will load a few tenths of a second later than the rest of the web app. If the transition to a working component is believable, your users won’t notice it at all, which is the gold standard for performance.

Keep in mind that even with a clever loading strategy, loading large JS libraries will still lead to some noticeable stuttering on most phones. So, even if you have the most clever loading strategy in the world, you should still try to load as little code as possible.

4) Develop on a midrange phone

One of the best things you can do as a developer is to actually code on the same hardware that most of your users have.

You’re free to write your code on a top of the line Macbook Pro, but you should get a cheap used Nexus 5X or Moto G4 and pack it in your bag just to try your code live on a midrange device. If you want to truly commit to this tip, you can also throttle that phone’s connection so it mimics a reasonably decent 3G connection. Using Chrome with Remote Debugging will make the experience almost as good as developing on your computer.

This will open your eyes as to how most people are actually experiencing your app in the real world, and it’ll also give you a clear idea of how little CPU and RAM is actually available to a real user. The second you start getting frustrated because reloading takes way too long on your phone, you know that you are shipping way more code than you should.

The differences between devices and platforms can be dramatic. For starters, iPhones are usually twice as fast compared to the best Android phones according to most benchmarks.

Just to give you an idea of just how slow most phones actually are, Addy Osmani wrote a great article on JS Startup Performance, and one of the datasets he shows is how long it takes to parse and execute 1MB of Javascript on different devices: