Pulse
A 360 performance-review platform in Bubble, and the workload optimisation that halved its running cost.
What the app does
A 360 performance-review platform for a whole organisation. Each person is reviewed from several directions (themselves, their peers, their reports and their managers), across defined values and competencies, each scored and weighted so a final result reflects the model the company actually uses, not a flat average. Cycles, questionnaires per person, responses and manager dashboards sit on top of that. The scoring, the weightings and who-reviews-whom are the kind of logic that has to be right and has to be readable, which is what made the next part matter.
Problem
It worked, but the Workload Units bill did not match the size of the tool. The heaviest screens recalculated everything (every score, every weighting, every rollup) on every visit, and generating a review cycle fired hundreds of operations at once. The question was not whether Bubble could do it. It was whether it could do it without paying for the same calculation over and over.
What I did
I started where I always start: measuring. Bubble's app metrics show consumption per workflow and per page, and the usual pattern showed up: a handful of searches and workflows caused most of the bill.
- Moved filtering into the searches. Lists that fetched everything and filtered client-side (
:filtered) became constrained searches, so the database only returned what each screen needed. - Removed searches from inside repeating groups. Result tables were running one search per cell per row. Replaced with a single search whose result is stored once and reused.
- Search once, filter over the result. Where a page needed the same dataset sliced several ways, I ran one search, kept the result, and filtered over that in memory instead of paying for near-identical searches.
- Denormalised the scores. Aggregated results and completion rates were recomputed with
:countand live rollups every time a manager opened a dashboard. They became stored fields, updated once when a response comes in. You pay when writing, once, not on every read. - Moved the heavy operations to the backend and out of the user's way.
- Killed the invisible consumers. No "do every X seconds" workflows left anywhere, and every "when condition is true" watcher either removed or given a strictly narrow condition.
- Used direct API calls where they beat the engine. Fetching exactly the fields needed can replace several expensive searches. Not free (API workflows consume too), but cheaper when one call substitutes repeated work.
- Measured again, and repeated on the next biggest consumer until the curve flattened.
Results
Consumption dropped to less than half with the same features and the same users, which brought the app comfortably back inside its plan. The dashboards, which recalculated every score on open, now read stored results and load immediately. Nothing was rebuilt: the interface stayed, the data-access pattern changed.
The wider point of this case: most Bubble apps with a workload problem do not have a Bubble problem. They have a data-access pattern problem, and that is fixable in days, not months.

Illustrative UI, not the real interface. The data model isn't shown, to protect confidentiality.