Continuous integration (1. rész) – az agilis szoftverfejlesztés alapja

Szerző: | 2019. 06. 24. | Agilis transzformáció az IT-ban

Ez a cikk egy sorozat első része. Ha a másodikat is el szeretnéd olvasni, itt megteheted:

A continuous integration (CI) koncepciója nagyon egyszerű, mégis nagyon kevesen értik pontosan, mi is volna a lényege. Nemzetközi viszonylatban is sok félreértéssel találkozni. Márpedig a koncepció „lézerpontosságú” ismerete elengedhetetlen ahhoz, hogy megvalósulhasson az agilis szoftverfejlesztés:

  • CI nélkül nem lehetséges magas bizalmi szinten működő szervezetet működtetni, ami az agilis működés alapja.
  • CI nélkül nem lehetséges nagy sebességgel (fenntartható módon) szoftvert fejleszteni.
  • CI nélkül lehetetlen a continuous delivery (CD).

Ha nincs CI, nem beszélhetünk hosszú távon agilis szoftverfejlesztésről sem.

Mit jelent a continuous integration?

➔  Jez Humble, az ipari zsinórmértéket teremtő Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation könyv egyik szerzője, a CD-mozgalom egyik vezető szakértője is, továbbá az Accelerate: Building and Scaling High Performing Technology Organizations című könyv írója is. Ez utóbbi könyvében kutatási eredményekkel igazolja, hogy mik azok a gyakorlatok, amelyeket a kiemelkedően teljesítő (high performing) csapatok alkalmaznak.

Jez Humble-nek van egy nagyon egyszerű tesztje, amivel megállapítható, hogy egy csapat valóban alkalmazza-e a continuous integration alapelveit.  A teszt kérdései így szólnak, amiket a kitalálója rendszeresen meg is kérdez hallgatóközönségétől:

  1. Ki használ CI-t? (A közönség legnagyobb része feltartja a kezét.)
  2. A csapat minden tagja commitál-e naponta legalább egyszer a master branchre (Git esetén)? (A hallgatóság nagyjából fele leengedi a kezét.)
  3. Minden commit triggerel-e buildet, és futnak-e tesztek? (A kezek száma ismét megfeleződik.)
  4. Ha elszállt a build, képes-e a csapat általában 10 perc alatt visszaállni a zöld állapotra? (Csak néhány kéz marad fenn; ők azok, akik átmentek a teszten.)

Egy fejlesztői meetupon én is feltettem ezeket a kérdéseket a kb. 100 tagú közönségnek, és megerősíthetem, ugyanez volt a tapasztalatom, és ugyanezt látom a munkám során is, amikor különböző cégeknél dolgozom.

A félreértés oka legtöbbször az, hogy sok fejlesztő úgy gondolja, ha van egy buildszerver (pl. Jenkins), és teszteket ír, amik mindig lefutnak a commit után, akkor az már CI. Ez sajnos egyáltalán nincs így.

Ezt CI-színháznak szokták nevezni (CI theater), azonban ez a CI lényegét pont figyelmen kívül hagyja. A legfőbb gondok:

  • A fejlesztők commitálnak, de nem túl gyakran, így az integráció egyáltalán nem folytonos.
  • A tesztlefedettség sokszor alacsony, így a tesztek nem nyújtanak valódi biztonsági hálót a meghibásodások ellen.
  • Sokáig hagyják pirosan a buildet, ezáltal az instabillá válik, azzal együtt pedig a teljes szállítási folyamat is.
  • A buildet feature brancheken futtatják, ami a continuous integration helyett continuous isolationt eredményez.

A CI lényege

➔ A CI lényege az, hogy ne hagyjuk, hogy egyetlen fejlesztőnél lévő kódbázis is „leszakadjon” a többiekétől. Ez csak akkor valósulhat meg definíció szerint, ha mindenki egy közös mainline-ra legalább napi egyszer commitál. Ez azért fontos, mert így lényegében mindenki ugyanazon a kódbázison dolgozik egy adott pillanatban, így a kód mindig integrált állapotban marad. Az utólagos integráció mindig munka- és időigényes feladat, ráadásul sok hibalehetőséget is rejt magában.

Sajnos a fejlesztői alapattitűd általában az, hogy szeretünk külön fejlesztgetni, és amikor pár nap elteltével végeztünk, akkor valahogy belerakjuk az új feature-t/módosításainkat a „nagy” kódba. Míg elképzelhető, hogy vannak esetek, amikor ez a módszer hatékonyabb, valójában azonban ez nagyon ritkán van így, tehát ez a hozzáállás inkább drasztikusan lecsökkenti a szoftverfejlesztés hatékonyságát. A dolog hátterében az áll, hogy a fejlesztőknek általában nincs kedvük időt és energiát fordítani a régi kód és az új, még nem létező kód közötti kapcsolat végiggondolására és kidolgozására; inkább megírják az új kódot, mivel azzal könnyebb elindulni.

Sokkal egyszerűbb megoldás egy új dolgot elkezdeni, és elhessegetni magunktól az időigényes és fárasztó utólagos integráció gondolatát. „Nyitok egy feature branchet, és ott leimplementálom a sztorit. Majd ha kész vagyok, bemerge-ölöm a developra/masterre.”  Ez a Git-flow-modell alapgondolata is, amit rengeteg fejlesztői csapat alkalmaz, azonban ezzel több gond is akad:

  1. Merge-pokol: valljuk be, a fejlesztők nagy része nem szereti merge-ölés közben feloldani a konfliktusokat. Ez igazán gyötrelmes és frusztráló lehet, leginkább akkor, ha időközben valaki refaktorálgatott a kódbázison (egy-két fájlt átmozgatott egy másik könyvtárba, osztályokat/fájlokat átnevezett). Ha rendszeresen commitálunk a master branchre (naponta többször is), ez a probléma elkerülhető.
  2. Az előző pontban kifejtett gondolatot folytatva, ha nem commitálunk gyakran, előbb-utóbb a kódon sem merünk majd nagyobb refaktorálást végezni, mert tudjuk, hogy ezzel a többiekkel szúrnánk ki merge-öléskor, márpedig ez a hozzáállás jelentősen visszaveti a kód továbbfejlesztésénék lehetőségét. Mindig adottnak kell lennie a lehetőségnek, hogy bárki bármikor refaktorálhassa a kódot, ugyanis ezek az apró változtatások biztosítják a kód folyamatos jobbá tételének lehetőségét. Ha ezt egy fejlesztő csapat nem tartja elsődleges fontosságúnak, az azt jelenti, hogy nem refaktorálják a kódot, ami bizony egy nagyon rossz jel.
  3. Lehet, hogy működött a kódunk a külön branchen, de ha merge-ölnünk kell, akkor mindenképpen újra kell tesztelni. Ez nem az automata teszteknél okoz gondot, hanem manuális tesztelés esetén.
  4. Minél hosszabb ideig nem commitálunk a masterre, annál nagyobb mértékben változik meg a kód a többiek munkája miatt, tehát annál több hiba jelentkezhet a merge-ölés során. Másképpen fogalmazva, minél gyakrabban commitálunk a masterre, annál kisebbek lesznek a commitok, és annál nagyobb az esélye, hogy minden rendben lesz.
  5. Daily standupokon gyakran okozhat gondot, hogy egy fejlesztő kezdetben egy sztori kapcsán gyors haladásról számol be, azonban a végén akár napokat is eltölt a már „elkészült” sztorijának a kódba történő integrálásával. Ez rontja a transzparenciát és a tervezhetőséget.

A trunk-based development a CI alapja

➔ Ahhoz, hogy a CI-t megvalósíthassuk, mindenkinek egy közös baseline-on kell dolgoznia a verziókövető rendszerben. Ezt Gitben master branchnek hívják, SVN-ben igazából nincsenek branchek, ott ezt trunknak nevezik. Így beszélhetünk trunk-based developmentről, ami lényegében kiiktatja a feature brancheket.

A trunk-based development a kiindulópont, de önmagában ez még nem elegendő a CI eléréséhez, mert könnyen megtörténhet, hogy a fejlesztők így is napokig „maguknál tartják” a kódot, ezért a CI másik fontos szabálya az, hogy a kódot naponta legalább egyszer mindenkinek be kell commitálnia.

Azonban a commitálás sem elegendő még önmagában, mivel automata teszteknek kell bizonyítaniuk, hogy a build zöld. Ha eltörik a build, vissza kell tudni állítani a zöld állapotot 10 percen belül. Ne feledjük, hogy ha 10 percnél tovább futnak a tesztjeink, akkor erre esélyünk sincs. Ha pont 10 percig futnak, akkor pedig automatikus reverttel tudunk csak az időlimiten belül visszaállni.

Az integráció akkor tekinthető megtörténtnek, ha a tesztek zölden lefutottak.

Ez a cikk egy sorozat első része. Ha a másodikat is el szeretnéd olvasni, itt megteheted:

Az Agiluu támogatja a szervezeteket, hogy agilisabbá váljanak.

Tetszett az írás? Iratkozz fel a blogra, hogy mindig értesülj az új írásokról!