Engineering note · May 2026

How the Healthify adaptive engine works

Most "smart" calorie trackers aren't smart at all. They calculate a TDEE once at signup using a 1919 formula, tell you to eat 500 kcal under it, and never look at your data again. If the number was wrong — or if your body adapts, as every body does — you find out by stalling for six weeks.

Healthify doesn't do that. The engine watches four signals every day and re-tunes your plan on a weekly cadence backed by a 28-day smoother. This piece walks through every formula, the safety clamps that keep it sane, and an 8-week worked example. No marketing fluff — every number below is the same number the app is computing right now.

On this page

  1. The four pillars
  2. Weekly TDEE recalibration
  3. Real-time daily adjust
  4. Body State macro tilt
  5. Deficit classification
  6. 8 weeks with Alex
  7. How this compares
  8. What we are not doing

The four pillars

#PillarCadenceWhat it adapts
1Weekly TDEE recalibration (EWMA + OLS slope smoother)Once per ISO weekYour daily calorie target
2Real-time daily adjustEvery HealthKit syncToday's calorie ceiling
3Body State macro tiltEvery morningToday's protein / carbs / fat split
4Deficit classificationContinuousThe recommendation card you see

The pillars stack. Pillar 1 sets your weekly anchor. Pillar 2 nudges that anchor up or down based on how active today actually was. Pillar 3 shuffles macros around the new calorie number based on your recovery and exertion signals. Pillar 4 reads where the day landed and tells you whether to add a meal, hold, or back off.

1Weekly TDEE recalibration

The textbook formula for energy balance is dead simple:

ΔWeight (kg) = (Intake − TDEE) × days / 7700

7700 kcal ≈ 1 kg of fat. Rearranged, if we know your actual weight trend and your actual logged intake, we can solve for TDEE directly:

TDEE = avgIntake − (slope_kg_per_day × 7700)

That's the whole game. The hard part is that both inputs are noisy:

A naïve "first weight minus last weight over two weeks" estimate is unusable. It will swing your calorie target by ±400 kcal week-to-week and you'll never trust it.

What we do instead

A two-stage smoother modelled on the MacroFactor whitepaper (services/expenditureSmoother.ts):

  1. Forward-fill EWMA on weight at α = 0.25. Each day's smoothed weight is 0.25 × today + 0.75 × yesterday's smoothed value. Filters single-day water swings without lagging real fat-mass change by more than a few days.
  2. Ordinary-least-squares slope fit on the smoothed series over a 28-day rolling window. The slope is in kg/day; multiply by 7 for the weekly trend.
  3. Average intake over days that actually had food entries. Zero-log days are ignored — they almost always mean the user forgot.
  4. TDEE = avgIntake − slope_kg_per_day × 7700.
85.0 86.0 87.0 Day 1 Day 14 Day 28
Raw daily weigh-in EWMA-smoothed (α = 0.25) OLS trend (−0.65 kg/wk)

Chart 1. 28 days of weigh-ins with realistic ±0.6 kg daily noise. The raw line wanders up to 1.5 kg over a single day; the EWMA captures the underlying 0.65 kg/week downward trend that the OLS fit then reads cleanly.

When the smoother turns on

We call this the "Kalman-style" path internally because it carries forward a state estimate the way a Kalman filter does, but the math is simpler — an EWMA on the weight series feeding an OLS slope — not a true Kalman update. We refuse to trust this path until all three gates are met:

Without these, two dense weeks of daily weigh-ins would silently flip the model on and you'd get a confident-looking number built on a fundamentally short history. Until the gates pass we use a deliberately conservative "fallback" path: a simple 14-day weight delta + 7-day intake average. The fallback is honest about its limits — it ships with a low confidence label and a "Based on N days" footer chip in the UI.

How the recalibration is applied

Even with a clean TDEE estimate, we never just slam the user's calorie target. The proposal goes through three safety layers:

  1. Branch-by-goal. The engine reads your goal (Fat Loss, Muscle Gain, Performance, Maintenance) and decides which direction the proposal can move. Fat Loss only fires when you're either losing too fast (faster than 1% of bodyweight per week, the ACSM-recommended ceiling) or too slow (within 0.15 kg/week of flat). Muscle Gain uses an asymmetric pair: too fast kicks in above +0.6 kg/week (water + fat creeping in) and too slow only kicks in below +0.05 kg/week (genuine stall) — the wide neutral band reflects how noisy lean-mass gains are at the weekly scale.
  2. ±250 kcal/cycle clamp. A single weekly proposal can never move your target by more than 250 kcal. If the math says "drop 600", we propose 250, watch for another week, and drop further if the trend confirms.
  3. Calorie floor. 1200 kcal for fat loss, 1500 kcal for muscle gain, applied after the delta. Below those thresholds, recovery, hormones, and lean mass start to suffer and no algorithm has any business pushing further.

After the proposal, the calorie target is recomputed into a fresh protein / carbs / fat split using the same heuristics as initial onboarding, so the macros stay internally consistent with the new ceiling. The user always sees the proposal first and taps Accept or Dismiss. We never silently rewrite goals.

2Real-time daily adjust

Pillar 1 sets your weekly anchor. Pillar 2 acknowledges a fact every serious lifter knows: a 12k-step day and a 3k-step day are not the same day. The TDEE smoother averages those out across the week, but you still want today's plan to reflect today's reality.

Formula (computeAdaptiveCalorieTarget):

rawDelta       = todayActiveCalories − baselineActiveCalories
clampedDelta   = clamp(rawDelta, −500, +500)
nominalDelta   = round(clampedDelta / 25) × 25
adjustedTarget = max(1200, baseTarget + nominalDelta)

baselineActiveCalories defaults to 400 kcal — roughly the active-energy output of a moderately-active office worker walking 7-8k steps. The calorie target rounds to the nearest 25 kcal so the number doesn't flicker minute-to-minute as HealthKit pushes step deltas. The ±500 cap prevents a single CrossFit class from suggesting you eat 800 extra kcal. The 1200 floor is non-negotiable.

2500 2000 1500 base +500 cap −500 cap Day 1 Day 14

Chart 2. Two weeks of Pillar 2 in action. Each dot is the day's adjusted calorie target after Pillar 2 reads the morning's active-energy total from HealthKit. Big lift days punch up; recovery days trim back. The band hard-clips at ±500 kcal from the weekly anchor.

Worked example. Base target 2000 kcal, baseline active 400 kcal:

You "earn" the calories you actually burned, but the cap keeps the math honest about energy compensation (your body downregulates basal output on big training days, so a true 1:1 refeed is overkill).

3Body State macro tilt

Same calorie ceiling, very different macro split — depending on how recovered, exerted, and stressed your body looks today. Five modes, chosen from sleep duration / efficiency, HRV, resting heart rate, wrist temperature delta, steps, active calories, and exercise minutes (services/bodyStateService.ts):

ModeTriggerCal ΔCarb ×Fat ×Why
Performrecovery ≥ 78 AND exertion ≥ 58+125 to +2251.14 – 1.220.94Body is ready to use fuel — push carbs around training
Recoverrecovery < 58 OR stress ≥ 76 (low-exertion side)−50 to −1000.78 – 0.861.10Steady carbs, more fat for satiety, protect protein
Stabilizehigh exertion + low recovery00.86 – 0.941.06No deficit today — sodium, moderate carbs
Cut Smartrecovery ≥ 72 AND exertion < 45−1750.901.02Lighter activity day with good recovery — gentle deficit
Baselinenothing else fires01.001.00Use your standard plan

Hard caps: calorie delta ±300 kcal, carb multiplier 0.65–1.35, fat multiplier 0.8–1.2, calorie floor 1200, protein floor 40 g, carb floor 30 g, fat floor 25 g. Every macro is also rounded to the nearest 5 g so the targets read cleanly in the UI.

The mode is recomputed every morning. If you wake up under-slept after two great days, your plan will quietly shift from Perform into Recover and your protein will hold while carbs trim — without you needing to remember to do anything.

4Deficit classification

Pillars 1-3 set what you should eat. Pillar 4 reads what you actually are eating, computes burned − eaten, and drops the day into one of eight buckets (classifyDeficit):

BucketRange (kcal under burn)ToneRecommendation theme
Extreme deficit≥ 1000dangerAdd 400–600 kcal, protect muscle/hormones
Aggressive deficit700 – 1000warnAdd 200–400 kcal, protein-forward
Optimal deficit300 – 700goodHold — fat-loss sweet spot
Mild deficit50 – 300goodSlow loss territory
Maintenance±50neutralAt maintenance
Mild surplus50 – 300neutralLean-gain territory
Moderate surplus300 – 700warnBulking — protein ≥ 1.6 g/kg
Large surplus≥ 700dangerPlan a lighter day tomorrow

Two gates before a classification is even produced: burn must be > 500 kcal (basal alone is ~1200) and intake must be > 0. Without both, the card returns no_data and tells you to log a meal or sync HealthKit. We refuse to make up numbers.

8 weeks with Alex

Alex is 86 kg, 32, male, office job, walks ~7k steps/day, lifts 3×/week. Goal: lose ~20 kg over six months without losing strength. Mifflin-St Jeor with PAL 1.4 estimates his TDEE at 2500 kcal. We seed his target at 2000 kcal/day (500 kcal deficit, ~0.45 kg/week expected loss).

WkAvg weight (kg)Avg intakeEngine actionWhy
186.12120No changeSmoother gates not met yet
285.92080No changeFallback sees −0.10 kg/wk, within target band
385.72050No change (still fallback)Trend on track; not enough span for the EWMA+OLS smoother
485.620102000 → 1750Smoother activates: smoothedTDEE 2400, slope −0.10 kg/wk — too slow; raw delta −440 kcal, clamped to −250
585.01750No changeSlope −0.55 kg/wk, on target (in −0.86 to −0.35 band)
684.41750No changeSlope −0.60 kg/wk, on target
783.517001750 → 1850Slope −0.95 kg/wk exceeds 1% safety ceiling (0.86 kg) — too fast; +100 kcal (no clamp needed)
883.01860No changeSlope −0.55 kg/wk, locked in
2100 1900 1700 W1 W2 W3 W4 W5 W6 W7 W8 −250 (clamp · too slow) +100 (too fast)
Calorie target Avg actual intake

Chart 3. Alex's calorie target across 8 weeks. The smoother sat out the first three weeks (gates not met), proposed a 250 kcal cut in week 4 (the raw math wanted −440, the safety clamp held it to −250), and pulled the cut back +100 kcal in week 7 when the trend overshot the safety ceiling. Net: 3.1 kg lost in 8 weeks at the conservative end of his target band.

2600 2500 2400 2300 W1 W2 W3 W4 W5 W6 W7 W8 Smoother activates — estimate drops 100 kcal
Static Mifflin–St Jeor estimate (2500) Smoothed TDEE (EWMA + OLS)

Chart 4. Alex's expenditure estimate over the same 8 weeks. The static formula sat at 2500 kcal the whole time. The data-driven smoother started there too (it falls back to fallback math while the gates are unmet), then in week 4 it had enough weigh-ins and intake days to compute a true slope — and immediately corrected the estimate down to ~2400 kcal, which is what triggered the −250 kcal target cut in Chart 3. Without the smoother, that 100 kcal of "phantom expenditure" would have kept Alex stuck at −0.10 kg/week for another month.

Net result: 3.1 kg down in 8 weeks (0.39 kg/week — exactly the conservative end of his target band), zero stalls, zero crashes. The engine caught the "I'm not actually losing" plateau in week 4 and the "I'm losing too fast" risk in week 7 — both the kind of signal a static TDEE app would never see.

Day 35 of his cut is also a perfect Pillar 2/3 case study. Alex's HealthKit shows:

Final macros for that day: 2275 kcal, 175 g protein, 280 g carbs, 65 g fat. A static-TDEE app would have told him to eat 1500 kcal — and his 4 PM training session would have suffered.

How this compares

CapabilityStatic MSJ appsMacroFactorHealthify
Adapts calorie target to real outcomes×✓ (weekly Bayesian)✓ (weekly EWMA + OLS slope, gates 12/10/21)
Real-time daily adjust from HealthKit×partial✓ (rounded to 25, ±500 cap)
Macro split adapts to recovery / exertion××✓ (5 Body State modes)
Safety clamp on weekly delta×✓ (~200 kcal)✓ (±250 kcal)
Calorie floor×✓ (1200/1500 by goal)
Honest "low confidence" labelling×partial✓ (low/medium/high + N-days chip)
Protein floor surfaced as nudge××✓ (15% under for 7d → coaching card)
Open formulas (no black-box)n/apartial✓ (this article + the source)

We are not pretending to be the only adaptive nutrition app. MacroFactor got there first and the smoother model in this app is built on the public foundations of theirs. The differentiators are the daily-adjust layer (Pillar 2), the recovery-aware macro split (Pillar 3), the explicit confidence labelling, and the fact that we're putting the formulas on a public page anyone can read.

What we are not doing

A few things this engine deliberately does not do, because the science doesn't support them or because they create false confidence:

Want to see it on your data?

The adaptive engine ships in every Healthify Meals account — free tier included. Log meals for ten days, sync HealthKit, drop a few weight readings, and the engine will start showing you a "Based on N days · model" chip on your Energy Balance card with the exact window backing your number.

Open Healthify →