AI vs Rules-Based Coaching: Where a Language Model Actually Helps
Updated July 27, 2026
Most good AI fitness features are a rules engine wearing a language interface. The architectural decision is not "AI or not AI" — it is which jobs get deterministic code and which jobs get a language model, and the dividing line is whether the job has a single correct answer. Progression schemes, set and rep math, weekly volume caps, deload timing, equipment substitution and heart-rate zone arithmetic all have correct answers, so they belong in ordinary code: testable, reproducible, effectively free per call, and incapable of hallucinating. The model earns its place on the genuinely open-ended parts — understanding what the user typed, explaining why the plan looks the way it does, adapting tone, and handling "I tweaked my shoulder, swap today's session". If you take one thing from this page: ship a model that narrates numbers your engine already computed before you ship a model that computes anything.
This page is about language models. Camera-based form work — pose estimation, rep counting, form scoring — is a different technology stack with a different set of trade-offs; that lives at how form feedback works.
The dividing line, stated as a test
Before you write a prompt, run each job through four questions. They are boring, and they will save you months.
- Is there one correct answer that a test can assert on? If yes, write the code. "Next week's top set is 2.5 kg heavier if all reps were completed at or below the target RPE" is a rule. You can assert equality on it. You cannot assert equality on a paragraph.
- Is the input space enumerable? Equipment lists, muscle groups, injury flags, session templates — these are categories, and categories are
WHEREclauses. A model asked to filter a catalogue is doing an expensive, non-deterministic version of a query you could have run. - Does a human read the output and would they notice it was wrong? A user reading "nice negative split on that second half" can see whether it matches their own splits. A user reading "your protein target is 143 g" has no way to check. The first is a good model job; the second is arithmetic that belongs to your code.
- What is the blast radius if it is wrong? Cosmetic (a clumsy sentence) versus corrupting (a bad load written into the user's training history and compounding for six weeks) versus harmful (advice given under an injury, pregnancy or disordered-eating context). Risk should push work toward the deterministic side, not away from it.
The failure mode this framing prevents is the common one: a team wires a model into plan generation, watches it produce plausible sessions in a demo, and only discovers months later that the same user profile produces a different weekly volume every time, that the "no barbell" constraint quietly evaporates on turn nine, and that nobody can write a regression test for any of it.
The allocation table
Model, rules, or both, for the jobs a real training or nutrition app actually contains. "Both" almost always means the same shape: rules own the boundaries and the arithmetic, the model works inside them.
| Job | Model, rules, or both | Why |
|---|---|---|
| Progressive overload / next-session load | Rules | A pure function of logged performance and your progression scheme. Must be reproducible week to week; an error compounds silently into the user's history. |
| Set, rep and tempo math; session volume totals | Rules | Arithmetic. There is exactly one right answer and it is cheap to test. |
| Weekly volume caps per muscle group | Rules | A ceiling is a comparison, and a ceiling the model can talk itself past is not a ceiling. |
| Deload timing | Rules | A scheduled or fatigue-triggered condition over data you already store. Deterministic, auditable, explainable. |
| Equipment substitution | Rules first, model on ambiguity | Your catalogue already tags equipment and movement pattern, so the substitution set is a filtered query. Bring in the model only to interpret woolly input like "the gym is packed, the rack is taken". |
| Heart-rate zone boundaries | Rules | Arithmetic over threshold values you already hold. Never regenerate a number you can compute. |
| Macro and calorie totals for a logged day | Rules | Sum the catalogue rows. Never persist a macro number the model emitted. |
| "What did I lift last Tuesday?" | Rules | A database query. Answer it with no model call at all — it is faster, exact, and free. |
| Parsing "did 3x8 at 60 but the last set felt rough and my shoulder is cranky" | Model | Unbounded natural language into structure. This is the thing models are genuinely good at and code is genuinely bad at. |
| Onboarding free-text goals into a profile schema | Model | Same job: messy human sentence in, typed fields out, validated by your code afterwards. |
| Resolving a spoken or photographed meal to a food entry | Both | The model identifies and proposes; the catalogue supplies the numbers. Resolve to a verified row rather than generate nutrition values. |
| Portion quantity | Both, with the user as final authority | The model proposes a quantity, your code canonicalises the unit and recomputes macros, and the UI treats correction as the primary interaction rather than an error path. |
| Choosing and ordering exercises within a fixed candidate set | Both | Pre-filter in SQL, hand the model the surviving candidates, let it select and sequence. Selection from an enumerated list is a far more constrainable task than generation. |
| Deciding whether a generated plan is acceptable | Rules | Server-side validation: every ID exists, every item satisfies the original filter, every number sits inside a bounded range, no duplicates, session fits the time budget. |
| Explaining why today is a deload | Model | Narration of a decision your engine already made. Nothing the model says can change the decision. |
| Weekly recap, session summary, activity narrative | Model | The correct numbers exist; the model writes prose about them. Structured metrics in, short natural-language summary out. |
| Coaching tone, brevity, encouragement, register | Model | There is no correct answer, which is precisely why a model belongs here. |
| Answering "why is my plan like this?" over the user's own data | Model with tools | The tools return your numbers; the model explains them. Validate every tool argument server-side — the model still chooses what to ask for. |
| Detecting safety red flags in free text | Rules first, then model | A cheap deterministic gate the user cannot negotiate with, ahead of any model-based filtering. Failing closed is the point. |
| Under-18, pregnancy and declared-condition policy paths | Rules | Which path a user is on is a state machine, not a judgement call. The model may write inside the path it is given; it may not choose the path. |
| Ranking free-text search over a large messy food catalogue | Both | Millions of user-generated rows is where embeddings and hybrid retrieval genuinely earn their place — unlike a few-thousand-row exercise table, which SQL serves better. |
Two rows are worth restating as principles. First, never regenerate a number you can compute — every arithmetic job handed to a model converts something with zero error into something with non-zero error, for no product benefit. Second, selection beats generation: constraining the model to pick from a filtered candidate set turns an open-ended task into a closed one, which is the single highest-leverage move in this whole design space. The mechanics of doing that against a real catalogue are covered in grounding an LLM in an exercise database.
The three shipped shapes, ranked by risk
The language-model features that have actually shipped in consumer fitness apps cluster into three shapes. They are not equally safe, and the order below is the order to ship them in.
Shape 1 — narrate structured data you already computed. Lowest risk. Your engine produces the workout, the recap, the readiness score, the weekly volume; the model turns those into readable prose. There is very little the model can get factually wrong that the user cannot see, because the user is looking at the same data on the same screen. Note that this safety property holds for a fixed recap over a known set of numbers and weakens as soon as the surface becomes an open-ended conversation — a chat assistant over the same data can be asked things the recap never had to answer. This is the safest and by far the most common first ship, and it is where a team with an existing rules engine already has everything it needs.
Shape 2 — resolve unstructured input to a catalogue row. Medium risk. The user speaks, types or photographs something and the model maps it onto a row your app already has: an exercise_id, a food_id. The failure mode is a wrong row rather than a wrong fact, which is visible, correctable, cheap to measure, and gradeable by code with no judge model in the loop. This is where the interesting retrieval engineering lives — grounding an LLM in an exercise database for the exercise side, AI nutrition logging for the food side.
Shape 3 — free estimation, where the model emits the number itself. Highest risk. Photo calorie estimation is the archetype; the pure photo-estimation product category (Cal AI and its imitators) lives here. What makes this tier risky is not that models are bad at vision — it is that nothing downstream can check the answer. There is no catalogue row to resolve to and no arithmetic your code can redo, so shapes 1 and 2 both have a safety net that shape 3 structurally does not. The honest frame is therefore not "is it as accurate as a professional", because nobody estimates calories accurately from a photograph; it is whether the feature beats the user logging nothing at all. AI nutrition logging covers the documented vision limits, the portion band and the correction UX properly.
The hidden cost of the LLM path
The cost that surprises teams is not tokens. It is that you cannot assert equality on prose, and everything about your engineering process quietly assumed you could.
A rules engine gives you this:
expect(nextLoad({ scheme: "double-progression", lastSession })).toBe(62.5);
That test is fast, deterministic, runs on every commit, and fails loudly the moment someone breaks progression. There is no equivalent for "explain to the user why today is lighter". Run the same prompt twice and you get two different, both-acceptable paragraphs. So the tooling changes shape:
- Assert on structure, not wording. Schema-valid output, every ID present in the catalogue, every number inside a bounded range, no duplicate movement, weekly volume within the ceiling. Note what that really means: every assertion you can still write is an assertion about your rules layer. Keeping the numbers in code is not only a safety decision — it is what keeps the feature testable at all.
- Grade by code first, by judge model only for the remainder. A grader that mechanically checks every emitted item against the catalogue and the original filter catches the failure that matters most, and it is free. Reserve a judge for tone and comprehensibility; a judge model is never the right tool for something a
WHEREclause can check. - Run the same input repeatedly. Wide variance in total volume or session length across identical inputs is a defect even when every individual output looks fine — and it is invisible to a single-shot test.
The cultural adjustment is the hard part: your CI gate stops being green-or-red and becomes a threshold on a distribution, and prompt, schema, catalogue snapshot and model now have to be versioned as one bundle, because a catalogue edit can regress output with no commit to blame. Budget for that in how you run releases, not just in how you write tests. Evaluating AI fitness features is the full method — golden-set construction, judge validation, red-team suites and thresholds.
Two smaller costs, and one cheerful corollary. Latency and availability now sit on a third-party dependency; a rules engine answers in the time of a database query and does not have an outage independent of yours. Per-call cost is dominated by how much context you resend rather than by which vendor's logo is on the model — AI fitness app cost has the arithmetic and the levers, and choosing an LLM for fitness apps the tiering. The corollary: every job you leave in the rules engine costs nothing per call, never times out, and never needs an eval.
Migrating an existing rules-based app
If you already have a working progression engine, the mistake is to rewrite it. You do not need to. The migration is additive, and each step is independently shippable.
Step 1 — expose the engine's output as structured data. It almost certainly already is. Do nothing else yet.
Step 2 — ship narration (shape 1). Feed the engine's output into the model with an instruction to explain it, restricted explicitly to the supplied data and permitted to say it does not know. Crucially, the model's output in this step is display text. Nothing it writes touches a stored number. If the model is wrong, the user sees a clumsy sentence next to correct data — not a corrupted training log.
Step 3 — add read-only question answering with tools. Give the model tools that call endpoints you already have: last four weeks of sessions, current programme, this week's volume by muscle group. It composes an answer from what your system returns. Still nothing writes.
Step 4 — let the model propose changes as arguments to functions you already have. This is the key architectural move, and it is the one that means you never rewrite the engine. The model does not author a plan. It emits an intent, and your engine executes it:
// The model's output is a request, not a result.
{
"tool": "swap_exercise",
"session_id": "s_8841",
"slot": 2,
"new_exercise_id": "ex_db_floor_press",
"reason_for_user": "shoulder discomfort reported; reducing overhead range"
}
Your existing code then does what it has always done:
function swapExercise(req, user) {
const ex = catalogue.get(req.new_exercise_id);
if (!ex) return reject("unknown_exercise"); // no dangling pointers
if (!ex.equipment.every(e => user.equipment.has(e))) return reject("equipment");
if (ex.contraindications.some(c => user.flags.has(c))) return reject("contraindicated");
if (ex.pattern !== originalSlot(req).pattern) return reject("pattern_mismatch");
return engine.applySwap(req); // volume caps, progression, session length: unchanged
}
Prefer deterministic repair or a filtered retry over re-prompting on failure, so a bad generation cannot loop into cost. And note that schema-valid is not the same as physiologically sane: bounds like "sets between 1 and 10" live in this validator, not in the schema — grounding an LLM in an exercise database explains why the decoding layer cannot do it for you.
Step 5 — only if you genuinely need it, let the model author whole plans. Most apps never need this step. If you take it, everything above still applies, plus a full server-side validator. AI workout plan generation covers that path in detail.
Throughout, keep a feature flag that turns the language layer off. Because you kept the engine, the app degrades to exactly what it was before: working. That fallback is free, and the day the API is slow or down you will be very glad it exists.
What you still own
None of this makes an LLM feature safe by itself, and it is worth being precise about where responsibility sits: the app publisher owns what the app tells a user, not the model vendor. Output about exercise or nutrition is not medical advice, and no model is safe to give medical advice.
The shipping pattern is constrain, not trust — which is the argument of this whole page arriving one more time, because every part of it is rules work. Generate from a vetted catalogue, validate output server-side, gate risk signals with a deterministic check before the model is involved, and log for audit. The rest is other pages' territory and they do it better: the failure taxonomy, the escalation triggers, and the constrain-rather-than-refuse tiering are in LLM safety for fitness advice; the platform obligations that attach the moment a health profile leaves your servers are in App Store health data rules and Google Play health data policy.
The short version
Write the rules engine. It is where correctness, testability and cost all point, and it is the part of the system you can still reason about at 2am. Then put a language interface on it, starting with narration of numbers you already trust, and let the model earn each additional responsibility by passing an eval you actually built. The teams that get burned are the ones who reached for a model because it was the interesting part, and discovered too late that they had traded a system with tests for a system with vibes.
If you are designing the whole product rather than one feature, building an AI fitness coaching app is the product-level view.
Frequently asked questions
- Should an LLM generate the workout plan, or should I just write the progression logic?
- Write the progression logic. Load progression, set and rep math, volume caps and deload timing are pure functions of data you already store, so they have one correct answer, they are cheap to unit test, they cost nothing per call, and they produce the same result twice. A model handed those jobs converts something with zero error into something with non-zero error for no product benefit. Where a model does help on plan generation is selection and sequencing: pre-filter your catalogue in code, hand the model the surviving candidates, and let it choose and order them. Then validate everything it returns server-side against the original filter.
- Can I ship an AI coach without a rules engine underneath?
- You can, but you are then trusting a language model with the parts of the product that have correct answers — progression, volume, load math, equipment substitution — and those are exactly the parts users notice when they drift. The rules engine is not the legacy thing the AI replaces; it is the thing that makes the AI safe to expose. Teams that ship the language layer first usually end up building the rules engine anyway, after the first round of users reports plans that contradict themselves week to week.
- Do I have to rewrite my rules engine to add an AI coach?
- No, and you should not. The migration is additive. First expose the engine's output as structured data, then ship narration — the model explains numbers your engine produced and writes nothing back. Next add read-only question answering through tools that call endpoints you already have. Then, if you need write behaviour, have the model emit an intent rather than a result: a tool call like swap_exercise with an exercise ID and a slot, which your existing engine validates and applies with its normal rules. The engine stays the source of truth, and a feature flag that disables the language layer leaves you with exactly the app you had before.
- Which AI fitness features are lowest risk to ship first?
- Features that narrate structured data you already computed. Your engine produces the session, the recap, the weekly volume; the model turns those into readable prose. Publicly announced examples of this shape include Strava's Athlete Intelligence, Oura's Advisor and WHOOP Coach, where the numbers already exist and the model's job is legibility. There is little the model can get factually wrong that the user cannot see, because the user is looking at the same data. Resolving unstructured input to a catalogue row (a spoken meal matched to a verified food database entry, as MyFitnessPal describes for Voice Log and Meal Scan) is the next tier. Free estimation, where the model emits the number itself, is the riskiest, because nothing downstream can check the answer.
- Is a rules-based fitness app less capable than an AI one?
- For anything with a correct answer, it is more capable, because it is right every time and you can prove it. Personalized and algorithmic are not the same thing as generative AI, and a good deal of what gets marketed as AI coaching is deterministic logic underneath. The honest positioning is that rules give you correctness and a language model gives you an interface: the ability to accept whatever the user types, explain the plan in their terms, adapt tone, and handle awkward one-offs like an aggravated shoulder. Both, layered correctly, beats either alone. And whatever the architecture, output about exercise or nutrition is not medical advice, and the app publisher, not the model vendor, owns what the app tells a user.
Keep reading
Engineering guidance, last reviewed July 27, 2026. This is not medical, nutritional, or legal advice. Language models produce fluent, confident output that can still be wrong — and when the output is exercise or nutrition guidance, being wrong has consequences. Whatever your app tells a user is your responsibility, not the model vendor’s. Model behaviour, pricing, and platform AI policies change often; verify against current official documentation, and design for a qualified human in the loop wherever the advice could cause harm.
← All ai features · by AIFitnessAPI