Proč jsme pesimističtí ohledně optimistického UI u webových aplikací?

Jedním z problémů, kterým při vývoji webových aplikací dlouhodobě čelíme, asi jako každý, je pomalost webu z pohledu uživatele. Kdysi dávno v internetovém pravěku jsme ve firmě psali weby v klasickém PHP a každá akce uživatele znamenala aktualizaci celé stránky. Uživateli se tedy stránka jevila jako poměrně neresponzivní – kdykoliv něco udělal, musel neúměrně dlouho čekat, než se změna uložila na serveru a projevila zpět na webu.

Postupem času jsme si řekli, že zkusíme jiný přístup a naskočili jsme na vlnu SPA, Single Page Aplikací. SPA sice přinesly ze začátku nějaké obtíže (proč mi nefunguje tlačítko Zpět?!), ale odbouraly jeden základní problém: najednou byly všechny akce uživatele asynchronní a nevyžadovaly aktualizaci celé stránky. My jsme se ale rozhodli, že půjdeme ještě o trochu dále a zkusili jsme použít něco, čemu se říká Optimistic UI.

Ve chvíli, kdy uživatel provedl nějakou akci, například když třeba přejmenoval reklamní kampaň, webová aplikace se tvářila, jako že se ta akce povedla ještě předtím, než jsme dostali odpověď od serveru. Výhoda je na první pohled jasná: web se tváří naprosto responzivně, protože nikdy na nic nečeká. Ještě, než nám přijde odpověď ze serveru, tak vám web na všech místech zobrazuje nové jméno kampaně. Vlastně už to nejde udělat responzivnější a rychlejší! Aby tato strategie mohla vůbec mít šanci na úspěch, museli jsme splnit některé předpoklady:

  • Museli jsme v prvé řadě zajistit, aby HTTP požadavky na server v drtivé většině opravdu procházely. Pokud by každý druhý požadavek selhal, asi by to nefungovalo moc dobře. A že jsme se snažili, aby požadavky prošly! Ještě v prohlížeči před odesláním dat na server jsme zvalidovali co jsme mohli. Pokud web odeslal požadavek na server, bylo na 99,9 % jasné, že to projde, leda by nefungovala databáze nebo kdyby nastalo nějaké podobné peklo.
  • “A to jste nepotřebovali číst odpověď ze serveru?” ptáte se. Nepotřebovali! Všechny akce na serveru máme rozdělené na Commandy a Query. Query je typicky GET požadavek, který vrací data ze serveru, ale nemění stav systému. Command je typicky POST/PUT/DELETE požadavek, který mění stav systému, ale zase nevrací žádná data. Výsledkem každého našeho commandu je buď chyba, nebo prázdná data. Web neměl na co čekat, optimisticky čekal, že command na serveru prošel a žádná data nazpět od něj nepotřeboval.

“No a co když ten command teda neprošel?”

Jako bych vás slyšel! To byl kámen úrazu. Přes všechnu snahu se samozřejmě občas stalo, že command neprošel. Možná fakt zrovna neběžela databáze, možná jsme měli chybu v kódu, nebo možná uživatel vjel do tunelu a ztratil připojení k internetu. V tuto chvíli command neprošel a my jsme to museli nějak vyřešit.

Jenomže to nebyl zrovna snadný úkol.

Představte si, že jako uživatel procházíte webovým rozhraním a něco si klikáte. Tady změníte nastavení, zvýšíte rozpočet, támhle něco přejmenujete, upravíte cílení kampaně, smažete nějaké štítky, přidáte nové… A až teď se web dozví, že na serveru neprošla změna rozpočtu kampaně, protože něco. Na serveru se něco zaseklo a frontend po dvaceti sekundách zjistil, že rozpočet se nepovedlo změnit. Jakým způsobem bychom to vůbec měli dát vědět uživateli, který už po dvaceti sekundách upravuje úplně jinou kampaň v úplně jiné části webu? A co máme dělat s tím zbývajícím nastavením, které uživatel provedl? Neměli bychom revertovat vše, co od té doby udělal? Když uživatel třikrát přejmenuje kampaň a druhé přejmenování failne, ale třetí ne, máme se tvářit, že je všechno v pohodě? A co když už uživatel ani nemá otevřené okno, ve kterém změnu prováděl? Co když zaklapl notebook a šel na oběd?

To jsou všechno otázky, na které určitě existují dobré odpovědi. Přesto jsme poměrně dlouho žili s tím, že jsme ty chyby moc neřešili a když už jsme nějaké ošetření chyb napsali, bylo to spíš ve stylu “ouha, něco se nepovedlo, aktualizujte si prosím stránku”. Tohle kupodivu na dlouhou dobu stačilo a naši zákazníci si příliš nestěžovali. Na druhou stranu některým našim zákazníkům vyhovuje to, že se ve špičce generují reporty přes deset minut, protože si aspoň můžou zajít uvařit kafe… Takže “Stěžují si zákazníci? Nestěžují.” nebyl v tomhle případě zrovna dobrý benchmark. Tohle všechno se navíc dělo v době před-reduxové, webová appka byla napsaná v AngularJS a změna názvu kampaně byla naimplementována tak, že se udělalo campaign.name = “newName”. Což není zrovna ta nejlepší možná implementace, pokud chcete dále mít věci jako undo.

Jak z toho ven? Pesimisticky!

Když jsme o několik let později začali vyrábět nový frontend, tehdy nad Angularem 2, znova jsme se řešili, jestli chceme zase jít cestou optimismu. A vůči této cestě jsme byli značně pesimističtí. Uvažovali jsme takto:

  • Máme-li pomalé commandy, strašně blbě se řeší undo a obecně error handling. Je blbé uživateli po x sekundách oznamovat, že ta předposlední akce, kterou provedl, se ve skutečnosti nepovedla. Pro programátora je náročné to naprogramovat, pro uživatele je těžké to pochopit.
  • Máme-li rychlé commandy, není třeba žádné optimistické rozhraní, protože rychlé commandy nebudou uživatele zdržovat. Web může vždy čekat na výsledek commandu a změny projevit až po úspěchu. Pro programátora je to jednoduché naprogramovat, pro uživatele je snadné to pochopit.
  • Téměř každá akce v našem webovém rozhraní je důležitá. Když failne jeden z tisíce lajků, které dáváte na Facebooku, tak se svět nezboří. Pokud si myslíte, že jste změnili rozpočet kampaně z milionu korun na sto tisíc korun a ono se to ve skutečnosti nepovedlo, tak je to velký problém.

A proto jsme se vrátili k pesimistickému UI. Změní-li uživatel nějaké nastavení, pošleme command na server a frontend poslušně čeká na výsledek. Během toho zobrazuje uživateli progress bar, aby uživatel věděl, že se něco děje. Fun fact: protože drtivá většina commandů je fakt rychlá, museli jsme uměle prodloužit dobu, po kterou zobrazujeme progress bar, protože jinak se mihl třeba jenom na 100 ms a uživatel si toho ani nevšiml a vypadalo to divně, spíš jako nějaká porucha. Během této doby může uživatel zároveň editovat jiné části webu, nemusí jenom hloupě čekat, až tento jeden command doběhne. Na konci uživateli zobrazíme třeba fajfkou, že se jeho akce zdařila, nebo mu někde zobrazíme nějakou krásnou chybovou hlášku. Uživatelé jsou spokojeni, že hned vidí výsledek, který je navíc spolehlivý.

Ve skutečnosti je z toho win-win, pro uživatele i pro programátory. Uživatele získali pesimistickým UI spolehlivější web a programátoři si zjednodušili práci. Co víc chtít?

Líbil se ti tento článek?

Odeslat komentář

Vaše emailová adresa nebude zveřejněna. Povinná pole jsou označena *