On 13 September we wrote about how Lonevi changed its default language without losing an address: every page, including the articles section, was announced to search engines in English, Russian and Kazakh. On 22 September the owner of Lonevi took the next decision for one part of the site: its articles are published in English only. The Russian and Kazakh versions were to be removed.

Removing them meant retiring 61 published articles whose addresses search engines had already indexed and people had already shared. This is how that was done without turning any of those addresses into a 404, why the order of the steps was not a matter of taste, and what is still open.

What was there

Measured on production on 22 September, before anything changed: 101 published articles in 40 groups of translations — 40 in English, 40 in Russian, 21 in Kazakh. Not one Russian or Kazakh article lacked an English version in its group. That zero mattered: it meant every address about to be retired had somewhere to lead.

The map lived in the rows being deleted

A retired address should lead to the same article in English, not to the section's front page. The obvious way to find that article is to ask the product which English article belongs to the same group as the Russian one. That works exactly until the Russian row is deleted — and the link between translations is stored on the article rows themselves. Delete them and the question has no answer: all 61 redirects would fall back to the section's list, silently, after a deployment that had passed every check.

So the map was taken from production by a script before anything was deleted and compiled into the site: 61 entries, each a retired address and its English heir. It was needed because the addresses are not simply the English ones with a language prefix: 25 of the 40 Russian slugs and 6 of the 21 Kazakh ones differ from their English counterparts. Stripping the prefix from those would have led nowhere.

A list compiled into code usually goes stale: an editor publishes something new and the list does not know about it. That objection does not apply here, because the same change closed the set. The publishing schema now accepts one language. We checked the live schema on 23 September: the language field for a new article allows only English, and for an edit, English or nothing. A 62nd Russian address cannot appear, so the list cannot fall behind.

The door nobody looks at

A retired address can arrive in three shapes, and each needed its own handling:

Three shapes of a retired address
AddressWhat leads it to the English article
/ru/articles/… and /kk/articles/…a new rule for the language prefixes
/articles/… with a Russian slug and no prefixa lookup in the compiled map
/en/articles/…the existing rule that drops the default-language prefix

The second row is the one that matters and the easiest to miss. The addresses search engines had indexed were the bare ones, because they are older than the default-language change. Until this release a bare Russian slug was served by asking the backend which language owned it. After the deletion the backend would have answered that no such article exists, and the 25 bare Russian addresses whose slug differs from the English one would have become 404s after a release that had passed its checks. That function was removed and its place taken by the map. Checking only the prefixed addresses would have tested the door search engines do not use.

Order: redirects first, deletion second

The work was split into two changes on purpose. The first put the redirects in place and closed the publishing schema to other languages; it deleted nothing. The second added the tool that archives and deletes the rows. Both went out in one release, but deploying the tool did not run it: its default is a dry run. The sequence was release, then checking the redirects on production, then the deletion.

The reason is the section above. The map had been taken from rows that still existed. Run the deletion first, and the retired addresses would have stopped leading anywhere, with the map recoverable only by digging it out of the archive.

A deletion that refuses to orphan anything

Two other tables point at articles, and in both the link is set to empty when an article is deleted. Deleting an article would not remove those rows. It would quietly blank their reference: a stored file would stay reserved by an article that no longer exists, and an enquiry from a real person would lose the article it came from. So before archiving anything, the tool counts rows in other tables that point at the articles it is about to remove, and stops, naming them, if there are any. On production there were none.

The list of those tables is not written in the code. The tool asks the database's own catalogue which foreign keys point at articles. A list written from memory is incomplete sooner or later — a table added next month would be found by the query and missed by the list. If the catalogue returns nothing at all, the tool stops too: the product's migrations create exactly two such keys and no later migration removes them, so an empty answer means the question failed, not that the database is clean.

That last branch was written before a test for it existed, and a deliberate mutation that removed it left the whole suite green. A branch no test can turn red is indistinguishable from a branch that is not there. A test was added that substitutes a query answering nothing, and the mutation now fails it.

Everything the tool removes is copied first into a permanent archive table, as whole rows rather than a chosen list of columns, and the number of archived rows is checked against the selection before anything is deleted. Each removal is written to the audit journal by name, with its former language and slug, so the question of who removed this particular article, and when, has an answer rather than a count.

A defect found on the way

While changing the section, the team found that the site's article feed was entirely Russian — all 31 links — and so was the preview card shared on social networks. Both took Russian as the default value of a parameter. The fix removed the parameter rather than changing its default: the language of articles is now asked of one module, the only place it is named.

What we checked on production

The deletion ran on production after the redirects had been checked there. It archived and removed 61 articles, 40 Russian and 21 Kazakh, and a repeat dry run found nothing left to remove. The team then made 122 checks — a prefixed and a bare address for each of the 61 retired articles. 92 answered with a permanent redirect to the named English article. 30 answered 200: they fall on 15 bare addresses, each shared by a Russian and a Kazakh version whose slug is the same as the English one, so the address is simply the surviving article. None answered 404. The sitemap and the feed carry no Russian or Kazakh article addresses.

For this article we repeated part of that on 23 September. The bare Russian address of the article on amblyopia redirects to its English heir, as does the same address with the /ru/ prefix and a Kazakh address to its heir; the section's /ru/, /kk/ and /en/ entry points redirect to the section; an address that never existed still answers 404. The feed declares English and lists only English articles, and the product's article index lists only English entries.

What is still open

  • The reading side still promises three languages. The schema for writing an article accepts only English; the type used for reading still lists Russian and Kazakh, which the database can no longer return. That was left deliberately for the window between release and deletion, so the feed and sitemap would not fail on the rows still waiting to be removed. Narrowing it is filed as a separate open issue.

  • Readers of Russian and Kazakh lose the section in their language. The rest of the interface stays in three languages; the articles are now English only. That is the decision, not a side effect, and we state it plainly.

  • Part of our own earlier article no longer describes the product. The 13 September piece described the articles section as announced in three languages, and an old bare Russian address as leading to the same article under /ru/. For articles neither is true any more: the section is English only, and an old bare Russian address now leads to the English article. The rest of what it described still stands.

If you are evaluating a supplier

When a supplier tells you a change keeps every old address working, ask where the list of redirects lives, and whether it survives the data it was built from. Ask what deleting a record does to the rows that point at it — not whether they are removed, but whether they are quietly blanked. And ask which address they tested: the one in their notes, or the one a search engine actually holds.

Lonevi is a longevity platform of the group. The previous step in its move between languages is in Changing the default language without losing an address.