štvrtok 12. mája 2016

Princípy dobrého designu

Dobrý design (čohokoľvek) je pojem, ktorý podľa môjho názoru súvisí jednak s pojmom "krása", ale zároveň je spojený s efektivitou technickej realizácie (zachováva "dobré" vlastnosti z technického pohľadu). Design (bez straty na všeobecnosti), po slovensky "návrh", je takým "rozhraním" alebo interfejsom medzi tým, čo treba, a tým, čo je komfortné a príjemné.

Krása je pojem, ktorý ide skôr do umenia, fantázie, bláznovstva, odlišnosti; je to niečo, čím sú posadnutí vizionári, rojkovia a umelci. Na druhej strane technická efektivita je spojená s minimom (orientovaná na presne definovanú funkcionalitu), výkonnosťou, škálovateľnosťou a akousi "čistotou". A dobrý design je spojenie týchto dvoch vecí dohromady.

Dobrý design nemusí byť úplne minimalistický, ale musí spĺňať nasledujúce body:
  • Rieši presne definované problémy (nič viac a nič menej) - ("čo" riešime),
  • Je krásny, v individuálnom zmysle ("ako" riešime).
Cestu k dobrému designu by som rád prirovnal k ceste aplikovaním zaujímavej fantázie a dobrých rozhodnutí, ktoré odzačiatku až do konca v rámci nej robíme.

Vytváranie a hodnotenie sú dve rozdielne veci

Spojenie požiadaviek spomenutých v predchádzajúcom odstavci je dosť pochybné. A to už len z nasledujúcich dôvodov:

  • "Presne" definovaný problém je vtedy, ak neexistuje viac možností jeho pochopenia 
  • Ďalej, ako už bolo povedané, "krásno" je veľmi individuálne.

Aj keď daný problém máme definovaný, často býva, že na jeho technickú realizáciu môže existovať viac riešení, alebo aj žiadne riešenie. "Krásota" designu je teda snáď daná výberom toho najvhodnejšieho riešenia pre daný problém, avšak intuitívne cítim, že aj niečoho viac.

Keď hovorím o "výbere najvhodnejšieho riešenia", evokuje to dojem, že máme pred sebou (pre predstavu) papiere riešení a ide len o to ukázať prstom, "ktoré" berieme (na strojníckej priemyslovke sme to označovali ako "varianty riešenia" a museli sme ich vypracovať niekoľko, z ktorých sme si na základe našich ďalších úvah museli jeden variant vybrať).

Avšak vypracovanie jednotlivých variantov nie je úplne to isté, ako keď už nejaké máme a hodnotíme ich. Vypracovanie vyžaduje kreativitu (fantáziu), motiváciu a akýsi občasný proces hodnotenia rozpracovaného výtvoru (či už pocitový alebo na základe vedomostí), keď sa na to priebežne pozeráme. Mohli by sme ten občasný proces chápať ako občasné "spojenie s realitou", kde kontrolujeme, či ideme "dobrým smerom" (spĺňa to zatiaľ požiadavky? Vyhovuje to "pravidlám"?)

Teda aj kreatívny proces má akési striedajúce sa fázy. Pustíme "rádio fantázie", ktorá predstavuje akési náhodné fluidum rôzne strelených nápadov v ktorých sa rôzne pohybujeme. Tento "pohyb" ďalej rôzne ovládame - či už obmedzíme alebo nasmerujeme, akoby sme otáčali volantom idúceho auta. To je podľa mňa to intuitívne a tajomné "priebežné kontrolovanie". Určite je iného druhu, než vyhodnocovanie hotových variantov riešenia. A myslím si, že je to ten najdôležitejší prvok kreatívneho procesu, pretože vedie cestou rozhodnutí, ktoré v rámci designu robíme.

Každopádne, schopnosť dobre "priebežne kontrolovať"  je úzko spätá so skúsenosťami, znalosťou pravidiel, princípov a tiež odborných vedomostí. Ide to teda skôr do toho chladného logického, analytického a nekreatívneho, zatiaľ čo to "kreatívne" ide skôr do emočného, pocitového. Teda striedajú sa ľavá a pravá hemisféra mozgu, kde kreatívna časť odpovedá skôr tej pravej, a analytická skôr tej ľavej.

Ako vyzerá dobrý design?

Hodnotenie skutočnosti, či ide o dobrý design je podľa mňa čisto empirická záležitosť. Pocit krásy pretrvá prvý pohľad len vtedy, ak vec obstojí aj v použiteľnosti. A použiteľnosť musí pretrvať (prirodzene) relatívne dlhodobo. Podľa mojich skúseností dobrý design vyzerá nejak takto:
  • vytvára dojem spojenia do seba zapadajúcich, osobitých, jasne odlíšiteľných prvkov
  • preferuje jednoduchosť, prehľadnosť, pochopenie
  • opakujúce sa mechanizmy explicitne identifikuje a zjednocuje do samostatných celkov
Keďže som programátor, tak dosť už rečí vo všeobecnosti. Zúžime trošku ten pojem na problémy v programovaní.

Inverzia - "výzor" ako cieľ

Dnešná doba sa z hľadiska programovacích trendov začala trochu viac hýbať. Každým rokom pribúda čoraz viac technológie a možností, v ktorých sa programátor stráca. Včera bolo "in" OOP, dnes je to funkcionálne programovanie, prípadne rôzne pokusy spojiť obe paradigmy. Myslím si však, že hľadanie dobrého designu neskončí nikdy, je jedno v akej paradigme sa pohybujeme. Je mi tiež jasné, že "there is no silver bullet in software development", ako už popísal Fred Brooks.

Aj napriek tomu však možno stojí za úvahu skúsiť nájsť akési viac všeobecné princípy (výsledky Googlu sú naozaj bohaté aj na túto tému), ktoré by reflektovali výzor designu popísaný v predošlej kapitolke.

V skratke zopakujem - dobrý design, ako spojenie krásy a akejsi "technickej dokonalosti", v zmysle troch spomenutých bodov sa dá popísať už existujúcimi princípmi v doméne programovania. Tieto princípy považujem ako dostatočne všeobecné, ktorých dodržiavanie zlepšuje design celkovo. Háčik je v tom, že podstata každého z nich je viac či menej vágne definovaná a spolieha sa na skúsenosti a schopnosti človeka - designéra.

Riešenie komplexného problému nás môže "odlákať" od myšlienok na dobrý design, pretože proces jeho zlepšovania je v podstate ďalším problémom, a človek nevie robiť niekoľko veci naraz. Preto som zástancom akejsi "priebežnej kontroly" (o ktorej som hovoril už vyššie), ktorá bude viac menej rutinná. Jej podstatou by mali byť práve takéto všeobecné princípy, ktoré si znovu-pripomíname ako určitú "mantru". Ich "automatické" aplikovanie nás tak priveľmi neodláka od riešenia pôvodného problému, to je môj dojem.

Taktiež, aplikovanie určitej "šablóny" nemusí byť klišé, v programovaní to vedie k dobre rozpoznateľným patternom (vzorom), ktoré sú esenciálne pri zlepšovaní designu a párovom programovaní. (poznámka: jednou z takých "šablón" je aj code style, ktorý si väčšinou firmy ako-tak definujú a je to dobrá vec).

Tak poďme konečne k tým slávnym princípom:
  • YAGNI (You Aren't Gonna Need It)
  • KISS - (Keep It Simple Stupid)
  • DRY - (Don't Repeat Yourself)

YAGNI

Tento princíp je podľa mňa ten najdôležitejší a myslím si aj najlepšie pochopiteľný (a najmenej dodržiavaný - žeby slabá vôľa?). Radí nám, že nemáme vytvárať nové funkcie v programe, keď ich práve teraz nepotrebujeme. Dodržanie tohto princípu:
  • Drží softvér vždy iba minimálne rozpracovaný. A to čo je rozpracované, je vždy jasne definované, niečo čo treba teraz.
  • Núti odkladať rozhodnutia na neskôr. Je veľmi dôležité nechať si otvorené možnosti do maximálnej doby aká je možná (požiadavky sa môžu (a budú) meniť) (Lean Software Development)
  • Drží nás pri zemi, design riadený požiadavkami a nie naopak (typu "to sa bude hodiť keď budeme v budúcnosti robiť XYZ!"). V praxi to bohužiaľ často vyzerá tak, že pridanie novej technológie je oveľa jednoduchšie než možnosť sa jej zbaviť. Takže pozor na to...
  • Udržuje nízky technický dlh (ehm.. skôr bráni jeho zvýšeniu)
Nie je to presne to, čo potrebujeme aby sme vedeli odhadnúť kedy bude práca hotová?

KISS

Tento princíp je bohužiaľ najviac vágny zo všetkých troch. Ak by sme sa ho mali striktne držať, tak to bude dosť závisieť na tom, kto si čo predstaví pod pojmom "jednoduché". Nedá sa to veľmi zovšeobecniť, a dodržanie tohto princípu vyžaduje buď znalosť ďalších X princípov ako riešiť - zjednodušiť určité veci v konkrétnych prípadoch, alebo designér musí mať skúsenosti a taký ten "cit", kedy sa ešte pýtať "dá sa to aj jednoduchšie?" a kedy s tým už prestať.

Čo si o designe myslia slávnejší ľudia než som ja?
  • Einstein si myslí, že: "Keep it simple, as simple as possible, but not simpler" (narážka na použitie princípu Occam's razor, ktorý radí vybrať si takú hypotézu s pomedzi niekoľkých, rovnako pravdepodobných, ktorá vyžaduje najmenej predpokladov)
  • C.A.R. Hoare zas (trochu podpichovačne): "There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies. And the other way is to make it so complicated that there are no obvious deficiencies."
Radšej by sme mohli urobiť skôr výčet toho, čo nie je jednoduchý design:
  • Over-engineering (needless complexity)
  • Nízka kohézia (kód je neusporiadaný, zodpovednosti sú "porozhadzované" a nejasné - kód nemá "myšlienku")
  • Tesný coupling (príliš veľa závislostí na konkrétnych implementáciách, opak princípu SDP (Stable Dependencies Principle))
Jednoduchosť je relatívny pojem, spätý s kontextom, alebo pohľadom na vec. Musíme vždy vedieť, aký je cieľ, požiadavka. A s tým súvisia aj kompromisy, ktoré často musíme vykonať. Napríklad, ak je cieľ "best performance ever", tak prehľadnosť a "čistota" často musia spraviť kompromis, a naopak.

DRY

Skoro ma láka povedať, že toto je jeden z princípov patriacich do Zenu. Hľadať v kóde repetitívnosť sa totiž dá občas prirovnať k riešeniu Zenového koanu, alebo ide o činnosť, keď meditujeme podľa Vipassany. Pomenúvame a identifikujeme. A zažívame AHA-efekty.

Hlavným nástrojom, ktorý nám pomáha v tejto činnosti je abstrakcia. Vo všeobecnosti je abstrakcia pojem vlastne priamo určený na eliminovanie opakujúceho sa (či zložitého) kódu. A málokto si to uvedomuje.

Už len jednoduchý for cyklus je abstrakciou (ktorú snáď poznáte všetci); keby neexistovala, mali by sme veľa if-ov za sebou, podľa toho, aké maximálne N by bolo. Samozrejme v rámci hĺbky "rozjímania" nad koanom DRY sa dá ísť až do výšin teórie kategórií, a exotických zigohistomorfických prepromorfizmov, ale väčšinou nemusíme :)

Hlavný princíp je "zgrupovať" súvisiace kódy, potom v rámci nich zgrupovať zdieľané funkcie a nahrádzať ich jedinou, alebo aj celú vec refaktorovať. Často (v rámci OOP) pomôže aj aplikovanie nejakého vhodného patternu, či dvoch.

Ako na nový design


Aj návrh návrhu má svoje YAGNI, KISS a DRY. Treba začať vždy tou najlacnejšou a najdôležitejšou vecou - API. Použitie. Má to niekoľko výhod:
  • Ujasníte si problém, prípadne identifikujete viac problémov.
  • Zmena API je v tomto bode neuveriteľne lacná
  • Krásne funguje párové programovanie
  • Hotové API je niečo ako "zadanie", "špecifikácia", na ktoré sme ako školáci boli zvyknutí a ktoré v Agile nedostaneme len tak (iba pracnou komunikáciou - user story).
  • Často nás to privedie k zamysleniu, aha-efektom a objaveniu nových súvislostí   
Tvorba API sa môže striedať s návrhom algoritmov či dátovou reprezentáciou. Nič nie je úplne oddelené, všetko má svoje prieniky.

Návrh API sa mi najviac osvedčil, keď som postupoval intuitívne - v princípe jeho použitia. Nezačal som robiť interface, ale kód, ktorý niečo "robí". S neexistujúcim API. Nemusí ísť vôbec o TDD (aj keď to k tomu smeruje), pretože použitie často môže používať príliš nedomyslené veci a sústredenie sa na pravidlá TDD v tejto fáze by nás iba "dekontextovalo". Kód, ktorý som písal, bol pseudo-kód, kde som vyjadroval len to, čo práve robím a aby to bolo pochopiteľné (aj prípadnému sparring-partnerovi).

Ako už vieme, zmena kontextu je rušivý element, ktorý ak je častý, znižuje IQ. Takže na začiatok žiadne pravidlá (okrem YAGNI, KISS a DRY), máme (teda skoro) free svet a navrhujeme to ako nám to "príde" fajn. Keď nás už nič nenapadá, vrátime sa kritickým okom a skontrolujeme zvyšné princípy (kohézia, coupling, SOLID, atď.)

Toto všetko sa dá riešiť už v tejto prvotinnej fáze, pri návrhu API v notepade alebo na papier, kde je všetko zadarmo.

Keď bude nejaký nástrel API, pri implementácii už budeme odbremenení od mnohých rozhodnutí (zázračne sme okúsili blahodárne výsledky použitia YAGNI v predchádzajúcej fáze).

Záver

Keď sa na to celé pozerám, použil som dosť pojmov, takže dobrý design asi nebude úplne "trivka" :) Na druhej strane, cieľ je vcelku jednoduchý, až zenový:
Princípmi:
  • Riešiť jednu, iba tú dôležitú, myšlienku v čase
  • Písať, kontrolovať a upravovať kód dovtedy, kým tá myšlienka nebude biť do očí