fuelivo is a rule-based calculator for sports nutrition. Endurance athletes and coaches enter session parameters - weight, duration, intensity, sport, conditions - and get concrete carbohydrate, sodium and fluid targets in g, ml and mg, split across before, during and after the session. The recommendations are deliberately deterministic: no black box, every output carries its reasoning and warnings. A backend, a web app and a native iOS app share the same logic.
ProblemEndurance athletes have to take in carbohydrates, fluid and electrolytes deliberately in training and competition - too little causes a performance drop, too much or the wrong thing causes GI trouble. The right amount depends on many factors at once: duration, intensity, sport, heat, gut tolerance. Blanket rules of thumb like '60 g of carbs per hour' ignore that, generic nutrition apps track daily balances instead of sessions, and AI coaches hand out recommendations you can't reason about.
RoleEverything - the idea, domain research, the calculation logic, design, backend, web frontend and the native iOS app. AI was a development tool here, not an autopilot.
The solutionfuelivo models published sports-nutrition heuristics as a deterministic rule engine. For each session it computes per-hour targets, scales them to the full duration and translates them into concrete products - drink, gel, bar, banana. Every calculation step produces a plain-language reason, and edge cases raise warnings. It positions itself deliberately as a sports-science calculator, not an AI coach.
- Carbohydrates per hour: a base value from the duration plus additive modifiers for intensity, sport and heat, capped by a tolerance matrix of gut sensitivity and gut training.
- Fluid and sodium per hour, depending on intensity and sport, optionally overridden by a measured sweat rate.
- Three phases: carb-loading beforehand, timed intake during, recovery with protein afterwards - plus a race schedule with aid-station timing.
- The total amount is translated into ready-to-buy products: depending on aid-station access, a plain drink, drink plus gel or a real-food mix.
- Separate engines for gym training and race day, because their calculation models are fundamentally different - kept apart for independent maintainability.
Features- Public calculator, no login requireddone
- Transparent reasoning and warningsdone
- Three-phase recommendation (before, during, after)done
- Concrete product options per phaseProdone
- Race plan with aid-station timingProdone
- Gym and strength calculatordone
- Athlete dashboard with historyProdone
- Coach portal with roster and templatesCoachdone
- Auth, Google OAuth and Sign in with Appledone
- Stripe and Apple StoreKit subscriptionsdone
- i18n German/Englishdone
- Native iOS appdone
ArchitectureFastAPI serves a Jinja2 shell that loads the built React SPA and injects auth and entitlement state straight into the HTML - no extra round trip. The iOS app talks to the same domain logic over a dedicated, versioned mobile API. Inside, it is Route -> Service -> Repository throughout.
- Route -> Service -> Repository: thin routes, business logic testable in isolation, reused for the second client.
- Calculation rows are append-only - an immutable audit trail instead of mutated records.
- State-mutating POSTs are protected with a double-submit CSRF token.
- Sentry, PostHog and the database are fail-open: missing config never blocks a local start.
- A contract check in CI stops the backend and the iOS app from drifting apart.
ChallengesMigrating the core formula without distorting results
ProblemThe original multiplicative model was to be replaced by a more transparent additive matrix model - without recommendations shifting unnoticed.
SolutionBoth modes stay in the code, switchable by an env flag, with a shadow-compare mode for side-by-side comparison. The switch was low-risk and the old logic stays reproducible.
One entitlement model across two payment worlds
ProblemPro access can exist via Stripe on the web or via Apple StoreKit on iOS. Routes must never check payment status directly.
SolutionA central entitlement service resolves Free, Pro and Coach across both providers - a single source of truth for feature flags.
Keeping the backend and the native iOS app in sync
ProblemTwo clients on one API: contract breaks otherwise surface late, and on the user.
SolutionA dedicated, versioned mobile API with an OpenAPI spec and its own contract-check workflow. Drift is caught in the pull request already.
The swimming special case
ProblemA gel or bar during the session is impractical in the pool - generic fueling makes no sense.
SolutionA dedicated pool-bottle logic with carbohydrate powder and an electrolyte tablet, including sip timing at the wall, plus an electrolyte warning.
Timeline- March 2026
Foundation
In a few weeks the calculation engine, web SPA, auth, billing and coach portal take shape - 257 of the 269 commits fall in this month.
- Late March
Maturity
CI workflows, the OpenAPI contract check, documentation and the new matrix calculation model alongside the legacy formula.
- April - May
iOS & billing
Native SwiftUI app, i18n and accessibility, Apple StoreKit subscriptions and the unified entitlement resolution across Stripe and Apple.
- June
Polish
Adjusting the fueling duration limits and final polish; the last commit lands on 17 June 2026.
What I learned- For the first time I combined a backend, a web frontend and a native iOS app in one project. The clean layering paid off with the second client: the logic was reusable, only the transport layer was new.
- Payments across two worlds - Stripe on the web and Apple StoreKit on iOS - behind a single entitlement service: one source of truth for feature flags.
- A versioned mobile API with OpenAPI and a contract check in CI is the point where 'backend and app drift apart' stops being a problem.
- The first project that grew beyond a single Python script with a README - including its own domain, DNS, CI/CD and deployment.
- Next time: one clear deployment path instead of Docker and Vercel in parallel, and shared types between backend and iOS earlier.