By Dan & Katya · April 12, 2026
The 308 Fartleks: When We Audited Our Own Catalog
We suspected the plans were slightly off. Weeks blurred together. A few users mentioned that the intermediate plans felt a little too hard. We had no data to back any of it — until last week.
So we built a small Swift command-line tool. It ran the engine for every (distance × level × duration) combination, dumped the plans to plain text, and put them next to the published reference plans from Hal Higdon, Pete Pfitzinger, and Jack Daniels.
The first line it printed:
Of 590 workouts in our catalog, 308 were fartleks. The engine picked zero of them across 31 test plans.
Not a typo. Zero.

That was the audit. This is the writeup.
Why we did this
The engine (about a thousand lines of deterministic Swift) takes your inputs, picks workouts from a catalog, and assembles a calendar. At audit time the catalog held around 590 workouts: easy, intervals, threshold, fartleks, ladders, pyramids, long runs, progressions, recovery, hill repeats, race rehearsals.
The question was simple: is it choosing well? It was not.
The tool came first
Before we could audit anything, we needed a way to see what the engine produced. Until then, the only way to inspect a generated plan was to:
- Launch Xcode.
- Build to the iPhone simulator.
- Tap through onboarding.
- Pick a configuration.
- Generate.
- Tap through the plan, week by week.
Five to ten minutes per plan, by hand. Thirty-one configurations is multiple hours of clicking, every time we touched engine code. That was never going to happen consistently, so the first thing we built was not a fix. It was a way to look.
A standalone Swift tool runs the engine directly over a hardcoded list of configurations and prints the plans as plain text. It compiles in five seconds, runs in under one, and shows all 31 at once. It took an afternoon to write and immediately became the most-used development tool in the project. It still is.
What the CLI showed
The summary table for 31 configurations, before the audit:
Plan Weeks Workouts E L H P F load/wk min/wk
Beg 5K (short, 5w) 5w 10 4 0 5 1 0 8160 63
Beg 5K (rec, 7w) 7w 14 6 0 6 2 0 7483 61
Beg 10K (rec, 9w) 9w 18 1 4 7 6 0 8822 84
Beg 21K (rec, 14w) 14w 40 6 12 10 12 0 12372 138
Beg 42K (rec, 18w) 18w 52 4 16 14 18 0 14136 158
Int 21K (rec, 14w) 14w 54 13 11 19 11 0 16706 167
Int 42K (rec, 18w) 18w 70 12 18 26 14 0 18378 184
Adv 42K (rec, 18w) 18w 90 24 18 26 22 0 29994 295
...Columns: E=easy, L=long, H=hard, P=progression, F=fartlek.
Three things were immediately clear.
The fartlek column was all zeros
308 fartlek templates in the catalog, chosen zero times across 31 plans. Half the catalog: parsed on every cold start, scored in every selection loop, and never once selected.
This is not an argument against fartleks. They are a good workout. But the engine picks the best fit for what each week needs, and a fartlek was never the best fit. Need intensity, and intervals or a threshold run scored higher. Need easy aerobic volume, and an easy or long run scored higher. The fartlek sat in the middle and lost every comparison.
Almost no easy runs
Easy running should be 70–80% of training volume in a healthy plan. That is Daniels’ rule and roughly the consensus of endurance coaching. In our plans, easy runs were 4 to 24 workouts across 14–18 week plans — somewhere between 5% and 30% of the total. Far too low.
The reason was in the catalog. It had four easy-run templates. Four, at 30, 35, 40 and 45 minutes, all Zone 2. The engine carries a duplicate-avoidance rule: repeating the previous week’s workout costs a large score penalty (+2.0). With only four easy templates and that penalty active, the engine ran out of easy variety around week 5 or 6. After that it preferred almost anything to another easy run — a progression, then a threshold, then intervals. By week 8 of a marathon plan the easy aerobic runs were gone and the plan was effectively all hard. No real base.
That is the 80/20 violation, in detail.
The marathon volume was too low
A beginner marathon plan averaged 158 minutes a week. Higdon’s Novice 1 marathon is around 250. We were 37% under. Higdon gets a first-time marathoner to 20-mile long runs, near 200 minutes; our plans capped the long run at 120. Our “trained” beginner arrived at the start line under-mileaged.
The full catalog, before the rebuild
| Workout type | Count | % of catalog |
|---|---|---|
| Fartleks | 308 | 52.3% |
| Ladder intervals | 78 | 13.2% |
| Intervals | 74 | 12.6% |
| Steady long runs | 36 | 6.1% |
| Progression runs | 33 | 5.6% |
| Threshold | 32 | 5.4% |
| Progressive long runs | 24 | 4.1% |
| Easy | 4 | 0.7% |
| Tempo | 0 | 0% |
| Long | 0 | 0% |
| Recovery | 0 | 0% |
| Strides | 0 | 0% |
| Hill repeats | 0 | 0% |
Half fartleks, none used. Easy runs at 0.7% of the catalog. No tempo. No real long-run type of its own, although the engine referred to one in several places — that path was dead.
We had not designed this catalog. We had grown it over months, adding workouts whenever we needed one and never looking at the totals. From the inside it looked varied — look at all these fartlek variations — but once the engine’s scoring ran over it, it was narrow.
What we changed
Three weeks of evenings and weekends, in five steps.
1. Drop the fartleks
fartleks: 308 → 0If the engine never picked them, they were weight and nothing else. The JSON shrank, cold-start parsing got faster, the scoring loop got shorter, and no plan changed — because none of them had used a fartlek in the first place.
2. Expand the easy runs
easy: 4 → 43Easy runs at varied durations — 20, 25, 30, 35, 40, 45, 50, 55, 60, 70 minutes — and varied targets: some Zone 1 for genuine recovery, some Zone 2 for aerobic base, some mixed. This was the single most important change to the catalog. With 5 to 10 unused easy templates always available, the duplicate-avoidance penalty stopped pushing the engine away from easy running.
3. Add the missing subtypes
The audit showed the gaps:
- Recovery — no fixed HR target, easy by feel, 20–30 minutes, after a hard day. Distinct from easy.
- Strides — short pickups, around 6×20s at 5K effort. Aerobic-friendly speed.
- Hill repeats — explicit hill work, BASE and SPEED only.
- Time trials — 3K or 5K all-out, placed in PEAK as a fitness re-check.
- Race rehearsals — a long run with a goal-pace middle, three weeks out.
- Mile repeats — the signature 10K-and-up workout.
- Yasso 800s — the marathon 10×800m predictor.
- Marathon-pace runs — explicit marathon-pace work in PEAK.
Each got between five and thirty templates. Dropping the fartleks had taken the catalog down to about 280 workouts; the new subtypes and the larger easy pool brought it back to roughly 640.
4. A few engine changes
The catalog work did most of the job. A few engine adjustments finished it:
- The third weekly slot defaults to easy — it used to default to a progression run. Together with the catalog work, this moved beginner marathon plans from about 11% easy to roughly 70%.
- Recovery weeks became phase-relative — a recovery week now falls every third week within a phase, not every third week of the plan. It lands more naturally.
- The marathon long-run cap went up, 120 → 180 minutes for beginner marathon, in line with Higdon.
- Hill repeats are scheduled only in BASE and SPEED, capped at one a week.
- Race-anchor workouts are gated by distance — Yasso 800s in marathon plans only, race rehearsals scaled per distance.
5. Check against the references
After the rebuild, the CLI showed:
Plan Weeks Workouts E L H P load/wk min/wk
Beg 42K (rec, 18w) 18w 52 38 16 14 18 16500 197
↑ was 438 easy runs instead of 4. 197 minutes a week against 158 before — still about 20% under Higdon’s 250, but much closer. The pace-based plans validated separately at 78% easy, 22% threshold-or-faster: Daniels’ 80/20, almost exactly.
How we compare now
| Metric | Beginner 42K (post-rebuild) | Higdon Novice 1 | Pfitzinger 18/55 |
|---|---|---|---|
| Volume | 197 min/wk | ~250 min/wk | ~450 min/wk |
| Easy runs | 38 in 18w | 50+ | 50+ |
| Long peak | 180 min | 200 min | 165 min @ faster pace |
| Hard workouts | 14 in 18w | 0–1 | ~14–18 |
| Easy:hard ratio | 73% easy | ~100% easy | ~75% easy |
In the Higdon range on purpose — Novice 1 is the canonical beginner reference. Pfitzinger 18/55 is higher-volume intermediate, and our intermediate marathon comes closer to that one.
What the audit taught us
The first lesson: the catalog is the product, not the engine. We had spent months treating the scoring function as the hard part and the catalog as mere data. It is the other way around — a good engine on a bad catalog produces bad plans, reliably.
The second was to build the tool that shows you what your system actually does before trying to improve it. The CLI took an afternoon and told us in one line something that months of working inside the app had hidden.
The third was to measure against people who know more than we do. “Get within 20% of Higdon’s Novice 1 volume” is a target you can check. “Generate good marathon plans” is not.
What’s next
The audit left one gap we have not closed. Our race-pace anchor workouts — race rehearsals, mile repeats, Yasso 800s, marathon-pace runs — exist only for half-marathon and marathon. 5K and 10K plans have no race-pace-anchored work at all. That is the next round: 5K-pace and 10K-pace runs, fast-finish runs (an easy run with a race-pace tail), and 10K race rehearsals. All of it gated by distance, so a 5K plan gets 5K-pace work and a 10K plan gets 10K-pace runs and mile repeats. It should ship in the next couple of weeks and take the catalog to around 740.
The CLI will check all of it. Every engine change and every catalog change now goes through it before it ships. If we had not built it, we would still have 308 fartleks and four easy runs — and we would not know.
Further reading
- Training for the Uphill Athlete — the book that gave us the framing for Step 2, aerobic base first.
- The Architecture Story — the engine the audit ran against.
- Hal Higdon — Training Programs — the free public plans we check against.
- Jack Daniels — Running Formula — source of the 80/20 rule and the pace tables.
- Pete Pfitzinger — Advanced Marathoning — source of the intermediate-marathon phase structure.
Run Plan is an indie iOS + Apple Watch training planner built by a 2-person team in Amsterdam. No accounts, no ads, no subscription. Your data stays on your device.