The March update of Graphite Comics just went live. It’s one of those updates that is very significant, with major changes under the hood — but which users will almost certainly not notice at all.
This makes for an interesting opportunity to talk about balancing needs in software development. In this case, we are looking at balancing speed and efficiency against operating costs. And even more interestingly, we can look at an example where a technical decision that improved efficiency and user experience evolved, over time, to actually have a detrimental effect on user experience!
In the early versions of Graphite, we didn’t have many of the features we have now — notably playlists, notifications, favorites, and so on. We really just had a big list of titles, and when you selected a title, we went out and fetched the books for that title. In order to keep things quick, when we fetched the books, we also fetched the pages for that book. That way when you selected a book to read, you didn’t have to wait for the app to go out and fetch the pages — you could just dive right in super quick.
The user experience, naturally, was amazing. Load times are a sure way to kill the mood, and we were avoiding load times completely by pre-loading the content.
In terms of economy, there was of course a minor increase in cost. This is a given, because we were loading content that may or may not have been necessary or needed. But that cost was so low as to be negligible.
A typical example in the beginning would look like this: a user opened a title, causing the app to fetch the books for that title. Titles had an average of around 10-20 books back then, each with 20-30 pages. So we were loading somewhere around 200-600 objects ahead of time. That might sound like a lot, but it really isn’t these days (I have a mental limit of 1000 objects that I keep in my head as an ideal maximum to fetch at once, and that’s just a personal preference), and because our system is designed efficiently, the overhead and cost to make queries like that is incredibly low. Fetching all of that data ahead of time added a tiny fraction of a second to the initial query, and an even smaller fraction of a penny to the cost of the query, but saved an entire query and all the time a query takes when the issue was tapped. The result was a great user experience at a low overall cost impact.
Over time, however, a few things changed. First, we started adding many, many more books to our system. Titles could now have hundreds or thousands of books. Even worse, books were starting to come in with hundreds of pages — sometimes up to 500 pages!
Additionally, we started adding new features like playlists and notifications. Those playlists included books, and notifications announced the release of new books. Similar to the query we ran when viewing a title, we were preloading books and pages for notifications (and other objects) that referenced a book — that way you could look at your notification feed, tap a book, and immediately — without an additional query — start reading that book. Neat!
Except for those big books, and the amount of top level objects being queried. In the notifications example, we were pulling down up to 1000 objects at the top level. Let’s look at a worst case scenario: you have 1000 notifications which alert you to a new book, each which has 150 pages. That query is pulling down 1000 top level objects, each with a book (so another 1000 objects), each with 150 pages — that’s 151,000 objects in one query!
Suddenly this low impact query which greatly improved user experience by pre-loading content became our longest and slowest query by far, and completely ruined the experience on a few screens. At that point, it became much more efficient and economical to load pages on demand.
Of course, there are ways to improve on this as well, and those improvements were part of the March update. For starters, we cache those loaded pages so, as long as the cache is still fresh, you don’t have to fetch pages to loaded recently (if, for example, you open a book you recently closed).
Also, as we have done from the beginning, we load the actual page content on demand — that’s what makes Graphite a streaming platform, after all — so the real heavy lifting of page content doesn’t happen until you are very close to viewing the page itself. So all in all, the impact of this change ended up being negligible, especially in light of the improvement to load times on those screens that contained hundreds or thousands of top level objects.
If you want to see this in action, download Graphite Comics for iOS or Graphite Comics for Android and start reading today! Graphite is a free to read, ad and subscription based platform for comic books which is currently in a public beta ahead of our official launch at San Diego Comic Con 2019.
Last modified: March 20, 2019