Grocery shopping is tedious and time consuming. In search of a more streamlined experience, I decided to try Instacart. Unfortunately, using their site is also tedious and time consuming.
In the video you’ll see I attempt to visit the landing page of my local grocery store and, after that loads, do a search for yogurt.
Over 25 seconds to perform a single load and search. Just loading the Cub Foods “storefront” page took 14 seconds and 154 requests.
On the plus side there were some very nice placeholder graphics that set the mood while I waited.
On the other hand it took 3 seconds to get the single page layout skeleton back.
The images to populate the placeholder template took another few seconds:
If you notice the first segment of the URL after the Cloudfront domain is
/156x/. These endpoints will return custom sized images and that first segment is the requested dimensions. You can change that segment to
/300x/, for example, and you’ll get a bigger image that maintains aspect ratio (it will be 300px wide by whatever the height should be to keep the ratio). You can also specify a height if you want a different effect:
Cool, but this is almost certainly part of the reason loading uncached images is so slow. The origin behind Cloudfront is doing a lot of work to make a custom image and send it over the wire on-demand.
In all fairness, these images have the proper cache response headers, so subsequent page loads will have the images served from the browser memory cache. But that first hit is very slow.
It isn’t just the page load and images that are slow. The servers responding to API requests are taking their time as well. Some of the calls to populate data on the page took over 5 seconds!
One of the endpoints shown here fetches coupon information. In the initial loading video you can see the coupon section is particularly slow to render. Even though there is content loaded below the fold, the user has no idea since the placeholders are still shown for the coupon section until that call returns.
This is where the hybrid rendering model falls apart a bit. There is a lot of dynamic content being rendered post page load. And since the API is slow the user is getting even more placeholders.
As the user scrolls down the page there are on-demand API calls made to show products from each grocery department. These calls can take upwards of 2 seconds each. And there’s a lot of them.
For each one we get more placeholder graphics until the server returns its response:
Placeholders do a nice job of minimizing jank or cumulative layout shift but they are a poor substitute for the actual content. Paradoxically I find they can also make a site feel slower since the UI is changing out from under the user so frequently.
There’s a few articles on the Instacart engineering blog discussing the back-end technical implementation of the site. In both the linked articles they discuss “improved performance” and the existing “healthy performance” of the site. Perhaps the main problem is they don’t think there’s a performance issue to fix?
Most modern technical stacks are capable of serving pages and API calls in sub-second time if that’s the company’s goal. I suspect in this case they have limited resources and other priorities. Maybe things are better in the phone app, but I think I’ll stick with going to the grocery store for now, it’s faster.