Build1 publisher3 min readPublished
A 90-day date window trims each hreflang decision from 1,083 candidates to twenty
GoodBarber's engineering lead reconciled 5,892 posts across seven language blogs with gemma2:27b on one MacBook. What makes it work is the retrieval layer that cuts every decision down to twenty summaries.
The Engineer · Build desk

What happened
- GoodBarber's blog held 5,892 posts across seven language hosts, the oldest dated November 7, 2011, and no field anywhere recorded which post was the translation of which.
- The whole backfill ran locally on a MacBook Pro with an M3 Max and 48 GB of memory, with Ollama as the runtime.
- The matching model was gemma2:27b, Google's June 2024 open-weight release, taking 15 GB on disk at the default 4-bit quantization.
- For each French pivot article, the script offers the model at most twenty candidate summaries from the target language published within 90 days, ordered closest first.
- A sidecar file records which pivot-and-language pairs were already attempted, so an interrupt or a rerun with new thresholds never pays for the same prompt twice.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- cost The expensive part of a bulk matching job is the reruns while the prompt is still wrong, and on a metered endpoint every threshold change re-bills the corpus; the author built his idempotence record only after rerunning several times.
- constraint The 90-day window sets a recall ceiling nothing downstream can lift: a translation published on day 100 is never offered to the model, and it never shows up as an error either.
- contradiction The prompt reserves confidence above 0.8 for a clearly identical article, while the pipeline accepts anything from 0.7 up, so the gate admits precisely the band the prompt calls unsure.
- capability 16.2 GB of weights on a 48 GB laptop keeps a seven-language record-matching job inside one machine, which means no corpus leaves the building and no rate limit applies to the tuning loop.
Most of the accuracy in this pipeline is bought before the weights load, and the author says so plainly: a retrieval step does not need to be clever, it needs to make the question small [20]. English holds 1,083 posts [18]. Each call sees twenty summaries at most, pulled from a 90-day window around the French original and ordered by date proximity [17]. That is under 2% of the English corpus per decision, a 54-fold cut in the comparison set [26]. The completeness requirement is what makes the job unavoidable in the first place: hreflang wants every version to carry the full list of its siblings [4], so seven languages [2] means 49 link entries per article family [27].
The hard cases are the ones the window keeps in. GoodBarber publishes a monthly "What's new at GoodBarber" post that carries the same title every month in every language [8], so a 90-day window holds roughly three of them [30], and the model has to separate them on summary text alone. The cheap keys fail structurally. The id at the end of a URL is a per-blog counter, -a1332 in French against -a1486 for the same English article [7]. Slug overlap survives only when the translator left the English words in place, which is to say when the translation was done badly [9].
Two rules sitting downstream of the model shape the output more than the prompt does. An article can belong to one row only, so a matched URL leaves the candidate pool for every later pivot [23]. That makes the assignment greedy and order-dependent rather than globally optimal: whichever French article reaches a contested English post first keeps it. The confidence figure the gate reads is also a token the model emits, not a measured error rate, and temperature is pinned at 0.0 in the call [22], which makes the run reproducible without making the number calibrated.
The supplied write-up does not close that loop. It reports no precision, recall or spot-check figure for the matches, no token count and no currency cost, and the text breaks off mid-sentence in the passage comparing this with a hosted API [31]. bge-m3 is listed as one of the two models used, MIT licensed at 1.2 GB and 1,024 dimensions [12], but the retrieval described ranks candidates by publication date, and the text never says where the embeddings do work [33]. Worth keeping in view: the author runs engineering at GoodBarber [5], and the post trails a Hacktoberfest event on this same subject on October 21 [6]. The strongest claim the evidence carries is narrower than the framing, and it is still a good one: a 23-month-old open-weight release [34], with a competent retrieval layer in front of it, was enough for a 5,892-article reconciliation [15] that nobody has published an error rate for.
The transfer conditions are specific. Your CMS has to expose summaries that describe content rather than tease it, because the summary is the entire evidence base the prompt gives the model [21]. One dominant source language has to exist, since French is the pivot and every other language is matched against it [16]. The mapping has to be one-to-one, which is what the exclusivity rule assumes [23]. Fourteen and a half years between the oldest post and the crawl [29] did not break that assumption here, but a blog that splits one article into two in another language, or merges two into one, will produce a confidently wrong hreflang set rather than no set at all.
What to watch
- Whether a precision and recall figure on a hand-labelled sample gets published, since the thresholds are unusable without one.
- Whether the 90-day window is widened, and what that does to the twenty-candidate cap on the same context budget.
- Whether the roughly 600-line script is released around the October 21 Hacktoberfest event; the supplied text does not say it is.