Home / Recipes / Build a morning digest someone will actually read

Worked query

Build a morning digest someone will actually read

/stories in recent mode. The parameters matter less than the reasoning behind them, so that is most of what is below.

The request

HTTP
GET /stories
  ?mode=recent
  &language=eng
  &from=<yesterday 06:00>
  &to=<today 06:00>
  &limit=20

Why these parameters

A digest lives or dies on whether the reader trusts it to be complete without being repetitive, and story collapsing is what makes that possible. Bound the window to the actual period rather than using a rolling "last 24 hours", so the digest is reproducible and two people reading it are reading the same thing. Language-scope it to what your readers read; an unscoped digest is not more informative, just less usable.

The obvious approach, and why it loses

The obvious approach is a rolling window — everything from the last twenty-four hours, computed whenever the job runs. It is one fewer parameter and it quietly makes the digest unreproducible: a run that fires four minutes late covers a different period, two readers comparing digests are comparing different windows, and an item can appear twice or not at all depending on where the boundary fell. Fixed boundaries cost nothing and make the digest a document rather than a snapshot. The other common shortcut, taking the top N by relevance rather than bounding the period, produces a digest that silently drops the quiet days it was most useful on.

When it returns too much, or too little

Too much means the language or topic scope is wider than the readership. A digest serving one team should be scoped to what that team reads, and the instinct to widen it so nobody is left out is what produces a digest nobody opens. Too little on a quiet day is correct and should be left alone; padding it with lower-ranked items trains readers that most of it is filler. If the digest is consistently short, the honest fix is a longer period rather than a looser query — a good weekly beats a thin daily, and the cadence is a product decision rather than a parameter.

How you would know it is working

Open rate is the wrong metric because it stays respectable long after a digest has stopped being read properly. Ask instead how often an item in it was the first place someone learned something, which you can only find out by asking; a short quarterly question to five readers is worth more than any dashboard. On the mechanical side, sample a week and count duplicates and empty sections, because those are what make a digest feel broken. If you run more than one digest, compare their overlap: two digests sharing most of their items are one digest with extra delivery cost.

When this is the wrong tool

A digest is the wrong format for anything urgent. By the time a scheduled digest is generated, an urgent story is hours old, and readers who need to know immediately will already have found out elsewhere — at which point the digest is reporting the past rather than informing. Run alerting separately, with a much tighter loop and a higher escalation bar, and let the digest do what digests are good at: comprehensiveness over speed. This recipe also degrades badly for very broad audiences. A digest that tries to serve several teams with different interests ends up relevant to nobody, and the usual failure is quiet — open rates stay respectable while nobody acts on anything in it. Narrower digests for narrower audiences outperform one broad digest every time.

Running it for real

Response fields, failure modes, adaptations and scheduling cost: Build a morning digest someone will actually read, in the docs.

Related recipes