Dio 1 – Verifikacija i validacija
Dio 2 – CI/CD i kontejneri
Dio 3 – Isporuka, nadzor, održavanje
Praksa – GitHub Actions; Dockerfile
Materijale priredio Erkan Islamović
erkan.islamovic@eubd.edu.ba · erkan.islamovic@gmail.com
Petnaest sedmica, svaka s tri časa teorije i tri časa prakse. Danas: poglavlje 14 (Sedmica 14, označeno). Isti primjer kroz knjigu: DeliveryApp, narudžba N-1001 (24,50 €).
| Sed. | Poglavlje | Sed. | Poglavlje |
|---|---|---|---|
| 1 | P1 Zašto projektujemo | 9 | P9 Arhitektura |
| 2 | P2 Životni ciklus | 10 | P10 Integracija i API |
| 3 | P3 Zahtjevi | 11 | P11 Korisnički interfejs |
| 4 | P4 Poslovni procesi | 12 | P12 Sigurnost i privatnost |
| 5 | P5 UML i slučajevi korištenja | 13 | P13 Realno vrijeme i IoT |
| 6 | P6 Klase i objekti | 14 | P14 Kvalitet i isporuka |
| 7 | P7 Sekvence i stanja | 15 | P15 AI i završni projekat |
| 8 | P8 Baza podataka |
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.
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.
| Č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 € |
Dvije riječi zvuče slično, ali pitaju različito. Najpoznatije objašnjenje dao je inženjer Barry Boehm jednim parom pitanja:
cijena_dostave(4.2, 2150) vraća 300, kako kaže PP-2?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.
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.
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.

Odgovori: tipka R ili klik na zamućeno polje.
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):
main uvijek sadrži kod koji radi i može se isporučiti.popust-student10.main.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.
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 -vProč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.
„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.
„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 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-apiPoslije 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.
Odgovori: tipka R ili klik na zamućeno polje.
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.

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:
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:
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 |
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.

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.
Odgovori: tipka R ili klik na zamućeno polje.
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.
kod/ kopirajte test_deliveryapp.py iz primjeri/deliveryapp/. Instalirajte pytest (pip install pytest) i pokrenite:pytest -vOčekivano: 12 passed.
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..github/workflows/ i u njega kopirajte ci.yml iz primjeri/ci/. Ako su testovi u folderu kod/, dodajte u posljednji korak working-directory: kod.git push. Na GitHubu otvorite karticu Actions i pratite kako se testovi pokreću. Kad završe, pored commita se pojavi zelena kvačica.main: zabranite direktan push i zahtijevajte da provjera „CI / testovi” prođe prije spajanja.test-n1002 (git checkout -b test-n1002), dodajte test za N-1002 i pošaljite granu (git push -u origin test-n1002).main. Sačekajte zelenu kvačicu, zamolite kolegu iz grupe za recenziju po listi iz 14.2, i tek onda spojite.kod/ kopirajte Dockerfile i requirements.txt iz primjeri/deliveryapp/.docker build -t deliveryapp-api .
docker run --rm -p 8000:8000 deliveryapp-apihttp 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.
docker images).U arhitektura/isporuka.md napišite kratak plan:
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.
Rješenja su sakrivena – tipka R ili klik na okvir.
a) 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.
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).
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.
a) jedinični; b) integracioni (API i baza zajedno); c) sistemski (nefunkcionalni zahtjev NZ-1); d) prihvatni.
U fajlu iz 14.5 promijenjeno je on: tako da sadrži samo push: branches: [main]. Šta se gubi?
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.
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.)
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.
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?
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.