Osvědčené postupy při tvorbě velkých aplikací.
Používejte knihovny a zavedené standardy
-
Neni nic horšího, než psát vše ve vaší velké aplikaci od začátku. Pokud je to projekt opravdu velký, většinou není čas soustředit se na vytváření hromady kódu, který už před vámi někdo napsal, odladil všechny edge casy a pořádně otestoval.
-
Obecně platí pravidlo, že čím víc mají knihovny stažení na npm.org, tím více jsou známé a tím víc podpory a kvalitní dokumentace pro ně existuje.
Knihovny, které můžu osobně doporučit:
- DayJs pro zpracování času
- Ramda pro funkcionální programování
- Bootstrap (a Bootstrap Vue) pro vzhled
- Store pro persistentní úložiště
- Axios pro api requesty
Dodržování postupů z dokumentace
-
Ať už používáte jakoukoliv knihovnu či framework, to nejlepší co můžete udělat je dobře se seznámit s dokumentací. Není nic horšího, než když začnete z neznalosti knihovny, která vám má usnadnit práci, zrychlit vývoj a zjednodušit kód používat různé hacky a workaroundy a svůj program tak naopak zkomplikujete. Tudíž se před použitím jakékoliv knihovny poctivě seznamte s dokumentací a vyzkoušejte si patterny/metody/funkce, které budete v projektu používat.
-
A až to zvládnete, nebojte se tyto patterny použít i tam, kde vás knihovna/framework nenutí přímo je používat. Např. používejte barevné varianty z Bootstrapu (
info
,primary
,secondary
,success
...) nejen v Bootstrapovských komponentech, ale i ve vlastních wrapper komponentech. Pokud máte globální soubor s scss variables, opět použijte názvy, které už dobře znáte. Naučte se specifickému řešení nějakého setu problémů a už nad nimi nebudete muset váhat, mozek si zvykne a jeho cenná kognitivní kapacita se uvolní pro řešení důležitějších věcí.
Spravně nastavený eslint
-
Správně nastavený eslint vás může zbavit mnoha bolehlavů. Již nemálo bugů jsme díky němu v práci odhalil. Umí najít použití nedefinovaných variables
no-undef
, zabrání commitnutí bizardností do kódu (porovnávání proměnné samotné se sebou -no-self-compare
, použití přiřazení (=) v if statementech, místo comparisonu (===) -no-cond-assign
), zabrání dvojtým importům -no-duplicate-imports
, zdvojeným argumentům ve funkcích -no-dupe-args
a parametrům v objektech -no-dupe-keys
) a vynutí standardy psaní kódu -camelcase
,quotes
... -
u některých věcí jsou jejich výhody jasné, u jiných (např. opinionated styl psaní uvozovek) to může vypadat na zbytečný code fascism, ale není tomu tak, jeden příklad za všechny - vynucení jen jednoho způsobu psaní uvozovek. Věřte že, není nic horšího, než muset hledat "string" a pak znovu 'string', jen aby jste mohli něco zrefaktorovat. Pokud si myslíte, že na to budete myslet a nezapomenete že někde v kódu se vám potuluje i ten druhý způsob, pletete se. Děláte velké aplikace a mozek máte jen jeden. Vžijte si jeden způsob psaní kódu, přestaňte myslet na zbytečnosti a uvolněte své mentální dovednosti tam kde jsou třeba.
Statická analýza - Typescript
- Typescript se používá z několika různých důvodů. Především vám pomůže předejít bugům typu "Cannot read property x of undefined," pomůže vám také lépe zdokumentovat váš kód, což je skvělé z toho důvodu, že nemusíte v takovém rozsahu udržovat běžnou dokumentaci, což se často ani nedělá. Je třeba brát v podtaz, že při použití typescriptu budete psát kód jinak. Předně tam přidate pár ifů, kterými budete zjišťovat jestli je element, ke kterému přistupujete skutečně definován. Pak např. aby jste nenarušili strukturu svých interfaců, budete přidávat různé pomocné proměnné. Když např. píšete harmoniku elementů a chcete uživateli zobrazit jen některé její položky, bez typescriptu by vás mohlo napadnout k objektu přidat novou propertu boolean "show", která bude určovat, zda je objekt viditelný. Při použití Typescriptu jste ovšem donucen napsat úplně nový array, ve kterem si budete ukladat pouze binarni informaci o tom, zda jde properta vidět.
Code review
- Je třeba pamatovat na to, že žádná, jakkoliv inovativní metoda, nedokáže vychytat především sémantické chyby a konzistentnost kódu. K tomu slouží code review, kde se developer, který kód reviewuje, snaží najít potencionální chyby, které může nový kód vytvořit, dává pozor na to, že jsou dodrženy standardy a napsány testy a především se seznamuje s nově napsanýma funkcionalitama, aby na ně byl v budoucnu schopný navázat. Tento developer má dle mého názoru potřebný nadhled k tomu, aby doporučil komentáře, nebo zjednodušení kódu a tak se zamergoval jen kód, který je pro něk pochopitelný.
Code ownership
- Code ownership je princip, kdy každý developer vlastní kód, který napsal a očekává se, že pokud bude třeba rozšířit fičuru, kterou psal on, rozšiřovat ji bude opět on. Code ownership se postupem času začalo považovat za anti-pattern a tedy došlo k posunu směrem ke sdílenému kódu. To je princip, kde kód vlastní a spolupracují na něm všichni programátoři a každý programátor může dostat úkol z libovolné oblasti dané aplikace. To samozřejmě nic nemění na tom, že určití programátoři mohou mít určitý focus na specificko část aplikace, nicméně pokud se při řešení svých úkolů dostanou do části programu, kterou nenapsali, neměli by se bát si ji upravit pro své potřeby, případně zrefaktorovat, pokud to uznají za vhodné.
- Rovněž při code review by se neměl brát zřetel na to, kdo aktuální řádek kódu napsal a programátorovi by se mohly vracet merge requesty i když část kódu, kvůli které se vrátí není psána jím. Programátor se tak lépe seznámí s codebasem a naučí se průběžně zlepšovat kvalitu softwaru a provádět malé refaktory či zlepšení pokaždé, když vidí, že to je třeba. Rovněž by programátor neměl kvůli každému drobnému bugu vytvářet merge request a zbytečně tak spomalovat vývoj byrokracií, ale pokud se tím nijak razantně neprodlouží práce na aktuálním tasku, je vhodné tento fix implementovat v rámci aktuální fičury.
Testování
- Každý to zná. Po dlouhých hodinách psaní konečně vytvořil fičuru, kterou konečně mohl nasadit na produkci a ejhle! Někde tam daleko vzadu, v neznámé stránce se bez jakéhokoliv jasného přičinění něco rozbilo. A kdo ví jak dlouho to tam bylo, kdo ví, kolik zákazníků to vidělo! Tohle by se nestalo, pokud bychom měli správně napsané testy.
- To co jsme si my oblíbili je testovací framework
Cypress
, jelikož ten imituje chování skutečného uživatele. Framework před vámi otevře prohlížeč který ovládá a kliká na jednotlivá tlačítka tak, jako by byl uživatelem. Ve velkých aplikacích opravdu oceníte tento nástroj. Nemusíte tak před deployem na produkci testovat každou fičuru. Jen necháte projít testy automaticky a pokud neselžou, můžete to tam s klidným srdcem poslat. Toto samozřejmě platí pouze za předpokladu, že máte váš software testy dostatečně pokrytý. Je třeba psát testy opravdu detailní a když zjistíte, že se přes ně dostal nějaký bug, opět je rozšířit. - Vhodné je rovněž nastavit si vaši continuous integration pipelinu tak, aby se na produkci nedeploylo nic, co testy neprošlo. V testech se používejte speciální identifikátor na jednotlivých html elementech
<a href="/" data-test-id="test-anchor"> test </a>
, namísto jejich targetování pomocí class a idček, která se mohou s vývojem změnit.
Definovaný styl projektu
- Pracoval jsem na projektech, které byly psány ve stejném programovacím jazyku, se stejným frameworkem, knihovnami a téměř stejnými eslint pravidly a i přesto vypadaly naprosto odlišně. To, co projektu dává v konečném důsledku jeho ráz je člověk nebo skupina lidí, kteří dělají společná rozhodnutí o podobě projektu a posléze je vynucují v code review. To oni by měli zaručit, že budou dodrženy standardy jak použitého stacku, tak arbitrárně stanovená pravidla specifická pro projekt. To umožní snadnější orientaci v projektu, který nebude napsán deseti různými styly, ale bude konzistentní. Je blbost, aby se v code review řešil každý řádek nového kódu, měly by ovšem být nastaveny určité mantinely, které dají projektu ráz, aniž by byly pro programátory příliš utlačující.
Funkcionální programováni
- Ačkoliv je starší než objektově orientovaný přístup, do módy vstoupilo teprve nedávno. Výhoda funkcionálního programování tkví v tom, že programátor minimalizuje kód, ve kterém se může nacházet bug tím, že ho odsune do skupiny dobře otestovaných tzv "pure" funkcí.
- Pure funkce je funkce, která se stejnými vstupy vygeneruje vždy stený výstup a nikdy nepracuje s ničím jiným, než se svými vstupními proměnnými, tedy nekomunikuje s žádnými globálními proměnnými a své argumenty rovněž neměnní, nýbrž si je "naklonuje" a ven vyhodí úplně nová data, uložená na jiném místě v paměti.
- Tím že vytvoříme takovýto dobře otestovaný kus kódu zmenšíme prostor, ve kterém hledáme bug.
- Další výhoda funkcionálního programování tkví v tom, že funkcionální kód je většinou daleko lépe čitelný a více deklarativní.
Pár základních pravidel funkcionálního programování:
- Nepoužíváme classy.
- Nemutujeme původní hodnoty, ale vytváříme si nové.
- Píšeme pure funkce, jednoduché pro pochopení, které nemutují stav žádných proměnných, pouze na daný vstup poskytnou vždy daný výstup.
- Místo for, while a do...while, používáme funkcionální metody - .map, .filter, .find, .findIndex, .reduce a funkce z knihovny
Ramda
.
Blíže k funkcionálnímu kódu vás může posunout i dobře nastavený eslint, např. pravidla no-param-reassign
, array-callback-return
, prefer-const
, no-const-assign
a především pak použití funkcionální knihovny, např. Ramda
.
DRY a YAGNI
- Don't repeat yourself (DRY) pravidlo říká, že pokud píšu funkcionalitu a vím že se bude používat na více místech, abstrahuji ji do funkce či metody, kterou z těch míst jen zavolám. Pokud pak funkcionalitu potřebuju upravit, můžu to udělat na jednom místě. Toto pravidlo občas vyžaduje koordinaci s ostatními developery, kteří mohou již psát, či už mít podobnou funkcionalitu napsanou. Je třeba ptát se kolegů, zda danou funkcionalitu např. již neimplementovali.
- Je ale třeba si dávat velký pozor a nikdy toto pravidlo nebrat do extrému. Existuje totiž další pravidlo, které se nazívá YAGNI - You ain't gonna need it. Které říká, že není třeba vytvářet přehnané abstrakce a komplikovaný kód, u kterého není jisté, jestli se znovu použije, nebo by napsání takového kódu trvalo tolik času, že to neopodstatňuje jeho následné výhody. Taky je třeba brát v podtaz, že někdy v budoucnu se může kód na různých komponentech rozejít a tak je vhodné kód zreplikovat, pokud toto předpokládáme, ale např. okomentovat, aby ostatní developeři věděli proč se tak stalo.
Component wrappery
- Moderní webové aplikace používají tzv. reusable komponenty. Ty do sebe balí template, styl i skript a jsou tak ideálním způsobem, pokud svůj kód nechceme opakovat.
- Příkladem může být např. komponent tlačítka (template), které je specificky nastylované (styl) a při kliknutí na něj vedle svého popisku zobrazí malé točící se kolečko, čekající na dokončení nějaké operace (skript).
- Tyto komponenty často chodí v celých knihovnách komponentů, které pokrávají nejčastější use casy. Nám se osvědčila Bootstrap-vue.
- Je častým pravidlem ve velkých projektech, že komponenty z těchto knihoven jsou obalovány do další vrstvy aby mohla být případně rozšířena jejich funkcionalita. Ve Vue se toto dá udělat následovně:
<b-modal v-bind="{ ...$props, ...$attrs }" v-on="$listeners"> <slot v-for="(_, name) in $slots" :name="name" :slot="name" /> </b-modal>
- Takto napsaný komponent na sebe převezme veškeré attributy a propy, které jeho parentovi zadáte a rovněž bude správně reagovat na @click a zpřístupní vám všechny své sloty. Můžete jej ovšem z jednoho místa přenastavit pro potřeby projekty.
Snadno debugovatelný kód
- Struktura vašeho projektu by měla vypadat tak, aby bylo snadné se v něm zorientovat a nalézat bugy. To dá např. usnadnit tak, že soubory představující url adresy vašeho projektu budou strukturované stejně jako by jste k nim z daných url adres přistupovali.
- Zachovávejte správné jmenné konvence, aby bylo jasné co vaše funkce dělají a vrací, metody mutují, proměnné uchovávají. Nespokojte se s názvy typu "id", vždy specifikujte, např. "userId". Nikdy nepoužívejte čísla ve funkcích, místo toho specifikujte v čem je funkce jiná.
- Použijte funkcionální knihovnu, např. ramda
- zachovejte jasně daný data flow, od odroje dat (api), přes store, po renderovací logiku (stránky, komponenty)