Run for Life: A Running Coach for People Who Run to Lose Weight
A weight-loss running coach born from my own 98→75 kg run — and why the plan engine I'm building is rules, not an LLM.
For a long stretch of the last few years I've had two jobs at once — software engineer during the day, hotel manager the rest of it — and most of both happens sitting in a chair. At the peak of Covid I stepped on a scale and it read 98 kg. The number itself wasn't the shock; the trajectory was. Keep sitting, keep gaining, and diabetes and coronary artery disease stop being abstract risks and start being the next entry in a family history. So I did the least original thing available to me: I started running. The name isn't a metaphor — I called the project Run for Life because that was literally what it was for me. I've kept running on a schedule I built myself ever since, and I'm at 75 kg today. This product is that schedule, packaged for the person I was in 2021.

At a glance
- What: Run for Life — a running coach for people who run to lose weight, built around beginner-friendly walk-run plans and intermittent fasting.
- My role: Solo product builder.
- Stack: Next.js 15 · React 19 · Supabase (Postgres, Edge Functions, pg_cron) · Drizzle · Tailwind 4.
- Status: Live prototype at runforlife.vercel.app — currently a single-user product: me.
- Timeline: Built through 2026; mid-pivot from generic AI coach to weight-loss coach.
The thesis
Strava and Runna are built for people chasing performance — tempo runs, HIIT intervals, plans written around a pace of 5–6 minutes per kilometer. At 98 kg none of that was physically available to me; something closer to an easy 10 minutes per kilometer was, and even that took real effort to sustain. Those apps aren't wrong, they're just answering a different question than the one I had. The gap I kept running into wasn't another performance dashboard — it was a coach for people who run to lose weight: office workers who gained weight sitting at a desk and need a system they can actually follow for months, not a training block for someone who's already fit.
That distinction sounds small until you try to build a plan around it. A performance app assumes you can already run continuously and just wants to make you faster; the honest starting point for someone at 98 kg is often walk-run intervals, and the plan has to be patient about that instead of treating it as a warm-up phase to rush through. The onboarding questions change too — resting heart rate and current weight matter more up front than a 5K personal best nobody has yet.
V1: the generic AI coach
The first version of the product was that generic AI coach. The landing page promised to sync from Garmin and Strava, but that was aspiration, not shipped reality — real sync means API integrations and partner deals I didn't have. What actually shipped took a different route: you upload a screenshot of any run, because a screenshot is the one export format every running app already produces, and it needs no watch integration and no API deals at all. An AI pipeline reads the screenshot, extracts the numbers, and writes a short insight back to you.

process-run Edge Function; a pg_cron job on a one-minute schedule runs reconcile-runs to retry anything stuck, and a daily job runs cleanup-storage to purge orphaned files.It worked, technically. Screenshots got parsed, insights got written, the pipeline recovered from its own failures without me babysitting it — a run whose dispatch got dropped got picked back up on the next tick instead of sitting broken until I noticed. And it was still the wrong product, because none of that machinery answered the only question that actually mattered to the person using it: what should I do about my weight. A well-engineered insight about pace splits doesn't help if pace was never the problem.

The pivot
A generic AI coach answers "how do I run better?" But the person the app was named for wasn't asking that — he was asking "how do I stop being 98 kg?" Those are different products with different plans, different onboarding, and different things that count as success. So I pivoted the positioning to running plus intermittent fasting for weight loss: fixed daily meal timing, walk-run programs aimed at a first 5K, and onboarding rebuilt around weight-loss fields and safety flags instead of performance targets.
Fixed meal timing mattered as much as the running plan. Weight loss for someone sitting at a desk all day is mostly a fasting and eating-window problem, not a mileage problem, so the product needed to own both sides — when you run and when you eat — rather than bolt weight loss on as an afterthought to a running app. Safety flags exist because the audience is, by definition, people carrying more weight and less running history than a typical fitness-app user; the onboarding has to catch that before it hands out a plan.
The coach engine: deterministic by design
Coaching advice touches health and trust, and both break the same way: a plan that changes every time you refresh is a coach you stop believing, and an LLM-hallucinated interval session is genuinely dangerous advice to hand someone at 98 kg. So the plan engine I'm building for Run for Life is deliberately not an LLM. It is specced as a deterministic, layered engine — and what follows is that design, not a tour of code that ships today. What actually runs right now is at the bottom of this section.
A fact builder will read only canonical records — runs, reviewed meal logs, weight check-ins, the current fasting protocol — and normalize them into local-day facts over rolling 7- and 14-day windows. It will never read images; that stays deliberately upstream of this engine. A snapshot scorer will turn those facts into a daily metabolic snapshot. A policy engine will then decide, by explicit rule rather than judgment call, whether to hold, advance, or downgrade the fasting protocol along its fixed progression: 12-12 → 16-8 → 20-4. A plan materializer will take the output of all three and write it into a stable read model, a WeeklyCoachPlan, that the app renders.
Same inputs, same plan, every time — reproducible, testable, explainable. That is the property the whole design exists to buy, and it doesn't mean AI is gone from the product. It means AI stays exactly where variance is acceptable: reading a screenshot and turning pixels into numbers, not deciding what a person recovering from 98 kg should do next week. That part is the one piece of this already shipping — it's the V1 pipeline above.
Splitting the engine into four stages instead of one function is a deliberate choice, not architecture for its own sake. The whole point of the split is that each stage can be tested in isolation: feed the fact builder a fixed set of runs and meal logs and assert on the exact facts it produces, feed the policy engine a fixed snapshot and assert on the exact decision it makes, without ever touching a model or a prompt. When the fasting protocol eventually advances from 16-8 to 20-4, I want to be able to point at the rule that fired and the 7- or 14-day window of facts that satisfied it. That's the bar coaching advice should have to clear before I'd trust it, whether the coach is software or a person — and it's a bar an LLM cannot clear by construction.
What I've actually built toward this so far is the boring half, which is also the half that is expensive to get wrong later: the schema and the seam. Three tables are live — fasting_protocol_assignments holds the active protocol and eating window, weight_check_ins holds the check-ins, and weekly_coach_plans holds a plan payload, the read-model table the materializer would write into. The read model that feeds the dashboard is in place too, and it is honest about the gap: it reads the latest coach plan and tags what it returns COACH_PLAN or LEGACY_INSIGHT, falling back to the old V1 insight when no plan exists. Today it always falls back, because nothing writes a plan yet. Onboarding assigns the starting 12-12 and nothing moves it — the progression above is a design and a TypeScript union, not a rule that runs. The four stages are specced and next.
Where it is now
Honestly: Run for Life is a live prototype with exactly one user — me. I'm dogfooding my own coach, mid-pivot, which means some screens still carry traces of the generic V1 positioning alongside the weight-loss rebuild. There are no user numbers to report, because there are no users to count yet, and I'd rather say that plainly than dress up a single-user prototype as something it isn't.
What exists is the V1 loop, and it's real: onboarding, the screenshot upload, the run-processing pipeline with its cron reconciler and storage cleanup, and run logging — plus the schema and the read model the coach engine is designed to write into. What doesn't exist yet is the engine itself — the four stages, the daily snapshot, and the rules that would move the fasting protocol off the 12-12 it's assigned at onboarding — nor the meal logging the fact builder is designed to read from. Until that lands, the dashboard serves the old V1 insight through the LEGACY_INSIGHT fallback, which is exactly why the plan card is empty in the screenshot above. Building the engine is the next block of work; after it comes opening the product up past a single user, which mostly means hardening the onboarding and safety-flag paths for people whose starting point I don't already know by heart, and finishing the migration off the generic V1 screens that still linger in a few corners of the app.
Some of that work is intentionally slow. I'd rather write a policy rule after watching the pattern hold up across my own weeks of data than spec one I haven't lived with. Being the only user is a constraint, but it's also the reason the rules I'm writing down come from a real person's actual weeks instead of a synthetic dataset — and the reason I can say what's built and what isn't without a growth number pushing me to blur the two.
The 98→75 kg isn't a product metric; there's only one of me. It's evidence that the schedule this engine is designed to encode is one I actually followed, not a number I invented to make the case study land.