A szoftverben a "komplex" nem objektív szó. Mindig fel kell tenni: kinek komplex? A felhasználónak, az API-fogyasztónak, a karbantartónak, vagy a jövőbeli fejlesztőnek, aki fél év múlva először nyitja meg a repót?
Két fajtával élünk együtt:
- Lényegi (essential): a domain hozza be. Adózási szabályok, üzleti logika, időzónák kezelése - nem tüntetheted el anélkül, hogy a lényeget is kidobnád. Itt a feladat: kerítsd körbe, legyen jól kijelölt helye és szűk interfésze.
- Véletlen (accidental): amit mi termelünk. Előre gyártott absztrakciók, overengineered architektúra, "hátha kell" rétegek, fölöslegesen behúzott library-k. Ezt egyértelműen vissza kell vágni.
A baj ott kezdődik, amikor a véletlen réteg eltakarja a lényegit, és már azt sem látod, mi az, ami valóban szükséges.
A komplexitás ellen a leghatékonyabb szó a "nem".
- Nem vezetünk be még egy réteget csak azért, mert "elegáns".
- Nem szórjuk szét a monolitot mikroszolgáltatásokra, amíg nem rajzolódtak ki a természetes vágási síkok.
- Nem generalizálunk előre, kitalált igényekre.
Karrier-szempontból a "igen" jobban mutat a CV-ben, az biztos. A kód viszont a "nem"-ért lesz hálás (és te is, amikor dolgoznod kell vele).
A projekt elején a kód olyan, mint a víz: minden formát felvesz. Ilyenkor a korai absztrakció csak akadály. Hagyd, hogy a rendszer alakot öltsön, és később vágj: amikor a határok maguktól kirajzolódnak. Jó jel a szűk, tiszta API, kevés függvény, kevés adatfolyam. Belül lehet zaj, kívül legyen béke.
(Túlgondoló kollégáknál működik: kérj holnapra demót. A valóság visszarántja az elméletet.)
A prototípus fázis után jöhetnek a tesztek, fókuszban az integrációs darabokkal:
- Unit: jó bemelegítés, de változásnál törékeny, néha inkább akadály.
- E2E: nem könnyű karbantartani, futtatása is költséges, így csak kritikus eseteket fedjen le.
- Integráció: a legjobb megtérülésű.
Egy kivétel van, amikor tényleg "első a teszt", amikor bugot javítasz.
A "nagy refaktor" ritkán hősies, viszont sokszor pofára esés. Lépj kicsiket, közben maradjon érintetlen a felhasználói élmény.
A jó API nem gondolkoztat. Adj egy triviális réteget a gyakori esetekre, és egy testreszabhatóbbat a ritka, bonyolult helyzetekre. Készítsd az API-t arra a dologra, ami csinálja (locality of behavior), ne szétszórt "helper" csomagokba. Ne kelljen a fejlesztőnek egy kurzust elvégezni, hogy egy sort()-ot meghívhasson.
A DRY jó elv, amíg nem szül túl okos megoldásokat. Néha két, világosan olvasható, majdnem egyforma 8 sor jobb, mint egy absztrakció, amit senki nem ért. A Separation of Concerns is addig hasznos, amíg nem kényszerít három fájlt és két mappát átböngészni, hogy egy gomb mit csinál. Tedd oda a kódot, ahol a viselkedés történik.
A legnehezebb feladat a jó határ. Ezt ne tetézd hálózattal: késleltetés, részleges hibák, eventual consistency. Monolittal kezdj, és ott vágj, ahol a valóság már kijelölte a határt.
A "korai optimalizálás" tényleg visszaüt. Profilozás nélkül ne nyúlj semmihez. A lassúság gyakran I/O vagy hálózat, nem az egymásba ágyazott for-ok. A Big-O szép, de a trace beszél.
Logolj bőven: fontos ágak, korrelációs ID, dinamikus logszint, akár felhasználóra szűkítve. Felhőben ez életmentő. Igen, beállítani macerás, de később ez spórol neked napokat.
Szenior felelősség kimondani: "ez így túl bonyolult, nem értem". Ezzel legitimmé teszed a legegyszerűbb működő megoldást, és leveszed a nyomást a többiekről. A komplexitás fele propaganda: ha nem félünk bénának látszani, összezsugorodik.
- A lényegi komplexitás szükséges, de kerítsd be, vékonyan kapcsolódj hozzá.
- Mondj "nem"-et, alkalmazz 80/20-at, későn absztrahálj.
- Integrációs tesztekkel őrizd a vágási pontokat, kicsiben refaktorálj.
- Mérj, mielőtt optimalizálsz; logolj és profilozz mielőtt káromkodsz.
Ha mindennek helyet adsz, a komplexitásnak is lesz egy - és nem a fejekben fog lakni.