- Our Engineering team recently endeavored to improve some of the slowest parts of building in Draftbit
- Two major parts of your experience are now significantly faster
- Other parts of building are also faster, as a result of this project
Here's what Sr. Engineers Patrick and Allen had to say:
Code generation and manipulating the component tree are two actions that are core to the experience of using Draftbit. It’s how you make Apps and see what they look like. On our server, we support code generation and manipulating the component tree with an essential duo of functions:
Lister: resolved the list of components inside a given screen.
Tree Maker: called the first function, then converted the list to a tree.
For various reasons, Lister recursively queried our database from
FlatList branches. These round trips to the database were slow, frequent, and sequential. So adding components to these branches and seeing your changes reflected in Web Preview was also slow. We knew we could do better!
Tree Maker not only used the Lister (slow!), it did additional work to verify the correctness of the tree it was building (slower!).
First, we optimized the Lister operation by writing a recursive PostgreSQL function that resolves an entire tree of components in a single query, including the components that the Tree Maker would typically fetch. The new Lister completed more work 2-3x faster than the old function for screens with
FlatList, and 5-10% faster for all other operations.
Then, we optimized Tree Maker to quickly do its work in-memory with no further trips to the database. Finally, we combined Tree Maker with the Lister because they were so frequently called together.
As a result, we saw an additional 2-3x speedup in code generation for screens that contain
For the visual folks out there, here are example Sentry.io performance traces on our code-rebuilding query before these improvements (1.2 second total!):
And after (220ms!):
And we still clearly have room to improve!
So what did we learn? A few lessons stand out:
- First, profile your apps! We had an obvious target here due to good data (thanks Sentry.io!). You don't want to waste your time optimizing what isn't slow.
- Second, always try to reduce the number of trips to your database! Sometimes the time/complexity it adds to write a view, a function, or a few more joins pays back dividends in response speed.
- Third, question your abstractions when things are slow! We were doing a whole lot of work in our "tree builder" that, on closer inspection, could have always been offloaded into the "lister."