Munchable has seven condition landing pages. They are the pages that bring in search traffic: "low FODMAP", "foods to avoid with reflux", that shape of query. They are also, in every company I have worked in, the pages where the claims quietly drift up and to the right.
Here is what ours says about SIBO, live, right now, on a page whose entire purpose is to get somebody to install the app (munchable.app/conditions/sibo):
No specific SIBO diet has strong evidence; low FODMAP is applied as a commonly used approximation. The rule set behind this page is versioned (0.1.0-draft) and cites its basis: Mapped to the low FODMAP rule set; no SIBO specific diet evidence base. Every verdict is produced by those fixed rules, never by a language model.
No copywriter chose to say that. Nobody could have chosen not to say it either, and that is the part worth writing about.
The claim lives with the rules, not with the copy
Every condition in the engine is a versioned rule set, and the rule set carries its own provenance:
export interface RuleSetMeta {
condition: ConditionId;
version: string;
sources: string[];
/** The evidence caveat this condition carries. */
evidenceCaveat: string;
}
/** A versioned rule set for one condition. */
export interface ConditionRuleSet {
meta: RuleSetMeta;
evaluate(product: NormalizedProduct, profile: Profile): ConditionVerdict;
}
evidenceCaveat is not a marketing string that happens to be stored near the rules. It is the same string the app shows in the result screen banner for that condition. The public page imports it:
// The evidence caveat is read from the rule set itself, so the public page and
// the in-app banner can never claim different levels of certainty.
const meta = RULE_SETS[page.condition].meta;
and renders it under a heading that asks the awkward question directly:
<section>
<h2>How certain is this?</h2>
<p>{meta.evidenceCaveat}</p>
<p>
The rule set behind this page is versioned ({meta.version}) and cites its
basis: {meta.sources.join('; ')}. Every verdict is produced by those fixed
rules, never by a language model.
</p>
</section>
That is the entire mechanism. It is four lines, and it changes who is in control of the claim: the page cannot be more confident than the rule set, because the sentence about confidence is a property of the rule set.
Why this is an engineering decision and not a values statement
Any team can write an honest paragraph once. The problem is what happens afterwards.
A separate marketing string drifts in three ways, all of them boring and all of them certain:
- The rules get better and the copy does not. A rule set moves from draft to a real evidence base, and the page still says draft, so you are underselling a feature you improved.
- The rules get more cautious and the copy does not. Worse direction. You discovered the evidence is thinner than you thought, you softened the in-app banner, and the page that search engines index still promises the strong version.
- A new condition ships with copy nobody remembered to hedge. The seventh page was written by copying the sixth.
Deriving the sentence removes all three. Adding a condition means adding a rule set, and a rule set that does not carry a caveat does not compile. The page then cannot be published without one, because the page reads the field.
The version string in the visible copy does a second job. 0.1.0-draft on a public page is mildly embarrassing, and embarrassment is a useful forcing function: it is a visible debt marker on the highest-traffic surface we own, rather than a TODO in a file nobody opens.
The rest of the page is derived too
Once the caveat comes from the engine, it is strange for anything else on the page to be hand-maintained. So:
The list of ingredients this condition flags is computed, not typed. Each item links to a page answering that one question, with the question phrased per condition ("Is apple OK for SIBO?", "Does chocolate cause reflux?"). Adding an ingredient to a rule set grows the internal linking of seven pages with no editorial work.
The recipes shown are the recipes that pass this condition's rule set, checked at the strictest setting the app offers, with fat and fibre computed from quantities rather than asserted in a field. A recipe cannot be listed under a condition it would fail on, because listing and passing are the same query.
The FAQ block is one array, rendered into visible markup and into FAQPage structured data from the same source, which is the only way I know to be sure the rich result and the page agree. There is no second copy of the answers in a JSON-LD literal.
The page is fully static and says so loudly:
// Fully static: the content is editorial, so there is nothing to revalidate.
export const dynamic = 'error';
export function generateStaticParams() {
return CONDITION_PAGES.map((page) => ({ slug: page.slug }));
}
dynamic = 'error' is the underused one. It does not merely prefer static rendering, it fails the build if anything in the tree reaches for a request-time API. Six months from now, when somebody imports a helper that reads a cookie three modules down, the build breaks instead of the page silently becoming dynamic and slow. Declaring an intention that is enforced beats declaring one that is hoped for.
What is still hand-written, on purpose
The editorial body of each page: what FODMAPs actually are, why packaged food is the hard part, what the pattern is and is not. That has to be written by somebody who understands the subject, and generating it would produce exactly the thin, samey pages that deserve to rank nowhere.
The split is: prose by a human, claims by the engine. Sections, ledes and FAQs live in a typed content array with a compiler-enforced shape, so a page missing a meta description or an H1 is a type error rather than a bad search result. The certainty, the version, the sources, the flagged ingredients and the recipe list all come from the code that does the actual work.
See it
- munchable.app/conditions for all seven, each with its own rule set.
- munchable.app/conditions/sibo for the honest one quoted above. Compare it with munchable.app/conditions/low-fodmap-ibs, where the evidence is genuinely strong and the caveat therefore reads completely differently. Same template, same component, opposite confidence, and neither was a copy decision.
- munchable.app/does-chocolate-cause-reflux for one of the per-ingredient pages those lists link to.
If you sell something whose accuracy varies by feature, try putting the hedge in the code next to the feature and rendering it. The uncomfortable pages get written honestly on the first pass, because nobody has to decide to be honest at the moment of writing the headline.
Top comments (0)