If your plan was to build all the features of your app first, then spend a week to “clean up” and fix any perf issues, then here’s some friendly advice: Make a new plan.

While I applaud the various DevTools teams around the world for constantly improving their performance tooling story, performance debugging on the web is still way too difficult. If you’re no tracing expert, then you’ll probably end up scanning to the last 100 or so commits to find out the one CSS property that decimated your FPS. Also, you will hate your life.

Here’s a better idea: Set a performance budget, then be strict about it. If you now introduce a change to your site that gets flagged at being too slow, you might still not know why, but you can now research and talk to your team mates about it, and troubleshoot together (or maybe even find a better solution).

What’s in a performance budget?

You’ll first want to split up your budget into run time and load time performance. Here are some basic starting recommendations:

  1. TTI (Time to interactive): 3 seconds on 3G (try the polyfill)
  2. Main content scroll: 60 fps
  3. Button responsiveness: 100ms
  4. Max image size in bytes: 100kb

Some of these seem very aggressive. It’s OK to adapt them to a realistic number (I certainly break them from time to time). If you’re building a very immersive game, you might drop to 30fps as animation target (as long as the frame rate is steady). And the context matters when it comes to load speed: If you’re creating a site that is primarily used at home in long sessions, users might be willing to wait longer.

Pre-commit: Headless Chrome / Lighthouse

Your best bet for pre-commit performance instrumentation might be the CLI version of Lighthouse, which reports many useful performance metrics and uses headless Chrome under the hood. Unfortunately, measuring TTI, short for time to interactive, is very difficult in an automated way. The most straightforward way is to let the application tell you when it’s ready to be used, maybe through postMessage or some other mechanism. If you use headless Chrome, you can capture the output and completely customize how you’re measuring.

Running these tests as pre-commit hook means that a commit to your codebase will fail and not even go through if the performance budget is exhausted, which means under ideal conditions, your site can never become slow in the first place!

Post-commit: Sauce Labs and other real device tests

Unfortunately the above methods only give you a rough idea of how your site or app will perform on real devices. Here’s where services like Sauce Labs come into play: They allow you to execute your tests not only on different browsers, but also on real world devices. They’re awesome, but running them as pre-commit hook would be prohibitively slow, as they can take minutes and sometimes hours to finish.

Run them as post-commit instead: If the test comes back as failure, you can either send a warning to the committer or automatically rollback the change.

Continuous monitoring: Performance beacons

With the pre- and post-commits in place, we know that our code is going to be reasonably fast on real world devices, but what about real world conditions? There’s a huge variety of devices out there, some with some incredibly frustrating quirks, and the current connection type, congestion, temperature and many more factors play into how your site performs on an actual user’s screen.

In addition, you can only ever be sure about your own code – if you include a JS file from a third party on your pages, you effectively gave them the key to your site. Everything could be fine for a week, and then they load a huge render-blocking ad or change their beacons to fire every 5 seconds. Of course, the sane solution to an evil maid that steals all your jewelry would to…well…get your keys back (AMP, anyone?), but I realize that’s not always an option (and before you protest, yes, technically AMP still requires trusting a third party – AMP – to do the right thing).

Whatever the reason, you can rarely account for all real-world edge cases, so it’s best to include some instrumentation, maybe using the window.performance API, that sends a beacon back for every page visit for you to receive aggregate real-world performance data. If you see something fishy in the graphs early, you might still be able to pinpoint it to a certain release or vendor.

Less debugging, more good times

All this stuff might sound intimidating, and will require a while to setup. Start with something basic like the pre-commit hook and one easy to track metric (e.g. one included in Lighthouse) and go from there. Good luck!