Skip to content
Back to the archive
From the archive. Written in our Bubble.io years and preserved as published — the tools have moved on, and so have we (here's the story).

Legacy

Lessons learned from taking over Bubble.io projects (“why is my Bubble App slow?”)

Lessons learned from taking over Bubble.io projects (“why is my Bubble App slow?”)

Photo by Krismas on Unsplash

Introduction

Bubble makes it easy for anyone to build software

Bubble’s vision is to democratize software development, creating opportunities for anyone to build software, not just the privileged few. This is a beautiful and worthy vision, and I applaud the founders for this (see more here in a recent keynote).

As a result, many have embarked on the journey of learning Bubble, or perhaps being time-poor, have engaged a developer to build their idea.

But there is a catch…poorly built apps

Within the Bubble community, there has emerged a trend of poorly built apps, that for various reasons have failed to meet the expectations of their owners.

I’ve heard so many stories.

“Our app is super slow to load”

“After phase 1, they said they needed to refactor the whole app to make small changes required as part of phase 1.1”

“They went over time and exhausted my budget, but didn’t deliver the full solution promised”

“They were good during the initial MVP build, but as I wanted to make more changes over time they took weeks to get back to me”

“I was ghosted by my dev”

Worryingly, it’s not only first-time developers or solo freelancers generating these kinds of stories - but some of the self-proclaimed “reputable” Bubble agencies listed on Bubble’s site.

Template troubles

Others have focused on template-based apps and template modifications as a service, but again these approaches can create a short-term win and a long-term headache for everyone involved.

The solution?

I’ve previously written about the things to look for in a good Bubble Dev, and the ways MomentumGroup.tech takes a long-term relationship approach to Bubble development vs simply churning out MVPs in the shortest time possible.

The best developers consider the overall vision of the app owner and make decisions with this in mind, not just how they can make some quick money and move on.

Lessons learned from Bubble app takeovers

In the last 12 months, we’ve built around 15 substantial Bubble apps (some apps with up to 1,000hrs of development time) and taken over several Bubble apps from others (devs and non-devs).

No right or wrong

From the outset, I acknowledge that there is no right or wrong way to do things in Bubble. In many cases, Bubble gives the developer up to 3–4 ways to achieve the same outcome.

It is up to the app developer to choose the better alternative relevant to the specific scenario, which might be different from the best approach in other scenarios.

Being a good developer is a combination of knowing all the options the tool offers, and then being strategic in the choice of which option will be scalable, secure, and maintainable.

No one is perfect

I readily acknowledge my own past mistakes and that sometimes making mistakes is the way that we learn. Mistakes are part of the process of improving and delivering better quality, more reliable applications. It is certainly possible that anyone can learn Bubble and build great applications, but it takes time and effort.

The best developers

What I can control and commit to as a developer is doing the best I can for my client, explaining to them that app development is an open-ended process, that sometimes we need to use trial and error, but that we will always try to put our client’s needs first and plan ahead for the best outcomes.

The best developers still make mistakes but are also highly adaptable, can quickly identify the sources of a mistake, and improve their solutions without excessive downtime or interruption to service.

For the rest of this article, I’ll share the common problems we see and the lessons we’ve learned.

Failure to plan is planning to fail

No documentation

The first big mistake is simply not documenting the solution. The eternal challenge of software documentation (as a whole, well beyond Bubble) is knowing how much documentation is enough to add value, while not overdoing things.

At MomentumGroup.tech, our principles are enough documentation and documenting the intention.

For each project, we will typically document:

  • A product brief (a 1–2 page document that describes the project intention at a high level), which will not only capture phase 1 requirements but also capture the app owner’s ideas for future phases.
  • The overall solution architecture (which services Bubble connects with)
  • The site map (a plan of each page and their hierarchical relationship)
  • The entities and their key relationships (we use a UML entity diagram)
  • The database tables and fields (we use a database schema diagram)
  • A table of privacy rules (a row for each database table, a column for each user role)
  • A table of API workflows (a plain description of what each should achieve)
  • A diagram of the main elements on each page (e.g. headers, footers, navs, security containers, group containers, url parameters)
  • A diagram of any complex page or backend workflows (e.g. whose function is not immediately obvious from reviewing the workflows in the Bubble editor, e.g. workflows that spill over into multiple API calls or custom events)
  • An agile board of phases and tasks, including release notes for each major deployment of new changes into Live.

The documentation process

For a reasonably simple app (say up to 4 weeks dev time), the lead developer on the project should be able to create and document the technical design work in around 1–2 days (we use Lucidchart) from the Figma design file (created by a UXUI Design team). Then a developer can implement the UIs from Figma and the technical design from Lucid for the rest of the project.

Anything that needs to be explained to an incoming developer (to save them hours of reverse-engineering the solution to find the answer) should be documented in the above format. Failure to plan and document the intention of complex data structures and workflows is passing on a heavy burden to future developers to pick up.

Current state documentation and app review

If your app is not working properly or performing properly, we are able to work through your existing app and document the Current State solution in order to recommend optimisations (visit momentumgroup.tech and send us a message).

Get Will Ericksson’s stories in your inbox

In our experience, this usually takes around 2 days of work, sometimes more for very complex apps. When we start documenting apps, these are the common problems we have found:

The data model

Not enough Tables, and/or a lack of relationship between Tables

We recently took over an app that had only four tables, with over 100 columns in some of the tables, and no relationships between the tables. Unsurprisingly, the app was loading slowly.

We re-evaluated the entities in the app and split the data model into several more tables, significantly reducing data load times.

Not using Option Sets

Option sets have a unique role in Bubble: Storing short, static lists of values that are loaded into the browser on page load, thus being very performant in terms of load times when the user clicks on, say a drop-down box.

They are a perfect way to centrally manage categorical data values that do not change regularly. Good examples of option set usage include:

  • User roles: User, Team Member, Manager, and Admin.
  • Statuses: Active, Inactive, Archived
  • Days of the week: Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday.

Option Sets cannot be changed by the users of the app, but only the developer. Adding Option Sets as a field in your table creates a very efficient way to filter the data shown in a table.

Every Bubble developer should know when to use Option Sets, and it is a red flag when they are not used at all.

The alternative — using free text fields — introduces the possibility of error (mistyping one of the values in a conditional logic statement causing that statement to fail, and time wasted debugging) and a maintenance headache (if you decide to change the label of one of your options — e.g. “admin” to “Admin” — you would have a major task on your hands rewriting logic and modifying database rows, whereas Option Sets make this operation painless).

Overusing Option Sets

Another problem we have encountered is when the developer gets a little too excited about their newly found knowledge of Option Sets.

In this scenario, Option Sets are used when a better scenario would be creating a separate Database Table.

Scenarios we have seen include:

  • Where the users of the app would like to update the data without going back to the dev (possibly a table structure would better support the next phase of dev in the app’s roadmap)
  • Where there are a lot of values in each Option Set (e.g. we’ve seen 10–100 values). Option sets are not configured to make it easy to work with large numbers of values in an Option Set. The Bubble UI makes it difficult to navigate all the values, especially if they have Attributes, and you cannot import or export Option Set values as CSV as you can for Table data.
  • Where there is heavy use of Option Set attributes (secondary fields within the Option Set) including relationships between different Option Sets stored in Attributes. Again, Option Sets cannot be viewed in the same way as Tables, and the ability to navigate and maintain the contents of an Option Set is limited compared to a Table.

Bringing these kinds of Option Sets into Tables creates a much more future-proofed app.

Inadequate privacy rules and/or application rights settings

Data security is of key importance when developing web applications, and we have taken over several Bubble.io applications where tables containing personally-identifying information were set to ‘Public’ in the Privacy Rules tab.

This is a major risk for the app owner, and should never occur. Luckily this is also easy to fix, however, can be time-consuming because each aspect of the app is affected by privacy rules and can require a period of testing and bug fixes.

Even worse, we’ve seen instances of Bubble apps where the Application Rights (Settings > General tab) was configured as Public. Not only does this expose the app’s data to the world, but also sensitive API keys. This should always be set to Private. External developer access can be controlled using the Collaboration tab in Settings if required.

Expression logic

Excessive use of nested logic

Where possible, dynamic expressions (blue coloured text in Bubble) should not be heavily nested. Heavily nested expressions are difficult to understand, debug and correct should errors be found.

An alternative is to break expressions down into several elements, for example, using invisible groups with data type = number, and then referencing each of these groups, conveniently showing the working out.

Excessive use of conditionals

Another pitfall is when elements have many conditionals, oftentimes combined with non-explicit logic. In projects we’ve inherited, we’ve seen elements with over 20 conditionals.

An alternative is to break up the conditionals by creating a number of different elements. For example, rather than creating one button with 7 different conditional logic statements, instead, create 7 buttons with 1 statement each.

On the point of non-explicit logic, my preferred approach is to map out your options into the number of discrete scenarios you are trying to cater for. This way, you can make sure the logic explicitly works for each and only that scenario, rather than allowing for overlap in the logic of each scenario.

Not tidying up

Every part of the expression should have a purpose. If parts of a dynamic expression are redundant, don’t leave them in. Future developers will wonder what you were thinking and curse you (hopefully in jest!) as they work through your app to understand why you included the unnecessary piece of logic.

Workflow logic

Similar to nested logic and conditionals, very long workflows with lots of conditionals on each workflow step can be harder to read than say using more, shorter workflows without conditionals.

It’s Showtime

Maintaining your app in production

The final kind of issues we’ve seen are apps that may work well within the controlled dev environment, tested with small workflows and volumes of data, but failing to perform when being used at scale.

Thinking forward to launching your app and having real-life users is what all developers should consider, but Bubble’s empowering of anyone to build apps means that many apps only just work during the developer’s testing, and haven’t considered what will happen when hundreds or thousands of users are trying to work with the solution.

Inefficient expressions or workflows

For starters, my rule of thumb is that nothing should take a really long time. If I write an expression, and it is taking 3–15 seconds to display some data in a repeating group or dropdown box or complete a single workflow execution, then I’m immediately concerned that my solution won’t scale and isn’t way Bubble was designed to be used.

This is especially true if the CPU ever hits 100% (Logs > Capacity).

In each of these cases, look for other options to improve performance. Most likely, you will need to refactor your database or break a complex expression into multiple steps, some of which execute on page load, others which execute on user demand.

I’ve often tried to get Bubble to make multiple, small requests to the server from the browser (e.g. auto binding or writing / deleting data when checkboxes are checked/unchecked) rather than less frequent, larger requests.

Timing

Solutions that use Pauses to allow time for critical data to return from an API call are not workable in production. These may fail without error if the conditions required are not met, and cause debugging headaches later on.

Data maintenance

It’s nice in theory to have a third normal form database where no data is replicated in multiple tables. However, in Bubble, it can be really useful to store multiple ways that tables can reference each other.

For example, it is often really useful to store a User field in most tables in case you ever need to find all data relating to a User (considering in some apps, the creator user won’t be the user you are looking for). Imagine you receive a GDPR request to delete all data for a user.

In a similar way, you might also wish to store the Order against data types that relate to Order, even though you could reference these types another way. The reason for this is that Bubble makes it difficult to reference data through another table in some situations, and bulk maintenance on existing datasets (e.g. delete or archive off orphaned records relating to a non-existent order) becomes very painful without these columns.

Conclusion

A well-designed, well-structured Bubble app should be able to handle all operations within a reasonable amount of time, and not feel excessively slow to the user. It should also work well at a reasonable scale (e.g. thousands of users connecting concurrently). Finanlly, it shouldn’t be difficult for the next developer to understand and maintain.

MomentumGroup.tech can help existing Bubble App owners with documentation and optimising existing Bubble apps, as well as provide Bubble App dev support packages and new app builds. Learn more here.

Looking to build and launch a career in Bubble? Join us at Momentum Academy

Where this story went

We spent five years mastering Bubble. Now we build with AI.

Momentum was one of the world's leading Bubble.io agencies — that's why we wrote this article. These days we build and migrate the same kinds of products with Claude Code: real, portable code your own team can amend with AI. If you're weighing up a move off Bubble (or any platform), we've made the journey ourselves.

Share: