# 🎉 Better Boilerplate by Not Quite Unicorns - Our take on what a good Bubble app looks like

**URL:** <https://forum.bubble.io/t/better-boilerplate-by-not-quite-unicorns-our-take-on-what-a-good-bubble-app-looks-like/381517>\
**Category:** Showcase\
**Created:** [September 24, 2025, 10:09am UTC](https://forum.bubble.io/t/better-boilerplate-by-not-quite-unicorns-our-take-on-what-a-good-bubble-app-looks-like/381517 "2025-09-24T10:09:01Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![georgecollier](https://sea1.discourse-cdn.com/bubble/user_avatar/forum.bubble.io/georgecollier/32/182327_2.png) [@georgecollier](https://forum.bubble.io/u/georgecollier)\
**Post date:** [September 24, 2025, 10:09am UTC](https://forum.bubble.io/t/better-boilerplate-by-not-quite-unicorns-our-take-on-what-a-good-bubble-app-looks-like/381517/1 "2025-09-24T10:09:01Z")

</div>

Today I’m open-sourcing Better Boilerplate - an opinionated Bubble boilerplate with native Stripe integration.

If you’ve wondered “what does a good Bubble app look like?”, this is _our_ answer.

> **Public editor** → [https://notquiteunicorns.xyz/boilerplate](https://t.co/JFrbu0osUB)
> 
> **Template** → [Better Boilerplate By NQU Template | Bubble](https://bubble.io/template/1758468402396x851054662327730200)
> 
> Run as the user to view the dashboard.

 ![image](https://us1.discourse-cdn.com/bubble/original/3X/6/d/6dc26b8ea4d9042ffe1a79293a5dd5571c96a43d.jpeg)

One of Bubble’s biggest problems: it’s hard to see how good apps are built - most are private, and the template marketplace is full of apps that aren’t built well. So we made a reference you can inspect, borrow from, and ship with.

Better Boilerplate is a public editor designed to share how we build at Not Quite Unicorns: pragmatic patterns, clear maintainable structure, and a robust production-ready Stripe setup.

This is opinionated. You may disagree with some choices - please say so publicly. Debate makes the ecosystem better. If we’re inefficient, we’re leaving money on the table for us and our clients.

Inside you’ll find:

- Maintainable build patterns
- Security practices
- Database conventions

All three are critical for secure, performant, maintainable apps.

If we’ve done our job, the app is mostly self-documenting. You can view the app to see how it works, then use the docs to understand the whys.

This is what we _think_ is good. There’ll be some things you disagree with - please share them, publicly! Discussing these things benefits us all. If we’re building things inefficiently, we’re leaving money on the table for ourselves and our clients and I want to know about it.

> **Dig into the editor** → [https://notquiteunicorns.xyz/boilerplate](https://notquiteunicorns.xyz/boilerplate)  
> **Read the rationale + comment on the docs** → [https://docs.google.com/document/d/1K2nthZadHXv44VFAxQ-QJXYSfmjq-Z8XxSjxqhJ2CYg/edit?usp=sharing](https://docs.google.com/document/d/1K2nthZadHXv44VFAxQ-QJXYSfmjq-Z8XxSjxqhJ2CYg/edit?usp=sharing)

---

<div class="post-metadata">

**Author:** ![branimir.ivanov](https://avatars.discourse-cdn.com/v4/letter/b/977dab/32.png) [@branimir.ivanov](https://forum.bubble.io/u/branimir.ivanov)\
**Post date:** [September 24, 2025, 11:27am UTC](https://forum.bubble.io/t/better-boilerplate-by-not-quite-unicorns-our-take-on-what-a-good-bubble-app-looks-like/381517/2 "2025-09-24T11:27:28Z")

</div>

hey nice boilerplate

noticed that you fetch the object from Stripe in the first steps when webhook is triggered, which is a great step. however, since the webhooks are exposed AND seeing that the Subscription data type privacy rules allow User to see all fields in the linked Subscription object, that user could try to send malicious requests to webhook endpoint with values they already have access to

for important webhooks like Stripe i tend to use combination of your method (fetching the object from Stripe at the end of webhook URL in Stripe) PLUS using API token to secure the workflow (adding ?api\_token=API\_TOKEN in Stripe). it identifies the API call as made by an App Admin so both:

- this workflow can be run without authentication

- ignore privacy rules when running the workflow

can be unchecked

---

<div class="post-metadata">

**Author:** ![georgecollier](https://sea1.discourse-cdn.com/bubble/user_avatar/forum.bubble.io/georgecollier/32/182327_2.png) [@georgecollier](https://forum.bubble.io/u/georgecollier)\
**Post date:** [September 24, 2025, 11:31am UTC](https://forum.bubble.io/t/better-boilerplate-by-not-quite-unicorns-our-take-on-what-a-good-bubble-app-looks-like/381517/3 "2025-09-24T11:31:01Z")

</div>

> [@branimir.ivanov](#):
>
> Subscription data type privacy rules allow User to see all fields in the linked Subscription object, that user could try to send malicious requests to webhook endpoint with values they already have access to

They could, and they won’t be able to do anything malicious.

The reason we fetch the data from Stripe after receiving the request, is that:

- if the user provides a valid object ID with malicious values, we fetch the results from Stripe and pretty much just update the object (likely with no change, or a ‘correct’ version from Stripe)
- if the user provides an invalid object ID, it fails

> [@branimir.ivanov](#):
>
> PLUS using API token to secure the workflow (adding ?api\_token=API\_TOKEN in Stripe). it identifies the API call as made by an App Admin so both:

So, this isn’t actually useful (alone).

1. We don’t want to hand out an admin API token any more than we need to. Stripe likely stores these in plain text which isn’t what we want for an admin token that an external service can access.
2. The idea that ‘unchecking this workflow can be run without authentication’ and using an admin token secures it is also not quite right. Any logged in user is authenticated, so unchecking that checkbox has almost no security value. That checkbox does _not_ check the user is an admin - only that they’re authenticated (who they are - not whether they’re permitted to do something). If you did want to secure it by verifying the request came with an admin API token, you should add something like ‘Current user’s unique ID contains admin’ as admin API tokens have a special unique ID that can be used to verify the authentication is from an admin token.

---

<div class="post-metadata">

**Author:** ![branimir.ivanov](https://avatars.discourse-cdn.com/v4/letter/b/977dab/32.png) [@branimir.ivanov](https://forum.bubble.io/u/branimir.ivanov)\
**Post date:** [September 24, 2025, 12:10pm UTC](https://forum.bubble.io/t/better-boilerplate-by-not-quite-unicorns-our-take-on-what-a-good-bubble-app-looks-like/381517/4 "2025-09-24T12:10:26Z")

</div>

you make good points about the stripe fetch protection, but i’m still not convinced about leaving the endpoint completely open and without privacy rules.

i just tested it - simply making POST requests to webhook endpoint /stripe-subscription, WF creates new database entries ([Subscription data type](https://bubble.io/page?id=better-boilerplate&name=index&tab=Data&type=api&type_id=subscription)) every time, even without valid stripe data.

when using api tokens, the workflow only runs when authorized, which prevents unauthorized workflow runs and database writes.

appreciate you sharing this with the community though!

---

<div class="post-metadata">

**Author:** ![georgecollier](https://sea1.discourse-cdn.com/bubble/user_avatar/forum.bubble.io/georgecollier/32/182327_2.png) [@georgecollier](https://forum.bubble.io/u/georgecollier)\
**Post date:** [September 24, 2025, 1:00pm UTC](https://forum.bubble.io/t/better-boilerplate-by-not-quite-unicorns-our-take-on-what-a-good-bubble-app-looks-like/381517/5 "2025-09-24T13:00:37Z")

</div>

> [@branimir.ivanov](#):
>
> i just tested it - simply making POST requests to webhook endpoint /stripe-subscription, WF creates new database entries ([Subscription data type](https://bubble.io/page?id=better-boilerplate&name=index&tab=Data&type=api&type_id=subscription)) every time, even without valid stripe data.

Ah, it’s because this workflow was missing a terminate action like all of the other ones 🙂 Still, whilst you could create empty rows, that’s not particularly useful! The design of the endpoint means that if mistakes _do_ happen that let a user through that shouldn’t be, they still can’t do any real damage.

Everything we build in Bubble should be built in a way such that there’s no single point of failure (and you found one example of why there’s multiple protections)

 ![CleanShot 2025-09-24 at 14.02.55@2x](https://us1.discourse-cdn.com/bubble/original/3X/6/f/6fb4c8c8700d8c4de82076b015a83901bca89624.png)

> [@branimir.ivanov](#):
>
> when using api tokens, the workflow only runs when authorized, which prevents unauthorized workflow runs and database writes.

No, that’s not true.

Take this workflow (I just edited it for demo) as an example:

 ![CleanShot 2025-09-24 at 13.59.28@2x](https://us1.discourse-cdn.com/bubble/original/3X/9/f/9ff690389b197c27f4c52bdb1180ff43b1b5f3e9.png)

This would _not_ be secure.

It seems to me that you think that it is secure because it requires an admin token. That is _not_ true. It requires to be _authenticated_, and _any_ user with an account in this case can be authenticated. So, all it does is restrict the endpoint to subscriptions the user’s privacy rules permit them to access (their own subscriptions).

Edit: You might find this post useful!

> [@Why your backend workflows might be your app's biggest vulnerability](https://forum.bubble.io/t/why-your-backend-workflows-might-be-your-apps-biggest-vulnerability/348030):
>
> Understanding and Securing Public Backend Workflows in Bubble The Common Misconception After misconfigured privacy rules, public backend workflows represent the second most prevalent security issue in live Bubble applications based on what I’ve seen from auditing dozens of apps. There’s a widespread misunderstanding about their security model that you may not be aware of. Many developers assume that requiring authentication is sufficient to secure a workflow. However, this assumption is incorre…

---

<div class="post-metadata">

**Author:** ![lindsay\_knowcode](https://sea1.discourse-cdn.com/bubble/user_avatar/forum.bubble.io/lindsay_knowcode/32/92967_2.png) [@lindsay\_knowcode](https://forum.bubble.io/u/lindsay_knowcode)\
**Post date:** [September 24, 2025, 1:57pm UTC](https://forum.bubble.io/t/better-boilerplate-by-not-quite-unicorns-our-take-on-what-a-good-bubble-app-looks-like/381517/6 "2025-09-24T13:57:01Z")

</div>

Bubble them-selves are largely silent on “best practice” so I applaud this - and George putting money where his mouth is. kudos.

There is a lot of mis-information and out-of-date information getting baked into LLMs.

---

<div class="post-metadata">

**Author:** ![georgecollier](https://sea1.discourse-cdn.com/bubble/user_avatar/forum.bubble.io/georgecollier/32/182327_2.png) [@georgecollier](https://forum.bubble.io/u/georgecollier)\
**Post date:** [September 24, 2025, 2:35pm UTC](https://forum.bubble.io/t/better-boilerplate-by-not-quite-unicorns-our-take-on-what-a-good-bubble-app-looks-like/381517/7 "2025-09-24T14:35:44Z")

</div>

> [@lindsay\_knowcode](#):
>
> George putting money where his mouth is. kudos.

tell me if I’m wrong about anything in this app because if I am then I’m spending more time than I need to when I build apps and could be making a lot more money 😃

---

<div class="post-metadata">

**Author:** ![randomanon](https://avatars.discourse-cdn.com/v4/letter/r/8491ac/32.png) [@randomanon](https://forum.bubble.io/u/randomanon)\
**Post date:** [September 24, 2025, 3:01pm UTC](https://forum.bubble.io/t/better-boilerplate-by-not-quite-unicorns-our-take-on-what-a-good-bubble-app-looks-like/381517/8 "2025-09-24T15:01:11Z")

</div>

George hard carrying the Bubble community once again. Stripe is one of the most annoying things to get right and takes weeks for a first-time builder, so this will speed things up a lot for them.

I really hate the fact that properties are only on reusable elements because they’re so much better than custom states, which require you to waste a workflow/action just to set default values. This leads me to the only thing I really disagree with (not because it’s inherently wrong, but because it doesn’t fit my needs):

**Multiple layers of nested reusables** - For me this completely breaks the flow of designing/iterating. I can see how it might be needed on very large apps or if you think there will be no further changes to the UI for a very long time, but it just feels like I’m constantly “losing my place.” I like seeing all my workflows in one place, separated by folders. Making sense of certain workflows would be very difficult without the recently-added “go to custom event” feature, you pretty much have to depend on that. It’s just a claustrophobic experience.

Also for the invoices I would personally just rely on Stripe’s “customer portal” thing instead of importing the invoices/etc into the Bubble DB.

There are a couple of questions I had:

1. What’s the point of the username database trigger? Basically auto-saving?

2. For API objects, does it really just say “data from API connector” in the editor? You can’t even click on it to see the data. That seems really annoying and inconvenient.

![Screenshot 2025-09-24 070629](https://us1.discourse-cdn.com/bubble/original/3X/a/c/ac6791a37d76c828c0477829f10308452629c1ac.png)

---

<div class="post-metadata">

**Author:** ![georgecollier](https://sea1.discourse-cdn.com/bubble/user_avatar/forum.bubble.io/georgecollier/32/182327_2.png) [@georgecollier](https://forum.bubble.io/u/georgecollier)\
**Post date:** [September 24, 2025, 3:27pm UTC](https://forum.bubble.io/t/better-boilerplate-by-not-quite-unicorns-our-take-on-what-a-good-bubble-app-looks-like/381517/9 "2025-09-24T15:27:42Z")

</div>

> [@randomanon](#):
>
> What’s the point of the username database trigger? Basically auto-saving?

So every place a user’s first or last name updates, I don’t need to also remember to set the full name.

> [@randomanon](#):
>
> For API objects, does it really just say “data from API connector” in the editor?

Yes

> [@randomanon](#):
>
> **Multiple layers of nested reusables** - For me this completely breaks the flow of designing/iterating.

I mean, fair enough. We work on apps with technical debt all the time and almost always find that the less reusable elements an app has, the harder it is to maintain. Of course that’s a correlation not causation and doesn’t cover people ‘consciously’ deciding to use less reusable elements, but when well organised and scoped appropriately, they are super super powerful.

---

<div class="post-metadata">

**Author:** ![randomanon](https://avatars.discourse-cdn.com/v4/letter/r/8491ac/32.png) [@randomanon](https://forum.bubble.io/u/randomanon)\
**Post date:** [September 24, 2025, 3:32pm UTC](https://forum.bubble.io/t/better-boilerplate-by-not-quite-unicorns-our-take-on-what-a-good-bubble-app-looks-like/381517/10 "2025-09-24T15:32:01Z")

</div>

> [@georgecollier](#):
>
> Yes

Of course there had to be a catch.

---

<div class="post-metadata">

**Author:** ![aleemansari97](https://sea1.discourse-cdn.com/bubble/user_avatar/forum.bubble.io/aleemansari97/32/219562_2.png) [@aleemansari97](https://forum.bubble.io/u/aleemansari97)\
**Post date:** [September 24, 2025, 7:57pm UTC](https://forum.bubble.io/t/better-boilerplate-by-not-quite-unicorns-our-take-on-what-a-good-bubble-app-looks-like/381517/11 "2025-09-24T19:57:25Z")

</div>

I love this so much

Thanks @georgecollier

---

<div class="post-metadata">

**Author:** ![aleemansari97](https://sea1.discourse-cdn.com/bubble/user_avatar/forum.bubble.io/aleemansari97/32/219562_2.png) [@aleemansari97](https://forum.bubble.io/u/aleemansari97)\
**Post date:** [September 24, 2025, 8:00pm UTC](https://forum.bubble.io/t/better-boilerplate-by-not-quite-unicorns-our-take-on-what-a-good-bubble-app-looks-like/381517/12 "2025-09-24T20:00:17Z")

</div>

Once refined (if there are any refinements needed LOL), can you also post this as a free template on the marketplace?

---

<div class="post-metadata">

**Author:** ![aleemansari97](https://sea1.discourse-cdn.com/bubble/user_avatar/forum.bubble.io/aleemansari97/32/219562_2.png) [@aleemansari97](https://forum.bubble.io/u/aleemansari97)\
**Post date:** [September 24, 2025, 8:07pm UTC](https://forum.bubble.io/t/better-boilerplate-by-not-quite-unicorns-our-take-on-what-a-good-bubble-app-looks-like/381517/13 "2025-09-24T20:07:51Z")

</div>

I was under the impression that we needed Privacy Rules to have a failsafe conditions like

`Current User's Account's Billing Account is This Billing Account AND This Billing Account is not empty`

This was highlighted by Victor in BubbleCon’24: [https://www.youtube.com/watch?v=Bx7mOnWtLrE](https://www.youtube.com/watch?v=Bx7mOnWtLrE) (around the 15 minute mark)

I can see that some privacy rules are structured like this, but some aren’t? Is this a mistake? Or am I not getting the complete picture in Privacy Rules?

Thanks @georgecollier!

 ![Screenshot 2025-09-25 at 1.01.16 AM](https://us1.discourse-cdn.com/bubble/original/3X/1/d/1dc3a7cd174dd6a561bfa6e9510e959462b35739.png)

---

<div class="post-metadata">

**Author:** ![akamarski](https://avatars.discourse-cdn.com/v4/letter/a/5e9695/32.png) [@akamarski](https://forum.bubble.io/u/akamarski)\
**Post date:** [September 24, 2025, 8:52pm UTC](https://forum.bubble.io/t/better-boilerplate-by-not-quite-unicorns-our-take-on-what-a-good-bubble-app-looks-like/381517/14 "2025-09-24T20:52:34Z")

</div>

when you have 1 is 1, you **don’t** have to validate if the first is empty. Else the condition fails =\> 0 is not 1

when you have 1 (but can be 0) = 1 (but can be 0) you have to validate that the first one isn’t 0, else it can enter a 0 is 0 state and expose data to anonymous visitors who also have 0 as their value

> [@Careful using only Current User's X in privacy rules](https://forum.bubble.io/t/careful-using-only-current-users-x-in-privacy-rules/308385/3):
>
> The other scenario this happens is when deleting data. If, for example, you delete an Organisation, and forget to delete some data related to that Organisation (let’s say, it’s Invoices), then the Organisation field on the Invoice would become empty. This is probably the most common scenario that could make a breach like this occur.

The trick is with the “Current User” because it has another layer of variability - whether is logged in or not.

---

<div class="post-metadata">

**Author:** ![georgecollier](https://sea1.discourse-cdn.com/bubble/user_avatar/forum.bubble.io/georgecollier/32/182327_2.png) [@georgecollier](https://forum.bubble.io/u/georgecollier)\
**Post date:** [September 24, 2025, 9:32pm UTC](https://forum.bubble.io/t/better-boilerplate-by-not-quite-unicorns-our-take-on-what-a-good-bubble-app-looks-like/381517/15 "2025-09-24T21:32:28Z")

</div>

As @akamarski points out, ‘This X’ is logically always not empty 🙂

---

<div class="post-metadata">

**Author:** ![georgecollier](https://sea1.discourse-cdn.com/bubble/user_avatar/forum.bubble.io/georgecollier/32/182327_2.png) [@georgecollier](https://forum.bubble.io/u/georgecollier)\
**Post date:** [September 24, 2025, 9:33pm UTC](https://forum.bubble.io/t/better-boilerplate-by-not-quite-unicorns-our-take-on-what-a-good-bubble-app-looks-like/381517/16 "2025-09-24T21:33:14Z")

</div>

No because template marketplace has all sorts of pain in the ass admin stuff to deal with, but you can probably export from the app settings in general tab if you want to use it as a template?

---

<div class="post-metadata">

**Author:** ![boston85719](https://sea1.discourse-cdn.com/bubble/user_avatar/forum.bubble.io/boston85719/32/77286_2.png) [@boston85719](https://forum.bubble.io/u/boston85719)\
**Post date:** [September 25, 2025, 4:37am UTC](https://forum.bubble.io/t/better-boilerplate-by-not-quite-unicorns-our-take-on-what-a-good-bubble-app-looks-like/381517/17 "2025-09-25T04:37:02Z")

</div>

> [@randomanon](#):
>
> **Multiple layers of nested reusables** - For me this completely breaks the flow of designing/iterating. I can see how it might be needed on very large apps or if you think there will be no further changes to the UI for a very long time, but it just feels like I’m constantly “losing my place.” I like seeing all my workflows in one place, separated by folders. Making sense of certain workflows would be very difficult without the recently-added “go to custom event” feature, you pretty much have to depend on that. It’s just a claustrophobic experience.

Very true @randomanon

> [@Do Not Overuse Parent Groups Thing](https://forum.bubble.io/t/do-not-overuse-parent-groups-thing/339467/14):
>
> Don’t overuse reusable elements either. Recently I started to rebuild an app previously started by other developers who overused the reusable elements. They had buttons as a reusable element when it should have been just a button on the actual reusable element that required the button. Basically they had about 10 reusable elements that I had to pick apart and turn into the one reusable element necessary. A lot of times people pick up a new trick or have a unique approach to something and they begin to overuse it and apply it to situations where it is unnecessary and makes maintaining/debugging an app more complicated and tedious than necessary.

> [@randomanon](#):
>
> Also for the invoices I would personally just rely on Stripe’s “customer portal” thing instead of importing the invoices/etc into the Bubble DB.

For the most part true. Definitely no need to overpay for anything, especially the storage and retrieval of information. If Stripe has it, let them handle it. If you really need it in the app DB as well, save it as a the api object itself and leverage the cost savings of fetching api objects instead of custom data things.

> [@georgecollier](#):
>
> So every place a user’s first or last name updates, I don’t need to also remember to set the full name.

Since I struggle with this, the improper data structure around names, I’ve decided to try and simplify things and put that data on one location only in the database. Not much use in having the same data stored on two different data types so long as you relate things, which bubble does with the creator field, so if every single data type has a built in relation to user through the creator field, the likely best place as a single location to put the name of the user is on the user data type.

> [@randomanon](#):
>
> For API objects, does it really just say “data from API connector” in the editor? You can’t even click on it to see the data. That seems really annoying and inconvenient.

Yes, this just means, do not rely on the data tab as the source of information and build out in five minutes a page and visual of the data so you can see it or the app owner could, similar to a dashboard with analytics.

> [@georgecollier](#):
>
> I mean, fair enough. We work on apps with technical debt all the time and almost always find that the less reusable elements an app has, the harder it is to maintain.

The correct and most balanced way to use reusable elements is lost on many developers and is a complicated topic that takes some time to fully understand the nuances and tradeoffs.

> [@georgecollier](#):
>
> No because template marketplace has all sorts of pain in the ass admin stuff to deal with

You already have the documentation, you have the link in forum, and can easily setup the ‘demo user’ access. Other than those things, for free templates the only admin stuff is pressing the submit button. Bubble then verifies it works and puts it on the marketplace.

@georgecollier Thanks for sharing.

One suggestion. If you are putting this out as how to do Stripe properly, you should show how to initialize the Create Subscription so as to enable multiple subscriptions at once. I’d say as well, add in how to do that for products only, so can check out and pay for products, not just subscriptions. And maybe consider showing how can do multiple products in one checkout session.

If you want to take that further, show a checkout with multiple products and multiple subscriptions in one checkout. Stripe allows for up to 40 products and 20 subscriptions in a single checkout (maybe those numbers are different now since I last looked), but an optimal Stripe setup will enable multiple subscriptions and/or multiple products at once with a single action.

Those points were some of the things I saw as missing from Gregory John video on Bubble youtube about how to setup Stripe via API.

---

<div class="post-metadata">

**Author:** ![DjackLowCode](https://sea1.discourse-cdn.com/bubble/user_avatar/forum.bubble.io/djacklowcode/32/222536_2.png) [@DjackLowCode](https://forum.bubble.io/u/DjackLowCode)\
**Post date:** [September 25, 2025, 6:45am UTC](https://forum.bubble.io/t/better-boilerplate-by-not-quite-unicorns-our-take-on-what-a-good-bubble-app-looks-like/381517/18 "2025-09-25T06:45:14Z")

</div>

Hey @georgecollier - nice work on putting this out to the community!

my 2c on a couple of things which will hopefully help

Camel Case

- usingCamelCase came because of a need to set variables in programming without spaces. In my view simple english syntax is better for most people and will decreaseCognitiveLoad when viewing everythingForYou as a Developer (whether in fields, groups or anywhere else in the editor) .

API connector objects in data - incredibly useful but important to be aware of the tradeoffs (and likely not suited for beginner users)

- stores the whole object and all the bubble connector ‘fluff’, so will inherently be a large field
- at some point, you’ll need to export your data , keeping the important stuff hardcoded (e.g. sub id) is key here
- at some point, you’ll likely break the setup or have legacy API structure (it’s based on what you have in the API connector), for that reason, you can store the actual API object as a string for redundancy if needed
- it’s undocumented, so who know’s where it will go in the future, so best to hedge bets with the actual JSON as a string (also) where required so you can extract info however you may need.

No OS denominator on Option sets

- The utility for having _‘OS option set’_ or _‘option set OS’_ grows with the app 😉

You may not agree with the above, but again great work contributing to the community yet again!

---

<div class="post-metadata">

**Author:** ![georgecollier](https://sea1.discourse-cdn.com/bubble/user_avatar/forum.bubble.io/georgecollier/32/182327_2.png) [@georgecollier](https://forum.bubble.io/u/georgecollier)\
**Post date:** [September 25, 2025, 7:05am UTC](https://forum.bubble.io/t/better-boilerplate-by-not-quite-unicorns-our-take-on-what-a-good-bubble-app-looks-like/381517/19 "2025-09-25T07:05:00Z")

</div>

> [@DjackLowCode](#):
>
> Camel Case
> 
> - usingCamelCase came because of a need to set variables in programming without spaces. In my view simple english syntax is better for most people and will decreaseCognitiveLoad when viewing everythingForYou as a Developer (whether in fields, groups or anywhere else in the editor) .

I think we agree on the principle that stuff should be as readable as possible in the editor.

The reason we use camel case in fields is to distinguish primitives (yes/no, text, number etc) from rich types (data types + option sets). I wrote more about this a bit in the docs. Overall I’d say the benefit of the convention we use is that when using it consistently not just in the database but in workflows/properties/logic all of the fields line up with your parameters nicely, and its easy to see what type of data you’re dealing with. I wrote about it a bit more in the docs.

Overall the naming convention is one of the things we’re least fixed on, as long as an app HAS a naming convention it’s generally going to be fine.

> [@DjackLowCode](#):
>
> API connector objects in data - incredibly useful but important to be aware of the tradeoffs (and likely not suited for beginner users)
> 
> - stores the whole object and all the bubble connector ‘fluff’, so will inherently be a large field
> - at some point, you’ll need to export your data , keeping the important stuff hardcoded (e.g. sub id) is key here
> - at some point, you’ll likely break the setup or have legacy API structure (it’s based on what you have in the API connector), for that reason, you can store the actual API object as a string for redundancy if needed
> - it’s undocumented, so who know’s where it will go in the future, so best to hedge bets with the actual JSON as a string (also) where required so you can extract info however you may need.

Yes on data export, definitely a downside, but should that be necessary, you can always add a new JSON string field or add more fields for the data you want to keep. It’s also a native feature that many apps use in other ways - which means that support for it will have to continue.

> [@DjackLowCode](#):
>
> The utility for having _‘OS option set’_ or _‘option set OS’_ grows with the app 😉

Given the app’s context (or sometimes with no app context) it should be easy to tell what’s an option set and what’s a data type. Both are interacted with in the expression editor in mostly the same way hence no real need to differentiate them except in instances where you have an option that’s similar to a data type. Storing it in a string as-is might be wise for peace of mind, I can see.

---

<div class="post-metadata">

**Author:** ![DjackLowCode](https://sea1.discourse-cdn.com/bubble/user_avatar/forum.bubble.io/djacklowcode/32/222536_2.png) [@DjackLowCode](https://forum.bubble.io/u/DjackLowCode)\
**Post date:** [September 25, 2025, 7:18am UTC](https://forum.bubble.io/t/better-boilerplate-by-not-quite-unicorns-our-take-on-what-a-good-bubble-app-looks-like/381517/20 "2025-09-25T07:18:20Z")

</div>

Thanks @georgecollier - I suppose we can agree that a simple, consistent approach is the best 🙂 . Every Dev has their own preference, that I can’t say is better or worse (which makes it challenging for those who work across apps with other developers)

[Next page](https://forum.bubble.io/t/better-boilerplate-by-not-quite-unicorns-our-take-on-what-a-good-bubble-app-looks-like/381517.md?page=2)
