Dirbtinio intelekto programavimo įrankiai skatina tikėti, kad svarbiausia tapo gerai aprašyti norimą rezultatą, o pačiu kodu gali pasirūpinti modelis. Pagal vadinamąjį specs-to-code principą žmogus parengia specifikaciją, AI ją paverčia programa, o prireikus pakeitimų redaguojama ne sistema, bet pradinis aprašas.
Šis modelis patrauklus, nes žada programavimą beveik be tiesioginio darbo su kodu. Tačiau pakartotinis specifikacijos perleidimas per generatorių gali duoti vis prastesnį rezultatą. Viena funkcija pataisoma, bet atsiranda naujų priklausomybių, dubliuojamos atsakomybės, auga failų skaičius, o bendra sistemos struktūra tampa vis sunkiau suprantama.
AI nepašalina senųjų programinės įrangos problemų. Jis leidžia jas sukurti greičiau ir didesniu mastu. Dėl to architektūra, testavimas, reikalavimų analizė ir aiškios modulių ribos tampa ne mažiau, o dar labiau svarbios.
Blogas kodas AI eroje tampa dar brangesnis
Johnas Ousterhoutas knygoje A Philosophy of Software Design sudėtingumą apibrėžia kaip sistemos struktūros savybes, dėl kurių programinę įrangą sunku suprasti ir keisti. Pagal šį kriterijų geras kodas nėra vien gražiai suformatuotas ar trumpas. Jis leidžia atlikti pakeitimus nesukeliant netikėtų gedimų kitose sistemos dalyse.
AI ypač gerai veikia aiškiai suprojektuotoje kodo bazėje. Kai moduliai turi suprantamas atsakomybes, o sąsajos yra stabilios, modelis gali greičiau rasti reikiamą vietą, atlikti pakeitimą ir patikrinti rezultatą. Chaotiškoje sistemoje jam tenka sekti daugybę smulkių priklausomybių, todėl didėja tikimybė, kad bus pakeista ne ta vieta arba sukurtas dar vienas nereikalingas sprendimo sluoksnis.
Knygoje The Pragmatic Programmer programinės įrangos irimas apibūdinamas kaip entropija. Kiekvienas izoliuotai vertinamas pakeitimas gali šiek tiek pabloginti visą sistemą, net jeigu konkreti užduotis formaliai įvykdyta. AI generuojamas kodas šią tendenciją sustiprina, kai vertinamas tik artimiausias rezultatas, o ne ilgalaikė projekto struktūra.
Teiginys, kad kodas tapo pigus, todėl tik iš dalies teisingas. Sugeneruoti eilutes iš tiesų galima greitai. Tačiau blogai suprojektuoto kodo priežiūra, taisymas ir plėtra lieka brangūs. Kuo daugiau pakeitimų sukuriama, tuo didesnė netvarkingos architektūros kaina.
Pirma kliūtis – neaišku, ką iš tikrųjų reikia sukurti
AI dažnai pateikia techniškai veikiantį, bet vartotojo lūkesčių neatitinkantį rezultatą. Tai nebūtinai reiškia, kad modelis netinkamai įvykdė aiškią užduotį. Dažnai pati užduotis dar nėra pakankamai aiški.
Frederickas P. Brooksas projektavimo procesą siejo su bendra kūrėjų galvoje egzistuojančia koncepcija. Ji apima ne tik funkcijų sąrašą, bet ir supratimą, kam produktas skirtas, kokias problemas sprendžia, kokios išimtys svarbios ir kodėl pasirinktas būtent toks veikimo principas.
Šios bendros koncepcijos nepakanka pakeisti vien ilgu specifikacijos failu. Dalis sprendimų paaiškėja tik dialogo metu, kai viena pasirinkimo šaka atveria kitus klausimus. Pavyzdžiui, sprendimas leisti vartotojui redaguoti įrašą iš karto iškelia prieigos teisių, versijų istorijos, patvirtinimo ir klaidų atkūrimo klausimus.
Naudingas metodas – prieš rengiant įgyvendinimo planą paprašyti AI nuosekliai apklausti projekto autorių. Klausimai turėtų apimti funkcijas, išimtis, vartotojų vaidmenis, duomenų būsenas ir techninius apribojimus. Tik pasiekus bendrą supratimą verta formuoti produkto reikalavimų dokumentą ar konkrečias programavimo užduotis.
Toks procesas gali užtrukti ilgiau nei iš karto pateiktas nurodymas „sukurk funkciją“, tačiau sumažina riziką greitai įgyvendinti netinkamą sprendimą.
Bendra kalba sumažina nesusipratimus
Kita dažna problema – pernelyg ilgi AI atsakymai ir terminų neatitikimas. Modelis, programuotojas ir konkrečios srities ekspertas tą pačią sąvoką gali suprasti skirtingai. Vienas žodis verslo procese gali reikšti klientą, techniniame modelyje – paskyrą, o duomenų bazėje – kelias atskiras lenteles.
Sritimi grįstas projektavimas, dar vadinamas Domain-Driven Design, siūlo kurti bendrą visų dalyvių kalbą. Joje tiksliai apibrėžiamos svarbiausios sąvokos, jų ryšiai ir vartojimo taisyklės. Tie patys terminai turi būti naudojami pokalbiuose, dokumentacijoje ir pačiame kode.
AI projekte šią kalbą galima saugoti atskirame faile. Jame aprašomi domeno objektai, veiksmai, būsenos ir terminai, kurių reikėtų vengti. Modelis tuomet turi aiškesnį žodyną ir rečiau kuria naujus pavadinimus tam, kas sistemoje jau egzistuoja.
Bendras žodynas naudingas ne vien atsakymų glaustumui. Jis padeda suderinti planą su įgyvendinimu. Jeigu reikalavimuose, modulių pavadinimuose ir testuose vartojamos tos pačios sąvokos, lengviau patikrinti, ar sukurtas kodas iš tiesų atitinka numatytą veikimą.
AI reikia trumpų grįžtamojo ryšio ciklų
Net teisingai supratęs užduotį AI gali sukurti neveikiantį kodą. Didžiausia rizika atsiranda tada, kai modelis vienu kartu pakeičia daug failų ir tik darbo pabaigoje pradeda tikrinti, ar projektas kompiliuojamas ir testai veikia.
The Pragmatic Programmer šią klaidą lygina su važiavimu greičiau, nei leidžia automobilio žibintų apšviestas kelias. Programavimo greitį turėtų riboti tai, kaip greitai galima gauti patikimą grįžtamąjį ryšį.
AI darbui tokį ryšį suteikia:
statinis tipų tikrinimas, kuris greitai parodo nesuderinamas reikšmes ir neteisingai naudojamas sąsajas;
automatizuoti testai, tikrinantys, ar pakeitimas nesugadino svarbaus elgesio;
kodo analizės įrankiai, aptinkantys dalį struktūrinių ir stiliaus problemų;
prieiga prie naršyklės ar veikiančios programos, leidžianti patikrinti vartotojo sąsają ir realų veikimą;
maži pakeitimų žingsniai, po kurių iš karto atliekama patikra.
Vien turėti šiuos įrankius nepakanka. AI turi būti aiškiai nukreiptas jais naudotis nuolat, o ne tik įgyvendinimo pabaigoje.
TDD veikia kaip darbo drausmė
Testais grįstas programavimas, arba TDD, verčia darbą skaidyti į mažus ciklus. Pirmiausia sukuriamas testas, apibrėžiantis pageidaujamą elgesį. Tuomet parašomas minimalus kodas, kad testas būtų sėkmingas, o galiausiai struktūra sutvarkoma nepakeičiant rezultato.
AI tai naudinga todėl, kad kiekvienas žingsnis turi aiškią ribą ir patikrinamą rezultatą. Modelis negali lengvai pasimesti dideliame nepatikrinto kodo kiekyje, nes kiekviena funkcija turi būti pagrįsta konkrečiu testu.
Tačiau testavimas nėra mechaninis procesas. Reikia nuspręsti, kokio dydžio sistemos dalį tikrinti, ką imituoti, kokį elgesį laikyti esminiu ir kiek testui leisti priklausyti nuo vidinio įgyvendinimo.
Prastai suprojektuota sistema sunkiai testuojama, nes vienas veiksmas priklauso nuo daugybės mažų komponentų. Todėl TDD kokybė tiesiogiai priklauso nuo architektūros. Geros modulių ribos leidžia testuoti elgesį per aiškią sąsają, neįtraukiant visos programos į kiekvieną patikrą.
Gilūs moduliai vietoje daugybės smulkių failų
AI lengvai kuria daug mažų funkcijų, klasių ir failų. Iš pirmo žvilgsnio toks kodas atrodo tvarkingas, nes kiekvienas elementas nedidelis. Tačiau sistema gali tapti sudėtingesnė, jeigu norint suprasti vieną operaciją tenka pereiti per ilgą tarpusavyje susietų komponentų grandinę.
Ousterhoutas skiria gilius ir seklius modulius. Gilus modulis slepia daug funkcionalumo už paprastos sąsajos. Naudotojui nebūtina suprasti viso vidinio veikimo – pakanka žinoti, ką modulis priima ir kokį rezultatą grąžina.
Seklus modulis turi mažai funkcionalumo, bet sudėtingą arba dažnai naudojamą sąsają. Daugybė tokių modulių sukuria didelį navigacijos krūvį tiek žmogui, tiek AI. Norint pakeisti vieną elgesį, tenka suprasti daug tarpusavyje priklausomų dalių.
Testuojama architektūra dažniausiai remiasi aiškiomis ribomis. Susijęs funkcionalumas sujungiamas į vieną modulį, o išorėje paliekama nedidelė ir stabili sąsaja. Testai tikrina šią sąsają, todėl vidinį įgyvendinimą galima keisti saugiau.
Toks principas nereiškia, kad visa programa turi būti sudėta į kelis milžiniškus failus. Modulio gylį lemia ne eilučių skaičius, o santykis tarp jo suteikiamų galimybių ir sąsajos sudėtingumo.
Projektuoti sąsają, deleguoti įgyvendinimą
AI gali sukurti daugiau kodo, nei programuotojas pajėgia kruopščiai peržiūrėti. Bandymas suprasti kiekvieną sugeneruotą eilutę gali sukelti dar didesnį protinį nuovargį nei tradicinis programavimas.
Aiškiai atskirti moduliai leidžia dalį sistemos vertinti kaip kontroliuojamas pilkąsias dėžes. Programuotojas projektuoja jų paskirtį, sąsajas ir testuojamą elgesį, o vidinį įgyvendinimą didesne dalimi patiki AI.
Šis metodas netinka vienodai visoms sritims. Finansiniai skaičiavimai, saugumo mechanizmai, prieigos kontrolė ir kiti kritiniai komponentai reikalauja nuodugnesnės žmogaus peržiūros. Mažesnės rizikos dalyse svarbiausia gali būti išorinė sutartis ir patikimi testai.
Kuo paprastesnė sąsaja, tuo lengviau patikrinti, ar modulis elgiasi tinkamai. Tai sumažina informacijos kiekį, kurį žmogus turi nuolat laikyti galvoje, ir leidžia koncentruotis į sistemos struktūrą, o ne kiekvieną techninę detalę.
Architektūra turi būti prižiūrima kiekvieną dieną
Kodo bazės dizainas nėra vien pradinis projekto etapas. Kiekviena nauja funkcija keičia modulių atsakomybes, sąsajas ir priklausomybes. Jeigu šie pokyčiai nevertinami sąmoningai, struktūra palaipsniui prastėja.
Kentas Beckas ragino kasdien investuoti į sistemos dizainą. AI programavime tai reiškia, kad planuose turėtų būti aprašoma ne tik tai, kokia funkcija atsiras, bet ir kuriame modulyje ji gyvens, kokią sąsają pakeis ir kokias priklausomybes sukurs.
Prieš įgyvendinant pakeitimą verta atsakyti į kelis klausimus:
kuris esamas modulis yra atsakingas už šį elgesį;
ar nauja funkcija neprideda antros tos pačios atsakomybės vietos;
kaip pasikeis vieša modulio sąsaja;
kokiais testais bus patikrintas naujas elgesys;
ar pakeitimui būtina kurti naują abstrakciją.
Specifikacijos generavimas be šių klausimų skatina rūpintis tik funkciniu rezultatu. Architektūra tada tampa atsitiktine visų ankstesnių AI sprendimų suma.
Programuotojo vaidmuo keičiasi, bet nepraranda vertės
AI galima vertinti kaip greitą taktinį programuotoją, gebantį atlikti konkrečius pakeitimus, tyrinėti projektą ir generuoti įgyvendinimo variantus. Tačiau jam vis dar reikia strateginio vadovavimo.
Žmogus turi nuspręsti, kokia sistema kuriama, kur eina jos ribos, kurie kompromisai priimtini ir kokia techninė skola gali būti toleruojama. Tam reikalingi tie patys įgūdžiai, kurie buvo svarbūs iki generatyvinio AI: reikalavimų analizė, architektūra, domeno modeliavimas, testavimas ir gebėjimas vertinti ilgalaikes pakeitimų pasekmes.
AI nesumažina programavimo pagrindų svarbos. Jis padidina atotrūkį tarp gerai ir prastai suprojektuotų sistemų. Tvarkingoje kodo bazėje modelis gali tapti dideliu našumo stiprintuvu. Netvarkingoje jis greitai pagamina dar daugiau kodo, kurį vėliau sunku suprasti ir saugiai keisti.
Didžiausia programuotojo vertė AI eroje slypi ne gebėjime pačiam įvesti kiekvieną eilutę. Ji slypi gebėjime sukurti aplinką, kurioje ir žmogus, ir AI gali nuosekliai priimti gerus techninius sprendimus.