Poglavlje 14: Kvalitet, verifikacija i isporuka

Priča za početak. Ujutro 1. augusta 2012. godine američka firma Knight Capital, jedan od najvećih posrednika na njujorškoj berzi, pustila je u rad novi program za trgovanje. Program se instalirao ručno, na osam servera. Na sedam je instaliran kako treba, a na jednom nije. Na tom osmom serveru ostao je stari kod koji godinama nije korišten, a novi program je slučajno ponovo upotrijebio jednu oznaku (zastavicu) koja je nekad pokretala baš taj stari kod. Kad je berza otvorena, osmi server je počeo slati ogroman broj pogrešnih naloga za kupovinu i prodaju dionica. Niko nije imao spreman postupak da sistem brzo zaustavi, a nadzor nije jasno pokazao šta se dešava. Za oko 45 minuta firma je izgubila oko 440 miliona dolara i za nekoliko dana morala je prodati veliki dio sebe novim vlasnicima da bi preživjela. Program na sedam servera bio je ispravan. Propao je način isporuke: ručna instalacija bez provjere, stari kod koji niko nije obrisao i nedostatak dugmeta „stani”.

U poglavljima 3 do 13 projektovali smo DeliveryApp: zahtjeve, modele, bazu, arhitekturu, API, ekrane, sigurnost i realno vrijeme. Ovo poglavlje odgovara na posljednje pitanje životnog ciklusa iz poglavlja 2: kako znamo da sistem radi, i kako ga pustimo u rad bez katastrofe? Prvo razlikujemo verifikaciju i validaciju i učimo da su recenzije i testovi dio projektovanja, a ne nešto što se dešava na kraju. Zatim kontinuiranu integraciju, koja pri svakoj izmjeni sama pokrene testove, i kontejnere, koji sistem pakuju tako da radi isto svuda. Na kraju načine isporuke koji smanjuju rizik, nadzor sistema u radu i održavanje. Na praktičnoj nastavi postavljamo GitHub Actions koji pokreće testove DeliveryApp i pakujemo API server u Docker kontejner.

Literatura. Osnovna literatura predmeta:

  1. B. Popović, K. Kuk: Informacioni sistemi, Kriminalističko-policijski univerzitet, Beograd, 2017.
  2. J. S. Valacich, J. F. George: Modern Systems Analysis and Design, 9th ed., Pearson, 2020.
  3. L. Jelenković: Sustavi za rad u stvarnom vremenu – skripta, FER, Zagreb, 2015.
  4. D. Milićev: Programiranje u realnom vremenu – skripta, Beograd, 2002.
  5. V. Kovačević, M. Popović: Sistemska programska podrška u realnom vremenu 1, FTN, Novi Sad, 2013.

Riječi ovog poglavlja.

  • Verifikacija – provjera da li pravimo sistem ispravno, prema specifikaciji.
  • Validacija – provjera da li pravimo pravi sistem, onaj koji korisniku treba.
  • Recenzija koda (code review) – kolega pregleda izmjenu prije nego što uđe u glavni kod.
  • Pull request – zahtjev na GitHubu da se izmjena iz jedne grane spoji u glavnu, uz pregled i automatske provjere.
  • Jedinični test – automatska provjera jedne funkcije ili klase.
  • Piramida testova – mnogo brzih jediničnih testova, manje integracionih, najmanje sporih testova cijelog sistema.
  • Kontinuirana integracija (CI) – svaka izmjena koda automatski se prevede i testira.
  • Kontinuirana isporuka (CD) – izmjena koja prođe provjere automatski je spremna ili puštena u rad.
  • Kontejner – paket koji sadrži program i sve što mu treba, pa radi isto na svakom računaru.
  • Docker slika i Dockerfile – slika je gotov paket; Dockerfile je recept po kojem se slika pravi.
  • Kanarinac isporuka – nova verzija se prvo pusti malom dijelu korisnika, pa postepeno svima.
  • Prekidač funkcije (feature flag) – podešavanje kojim se nova funkcija uključi ili isključi bez nove isporuke.
  • Vraćanje (rollback) – brz povratak na prethodnu ispravnu verziju.
  • Nadzor (monitoring) – stalno praćenje sistema u radu: metrike, logovi i upozorenja.
  • SLO (service level objective) – mjerljiv cilj kvaliteta usluge, npr. dostupnost 99,5 %.

Ciljevi poglavlja. Nakon poglavlja možete: razlikovati verifikaciju i validaciju; objasniti zašto su recenzije najjeftiniji način otkrivanja grešaka; organizovati rad s granama i pull requestom; napisati i pokrenuti jedinične testove za pravila DeliveryApp u pytestu; objasniti piramidu testova; postaviti GitHub Actions koji pokreće testove pri svakoj izmjeni; napisati Dockerfile i pokrenuti API u kontejneru; uporediti isporuku „sve odjednom”, plavo–zelenu i kanarinac isporuku; objasniti prekidač funkcije i vraćanje; navesti četiri zlatna signala nadzora i izračunati budžet grešaka iz SLO-a; nabrojati vrste održavanja i objasniti tehnički dug.

Plan poglavlja – Sedmica 14: tri časa teorije i tri časa praktične nastave

Čas Sadržaj Na času
Teorija 1 14.1–14.3 Verifikacija i validacija; recenzije; testovi Pull request s listom za pregled; 12 testova
Teorija 2 14.4–14.6 Grane; CI; kontejneri GitHub Actions; Dockerfile DeliveryApp
Teorija 3 14.7–14.9 Isporuka; nadzor; održavanje Knight 2012; kanarinac; budžet 3,6 h
Praksa 1–3 pytest, GitHub Actions, Docker Zelena kvačica i kontejner s 24,50 €

Dio 1 – Verifikacija, recenzije i testovi

14.1 Verifikacija i validacija

Dvije riječi zvuče slično, ali pitaju različito. Najpoznatije objašnjenje dao je inženjer Barry Boehm jednim parom pitanja:

  • Verifikacija: Pravimo li sistem ispravno? Da li sistem radi ono što piše u specifikaciji? Da li cijena_dostave(4.2, 2150) vraća 300, kako kaže PP-2?
  • Validacija: Pravimo li pravi sistem? Da li sistem radi ono što korisniku zaista treba? Da li vlasnik restorana, kad vidi DeliveryApp u radu, kaže „to je to”?

Sistem može proći verifikaciju i pasti na validaciji. Sjetite se vlasnika koji je tražio „štampač za narudžbe” (poglavlje 3): mogli smo savršeno napraviti štampanje, a kuhar bi i dalje gubio narudžbe. Verifikacija se radi testovima i recenzijama prema specifikaciji; validacija se radi s korisnicima, prototipima (poglavlje 11), prihvatnim testom i, na kraju, posmatranjem sistema u radu.

V-model iz poglavlja 2 povezuje obje: lijeva strana (zahtjevi, arhitektura, dizajn, kod) ima svoj par na desnoj (prihvatni, sistemski, integracioni, jedinični test). Gornji par je validacija, donji su verifikacija.

Ukratko.

  • Verifikacija pita da li pravimo sistem ispravno, prema specifikaciji.
  • Validacija pita da li pravimo pravi sistem, onaj koji korisniku treba.
  • Sistem može proći verifikaciju i pasti na validaciji.
  • Verifikacija se radi testovima i recenzijama, a validacija s korisnicima i prihvatnim testom.

14.2 Recenzije: najjeftiniji test

Prije nego što ijedan test pokrene kod, greške se mogu naći čitanjem. Recenzija je pregled dokumenta, modela ili koda od strane druge osobe, prema listi pitanja. Već sedamdesetih godina inženjer IBM-a Michael Fagan opisao je formalne inspekcije koda i pokazao da one nalaze velik dio grešaka prije testiranja, kad je ispravka najjeftinija (slika 1.3).

Danas se najčešće radi lakši oblik: recenzija koda kroz pull request. Programer ne mijenja glavni kod direktno, nego predloži izmjenu, a kolega je pročita, komentariše i odobri. Dobra recenzija ima listu za pregled, za DeliveryApp na primjer:

Pitanje Zašto
Da li izmjena ispunjava kriterije prihvatanja priče? validacija na malom (poglavlje 3)
Postoji li test za novo pravilo i za njegove granice? 30,00 € i 29,99 €, 3,0 i 3,1 km
Da li server sam računa cijenu i provjerava vlasništvo? sigurnost (poglavlja 9 i 12)
Da li je SQL parametrizovan? injekcija (poglavlje 12)
Da li se poruka greške prevodi na jezik korisnika? UX (poglavlje 11)
Da li je obrisan kod koji se više ne koristi? Knight Capital 2012
Da li su dokumenti i modeli dopunjeni? usklađenost (poglavlje 7)

Recenzija nije samo traženje grešaka. Ona širi znanje: bar dvoje ljudi zna svaki dio koda, pa tim ne zavisi od jednog čovjeka. I recenzira se izmjena, a ne osoba: komentar „ova funkcija ne provjerava granicu od 10 km” pomaže, a „opet si zaboravio” ne pomaže.

Ukratko.

  • Recenzija je pregled dokumenta, modela ili koda od druge osobe, prema listi pitanja.
  • Fagan je pokazao da inspekcije nalaze mnogo grešaka prije testiranja, kad su najjeftinije.
  • Danas se kod recenzira kroz pull request, s listom za pregled specifičnom za projekat.
  • Recenzija širi znanje u timu i ocjenjuje izmjenu, a ne osobu.

14.3 Testovi i piramida testova

Testiranje je posebna disciplina s vlastitim predmetom (Testiranje softvera), pa ovdje uzimamo samo ono što projektant mora znati.

Nivoi testiranja prate V-model:

Nivo Šta provjerava Primjer u DeliveryApp
jedinični jednu funkciju ili klasu cijena_dostave(4.2, 2150) == 300
integracioni saradnju dijelova API + baza: POST /v1/narudzbe upisuje red u tabelu
sistemski cijeli sistem, i nefunkcionalne zahtjeve 200 korisnika, 95 % odgovora ≤ 2 s (NZ-1)
prihvatni ono što korisnik traži vlasnik R-1 prođe cijeli tok narudžbe i potvrdi

Koliko testova kojeg nivoa? Pravilo se crta kao piramida testova: u podnožju mnogo jediničnih testova (brzi su, hiljade se pokrenu za nekoliko sekundi, i tačno pokazuju gdje je greška), u sredini manje integracionih, a na vrhu malo sporih testova cijelog sistema kroz korisnički interfejs. Obrnuta piramida (mnogo sporih testova kroz ekran, malo jediničnih) je čest i skup problem: testovi traju satima, često padaju bez stvarnog razloga, i niko ih ne voli pokretati.

Jedinični testovi DeliveryApp u pytestu (fajl primjeri/deliveryapp/test_deliveryapp.py):

import pytest
from deliveryapp import Narudzba, cijena_dostave, popust, ukupno

def test_n1001_ukupno():
    assert ukupno([(950, 2), (250, 1)], 4.2) == 2450

@pytest.mark.parametrize("km, ocekivano", [(0, 200), (3, 200), (3.1, 250), (4.2, 300), (10, 550)])
def test_cijena_dostave(km, ocekivano):
    assert cijena_dostave(km, 1000) == ocekivano

def test_besplatna_od_30_eura():
    assert cijena_dostave(5, 3000) == 0
    assert cijena_dostave(5, 2999) == 300

def test_izvan_zone():
    with pytest.raises(ValueError):
        cijena_dostave(11, 1000)

def test_otkazivanje_u_pripremi_nije_dozvoljeno():
    n = Narudzba("N-1001")
    n.promijeni("PRIHVAĆENA")
    n.promijeni("U_PRIPREMI")
    with pytest.raises(ValueError):
        n.promijeni("OTKAZANA")

Svaki test je jedan kriterij prihvatanja iz poglavlja 3, pretočen u kod: N-1001 košta 24,50 €; granica od 3 km i 30,00 €; 11 km je izvan zone; pravilo PP-6. Naredba pytest -v pokreće cijeli fajl, a rezultat je 12 testova koji prolaze za manje od sekunde (dekorator parametrize od jednog testa pravi pet, za pet udaljenosti).

Ovo je trenutak kad se vidi vrijednost svih prethodnih poglavlja: dobri zahtjevi s brojevima i granicama gotovo se sami pretvaraju u testove.

Slika 14.1 – Piramida testova DeliveryApp

Ukratko.

  • Nivoi testiranja su jedinični, integracioni, sistemski i prihvatni, kao u V-modelu.
  • Piramida testova: mnogo brzih jediničnih, manje integracionih, malo sporih testova cijelog sistema.
  • Svaki jedinični test DeliveryApp je kriterij prihvatanja iz poglavlja 3 pretočen u kod.
  • Dvanaest testova u pytestu prolazi za manje od sekunde.

Provjeri se.

  1. Koje pitanje postavlja validacija? – Pravimo li pravi sistem, onaj koji korisniku treba?
  2. Zašto se recenzija radi kroz pull request? – Da kolega pregleda izmjenu prije nego što uđe u glavni kod, uz automatske provjere.
  3. Kojih testova treba biti najviše? – Jediničnih, jer su brzi i tačno pokazuju gdje je greška.

Dio 2 – Kontinuirana integracija i kontejneri

14.4 Rad s granama

U timu od četiri čovjeka svi mijenjaju isti kod. Da se ne bi gazili, koriste se grane u Gitu: odvojene linije razvoja. Jednostavan i raširen način rada (često nazvan GitHub flow):

  1. Glavna grana main uvijek sadrži kod koji radi i može se isporučiti.
  2. Za svaku stavku backloga pravi se nova grana, npr. popust-student10.
  3. Na grani se radi i često pravi commit.
  4. Kad je stavka gotova, otvara se pull request prema main.
  5. Automatske provjere (CI, 14.5) pokrenu testove; kolega radi recenziju.
  6. Tek kad su testovi zeleni i recenzija odobrena, grana se spaja u main.

Na GitHubu se main može zaštititi: postavka branch protection zabranjuje direktan commit i spajanje dok testovi ne prođu. Tako pravilo „ništa ne ulazi bez testa i pregleda” čuva alat, a ne samo dobra volja.

Ukratko.

  • Grane odvajaju rad članova tima; main uvijek sadrži kod koji radi.
  • Svaka stavka backloga dobija granu, a završava pull requestom s testovima i recenzijom.
  • Zaštita grane main na GitHubu sprečava spajanje dok testovi ne prođu.

14.5 Kontinuirana integracija

Kontinuirana integracija (CI, continuous integration) znači da se pri svakoj izmjeni koda automatski pokrenu prevođenje i testovi. Ne „prije isporuke”, ne „kad neko stigne”, nego svaki put. Ako neko pokvari pravilo PP-4, test pada za par minuta, na njegovoj grani, prije nego što izmjena uđe u main.

Na GitHubu se CI postavlja kroz GitHub Actions: fajl u formatu YAML, u folderu .github/workflows/, koji opisuje šta se pokreće i kada. Za DeliveryApp (fajl primjeri/ci/ci.yml):

name: CI
on:
  push:
    branches: [main]
  pull_request:
jobs:
  testovi:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install pytest fastapi httpx
      - run: pytest -v

Pročitajmo ga red po red: kada (pri svakom push na main i pri svakom pull requestu), gdje (na virtuelnom računaru s Ubuntu Linuxom koji GitHub sam pokrene), šta (preuzmi kod, postavi Python 3.12, instaliraj biblioteke, pokreni pytest). Ako svi testovi prođu, pored commita se pojavi zelena kvačica; ako ne, crveni krstić i link na test koji je pao.

Primijetite da je ovo ista ideja kao odbrana u dubinu iz poglavlja 8 i 12: pravilo čuva kod, čuva ga baza, a sada ga čuva i proces, jer izmjena koja ga krši ne može ući u glavnu granu.

Česta zabluda. „CI je za velike firme.” Postavljanje CI-ja za DeliveryApp je petnaestak redova teksta i deset minuta posla, a besplatno je za javne repozitorije. Za mali tim CI je još važniji, jer nema posebnog tima testera koji bi grešku uhvatio kasnije.

Ukratko.

  • Kontinuirana integracija pri svakoj izmjeni automatski pokreće prevođenje i testove.
  • GitHub Actions opisuje se YAML fajlom: kada, gdje i šta se pokreće.
  • Za DeliveryApp CI pokreće pytest pri svakom push na main i svakom pull requestu.
  • Zelena kvačica znači da svi testovi prolaze, a crveni krstić vodi do testa koji je pao.

14.6 Kontejneri

„Kod mene radi” je možda najpoznatija rečenica u softveru. Program radi na računaru programera, a na serveru ne radi, jer je tamo druga verzija Pythona, nedostaje biblioteka ili je drugačije podešavanje. Kontejner rješava taj problem tako što program zapakuje zajedno sa svim što mu treba: verzijom jezika, bibliotekama i podešavanjima. Kontejner tada radi isto na laptopu, na serveru u učionici i u oblaku.

Slika iz života: brodski kontejner. Prije standardnih kontejnera, luke su teret utovarivale komad po komad, i svaka luka je imala svoje dizalice. Kad je sav teret počeo putovati u kutijama iste veličine, ista dizalica podiže kontejner s bananama i kontejner s računarima. Softverski kontejner je ista ideja: platforma ne mora znati šta je unutra, samo kako se kutija pokreće.

Najpoznatiji alat je Docker. Dva pojma:

  • Dockerfile je recept: tekst koji kaže od čega se kreće i šta se dodaje.
  • Slika (image) je gotov paket napravljen po receptu. Kad se slika pokrene, nastaje kontejner.

Dockerfile za API DeliveryApp (primjeri/deliveryapp/Dockerfile):

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY deliveryapp.py api.py ./
EXPOSE 8000
CMD ["uvicorn", "api:app", "--host", "0.0.0.0", "--port", "8000"]

Red po red: kreni od male slike s Pythonom 3.12; radni folder je /app; kopiraj spisak biblioteka i instaliraj ih; kopiraj kod; server sluša na portu 8000; pri pokretanju pokreni uvicorn s našim API-jem. Spisak biblioteka requirements.txt ima tačne verzije (fastapi==0.115.0), da bi slika napravljena danas i slika napravljena za šest mjeseci bile iste.

Pravljenje i pokretanje:

docker build -t deliveryapp-api .
docker run --rm -p 8000:8000 deliveryapp-api

Poslije toga isti zahtjev iz poglavlja 10 (POST /v1/cijena za N-1001) vraća iz kontejnera isti odgovor: "ukupno": 2450. Kontejneri su i osnova horizontalnog skaliranja iz poglavlja 9: dva servera iza balansera su dva kontejnera iz iste slike, pa su sigurno isti.

Ukratko.

  • Kontejner pakuje program sa svim što mu treba, pa radi isto svuda; „kod mene radi” prestaje biti problem.
  • Dockerfile je recept, slika je gotov paket, a pokrenuta slika je kontejner.
  • Dockerfile DeliveryApp kreće od Pythona 3.12, instalira tačne verzije biblioteka i pokreće API na portu 8000.
  • Više servera iza balansera su kontejneri iz iste slike.

Provjeri se.

  1. Šta se dešava s pull requestom ako CI test padne? – Ne smije se spojiti u main dok se greška ne popravi.
  2. Koja je razlika između Dockerfilea i slike? – Dockerfile je recept, a slika je gotov paket napravljen po njemu.
  3. Zašto requirements.txt ima tačne verzije? – Da slika napravljena danas i kasnije bude ista.

Dio 3 – Isporuka, nadzor i održavanje

14.7 Kako se sistem pušta u rad

Izmjena koja je prošla testove i recenziju treba stići do korisnika. Način na koji se to radi je projektna odluka, i ona odlučuje koliko će boljeti ako nešto ipak nije u redu.

Način isporuke Kako izgleda Rizik Povratak
Sve odjednom, ručno instalira se na svaki server po uputstvu jedan zaboravljen server (Knight 2012) spor, ručan
Postepeno, server po server (rolling) jedan po jedan server dobija novu verziju kratko rade dvije verzije zajedno srednje brz
Plavo–zeleno (blue–green) nova verzija se podigne pored stare; balanser prebaci saobraćaj odjednom dupli resursi kratko vrijeme trenutan: balanser vrati na staru
Kanarinac (canary) nova verzija ide prvo 1 % korisnika, pa 10 %, pa svima mali, jer grešku vidi mali broj ljudi brz, prije nego što je pogođeno mnogo

Ime kanarinac dolazi iz rudnika: rudari su nekad u okna nosili kanarince, jer ptica prva osjeti otrovni gas. Ako kanarinac prestane pjevati, rudari izlaze. U isporuci, 1 % korisnika je kanarinac: ako se broj grešaka kod njih poveća, isporuka se zaustavlja prije nego što pogodi ostale.

Slika 14.2 – Kanarinac isporuka: 1 %, 10 %, 50 % pa 100 % korisnika, uz zaustavljanje ako greške porastu

Iz stvarnog svijeta. Dana 19. jula 2024. firma CrowdStrike poslala je jednu pogrešnu konfiguracionu datoteku za svoj sigurnosni softver na Windows računare, i to svim korisnicima istovremeno. Pogođeni računari, prema procjeni Microsofta oko 8,5 miliona, nisu se mogli pokrenuti; stajali su aerodromi, banke i bolnice. Jedna od najčešćih pouka koje su stručnjaci tada izvukli bila je upravo ova: i male izmjene treba puštati postepeno, prvo malom broju računara, s mogućnošću da se isporuka zaustavi.

Dva alata smanjuju rizik bez obzira na način isporuke:

  • Prekidač funkcije (feature flag): nova funkcija se isporuči, ali je isključena podešavanjem; uključi se kad tim odluči, prvo za neke korisnike. Ako pravi problem, isključi se za sekundu, bez nove isporuke. Pažnja: Knightov slučaj pokazuje i opasnost: stara, zaboravljena zastavica s novim značenjem probudila je mrtav kod. Zato se prekidači brišu kad više nisu potrebni.
  • Vraćanje (rollback): uvijek postoji uvježban, po mogućnosti automatski, način da se za nekoliko minuta vrati prethodna verzija. Kod kontejnera to je pokretanje prethodne slike. Dugme „stani” koje je Knightu falilo.

Ukratko.

  • Isporuka može biti sve odjednom, postepeno, plavo–zeleno ili kanarinac.
  • Kanarinac isporuka prvo pogodi mali dio korisnika, pa se greška vidi prije nego što pogodi sve.
  • CrowdStrike 2024. i Knight Capital 2012. pokazuju cijenu isporuke svima odjednom i bez provjere.
  • Prekidač funkcije isključuje funkciju bez nove isporuke, ali stari prekidači se moraju brisati.
  • Vraćanje na prethodnu verziju mora biti uvježbano i brzo.

14.8 Nadzor sistema u radu

Kad je DeliveryApp u radu, tim mora vidjeti šta se dešava, prije nego što kupci počnu zvati. To je nadzor (monitoring), i ima tri izvora:

  1. Metrike – brojevi kroz vrijeme: broj zahtjeva u sekundi, trajanje odgovora, broj grešaka, zauzeće procesora.
  2. Logovi – zapisi događaja: „N-1001 prešla u PRIHVAĆENA”, „servis mapa nije odgovorio za 10 s”.
  3. Upozorenja – pravila koja nekoga probude kad metrika pređe granicu: „više od 2 % zahtjeva vraća grešku 5xx u posljednjih 5 minuta”.

Inženjeri Googlea u knjizi o pouzdanosti sistema (Site Reliability Engineering) predlažu da se za svaku uslugu prate bar četiri zlatna signala:

Signal Pitanje Za DeliveryApp
kašnjenje koliko traju odgovori? da li je 95 % odgovora ispod 2 s (NZ-1)?
saobraćaj koliko zahtjeva dolazi? oko 40 zahtjeva u sekundi u podne (poglavlje 9)
greške koliko zahtjeva ne uspije? udio odgovora 5xx; udio odbijenih plaćanja
zasićenje koliko je sistem pun? zauzeće procesora i memorije, red poruka koji raste

SLO i budžet grešaka

Nefunkcionalni zahtjevi iz poglavlja 3 u radu postaju ciljevi nivoa usluge (SLO, service level objective): NZ-1 i NZ-2 su upravo to. Korisna posljedica je budžet grešaka: ako je cilj dostupnost 99,5 %, onda je 0,5 % vremena dozvoljeno „potrošiti” na zastoje. Za mjesec od 30 dana to je 0,005 × 720 h = 3,6 sati (isti broj kao u poglavlju 3).

Budžet grešaka pomaže timu da odluči: ako je ovog mjeseca potrošeno tek 20 minuta, tim smije isporučiti rizičniju novu funkciju; ako je potrošeno 3 sata, isporuke se usporavaju dok se sistem ne stabilizuje. Tako se brzina razvoja i pouzdanost vagaju brojem, a ne raspravom.

Slika 14.3 – Nadzorna ploča DeliveryApp: četiri zlatna signala i budžet grešaka

Ukratko.

  • Nadzor ima tri izvora: metrike, logove i upozorenja.
  • Četiri zlatna signala su kašnjenje, saobraćaj, greške i zasićenje.
  • SLO je mjerljiv cilj usluge, kao NZ-1 i NZ-2.
  • Budžet grešaka za 99,5 % je 3,6 sati mjesečno i pomaže odlučiti kada smije rizičnija isporuka.

14.9 Održavanje i tehnički dug

Poglavlje 2 je reklo da održavanje najčešće traje najduže. Održavanje nije samo popravljanje grešaka; postoje četiri vrste:

Vrsta Šta je Primjer u DeliveryApp
korektivno ispravka greške dostava za 3,1 km bila je 2,00 € umjesto 2,50 €
adaptivno prilagođavanje promjeni okoline nova verzija Androida; nova pravila za kartice; promjena zakona
perfektivno poboljšanje i nove funkcije „Ponovi posljednju narudžbu”
preventivno sprečavanje budućih problema ažuriranje biblioteka (Equifax), brisanje mrtvog koda (Knight)

Posljednja vrsta vezana je za pojam tehničkog duga. Kad tim uradi nešto brzo i „privremeno” (preskoči test, kopira funkciju umjesto da je doradi, ostavi stari kod „za svaki slučaj”), uzima dug: danas štedi vrijeme, ali sutra plaća kamatu, jer svaka sljedeća izmjena traje duže i nosi veći rizik. Malo duga je normalno i ponekad pametno (npr. da se stigne na sezonu). Problem je dug koji se nikad ne vraća. Zato dobri timovi u svaki sprint stavljaju i stavke vraćanja duga, i zapisuju svjesne prečice kao zadatke u backlogu, a ne kao komentar „TODO” koji niko ne čita.

Knight Capital je platio upravo kamatu na stari tehnički dug: kod koji godinama niko nije koristio, ali ga niko nije ni obrisao.

Ukratko.

  • Održavanje je korektivno, adaptivno, perfektivno i preventivno.
  • Tehnički dug je brza prečica danas koja sutra traži kamatu u vidu sporijih i rizičnijih izmjena.
  • Dug se zapisuje u backlog i redovno vraća, a mrtav kod se briše.
  • Knight Capital 2012. platio je kamatu na star, neobrisan kod.

Provjeri se.

  1. Zašto se kanarinac isporuka tako zove? – Po pticama u rudnicima, koje su prve osjetile otrovni gas; mali dio korisnika prvi „osjeti” grešku.
  2. Koliki je mjesečni budžet grešaka za SLO 99,5 %? – 3,6 sati u 30 dana.
  3. Kojoj vrsti održavanja pripada ažuriranje biblioteka? – Preventivnom.

Praktična nastava – testovi, CI i kontejner

Cilj je da repozitorij deliveryapp-projekat dobije automatske testove, CI koji ih pokreće pri svakoj izmjeni, zaštićenu glavnu granu i Docker sliku API servera.

Korak 1 – Testovi u pytestu

  1. U kod/ kopirajte test_deliveryapp.py iz primjeri/deliveryapp/. Instalirajte pytest (pip install pytest) i pokrenite:
pytest -v

Očekivano: 12 passed.

  1. Namjerno pokvarite pravilo: u deliveryapp.py promijenite iznos >= BESPLATNA_OD u iznos > BESPLATNA_OD i ponovo pokrenite testove. Koji test pada i zašto? (test_besplatna_od_30_eura, jer za tačno 30,00 € dostava više nije besplatna.) Vratite ispravku.
  2. Dodajte dva testa po svom izboru iz kriterija prihvatanja (poglavlje 3), npr. za N-1002 (32,00 €) i za kod napisan malim slovima.

Korak 2 – GitHub Actions

  1. U repozitoriju napravite folder .github/workflows/ i u njega kopirajte ci.yml iz primjeri/ci/. Ako su testovi u folderu kod/, dodajte u posljednji korak working-directory: kod.
  2. Napravite commit i git push. Na GitHubu otvorite karticu Actions i pratite kako se testovi pokreću. Kad završe, pored commita se pojavi zelena kvačica.

Korak 3 – Grana, pull request i zaštita

  1. U postavkama repozitorija (Settings → Branches → Add branch ruleset ili branch protection rule) zaštitite granu main: zabranite direktan push i zahtijevajte da provjera „CI / testovi” prođe prije spajanja.
  2. Napravite granu test-n1002 (git checkout -b test-n1002), dodajte test za N-1002 i pošaljite granu (git push -u origin test-n1002).
  3. Na GitHubu otvorite pull request prema main. Sačekajte zelenu kvačicu, zamolite kolegu iz grupe za recenziju po listi iz 14.2, i tek onda spojite.
  4. Za vježbu otvorite i drugi pull request s namjerno pokvarenim testom: GitHub neće dozvoliti spajanje dok je provjera crvena.

Korak 4 – Docker

  1. Ako Docker Desktop nije instaliran, instalirajte ga (docker.com); u učionici je već instaliran.
  2. U kod/ kopirajte Dockerfile i requirements.txt iz primjeri/deliveryapp/.
  3. Napravite i pokrenite sliku:
docker build -t deliveryapp-api .
docker run --rm -p 8000:8000 deliveryapp-api
  1. U drugom terminalu pošaljite zahtjev iz poglavlja 10:
http POST :8000/v1/cijena kupac=K-01 udaljenost_km:=4.2 stavke:='[{"jelo_id":1,"kolicina":2},{"jelo_id":3,"kolicina":1}]'

Očekivano: "ukupno": 2450, isto kao bez kontejnera. 5. Zaustavite kontejner (Ctrl+C) i pogledajte koliko je slika velika (docker images).

Korak 5 – Plan isporuke i nadzora

U arhitektura/isporuka.md napišite kratak plan:

  • način isporuke (predlog: kanarinac 10 % pa 100 %, uz kontejnere iza balansera) i obrazloženje;
  • tri metrike i dva upozorenja s brojevima (npr. „95. percentil trajanja odgovora > 2 s duže od 5 minuta”);
  • postupak vraćanja u pet koraka;
  • budžet grešaka za NZ-2 i pravilo kada se isporuke usporavaju.

Korak 6 – AI kao recenzent (opcionalno)

Zatražite od AI asistenta recenziju jednog svog pull requesta (kopirajte samo izmjenu, bez lozinki i ključeva) po listi iz 14.2. Uporedite s recenzijom kolege. Koje je primjedbe dao samo AI, a koje samo čovjek? Zatražite i prijedlog dodatnih testova; svaki predloženi test pokrenite i zadržite samo one koji provjeravaju stvarno pravilo DeliveryApp.

Rezultat koji se predaje: link na repozitorij u kojem se vidi zelena provjera CI-ja, zaštićena grana main i bar jedan spojeni pull request s recenzijom; kod/Dockerfile i zapis odgovora iz kontejnera; arhitektura/isporuka.md.

Vježbe

Vježba za studenta 1 – verifikacija ili validacija.

  1. Test provjerava da je dostava za 3,1 km 2,50 €. b) Pet kupaca isprobaju prototip i kažu da ne nalaze dugme za korpu. c) Kolega u recenziji primijeti da funkcija ne provjerava granicu od 10 km. d) Vlasnik R-1 nakon mjesec dana kaže da mu izvještaj ne pokazuje ono što mu treba za nabavku.

Rješenje. a) verifikacija; b) validacija (korisnik ne može postići cilj); c) verifikacija (prema specifikaciji PP-3); d) validacija (sistem ne radi ono što korisniku treba).

Vježba za studenta 2 – nivo testa.

Na kojem nivou je test? a) popust(8000, "STUDENT10") == 500; b) poslije POST /v1/narudzbe u tabeli narudzba postoji novi red; c) 200 simuliranih korisnika naručuje 10 minuta, a mjeri se trajanje odgovora; d) vlasnik restorana prođe cijeli dan rada sa DeliveryApp.

Rješenje. a) jedinični; b) integracioni (API i baza zajedno); c) sistemski (nefunkcionalni zahtjev NZ-1); d) prihvatni.

Vježba za studenta 3 – čitanje CI fajla.

U fajlu iz 14.5 promijenjeno je on: tako da sadrži samo push: branches: [main]. Šta se gubi?

Rješenje. Testovi se više ne pokreću za pull requestove, nego tek kad je izmjena već spojena u main. Greška se tada otkrije poslije spajanja, a ne prije, i zaštita grane ne može zahtijevati zelenu provjeru za pull request. Gubi se glavna korist CI-ja: da pokvaren kod uopšte ne uđe u main.

Vježba za studenta 4 – kanarinac.

DeliveryApp ima 2000 aktivnih kupaca dnevno. Nova verzija ima grešku koja pogađa svakog kupca koji je koristi. Koliko je kupaca pogođeno ako se greška primijeti poslije jednog sata, uz isporuku: a) svima odjednom; b) kanarinac 5 %? (Pretpostavite da je u tom satu aplikaciju koristila osmina dnevnih kupaca.)

Rješenje. U tom satu aplikaciju koristi 2000 / 8 = 250 kupaca. a) Svi koriste novu verziju: 250 pogođenih. b) Samo 5 % ih koristi novu verziju: 0,05 × 250 = 12,5, dakle oko 12 do 13 pogođenih, dvadeset puta manje.

Vježba za studenta 5 – budžet grešaka.

SLO je 99,9 % dostupnosti mjesečno (30 dana). U prvih 20 dana bila su dva zastoja, od 18 i 15 minuta. Koliko je budžeta ostalo? Šta biste preporučili za isporuke do kraja mjeseca?

Rješenje. Budžet: 0,001 × 720 h = 0,72 h = 43,2 minute. Potrošeno 18 + 15 = 33 minute; ostalo 43,2 − 33 = 10,2 minute. Preporuka: odgoditi rizične isporuke do kraja mjeseca, isporučivati samo ispravke uz kanarinac i provjeriti uzroke dva zastoja.

Sažetak u pet rečenica

  1. Verifikacija pita da li pravimo sistem ispravno, a validacija da li pravimo pravi sistem, i oba pitanja se postavljaju kroz cijeli životni ciklus.
  2. Recenzije kroz pull request s listom za pregled najjeftinije nalaze greške, a piramida testova traži mnogo brzih jediničnih testova, kao dvanaest testova DeliveryApp koji su kriteriji prihvatanja pretočeni u kod.
  3. Kontinuirana integracija kroz GitHub Actions pokreće testove pri svakoj izmjeni, a zaštićena grana main ne prima izmjenu dok provjera nije zelena.
  4. Docker kontejner pakuje API sa svim što mu treba, pa isti zahtjev za N-1001 i iz kontejnera vraća 24,50 €, a ista slika pokreće sve servere iza balansera.
  5. Knight Capital 2012. i CrowdStrike 2024. pokazuju cijenu isporuke svima odjednom, pa se isporučuje postepeno, uz prekidače funkcija, uvježbano vraćanje, nadzor četiri zlatna signala, budžet grešaka i redovno vraćanje tehničkog duga.

Pitanja za ponavljanje

  1. Šta se desilo Knight Capitalu 1. augusta 2012. i koje su pouke za isporuku?
  2. Koja je razlika između verifikacije i validacije? Dajte primjer za svaku.
  3. Šta je recenzija koda i šta sadrži dobra lista za pregled?
  4. Navedite nivoe testiranja i primjer za svaki iz DeliveryApp.
  5. Šta je piramida testova i zašto je obrnuta piramida problem?
  6. Kako se kriterij prihvatanja pretvara u jedinični test? Dajte primjer.
  7. Opišite rad s granama i pull requestom.
  8. Šta je kontinuirana integracija i šta radi fajl ci.yml za DeliveryApp?
  9. Šta je kontejner i koji problem rješava? Objasnite Dockerfile DeliveryApp.
  10. Uporedite isporuku sve odjednom, plavo–zelenu i kanarinac isporuku.
  11. Šta je prekidač funkcije i koja je njegova opasnost?
  12. Koja su četiri zlatna signala nadzora?
  13. Šta je SLO i kako se računa budžet grešaka?
  14. Navedite četiri vrste održavanja i objasnite tehnički dug.

Odgovori na pitanja za ponavljanje.

  1. Novi program za trgovanje ručno je instaliran na osam servera, ali na jednom nije; tamo je stari, neobrisan kod ponovo pokrenut preko ponovo upotrijebljene oznake i za oko 45 minuta poslao pogrešne naloge, uz gubitak od oko 440 miliona dolara. Pouke: automatska i provjerena isporuka, brisanje mrtvog koda, brz način zaustavljanja i vraćanja, i nadzor koji odmah pokaže problem.
  2. Verifikacija provjerava da li sistem radi prema specifikaciji (test da je dostava za 3,1 km 2,50 €). Validacija provjerava da li sistem radi ono što korisniku treba (pet kupaca na testu prototipa ne nalazi korpu).
  3. Recenzija koda je pregled izmjene od strane kolege prije nego što uđe u glavni kod, najčešće kroz pull request. Dobra lista za pregled pita za kriterije prihvatanja, testove i granice, sigurnost (cijena na serveru, vlasništvo, parametrizovan SQL), poruke korisniku, obrisan mrtav kod i dopunjene dokumente.
  4. Jedinični (cijena_dostave(4.2, 2150) == 300), integracioni (POST /v1/narudzbe upisuje red u bazu), sistemski (200 korisnika, 95 % odgovora ispod 2 s) i prihvatni (vlasnik R-1 prođe cijeli tok i potvrdi).
  5. Piramida testova kaže da treba mnogo brzih jediničnih testova, manje integracionih i malo sporih testova cijelog sistema. Obrnuta piramida je problem jer spori testovi kroz ekran traju dugo, često padaju bez stvarnog razloga i slabo pokazuju gdje je greška.
  6. Kriterij s brojevima postaje provjera u kodu. Npr. „artikli 21,50 €, adresa 4,2 km, ukupno 24,50 €” postaje assert ukupno([(950, 2), (250, 1)], 4.2) == 2450.
  7. Grana main uvijek sadrži kod koji radi; za svaku stavku pravi se nova grana; kad je gotova, otvara se pull request prema main; CI pokrene testove i kolega radi recenziju; tek sa zelenim testovima i odobrenjem grana se spaja.
  8. Kontinuirana integracija pri svakoj izmjeni automatski pokreće prevođenje i testove. Fajl ci.yml kaže da se pri svakom push na main i svakom pull requestu na Ubuntu računaru preuzme kod, postavi Python 3.12, instaliraju biblioteke i pokrene pytest.
  9. Kontejner pakuje program sa svim što mu treba (jezik, biblioteke, podešavanja), pa radi isto svuda i rješava problem „kod mene radi”. Dockerfile DeliveryApp kreće od slike s Pythonom 3.12, instalira biblioteke iz requirements.txt s tačnim verzijama, kopira deliveryapp.py i api.py, otvara port 8000 i pokreće uvicorn.
  10. Sve odjednom je najjednostavnije, ali greška pogađa sve i povratak je spor. Plavo–zeleno podiže novu verziju pored stare i balanser prebaci saobraćaj, s trenutnim povratkom. Kanarinac prvo pušta novu verziju malom dijelu korisnika, pa postepeno svima, tako da grešku vidi mali broj ljudi.
  11. Prekidač funkcije je podešavanje kojim se isporučena funkcija uključi ili isključi bez nove isporuke. Opasnost su stari, zaboravljeni prekidači koji dobiju novo značenje i probude star kod, kao kod Knight Capitala; zato se brišu kad više ne trebaju.
  12. Kašnjenje (koliko traju odgovori), saobraćaj (koliko zahtjeva dolazi), greške (koliko zahtjeva ne uspije) i zasićenje (koliko je sistem pun).
  13. SLO je mjerljiv cilj nivoa usluge, npr. dostupnost 99,5 %. Budžet grešaka je dozvoljeni dio vremena bez usluge: (1 − 0,995) × 720 h = 3,6 sati mjesečno.
  14. Korektivno (ispravka greške), adaptivno (prilagođavanje promjeni okoline), perfektivno (poboljšanja i nove funkcije) i preventivno (sprečavanje budućih problema). Tehnički dug je brza prečica danas koja kasnije traži kamatu u vidu sporijih i rizičnijih izmjena; zapisuje se i redovno vraća.