Here’s a quick look at a few things we’ve done in the past few months to make sure Expensify runs faster than a <insert inappropriate joke here>.
Cache, Cache, Cache
The biggest change we’ve made to speed up our API was introducing a global caching layer, aware of the relationships between the data we handle, and capable of prefetching resources in bulk when necessary. This has reduced the read load on our database server by around 25% (!), freeing resources to serve more customers at any time, especially at peak load. The API is used by all of our apps, including expensify.com – so the benefits of this can be perceived across all of our services.
Similarly, all of our clients cache most of the data fetched from the API – but you guessed that didn’t you? You’re smart! So let’s look at a couple non-obvious caching examples, courtesy of our mobile apps.
In-Memory File Cache
The app uses a number of JSON files to preserve app state and persist some other non-critical data. These files are accessed frequently, changed frequently, and pretty small – which makes them great candidates for an in-memory caching layer. They’re loaded in memory when first accessed, then read and written in memory, and committed to persistent storage when the app is about to shut down. This greatly outperforms any caching provided by the system.
Gradients!
While profiling the iOS version of our mobile app, we realized drawing the gradients that acted as a background for the buttons was a performance bottleneck – even though that choice was initially made with performance in mind: fewer images to load, so it’s gotta be faster, right? Well, wrong! The initial load time for that tiny PNG is negligible and only happens once – subsequent drawing is fast as can be. Never assume! So we went back to using images for the button backgrounds, which when you think about it, qualifies as “caching” those gradients.
Asset Optimization
We all know that distributing smaller assets is important for web applications, but what about mobile apps? They don’t need to be downloaded so it doesn’t matter, right? Well we profiled that, and as it turns out loading image assets has a significant impact on launch time. Since we weren’t loading any useless images, the only solution was to shrink the ones we had.
You’d be surprised how much this helps, especially considering it’s extremely easy to do. We ran all of our image assets through optipng, a free and open source lossless PNG optimizer, which shrank them by ~20%, or ~220KB. This has no impact on memory usage and the savings are irrelevant in terms of reducing the total size of the app, but it significantly reduces the time it takes to load those images into memory, making the app launch and load new screens perceivably faster.
Brain hacks!
Sometimes, performance is in the eyes of the beholder. Here are a few examples.
Highlighting UI Controls
When I first started working on our mobile apps, I noticed that something felt a bit slow and clunky when navigating around. This turned out to be caused by a small delay between the time a button was touched and the time where it visually highlighted. The cause of the delay was the simultaneous presence of two gesture recognizers on our pages: the “pan” recognizer for scrolling, and the “tap” recognizer for button presses. The “tap” recognizer would only fire after the “pan” recognizer was sure that the current touch was never going to be a scrolling gesture. Disabling scrolling (where appropriate) resolved the issue. This did nothing to the actual time it takes to navigate between screens, but it surely feels snappier!
Animation
Did you see the new loading animation on expensify.com? That great-looking and infinitely folding/unfolding version of our logo? Things like these have been proven to reduce the perceived duration of waits. Waiting for a task to complete is boring and stressful at the same time, because there’s nothing to reassure you that the task is going to complete successfully. Animation alone plays a big role in relieving the stress, because it visually communicates that progress is being made – even if it isn’t! But I also believe that the playfulness and complexity of the specific animation we chose helps with the boredom part. It’s nice to look at and kind of mesmerizing. Additionally, it takes longer for it to complete (before it repeats) than the classic “spinners” you see everywhere – I even find myself wishing for it to run for a few more seconds at times. But don’t worry – we’re always working to reduce actual wait times too.
Beauty
Did you know that beauty plays a great role in performance? Studies have shown that users are significantly more patient, tolerant and trusting if the product they’re using is beautiful. Slow doesn’t feel so slow, bugs don’t seem so annoying. I’m sure you noticed that our design language has evolved over the last couple years, and in my eyes the results are impressive across all of our services. Now that everything is prettier, it doesn’t only look nicer – it feels nicer (and faster, and better!) as well.
What’s next?
There are many performance oriented projects cooking here – some big, some small, some planned, some in progress. You might read about some of them in this blog soon. And a lot more has been done so far in addition to the few things mentioned above. We’re working hard so that Expensify is always there when you need it, ready to make your expense reports suck less every day.