28. 7. 2026

Kdo se moc ptá, moc se dozví

Po vší té práci na blogu jsem chtěl vědět, co by šlo na blogu zrychlit, co vyhladit, co utěsnit, a tak jsem se na to zeptal Fable - nejnovějšího LLM modelu Claude od Anthropic. Čekal jsem, že mi dá pár nápadů na později a dostal jsem osm commitů, přepsaný velký kus architektury a opravu bugu, o kterém jsem ani netušil.

Půlka archivu, o které jsem nevěděl

První věc, na kterou analýza narazila, byla, že content/posts/ mělo najednou 5 822 souborů místo 3 274. iCloud si při nějaké synchronizaci sám pro sebe vyrobil 2 548 kopií — se jménem <slug> 2.json, macOSí konvencí pro konfliktní soubor. Build by to tiše sežral a zdvojil by se mi celý archiv, výpisy, RSS, sitemapa i vyhledávání.

Server je jediný autoritativní zdroj, tak jsem to neopravoval, lokální kopii prostě zahodil a stáhl znovu — rsync --checksum --delete. Vedlejší bonus: několik nových a dva upravené posty se mi nově stáhlo přímo ze serveru.

Aby se tohle příště nestalo, přidal jsem do buildu pojistku: když dva posty vyjdou na stejný rok a slug, celý build spadne s jasnou hláškou místo tichého zdvojení. A do nahrávání na server zase zrcadlovou pojistku — když se počet souborů k nahrání najednou zvýší o víc než pětinu, deploy se zastaví. Dřív hlídal jen prudký úbytek (rozbitý build), ne prudký nárůst.

Fonty, hlavičky a jeden zapomenutý roh archivu

Web dosud tahal Open Sans z Google Fonts — cizí origin do kritické cesty vykreslení a IP každého návštěvníka mířící rovnou Googlu. Stáhl jsem si font k sobě a díky tomu zjistil, že aktuální verze Open Sans na Google Fonts je variabilní font, takže tři váhy (400/600/700) sedí v jediném souboru na jazykový subset a budu mít dva soubory místo šesti.

Po fontech přišla na řadu Content-Security-Policy, a ta odhalila něco 29 starších postů z migrace s vloženým syrovým embed videem z youtube.com, ne z bezpečnějšího youtube-nocookie.com, jak to dělají nová videa. CSP musela počítat s oběma, jinak by starým videím zčerstva přestalo fungovat přehrávání.

Dva posty také táhly skript z platform.instagram.com — čistý trackovací kód z dob migrace. Ten jsem z dat vystřihl úplně a nahradil obyčejnou kartou s odkazem.

Jednu díru jsem naopak zavřel jen na papíře, protože se zavřít nedá: Surfer, kam se web nahrává, bere přihlašovací token jako součást URL, takže končí v access logu. Ověřil jsem, že token přes hlavičku Authorization prostě na Surfer nejde poslat, a u nahrávání souborů (naprostá většina provozu) by ani tělo požadavku nepomohlo. Zůstává to tedy tak, jak to je, se zdůvodněním rovnou v kódu, ať se za k tomu za rok nevracím se stejnou otázkou.

.nosync, aneb jak nesyncovat, co nepotřebuju

Lokální public/ jsem měl na Macu v iCloud Drive — každý build tak přepisoval stovky megabajtů, co iCloud hned začal poctivě nahrávat pryč, přestože jde o čistě odvozený výstup, který jde kdykoli znovu "vyrobit". Přejmenování na public.nosync iCloud ze sledování vyřadí. Je to nedokumentované chování, ale ověřil jsem si ho přímo: xattr na přejmenovaném adresáři ukáže com.apple.fileprovider.dir#N, kterou synchronizovaný adresář nemá.

Stejná úvaha platila i pro content/ a media/ — takže i tyhle dva adresáře dostaly .nosync a lokální build, který ještě ráno neproběhl ani za deset minut kvůli iCloudí I/O režii, teď proběhne za deset vteřin.

Vyhledávání, které se učilo za pochodu

Index pro vyhledávání rostl s každým postem a stahoval se celý na každou návštěvu /search/ — 488 kB komprimovaně, i když návštěvník hledal něco z minulého týdne. Rozdělil jsem ho na dva: pět set nejnovějších postů se stáhne hned, zbytek archivu líně, až někdo skutečně něco zadá do políčka.

Cheat sheet přestal být rukojmím jednoho postu

Nápověda pro markdown syntaxi žila jako obyčejný publikovaný post, a odkaz na ni v editoru byl natvrdo zadrátovaný na jeho adresu. Kdybych ho někdy smazal nebo odpublikoval, nápověda by mířila do prázdna — nikdo by o tom nevěděl, dokud by na to nekliknul.

Vytáhl jsem markdown parser (270 řádků, co dosud žily zavřené v nástroji pro psaní postů) do vlastní knihovny a naučil build stavět i stránky mimo content/posts/. Cheat sheet je teď /markdown/ — generuje se přímo ze souboru v repozitáři při každém buildu, stejně jako se dnes generuje vyhledávání. Nejde ho smazat ani odpublikovat, protože to není post. Existuje, dokud existuje ten soubor.

Jak to dopadlo

  • lokální build: z přes 10 minut na 10 vteřin
  • soubory k hashování při běžném nasazení: ze všech ~7 700 na jen změněné
  • search-index.json na každou návštěvu vyhledávání: z 488 kB na 137 kB, zbytek líně
  • requesty na Mastodon při běhu cronu statistik: ze všech tootovaných postů na ty z jen posledních 90 dní (starší jednou týdně)
  • CSP hlavička, self-hosted fonty, žádný trackovací skript v datech: 0 → hotovo
  • osm commitů, 18 souborů, skoro devět set přidaných řádků — a jedna oprava bugu, o kterém jsem ještě ráno nevěděl

Chtěl jsem krátký přehled, ale Fable mi dal kompletní analýzu včetně řešení a oprav.

Komentáře