At what point did your Bubble app become difficult to maintain?

I’ve been thinking about this while working on a team project ( at Girmairi).

When an app is still small, everything can feel pretty manageable. You know where the workflows are, the database structure still makes sense, and changing one feature doesn’t usually affect five other things.

But I’m curious about what happens later.

For those of you who have worked on Bubble apps for a longer time, was there a point where you realized:

“Okay, this is starting to become difficult to maintain.”

Was it because of:

  • too many workflows depending on each other?
  • the database structure becoming harder to change?
  • repeating the same logic across different pages?
  • too many plugins?
  • several developers working on the same app?
  • performance getting worse as more users or data came in?
  • simply not documenting enough decisions early on?

I’m especially interested in mistakes that didn’t look like mistakes at the time but became painful months later.

For example, maybe something felt like the quickest way to build a feature at the time, but later made every new change harder. Would really like to hear from people who have taken a Bubble project from a relatively simple build into something much larger.

The worst issues I have seen are apps built by very bad or new developers causing a lot of technical debt that make it very time consuming and expensive to fix.

Privacy rules not being done properly or sometimes at all.

Bad database structure.

Sometimes just bad naming conventions can be horrible to sift through.

:white_check_mark: Especially with bad naming conventions and a bad database structure.

I would say you start noticing maintenance difficulties when you haven’t taken the time to plan your product on paper before moving to Bubble or code. I mean, you start in the editor with an idea but no plan, so you end up implementing the same elements on different pages rather than using reusable elements, and creating or deleting fields and databases to cover cases you hadn’t thought about before. In a few words, you end up with bad naming, poorly structured database relationships and workflows, duplicate or triplicate elements, and patches everywhere.

Today, if I had to start a new project, I would first think about the structure, relationships, database and option set structure, naming, etc. Try to think through all the parts of the project before starting to build it. And once you’ve built it, create documentation. This will be useful when you come back to certain parts a few weeks later, or if you want to hand the project over to someone on your team.

And finally, you can use Buildprint. Honestly, with Opus 5.5, things move so quickly. But of course, you also have to stay organized here, otherwise you’ll get lost in the discussion…

In addition to everything that has already been said—and what will likely be said—I would add that…

Well, there are some projects that are gradually changing here and there. For example, if you are working on a growing app that constantly requires new features and implementations, one thing is almost certain: you will need to tweak the database here and there, adding new data types, fields, option sets, and so on.

This is common for apps like these and then you will need to plan the data migrations to populate new fields/datatypes (where applicable) and remove old ones (in the past I used to put a prefix on these old things to remove them after everything was done, something like “DEL_”, but now Bubble implemented one way to flag these fields.

And, you know… not everything needs to be created as reusable from the start like reusable element, reusable event, backend workflow, or even a global variable (a feature recently implemented by Bubble). However, you will start to recognize the need for these as you start to see repetitive patterns emerge. This is also normal, some developers anticipate future repetition and build with a reusable architecture from the start, though this isn’t necessary in every case.

In other cases, you might find yourself using these reusable artifacts not because they appear in multiple places across the app, but simply to consolidate logic, a screen, or a workflow into a single, centralized location.

You should also use colors in your workflows, give them descriptive names, use folders to separate logic and flows, and adopt a naming convention that makes sense to you but without making a mess.

There was a time when the element tree didn’t use icons to distinguish between elements, so some people used emojis to identify them, and I believe, some still do.

I picked up some habits from that era, so even today I still use “:recycling_symbol:” for reusable elements . You know, some people are more visual than others :sweat_smile:. However, while some people use emojis in the names of option sets and elements, you should avoid using them in the names of data types, fields, options within option sets, and so on, they can cause problems over time and end up creating a mess rather than helping.

And yeah, documenting everything you can is incredibly helpful, while documentation should be an essential part of the process, I understand that it isn’t always easy to document things when the speed of creation and constant changes leave you with no spare time.

So, at least, you should focus your attention primarily on the most complex parts. If you can’t produce written documentation, try making videos. I personally like making videos for myself to explain complex workflows, in addition of written documentation.

This is different from the well-organized written documentation a project ideally requires. Fortunately, there are tools available today that can assist you, such as Buildprint.

I have few apps in development and maintenance for years.

  1. Proper conventions for naming data types/fields, elements, workflows, and other. Colors and folders.
  2. Extensive use of reusables
  3. Screen recording of delivered feature including front-end walkthrough and bubble setup (workflows + serverside)
  4. Trained Claude Code skill that answers any question related your app (runs locally on your machine, fed with the app’s export JSON).

wait why not just use buildprint, it’s like 10x better and it’ll build your app for you….

actually maybe I’ll make a local Buildprint mode so you can point it to an export and don’t even need a Buildprint account to read it

Ok if you run:

npm install -g buildprint@latest && buildprint open ./my-app.bubble then your Bubble app becomes TypeScript. It is much more token efficient, readable, and intuitive for agents. You will hit your rate limits less, and have much better agent output quality and speed.

See BubbleScript | Buildprint | Docs for more!

“Too difficult to maintain” is kind of code for technical debt and poor architecture. Which we’ve all been guilty of but we do our best to mitigate as much as possible at all costs.

Good database practices and architecture are key, even when small. I’ve paid this price multiple times and I’ve always regretted cutting corners when the scope was small, examples:

  • Having a single source of truth.
  • API’s should have retries and fallbacks.
  • Normalizing a database

Since you asked things that didn’t looked like mistakes, but it became painful later, here are some I’ve made:

  • Showing numbers in a text element instead of a group of type number and nesting a text underneath:
    In bubble you can’t reference a text so if you show something like Total Sales or Total Cash Collected, and then you have more metrics that go off of that one (like per month/week, etc) then you’ll end up with 5 groups, duplicates basically, with the same number, which if the expressions are all exactly the same then Bubble will bundle them and you won’t get an increase in WU.

    However, if you later decide to change how that expression is calculated (because maybe you included more Invoice status option sets or something) then you’ll need to find all those 5 places and change them all, instead of having a single source of truth.

  • Not using internal API’s enough and just relying on backend workflows: Initially in our app we just had 1 place where users could make payments, 4 years later there’s now 5 places. When the payflow was built the 1st time we didn’t consider that in the future a user would need this in other places so it would be good to have it built as a reusable element and the workflows as API’s, instead of all being a “Scheduled API Workflow” (since backend workflows don’t give a return value) and they operate asynchronously we ran into problems like “If the user clicks too quick then it breaks” and if we used database triggers too much then the app would have latency, not to mention the fact that your workflows aren’t linear so you wouldn’t know where to look to find that there’s a database trigger workflow that continues the User behavior sequence, plus a new dev wouldn’t know where to find these anyway. Having my own internal API calls in the backend meant that I could call those workflows from any reusable element, and not run into asynchronous problems in the app.
    The first time this was built I realized this at the end of the build and decided that to make it proper I’d have to re-do it but the feature worked and it passed testing so we moved onto the next task. I SHOULD HAVE RE-DONE IT RIGHT THERE, I was fresh.
    Years later, the 4 following places had to have that same structure so they all had the same latency bugs and edge case bugs. I had to go back and change all of this throughout those 5 places, it took me about a month just paying down that technical debt, when I would have just done it for 1 and the start, this was an avoidable mistake that didn’t look like it at the time.
    This was actually 2 months ago, I tried using AI (buildprint) to help me do it quicker, but I find that AI injects technical debt, it doesn’t solve it. So it was a dev only task.

  • Using Exclusion logic instead of inclusion logic: If you’re using a condition like “When, Invoice Status is not equal to paid” paid being an option set, and then you decide to add another option set then you have to go back and make sure the logic stands for all of them. If you use inclusion logic you don’t run into this problem (as much).

Hopefully my mistakes help other devs, would love to learn from others too!

Cheers!
Pedro

This would only be the single most impactful Bubble release of all time

I think the biggest thing I’ve noticed in the app world is that people want to add too many things too quickly…

They’ll have an idea for an app and build it…

then they’re suddenly thinking they need to add this and that and this and maybe that, and hey how about this feature and that feature and on and on to attract more users.

This is all very poor planning from the beginning.

You should already have users (other than your family) lined up, excited to use the app. Adding features and more features and more features to get more users puts you behind already. Usually, you’re chasing a lost cause from the start.

I mean, you can build an app on Bubble and probably start paying $32 a month for a while as you grow it. The no-brainer charge for an app where people don’t think about hesitating is $3.99 a month.

You build the app to 5000 users, and you’re at $20,000 a month in revenue at $3.99 a month.

If you need to go dedicated, I think that’s like $5K a month.

So you have $15K left for more advertising and growing your app because you’re not paying for developers and a bunch of other stacks of some sort.

I just think, and I’ve seen many times, that people think they need to keep adding features… that’s just not true. Grow the app to a very profitable place and then maybe decide if you need to add something…

it’s a hard concept for a lot of people to grasp.

A lot of times, people just need to get sensible with their apps.

Probably, I have to try it one day :slight_smile: . Until now, I didn’t need it, as my bubble-copilot skill does all the work, including dynamic expressions and such. Plus, I have my own-built bubble copilot app and Chrome extension…

why drive a toyota when you can have a ferrari!

Do you mean seeing how the copilot is doing Bubble development ticket by ticket in the Bubble editor, without me touching the keyboard or creating/updating the database with 100+ types, including adding/removing fields on autopilot, is driving Toyota :slight_smile: :slight_smile: :slight_smile: ? I’m not so sure…

who’s gonna tell him :rofl:

buildprint one shot these for one of our clients today

a revamped calendar built natively in Bubble, previously a plugin:

a built in AI chat that chats with their data:

overhauled charts:

if you want to try it, DM me your email, I promise you’re missing out! from that client:

it really is fun. the e2e testing and the double checking in prudction data a few hours in is really where the speed comes from too. can just build with crazy confidence, review/test the tricky parts, and your done

You know my email, we are connected on LinkedIn

just use code OFFICEHOURS and it’ll give you a free month

once created, install the CLI and you’re good to go: