Sunday, November 20, 2016

Bevezetés a fejlesztési módszertanokhoz

Az általános probléma-felvetés után megnézünk
  • 4 történelmi és 4 új fejlesztési módszertant
  • A 2 fő kód-kezelési policy-t
  • Kitérő: a verziókezelés alapfogalmai
  • Continuous Integration
  • DevOps, mert az mindenhonnan kilóg :D

A fejlesztés problémái - [7:00] - 0:00 .. 7:00

Megvan, hogy mit kell csinálni, milyen eszköztárral; megvan a csapat, akkor hát hol a probléma? Merthogy az évtizedes tanulságok szerint igen ritka az, ami időre készül el...

  • Nem mindig jól ill. eléggé értjük a feladatot
    • Nem a tényleges feladatra *tervezzük* a rendszert, hanem arra, amit értettünk belőle
    • Nem a teljes feladatra *tervezzük* a rendszert, hanem csak annyira, amennyi le is volt írva.
    • Más nekünk a 'nyilvánvaló' és a 'magától értetődő', mint ami a megrendelőnek az.
  • Valamely előfeltevés menet közben hamisnak bizonyul
    • A fejlesztőeszközök hibásak ill. nem a vártak szerint működnek
    • A 3rd party komponensek hibásak vagy a teljesítményük gyengébb
    • Valamely külső komponens nem készül el időben, vagy kivezetődik a piacról
    • stb.
  • Benézünk valamit a tervezésnél, elsiklunk valami fölött, elírunk valamit a megvalósításban, stb.
  • Menet közben megváltozik az elvárás
    • Mert menet közben lehetséges új feature-ök derülnek ki, és az lesz a döntés, hogy kérik ezeket is
    • Mert megjelenik egy konkurrens termék, aminél többet kell tudnunk, vagy különben ki se hozzuk a piacra a miénket
    • Mert csak - az ügyfélnek változik a hangulata
    • Mert a láncban előttünk állókra is igaz az összes fenti probléma
Emiatt aztán:
  1. A leggondosabb előkészítésnél is lesznek váratlan, előre nem látott problémák
  2. Nem lehet előre olyan tervet csinálni, amit változtatás nélkül időre végig is lehet vinni
  3. Tehát olyan módszert/processzt kell kialakítanunk, ami *tolerálja* a hibákat:
    1. Kis hiba - kis csúszás, nagy hiba - nagy csúszás, de katasztrófa semmiképp
    2. A hibák ne blokkolják a folyamat többi részét: a 'tökéletes' késsen, de az 'elég jó' lehetőleg ne
    3. A hibák lehetőleg hamar derüljenek ki, hogy minél kevesebb dolog épülhessen a hibás részekre
  4. Minél gyakrabban tudjunk feedbacket adni a megrendelőnek
    1. Hogy megnyugodjon a lelke, mert látja a haladást (és nem akar kétségbeesetten mikro-menedzselni)
    2. Hogy a feladat-értési problémák hamar kibukjanak
    3. Hogy ő is tudjon tesztelni
    4. Nem biztos, hogy mi vagyunk az utolsók a láncban, és a mögöttünk jövőknek ez aranyat ér
  5. Mindezt *a lehető legkevesebb overhead árán*
Az évek során sok ilyen módszer/processz lett kitalálva, de *egyik sem silver bullet*, bár vannak jók és vannak még jobbak. Általában érdemes a konkrét esethez testre szabni valamelyiket, szégyentelenül átvéve bárhonnan bármit, ami hasznos.

Minden előny ugyanis valamilyen árért jön, pl. a jó skálázhatóság a többlet adminisztráció árán, és az mindig esetfüggő, hogy melyik előny mennyit ér, ill. melyik ár mennyire fájdalmas.

Egy háromfős csapatnál nem feltétlenül érdemes a kódban 'fennhatósági területeket' felosztani, de ha tizenöt ember koordináció nélkül módosítgat bármit összevissza, abból káosz lesz. Fog-e bővülni a csapat, és ha igen, akkor fogunk-e tudni ezzel kezdeni valamit?

Megjegyzés: Érdemes az elején pesszimistának lenni, és amikor valaki az '...úgysem...' ill. az '...úgyis...' szavakat kimondja, akkor már eleve készülni a pillanatra, amikor a '...mégis...' ill. a '...mégsem...' beüt :D.

Ad-hoc, vagy Cowboy coding - [1:45] - 7:00 .. 8:45

"Megvan az ötlet, a formaság a plebsnek való, mi viszont zsenik vagyunk, úgyhogy ide a billentyűzetet, és rajta, jövő keddre kész is lesz!"

Ennek az eredménye mindig kaotikus, karbantarthatatlan gányhalmaz lesz, ami leginkább akkor szomorú, ha megéli az első release-t, mert onnantól már sohasem lesz idő/kedv/akarat/pénz kivágni és újrakezdeni, de ha netán mégis, akkor is az új verzióknak már kompatibilitást kell tartaniuk az itt muszájból bevezetett megalkuvásokkal.

Tehát ez a hozzáállás még magát a feladatot is képes korrumpálni!
Egyszer használatos feladatokhoz persze lehet jó megközelítés, mert úgysem kell majd bővíteni ('...úgysem...', ugyebár).

Tehát az így készített kódot *fertőzőként* kell kezelni, az értéke *nulla*, azaz a feladat terén semmilyen megalkuvást nem szabad vállalni érte, illetve miután a feladatot elérte, később hozzányúlni *tilos*.

Waterfall modell - [1:45] - 8:45 .. 10:30

Az 50-es évek klasszikus, termék-ipari folyamatainak a software-re való alkalmazása (ill. ennek kísérlete)
  1. Elvárások összeírása: az eredmény a specifikáció, annak az ellenőrzése a spekulatív review
  2. Tervezés: az eredmény a formális rendszerterv, annak ellenőrzése a spekulatív review
  3. Implementáció: az eredmény a kész kódbázis
  4. Ellenőrzés: az eredmény az átvételi nyilatkozat
  5. Maintenance: az eredmény a folyamatos működés, és a meg-megújított maintenance szerződés

Teljesen rugalmatlan, mert a menet közbeni változásokkal szó szerint semmit sem tud kezdeni; a tervezési hibák végig megmaradnak, ha pedig zsákutcába kerül, hát tolatni akkor sem tud, úgyhogy akkor törölni kell az egész projektet.

Tulajdonképpen a későbbi, összetettebb módszerek elemi lépései pont ilyenek, tehát ez lesz nagyjából az építőkocka a későbbiekben.

Inkrementális v. iteratív modell - [4:05] - 10:30 .. 14:35

Ez a Waterfall logikus kiterjeszése:
  1. Először definiálunk egy minimális koncepciót, amit már futtatni lehet
  2. Azt megvalósítjuk a Waterfall szerint
  3. Aztán definiáljuk a következő, már többet tudó verziót
  4. Azt is megvalósítjuk, stb.
  5. Ha valamelyiknél elbuktunk, akkor azt a lépést töröljük és elkezdjük megint, az utolsó stabilról kiindulva

Mindig az a legfontosabb feladat, ami
  • Hoz valami működést a usernek
  • Más rész-feladatot feltartana
  • Stabilra lehet tesztelni, hogy többé ne kelljen bele se nézni

Minden iterációs lépés előtt:
  1. Felmérjük, hogy *a megrendelő szemszögéből* mik a feltétlenül elérendő dolgok, mik jelentenének pluszt, és mi az, ami közvetlenül semleges
  2. Felmérjük, hogy ezeket milyen módon, milyen megközelítéssel érhetnénk el
  3. Felmérjük, hogy ezekhez milyen előfeltételeket látunk szükségesnek
  4. Felmérjük, hogy ezek melyikével mennyi kockázatot látunk
  5. Azt vesszük előre, ami a legkisebb kockázattal a legnagyobb előrelépést hozza
  6. Az összes szükséges közreműködő bevonását előre lekötjük, az összes szükséges előfeltételt előre biztosítjuk

Működni működik, szóval ez már jelentős előrelépés, és van, ahol konkrétan használható is:
  • Ahol a köztes verziók már élet- és piac-képesek, így az egyes lépések nem pass-or-die jellegűek
  • Ahol az egyes lépések elég kicsik ahhoz, hogy a Waterfall jó eséllyel meglépje őket

Persze ennek is ára van:
  • Ha egy későbbi koncepció kivált egy korábbit, akkor annak a korábbinak a fejlesztési költsége megy a lecsóba.
  • A szakadékokon nem lehet sok apró lépéssel átkelni; vagy egy nagy ugrással, vagy sehogy.

Rapid Application Development (RAD) - [2:20] - 14:35 .. 16:55

Ez a módszertan szintén az iteratív modellről indul, csak a specifikálási, a tervezési és a tesztelési lépésekbe bevonja a megrendelőt is, *azonnali* visszajelzést érve el ezzel, illetve a megrendelő is jobban át fogja látni a rendszer viselkedését.

Tipikus példa erre, amikor egy esetleg bonyolult viselkedésű program esetén először csak statikus példa-adatokat használva a userrel együtt megtervezzük a részegységek bemenetét, user interface-ét, és kimenetét, megelőzve ezzel rengeteg *félreértést*.

Ez viszont sajnos eseti feladatokra eseti megoldásokat fog szállítani, ami gyengén tervezett, azaz későbbiekben nehezen bővíthető, nehezen módosítható rendszert eredményez.

Tipikus példái ezeknek a 2000-es évek vége tájéki Delphi-ben írt könyvelő vagy készlet-nyilvántartó programok, amiket akkor pár nap alatt 'fejlesztettek' ki, viszont 3-4 évvel később már nem lehetett a közben megváltozott igényekhez alakítani, és még az addigra bennük felgyűjtött adatokat is nehéz volt elmigrálni.

Kisebb, tool-jellegű programokhoz ill. ötletek prototípus-tesztjéhez jó, de ahol fennáll a komoly rendszerré fejlődés esélye, ott nem ajánlatos.

Agile - [3:30] - 16:55 .. 20:25

Buzzword, amivel még a Szaharában homokot is el lehet adni manapság :D. Na jó, talán mégsem annyire csak ez.
Ezen szóval azon fejlesztési módszertanokat szokás összefoglalni (innentől ezek jönnek majd), amelyek:
  • Tudják kezelni a valóságot, az elkerülhetetlen változásokat kezelni tudják
  • Működő, használható terméket állítanak elő, még ha a megrendelő nem is tudta ezt pontosan megfogalmazni
  • A fejlesztőket is egyénként kezelik, ennek minden előnyével és hátrányával, és nem csak fogaskerékként próbálnak rájuk építeni.

Ehhez pedig az alábbi alapelvek vezetnek el:
  • Gyakran vagy folyamatosan generálni kipróbálható eredményt
  • Motivált és célirányossá tett résztvevők minden területen, a ballaszt semmire sem jó
  • Gyakori és lehetőleg személyes kommunikáció, ez a legpontosabb és leggyorsabb információ-átadás
  • Egyszerűségre törekvés
  • Gyors körülfordulási ciklusok jól definiálható célokkal és jól mérhető eredményekkel

Ezzel a hozzáállással el lehet érni, hogy a folyamat ne lassuljon be és ne akadjon el az előre nem látott akadályoknál, viszont természetesen ennek ára is van:

Előre nem lehet *egyszerre* megmondani azt, hogy *mi* lesz kész és azt, hogy *mikorra* (a'la Heisenberg)

Az Agile módszertanok erre azzal válaszolnak, hogy
  • Minden lépésben kész lesz *valami*
  • Mindig tudjuk, hogy meddig látunk előre, és
  • Ekkorra mit fogunk majd kapni

Mission-critical rendszerekhez ez természetesen alkalmatlan, és egy mosógép mikrokontrolleres vezérléséhez sem érdemes ezzel hozzáállni, de a valós élet feladataira egész jól alkalmazhatóak, túlnyomórészt mert az akadályoknál tudnak alternatív megoldások felé mozdulni.

Extreme Programming (XP) - [2:40] - 20:25 .. 23:05

Itt az 'extreme' arra utal, hogy ami hasznos dolog, azt az extremitás határáig pörgeti, azaz mindaddig, amíg az hasznot hoz.
Az alapfeltevés itt az, hogy 'az egyszerű szép', azaz a feladatot a lehető legegyszerűbben kell megoldani:
  • erős code review / pair programming
  • homogén csapat, nincs code ownership, egymás észrevételeit is ügyfél-észrevételként kezeljük.
  • gyakori teszt mindenre: unit test, integration test, acceptance test, TDD
  • YAGNI / KISS
  • 'always stable' -> commit előtt is unit test, CI
  • bármi eldobható, ha a feladat túlnőtt rajta
  • egyezményes coding & naming style, 'legyen magától értetődő'

Ez rengeteg adminisztrációs overheadet megszüntet, és jól kezeli a *kezdetben* nem teljesen tisztázott feladatokat, de mint mindennek, ennek is ára van:
  • csak motivált *és* tapasztalt emberekkel működik
  • scope creep lehetősége (mindkét irányba)
  • a sok teszt időköltséges
  • a sok újratervezés szintén

Inkább 'házon belüli' ill. K+F projektekhez alkalmas, és teljes csőd a nem kooperatív vagy részint másban érdekelt ügyféllel.

Scrum - [13:20] - 23:05 .. 36:25

Ez már kicsit komplexebb lesz, mert már nem csak egyszerűen megoldást keres a problémára, hanem igyekeznek *optimális-közeli* megoldást találni, és ehhez már nem csak általános törekvéseket fogalmaz meg, hanem konkrét szereposztást és koreográfiát, folyamat-lépéseket is.

Az alap elgondolás az, hogy
  • Ne akarjunk messzebbre tervezni, mint ameddig ellátunk, de addig viszont igen, hogy fölöslegesen ne aprózzuk el
  • Ne áltassuk magunkat olyasmi bevállalásával, amiről már az elején tudjuk, hogy úgy és akkorra lehetetlen megcsinálni
  • A fontosabb dolgokkal foglalkozzunk először
  • A processz mindig egyértelmű lépést mutasson, ne legyen nem definiált helyzet
  • Jól definiált hatáskörök, hogy ne legyen se káosz, de túl-adminisztrálás se

Háromféle szereplővel dolgozunk:
  • Product Owner / customer representative. Ő
    • Képviseli az ügyfelet a csapat felé és a csapatot az ügyfél felé
    • Vezeti a projekt státuszát
    • Tervezi és szervezi a release-eket
  • Developer
    • Részt vesz a tervezésben
    • Fejleszt, tesztel, integrál, dokumentál, stb.
    • A Dev csapat létszáma általában 3..7 között üzemképes.
    • Az eredeti séma szerint a csapat homogén, mindenki mindenhez ugyanannyira ért, de ez persze ritkán teljesül
  • Scrum Master / játékmester
    • Ügyel, hogy a folyamatok a medrükben maradjanak
    • Kezeli a váratlan helyzeteket

A sikerhez szükséges előfeltételek (feature, bugfix, stb.) egy 'Product Backlog' nevű listán vannak nyilvántartva, erre menet közben is kerülnek fel elemek (pl. bugfixek).
Minden ilyen elem kap két *pontszámot*: egyet a PO-tól, hogy ez mennyire értékes az ügyfél számára, és egyet a Dev csapattól, hogy ez mennyi erőfeszítést igényel.
Ez alapján lehet majd eldönteni, hogy melyik elem mennyire fontos, mire mikor kerüljön sor.

Ide kívánkozik egy fogalom, a 'User Story': ez valójában valamilyen ügyfél-elvárás naív megfogalmazását takarja, pl.
"Mint felhasználó, szeretném látni a futó megrendeléseim listáját", "Mint ügyfélszolgálatos, tudnom kell a felhasználók emailcímét módosítani",
"Mint admin, tudnom kell a bejelentkező képernyőre aktuális értesítéseket kiírni, hogy a karbantartásokat jelezni lehessen"

Ezek általában a "Mint ..., tudnom kell .... [, hogy ...]" formájúak, emberi nyelven és ügyfél-szemszögből vannak megfogalmazva, és a *mit*-re koncentrálnak, nem a *hogyan*-ra.

Az ilyen 'User Story'-k felhasználói értékességét jelző pontszám mértékét hívják Story Point-oknak, azaz pl. 'Ez a User Story 3 Story Point-ot ér, az 8-at'.

A pontszámokat -tapasztalat szerint- célszerű a Fibonacci-számokból kiosztani, így könnyebb lesz kisakkozni, hogy mi fér bele egy lépésbe :). A fejlesztési csapat pontszámai jók, ha konkrét időegységekre is válthatóak (óra/fél nap/nap), az értékességre ilyen megkötés nincsen.

Maga a fejlesztési folyamat két finomságú lépésközzel halad: vannak 1..6 hetes 'sprintek', illetve azon belül a napok.

A sprint egy tervezéssel kezdődik:
  • A hossza a sprint hosszának 1/20-a (2 hét esetén 1/2 nap, 1 hét esetén 2 óra, 1 hónap esetén 1 nap, stb)
  • Az első felében a Dev csapat kiválogatja azokat, amik szerintük megoldhatóak a sprint alatt (ez lesz a Sprint Backlog). Itt értelemszerűen törekedni kell az 'értékesebb' elemek priorizálására.
  • A második felében ezeket az elemeket atomi lépésekre bontják, valamint meghatározzák, hogy mikor számít az adott elem 'kész'-nek (Definition of Done – DoD)
  • Ha ilyenkor kiderül, hogy valami mégsem fér be, akkor azt lehet osztani vagy átpriorizálni
  • Eközben alakul ki az is, hogy melyik atomi lépést ki fogja csinálni
  • Ügyelni lehet arra, hogy senki se vállalja túl magát.

Fontos koncepció: *nem szabad homályos tételt hagyni*!

Tehát nincs olyan, hogy 'megjavítani az X hibát', mert amíg azt sem tudjuk, hogy mi a baj, addig nem tudjuk becsülni az időigényét sem.
Ez két lépés lesz: (1) kivizsgálni a lehetséges hipotéziseket, (2) majd az 1. után meglátjuk!
Minden elemi lépés definit és véges legyen, jól meghatározott eredménnyel (deliverable), amit a végén fel lehet mutatni.
Ez nem feltétlenül kód vagy teszteset vagy dokumentáció, hanem lehet pl. egy elemzés eredményének a szóbeli(!) összefoglalása, pl. 'az 5 eredeti hipotézisből a 4. a valós'.

A napi időlépés egy szigorúan 15 perces rövid státusz-egyeztetést jelent, amin:
  • Bárki részt vehet, nem csak a Dev csapat
  • Senkire sem várunk, pont a megadott időben zajlik
  • Elvárás, hogy mindenki felkészülve érkezzen, elvégre nem nagybeszámolót kell tartani
  • Mindenki 3 dolgot mond el:
    • Mit végzett tegnap óta
    • Mit tervez mára
    • Lát-e valamit, ami akadályozza a sprint teljesítését
  • Semmi egyéb nem hangzik el, tehát nincs részletezés, vagy visszakérdezés
  • A PO adminisztrál, eredetileg neki szól ez, csak így a többiek is hallják
  • Ha van valami előre nem látott akadály, akkor azt a SM intézi, *a terven kívül* (Emlékszünk: menet közben kiderülő dolog nem lehet, mert olyat nem tettünk a backlogra tovább bontás nélkül)

A sprint végén megint tart egy *max.* 1/20-os szeánszot a csapat, aminek szintén két fele van:
  • Review: Mi lett készen? Ezt be is lehet mutatni, ha van rá igény.
  • Retrospective: A Dev csapat beszéli meg, hogy mi ment jól/rosszul, mivel lehetne/kéne javítani a köv. sprintben.

Ezen hozzáállással *elvileg* a sprint elején lehet tudni, hogy mi lesz a végén; a hátralévő elvárások száma monoton módon csökken, és a haladás jól definiált mérőszámokon követhető:
  • Sprint Burndown: a napok előrehaladtával a lezárt ill. hátralévő
    • Taskok száma
    • Időigény
    • Érték (Story Point-ok száma)
  • Product Burndown: az elért ill. kész termékig hátralévő Story Point-ok száma
  • MVP = Minimal Viable Product, ill. az ehhez szükséges pontszám
  • Ezek alapján valamelyes 'tól-ig' becslést is lehet adni a várható haladás ütemére, de ez persze csak becslésként értendő!

Korlátok viszont:
  • Szoros csapaton belüli kommunikációt igényel
  • Általános hozzáértésre épít, a speciális képességeket rosszul kezeli
  • Továbbra sem tud abszolut határidőket jósolni/betartani
  • Az elkészülési sorrendet sem tudja előre jósolni, mert az a menet közbeni változások mentén alakul

Kanban - [3:35] - 36:25 .. 40:00

Ez a módszertan konkrét gyártási ütemezésből lett átvéve, és ez valamelyest meg is látszik rajta :).
Sokkal egyszerűbb és rugalmasabb a Scrumnál, ugyanakkor viszont (sajnos) még kevésbé pontos, és főképp DevOps jellegű fejlesztéseknél van nagy szerepe.

A központi eleme a Kanban-tábla, ami kb. egy faliújság-szerű tábla, oszlopokkal az egyes állapotoknak, és ezen cetlik képviselik az egyes teendőket.
'Teendő' bármilyen igény lehet, de itt is igaz az, hogy indefinit feladattal nem foglalkozunk, tehát ha valami nem látható át végig, akkor azt darabolni kell, új cetliket adva a rendszerbe.

Minden teendő az alábbi 'állapotok' között mozog:
  • Vár: aktuálisan még épp nem foglalkozunk vele, mert pl. egy másik elem logikailag előbb jön
  • Folyamatban: dolgozunk rajta, foglalkozunk vele
  • Tesztelés alatt: reméljük, hogy kész, de ezt még igazolni kell
  • Kész: ellenőrizve, készen van

Mivel a túl gyakori kontextus-váltás nem jó dolog, érdemes maximalizálni, hogy egy embernek legfeljebb hány task-ja lehet az egyes kategóriákban egyszerre.

A csapat lehet ön-koordináló, de lehet csop.vez. általi kiosztás is, a lényeg az, hogy mindenki valamelyik oszlopból dolgozik valamely másikba, pl.:
  • Az 'új' oszlopba bárki vehet fel jegyet
  • A QA az 'új' oszlopból húz jegyeket, ellenőrzi őket (minden szükséges info meg van-e adva, ellenőrizhető-e, fennáll-e még, stb.), és teszi át vagy a 'kész (semmis)' oszlopba, vagy az 'ellenőrizve' oszlopba
  • A fejlesztők az 'ellenőrizve' kupacból húznak cetlit, menet közben a 'folyamatban'-ba teszik, végül a 'befejezve' rovatba rakják (vagy darabolják, és akkor a maradék, nem becsülhető rész megy vissza az 'ellenőrizve' fázisba).
  • A QA a 'befejezve' állapotú jegyeket ellenőrzi, és teszi vagy vissza az 'ellenőrizve', vagy a 'kész' rovatba
  • stb.

Ha menet közben célszerűnek látszik, akkor lehet az igényekhez igazítani a workflow-t (fázis-átmenetek, tevékenységi körök, stb.)

ScrumBan - [1:45] - 40:00 .. 41:45

A Scrum és a Kanban ötvözete, mi más :D.
Kevésbé 'viráglelkű' idealista, mint a nyers Scrum, de jobban darabolódik, mint a Kanban, így konstruktív fejlesztésekhez is használhatóbb:
  • Csapatokkal dolgozik, és nem egyénekkel
  • De ezek a csapatok struktúráltak, van vezetőjük, stb.
  • Engedi a specializációt
  • Konkrét workflow policy-ket írhatunk elő
  • A priorizáció nem csak a Dev csapaton múlik
  • Időegységekben/lépésekben halad, nem szabad folyamatossággal
  • A visszacsatolásnak határozott helye van: a Scrum szeánszai
  • On-demand planning: ha a Product Backlog-ból adott mennyiséget feldolgoztunk, ideje újraértékelni a maradékot
  • Feature freeze: ha közeledik valamely határidő, akkor addig a már folyamatban lévőket kell lezárni, új dolgot megkezdeni nem szabad

Egyéb - [0:55] - 41:45 .. 42:40

Agile filozófia: Ami beválik, az beválik és át kell venni; ami nem, azt el kell hagyni; ami meg nem biztos, azt ki kell próbálni, mert esete (cége, projektje, ügyfele, stb-je) válogatja, hogy mi lesz a nyerő stratégia. Viszont érdemes először utánakeresni, hogy más próbálkozott-e már ilyesmivel, és milyen tapasztalatokra jutott.
Nincs silver bullet, semmiből nem lesz valami, tehát továbbra sem fogjuk tudni előre pontosan megmondani egy komplex feladat időigényét, lefutását, de a folyamatos és fokozatos fejlődést, valamint a problémák korai észlelését elérhetjük.

Kódbázis-kezelési policy-k - [0:45] - 42:40 .. 43:25

Az említett módszertanok a konkrét fejlesztői tevékenységekről csak annyit mondak, hogy 'fejlesztés' ill. 'tesztelés', de ennek a részletein nagyvonalúan túlléptek.
Ehhez hasonlóan arra sem válaszoltak, hogy mi történik, ha valami hiba csúszik a folyamatba, és valami balul sül el.
Az ember olykor hibázik, de a felelősség *mindig* a processzen van, mert annak kell tudnia észlelnie és kontrolláltan lereagálnia az ilyen hibákat is!

A 'Baseline mindig stabil' policy - [14:35] - 43:25 .. 58:00

Főképp *kritikus termékeknél* elvárás, hogy a kiadott termékbe csak (ismert) 100%-ra tesztelt kód kerülhessen, és mivel az ilyen rendszerek fejlesztése általában erősen költséges, az ügyfél a határidők pontos betartását is el szokta várni a pénzéért.

Ez lehetséges is, csak mint mindennek, ennek is ára van, mégpedig a szigorú minőségbiztosítási folyamat, ami összetettséggel, erős policy-kkel, fajlagosan nagy adminisztratív és tesztelési időigénnyel jár.

Amikor egy ilyen rendszerben egy fejlesztési igény megjelenik, akkor

  1. Jó esetben (ha új feature-ről vagy reprodukálható hibáról van szó), vagy a fejlesztő, vagy a QA ír egy teszt-esetet, ami el tudja dönteni majd a fejlesztői munka sikerét (TDD - Test Driven Development).
    Kevésbé jó esetben (nem reprodukálható hiba) megfogalmazzuk a '
    Definition of Done'-t, azaz azt a követelményt, amit elvárunk majd
  2. A fejlesztő a saját munkakörnyezetében addig reszeli a kódot, amíg a fenti teszt sikerrel le nem fut, ill. úgy nem véli, hogy a követelményt elérte.
    Amíg ezt el nem éri, addig próbálkozik, vagy ha elakad, akkor eszkalálja a problémát és lép a következőre. Ez a '
    fejlesztői teszt'.
  3. Ezután ezt másvalakinek (QA) is ellenőriznie kell, mert a fejlesztő óhatatlanul is arra koncentrál, amivel ő nehézségekbe ütközött, és a technikailag egyszerű dolgokat hajlamos átlépni.
    Pl. Egy felolvasandó file struktúrális hibáit valószínűbb, hogy jól kezeli, mint az olyan corner case-eket, hogy az adott néven nem file van, hanem könyvtár. "A fene se gondolta, hogy..."
    Ez a '
    feature teszt' vagy 'bugfix teszt'.
  4. Ezután azt is ellenőrizni kell, hogy ez az új változtatás kompatibilis-e a többi változtatással, azaz nem ront-e el valami mást.
    Pl. Valaki átszervez egy hívási interface-t, ezt minden használt ponton át is vezeti, de közben másvalaki meg egy új ponton használná a régi séma szerint. Önmagában mindkettő helyes, együtt viszont ütik egymást. Ez az '
    integrációs teszt'.
  5. Ezután a megfelelésen túl a teljesítmény-vonatkozásokat is ellenőrizni kell, úgyhogy ez a 'teljesítmény teszt'.
  6. (Opcionálisan) Ezután kikerülhet a termék adott állapota béta-tesztre.
  7. Végül kikerülhet éles release-be.

Mivel több fejlesztő dolgozik egyszerre, biztosítani kell azt is, hogy ne húzkodják egymás lába alatt a szőnyeget, azaz az egyik fejlesztői tévedés ne akadályozza a többiek munkáját.
Ehhez hasonlóan szintén elvárás, hogy az egyik task (feature/bugfix) hibája ne akadályozza a többi task előrehaladását.
Mindemellett a rendszernek magának skálázhatónak, párhuzamosíthatónak kell lennie, hogy lehetőleg ne legyen szűk keresztmetszet a folyamatban.

A gyakorlat ezen elvárásokra általában hasonló rendszer-kialakításokat eredményezett, több-kevesebb eltéréssel, helyenként az esetileg szükségtelen részek kihagyásával:
  • A forráskód és minden, a termékhez szükséges információ/adat verziókezelve van. Ez azt takarja, hogy ugyanazon dolognak a múltbeli állapotai is elérhetőek, valamint lehetséges párhuzamos fejlődési vonalakat is elágaztatni, illetve az ezeken véghezvitt változtatásokat egymástól (többé-kevésbé) függetlenül másik ágra *olcsón* átvinni. (Erről kicsit bővebben külön szakaszban később.)
  • Stage
    Ezen az ágon a
    következő kiadásra szánt javításokat/fejlesztéseket gyűjtjük itt. Ide csak ellenőrzött, stabil kód kerülhet, ami *nem rosszabb*, mint az utolsó release volt.
  • Integration
    Ez majdnem olyan, mint a Stage, csak egy lépéssel előtte jár: ide kerül(nek) először azon,
    külön-külön már ellenőrzött javítások, amiknek az összes többire gyakorolt hatását még nem ellenőriztük.
    Ha ezen az integrációs teszten (és a performance teszten) átmegy, akkor ez a változás-csomag
    kerül át a Stage-re, ha viszont nem, akkor a változás-csomagot (innen) eldobjuk, ezt az ágat visszaállítjuk a Stage-dzsel megegyező állapotra, és az érintett fejlesztőket összetrombitáljuk, hogy ugyan beszélnék meg a dolgot közösen :D.
  • Development
    Ez is a Stage-ről van elágaztatva, a
    fejlesztők ezen dolgoznak, és a feature/bugfix tesztek is ezen vannak végrehajtva.
    Amikor egy feature önmagában megáll a lábán, akkor innen kerül át az Integration-re.
    Célszerű
    párhuzamos fejlesztésenként egy-egy ilyen ágat létrehozni, így a félkész állapotú, vagy tévedésből elrontott kód nem destabilizálja a többiek munkáját.
  • Release
    Amikor a Stage-en összeáll a soron következő kiadás anyaga, akkor arról
    archiválási céllal készítünk egy-egy Release ágat, és az ügyfélnek kiadandó binárist már erről buildeltetjük. Később viszont ezen ágakhoz már *nem* nyúlunk, lévén archív adat.
  • Időről időre a Development ágakat szinkronizáljuk a Stage-ről, hogy amit közben a többiek elkészítettek, az a Developmenten is ott legyen. Ha itt kód-ütközés van, mert pl. ugyanazt a részt mindketten módosítanánk, de másképpen, akkor a Stage-ről jövő kódnak van prioritása (mert az már tesztelve van), és a Development ágat kell ahhoz igazítani.
  • Ha a fejlesztő úgy érzi, hogy készen van, akkor a Development ág tesztelését átadja a QA-nak.
  • A QA a Development ágról generáltat egy termék-binárist (Continuous Integration-nél részletezzük), és elvégzi az adott hibajegy ellenőrzését.
    Ha a QA hibát talált, akkor a labda visszapattan a fejlesztőhöz, ha nem, akkor pedig a változtatást át lehet vinni a
    Developmentről az Integration-re.
  • Az integráláskor általában az erre kész összes javítását egyszerre visszük az Integration-re.
    Ezután az Integration-ből lehet buildeltetni egyet, és azt funkcionálisan ellenőrizni, hogy nem rontottunk-e el valami korábban már működő dolgot.
    Ha kudarcra fut a teszt, pl. az átemelt 20 hibajegyből *valamelyik* elrontott valamit, akkor lehet felezgetni, hogy mely(i|e)k volt(ak) a bűnös(ek).
    Worst case: ha az N átemelt hibajegyből egyik sem jó, akkor ennek kiderítése N*log2(N) Integrációs teszt-futtatást igényel.

    Fontos, hogy az eddigi *összes* build ún.
    debug-build volt, azaz a kód nem teljesítményre, hanem nyomozhatóságra volt optimalizálva, azaz pl. voltak benne részletes progress-jellegű log-üzenetek, belső integritás-constraint-ellenőrzések, stb., amik a végső, kiadási buildekbe már nem fordulnak bele.
  • Ha az integrációs teszt sikeres volt, akkor az Integration ágról lehet egy production-buildet forgattatni, ebben már nincsenek részletes debug log-üzenetek, sebességre van optimalizálva, nincsenek benne debug szimbólumok (emiatt kvázi nyomozhatatlan), viszont már van értelme a teljesítmény-tesztet is lefuttatni rajta.
  • Ha a teljesítmény-teszt is elfogadható lett, akkor az Integrációs ágról az ott lévő változásokat át lehet emelni a Stage-re.
  • Amikor a Stage-en összejött a megcélzott javítás-halmaz (vagy elértük a határidőt :D), akkor arról készül egy Release snapshot-ág és az erről forgattatott bináris megy ki az ügyfél felé.
Mint mindenhol, itt is tele vagyunk kellemetlen tradeoff-döntéssel, pl.
  • Minden hibajegyhez csináljunk-e külön Development ágat?Ha igen, akkor az esetleg elrontott javítások nem destabilizálják a többi javítást, még akkor sem, ha valamiről menet közben derül ki, hogy elhúzódik. Viszont ez plusz adminisztrációval jár, bár ezt lehet automatizálni.Ha nem, hanem pl. fejlesztőnként/feature-önként/csapatonként van egy-egy ág, akkor az relatíve 'olcsó' lesz, viszont egyrészt egy elrontott commit kihat mások munkájára is, másrészt pedig nyilván kell tartani, hogy *melyik ág épp milyen fázisban van, és ki foglalkozik vele*.
    Ha Scrum-jellegű rendszert használunk, akkor már a sprint elején láthatjuk, hogy hány párhuzamos láncolaton fogunk dolgozni, tehát akár külön Dev-ágakat is rendelhetünk hozzájuk, és mivel a sprint végére minden folyamat véget ér, ezért nem marad elvarratlan szál sem.
  • Tehát ha kevés, és egymástól jól elkülönülő hibajegy-láncolatunk (pl. független feature-ök megvalósításai) van, akkor célszerű Feature-brancheket tartani, de ha minden mindenre kihatással lehet, és sok a meglepetés (akkor elrontottunk valamit a tervezésnél...), akkor extrém esetben "one bug - one branch".
    Mindenesetre az infrastruktúrát célszerű már
    eleve úgy kialakítani, hogy olcsó, de legalábbis lehetséges legyen a Development-ágak létrehozása ill. az azokkal való fejlesztés.
  • Ha túl sok az integrációs hiba, akkor jegyek gyakrabban érik el az integrációra kész állapotot, mint amennyi idő alatt egy Integrációs teszt lefut, és így ez utóbbi sosem éri utól magát. Ezzel viszont *nem lehet* mit kezdeni, az ilyesmi azt jelzi, hogy a kód koncepciója túl gyakran változik, azaz *nem fejlődik, hanem burjánzik*, és valami nagyon el van rontva már a feladat-felfogás szintjén.

A 'Baseline mindig stabil' séma tehát garantálja, hogy minden pillanatban van egy kiadható, ellenőrzött változatunk, ez azonban erős szervezést és intenzív tesztelést igényelt.

A 'Baseline mindig friss' policy - [5:10] - 58:00 .. 1:03:10

Sok esetben, pláne a projektek elején a fenti adminisztrációt túlzásnak tartják, mert 'még nincs mit tesztelni', 'tudjuk, hogy még nem stabil', ezért inkább a 'Baseline mindig friss' elvet követik, miszerint:

  • Minden változtatás *azonnal* a közös Baseline-ra történik, esetleg minimális helyi fejlesztői tesztelés után, de ez is opcionális
  • Amikor valaki 'eltöri' a kódbázist, akkor akár ő, akár aki először észrevette, az javítja és commitolja is
  • Amikor közeledik egy release, akkor *leágaztatnak a Baseline-ról egy Stage ágat*, és elkezdik azt stabilizálni
  • A fejlesztés a Baseline-on halad tovább (!)
  • Minden megtalált hibát a Baseline-on javítanak, és onnan cherry-pickelik át a Stage ágra
  • Amikor a tesztek szerint a Stage ág stabil, akkor archiválási céllal elágaztatnak róla egy Release ágat
  • Ezt a Stage-et letörlik (majd a köv. release-kor ágaztatnak újat)

Mondani sem kell, hogy kezdetben ez roppant gyors és fordulékony hozzáállás, és mint láthatjuk, a fejlesztési fázisban szinte semmi adminisztrációt nem igényel, tehát *egész amíg nincs valami release-elni való*, addig igencsak kecsegtető.

A problémák ott kezdenek szaporodni, amikor eljön a release ideje:
  • Minél több a kiteszteletlen, félkész, potenciálisan bugos kód a Baseline-on, annál bizonytalanabb, hosszabb és nehezebb a Release ág stabilizálása
  • Ha többen dolgoznak a Baseline továbbfejlesztésén, mint a Release stabilizálásán, akkor ott a teszteletlen kód szaporodni fog
  • Amikor a stabilizálás már éppen csak sikerül határidőre, akkor a következő Release-t már nem a Baseline-ról fogják elágaztatni, hanem kényszerből az előző Release-ről
  • Onnantól a két ág között a szinkron megszűnik, és egy idő után a Baseline-ról egyáltalán nem lehet javításokat átvinni az aktuális Release-re, tehát a kért feature-öket/javításokat már direkt a Release ágon kell megcsinálni
  • Tesztelés híján a Baseline annyira instabil/esetleges/ismeretlen állapotúvá válik, hogy önmagában használhatatlan lesz, értelmetlen leágaztatni is róla, így az összes fejlesztés szépen átköltözik a Release ágra
  • Tehát innentől az aktuális Release ág veszi át a Baseline szerepét, és ebből fognak új stabilizációs ágakat indítani, és az egész folyamat kezdődik előről egy szinttel lejjebb a fában :(

Egy-egy ilyen elkuszálódási lépés 1..2 év alatt következik be, és a kód minden szinttel egyre merevebbé, eseti patkolásokkal telibbé, és így karbantarthatatlanabbá, módosíthatatlanabbá válik.
A 6.-7. szinttől a lehetőségek teljesen bezáródnak, csak az eseti bugjavítgatás marad, a helyzet dollár-árverés jellege miatt sem a teljes újraírást, sem az ennél amúgy nehezebb letisztázást nem fizeti ki senki, és a projekt holtpontra ér.

A 'Baseline mindig friss' módszer tehát egyszerű és működőképes, de:
  • Gondoskodni kell róla, hogy Baseline folyamatosan tesztelve legyen (lásd Continuous Integration)
  • A hibajavításnak a további fejlesztésekkel szemben abszolút prioritást kell adni
  • A Stage stabilizálásával mindig több ember foglalkozzon, mint a Baseline továbbfejlesztésével

Version Control alapfogalma - [9:25] - 1:03:10 .. 1:12:35

A dolog legelső vonatkozása az, hogy a file-jainknak nem csak az aktuális állapotát szeretnénk ismerni, hanem az összes múltbelit is, és ezen állapotokhoz meta-információt (dátum, módosító ember, hibajegy-szám, stb.) is szeretnénk csatolni. Pl. amikor egy hibás/gyanús kódrészt találunk, szeretnénk tudni, hogy az mikor, milyen módosítás-halmaz keretében keletkezett, mert lehet, hogy az ezzel egyszerre végigvitt többi között is lesz hiba. A javításhoz jó tudni, hogy ki csinálta, és milyen céllal, stb.

A második igény onnan ered, hogy ha többen szeretnének ugyanazon file-okon egyszerre dolgozni, akkor a sima file-szintű nyilvántartásnál aki utoljára menti el a változtatását, az felülírja azokat a változtatásokat, amik az ő munkája közben keletkeztek.

Ezen lehetne segíteni a file zárolásával, de mint mondtuk, *egyszerre* szeretnének többen dolgozni, és nem pedig *egymás után*...

Az erre adott megoldások alapgondolata az, hogy a felhasználók nem magukat a (teljes) file-okat 'mentik el', hanem azokat a *differenciális változtatásokat*, amiket azokon véghezvittek, azaz nagyjából pl. 'az X és Y sorok közé szúrd be a Z-t', 'az A és a C közül töröld ki a B-t', ill. 'a D sorban az "alma"-t cseréld le "körte"-re'.

Így aztán ha valaki a fenti példánál maradva beszúrja a Z-t, közben másvalaki meg kitörli a B-t, akkor teljesen mindegy, hogy melyikük *változtatása* hajtódik végre először, az eredmény ugyanaz lesz: bekerül a Z és eltűnik a B.

Gond akkor van, ha többen konkrétan ugyanazt a részt változtatják, pl. mindketten az X és az Y sorok közé szúrnának be valamit, csak az egyik Z-t, a másik meg W-t. Erre már nincsen automatikus megoldás, ilyenkor az első 'beküldő' változtatása sikerül, a másiké másik 'beküldése' viszont meghiúsul, hiszen (már) nincs X és Y közvetlenül egymás után.

Ezt hívják 'conflict'-nak, és ilyenkor a második versenyző feladata, hogy megnézze, hogy mi változott a közös kódbázisban az ő munkálkodása közben, ill. ő mit változtatna azon, és a két változást összefésülje, majd ennek az eredményét küldje be. Ezt hívják a 'conflict feloldásának', ill. magát a beküldést 'commit'-olásnak.

Minden ilyen commit során az elkövetési dátum és az elkövető azonossága is feljegyzésre kerül, továbbá szokás csatolni egy leírást is, amiben megmondjuk, hogy mit miért csináltunk. Hagyományosan ennek az első sora egy rövid összefoglalás, hogy amikor egy-egy file élettörténetét vizsgáljuk, akkor csak az első sorokat listázva szép tömör áttekintést kapjunk.

A 'közös kód' elérhetőségi helyét nevezzük 'repository'-nak, az onnan helyi munkapéldány kihozatalát 'checkout'-nak, illetve a helyi munkapéldánynak a repo-ból való frissítését 'update'-elésnek.

Mármint többnyire, mert ez már az épp használt verziókezelő rendszer nevezéktanától és működési logikájától függ, mert lehetnek (vannak...) a folyamatban részletesebb lépések és egyéb szintek is, de az alap gondolat nagyjából ez.

Ha a file-oknak az élettörténetét lánc-szerűen nyilván tudjuk tartani, akkor nem nagy ugrás innen az, hogy ezeket a láncokat el is tudjuk ágaztatni. Például egy hosszabb, több napon/héten átívelő fejlesztés esetén elkülöníthetünk egy 'másolatot' a közös kódbázisról, aminek az elágazás pillanatától saját élettörténete van.

Ha a file-oknak bármelyik múltbeli állapotához hozzá tudunk férni, akkor az sem okoz gondot, hogy bármely két állapot közötti különbséget előállítsuk: csak össze kell fűznünk a két állapot közötti változtatásokat.

Ez pedig akkor lesz nagyon hasznos, ha pl. az egy elágazást 'vissza szeretnénk futtatni' a főágra: az elágazási pont óta ott elkövetett változtatásokat szépen rájátsszuk a főágra, mintha egy fejlesztés eredménye volna. Persze itt is lehet conflict, amit fel kell oldani, és érdemes eladminisztrálni azt is, hogy mit és honnan hoztunk át, de a működés ugyanaz.

Ha már korábban egyszer így átszinkronizáltuk az egyik ág változásait a másikra, de azután azon tovább dolgoztunk, és most megint szeretnénk ezt megtenni, akkor értelemszerűen nem az elágazási ponttól vett változásokra lesz szükségünk, hanem az utolsó szinkronizálási ponttól, de a működés megintcsak ugyanaz.

Amikor így valamit átveszünk egy másik ágról, azt hívjuk 'merge'-elésnek, amikor pedig egyesével (tehát nem valamely pontig mindent mindent), azt 'cherry picking'-nek, de ez utóbbi rizikós művelet, a hiányzó előfeltételek miatt nem feltétlenül sikerül.

A mai verziókezelő rendszereknél az elágazás amúgy 'olcsó' művelet, tehát ténylegesen csak a változtatások (és minimális adminisztratív adat) kerül eltárolásra, függetlenül az elágaztatott kódbázis méretétől.

Érdemes még megemlíteni a verziókezelők kapcsán egy koncepcionális különbséget:
  • A 'centralizált' rendszerek valami dedikált szerveren tartják a repository-t.
    Ennek előnye, hogy az embernek csak az őt érdeklő részeket kell kihoznia, hátránya viszont, hogy a kód eléréséhez a szervert el kell tudni érni.
  • Az 'elosztott' rendszereknél mindenkinél megvan a repository másolata, és alkalmi jelleggel ezeket a repository-kat szinkronizálják egymáshoz.
    Ennek előnye, hogy pl. repülőn is dolgozhat az ember, és ha valamelyik repo alatti gép megsemmisül, bármelyik másolata teljes értékű replika, hátránya viszont, hogy némi önfegyelmet igényel, hogy az ember gyakran szinkronizáljon, különben ha egy heti conflict szakad egyszerre a nyakába, akkor abból nehezen ássa ki magát :D.

Continuous Integration (CI) - [4:35] - 1:12:35 .. 1:17:10

Beszéltünk arról, hogy 'valaki ilyen/olyan ágról fordíttat egyet'. Az emögötti koncepció és infrastuktúra a következő:
  • A fejlesztők helyileg fordított kódjai megbízhatatlanok, mert tele vannak helyi, commitolatlan kiegészítésekkel, ki tudja milyen beállításokkal lettek fordítva, és semmi garancia, hogy utólag reprodukáhatóak lennének.
  • Tehát kellenek dedikált *build szerverek*, amelyek *valamely ág valamely állapotát* checkout-olva, *valamely beállítás-halmaz szerint* lefordítják, csomagolják, stb., előállítva belőle a kész termék-csomagot.
  • Ehhez az előállítási folyamatot emberi beavatkozástól mentesre kell kialakítani: fordítás-vezérlő rendszereket (make, ant, gradle, stb.) kell használni, de legalábbis le kell scriptelni mindent.
  • Az eredményt, az összes bemenő paraméterrel és a folyamat során keletkező logokkal együtt egy azonosítható helyen tárolni kell, szükség esetben rotálva ritkítani, stb.
Erősen ajánlott az ilyen 'build szervereket' *opcionálisan* az automatizált teszteléssel is összekötni, hogy
  • Minden keletkező buildre egyben azonnal ráfussanak az eddigi tesztesetek is

Ahol a 'Baseline mindig friss' policy-t használják, ott ezen felül:
  • Minden lehetséges alkalommal, tehát *folyamatos* jelleggel a Baseline buildelődjön le
  • Fussanak rá az automatizált tesztek
  • Az összes talált hiba prioritást kapjon, még a határidős feature fejleszés fölött is
  • Worst case a hibás commitokat el kell revertálni, és ez annál könnyebb, minél kevesebb minden épül rájuk azóta
  • Ha épp van Stage, akkor az is folyamatosan buildelődjön, tesztelődjön
  • És annak a hibái abszolut prioritást kapjanak, még a Baseline hibái fölött is
  • A Stage build eredménye automatikusan kerüljön ki a Teszt környezetbe

Hát, innen ered a 'Continuous Integration' neve, és ebből láthatjuk a 'Baseline mindig friss' elterjedtségét is...

A Continuous Integration maga tehát elég jól skálázódik, az ilyen rendszerek tipikusan egy központi orchestrator rendszerből és nagy halom build/teszt agentből állnak, hiszen
  • Egyszerre több ágat is kellhet buildelni
  • Több támogatott platformra
  • Többféle opcióval
  • Többféle teszteset-készletet is kellhet lefuttatni rajtuk

A DevOps fejlesztési helyzet - [7:15] - 1:17:10 .. 1:24:25

A klasszikus 'dobozos' termék fejlesztésétől némiképp eltér az *online termékek* fejlesztése, hiszen itt
  • Olcsó és ezért gyakori release-ek vannak, akár olyan gyakran, ahogy egy-egy feature elkészül vagy egy-egy bug (tesztelve) kijavul
  • Tehát általában egyetlen, de maximum egy pár fejlesztési vonulat zajlik párhuzamosan
  • Azaz a specialitása mellett mindenki ért mindenhez, mert különben az épp nem fejlesztett terület 'gazdái' kihasználatlanul állnának
  • A fejlesztés és a stabilizálás szinte semennyire sem fedi át egymást, kvázi egymás utáni szakaszokként jelentkeznek
  • A stabilizálás és a tesztelés egyszerre zajlik, és mivel kvázi egyetlen feature-ről van szó, ezért a QA-ra és a Dev-re csak váltakozva van szükség, és nincs olyan, hogy 'Amíg a QA teszteli az X feature-t, azalatt a Dev stabilizálja az Y-t és/vagy fejleszti a Z-t'
  • A bugjavításért sincs külön release-re szükség, a következő feature lépéssel egyben kerül kiadásra

A fenti ismérvek szinte kiáltanak a Scrum/Kanban után, valamint magukkal vonják, hogy nincs értelme külön Dev, QA és üzemeltetési csapatot szervezni, mert ezekre egymással váltva van szükség, egyszerre szinte soha! Tehát a DevOps csapat egyszerre fejlesztő, QA-s és üzemeltető is egyben.

Ami persze maga után von egy pár, eddig evidens követelményt:
  • Aki egy hibajegyhez tesztesetet, egy feature-höz specifikációt ír, annak nem szabad azt implementálnia, mert akkor óhatatlanul is lesznek le nem írt, 'magától értetődő', 1 db fejben megtartott információk.
  • Aki valamit implementál vagy javít, annak nem szabad azt tesztelnie, mert akkor óhatatlanul az általa nehéznek talált részekre fog fókuszálni, és elsiklik a corner case-ek fölött.

Hogy is néz ki ilyen esetben a ScrumBan?
  • Egy csapat van, *A* csapat, ha ennél ennél komplexebb a rendszer, akkor azt rendszer-szinten kell független egységekre darabolni
  • A csapat kvázi homogén
  • A backlog az élő hibajegyekből és a felvetett feature-ökből áll
  • A sprint teljesen definit, mindennek látszik a vége (Ami csak valameddig látható előre, az a tervezésnél ott el lett vágva)
  • A sprintbe bevállalt elemek közül szabad vadászat van
  • Minden elemről mindig tudni lehet, hogy hogyan áll, ki mit csinált vele
  • A sprint végén release
Mi újság az ág-kezeléssel? Friss-e a Baseline, vagy stabil? Van, amikor egyik sem, és van, amikor mindkettő :D !
  • A sprint eleje táján a Baseline mindig friss, mindenki commitolja, amit épp csinál
  • A Continuous Integration keretében a Baseline mindig fordítódik és tesztelődik
  • A fogott hibák javításának abszolút prioritása miatt a kód fokozatosan stabilizálódik
  • Ahogy a sprint feladatai fogynak, a végére csak stabilizáció marad
  • Mivel a sprint feladatai véges sokan vannak, és *amíg ez nincs kész, addig újakba nem kezdünk bele*, ezért
  • A sprint végére a Baseline megint *biztosan* stabil
  • Release ágat igazából csak archiválási céllal kell csinálni, mintegy felcímkézendő a Baseline adott állapotát

Tehát háromféle *környezetről* vagy *állapotról* beszélhetünk:
  • A *fejlesztési környezet* a fejlesztők helyi munkapéldányát jelenti
  • A *teszt környezet* a Baseline utolsó (lefordult) buildje
  • A *production környezet* az utolsó Release

Ez a módszer sem silver bullet persze, mert korlátozott az érvényességi tartománya:

  • Online, olcsó és gyakori release kell hozzá
  • A rendszeren csak kis (max 3..9 fős) csapatok által rövid idő (max. 6 hét) alatt megvalósítható lépéseket lehet fejleszteni
  • A konkrét megvalósulási időket (mi mikorra lesz kész) nem lehet előre megmondani

Idő- és költség-becslés

Tehát akkor egy projekt indításakor hogyan tudunk időt/költséget becsülni? Sajnos sehogyan, a jövőbe továbbra sem látunk, valamint ezek a módszerek egy másik problémát céloztak.
Ha a befejezés után visszatekintve megnézzük egy projekt történetét, akkor már látjuk, hogy
  • összesen mennyi fejlesztési + tesztelési idő ment bele
  • ebből mennyi volt az előre nem látott kitérő
  • amiből mennyi volt a szükségszerű, ami a menet közbeni változások miatt kellett
  • és mennyi volt az, ami fölösleges volt, amit, ha előre látunk mindent, akkor megspórolhattuk volna
A fentebb említett projekt-szervezési módszertanok nem arra céloznak, hogy előre becsüljük az egészet, hanem csak arra, hogy a fölösleges kitérőket redukáljuk
minimálisra.

Menet közben ehhez tesznek ugyan rövid távú becsléseket, de ez nem segít a hosszú távú becslés problémáján.
A 'mindset' viszont lehet, hogy használható ide is, és bár az egzakt megoldás lehetetlen, de az 'elég jó' segíthet, szóval
  • A kezdeti tervezésnél bugjavítás még nincs, csak a feature-öknek kell megbecsülnünk az időigényét
  • Az előzetes specifikációt mindenképpen meg kell csinálnunk. Ez kb. a Product Backlog összerakását jelenti, illetve egyes elemeket addig osztani/finomítani, amíg csak teljesen egyértelmű és teljesen bizonytalan részek (amiből már nem lehet egyértelmű részt kiemelni) lesznek benne.
  • Az egyértelmű részekhez becsülhetünk időt, és ezekkel mindenképpen számolnunk, már mehetnek is a végösszeghez.
  • A bizonytalan részek közül pár fajtát szintén tudunk kezelni:
    • Ami a hatáskörünkön kívüli dolog (pl. a projekt másik résztvevőjétől jövő input), az külső függőség, így nem csak hogy nem lehet, de nem is szabad belevonni a mi költségbecslésünkbe, ezt külön kell amellett feltüntetni.
    • Ami valami viszonylag olcsó ellenőrzéstől, vagy könnyen beszerezhető információtól függ, azt esetleg érdemes a költségbecslés előtt egy feasibility study keretében eldönteni.
    • Amihez hasonló már korábbi projektek során fordult elő, ott az akkori tapasztalatok alapján lehet tippelni, de ez már ingoványos terület
  • Ami ezután marad, az az, amiről már konkrétan fogalmunk sincsen!
Ha ez megvan, akkor már 'csak' az előre nem látható, de szükségszerű tételeket kellene valahogy belevonni. Ezeknek a leggyakoribb okai a következőek, de emellett a korábbi projekteknél előfordult hasonlóakat is célszerű figyelembe venni:
  • Új feature merül fel/válik lehetségessé, ill. az ügyfél új igénnyel áll elő
    Ez a fajta már egy Change Request keretében új megállapodás tárgyát kell, hogy képezze, ami kitér a dolog plusz időigényére is.
  • Valamely tool/3rd-party lib hibája, megszűnése
    Az előzetes tervben használt ilyesmik megbízhatóságának érdemes előre utánanézni.
  • Mi néztünk el valamit
    Hát ennek az esélyét legfeljebb csak módszeres hozzáállással lehet csökkenteni, pl. az előzetes specifikáció és terv elkészítését is egy külön projektként kezelni.
Sajnos mindezek mellett sincsen silver bullet, ami kiküszöbölné a kockázatot, mert mindig, és a magasabb szinteken is lesznek előre nem látott dolgok, amikre időtartalékot kellene becsülni - ez viszont már megintcsak a projekt mérete alapján a korábbi tapasztalatok alapján lehetséges, pl. 'három hónaposra becsült webes projektekre 40% rátartás kell'.

Ennek viszont már több köze van a tőzsdei becslésekhez és a csillagjósláshoz, mint bárminemű módszertanhoz :D.

No comments:

Post a Comment