Performance is one of those things that often pop up in conversations about web development, especially when using no-code tools. It takes on different names, like speed, scaling, and capacity. These different words all point to the same question: will my app run fast enough as I keep adding users and data to it?
The good thing is that the answer in most scenarios is yes. Bubble’s no-code web building platform is capable of hosting applications that match or exceed the performance of many traditionally coded pieces of software and a lot of the optimization that takes place to make that happen is happening under the hood and being constantly improved.
Still, no matter the framework, performance is about the work that you do as the app creator. Even if Bubble is generally optimized for a fast, performant user experience, it's not impossible for you to accidentally slow your app down to a grind if you set things up the wrong way. Many Bubble users are attracted to the platform precisely because they are new to web development, and they’re unaware that many of the same principles apply to both code and no-code web development.
In this blog post, we’ll go over some of the typical pitfalls that app builders make in their Bubble apps, and what you can do to ensure a smooth experience for your Users.
For a much more detailed approach to Bubble performance, you may be interested in this author’s book The Ultimate Guide to Bubble Performance. You can also follow Petter on Twitter.
Tips for improving your web app's performance include:
Tip 1: Make it "magic" – perceived versus actual performance
Here's a helpful framework for how to think about performance: the performance of your app is a stage show where you are the magician.
Uhm… you might say – what exactly does that mean, and how do magicians fit into this? Well, magic shows are about perception. It’s not about doing actual magic – it’s about controlling what the audience sees. Designing for performance is the same thing. To your users, it doesn’t matter if your app is the fastest data processor in the world, or if it uses hours to process a single record – what matters is how they perceive its performance.
Just like the magician, you are welcome to use all sorts of tricks to make your app seem a lot faster than it is, or to distract the User while some heavy processing takes place without them knowing. We’ll get back to this later in the article.
The perceived performance of your application is all that matters – the actual performance goes on backstage where no one is looking.
Tip 2: Planning is essential
Like all development processes, no-code or not, lack of planning can send your application into chaos where old and new ideas intertwine in a chaotic ball of yarn. It’s tempting to set up your database structure, page design and workflows as you go along, letting each potential solution and new idea take precedence over the last one until you’re stuck with a bloated product riddled with yesterday’s ambitious plans.
You should spend some time before you open the Bubble editor to establish just what kind of product you are creating:
What kind of Data Types do I need? (using the Data Concept model is useful for this exercise)
What features do I have to include in this first version, and what can I leave out?
What are my User’s expectations when it comes to security and privacy?
What does my User journey look like for key processes like registering an account, onboarding and daily work?
How does that affect my page structure?
What should my design look like, and what can I do to set up a consistent look over all sections of my app?
Now these points may not seem to be obviously connected to performance, but they are: as we’ll explore later in this article, everything that you add to an app will slowly, but steadily slow it down. Poorly organized work tends to end up in a lot of legacy data lying around that bloats your app: you should know what you’re building before you build it.
I wouldn’t use Bubble as a notepad. I find Bubble works best for me for building finished applications: ideas, unfinished features and random whims (even if they are absolutely awesome) belong somewhere else.
Tip 3: Keep your pages lightweight
From a performance point of view, that means that whatever you add to the app, you also add to the finished code that Bubble generates. If you add thousands of elements (inputs, buttons, images, groups, etc) Bubble needs to add thousands of lines of code. Now, each line doesn’t make much of a difference, but gradually you’ll see that the page needs a longer time to load, the scrolling may become choppy and it all seems… sluggish. Every element that you add to the page also needs to have its size, position and styling calculated by the browser before everything is rendered on the screen.
Elements on the page are not the only thing that are added to the codebase: every workflow and action that you add in the workflow editor will also add to the total weight of that page.
The Repeating Group trap
A common pitfall on this part of an app’s development is the Repeating Group trap. When we talk about page elements, we need to talk about them from a front-end perspective – not a Bubble editor perspective.
When you add a Repeating Group with 5 elements inside of it, you can rest assured those 6 elements will not make a huge dent in your performance.
But, in the front-end those elements can balloon into a completely different monster. Let’s say you have a Repeating Group showing a list of Users with five elements showing information like profile image, name, phone number etc. In the beginning, it works just fine, but at some point you may have 100 Users. Or 1,000. Or 10,000. How does that affect those innocent 6 elements we see in the editor?
Well, you suddenly no longer have 6 elements, but 50,001 – that’s one Repeating Group of 10,000 rows with 5 elements in each row. That’s a lot of calculating and rendering that the poor User’s device has to do. Things will become visibly slow to load and choppy to move around.
Tip 4: Single-page applications can be lightning fast – if you plan them right
I know what you’re thinking: so Single-page applications are bad, since they contain a lot of elements? No, that’s not true – but we come back to planning.
Single-page applications can actually be lightning fast, since they don’t need to keep reloading the page (usually the slowest part of any website process), but instead they rely on hiding and showing groups and elements as needed. Performance is a game of compromises, and with a single-page application you are trading a slightly longer page load time with a much faster navigation. So as you can see, that decision can make very good sense for a SaaS product, but can become a problem for a website that relies on page load speed for SEO purposes. The trick is to identify what kind of compromises and performance decisions you are willing to make, and then stick to that.
If you do decide to make an app with only one page, keep the following in mind: for every feature that you add to the page, you make the page slightly more heavy. Identify which features that your Users will need to access on a frequent basis and keep those on the Single Page. Every feature that’s not needed as frequently (things like sign-up, onboarding, User profile settings, General settings, etc) you can move to a different page to keep the app nice and lean.
Tip 5: Stay on top of file downloads
A web page consists of files, pure and simple. We’ve already covered that the Bubble code base is a file that needs to be downloaded, but have you thought about all the other good stuff that you add to a page that adds to the total download size?
Keep in mind that most files are only downloaded once for each device; so this is particularly important for first-time users and search engine bots.
Images often make up the majority of a page’s total size since, by their very nature, they contain a lot of information. Keep your images small and keep your static images compressed with a lossless service like Tinypng.
Fonts and icons
Fonts and icons are actually in many ways the same thing: a file that contains symbols. It doesn’t really matter if they are letters or smiley faces – they are downloaded in packages. Try to minimize the number of different fonts that you use, as each one needs to be downloaded. The same goes for icon packs. If you are using Bubble’s built-in icons (which are part of the FontAwesome library), you should avoid using the Material Font library too (since that means every User has to download two libraries).
Tip 6: Use Chrome Developer Tools
The hair on the back of your neck may stand up from the mere mention of DevTools. But what seems like a highly complicated, bloated piece of software for programmers is actually an extremely useful tool for everyone who works with web development, including you, the no-code app builder.
So what exactly can Chrome Developer tools do? Well, the way your application works is by ongoing communication between the device the User is on (computer/phone/tablet) and the server (Amazon AWS). You may think that this information is what you see on the screen in your app – and it kind of is – but the fact is that a lot more information is exchanged to keep your app loading and running.
DevTools opens up this line of communication for you to check out. By opening DevTools and looking at the network tab, you can see all network activity going on on a single page, such as:
Listing all files that are downloaded on a page and their size (images, fonts, JS, etc)
Calculating how fast a search performs and how much data it has to download
Showing the content of all communication between your app and the server (searches, workflows, downloads, etc)
Showing you how to use DevTools is out of the scope of this article, but you can check out this DevTools and Bubble tutorial to get started if you want to learn more.
Tip 7: Avoid complex searches and lists
In many cases, when you need to return data from your database, you will be relying on a search (as opposed to something that you can reference directly, like the Current User).
A search behaves in a straightforward way: you tell Bubble what you want to search for (Users) and then you provide a set of constraints that these Users need to match: the first name is John, year of birth is 1984 and the country is USA. Bubble proceeds to use those constraints to rule out all the Users that don’t match, and then return the list of Users that do.
Generally, searches are very fast. Bubble does a lot of complex work under the hood to optimize that process, and you’ll get the result within a few tenths of a second. So how do we know exactly what a complex search is?
The answer is not entirely straightforward, since the potential possible number of scenarios is so huge, but we do have some things to work on.
Searches that forces Bubble to check each row
The way Bubble searches (among other things) is by use of indexing. Without going into too much complicated detail, indexing is a process that helps the search engine exclude vast portions of the database in one quick swoop, drastically reducing the number of records that you have to go through. For example, if you search by Users that are born in 1984, you can quickly exclude all Users that are born before or after that, no matter what their name is. When we get to the second constraint (first name) we already have a much smaller dataset to work on.
But searching for a number is an easy process; and believe me, setting up a much more complicated one is very easy. Take a look at the constraint below:
In this case, it might seem like we’re simply asking Bubble to search for Users that match a number (just like the birth year in the last example). But what we’re actually doing is asking Bubble to perform a second search for each row in our original search. What does this mean in practice? Let’s revisit our list of 10,000 Users from earlier: if we set the search up this way, we’re no longer performing 1 search, but 10,001. Yikes!
Now, Bubble’s internal processes may be able to process that more efficiently, leaving the numbers a bit exaggerated, but they serve just fine as an example of just how easy it is to drastically complicate a search to the point where you can experience a server timeout.
Think about constraints as a process of elimination: the more simple, straightforward information you can give Bubble to exclude records, the faster the search will be. Whenever you push Bubble to perform some sort of calculation on each row, you’re raising the temperature of Amazon’s server parks.
Tip 8: Think about how you structure your database
So a search can be set up to be simple or complex, but what does that tell us about the database? It helps to think about the database as a spreadsheet. Each row is a record (like one User) and each column is a field on that User (like first name, last name and email address). This kind of information is known as structured data. It’s perfectly recognizable, easy to understand even for a computer and contains only small pieces of information in each cell (such as “Last Name, First Name” and [email protected]).
In the Bubble ecosystem, we often call this a Data Type’s Data Weight. The example above has a low data weight, which means it’s both quick to search through and a small amount of data to download. So what gives a Data Type a high data weight?
A high data weight often comes from unstructured data. This is information that is longer and cannot easily be organized and searched through by a computer. For example, the article you are reading right now is a piece of unstructured data: it’s a long-form text post containing all sorts of information. This is saved in the same way as the first name and last name, only it’s a lot longer. If we go back to the Excel visualization, you can already see the spreadsheet becoming a lot more messy. One article is fine, but when you add 10,000 blog posts – you’ve got a whole lot of text for Bubble to search through.
The number of fields you set up can also have an impact on how fast Bubble is able to search through the information. I’ve seen apps with hundreds of fields on a single Data Type, which bloats both searches and the downloading of data – keep it lean!
But… you may say. I need to store all that data! I can’t have a blog without blog posts! Right you are of course – performance doesn’t trump every other priority in your app. Still, there are things you can do to speed things up even if you have a lot of data stored. We’re back to the magician role here: your job is not to set up a database that your Users expect. Your job is to set up a great app experience.
Tip 9: Move heavy workloads to the server
Sometimes you’ll need to run workflows that take time. This is not a Bubble problem – it’s a problem for any kind of computation. If you have to make changes to a lot of records, perform complex calculations or searches or otherwise push the server to do a lot of heavy lifting, you should move those processes into backend workflows.
Bubble is built to be responsive to a User’s actions. In other words, if a User clicks a button to start some sort of process, Bubble will let the User know how that process is going by showing the loading bar or otherwise freezing its processing for a little bit. In the meantime, the User simply has to wait it out, making your app seem slow. Aah, but we’re magicians: we can move this process backstage, where the User doesn’t see it. By using backend workflows, the server can chew through a lot of work without the User noticing any difference, and you can even schedule those workflows to run at specific times.
If you have to run a process on a list of records (performing a looping workflow), I recommend you look into using recursive workflows. You can even set up Triggers on the server that react and run a process every time a specific change is made in the database, moving even the launching of the workflow into the backend.
Don’t assume that all workflows need to take place on the page itself. Familiarize yourself with Bubble’s backend and move the heavy loading over there.
Tip 10: Communicate clearly
Let’s stick with the metaphor of a magician for just a bit longer.
As we discussed in the opening, the only performance that matters is the one that your Users perceive. Since you are in full control of what’s happening on the screen, you are free to communicate how a process is going in any way you like. You can give your User the illusion that a workload is finished even if it isn't. You can make it seem like it’s moving fast even if it’s actually glacial. You can even hide the fact that any work is going on at all. Complete honesty about every point of delay in your application is not the kind of honesty your parents raised you to stick with – it’s not helping anyone. So think of clever ways to make your app seem like a finely oiled machine, even when it isn’t and your Users will be all the happier for it.
That doesn’t mean honesty and transparency is all bad of course: sometimes, a little wait is unavoidable. What do you do then?
Show your users some interesting stuff or keep them occupied. If you have a heavy process that has to run each time a user signs up to your service, have the user focus on a form or go to the next step of a user registration process while the workflow is running in the background. Show a nice loading information, a welcome video, a quote of the day, a dad joke or whatever you can think of to keep them busy while the server finishes up its task.
You’d be surprised at just how forgiving users are if you communicate early and clearly. Some notoriously slow online processes (like big data imports, copying data or making backups) can take minutes or even hours to complete no matter what platform you’re using. Even going as far as to simply tell your users that “this is going to take some time, but we’ll email you when it’s done” is a perfectly good way to communicate. Brilliant. There’s nothing we can do about that processing time, so why pretend – make it easy for the user. They can go get a cup of coffee while we finish things up. Imagine if you said nothing and the app simply froze for ten minutes. Now that would be poor communication.
All your favorite apps do the same thing: keep an eye open for it and learn from it. You’ll get some chuckles along the way seeing how applications hide slow processes (like Uber showing you a fancy animation on a map, as if they were actually physically out there looking for a car for you and the money transfer service Wise showing you an animation of the money physically moving across the planet) and it’s one of the fun creative challenges that we as developers get to throw ourselves at. Yes, we’re fun at parties.
I’ve read all these tips on improving my app’s performance. What’s next?
No programming language is fast on its own. It takes planning, experience and know-how to set up efficient applications no matter what language you speak. Bubble is no different: you can set up apps to run like well-oiled machinery, or you can slow them down to look like grass growing.
It all depends on the decisions you make along the way.
But the actual performance of the app is only a part of the picture: perceived performance is a mixture of technology, design and psychology. Don’t mistake your goal to be “make the fastest app possible”. Even though you should of course set your app up to be efficient, your goal should be to design an application that makes your users happy. That means building an app that’s fast enough, and making up for the rest by clever use of design and communication.
Performance is an important metric, but not the only metric: find the right balance as you prioritize where you put your development hours. Getting your product over the finish line and to the market is more important than shaving hundredths of a second off your page load time. Take note of processes that are slow enough to hurt your user experience, but don’t stopwatch every button click: all apps have some delays, and Users are fine with that.
This article gives some pointers that will get you going, and if you really want to dig deeper into how to set up Bubble apps that perform well, check out The Ultimate Guide to Bubble Performance and our other Bubble books.