Čitljiv kod - preduvjet za uspjeh

Kodiranje 26. tra. 2022

Developeri većinu svog vremena provode čitajući i održavajući postojeći kod. Pokušavaju razumjeti što on ustvari radi te kako ga poboljšati ili proširiti novim mogućnostima. Unatoč tome, često se razmišlja samo o performansama koda, ali ne i o čitljivosti i održivosti istog.

Kada gradimo softver koji bi trebao imati dug životni vijek, čitljivost bi trebala biti jednako bitna kao i performanse - čitljiv kod uvelike pomaže u održavanju, dodavanju novih funkcionalnosti, mijenjanju starih, itd.

Dodatni benefit se javlja i pri onboardingu novih developera - uspješan onboarding uvelike ovisi o čitljivosti postojećeg koda.

Kada radite na proizvodu koji se razvija dugi niz godina, sigurno ćete se morati uhvatiti u koštac s legacy kodom kojeg najvjerojatnije niste vi napisali. Kad taj kod ima na tisuće linija, a pritom nije čitljiv, bilo kakav zahvat postaje ogroman izvor frustracija.

U Mediatoolkitu razvoju pristupamo s idejom dugoročne održivosti; uz ostalo, konkretno, ulažemo dodatni napor u stvaranje čitljivog koda.

Da budemo potpuno iskreni, do prije par godina nismo pretjerano pazili na to i dogodilo nam se da smo imali velikih problema kod upgradeanja featurea, ali i kod onboardinga novih ljudi. No, naučili smo iz svojih grešaka.

Motiv ovog bloga je da drugi ne ponavljaju naše greške i na vrijeme krenu pisati čist kod. Evo nekoliko dobrih praksi kojih se u Mediatoolkitu držimo kako bismo osigurali čitljivost koda.

Čitljivost je bitna

Kao što smo rekli, developeri većinu svog vremena provedu nad postojećim kodom. Ako je neki dio sustava bio razvijan kroz period od više godina, skoro sigurno je da će značajno vrijeme biti utrošeno na dešifriranje što kod radi i kako ga izmijeniti da odgovara novim zahtjevima. Postavlja se pitanje - na što se fokusirati kod pisanja koda da bi bio čitljiv i dugoročno održiv?

Prema tome, prvi koraci su sljedeći:

  • Korištenje smislenog nazivlja
  • Poboljšanje funkcija
  • Davanje prednosti nepromjenjivom kodu (immutability)
  • Deklarativno ispred imperativnog programiranja
  • KISS princip
  • DRY princip
  • YAGNI princip

Korištenje smislenog nazivlja

Imenujemo svaki dio koda i zato je potrebno to učiniti kvalitetno. Smislena imena daju kontekst, objašnjavaju što se događa. Nazivi varijabli i metoda bi trebali objasniti namjeru iza produciranog koda - razlog zašto se nešto tu nalazi, što radi i kako se koristi. Uzmite si vremena za smisliti dobro nazivlje - u protivnom biste mogli potrošiti mnogo vremena samo da shvatite što ste željeli izraziti kroz besmislena imena.

Evo jednog primjera koda s besmislenim imenima:

Isti takav kod, ali sa smislenim nazivljem:

Nazivlje treba doći iz domene problema kad god je to moguće. Nije poželjno koristiti tehničke nazive ako postoji bolje ime u domeni.

Klase i objekti bi trebale imati imenicu u svojim imenima (customer, emailSender, htmlParser). Česta pogreška je da se koriste previše općenite imenice koje same po sebi ne daju previše značenja, pogotovo ne unutar domene problema (data, info, manager, processor, controller…).

Na primjer, objekt nazvan info je mnogo teže pročitati nego userInfo. Također, moguće je imati više infosa u bloku koda, tako da je lakše pratiti logiku kada su imena što specifičnija.

Metode bi trebalo nazivati tako da se koriste glagoli (postPayment, deletePage, insert). Dobra praksa je da se izbjegavaju kratice jer su najčešće razumljive samo pri inicijalnom razvoju, dok s vremenskim odmakom mogu zbunjivati.

Primjer lošeg naziva varijable, uz komentar koji objašnjava varijablu, je česta loša praksa:

Bolja alternativa je uvijek:

Dobra praksa je odabrati jedan izraz po konceptu i držati se toga. Na primjer, umjesto da se koriste fetch, retrieve ili get za jednake mehanike dohvata u projektu, treba odabrati jedan izraz kojeg ćete se držati. U protivnom, osoba koja čita kod najčešće se pita zašto su slične stvari različito nazvane i u čemu se razlikuje ponašanje takvih stvari, kad se već svjesno različito zovu.

Poboljšanje funkcija

Prva stvar koja se može napraviti jest da se funkcije smanje, idealno na manje od 15 linija koda. Manjim funkcijama smanjujemo kontekst koji moramo držati u glavi dok čitamo logičku cjelinu u kodu.

Indentacija koda također pomaže u držanju konteksta. Po mogućnosti bi se trebalo zadržati na 1-2 razine indentacije u funkciji, dok bi za više od toga trebalo imati dobro opravdanje. Ako je moguće, previše indentiran kod bi trebalo zamijeniti pozivom nove funkcije.

Primjer dobrog pojednostavljenja funkcije se može vidjeti na:

Da biste poboljšali čitljivost, funkcije bi trebale biti zadužene strogo za jednu stvar. Velike funkcije su često odgovorne za više stvari te bi ih trebalo podijeliti na više manjih funkcija. Funkcije koje zovu druge funkcije bi i dalje trebale biti zadužene samo za jednu stvar, samo na višem nivou apstrakcije.

Uvođenje više slojeva apstrakcije

Lakše je shvatiti veći broj funkcija višeg nivoa apstrakcije, nego manji broj funkcija niskog nivoa. Stoga je dobro uvoditi viši sloj apstrakcije kad god je to moguće.

Apstrakcije bi se trebale temeljiti na domenskom ponašanju; proizvoljan odabir apstrakcije često može pokvariti čitljivost koda.

Iduće pravilo za funkcije je da bi one trebale imati što je moguće manje argumenata; više od dva argumenta bi trebala biti iznimka, a ne pravilo. Mnogo argumenata otežava razumijevanje koda, a pogotovo u slučajevima kada se argumenti mijenjaju kroz funkciju i izmijenjeni koriste kao rezultati funkcije.

Generalno, funkcija bi trebala biti ili naredba koja obavlja neku akciju, ili upit koji vraća željene podatke pozivatelju, a nikad oboje. Imenovanjem bi trebalo biti jasno koja je uloga funkcije.

Funkcije bi samo iznimno trebale imati nuspojave (side effects), a ako postoje, moraju biti eksplicitne. Vrlo je bitno za lakšu čitljivost da su funkcije čiste, tj. ne mijenjaju stanje nakon poziva (stateless).

Korištenje svojstva nepromjenjivosti (immutability)

Nepromjenjivost je svojstvo koje nam garantira da se objekt / varijabla neće mijenjati nakon što je kreirana. Nepromjenjiv kod stvara kopije objekata koji imaju izmijenjeno stanje.

Primjer koda koji mijenja stanje:

Isti problem riješen s kodom koji ne mijenja stanje objekata:

Ovo svojstvo čini kod lakšim za pratiti. Znajući da su objekti nepromjenjivi, ne trebamo se brinuti hoće li ih neka funkcija promijeniti. Također, lakše je izvesti multi-threaded sustave - nepromjenjivi objekti su thread-safe i ne trebamo ih okruživati sinkronizacijskim blokovima.

Ponekad neće biti moguće postići jednostavnu logiku i čitljiv kod uz uvjet da je kod immutable - u takvim slučajevima je u redu pribjeći mutable kodu, ali mu treba minimizirati doseg.

Deklarativno ispred imperativnog programiranja

Imperativno programiranje govori kako bi stvari treble biti odrađene; korak po korak, uvjeti, petlje, itd. S druge strane, deklarativno programiranje kaže što treba napraviti, a ne kako; izgrađeno je povrh imperativnog načina.

Deklarativni stil je mnogo jasniji pri iskazivanju poslovne logike i namjere nekog koda. Kada nova osoba krene čitati neki kod, uvelike joj olakšava kad isprve vidi koja je namjera tog koda te samim tim omogućava brže zaranjanje u relevantan dio koda. S imperativnim načinom je česta situacija da se prvo treba pročitati cijeli kod da bi se tek onda shvatilo što taj kod radi.

Primjer iste metode napisane imperativno u usporedbi s deklarativnim načinom:

KISS princip

Programeri često znaju smisliti “genijalna” rješenja za neke probleme, koliko god takva rješenja povećavala kompleksnost koda. Razlozi mogu biti razni - od isprobavanja novih tehnika, hranjenja ega pa sve do prirode samog programera. No, jednostavnost bi trebala ići ispred svega - ako je ikako moguće, treba izbjegavati nepotrebne kompleksnosti. Sam engleski naziv jako dobro opisuje ideju: “Keep it simple stupid” (KISS)

DRY princip (don’t repeat yourself)

Dupliciranje koda je stvar koju u pravilu treba izbjegavati; trebamo težiti imati jedno mjesto na kojem se nekakvo znanje odvija. Ako imamo dupliciran blok koda po projektu, problem nastaje pri promjenama tih blokova - moramo se sjetiti gdje smo taj isti kod duplicirali i promjene napraviti na svim tim mjestima. Kopiranje blokova koda je u redu u trenucima brzog prototipiranja, ali u trenu kad taj kod krene prema nečem ozbiljnijem potrebno ga je počistiti od svih potencijalnih duplikata.

YAGNI princip (You ain’t gonna need it)

Ne bismo smjeli implementirati stvari koje bi nam “mogle” trebati u budućnosti, već jedino ono što nam sigurno treba. Preuranjena implementacija zatvara mogućnosti dizajniranja sustava na najbolji način - često proizvod na kraju bude dizajniran za stvari koje bi nam mogle trebati, a ne za ono za što nam stvarno treba.

Zaključak

Prateći gore navedene principe (uz još poneke), u Mediatoolkitu smo uspjeli zadržati većinu našeg koda i dizajna čitljivim, lako održivim i pogodnim za unaprjeđivanje. Također, olakšali smo iskustvo onboardinga novim kolegama tako što smo pretvorili doticaj s postojećim kodom u ugodno iskustvo.

Oznake

Mediatoolkit

SaaS tvrtka koja je razvila nagrađivani alat za praćenje medija i društveno slušanje koji prati relevantna online spominjanja robnih marki i tvrtki u stvarnom vremenu.

Tvoja prijava je uspješno sačuvana!
Odlično! Kako bi imao pristup cijelom sadržaju bloga potrebno je izvršiti proces plaćanja.
Tvoja prijava je uspješna!
Tvoj nalog je aktiviran, sada imaš pristup cijelom sadržaju bloga.