not a slop bowl
live in active development — subscriptions open, polish ongoing.
the problem
not a slop bowl is a personalised recipe subscription for the uk market. it scales every recipe to a user's exact calorie and protein targets, so the meal they're shown already fits their goal. the gap it closes: most food apps give you macros with no recipes, or recipes with no macros — and users don't think in grams of protein, they think in goals. the distance between "i want to lose fat" and "cook this tonight" was the part nobody had solved.
the approach
- 01 target
onboarding turns a goal into exact daily numbers.
- bmr is calculated with the mifflin-st jeor equation, then scaled by activity level into a daily maintenance figure.
- the goal adjusts it — fat loss cuts calories, muscle raises them — and sets a protein target in grams per kilo of bodyweight.
- 02 score
every recipe is scored against nine health dimensions.
- the dimensions are fat loss, muscle, gut health, skin, energy, inflammation, immunity, sleep and focus.
- recipes are ranked multiplicatively against the user's primary goal, so a recipe weak on the goal can't be rescued by being average everywhere else.
- 03 scale
the chosen recipe is scaled to the user's numbers.
- dinner portions are calculated per-user from their daily targets, then rounded to human-sensible amounts — no "143g of rice".
- all scaling logic lives in tested utility functions — pure maths, no model calls; svelte templates do zero calculation.
- 04 unlock
stripe gates access on a drip-unlock model.
- a free tier shows a few fixed recipes; a trial unlocks one recipe a day; a paid subscription unlocks seven a week.
- stripe handles checkout, the billing portal and the subscription lifecycle through webhooks.
engineering challenges
- correctness of a health calculation
this app does maths that decides what people eat — a wrong calorie figure is not a cosmetic bug.
- bmr, activity scaling, goal adjustment and per-recipe portioning all compose into one number on a plate.
- if any of that logic is buried in a template, it can't be tested, and a silent error reaches the user as food.
every calculation is pulled out of the ui into tested pure functions.
- scaling, scoring and rounding are pure functions with no ui dependency.
- svelte templates only display results, never compute them — so the maths is covered by tests, not by hope.
- 394 tests cover the scaling logic, the ui and the end-to-end flows.
- ranking recipes to a goal
showing someone the right recipe means ranking thousands against what they actually want.
- a recipe can be good for muscle but bad for sleep — an additive score lets strengths quietly hide weaknesses.
- a user whose goal is fat loss should never be served a recipe that's merely average on fat loss.
recipes are ranked multiplicatively, not additively.
- each recipe carries a 0–100 score on all nine health dimensions.
- the final rank multiplies the primary-goal score against the rest, so a low score on the goal drags the whole ranking down — exactly as it should.
- subscription states
"can this user see this recipe" has far more than two answers.
- a user can be on a free tier, an active trial, an expired trial, a paid plan, a cancelling plan still inside its paid period, or past-due while stripe retries payment.
- scatter that logic across the app and access bugs are inevitable — someone loses access they paid for, or keeps access they didn't.
every subscription state resolves through a single access gate.
- stripe webhooks keep the subscription state current as payments succeed, fail or cancel.
- one function maps state to what the user may see — every page asks it, and nothing decides access on its own.