Az előző cikkben azt írtam, hogy a verziószám olyan, mint a fűrészpor: nem ez a munka célja, mégis elárulja, szólt-e mostanában a fűrész, és ezzel rögtön felvet egy fontos kérdést is: ha nem a fejből felmondott technológiai részletek mutatják meg az architekt értékét, akkor mi?
A rövid válasz meglepően ellentmondásos, mert az architekt akkor végzi jól a dolgát, ha idővel egyre kevesebb döntéshez van szükség architektre.
A címet Martin Fowlertől kölcsönöztem: a Who Needs an Architect? utolsó része a szoftverarchitektúra felszámolásáról szól; a megfogalmazás ugyan hatásvadász, mégsem túlzás, hiszen Fowler szerint az architekt egyik legfontosabb feladata éppen az, hogy csökkentse a nehezen visszafordítható döntések számát.

Nem jósolni kell, hanem mozgásteret hagyni
Fowler az architektúrát azokkal a dolgokkal azonosítja, amelyeket a csapat nehezen változtathatónak érez, ebből pedig fontos következtetés adódik: az architekt feladata nem az, hogy minden ilyen döntést évekre előre hibátlanul eltaláljon, hanem az, hogy minél kevesebb döntés váljon idő előtt véglegessé.
Ez elsőre egyszerű rugalmassági programnak hangzik: tervezzünk mindent cserélhetőre, és később nem érhet meglepetés. Csakhogy minden új absztrakció, konfigurációs pont és kiterjesztési lehetőség növeli a rendszer összetettségét. Ha mindent könnyen változtathatóvá akarunk tenni, végül éppen a hozzáadott mechanizmusok miatt lesz nehéz bármihez hozzányúlni.
A valódi munka ezért a válogatás: el kell döntenünk, hol éri meg már most megfizetni a rugalmasság árát, hol lenne ugyanez puszta találgatás, és melyik döntést hagyhatjuk későbbre anélkül, hogy közben beszűkülne a mozgásterünk.
Melyik ajtón lehet visszajönni?
Jeff Bezos a 2015-ös Amazon-részvényesi levélben egyirányú és kétirányú ajtókról ír: míg az előbbin átlépve nehéz vagy lehetetlen visszafordulni, az utóbbinál kipróbálhatunk valamit, majd elfogadható áron visszatérhetünk.
Egy webes rendszerben kevesebb valódi egyirányú ajtó van, mint amennyit annak érzünk, de ilyen lehet egy széles körben használt publikus API-szerződés, egy más csapatok által fogyasztott eseményséma vagy az adatok üzleti jelentése, ahogyan a hitelesítési és tenantmodell is mélyen belenőhet a rendszer minden rétegébe.
A belső mappaszerkezet, a CSS-módszertan vagy egy állapotkezelő könyvtár viszont többnyire cserélhető, és bár a váltás lehet drága, hosszadalmas vagy kellemetlen, ettől még nem válik visszafordíthatatlanná; az architekt munkájának egyik legfontosabb része éppen az, hogy a két tulajdonságot ne keverjük össze, mert ami költséges, nem feltétlenül végleges.
Amikor az adatbázisséma hétköznapi kóddá válik
Fowler egy adatbázis-fejlesztési gyakorlatról szóló írásában bemutatja, hogyan tették folyamatosan változtathatóvá a sémát és az adatot, a következményt pedig úgy fogalmazta meg, hogy az adatbázisséma többé nem volt architekturális elem.
Nem maga a séma változott meg, hanem a csapat tanulta meg biztonságosan változtatni.
Ma ennek egyik bevett eszköze a parallel change, más néven expand–contract, amelynek során az új mező, oszlop vagy endpoint először a régi mellé kerül. Ezután átvezetjük rá az írókat és az olvasókat, szükség esetén visszatöltjük a korábbi adatokat, a régi megoldást pedig csak akkor távolítjuk el, amikor már nincs rá hivatkozás.
Így egyetlen nagy, feszült migráció helyett több kisebb lépést kapunk, amelyek külön-külön megfigyelhetők, kiadhatók és egy ideig vissza is fordíthatók, vagyis a korábban egyirányúnak tűnő ajtóból kétirányú ajtók sora lesz.

Egyetlen hely tudjon a külső szolgáltatóról
Hasonló mozgásteret ad, ha a nehezen cserélhető külső szolgáltatást saját, vékony határ mögé tesszük. Az üzleti kódnak például nem kell tudnia, hogy a fizetést éppen melyik szolgáltató kezeli:
export interface PaymentGateway {
charge(amountHuf: number, customerId: string): Promise<{ id: string }>;
refund(chargeId: string): Promise<void>;
}
export class StripeGateway implements PaymentGateway {
constructor(privateClient: StripeClient) {
this.client = privateClient;
}
private client: StripeClient;
async charge(amountHuf: number, customerId: string) {
return this.client.createCharge({ amountHuf, customerId });
}
async refund(chargeId: string) {
await this.client.createRefund(chargeId);
}
}
A cél nem az, hogy bármikor, néhány perc alatt lecserélhessük a Stripe-ot, hiszen a fizetési szolgáltatók fogalmai és képességei sosem illeszkednek tökéletesen egymáshoz. A határ inkább arra való, hogy az adott szolgáltató sajátosságai ne szivárogjanak szét az egész kódbázisban, így egy későbbi váltás továbbra is projekt marad ugyan, de nem válik régészeti feltárássá.
Az ilyen határok csak akkor maradnak épek, ha a rendszer ellenőrzi őket: egy dependency-cruiser szabály például megtilthatja, hogy az adapteren kívüli modul közvetlenül importálja a szolgáltató SDK-ját, így a dokumentált elvárásból automatikusan vizsgált tulajdonság lesz.
Ugyanez a gondolat jelenik meg a feature flageknél is, mert ha a telepítést elválasztjuk a funkció bekapcsolásától, a kiadás könnyebben visszafordíthatóvá válik, és probléma esetén nem feltétlenül kell az egész buildet visszagörgetni: elég lehet kikapcsolni az érintett útvonalat.
A túl korai absztrakció ára
Ezekből könnyű általános szabályt gyártani — minden külső függőség elé interfészt, minden döntés mögé kapcsolót —, és én is készítettem már olyan absztrakciót, amelynek hat éven át egyetlen implementációja volt: soha nem akartuk lecserélni, mégis minden olvasó megfizette az extra közvetettség árát.
Ez a YAGNI iskolapéldája, de a mozdulat mögött gyakran több van egyszerű túlbuzgóságnál, hiszen a bizonytalanság kellemetlen, egy új interfész pedig könnyen azt az érzést kelti, hogy felkészültünk a jövőre, miközben lehet, hogy csak drágábban halasztottuk el a döntést.
Néha becsületesebb kimondani, hogy valamit még nem tudunk, majd addig nyitva tartani a döntést, amíg ennek nincs komoly ára, és csak akkor építeni rugalmasságot, amikor már látszik, milyen irányban lesz rá szükség. Máskor viszont az eldobható architektúra a legolcsóbb megoldás, vagyis egy jól körülhatárolt első változat, amelynek későbbi újraírását eleve a tanulás árába számítjuk.
Kevesebb architektúra, több figyelem
Minden biztonságossá tett migráció, tisztán tartott rendszerhatár és visszafordítható kiadás után kevesebb olyan dolog marad, amelyet nehéz megváltoztatni, ebben az értelemben tehát valóban felszámolom az architektúrát.
Nem azért, mert nincs szükség tudatos tervezésre, hanem éppen azért, mert a figyelem véges. Ha a séma, a szolgáltató és a kiadási folyamat már nem követel rendkívüli óvatosságot, több idő jut azokra a döntésekre, amelyek valóban mélyen beépülnek a rendszerbe: a publikus szerződésekre, az adatok jelentésére és a biztonsági modellre.
Az előző cikkben azt írtam, hogy ne a verziószámot kérdezd, hanem azt, mit építettem mostanában, erre pedig most az a válaszom, hogy olyan rendszeren dolgozom, amelyben holnap olcsóbb lesz meggondolni magunkat, mint ma volt; ha jól csinálom, egyre kevesebb döntésnél kell majd az architekt címére hivatkozni.
Források
- Martin Fowler: Who Needs an Architect? — IEEE Software, 2003
- 2015 Letter to Shareholders — Amazon
- Pramod Sadalage, Martin Fowler: Evolutionary Database Design
- Danilo Sato: Parallel Change
- Pete Hodgson: Feature Toggles
- Martin Fowler: YAGNI, Sacrificial Architecture
- dependency-cruiser — Sander Verweij