Jaké předpoklady tenkrát měli?

Na twitteru jsem zachytil zajímavé vlákno od Graham Lea (@evolvable). S laskavým svolením autora překládám do češtiny.
Text vyšel původně na webu autora.
Začátek vlákna, které najdete přeloženo v tomto článku:
Had a prod incident today. CPU riding at 100% in a typically sleepy webapp. Eventually tracked down the recent changes and the "offending" code that we think is causing the issue. Had the programmer done something stupid? Unlikely, she was one of our very best. But…
— Graham Lea (@evolvable) April 2, 2019
Dnes jsme měli incident na produkci. U obvykle ospalé webové aplikace vylétlo CPU na 100 %. Nakonec jsme kontrolovali poslední změny kódu, o kterém jsme si mysleli, že způsobil chybu. Udělala programátorka něco hloupého? Patří k jedněm z našich nejlepších, takže je to dost nepravděpodobné, ale…
Našli jsme kód, který iteroval přes dva seznamy a používal k tomu vnořený for cyklus, což má složitost kartézského součinu: O(MN). Tento týden M i N vyskočily na čtyř až šestinásobek, takže funkce měla náhle zhruba 30 krát větší složitost než minulý týden. Myslím si snad, že je to špatný kód? Ne, nemyslím. Proč?
Kód byl napsaný v roce 2012, tedy před sedmi lety. V té době měl každý z obou seznamů kolem dvaceti prvků a plán, byznys plán, byl, že by mohly pomalu růst. Možná o pár nových užitečných prvků za rok.
Plán se ovšem změnil. Mělo se za to, že seznamy porostou o desítky prvků za rok. V roce 2012 měly kolem padesáti prvků, letos obsahovaly tisíce a vyskočily do desítek tisíc.
Kód je snadné spravit: celý obsah jednoho seznamu se sežvýkne do mapy a pak se jednou proiteruje druhý seznam, aby se našla shoda z mapy. Jednoduché.
Proč to tak neudělal programátor původně? Protože pro původní požadavky to bylo příliš komplexní. Vnořený cyklus byla ta nejsnazší věc, která mohla fungovat, a nebyl důvod se domnívat, že by to způsobilo problém.
Sakra, kód přežil 7 let nepředvídatelného růstu před tím, než způsobil problém. Kdo by tipoval, že bude za 7 let vůbec existovat? (Vím, že většina vývojářů si přeje, aby už tato webová aplikace zmizela 😝)
Před sedmi lety nemohl nikdo předvídat, že seznamy vyrostou do desítek tisíc. (Pořád nedává smysl, že se to stalo.) Psaní komplexního kódu, který by zvládl budoucí bezdůvodné možnosti, je plýtvání časem.
Dobrá, možná v této konkrétní funkci by to bývalo ušetřilo čas. Ale sázet v každé řádce kódu na apokalyptickou budoucnost – což je to, co byste museli dělat, jelikož nevíte, který předpoklad se v budoucnu rozbije – je cesta k obrovskému plýtvání vývojářské produktivity.
Proto tleskám tomuto jednoduchému kódu. Byl založený na rozumném předpokladu a fungoval ještě léta po tom, co byl předpoklad porušen, než si někdo začal stěžovat. Tleskám vývojáři, že byl dost zralý na to, aby to vyřešil jen O(MN) funkcí. Je to agilní.
Je z toho ještě jedno ponaučení. Až budete příště ovlivnění nějakým legacy kódem, tak si nemyslete: „Co si mysleli?!“ Raději se ptejte: „Jaké předpoklady tenkrát měli?“ Je to dobrý způsob, jak si vštěpit empatii k těm, kteří přišli před námi.
Je to tak.
Dobra kravina, obhajovat kvadratickou slozitost slovy „je to agilni“ :D usetreny cas proti mape kolik? 5 minut? Hloupoucke uvazovani.
Ale on ji tak neobhajuje. Takový text by vůbec nestál za otištění. On v několika odstavcích rozebral své důvody. Já věřím, že takhle napsané to má smysl. Ne pro každého, ale s tím nic neudělám.
Článek předpokládá, že čím rychlejší kód, tím nutně musí být složitější. To ale vůbec nemusí být pravda, obzvlášť pokud se použijí knihovní funkce, které jsou obvykle optimalizované. Obhajovat neefektivní kód tím, že je jednodušší napsat, je obvykle jen výmluva. Výkonnější kód také může vést ke snadněji pochopitelnému výsledku, protože i když je třeba složitější, donutí nás to vyčlenit tu složitost stranou.
Proto je lepší se takovýmto botám (kvadratická složitost, kde je triviální dosáhnout lineární) vyhnout už předem.
Já myslím, že v článku je jasně napsané jak ten kód vypadal a jak vypadala optimalizace. Nepřišlo mi, že by se tam nějak zdůrazňovala složitost kódu. Každý kdo umí programovat má jasnou představu, že ta optimalizace byla velmi jednoduchá. Hlavní poučení z článku je, že to že něco vypadá jako špatně napsané nemusí být nutně pravda, protože v kontextu původního zadání/předpokladů to bylo ok.
Co se týká optimalizací, tak jsou dva extrémy: 1. „příprava na 3. světovou a přílet ufonů“, 2. „PNJ = problém někoho jiného…kompilátoru/interpretu/toho kdo ten kód dal na stack overflow…“. Ani jeden z těchto přístupů není správný. Starší generace, pod dojmem šetření prostředků za každou cenu, a vědecké typy inklinují k extrému 1. Mladší či méně zkušení pak k 2. Umění je najít ten správný balanc. No a někdy ten nalezený balanc v aktuálním kontextu nemusí vypadat ideálně, zatímco v kontextu minulém ideální byl. No a právě o tom je ten článek…
Ne, článek nepředpokládá, že rychlejší kód musí být nutně složitější. Ale dobře žes upozornil, že by si to někdo mohl do textu promítnout.
Preberal som niekolko velkych projektov. Po prvotnom nadavani som casom „dospel“. Snazil som sa pochopit preco to ten predomno tak napisal (to vzdy pomoze ked pochopite ako rozmyslal ten pred vami). No a teraz uz mam pokoru k takymto kodom. Pretoze viem kedy a ako vznikali. No a ked to vsetko vstrabam tak hura prerobit.
PS: Skusali ste si otvorit svoj 8 rokov stary projekt do ktoreho ste nezasahovali?