Kaip nesunaikinti projekto dėl blogos kodavimo praktikos?
Bartosz Slysz
Software Engineer
Daugelis karjerą pradedančių programuotojų mano, kad kintamųjų, funkcijų, failų ir kitų komponentų pavadinimų suteikimo tema nėra labai svarbi. Dėl to jų projektavimo logika dažnai būna teisinga - algoritmai veikia greitai ir duoda norimą efektą, o gali būti vos įskaitomi. Šiame straipsnyje trumpai pabandysiu apibūdinti, kuo turėtume vadovautis suteikdami pavadinimus įvairiems kodo elementams ir kaip nepulti iš vieno kraštutinumo į kitą.
Kodėl apleidus pavadinimų kūrimo etapą, projekto kūrimas užsitęs (kai kuriais atvejais - labai)?
Tarkime, kad jūs ir jūsų komanda perima kodas iš kitų programuotojų. Svetainė projektas jūs paveldite buvo sukurtas be jokios meilės - jis veikė puikiai, tačiau kiekvienas jo elementas galėjo būti parašytas daug geriau.
Kai kalbama apie architektūrą, kodo paveldėjimo atveju ji beveik visada sukelia neapykantą ir pyktį iš ją gavusių programuotojų. Kartais taip nutinka dėl mirštančių (arba išnykusių) technologijų naudojimo, kartais dėl neteisingo mąstymo apie programą kūrimo pradžioje, o kartais tiesiog dėl atsakingo programuotojo žinių stokos.
Bet kokiu atveju, projektui tęsiantis, galima pasiekti, kad programuotojai ims pykti ant architektūrų ir technologijų. Juk kiekvienai programai po kurio laiko reikia perrašyti kai kurias dalis arba tiesiog pakeisti tam tikras dalis - tai natūralu. Tačiau problema, nuo kurios programuotojams pasišiaušia plaukai, yra sunkumai skaitant ir suprantant paveldėtą kodą.
Ypač kraštutiniais atvejais, kai kintamieji įvardijami atskiromis, nieko nereiškiančiomis raidėmis, o funkcijos yra staigus kūrybiškumo proveržis, niekaip nederantis su likusia programos dalimi, jūsų programuotojai gali išprotėti. Tokiu atveju bet kokiai kodo analizei, kuri galėtų vykti greitai ir efektyviai, jei būtų teisingai įvardyti pavadinimai, reikia papildomai analizuoti algoritmus, atsakingus už funkcijos rezultato gavimą, pvz. O tokia analizė, nors ir nepastebima - sugaištama labai daug laiko.
Įgyvendinant naujas funkcijas skirtingose programos dalyse, tenka patirti košmarą analizuojant kodą, o po kurio laiko tenka grįžti prie kodo ir vėl jį analizuoti, nes jo ketinimai neaiškūs, o ankstesnis laikas, praleistas bandant suprasti jo veikimą, buvo iššvaistytas veltui, nes nebeprisimenate, kokia buvo jo paskirtis.
Taip mus įtraukia netvarkos tornadas, kuris viešpatauja programoje ir pamažu pasiglemžia kiekvieną jos kūrimo dalyvį. Programuotojai nekenčia projekto, projektų vadovai nemėgsta aiškinti, kodėl jo kūrimo laikas ima nuolat ilgėti, o klientas praranda pasitikėjimą ir pyksta, nes niekas nevyksta pagal planą.
Kaip to išvengti?
Pripažinkime - kai kurių dalykų negalima praleisti. Jei projekto pradžioje pasirinkome tam tikras technologijas, turime žinoti, kad laikui bėgant jos arba nustos būti palaikomos, arba vis mažiau programuotojų mokės naudotis prieš kelerius metus sukurtomis technologijomis, kurios pamažu sensta. Kai kurių bibliotekų atnaujinimai reikalauja daugiau ar mažiau susijusių kodo pakeitimų, kurie dažnai sukelia priklausomybių sūkurį, kuriame galima dar labiau įstrigti.
Kita vertus, tai nėra toks juodas scenarijus; žinoma, technologijos sensta, tačiau veiksnys, kuris neabejotinai lėtina su jomis susijusių projektų kūrimo laiką, dažniausiai yra negražus kodas. Ir, žinoma, čia reikia paminėti Roberto C. Martino knygą - tai biblija programuotojams, kurioje autorius pateikia daug gerosios praktikos ir principų, kurių reikėtų laikytis norint sukurti tobulumo siekiantį kodą.
Pagrindinė kintamųjų pavadinimų reikšmė - aiškiai ir paprastai perteikti jų paskirtį. Tai skamba gana paprastai, tačiau kartais daugelis žmonių to nepaiso arba ignoruoja. Geras pavadinimas nurodys, ką konkrečiai kintamasis turi saugoti arba ką funkcija turi daryti - jis negali būti pavadintas pernelyg bendrai, bet, kita vertus, negali tapti ilgu šlamštu, kurio vien perskaitymas sukelia nemažą iššūkį smegenims. Po kurio laiko su gerais kokybės kodas, patiriame panardinimo efektą, kai galime pasąmoningai organizuoti vardų suteikimą ir perdavimą duomenys prie funkcijos taip, kad visa tai nepalieka iliuzijų, koks ketinimas ją skatina ir kokio rezultato tikimasi ją iškvietus.
Kitas dalykas, kurį galima rasti JavaScript, be kita ko, bandoma pernelyg optimizuoti kodą, todėl daugeliu atvejų jis tampa neįskaitomas. Normalu, kad kai kuriems algoritmams reikia ypatingos priežiūros, o tai dažnai atspindi tai, kad kodo paskirtis gali būti šiek tiek painesnė. Vis dėlto atvejai, kai reikia pernelyg didelio optimizavimo, yra itin reti, arba bent jau tie, kai mūsų kodas yra nešvarus. Svarbu nepamiršti, kad daugelis su kalba susijusių optimizavimų vyksta šiek tiek žemesniame abstrakcijos lygmenyje; pavyzdžiui, V8 variklis, esant pakankamam iteracijų skaičiui, gali gerokai pagreitinti ciklus. Reikėtų pabrėžti tai, kad gyvename XXI amžiuje ir nerašome programų, skirtų "Apollo 13" misijai. Turime kur kas daugiau manevro laisvės išteklių tema - jie yra tam, kad būtų naudojami (pageidautina, kad protingai :>).
Kartais kodo skaidymas į dalis tikrai daug duoda. Kai operacijos sudaro grandinę, kurios tikslas - atlikti veiksmus, atsakingus už konkretų duomenų pakeitimą, - lengva pasiklysti. Todėl paprastai, užuot viską atlikę vienoje grandinėje, atskiras kodo dalis, atsakingas už konkretų dalyką, galite suskaidyti į atskirus elementus. Tai ne tik leis aiškiai suprasti atskirų operacijų paskirtį, bet ir leis išbandyti už vieną dalyką atsakingus kodo fragmentus, kuriuos galima lengvai pakartotinai naudoti.
Keletas praktinių pavyzdžių
Manau, kad tiksliausiai kai kuriuos iš aukščiau pateiktų teiginių bus galima perteikti parodant, kaip jie veikia praktiškai - šioje pastraipoje pabandysiu apibūdinti kai kurias blogas kodo praktikas, kurias daugiau ar mažiau galima paversti geromis. Nurodysiu, kas kai kuriais momentais trikdo kodo skaitomumą ir kaip to išvengti.
Vienos raidės kintamųjų prakeiksmas
Baisi praktika, kuri, deja, gana paplitusi net universitetuose, yra kintamųjų pavadinimai iš vienos raidės. Sunku nesutikti, kad kartais tai gana patogus sprendimas - išvengiame nereikalingo galvojimo, kaip nustatyti kintamojo paskirtį, ir užuot naudoję kelis ar daugiau simbolių jam pavadinti, naudojame tik vieną raidę, pvz., i, j, k.
Paradoksalu, bet kai kurie šių kintamųjų apibrėžimai yra aprūpinti daug ilgesniu komentaru, iš kurio galima spręsti, ką autorius turėjo omenyje.
Geras pavyzdys būtų iteracija per dvimatį masyvą, kuriame stulpelių ir eilučių sankirtoje yra atitinkamos reikšmės.
const array = [[0, 1, 2], [3, 4, 5], [6, 7, 8]];
// gana blogai
for (let i = 0; i < array[i]; i++) {
for (let j = 0; j < array[i][j]; j++) {
// čia yra turinys, bet kiekvieną kartą, kai naudojamos i ir j, turiu grįžti atgal ir analizuoti, kam jos naudojamos
}
}
// vis dar blogai, bet juokinga
leiskite i; // eilutė
let j; // stulpelis
for (i = 0; i < array[i]; i++) {
for (j = 0; j < array[i][j]; j++) {
// čia yra turinys, bet kiekvieną kartą, kai naudojamos i ir j, turiu grįžti ir patikrinti komentarus, kam jos naudojamos
}
}
// daug geriau
const rowCount = array.length;
for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
const row = array[rowIndex];
const columnCount = row.length;
for (let columnIndex = 0; columnIndex < columnCount; columnIndex++) {
const column = row[columnIndex];
// ar kam nors kyla abejonių, kas yra kas?
}
}
Klastingas per didelis optimizavimas
Vieną gražią dieną susidūriau su labai sudėtingu kodu, kurį parašė programinės įrangos inžinierius. Šis inžinierius išsiaiškino, kad naudotojo leidimus siųsti kaip konkrečius veiksmus nurodančias eilutes galima labai optimizuoti naudojant kelias bitų lygio gudrybes.
Tikriausiai toks sprendimas būtų tinkamas, jei tikslas būtų "Commodore 64", tačiau šio kodo tikslas buvo paprastas žiniatinklio svetainė programa, parašyta JS. Atėjo laikas įveikti šią keistenybę: Tarkime, kad naudotojas visoje sistemoje turi tik keturias turinio keitimo parinktis: kurti, skaityti, atnaujinti, ištrinti. Visiškai natūralu, kad šias teises siunčiame JSON forma kaip objekto su būsenomis raktus arba kaip masyvą.
Tačiau mūsų sumanus inžinierius pastebėjo, kad skaičius keturi yra stebuklinga dvejetainio pateikimo reikšmė, ir tai išsprendė taip:
Visą galimybių lentelę sudaro 16 eilučių, aš išvardijau tik 4, kad perteikčiau šių leidimų kūrimo idėją. Leidimų skaitymas vyksta taip:
Tai, ką matote pirmiau, nėra "WebAssembly" kodas. Nenoriu, kad mane neteisingai suprastumėte - toks optimizavimas yra įprastas dalykas sistemose, kuriose tam tikri dalykai turi užimti labai mažai laiko arba atminties (arba ir vieno, ir kito). Kita vertus, žiniatinklio programos tikrai nėra ta vieta, kur toks perteklinis optimizavimas yra visiškai prasmingas. Nenoriu apibendrinti, bet priekinės dalies kūrėjų darbe sudėtingesnės operacijos, pasiekiančios bitų abstrakcijos lygį, atliekamos retai.
Jis paprasčiausiai neįskaitomas, o programuotojas, galintis atlikti tokio kodo analizę, tikrai susimąstys, kokių nematomų privalumų turi šis sprendimas ir kas gali būti sugadinta, kai kūrimo komanda nori jį perrašyti į priimtinesnį sprendimą.
Dar daugiau - įtariu, kad siunčiant leidimus kaip paprastą objektą programuotojas galėtų perskaityti ketinimą per 1-2 sekundes, o viso šio dalyko analizė nuo pat pradžių užtruks bent kelias minutes. Projekte bus keli programuotojai, kiekvienam iš jų teks susidurti su šia kodo dalimi - jie turės ją analizuoti kelis kartus, nes po kurio laiko pamirš, kokia magija ten vyksta. Ar verta išsaugoti tuos kelis baitus? Mano nuomone, ne.
Skaldyk ir valdyk
Interneto svetainių kūrimas sparčiai auga ir nėra jokių požymių, kad greitai kas nors pasikeis. Tenka pripažinti, kad pastaruoju metu gerokai padidėjo priekinės dalies kūrėjų atsakomybė - jie perėmė logikos dalį, atsakingą už duomenų pateikimą naudotojo sąsajoje.
Kartais ši logika yra paprasta, o objektai, kuriuos pateikia API turi paprastą ir lengvai skaitomą struktūrą. Tačiau kartais, norint juos pritaikyti skirtingose puslapio vietose, reikia atlikti įvairius atvaizdavimo, rūšiavimo ir kitus veiksmus. Ir tai yra ta vieta, kur galime lengvai pakliūti į pelkę.
Daug kartų esu save pagavęs, kai mano atliekamų operacijų duomenys buvo beveik neįskaitomi. Nepaisant teisingo masyvų metodų naudojimo ir tinkamo kintamųjų pavadinimų suteikimo, operacijų grandinės kai kuriose vietose beveik prarado kontekstą to, ką norėjau pasiekti. Be to, kai kurias iš šių operacijų kartais reikėdavo naudoti kitur, o kartais jos būdavo pakankamai globalios arba sudėtingos, kad reikėtų rašyti testus.
Žinau, žinau - tai nėra triviali kodo dalis, kuri lengvai iliustruoja tai, ką noriu pasakyti. Taip pat žinau, kad šių dviejų pavyzdžių skaičiavimo sudėtingumas šiek tiek skiriasi, o 99% atvejų mums dėl to jaudintis nereikia. Skirtumas tarp algoritmų yra paprastas, nes abu jie parengia vietovių ir įrenginių savininkų žemėlapį.
Pirmasis šį žemėlapį parengia du kartus, o antrasis - tik vieną kartą. Ir paprasčiausias pavyzdys, kuris rodo mus kad antrasis algoritmas yra labiau perkeliamas, slypi tame, kad mums reikia pakeisti šio žemėlapio kūrimo logiką pirmajam algoritmui ir, pavyzdžiui, padaryti tam tikrų vietų išbraukimą ar kitus keistus dalykus, vadinamus verslo logika. Antrojo algoritmo atveju keičiame tik žemėlapio gavimo būdą, o visi kiti vėlesnėse eilutėse vykstantys duomenų pakeitimai lieka nepakitę. Pirmojo algoritmo atveju turime koreguoti kiekvieną bandymą gauti žemėlapį.
Tai tik pavyzdys - praktikoje yra daugybė tokių atvejų, kai reikia transformuoti arba pertvarkyti tam tikrą duomenų modelį visoje programoje.
Geriausias būdas išvengti įvairių verslo pokyčių - parengti visuotinius įrankius, kurie leistų gana paprastai išgauti dominančią informaciją. Net ir tų 2-3 milisekundžių, kurias galime prarasti deoptimizacijos sąskaita, kaina.
Santrauka
Programuotojo profesija yra tokia pati kaip ir bet kuri kita - kasdien mokomės naujų dalykų ir dažnai darome daug klaidų. Svarbiausia iš šių klaidų pasimokyti, tapti geresniu savo profesijos specialistu ir ateityje tų klaidų nebekartoti. Negalima tikėti mitu, kad mūsų atliekamas darbas visada bus nepriekaištingas. Tačiau galite, remdamiesi kitų patirtimi, atitinkamai sumažinti trūkumų skaičių.
Tikiuosi, kad perskaitę šį straipsnį išvengsite bent kai kurių bloga kodavimo praktika kuriuos patyriau savo darbe. Jei kiltų klausimų dėl geriausios kodo praktikos, galite kreiptis į The Codest įgula pasitarti dėl savo abejonių.