AI-DEV 2026 · Radionica 4 · Coinis Hub, Podgorica

Backend
i AI osnove

Teorija: jedna obična aplikacija, od prototipa do stotina zahtjeva u sekundi, blok po blok. Praksa: blok jedan, najobičniji backend za vaš projekat, sa agentom.

Pavle Džuverovićpavle@coinis.com24. oktobar 2026.
→ sljedeći korakO pregledF cijeli ekran (sakriva komande)T svijetla temaH sakrij komande
O predavaču

Pavle Džuverović

Skoro dvije godine u Coinis-u. Počeo kao full stack developer, brzo prešao u mediora, a od nedavno Agentic Product Lead, Growth Products.

Claude Certified Architect – Foundations
Sertifikat · AnthropicClaude Certified Architect
Foundations
credly.com/badges/4b305bf6…
Pavle Džuverović
LinkedInlinkedin.com/in/pavledzuverovicpavle@coinis.com
Backend nekad i danas

Šta je radio junior, šta se očekuje danas

Nekad · u već dobro postavljenom projektu

Neko je sjedio i pisao boilerplate: DTO, mapiranja, konfiguraciju, isti CRUD po deseti put.

Neko je sjedio i pisao mail template. Za svaki servis. Ručno.

Neko je dobio najnebitniji read endpoint na najobičnijoj aplikaciji i to mu je bio dan. Ništa teško, ništa zanimljivo, i to je bio posao.

Danas · u dobro postavljenom agentskom projektu

O tome više ne razmišljamo. Boilerplate, template, endpoint: agent, sat vremena, pročitate diff.

MVP i prototip su lagani. Za par dana imate zategnut prototip koji izgleda savršeno i radi za vas, za deset ljudi.

Pitanje koje ostaje: hoće li to izdržati kad se otvori?

Ko za sebe kaže backend, full stack, software engineer: ne vrednuje ga se više po tome koliko brzo kuca, nego po tome šta odluči da se napravi i da li to podnese opterećenje.

Radi za 10 ljudi. A pod pravim saobraćajem?

Isti endpointi napisani za prototip, ista baza. Realni opsezi, u zahtjevima u sekundi ka backendu:

5
zatvorena platforma

Interni alat, paywall, složena registracija. Ovo preživi bilo kakva arhitektura; jedan server i SQLite su dovoljni.

50
javni sajt sa korisnicima

Jedan dobar server i jedna baza rade, pod uslovom da su upiti u redu. Jedan spor upit i sve stane.

500+
javna platforma koju crawluju

Google, SEO crawleri, scraperi, konkurencija. Ovo ne podnese bilo kakva arhitektura: tu počinju blokovi o kojima danas pričamo.

Grafana: Ingress Request Volume, estitor-backend-v2-api, ~821 req/s

Estitor, najveći portal za nekretnine u Crnoj Gori, običan radni dan: ~820 zahtjeva u sekundi ka backendu, veliki dio su crawleri.

Zato se ovo predavanje zove „Backend i AI”, a zapravo je o arhitekturi: odluke koje agent ne donosi umjesto vas. Ono što se danas zove software architect.

Zašto arhitektura

Odluke koje je teško promijeniti kasnije

Martin Fowler: arhitektura su odluke koje je skupo mijenjati. Koja baza. Koliko servera. Idu li fajlovi kroz server. Korisnik nijednu ne vidi, a svaka određuje koliko košta sljedeća funkcionalnost.

Ne puca odmah

Loša arhitektura radi. Pa se svaka nova stvar radi sporije i skuplje, dok jednog dana ne stane.

Svako je imao crvene dane
Netflix Badnje veče 2012Facebook 6 h mraka, 2021Cloudflare globalni pad, 2025Vaš projekat ?
Pitanje za salu

Ko je imao situaciju: „radi na mom laptopu", a kod nekog drugog ne? Danas učimo zašto, i šta se radi da se ne desi.

Nijedan blok koji danas nacrtamo nije tu „jer se tako radi". Svaki je odgovor na pitanje. Idemo graditi.

DIO 1 OD 3
Prototip, dan prvi

Obična aplikacija, kakvu i vi pravite: oglasi sa slikama, prijava, lista i detalj. Prvi korisnici dolaze. Jedan server. Idemo odatle, blok po blok.

01 Arhitektura02 Baze i stack03 Praksa
Korak 0

Jedan server, sve u njemu

Ovo nije loše. Za prvih sto korisnika, za MVP, za vaš projekat danas u praksi: tačno ovo treba. Ne pravite mikroservise za aplikaciju koju koristi vaša tetka.

Šta ako server umre?

Nestane struje. Disk otkaže (nije „ako", nego „kad"). Kolega uradi rm -rf u pogrešnom folderu. Šta ostaje?

Stanje je sve što ne možete regenerisati iz koda: podaci korisnika, fajlovi, istorija. Bilo je vezano za mašinu. Ko izgubi mašinu, izgubi biznis.

Blok 1

Baza, odvojena od servera

Separation of concerns

Baza ide na svoju mašinu. Svako radi jedan posao: server služi zahtjeve, baza čuva stanje. Kad nešto pukne, znate gdje gledate.

Stateless

I fajlovi odlaze sa servera, na zaseban disk. Server više ne pamti ništa. Umre? Podignete drugi iz gita, pokažete mu gdje su baza i disk, radi.

Iz prakse

Najčešće se krši nenamjerno: sesija „privremeno" u memoriji, upload na lokalni disk. Radi mjesecima, dok ne dodate drugi server.

Šta kad treba drugi server?

Prvi instinkt: kopiraj mašinu. Server A i server B, svaki sa svojom bazom. Gdje je problem?

Dva servera, dvije istine. Korisnik sačuva oglas na A, sljedeći zahtjev ode na B koji ne zna ništa. Ne pada, ne baca error, samo povremeno laže. Rješenje: jedna baza. Server smije umrijeti, baza ne smije.

Aplikacija je hit. I spora.

Deset hiljada ljudi, server na 100% CPU. Dodali smo drugi. Ali korisnik ukuca oglasi.me: koja IP adresa? Ko odlučuje kome ide koji zahtjev?

Blok 2 · probajte
0server 1
0server 2
0server 3
0izgubljeno
Pravilo:

Round robin: redom, 1, 2, 3, 1… Najčešće dovoljno. Least connections: kome je najlakše, taj dobija; upload slika i GET liste nisu isti posao.

Health check: svake sekunde GET /health. Kliknite na server da ga „ubijete": ispada iz rotacije, korisnici ne primijete. Napišite /health u svakom servisu koji ikad napravite.

U 20 h treba 8 servera. U 4 ujutru, dva.

Load balancer pušta da dodajemo servere. Ali kapacitet se dodaje na dva načina. Koji biste vi?

Vertikalno: veća mašina

Više jezgara i RAM-a, kod se ne mijenja. Ali ima plafon, najveća mašina koju neko prodaje, a jedan pad je i dalje sve palo.

Horizontalno: više manjih, iza load balancera

Bez plafona. Autoscaling ih dodaje i gasi po opterećenju: min 2 (jedan smije umrijeti), max 10; u 20 h osam, u 4 ujutru dva. Plaćate ono što koristite.

Horizontalno radi samo zato što su serveri stateless: load balancer smije da pošalje korisnika bilo kome. „Sticky session" (isti korisnik uvijek na isti server) zvuči zgodno, a najčešće je zakrpa za stanje koje se negdje sakrilo.

Upload slika obara sve.

Korisnik objavi oglas sa osam slika po 10 MB. Sto korisnika u istom minutu. Naivno: POST /upload, server primi fajl u memoriju, upiše u bazu kao BLOB. Šta se dešava ostalih deset hiljada?

RAM servera
Čekanje ostalih korisnika

Blok 3: metapodaci prije bajtova. Najgore: bajtovi kroz server u bazu. Bolje, ali i dalje loše: kroz server pa u storage (S3, R2, svejedno), server i dalje nosi svaki bajt dvaput. Optimalno: API upiše red (slika, oglas, status pending), vrati privremeni link, korisnik šalje direktno u bucket. Server ne vidi nijedan bajt. Ovo pravilo ide u vaš CLAUDE.md.

Slika je stigla. Ko pravi thumbnail?

Treba thumbnail i smanjena verzija za listu, notifikacija pratiocima, indeks za pretragu. Storage pozove servis za obradu slika, a on je baš tada pao.

0obrađeno
0u redu
0izgubljeno

Događaj umjesto poziva. Storage javi jednom: „slika 123 stigla". Broker (Kafka, RabbitMQ, SQS) poruku trajno zapiše i dostavi svima koji slušaju, svako svojim tempom. Obrada pala? Poruka čeka.

Blok 4 · detalji

Šta broker garantuje

Durability

Poruka je zapisana na disk, ne samo u memoriji. Broker se restartuje, poruke su tu.

Redelivery

Nema ack za X sekundi? Stiže opet. Posljedica: isti posao može doći dvaput, pa mora biti idempotentan. Napraviti isti thumbnail dvaput: isti fajl. Naplatiti karticu dvaput: katastrofa.

Dead-letter queue

Poruka koja pada deset puta ne blokira red zauvijek. Ide u DLQ, stiže alarm na Slack. Znate ŠTA je propalo, ne otkrivate za tri dana.

Fan-out

Jedan događaj, pet primalaca. Sjutra dodate šesti servis: samo se pretplati, storage ne dirate.

Pauza 15 minuta. Poslije pauze problem prestaje da bude tehnički i postaje ljudski: tim od 30 ljudi.

Previše vrata. Treba jedna.

Backend je jedan kod u tri kopije, pretraga izdvojena jer ima drugačiji load. Svako od njih ima javnu adresu i sam provjerava token, broji zahtjeve, završava TLS.

Blok 5. API gateway je jedini javni ulaz: provjerava token, broji zahtjeve, rutira po putanji (nginx, Traefik, Kong; u cloudu AWS API Gateway, Cloudflare). Sve iza njega je privatna mreža bez javnih adresa. Ovo nije poziv na mikroservise: iza gateway-a je i dalje monolit, plus ono što ste stvarno izdvojili.

Blok 5 · monolit ili servisi

Kad se servis izdvaja, a kad ostaje u monolitu

Izdvojite kad postoji konkretan razlog

Drugačiji load ili resurs (pretraga sa svojim indeksom, obrada slika koja troši CPU). Drugačiji ritam promjene ili drugi jezik. Posao koji smije da padne, a da aplikacija nastavi da radi.

U našoj aplikaciji to su pretraga i obrada slika. Oglasi, korisnici i naplata ostaju zajedno: dijele iste tabele i iste transakcije.

Šta svaki novi servis košta

Poziv između servisa je mrežni poziv: kasni i otkazuje. Ono što je bilo jedan COMMIT sad ide preko dva servisa. Svaki ima svoj deploy, logove, monitoring; za debug treba tracing.

Primjer: „kupi premium paket" je u monolitu jedan COMMIT nad dvije tabele. Sa posebnim servisom za naplatu postaje: naplati, pozovi oglase, ako padne vrati novac. Taj kod neko mora da napiše i testira.

Podrazumijevano: monolit sa jasnim modulima, što je app/<domen>/ u vašem backendu. Servis se izdvaja kad za to imate izmjeren razlog, ne unaprijed.

Blok 5 · auth

Authentication na gateway-u: JWT

potpis važi → 200provjera je matematika, bez odlaska u bazu
Authentication

KO SI. Radi gateway, jednom, na ulazu. Potpis i rok tokena.

Authorization

ŠTA SMIJEŠ. Radi svaki servis sam: smiješ li da mijenjaš baš ovaj oglas. U praksi: 403 kad nisi vlasnik.

20:00. 800 puta u sekundi ista lista oglasa.

Korisnici, Google, scraperi. Svaki zahtjev: gateway → oglasi → baza. Isti upit „daj oglase, Podgorica, najnoviji", osamsto puta u sekundi. Zašto to radimo?

0hit
0miss
–TTL (s)
Redis (RAM) 0,1 ms
Postgres (disk) 5 ms

Cache-aside: prvo pitaj RAM; nema → čitaj bazu, upiši u keš sa TTL-om, vrati. U keš idu podaci, nikad bajtovi slika. Druga greška: invalidacija. Cijena se promijeni u bazi, keš još 30 s laže. Ili je to prihvatljivo, ili na svaku izmjenu brišete ključ.

Blok 6 · detalji

Keš ne hvata sve. Šta sa bazom?

Indeks

Lista po gradu i datumu bez indeksa čita cijelu tabelu: 400 ms. Sa indeksom na (city, created_at): 2 ms. Prvo pitanje kad je upit spor.

Connection pool

8 servera × 10 konekcija = 80 na bazu, gornja granica ma koliko zahtjeva stiglo. Bez pool-a svaki zahtjev otvara svoju; Postgres klekne oko 500. PgBouncer ispred baze.

Read replica

Upisi na primar, čitanja na kopije koje kasne milisekunde. Lista oglasa smije da kasni 50 ms; stanje računa ne smije, ono ide sa primara.

Redoslijed: indeks, pa keš, pa pool, pa tek replika. Svaki korak je deset puta jeftiniji od sljedećeg, a agent prvi predloži posljednji.

Blok 7

CDN: edge cache blizu korisnika

Isti princip kao Redis, keš ispred sporog izvora, samo za fajlove i geografski. Prvi korisnik iz Podgorice napuni edge PoP slikom oglasa, ostalih 49.999 dobija sa 5 ms. Isto važi za JS i CSS vašeg frontenda: statika ide sa edge-a, ne sa vašeg servera.

Neko šalje 10.000 zahtjeva u sekundi.

Agresivan crawler, scraper konkurencije, ili vaš sopstveni frontend poslije greške: N+1 poziv u listi, retry bez pauze. Autoscaling to posluša i dodaje servere; račun na kraju mjeseca raste.

Blok 8 · token bucket: 10 tokena, puni se 1 svakih 6 s
0200
0429
Gdje

Na gateway-u, prije servisa. Brojač po IP-u ili korisniku u Redisu, jer ga svi gateway serveri dijele.

Odgovor

429 Too Many Requests i header Retry-After: 60. Servis iza gateway-a zahtjev nikad ne vidi.

Štiti i od vas samih

Najčešći „napadač" je sopstveni kod: N+1 poziv sa frontenda, retry bez backoff-a, cron koji se preklopio. Limit ga zaustavi na 11. zahtjevu, prije računa.

Cijela slika

Vaša aplikacija, dan hiljaditi pređite mišem preko bloka: koje pitanje ga je dovelo

Svaka strelica je jedna odluka koju smo danas donijeli zajedno, i jedno mjesto gdje može da pukne. Kad sljedeći put vidite ovakav dijagram, ne pitajte „šta je ovo", nego „koji problem je ovo riješilo". Ako ne nađete problem, blok je višak.

Rekapitulacija

Osam blokova, osam pitanja

Redoslijed nije slučajan: svaki problem nastaje tek kad prethodni riješite. Gradite samo ono što vam trenutno treba, i znate šta je sljedeće.

1Bazastanje van mašine
2Load balancerviše servera
3Storagebajtovi van API-ja
4Brokerdogađaji, ne pozivi
5Gatewayservisi i auth
6KešRAM prije diska
7CDNbajtovi na edge-u
8Rate limit429 prije računa

Vaš projekat, za sat vremena, je blok 1 sa JWT-om iz bloka 5. Ostalo znate prepoznati kad zatreba. Pitanja o crtežu, prije nego pređemo na baze i alate?

DIO 2 OD 3
Baze i stack

Aplikacija je prošla put. Usput je dobila Postgres, Redis, object storage i broker: četiri mjesta za podatke, i to nije kraj. Ko je to odlučio, i po čemu?

01 Arhitektura02 Baze i stack03 Praksa
Vrste baza · 1 od 8

Relacione: PostgreSQL, MySQL, SQLite

Šta je

Tabele sa vezama, ključevi, JOIN, transakcije: „naplati pa aktiviraj", oboje ili ništa. SQL koji razumije svaki developer i svaki agent. Postgres uz to ima JSONB kolone, full-text i pgvector.

Dobro za

Sve što ima strukturu i mora biti tačno: korisnici, oglasi, paketi, naplata. Podrazumijevani izbor; devedeset posto aplikacija nikad ne treba drugu bazu.

Granice i cijena

Jedan primar za upise; vertikalno skaliranje ima plafon, šardovanje je ručni posao. Milion upisa u sekundi nije za njega. Košta malo: open source, jedna mašina ide jako daleko.

Ko koristi (javno poznato)

Instagram je prve godine, sa 30 miliona korisnika i tri inženjera, vodio na Postgresu i on je i danas jezgro. Reddit, Apple (interno), Stripe (Postgres i MongoDB).

Kad ne znate šta da uzmete, uzmite ovo. Drugu bazu dodajte tek kad izmjerite problem koji ova ne rješava.

Vrste baza · 2 od 8

Key-value u RAM-u: Redis, Memcached, Valkey

Šta je

Ključ → vrijednost, sve u RAM-u, odgovor u mikrosekundama. Strukture: brojači, liste, skupovi, TTL na svakom ključu. Nema upita „daj mi sve gdje je…".

Dobro za

Keš (cache-aside iz bloka 6), brojači za rate limit, sesije i refresh tokeni, redovi i pub/sub za sitne poslove. Sve što smije da nestane jer se napuni iz baze.

Granice i cijena

RAM je najskuplji gigabajt koji kupujete, deset puta skuplji od SSD-a; podaci stanu koliko stane memorije. Trajnost je opciona, pa nije izvor istine. Jedan proces: velike vrijednosti blokiraju sve.

Ko koristi (javno poznato)

Twitter je timeline svakog korisnika držao u Redisu; Stack Overflow kešira stranice u Redisu ispred SQL Servera; GitHub, Snapchat. AWS ga prodaje kao ElastiCache, Cloudflare i Amazon forkovali u Valkey.

Prva baza koju dodate poslije Postgresa, gotovo uvijek. Pravilo: u Redis ide ono što smijete izgubiti.

Vrste baza · 3 od 8

Dokument baze: MongoDB, DynamoDB, Firestore

Šta je

JSON dokument po ključu, bez fiksne šeme; svaki zapis može imati drugačija polja. Čitanje cijelog dokumenta jednim potezom. DynamoDB i Firestore su potpuno upravljani, naplata po zahtjevu.

Dobro za

Kad zapisi nisu isti: stan ima sprat i grijanje, auto kilometražu, i ne želite 40 nullable kolona. Korpa, profil, konfiguracija. DynamoDB: ista milisekunda na milion zahtjeva u sekundi.

Granice i cijena

JOIN-ove radite vi, u kodu; šemu morate modelovati po tome kako čitate, ne po tome šta imate. Transakcije preko više dokumenata su ograničene. DynamoDB po zahtjevu ume da bude skup pri pogrešnom modelu.

Ko koristi (javno poznato)

Amazonova korpa je rođena u Dynamu (2007), zato da radi i kad pola data centra padne. Lyft i Disney+ na DynamoDB-u. eBay, Forbes i EA na MongoDB-u. Postgres JSONB pokriva većinu ovih slučajeva bez nove baze.

Prije nego uzmete Mongo, probajte JSONB kolonu u Postgresu. Dokument baza dobija smisao kad je šema zaista različita ili kad vam treba upravljana baza bez servera.

Vrste baza · 4 od 8

Wide-column: Cassandra, ScyllaDB

Šta je

Tabele bez JOIN-a, raspoređene po particijskom ključu na desetine mašina, bez jednog primara: svaki čvor prima upise. Pad mašine ili cijelog regiona ne zaustavlja upis.

Dobro za

Ogroman broj upisa koji nikad ne stane i čitanje po ključu: poruke po razgovoru, istorija gledanja po korisniku, metrike sa uređaja. Milion upisa u sekundi je normalan radni dan.

Granice i cijena

Upit mora da prati particijski ključ; „daj mi sve oglase jeftinije od X" ne postoji. Nema transakcija preko particija. Skupo u ljudima: klaster od desetina čvorova traži nekoga ko ga razumije.

Ko koristi (javno poznato)

Netflix drži istoriju gledanja svakog korisnika u Cassandri (milijarde upisa dnevno, mora preživjeti pad AWS regiona). Discord je milijarde poruka držao u Cassandri, pa prešao na ScyllaDB. Apple vodi jedan od najvećih Cassandra klastera na svijetu.

Tek kad Postgres fizički ne može da primi upise. Za aplikaciju sa oglasima: nikad u prvoj godini.

Vrste baza · 5 od 8

Kolonske: ClickHouse, Snowflake, BigQuery + S3

Šta je

Podaci se čuvaju po kolonama, pa „prosjek cijene po gradu" čita samo dvije kolone od pedeset, komprimovane deset puta. Napravljene za agregacije preko milijardi redova, ne za izmjenu jednog reda.

Dobro za

ClickHouse: događaji uživo, upit u milisekundama, jeftin po upitu, vaš server. Snowflake i BigQuery: izvještaji, terabajti, naplata po računanju, nula administracije; uz njih sirovi podaci u S3/Parquet za cente po GB.

Granice i cijena

Zašto klikove ne pišemo pravo u Snowflake? Može, i radi se (Snowpipe Streaming), ali upit traje sekunde i plaća se svaki put: za „koliko klikova ima ovaj oglas", koje se traži na svakom prikazu, treba ClickHouse ili Redis brojač. Nijedna nije za transakcije ni za UPDATE jednog reda.

Ko koristi (javno poznato)

Cloudflare analitiku svakog HTTP zahtjeva (milioni u sekundi) servira iz ClickHouse-a; Uber i eBay logove isto. Spotify izvještaje vrti na BigQuery-ju, Instacart i Canva na Snowflake-u.

Dva pitanja odlučuju: ko gleda rezultat (korisnik na stranici ili analitičar jednom dnevno) i koliko puta (hiljadu puta u sekundi ili deset puta na dan). Vruće u ClickHouse, hladno u Snowflake + S3.

Vrste baza · 6 od 8

Search: Elasticsearch, OpenSearch, Meilisearch

Šta je

Invertovani indeks: za svaku riječ lista dokumenata u kojima se javlja. Tolerancija na greške u kucanju, sinonimi, ranking po relevantnosti, filteri i faceti u istom upitu.

Dobro za

Pretraga kakvu korisnici očekuju: „dvosoban podgorca" nađe dvosobne stanove u Podgorici. Logovi i njihovo pretraživanje (ELK). Meilisearch za male timove: jedan binarni fajl, radi za sat.

Granice i cijena

Nije izvor istine: indeks je kopija Postgresa koja se puni preko brokera i smije da kasni sekundu. Gladan RAM-a; klaster je skup. Postgres full-text pokriva jednostavnu pretragu bez nove baze.

Ko koristi (javno poznato)

Wikipedia pretraga radi na Elasticsearch-u. Uber, Netflix (logovi), GitHub (code search godinama), Booking. Algolia i Meilisearch kao upravljane alternative za e-commerce i portale.

Dodajte je kad korisnici traže riječima, a ne filterima; dotle Postgres full-text. I nikad kao jedino mjesto gdje podatak živi.

Vrste baza · 7 od 8

Vektorske: pgvector, Pinecone, Qdrant, Weaviate

Šta je

Svaki tekst ili slika postane vektor, niz od recimo 1.536 brojeva (embedding) koji opisuje značenje; „slično" je udaljenost dva vektora. Baza indeksira vektore tako da najbližih deset nađe za milisekunde.

Dobro za

„Slični oglasi", „pronađi stranice slične ovoj", semantička pretraga, i RAG: AI asistent koji prvo nađe vaše dokumente, pa ih da modelu. Radionica 6 je ovdje.

Granice i cijena

Embedding se plaća po tokenu i mora se ponovo računati kad promijenite model. Kvalitet zavisi od modela više nego od baze. Pinecone je upravljan i naplaćuje po vektoru; pgvector je besplatan ali na velikim skupovima sporiji.

Ko koristi (javno poznato)

Notion AI pretražuje milijarde blokova preko Pinecone-a. Supabase gura pgvector kao podrazumijevano. Spotify i Instacart imaju sopstvene vektorske sisteme za preporuke.

Počnite sa pgvector-om u Postgresu koji već imate; posebna vektorska baza tek kad izmjerite da je spor.

Vrste baza · 8 od 8

Graf: Neo4j, Amazon Neptune

Šta je

Čvorovi i veze su prvoklasni podaci; upit prati veze: „prijatelji mojih prijatelja koji su gledali ovaj oglas". Ono što je u SQL-u pet JOIN-ova, ovdje je jedan korak dublje.

Dobro za

Kad su veze važnije od zapisa: preporuke po mreži, detekcija prevare (dva naloga, isti telefon, isti IP, ista kartica), prava pristupa, kuda-koji-dio-ide u logistici.

Granice i cijena

Skupo skaliranje preko više mašina, mali ekosistem i malo ljudi koji ga znaju. Za jednostavne veze Postgres sa dvije tabele radi isti posao. Rijetko prva baza, često treća.

Ko koristi (javno poznato)

LinkedIn je sagradio sopstveni graf za „ljudi koje možda poznajete". NASA Neo4j-em pretražuje decenije inženjerskih izvještaja. Banke i platne kuće ga koriste za prevare: PayPal, Zurich. Airbnb ga je probao za knowledge graph.

Uzmite ga kad vam upit u SQL-u ima pet JOIN-ova i još je spor. Dotle: tabela veza u Postgresu.

Ista aplikacija, osam vrsta podataka

Oblik podataka bira tehnologiju, ne obrnuto

Šta čuvamoKako se koristiTehnologija
Oglasi, korisnici, premium paketi, naplatarelacije, transakcije („naplati pa aktiviraj"), tačnostPostgreSQL
Keš liste, brojači, rate limit, sesijemikrosekunde, RAM, smije da nestaneRedis
Polja različita po kategoriji (stan: sprat, auto: km)JSON po ključu, šema se mijenjaPostgres JSONB · MongoDB
Poruke između korisnika, milijarde, po razgovoruupisi koji ne staju, čitanje po ključu, bez JOIN-aCassandra / ScyllaDB
Prikazi i klikovi: uživo na stranici / izvještajimilion događaja/s; ms upiti za korisnika / terabajti jednom dnevnoClickHouse Snowflake + S3
Pretraga „dvosoban sa pogledom na more"full-text, greške u kucanju, ranking; kopija, ne istinaElasticsearch
„Slični oglasi", asistent nad oglasimasličnost vektora (embedding)pgvector / Pinecone
Prevare: dva naloga, isti telefon, ista karticaveze važnije od zapisaNeo4j

Polyglot persistence: više baza, svaka za svoj oblik podataka. Crveni red je ono što ubija Postgres: milion upisa u sekundi. Sve ispod prve dvije linije dolazi tek kad izmjerite problem.

Kako biramo · pitalica

Četiri pitanja, pa jedno pravilo

1 · Oblik

Tabele sa vezama, dokumenti, ključ-vrijednost, vektori?

2 · Čitamo ili pišemo

I koliko brzo mora: milisekunde za korisnika, minuti za izvještaj?

3 · Smije li da se izgubi ili kasni

Keš smije, naplata ne smije.

4 · Koliko dugo i koliko košta

Hot u RAM-u, warm na SSD-u, cold u S3 za cente.

Pitalica: šta misle da koriste?

Pravilo: počnite sa PostgreSQL-om. Za 90% aplikacija je dovoljan (ima JSON kolone, full-text i pgvector). Drugu bazu dodajte tek kad izmjerite problem koji prva ne rješava, i onda onu koja rješava baš taj problem.

Stack

Čime se backend danas piše

Šta vam treba?

Kliknite framework za detalje, ili filter iznad da vidite ko je dobar za šta.

Naš izbor za praksu

FastAPI + SQLAlchemy + Poetry, PostgreSQL (SQLite za početak)

Zašto Python danas

Jezik AI ekosistema: svaki LLM SDK, svaka ML biblioteka prvo izađe za Python. Radionica 6 i vaši budući projekti sa agentima su tu.

Zašto FastAPI

Pydantic šeme su Zod za Python: tipovi i validacija u jednom. Automatski /docs (OpenAPI) koji čitaju i frontend i agent. Starter je složen kao moji produkcijski servisi: domeni, servisi, repozitorijumi, DI.

Zašto SQLite pa Postgres

SQLite je fajl: nula instalacije na 35 laptopova. Isti kod, jedan DATABASE_URL, i radi na Postgresu u Dockeru.

Radionica 3 (frontend)Danas (backend)
features/<name>/types.tsmodels/ (tabela) + schemas/ (JSON ulaz/izlaz)
Zod šema u formiPydantic Field(min_length=3, max_length=80), ista pravila, 422 sa fieldErrors
api/<name>-api.ts (mock)api/ + services/ + repositories/; frontend fajl dobija fetch umjesto niza
docs/entities.mdOstaje ugovor. Mijenja se u istom commitu kao kod.
TanStack Query hook-oviNe mijenjaju se. Zato je pravilo „podaci samo kroz api/" bilo važno.
DIO 3 OD 3
Od dijagrama do koda

Pored frontend repoa od prošle subote nastaje backend repo: baza, CRUD API, prijava sa tokenom. Agent kuca, vi crtate i čitate.

01 Arhitektura02 Baze i stack03 Praksa
Nazad u vaš projekat

Podsjetnik: gdje ste stali prošle subote

Frontend šalje

fetch("/api/listings") iz features/<name>/api/ foldera. Zahtjev ima metod, putanju, header-e i, za upis, JSON tijelo.

Backend odgovara

Statusni kod (200, 201, 404, 422) i JSON. Frontend prikaže podatke ili grešku. Tri stanja: učitava se, uspjeh, greška.

Ugovor

docs/entities.md i types.ts kažu koja polja i koje rute postoje. Danas backend dobija zadatak da ih ispoštuje, pa frontend radi bez prepravke.

Rad sa agentom na backendu

Vi crtate, agent kuca

Šta agent jeste

Model plus alati plus cilj. Odličan u pretvaranju jasne odluke u kod. Loš u donošenju odluke umjesto vas: pitate „kako da radim upload", dobijete fajl kroz server koji smo precrtali. Dijagram je vaš posao.

Pravila prije koda

CLAUDE.md i .claude/rules/: stack sa verzijama, struktura, „svaki write traži token", „vlasnik je uvijek prijavljeni korisnik, nikad id iz tijela", „šema = migracija". Što ne zapišete, agent izmisli, uvjerljivo.

Provjera su testovi

Agent kaže „gotovo" kad prestane da kuca. Vi kažete gotovo kad su pytest i ruff zeleni, /docs pokazuje rute iz entities.md, i razumijete svaku liniju.

Slab prompt

„Napravi API za oglase." Agent izmisli: bez auth-a, id vlasnika iz tijela, upit u endpointu, create_all umjesto migracije, nijedan test za 403.

Radni nalog

„/new-entity listings po docs/entities.md: PATCH i DELETE samo vlasnik (403), greške u obliku {detail, code, fieldErrors}, migracija, test za 401/403/404/422." Blok 1 i 5 sa dijagrama, riječ po riječ.

Vaš repo

Šta se mijenja, a šta ostaje

my-team-frontend/ prošla subota ├── docs/entities.md ugovor └── src/features/listings/ ├── types.ts usklađen sa backendom └── api/listings-api.ts fetch umjesto niza my-team-backend/ danas, iz startera ├── CLAUDE.md, .claude/ rules, /new-entity, api-reviewer, hooks ├── docs/entities.md kopija ugovora, mijenja se u oba ├── manage.py poetry run start · test · migrate · seed ├── app/database, utils sesija, DI, greške, paginacija ├── app/auth/, app/users/ JWT prijava, tabela users ├── app/listings/ api · services · repositories · models · schemas ├── alembic/ migracije ├── tests/unit, tests/integration └── docker-compose.yml Postgres kad zatreba
Prošla subotaDanas
types.ts: tipovi entitetaModeli i tabele u bazi, migracija
docs/entities.md: polja, veze, pravilaSpecifikacija za API i bazu, agent je čita prvu
api/ (mock): list, get, create, update, removePravi HTTP pozivi na CRUD rute; hook-ovi i ekrani netaknuti
Prijava (mock), zaštićene akcijePrava prijava: /auth/login vrati JWT, backend provjerava vlasnika

Starter sa primjerom listings: github.com/PavleDz/ai-dev-backend. „Use this template” → vaš backend repo; docs/entities.md prepišite iz svog frontend repoa.

Kako FastAPI backend radi

Put jednog zahtjeva: POST /api/listings

→ → → → →

    
Jedan entitet, četiri koraka

Od entities.md do rute koja radi

1 · Tabela
class Listing(Base):
    id: Mapped[int] = mapped_column(primary_key=True)
    title: Mapped[str] = mapped_column(String(80))
    price: Mapped[float] = mapped_column(Numeric(10, 2))
    owner_id: Mapped[int] = mapped_column(
        ForeignKey("users.id", ondelete="CASCADE"))
    __table_args__ = (CheckConstraint("price >= 0"),)

Što mora važiti za svaki red, važi u bazi, ne samo u Pythonu.

2 · Migracija
$ poetry run makemigrations "create listings"
# pročitaj alembic/versions/<novi fajl>.py, pa:
$ poetry run migrate

Šema se mijenja migracijom, nikad create_all. Migracija je git za bazu: svaki laptop i server prođu iste korake.

3 · Endpoint + servis
@router.post(path="", status_code=201)
def create_listing(
    data: ListingCreateRequestSchema,
    service: Annotated[IListingService, Depends(...)],
    user: Annotated[CurrentUser, Depends(get_current_user)],
) -> ListingResponseSchema:   # vlasnik = user.id iz tokena
    return service.create(data=data, current_user=user)
4 · Test
def test_patch_by_other_user_is_403(
        client, other_auth_headers, listing_id):
    r = client.patch(f"/api/listings/{listing_id}",
        json={"price": 1}, headers=other_auth_headers)
    assert r.status_code == 403

Za svaku rutu: srećan put, 401, 403, 404, 422. Test za tužan put je ono što agent preskoči.

Uz to repozitorijum (upiti, flush, nikad commit) i izuzeci sa kodom; sve radi /new-entity <name>: <opis>. Agent prvo predloži tabelu i pravila pristupa i čeka vašu potvrdu.

Auth i greške · {detail, code, fieldErrors}

Prijava sa tokenom i jedan oblik greške

Lozinka se čuva kao bcrypt hash, nikad čista. Token važi 60 min. Frontend ga drži u localStorage i šalje kao Authorization: Bearer. Email je UNIQUE u bazi, pa 409 dolazi iz baze, ne iz if-a.

StatusKadaTijelo
401nema ili loš token, pogrešna lozinka{"detail": "Authentication required", "code": "not_authenticated"}
403prijavljen, ali nije vlasnik{"detail": "Only the owner…", "code": "not_listing_owner"}
404zapis ne postoji{"detail": "Listing not found", "code": "listing_not_found"}
409email već postoji{"detail": "…", "code": "email_already_registered", "fieldErrors": {"email": "…"}}
422neispravan unos{"detail": "Validation failed", "code": "validation_error", "fieldErrors": {"title": "…"}}

Frontend već zna ovaj oblik: ApiError iz lib/api-client.ts prikazuje fieldErrors ispod polja, po code odlučuje šta dalje. Najčešća laž u API-jima: sve vraća 200 sa „greška" u tekstu.

.claude folder · dopuna iz produkcije

Projekat spreman za agenta, i za backend

.claude/ ├── decisions.md ADR-001…017: zašto je tako ├── frequent-mistakes.md živi dnevnik grešaka ├── rules/ backend-api · backend-db · backend-tests │ anti-patterns · safety (uvijek) ├── commands/ /brief · /new-entity · /connect-feature │ /api-check · /review-changes ├── agents/ api-reviewer · security-reviewer │ migration-reviewer · contract-keeper ├── skills/ backend-patterns · test-endpoint · check-n1 ├── hooks/ guard-bash · guard-edits · format │ verify-changes · session-start └── settings.json dozvole + 5 hook-ova
Hooks: garancija, ne molba

guard-bash blokira push --force, reset --hard, rm -rf app/, alembic downgrade, uv add bez pitanja. guard-edits čuva commitovane migracije i .env. verify-changes na kraju zadatka pokrene ruff + pytest: agent ne smije reći „gotovo" dok nije zeleno.

Svježe oči, paralelno

/review-changes pusti tri recenzenta odjednom: api (ugovor, greške), security (auth na write-u, vlasnik iz tokena, tajne, SQL injection), migration (tabela bez migracije). Nalaz koji nađu dva ima veću težinu. Verdikt: APPROVED / NEEDS CHANGES / BLOCKED.

Dva fajla koja rastu sa vama

decisions.md: svaka odluka ima broj (ADR-006: vlasnik iz tokena), pa recenzenti citiraju broj umjesto da raspravljaju. frequent-mistakes.md: agent drugi put pogriješi isto → jedan red ovdje; recenzenti ga čitaju prvi. Iz moje produkcije, svaki red poslije jedne greške.

Vježba · 60 min

Backend za vaš projekat, u pet koraka

0

Priprema · 10 min

pipx install poetry         # jednom; Python 3.13
git clone github.com/PavleDz/ai-dev-backend.git my-team-backend
cd my-team-backend && poetry install
poetry run migrate
poetry run seed
poetry run start            # :8000/docs
1

Brief + entiteti · 20 min

/brief za glavni entitet, pa /new-entity <name>: <opis> po docs/entities.md; agent predloži tabelu i pravila, vi potvrdite. Kontrolna tačka: /docs pokazuje vaše rute, poetry run test zelen.

2

Veza sa frontendom · 15 min

/connect-feature <name>: mock api postaje fetch, hook-ovi netaknuti. Kontrolna tačka: lista i detalj rade sa pravim podacima i prežive osvježavanje stranice.

3

Prijava · 10 min

Login forma zove /api/auth/login, čuva token, zaštićene akcije šalju Bearer. Kontrolna tačka: tuđi zapis ne možete obrisati (403 u Network tabu).

4

Provjera i push · 5 min

/api-check, pa /review-changes: tri recenzenta paralelno. Ispravite nalaze visoke važnosti. git diff prije commita; hook vas ionako ne pušta sa crvenim testom.

Podjela u timu

Jedan laptop vodi sesiju sa agentom, drugi čita entities.md naglas i provjerava u /docs. Mijenjajte se poslije svakog entiteta, uz commit i push.

Provjera koda

Šta gledati kad čitate backend diff

Ne morate razumjeti svaki red. Ovih devet znakova uočite i kao početnik.

Znak u diffuZašto je problem
Write endpoint bez Depends(get_current_user)Svako može da piše. Blok 5: svaki write traži token.
owner_id ili user_id dolazi iz tijela zahtjevaKorisnik bira čiji je zapis. Vlasnik je uvijek iz tokena.
SQL sklopljen od stringova (f"... WHERE id={id}")SQL injection. Upiti idu kroz SQLAlchemy sa parametrima.
Nova tabela, a nema fajla u alembic/versions/Radi samo na agentovom laptopu. Kolega i server nemaju tabelu.
Base.metadata.create_all u kodu aplikacijeZaobiđene migracije; sljedeća izmjena šeme nema put.
Lozinka, ključ ili token kao string u koduTajne idu u .env, koji je u .gitignore. Agent ih zna ubaciti kad „neće da radi".
db.commit() u repozitorijumu ili upit u api/Transakcija je posao endpointa (DBAPIRouter); upiti žive u repozitorijumu. Sloj pogrešan, test nemoguć.
Nema testa za 401, 403 i 422Testiran samo srećan put. Pitajte zašto.
Novi paket u pyproject.toml koji niste tražiliZavisnost koju niko ne poznaje. Pitajte zašto i šta bez nje.
Do sljedeće subote

Šta treba da radi, i šta je bonus

01

Glavni tok radi do kraja

Lista, detalj, kreiranje i izmjena vašeg glavnog entiteta idu kroz pravi API, sa prijavom. Osvježite stranicu i podaci ostaju.

02

Entiteti usklađeni

docs/entities.md, types.ts i schemas/ kažu isto. Neka contract-keeper to provjeri, i dopišite bar jedan red u frequent-mistakes.md.

03

Repo spreman

Testovi i lint prolaze u CI-ju (GitHub Actions je u starteru). README kaže kako se pokreće backend. Link u Slack kanal.

Bonus: Postgres

docker compose up -d db, promijenite DATABASE_URL, poetry run migrate. Isti kod. Blok 1 na pravoj bazi.

Bonus: upload slike

Presigned princip u malom: metapodaci u bazu prije bajtova, fajl van baze. Blok 3.

Bonus: keš i limit

Cache-aside za listu (TTL 30 s, invalidacija na upis) i rate limit na login (5 pokušaja / 15 min). Blok 6 i 8.

Materijali

Za dalje

Arhitektura

Martin Fowler, Software Architecture Guide (martinfowler.com/architecture). System Design Primer (GitHub). Netflix, Shopify i Instagram engineering blogovi: stvarne verzije današnjeg crteža, sa brojevima.

Za godinu dana

Kleppmann, Designing Data-Intensive Applications. Ne sad; kad prvi put budete imali problem koji rješava, tad je najbolja knjiga koju ćete pročitati.

Stack

fastapi.tiangolo.com (čita se kao knjiga) · docs.sqlalchemy.org (2.0 ORM Quick Start) · alembic.sqlalchemy.org · python-poetry.org/docs · postgresql.org/docs · redis.io/docs

Agent

code.claude.com/docs: CLAUDE.md, rules, hooks, subagenti · Context7 za ažurnu dokumentaciju biblioteka („use context7" u promptu) · starter: github.com/PavleDz/ai-dev-backend

I najvažnije: napravite tri svoja mala servisa. Prvi će biti loš, drugi bolji, treći dobar. Nema prečice.

AI-DEV 2026 · Radionica 4

Sad je red na vas.

Pitanja, pa ostatak vremena za one koji su zapeli u vježbi: rješavamo uživo, ovdje.

Pavle Džuverovićpavle@coinis.com
Uvod1 / 1