Ez a cikk egy sorozat második része. Ha az elsőt is el szeretnéd olvasni, itt megteheted:
Feature branchek nélküli fejlesztés?!
➔ Nehéz lehet elfogadni, hogy – mivel manapság a Git a mainstream verziókövető rendszer, aminek legfőbb előnye a branchek könnyű létrehozása – nem használjuk a (feature) brancheket. Mintha visszafelé haladnánk az időben, és újra központosított verziókövetőt (pl. SVN-t) használnánk. Persze ez így azért nem igaz.
A Google pl. egyetlen monolitikus repositoryban tárolja az összes projektje forráskódját. Ily módon bármelyik fejlesztő hozzáfér a projekt legfrissebb működő kódjához. Trunk-based developmentet alkalmaznak, minden commit azonnal látható a 25.000, Google-nél dolgozó fejlesztő számára (2014-es adat). A monorepositoryba egy tipikus munkanapon 16.000 commit érkezik a fejlesztőktől (és további 24.000 automata rendszerektől).
Fontos tudatosítani, hogy milyen projektben és milyen munkakörnyezetben dolgozunk. A Gitet eredetileg a Linux kernel fejlesztéséhez találta fel Linus Torvalds, a Linux megalkotója. A Linux egy open source projekt, amit nagyon sokan fejlesztenek egyszerre, ami így egy elosztott, nemlineáris és aszinkron munkafolyamatot eredményez, amihez a Git és az általa ajánlott könnyű branchelés passzol a legjobban. Elosztott a munkafolyamat abban az értelemben, hogy a fejlesztők földrajzilag különböző helyeken dolgoznak egy időben; nemlineáris, mert nem egy meghatározott menetrendet követnek közösen végig, hanem csak részben koordinált módon dolgoznak; továbbá aszinkron, mert általában úgy tudnak dolgozni, hogy nem kell másra várni ahhoz, hogy dolgozhassanak egy-egy feladaton. (Egy feladat befejezésekor pull requestet küldenek a maintainereknek, ami egy szűk keresztmetszetet eredményez a folyamatban.)
Ezzel szemben nagyon sok üzleti környezetben a megvalósító csapat tagjai együtt haladnak előre. A tipikus működés szerint a csapatok full-time egy irodában dolgoznak, és egy meghatározott backlog alapján haladnak. Ez egy lineáris és egyben szinkronizált működést eredményez. Lineáris a működés, mert koordinált módon együttesen haladnak (sprintek), és szinkronizált, mert nagyon sokszor együtt, összedolgozva kell megoldani összetett feladatokat (pl. egy fejlesztőnek gyakran kell egyeztetnie a BA-val meg a tesztelővel vagy más fejlesztővel). Bonyolultabb üzleti problémák és architektúrák esetén fontos, hogy a szereplők egymástól kérdezhessenek, tanulhassanak; ez a munkafolyamat megkerülhetetlen része.
Üzleti környezetben távmunka esetén a csapatok szintén elosztottan működnek, lineárisan haladnak, de aszinkron működést választanak. Ez egy nagyon produktív munkavégzési mód lehet, mert a szakemberek egyedül képesek dolgozni, kiküszöbölik vagy legalábbis minimálisra csökkentik a másikra való várakozást a továbbhaladás érdekében. Ezt a módszert csak nagyon tudatos kommunikációval lehetséges fenntartani, és véleményem szerint nem is minden típusú projekt teszi ezt lehetővé (ld. komplex üzleti alkalmazások, ahol a fejlesztőnek is folyamatosan tanulnia kell a domaint).
A feature branchek használata aszinkron működés esetén elkerülhetetlen, szinkronizált működés esetén a trunk-based development (és a CI) javasolt.
A branchek használata más esetekben is hasznos lehet, pl. spike-ot készítünk (rövid ideig tartó, eldobható kódkísérlet), vagy release-enként hozunk létre egy release branchet (ami szintén nem túl hosszú életű). Tehát a Git mindenképpen hasznos és fontos, de normál fejlesztés esetén – ha CI-t szeretnénk – érdemes mellőzni a feature branchek használatát (vagy maximum 1 napra korlátozni az életciklusát).
És a code review-k?
➔ Gyakran felmerülő kérdés, hogy ha nincsenek branchek, és így nem küldünk pull requesteket sem, akkor hogyan tudjuk megvalósítani a code review-t. Úgy tűnik elsőre, hogy ez a kontrollmechanizmus megvalósíthatatlan trunk-based development esetén. Valóban, ha nagy a csapattagok közötti különbség (pl. junior-senior, vagy újonnan érkezett egy kolléga a projektbe, és nincs még megfelelő projekttapasztalata), akkor frusztráló lehet az új tag számára, hogy úgy kell commitálnia a masterre, hogy még bizonytalan magában, és a csapattagokat is nagyon zavarhatja, ha az új kolléga elront valamit. Itt valójában egy bizalmi problémáról van szó, tehát akként kell kezelni és megoldani. Az új kolléga „beintegrálása” a feladat, ezért meg kell találni a módját annak, hogy ez minél hamarabb megtörténjen. Akár az is egy járható út lehet, hogy ez esetben kivételt teszünk, és az illető változtatásai pull requesteken kerülnek be a masterbe, de ezen lehetőség szerint minél hamarabb tovább kell lépni.
A code review pedig megvalósítható egy megfelelő eszközzel trunk-based development esetén is (pl. Jetbrains Upsource terméke). Ebben az esetben a commitban használt issue-azonosítók segítenek összekapcsolni az egy sztorihoz tartozó commitokat, amik egyben review-zhatók is, miután a fejlesztő becommitálta a masterre a változásokat.
A kód összeintegrálása tehát bizonyos szempontból bizalmi kérdés is: „Be merem-e commitálni, amit csináltam (bízom-e magamban eléggé, és bízom-e a többiekben is, hogy nem fognak hibáztatni, ha elrontok valamit)?”
A pszichológiai biztonság megteremtése ezért különösen fontos a munkahelyen. (A hibák miatt nem hibáztatunk másokat, a hibák valójában lehetőséget adnak a tanulásra.)
A feature-ök fejlesztése
➔ A trunkon való dolgozás nagy fegyelmet kíván meg a fejlesztőktől. Mielőtt lefejlesztünk egy feature-t, végig kell gondolnunk, hogy milyen apró lépésekben tudunk elérni a célunkhoz, miközben folyamatosan működő kódot írunk (napi commitok mellett folyamatosan zöld build).
A nagy feature-öket, amiknek az elkészítése akár több hetet vagy hónapot is igénybe vehet, is lehet trunk-based developmenttel fejleszteni. Megfelelő feature toggle-ökkel elrejthetők a még el nem készült funkciók a felhasználók elől. Természetesen ezeket minél hamarabb meg kell szüntetni, ugyanis nagymértékben megnövelhetik a komplexitást a kódban.
A CI jelentette kihívás
➔ A continuous integration megvalósításának legkritikusabb és legnehezebb része a megfelelő automata tesztek létrehozása és karbantartása.
Ha a tesztek nem elég gyorsak, akkor egy idő után túllépjük a 10 perces tesztfuttatási időkorlátot. Amikor viszont ezzel a problémával szembesülünk, már nagyon nehéz azon változtatni. Egyrészt azért, mert elképzelhető, hogy át kellene dolgozni a teszteket (a PO-k általában nem mennek bele, mert nem tapasztalható hozzáadott érték a termékben ennek hatására.) Másrészt pedig maguk a választott technológiák is nagyban befolyásolják a tesztek futási sebességét. A „pehelysúlyú” technológiák alkalmazása pl. elősegíti a tesztek gyors elindulását, ami jelentősen csökkentheti a futási időt. Ha pl. van 100 integrációs tesztünk, és mindegyik 2-3 mp-ig tart, amíg elindul, akkor csak e tesztek elindulása önmagában 3-5 percet tesz ki. Ha rosszul választunk technológiákat, félő, hogy azokat már nem lesz lehetőségünk később lecserélni másikra.
A tesztfutási idő jelentős csökkentése úgy is megoldható, ha az alkalmazásunkat szolgáltatásalapú architektúrában (service-based architecture, nem SOA!) valósítjuk meg, vagy esetleg modularizált kódbázisban, azonban ekkor nagyon ügyelnünk kell arra, hogy ennek megtervezése ne upfront történjen meg, hanem organikus fejlődés eredményeképpen alakuljon ki.
Olyan tesztkészletre van szükségünk, amely megfelelő lefedettséget nyújt, hogy a CI eredményeképpen olyan biztonsági hálót kapjunk (folyamatos zöld jelzés eredményeképpen), amely hatékonyan képes jelezni, hogy a kódba nem vezettünk be újabb „jelentős” hibát.
A nagy lefedettség elérése egyáltalán nem triviális feladat. Sok tesztet kell írnunk a jó lefedettséghez, azonban ha sok a tesztünk, és valamilyen változás történik a produkciós kódban, jelentős időt vihet el a tesztek karbantartása. A tesztjeinknek ellenállónak kell lenniük, ellenkező esetben abban a helyzetben találhatjuk magunkat, hogy a fejlesztők a teszteket nem akarják vagy tudják karbantartani, így oda lesz a CI-nk is.
Mindez pedig oda vezet, hogy hatékony és átfogó (nem csak a fejlesztőket érintő) tesztelési stratégiát dolgozunk ki, aminek sarokköve a CI.
CI csak a megfelelő műszaki kiválóság megléte esetén valósulhat meg.
Konklúzió
➔ A hagyományos üzleti környezetben megvalósuló projektek esetén az üzleti szereplők és a fejlesztők nagy fokú együttműködése szükséges, hogy a domaintudás átadódhasson, és kialakuljon egy mindent átható közös megértés (shared understanding), ami a bizalom kialakulásának alapfeltétele. A világban jelenleg zajló, sok agilis transzformációs folyamat egyik legnagyobb kihívása is az üzleti oldal és a fejlesztői csapat „összeboronálása”.
Ha nincs rendben a CI, akkor a csapat nem tud fenntartható módon (azaz egyenletes sebesség mellett, tervezhetően) minőségi szoftvert szállítani, ami aláássa a bizalmat az üzleti oldal és a fejlesztői csapat között. Ezért a CI-re érdemes úgy tekinteni, mint egy kritikus komponensre, ami a bizalom és így az agilis működés alapját biztosítja.
CI-re nem minden esetben van szükség. Alapvetésként azt lehet mondani, hogy minél komplexebb egy projekt, annál nagyobb szükség van a CI-re. Rövid távú projektmunkáknál általában CI nélkül is lehetséges a szállítás, hosszú távú szoftverfejlesztési projektek esetén viszont megkerülhetetlen.
Ez a cikk egy sorozat második része. Ha az elsőt is el szeretnéd olvasni, itt megteheted: