
Ne potrebujete popolnega načrta že na začetku. Potrebujete smer in voljo, da izboljšujete, dokler se stvari ne zbližajo.
To sem se naučil, ko sem isto aplikacijo zgradil dvanajstkrat, jo zrušil in zgradil znova, dokler me sistem ni naučil, kaj želi biti.
Vsak programski projekt se začne enako: nekdo nariše škatle na tablo in razglasi: »To je arhitektura.« Običajno je veliko prikimavanja. Včasih se pojavi še druga tabla.
Potem pa dejansko začnete graditi in resničnost se pojavi nepovabljena.
Škatle ne pasejo. Podatki tečejo v napačno smer. Storitev, za katero ste mislili, da bo preprosta, ima sedemnajst robnih primerov. Do tretjega tedna pišete kodo, ki se tiho opravičuje tabli.
Trda resnica je: polne oblike svojega sistema ne morete poznati, preden ne zgradite njegovih delov. Zahteve lažejo. Uporabniki vas presenetijo. Obseg razkrije stvari, ki jih noben diagram ni napovedal.
In to je v redu. Težava ni v tem, da je bil vaš načrt napačen. Težava je v tem, da prvotni načrt obravnavate kot svetinjo.
Če ste delali s porazdeljenimi sistemi, poznate pojem eventual consistency (sčasoma doseženo ujemanje): različna vozlišča v sistemu lahko v nekem trenutku vidijo podatke drugače, a ob dovolj časa in komunikacije se vsa zbližajo v isto stanje.
Z oblikovanjem vaše programske opreme je enako.
Začnete z nepopolnimi informacijami. Vaša arhitektura ima vrzeli. Vaše razumevanje področja je delno. Toda če ponavljate, in vsak krog naredi načrt malo bolj iskren, malo bolj usklajen z resničnostjo, se zbližate z nečim, kar res ustreza.
Cilj ni, da bi bilo takoj prav. Cilj je biti vedno manj zmoten.
Imenujmo to razvoj »Figure-It-Out-As-You-Go« (FIAYG), torej »ugotovi sproti«. Ni tako privlačno kot TDD ali DDD, a precej bolj iskreno glede tega, kako se programska oprema dejansko gradi.
Bistvo je: vsakič, ko se naučite kaj novega, iz hrošča, iz odziva uporabnikov, iz trka z mejo obsega, to znanje vgradite v arhitekturo. Ne popravite samo simptoma. Popravite sistem, da se simptom ne more ponoviti.
Almadar je deklarativni okvir, ki sheme avtomatov stanj prevede v celovite aplikacije. Ena sama datoteka .orb opisuje vaše entitete, vedenja in uporabniški vmesnik, prevajalnik pa ustvari delujoč frontend v Reactu, zaledje v Expressu in modele podatkovne baze.
Tak je zdaj. Takole smo prišli do tja.
Začelo se je preprosto. Imeli smo en projekt, stranko, ki je potrebovala poslovno aplikacijo z veliko operacijami CRUD. Napisali smo jo ročno, na običajen način: komponente React, poti Express, entitete TypeORM. Ponavljajoča se koda povsod.
Opazili smo, da vedno znova pišemo iste vzorce: tabelo za izpis zapisov, modalno okno za njihovo ustvarjanje, obrazec za urejanje in potrditev za brisanje. Kopiraj. Prilepi. Preimenuj. Ponovi.
Mora obstajati boljši način.
Zato smo poskusili: kaj če bi podatke in vedenje opisali v shemi JSON in iz nje ustvarili ponavljajočo se kodo?
Bilo je grobo. Shema je bila komaj strukturirana. Generator je bil nizovna predloga s klici fs.writeFileSync. A je delovalo. Stranka je bila zadovoljna. Oddajali smo hitreje.
Kar smo se naučili: teh 80 % vsake aplikacije, ki je ponavljajoča se koda, je mogoče opisati deklarativno. Tistih 20 %, ki je edinstvenih, pa je tisto, čemur dejansko želite posvetiti čas.
Druga stranka je imela drugačne zahteve. Naš generator je bil preveč tesno vezan na vzorce prve stranke. Povsod smo trdo zapisali predpostavke: poimenovalne konvencije, strukturo datotek, hierarhijo komponent.
Lahko bi ga zakrpali. Namesto tega smo se ustavili in vprašali: Kako bi izgledala splošna različica tega?
Tu se je arhitektura začela kristalizirati. Uvedli smo pojem Orbitals, samostojne funkcijske module, ki združujejo entiteto (obliko podatkov), lastnost (trait, vedenje kot končni avtomat) in stran (pot). Shemo smo ločili od generiranja kode.
Kar smo se naučili: v trenutku, ko imate drugi primer uporabe, se vaše abstrakcije razkrijejo. Če se ne posploši, ni bila abstrakcija. Bila je naključje.
Ko je število projektov raslo, je rasel tudi kaos. Vsak projekt stranke je imel svoj oblikovalski sistem, svoje različice komponent, svojo predstavo o tem, kako izgleda »gumb«. Vzdrževali smo deset različic iste komponente.
Zato smo vse preoblikovali v monorepozitorij z izrecnimi pravili:
@almadar/ui, skupne, z verzijami, nikoli podvojeneprojects/{name}/design-system/, razširjajo jedro in ga ne nadomeščajoorbital-rust/, svet, brez razprave ga ne spreminjamoprojects/{name}/app/, nikoli se je ne ureja neposrednoŠe pomembneje, dodali smo omejitve. Stroga pravila, ki jih ekipa zapiše v datoteko CLAUDE.md in jih mora upoštevati vsak razvijalec (človek ali umetna inteligenca):
.orb, ponovno prevedite, ustvarjene kode se nikoli ne dotikajteTe omejitve smo uveljavili z lastnim vtičnikom ESLint (@almadar/eslint). Ne neobvezna priporočila, ampak pravi errorji, ki blokirajo kavelj pred potrditvijo (pre-commit hook). Opozorila niso dovoljena. Nekatera pravila, ki smo jih zgradili:
| Pravilo | Kaj ujame |
|---|---|
no-raw-dom-elements | Prepove <div>, <span>, <button> in podobno. Namesto tega uporabite VStack, Typography, Button |
no-as-any | Prepove pretvorbe tipov as any. Popravite dejansko težavo s tipom |
no-import-generated | Prepove uvoz iz projects/*/app/. Ustvarjena koda ni API |
require-closed-circuit-props | Vsaka komponenta mora sprejemati className, isLoading, error, entity |
require-event-bus | Interakcije morajo uporabljati useEventBus(), ne golega onClick |
require-display-name | Vsaka komponenta mora nastaviti displayName za razhroščevanje |
require-stories | Vsaka nova komponenta mora priti z datoteko .stories.tsx |
event-naming-convention | Dogodki uporabniškega vmesnika morajo slediti vzorcu UI:EVENT_NAME |
template-no-hooks | Komponente predlog so čiste. Brez useState, brez useEffect |
template-no-iteration | Predloge ne ponavljajo zank. Iteracijo opravi organizem |
organism-no-callback-props | Organizmi komunicirajo prek vodila dogodkov, ne prek povratnih klicev v lastnostih |
Kavelj pred potrditvijo požene ESLint na vsaki pripravljeni datoteki .ts/.tsx. Če se sproži katero koli pravilo, je potrditev blokirana. Brez izjem. Vsako pravilo ima resnost 2 (error), ne opozorila. Opozorila so želje. Errorji so arhitektura.
Rezultat: nov razvijalec, človek ali umetna inteligenca, po naključju ne more kršiti oblikovalskih načel. Linter reče ne, še preden je zahtevek za združitev sploh odprt.
Omejitve zvenijo omejujoče. V resnici osvobajajo. Ko so pravila jasna, so odločitve hitrejše, neskladja so ujeta prej in novi člani ekipe lahko prispevajo, ne da bi kaj pokvarili.
Kar smo se naučili: monorepozitorij brez omejitev je le vljuden monolit. Omejitve so arhitektura, linting pa je način, kako jih naredite resnične.
Tu postane zanimivo.
Ko so se pojavili pomočniki za programiranje z umetno inteligenco, se je naša arhitektura, zasnovana na omejitvah, izkazala za natanko tisto, kar potrebujejo, da dobro delujejo. Veliki jezikovni model (LLM) z jasno shemo za urejanje, registrom vzorcev za preverjanje, linterjem, ki blokira slabe vzorce, in prevajalnikom, ki reče »ne« pokvarjenemu izhodu? To je produktiven agent umetne inteligence.
LLM, ki dela v prosti kodni zbirki brez pravil? To je zelo samozavesten agent, ki samozavestno stori napačno stvar.
To smo formalizirali: umetna inteligenca dobi omejitve v CLAUDE.md, pred dodajanjem komponent preveri register vzorcev, na vsaki spremenjeni datoteki požene ESLint in shemo preveri s prevajalnikom Orbital, preden razglasi, da je kaj končano. Prevajalnik je pregledovalec kode umetne inteligence, nikoli ni utrujen, nikoli ne spregleda primera in teče v manj kot 50 ms.
Arhitektura, na katero smo naleteli z iteracijo, se je izkazala za pravo arhitekturo za razvoj s pomočjo umetne inteligence. Tega nismo načrtovali. Zbližali smo se z njo.
Kar smo se naučili: umetna inteligenca ojača vsako strukturo (ali kaos), ki v vaši kodni zbirki že obstaja. Omejitve niso birokracija. So razlika med umetno inteligenco, ki pomaga, in umetno inteligenco, ki samozavestno kvari stvari.
Če ste začetnik, takole to uporabite, ne da bi okvir gradili iz nič:
Vaša prva različica naj naredi eno stvar. Ne stvari plus obravnave napak plus predpomnjenja plus sistema nastavitev. Samo stvar. Z oddajo nečesa majhnega se boste naučili več kot z načrtovanjem nečesa velikega.
Ko isto kodo napišete tretjič, vam kodna zbirka pove, da bi se nekaj rado abstrahiralo. Ne abstrahirajte ob prvem ponavljanju (prezgodaj). Ne prezrite tretjega (malomarnost). Drugič je znak, tretjič potrditev.
Ko najdete vzorec hrošča, na primer modalno okno, ki lahko obtiči odprto, ne popravite samo tega okna. Ugotovite, katero pravilo bi ta razred hroščev preprečilo v celoti. Zapišite ga. Uveljavite ga. Pri Almadarju smo »vsako modalno okno mora imeti prehod CANCEL« spremenili v pravilo za preverjanje v prevajalniku, »nikoli ne uporabljajte golih elementov HTML« pa v napako linterja. V vašem projektu je to lahko lastno pravilo ESLint, kavelj pred potrditvijo ali kontrolni seznam za pregled kode. Medij ni pomemben. Pomembno je, da je pravilo samodejno. Če si ga mora zapomniti človek, bo sčasoma pozabljeno.
Vsakič, ko opazite vzorec in ga posplošite, bi morali za isto stvar pisati manj kode. Če vaša kodna zbirka za vedno raste z enako hitrostjo, kopičite kompleksnost, ne upravljate je. Cilj je več funkcionalnosti na vrstico, ne več vrstic.
Stvari smo preimenovali. Izbrisali smo cele pakete. Oblikovalski sistem smo dvakrat premaknili. Vsakič je sistem postal čistejši in pravila jasnejša. Zasuk ni neuspeh. Je dokaz, da se je vaše razumevanje izboljšalo.
Po več letih in mnogih projektih strank smo se zbližali s tem:
Najboljša programska arhitektura ni tista, ki jo zasnujete, preden začnete. Je tista, ki nastane iz poslušanja, kaj vam sistem poskuša povedati, prek hroščev, prek ponavljanja, prek trenja, ki ga začutite, ko nekaj ni prav.
Ugotovite sproti. Zapišite, kar se naučite. Bodite vedno manj zmotni.
Ujemanje pride sčasoma. Oddajte vseeno.