Aprofundează
Data warehouse
Un curs în 27 de lecții, fiecare cu o carte, un citat verificat și o lecție video de urmărit pe loc: de la Codd și Inmon la lakehouse și data mesh, modelare dimensională și SCD, ETL/ELT și calitate, pregătirea datelor pentru AI, pseudonimizare, k-anonimitate și confidențialitate diferențială, securitate, cloud sau on-prem, join-uri, indecși și memorie.
Traseul de lectură
- 01
Lecția 1 · Un depozit de date nu e o bază de date mai mare, ci una construită pentru altă întrebare.
Building the Data Warehouse · W. H. Inmon · 1992
Cele patru adjective ale lui Inmon sunt, de fapt, patru decizii de proiectare. Orientat pe subiect: datele sunt organizate pe client, produs, comandă — nu pe aplicațiile care le-au produs. Integrat: aceleași coduri de țară, aceleași unități, aceleași chei, oricâte surse ar fi în spate; integrarea e cea mai scumpă parte a muncii și motivul pentru care depozitul există. Variabil în timp: fiecare rând e o fotografie datată, iar istoricul se păstrează, nu se suprascrie. Nevolatil: se încarcă și se citește; nu se corectează pe loc, ca într-un sistem de tranzacții. Sistemul de producție răspunde la „care e adresa clientului acum?”. Depozitul răspunde la „cum s-a mutat valoarea comenzilor pe județe în ultimii cinci ani?”. Prima întrebare cere un rând, repede și consistent. A doua cere milioane de rânduri, comparabile între ele. Sunt două sarcini diferite, și lecțiile care urmează arată de ce nu le rezolvă bine același motor.
Un depozit de date este o colecție de date orientată pe subiect, integrată, variabilă în timp și nevolatilă, în sprijinul procesului decizional al conducerii.Building the Data Warehouse (1992), cap. 2 — definiția depozitului de date
De ce contează Fără un depozit, fiecare raport își face propria integrare — și fiecare o face puțin altfel. Deciziile cer istoric comparabil, iar istoricul comparabil e exact ce sistemele de producție nu păstrează.
Deschide pe YouTube ↗ - 02
Lecția 2 · Independența datelor: utilizatorul descrie ce vrea, nu unde stă.
A Relational Model of Data for Large Shared Data Banks · E. F. Codd · 1970
Codd a propus în 1970 ceva ce azi pare evident: tabele (relații) cu rânduri și coloane, chei primare care identifică un rând și chei străine care leagă tabelele, plus un limbaj declarativ în care spui ce rezultat vrei, nu ce fișiere să deschidă mașina. Normalizarea vine din aceeași idee: dacă o adresă de client apare în o mie de comenzi, o corectezi în o mie de locuri sau în niciunul. A treia formă normală se ține minte într-o frază: fiecare atribut depinde de cheie, de toată cheia și numai de cheie. Pentru un depozit de date, normalizarea e o alegere, nu o lege. Sursele sunt normalizate ca să scrie fără anomalii; depozitul denormalizează deliberat ca să citească fără zece join-uri. Ce rămâne neatins e principiul lui Codd: modelul logic separat de cel fizic. De aceea poți schimba indecși, partiții și formatul de stocare fără să rescrii interogările — și de aceea lecțiile despre indecși și memorie, de mai târziu, nu ating deloc SQL-ul.
Viitorii utilizatori ai marilor bănci de date trebuie protejați de obligația de a ști cum sunt organizate datele în mașină (reprezentarea internă).A Relational Model of Data for Large Shared Data Banks, Communications of the ACM 13(6), 1970 — prima propoziție a rezumatului
De ce contează Fiecare lecție de optimizare din acest curs se sprijină pe separarea logic–fizic. Cine o pierde ajunge să rescrie rapoartele de fiecare dată când mută un tabel.
Deschide pe YouTube ↗ - 03
Lecția 3 · Un motor bun la tranzacții e slab la analiză, și invers — nu e un defect, e fizică.
“One Size Fits All”: An Idea Whose Time Has Come and Gone · Michael Stonebraker, Uğur Çetintemel · 2005
OLTP (procesare de tranzacții): mii de operații mici pe secundă, fiecare atinge câteva rânduri întregi — o comandă, un client — și trebuie să fie corectă chiar dacă alte mii se întâmplă simultan. De aici stocarea pe rânduri, indecșii B-tree și izolarea tranzacțiilor. OLAP (procesare analitică): câteva interogări mari, fiecare atinge două-trei coloane din sute de milioane de rânduri și le adună. De aici stocarea pe coloane, compresia și scanarea în loturi. Stonebraker a spus-o în 2005: era unui singur motor pentru toate s-a încheiat, iar de atunci au apărut motoare columnare, de streaming, de serii de timp, de documente. Sistemele HTAP încearcă ambele într-unul; prețul e complexitatea și, de obicei, o copie columnară ținută la zi în fundal. Regula practică: nu rula rapoarte pe baza de producție. O scanare analitică pe un motor de tranzacții blochează sau încetinește scrierile și folosește greșit indecșii. Replica de citire e primul ajutor; depozitul e tratamentul.
Ultimii 25 de ani de dezvoltare comercială a SGBD-urilor pot fi rezumați într-o singură formulă: «o mărime pentru toți». Formula se referă la faptul că arhitectura tradițională de SGBD (proiectată și optimizată inițial pentru prelucrarea datelor de afaceri) a fost folosită pentru a susține multe aplicații centrate pe date, cu caracteristici și cerințe foarte diferite.“One Size Fits All”: An Idea Whose Time Has Come and Gone, ICDE 2005 — rezumat
De ce contează Cea mai frecventă cauză a unui sistem de producție lent nu e volumul de tranzacții, ci raportul de dimineață care scanează tot tabelul de comenzi.
Deschide pe YouTube ↗ - 04
Lecția 4 · Declară granularitatea înainte de orice; restul modelului decurge din ea.
The Data Warehouse Toolkit · Ralph Kimball, Margy Ross · 2013
Cei patru pași ai lui Kimball sunt în ordinea asta dintr-un motiv. Procesul de afaceri e un lucru care se întâmplă — o vânzare, o plată, un transport — nu un departament. Granularitatea e propoziția care spune ce reprezintă un rând din tabelul de fapte: „o linie de bon, într-un magazin, într-o zi”. Abia apoi vin dimensiunile — cine, ce, unde, când, contextul descriptiv — și faptele, măsurile numerice care se pot aduna la granularitatea aleasă. Rezultatul e schema stea: un tabel de fapte îngust și lung, cu chei străine spre dimensiuni late și denormalizate, fiecare cu o cheie surogat (un întreg generat de depozit, nu codul din sursă). Greșeala clasică e să amesteci granularități — linii de comandă și totaluri de comandă în același tabel — și să obții sume duble în primul raport. A doua greșeală e să ai trei dimensiuni „Client” ușor diferite; dimensiunile conformate, aceleași pentru toate procesele, sunt ce face două rapoarte comparabile.
Alege procesul de afaceri. Declară granularitatea. Identifică dimensiunile. Identifică faptele.Kimball Group, Dimensional Modeling Techniques — Four-Step Dimensional Design Process (aceiași pași ca în The Data Warehouse Toolkit, ed. a 3-a, cap. 3)
De ce contează Un tabel de fapte cu granularitate nedeclarată produce cifre care se contrazic între rapoarte, iar nimeni nu poate spune care e cea corectă.
Deschide pe YouTube ↗ - 05
Lecția 5 · Data Vault separă ce e stabil (cheile), ce leagă (relațiile) și ce se schimbă (atributele).
Data Vault Series 1 — Data Vault Overview · Dan Linstedt · 2002
Schema stea e rapidă de citit și ușor de înțeles, dar rigidă: o sursă nouă sau o coloană nouă înseamnă rescrierea dimensiunii. Fulgul de zăpadă normalizează dimensiunile — mai puține duplicate, mai multe join-uri — și de obicei nu merită. Data Vault răspunde la altă problemă: cum integrezi douăzeci de surse care se schimbă des, fără să rescrii nimic și cu audit complet. Trei tipuri de tabele: hub (cheile de business — un client, un produs), link (relațiile dintre chei — clientul a cumpărat produsul) și satellite (atributele descriptive, cu data încărcării și sursa, deci cu istoric complet). Costul e că un vault nu se citește direct: are prea multe tabele. Arhitectura tipică e staging, apoi vault (brut și de business), apoi marts în stea deasupra, pentru rapoarte. Alegerea nu e ideologică. Surse puține și stabile: stea direct. Surse multe, schimbătoare, cerințe de audit: vault dedesubt, stea deasupra.
Modelul Data Vault este un set de tabele normalizate, orientat pe detaliu, cu urmărire istorică și legături unice, care susține una sau mai multe arii funcționale ale afacerii.Dan Linstedt, Data Vault Series 1 — Data Vault Overview (2002), definiția modelului
De ce contează Un depozit care se rescrie la fiecare sursă nouă nu ajunge niciodată la a zecea sursă. Alegerea modelului decide cât de ieftină e schimbarea.
Deschide pe YouTube ↗ - 06
Lecția 6 · Lakehouse: fișiere deschise pe stocare de obiecte, cu tranzacții și schemă deasupra.
Lakehouse: A New Generation of Open Platforms that Unify Data Warehousing and Advanced Analytics · Michael Armbrust, Ali Ghodsi, Reynold Xin, Matei Zaharia · 2021
Data lake-ul a promis stocare ieftină pentru orice: fișiere Parquet pe stocare de obiecte, fără schemă impusă, fără tranzacții. A livrat, adesea, o mlaștină: nimeni nu știe ce e curat, două joburi scriu peste același director, o citire prinde jumătate dintr-o scriere. Depozitul clasic are tranzacții, schemă și viteză, dar într-un format închis, ca o copie a datelor. Lakehouse-ul pune între ele un format de tabel deschis (Delta, Iceberg, Hudi): un jurnal de metadate peste fișierele Parquet care aduce tranzacții ACID, „time travel” la o versiune anterioară, evoluție de schemă și, mai ales, mai multe motoare — SQL, Spark, un framework de ML — care citesc aceleași fișiere fără copii. Arhitectura medalion organizează straturile: bronze (datele exact cum au sosit, niciodată modificate), silver (curățate, deduplicate, cu tipuri corecte), gold (agregate, modelate pentru consum — de obicei în stea). Regula de aur e că bronze e dovada: orice din silver și gold trebuie să se poată reconstrui din el.
Această lucrare susține că arhitectura depozitului de date, așa cum o știm astăzi, se va ofili în anii următori și va fi înlocuită de un nou tipar arhitectural, Lakehouse, care (i) se va baza pe formate deschise, cu acces direct, precum Apache Parquet, (ii) va avea suport de prim rang pentru învățare automată și știința datelor și (iii) va oferi performanță de ultimă generație.Lakehouse: A New Generation of Open Platforms that Unify Data Warehousing and Advanced Analytics, CIDR 2021 — rezumat
De ce contează Când o transformare se dovedește greșită peste șase luni, singura întrebare care contează e dacă mai ai datele brute. Bronze răspunde da.
Deschide pe YouTube ↗ - 07
Lecția 7 · Data mesh mută proprietatea datelor la domeniul care le produce; platforma și regulile rămân comune.
Data Mesh Principles and Logical Architecture · Zhamak Dehghani · 2020
Problema pe care o rezolvă data mesh-ul e organizațională: echipa centrală de date devine gât de sticlă și nu cunoaște semantica a douăzeci de domenii; când comanda „a fost anulată” înseamnă altceva în logistică decât în facturare, echipa centrală află ultima. Primul principiu mută proprietatea la domeniu. Al doilea cere ca ce publică domeniul să fie un produs: set de date cu schemă, proprietar, SLA, documentație, adresă de acces — descoperibil, de încredere, interoperabil. Al treilea, platforma self-serve, e ce face primele două posibile: echipele publică fără să-și construiască fiecare propria infrastructură. Al patrulea, guvernanța federată computațională, spune că regulile comune — ce e PII, ce formate, ce identificatori — sunt cod verificat automat, nu un comitet. Capcana: mesh fără platformă înseamnă douăsprezece depozite incompatibile cu un nume nou. Și scara contează: pentru o firmă cu o singură echipă de date, un depozit central bine modelat e răspunsul corect, nu o versiune diminuată de mesh.
Proprietate și arhitectură descentralizate ale datelor, orientate pe domenii. Datele ca produs. Infrastructura de date self-serve ca platformă. Guvernanță computațională federată.Data Mesh Principles and Logical Architecture, martinfowler.com (2020) — cele patru principii, în ordinea din articol
De ce contează Cele mai multe eșecuri de „mesh” sunt eșecuri de platformă: s-a descentralizat proprietatea înainte să existe ceva comun pe care să se publice.
Deschide pe YouTube ↗ - 08
Lecția 8 · Stocarea pe coloane, compresia și execuția vectorizată sunt de ce o interogare pe un miliard de rânduri durează secunde.
C-Store: A Column-oriented DBMS · Michael Stonebraker et al. · 2005
Trei idei explică aproape tot din bazele de date analitice moderne. Prima: stocarea pe coloane. O interogare care atinge trei coloane din cincizeci citește 6% din octeți, nu 100%. A doua: compresia. Într-o coloană, vecinii seamănă — aceeași țară de o mie de ori, date crescătoare, sume din același interval — deci dicționar, run-length și delta comprimă de zece ori, iar motorul poate lucra adesea direct pe datele comprimate. A treia: execuția vectorizată, în loturi de mii de valori, nu tuplu cu tuplu, ca procesorul să nu aștepte memoria. Peste ele stau două decizii de arhitectură. MPP (procesare masiv paralelă): datele sunt partiționate pe noduri, fiecare scanează partea lui, iar join-urile și agregările cer redistribuire (shuffle) — partea scumpă. Separarea stocare–calcul: datele stau pe stocare de obiecte, ieftină și durabilă, iar calculul se pornește la cerere și se scalează separat; prețul e latența la rece și cache-ul local. Micro-partițiile poartă min/max per bloc, deci un filtru pe dată sare peste blocurile care nu-l pot conține. Ce nu merge bine pe columnar: scrieri mici și dese — se încarcă în loturi.
Această lucrare prezintă proiectarea unui SGBD relațional optimizat pentru citire, care contrastează puternic cu majoritatea sistemelor actuale, optimizate pentru scriere.C-Store: A Column-oriented DBMS, VLDB 2005 — prima propoziție a rezumatului
De ce contează Cine înțelege de ce columnarul e rapid scrie interogări care filtrează pe coloanele partiționate și evită să insereze rând cu rând într-un motor analitic.
Deschide pe YouTube ↗ - 09
Lecția 9 · Log-ul e sursa adevărului; baza de date e un cache al ultimei valori din log.
The Log: What every software engineer should know about real-time data's unifying abstraction · Jay Kreps · 2013
Un log e o listă la care doar adaugi, în ordine. Pare banal, dar e abstracția care leagă replicarea, streaming-ul și depozitul de date. Orice bază relațională are unul înăuntru — jurnalul de tranzacții — și fiecare insert, update sau delete trece pe acolo înainte să ajungă în tabele. Change data capture (CDC) citește exact acel jurnal și emite fiecare schimbare ca eveniment: fără interogări periodice care încarcă sursa, fără fereastra pierdută dintre două rulări și, mai ales, cu ștergerile incluse — un SELECT de noapte nu vede niciodată rândul care a dispărut la prânz. Evenimentele ajung într-un log distribuit; consumatorii le citesc cu un offset, deci pot reporni de unde au rămas sau, dacă e nevoie, de la zero. În depozit, bronze primește evenimentele brute, iar silver aplică „ultima valoare per cheie” (merge) sau păstrează tot istoricul. Semantica realistă e cel-puțin-o-dată plus idempotență: fiecare eveniment poartă cheie și versiune, iar aplicarea lui de două ori dă același rezultat. Ordinea contează per cheie, nu între chei.
Un log este poate cea mai simplă abstracție de stocare posibilă. Este o secvență de înregistrări doar-cu-adăugare, total ordonată, ordonată în timp.The Log: What every software engineer should know about real-time data's unifying abstraction, LinkedIn Engineering (2013) — Part One: What Is a Log?
De ce contează Extragerea periodică cu SELECT ratează ștergerile și încarcă sursa; un depozit alimentat așa își pierde încet acuratețea fără să dea nicio eroare.
Deschide pe YouTube ↗ - 10
Lecția 10 · Contabilii nu folosesc radiera: înregistrezi fapte, derivezi stări și recalculezi când ai greșit.
Immutability Changes Everything · Pat Helland · 2015
Un contabil nu șterge o înregistrare greșită; adaugă una de corecție. Registrul rămâne complet, oricine poate reface calculul, iar „soldul” e o valoare derivată, nu un fapt. Helland arată că tot calculul modern merge în direcția asta: jurnale de tranzacții, log-uri de evenimente, fișiere Parquet scrise o dată, chiar și SSD-urile care nu suprascriu pe loc. Când datele sunt imuabile, dispar clasele întregi de probleme — actualizări pierdute, citiri inconsistente, „cine a schimbat asta?” — și apar două lucruri prețioase: auditul e gratis și orice stare se poate recalcula. Într-un depozit de date, faptele sunt natural imuabile: o vânzare s-a întâmplat. Corecțiile devin rânduri noi cu semn invers sau cu perioadă de valabilitate, iar faptele care sosesc târziu se adaugă, nu se „lipesc” în trecut. Dimensiunile își păstrează istoricul prin versiuni — lecția următoare. Singurul conflict real e cu dreptul la ștergere din GDPR, pentru care există un răspuns compatibil cu imuabilitatea, în lecția 19.
Există o tendință de neoprit spre stocarea și transmiterea de date imuabile. Avem nevoie de imuabilitate ca să ne coordonăm la distanță și ne putem permite imuabilitatea, pe măsură ce stocarea se ieftinește.Immutability Changes Everything, CIDR 2015 — rezumat (secțiunea 2 se numește „Accountants Don't Use Erasers”)
De ce contează Un depozit care se corectează pe loc nu poate răspunde la „ce arăta raportul luna trecută și de ce”. Unul doar-cu-adăugare răspunde întotdeauna.
Deschide pe YouTube ↗ - 11
Lecția 11 · SCD tip 2: când un atribut se schimbă, nu suprascrii — adaugi un rând și închizi perioada celui vechi.
The Data Warehouse Toolkit · Ralph Kimball, Margy Ross · 2013
Un client se mută din Cluj în Iași. Ce faci cu rândul lui din dimensiunea Client? Tip 1: suprascrii. Simplu, fără istoric — iar vânzările din 2023 apar brusc în Iași, deci rapoartele vechi se schimbă. Bun pentru corectarea greșelilor și pentru atribute fără valoare istorică. Tip 2: adaugi un rând nou, cu o cheie surogat nouă, cu valid_from și valid_to, și marchezi care e curent. Faptele vechi rămân legate de versiunea de atunci, faptele noi de versiunea nouă; raportul din 2023 rămâne exact cum era. Tip 3: păstrezi o coloană „valoarea anterioară” — un singur pas de istoric, util la realinieri de teritorii de vânzări, unde vrei să vezi ambele împărțiri deodată. Alegerea se face per atribut, nu per tabel: aceeași dimensiune poate avea coloane tip 1 și tip 2. Două capcane. Prima: join-ul faptelor pe cheia naturală în loc de surogat, care dublează rândurile de câte versiuni are clientul. A doua: un „update” de tip 1 rulat din greșeală pe o dimensiune tip 2, care rescrie toate versiunile deodată și șterge istoricul fără să dea nicio eroare.
Schimbările de tip 2 într-o dimensiune cu schimbare lentă adaugă un rând nou în dimensiune, cu valorile actualizate ale atributelor. Asta cere generalizarea cheii primare a dimensiunii dincolo de cheia naturală sau durabilă, pentru că vor exista, potențial, mai multe rânduri care descriu fiecare membru.Kimball Group, Dimensional Modeling Techniques — Type 2: Add New Row (The Data Warehouse Toolkit, ed. a 3-a, cap. 5)
De ce contează Fără tip 2, „vânzări pe județ în 2023” dă alt răspuns în fiecare an, pe măsură ce clienții se mută. Istoricul trebuie ținut în dimensiune, nu ghicit din fapte.
Deschide pe YouTube ↗ - 12
Lecția 12 · Datele supraviețuiesc codului: orice schimbare de schemă trebuie citită și de codul vechi, și de cel nou.
Designing Data-Intensive Applications · Martin Kleppmann · 2017
O aplicație se înlocuiește în cinci minute; datele ei stau cinci ani. De aici două compatibilități: înapoi (codul nou citește datele vechi) și înainte (codul vechi citește datele noi — fiindcă niciodată nu se actualizează totul deodată). Regulile care le păstrează pe amândouă sunt puține și stricte: adaugi coloane opționale, nu refolosești niciodată un nume cu alt sens, nu schimbi tipul pe loc. O schimbare de tip se face în patru pași: adaugi coloana nouă, o umpli (backfill), muți cititorii, abia apoi ștergi vechea. Formatele cu schemă (Avro, Protobuf, Parquet) fac regulile verificabile; JSON-ul liber le lasă pe seama norocului. Contractul de date e aceeași idee, ridicată la nivel de echipă: producătorul și depozitul cad de acord pe schemă, semantică, SLA, proprietar și politica de schimbare, iar acordul e verificat automat la fiecare publicare. O schimbare care rupe contractul se publică ca versiune nouă, alături de cea veche. Motivul e că cele mai grave defecte sunt tăcute: o coloană redenumită la sursă sosește ca NULL, raportul scade cu 30% și nimeni nu primește nicio eroare. Contractul o prinde la graniță, unde e ieftin.
Astfel, datele vechi de cinci ani vor fi tot acolo, în codificarea originală, dacă nu le-ai rescris explicit între timp. Observația aceasta e rezumată uneori prin: datele supraviețuiesc codului.Designing Data-Intensive Applications (2017), cap. 4 Encoding and Evolution — Dataflow Through Databases
De ce contează O schimbare de schemă neanunțată nu dă eroare; dă un raport greșit, descoperit peste o lună. Contractul mută descoperirea la graniță și la minut.
Deschide pe YouTube ↗ - 13
Lecția 13 · ELT mută transformarea în depozit; ce rămâne de proiectat sunt orchestrarea și idempotența.
Fundamentals of Data Engineering · Joe Reis, Matt Housley · 2022
ETL transformă datele într-o unealtă separată înainte să le încarce; ELT le încarcă brute și le transformă cu SQL în depozit, unde calculul e ieftin, SQL-ul stă în git și proveniența se citește din interogări. Ordinea nouă face extragerea subțire (copiază fidel) și transformarea bogată (modelează, testează, documentează). Orchestrarea e graful de sarcini: dependențe, program, reîncercări, alerte, o vedere a ce a rulat și ce nu. Parametrul unei rulări e fereastra de timp, niciodată „acum” — altfel rularea de ieri nu se mai poate repeta. Idempotența e proprietatea care face totul reparabil: rularea repetată pentru aceeași fereastră produce exact aceleași rânduri. Se obține ștergând și reîncărcând partiția, sau prin merge pe cheie — niciodată prin adăugare oarbă, care dublează datele la a doua rulare. Cu ea, un backfill e doar o buclă peste ferestre vechi, iar o eroare de la 3 dimineața se rezolvă cu o reluare, nu cu o investigație. Încărcările incrementale poartă un watermark și o fereastră pentru datele care sosesc târziu; fișierele mici se compactează; totul se testează în CI, ca orice cod.
Ingineria datelor este dezvoltarea, implementarea și întreținerea sistemelor și proceselor care primesc date brute și produc informație de calitate, consistentă, care susține cazuri de utilizare din aval, precum analiza și învățarea automată.Fundamentals of Data Engineering (2022), cap. 1 — definiția ingineriei datelor
De ce contează Un pipeline care nu poate fi rerulat fără să dubleze datele transformă fiecare incident într-o operațiune manuală de curățenie.
Deschide pe YouTube ↗ - 14
Lecția 14 · Calitatea se construiește în pipeline, nu se inspectează în raport.
Out of the Crisis · W. Edwards Deming · 1986
Calitatea datelor are dimensiuni care se pot măsura: completitudine (lipsesc rânduri sau valori?), unicitate (chei duplicate?), validitate (valori din setul permis, tipuri corecte), consistență (totalul liniilor egal cu totalul comenzii?), actualitate (au sosit la timp?) și acuratețe (corespund realității?). Punctul lui Deming aplicat aici: testele sunt cod și stau lângă transformări, nu într-un raport de audit trimestrial. La intrare, contracte (lecția 12). Pe fiecare model: not null, unic, valori acceptate, relații între tabele, prospețime, volum așteptat. La ieșire, monitoare de anomalie pe cifrele care contează — numărul de rânduri pe zi, distribuțiile, sumele. Ce faci când un test pică depinde de ce e în joc: pentru cifre critice, încărcarea se oprește (fail-closed) și cineva se uită; pentru rest, rândurile suspecte merg în carantină și restul trece. Fiecare set de date are un proprietar și un SLA, iar cauza se repară în amonte, nu în raport. Raportul e locul unde afli; nu e locul unde repari.
Încetați să depindeți de inspecție pentru a obține calitate. Eliminați nevoia de inspecție în masă construind calitatea în produs de la bun început.Out of the Crisis (1986), cap. 2 — cele 14 puncte pentru management, punctul 3
De ce contează Aceeași eroare costă o linie de test la intrare și o săptămână de reconciliere când e găsită în raport. Diferența e locul, nu efortul.
Deschide pe YouTube ↗ - 15
Lecția 15 · Gunoi la intrare, gunoi la ieșire — iar proveniența e singurul mod de a afla pe unde a intrat gunoiul.
Passages from the Life of a Philosopher · Charles Babbage · 1864
Întrebarea care l-a scos din sărite pe Babbage e pusă și azi, doar că altfel: „modelul e bun, deci cifrele sunt bune?”. Nu. Un depozit nu repară ce primește; cel mult face vizibil de unde a venit. Asta e proveniența (lineage): pentru fiecare cifră dintr-un raport, drumul înapoi prin transformări până la rândurile și fișierul sursă, la nivel de coloană, dedus din SQL-ul care le-a produs. Pe fiecare rând, metadate mici: sistemul sursă, identificatorul încărcării, momentul extragerii. La ce folosește: analiza de impact (dacă sursa X se strică, ce rapoarte suferă?), depanarea (cifra asta ciudată — din ce rânduri? din ce fișier? din ce zi?), conformitatea (pe unde circulă datele personale, lucru pe care GDPR îl cere explicit) și reproductibilitatea (același cod, aceeași versiune de date, același rezultat). Cazul tipic: o coloană de sumă trece în amonte din lei în euro fără anunț; totalurile „arată plauzibil” două săptămâni. Nicio validare de tip nu prinde asta. Singura apărare e să poți urmări fiecare cifră înapoi și să vezi ziua în care distribuția s-a mutat.
De două ori am fost întrebat: «Vă rog, domnule Babbage, dacă introduceți în mașină cifre greșite, vor ieși răspunsurile corecte?» … Nu sunt în stare să pricep ce fel de confuzie a ideilor ar putea provoca o asemenea întrebare.Passages from the Life of a Philosopher (1864), cap. V — Difference Engine No. 1
De ce contează Cifra greșită care „arată bine” e cea mai scumpă din depozit. Fără proveniență, o cauți săptămâni; cu ea, o găsești într-o interogare.
Deschide pe YouTube ↗ - 16
Lecția 16 · Pregătirea datelor pentru ML: același depozit, dar cu timpul corect, fără scurgeri și cu trăsături reproductibile.
The Unreasonable Effectiveness of Data · Alon Halevy, Peter Norvig, Fernando Pereira · 2009
Argumentul din 2009 a devenit doctrină: mai multe date bat un model mai deștept. Condiția nespusă e că datele sunt corecte în trei feluri pe care un depozit de raportare nu le cere. Întâi, corectitudinea în timp: o trăsătură (feature) se calculează „așa cum era la momentul evenimentului”, nu „așa cum e acum” — un client care azi e premium era, poate, gratuit când a făcut comanda din 2023; dimensiunile de tip 2 sunt exact ce trebuie aici. Apoi, scurgerea (leakage): o coloană care conține răspunsul, direct sau indirect — data anulării când prezici anularea; modelul pare excelent la antrenament și eșuează în producție. În fine, decalajul antrenament–servire (skew): trăsăturile calculate în batch pentru antrenament și cele calculate live pentru predicție trebuie să vină din același cod; feature store-ul există ca să garanteze asta. Restul e disciplină de depozit: împarți seturile pe timp, nu la întâmplare; documentezi definiția etichetei; versionezi setul de antrenament ca să-l poți reproduce peste un an; păstrezi echilibrul claselor în minte. Abia atunci „multe date” înseamnă mai mult decât „multe rânduri”.
Dar, invariabil, modelele simple și multe date bat modelele mai elaborate bazate pe mai puține date.The Unreasonable Effectiveness of Data, IEEE Intelligent Systems 24(2), 2009
De ce contează Un model antrenat pe trăsături „de azi” pentru evenimente „de atunci” învață viitorul și îl uită în producție. Depozitul trebuie să știe să răspundă „cum era la momentul acela”.
Deschide pe YouTube ↗ - 17
Lecția 17 · Embeddings: sensul devine un vector, iar depozitul primește o coloană pe care nu o poate compara cu «=».
A synopsis of linguistic theory 1930–1955 · John Rupert Firth · 1957
Ideea lui Firth, formalizată: un model citește un text și îl transformă într-un vector de câteva sute sau mii de numere, astfel încât texte cu sens apropiat ajung vectori apropiați. Apropierea se măsoară — de obicei cosinus — nu se verifică prin egalitate. De aici o structură nouă în depozit: indexul vectorial (HNSW, IVF), care găsește aproximativ cei mai apropiați vecini fără să compare cu fiecare rând. Peste el stă RAG: caută fragmentele relevante după sens, apoi lasă un model generativ să răspundă folosindu-le. Ce se schimbă pentru inginerul de date: versiunea modelului de embedding face parte din schemă — schimbi modelul, recalculezi coloana, altfel compari vectori din spații diferite. Se stochează lângă vector: textul, un hash al lui, modelul, dimensiunea. Fragmentarea (chunking) e o decizie de modelare, nu un detaliu. Filtrele pe metadate (limbă, dată, client) se aplică înainte sau împreună cu căutarea vectorială, nu după. Și vectorii nu înlocuiesc cheile și join-urile: sunt o coloană în plus într-un tabel care rămâne relațional.
Vei cunoaște un cuvânt după compania pe care o ține.A synopsis of linguistic theory 1930–1955, în Studies in Linguistic Analysis (1957)
De ce contează Un depozit care nu știe ce model a produs un vector nu poate spune dacă doi vectori sunt comparabili. Coloana fără versiune e o coloană fără sens.
Deschide pe YouTube ↗ - 18
Lecția 18 · Separă identitatea de fapte: un seif de identități, un token în depozit și nicio cale înapoi fără cheie.
Regulamentul general privind protecția datelor (GDPR) · Parlamentul European și Consiliul Uniunii Europene · 2016
Definiția din regulament e, citită ca arhitectură, o schemă cu trei părți. Seiful de identitate: nume, e-mail, cod numeric personal, adresă — criptat, cu acces restrâns, în propriul lui sistem. Harta de tokeni: legătura dintre persoană și un pseudonim, fie un surogat aleatoriu, fie un HMAC cu o cheie secretă; un simplu hash al e-mailului nu ajunge, se inversează cu un dicționar. Depozitul: faptele, cheiate pe token. Analiștii văd tokeni și comportamente, niciodată identități; join-ul înapoi e posibil doar pentru cine are cheia, și e auditat. Ce câștigi: ștergerea unei persoane devine ștergerea rândului din seif — faptele rămân, dar nu mai duc la nimeni; accesul la identitate se acordă separat de accesul la analiză; „unde sunt datele acestei persoane” se răspunde din proveniență (lecția 15). Ce nu câștigi: datele pseudonimizate rămân date personale, fiindcă informația suplimentară există. Toate obligațiile rămân; riscul scade. Anonimizarea reală e alt prag, cu propriile ei capcane — lecția 20.
«pseudonimizare» înseamnă prelucrarea datelor cu caracter personal într-un asemenea mod încât acestea să nu mai poată fi atribuite unei anume persoane vizate fără a se utiliza informații suplimentare, cu condiția ca aceste informații suplimentare să fie stocate separat și să facă obiectul unor măsuri de natură tehnică și organizatorică care să asigure neatribuirea respectivelor date cu caracter personal unei persoane fizice identificate sau identificabileRegulamentul (UE) 2016/679, art. 4 pct. 5
De ce contează Un depozit cu numele pe fiecare rând nu poate șterge o persoană fără să rescrie istoricul. Unul cu tokeni o șterge dintr-un singur rând, în altă cameră.
Deschide pe YouTube ↗ - 19
Lecția 19 · Datele personale sunt un pasiv: inventariază-le, colectează minim, păstrează cu termen și șterge demonstrabil.
Data Is a Toxic Asset, So Why Not Throw It Out? · Bruce Schneier · 2016
Schneier răstoarnă intuiția „datele sunt petrolul nou”: ce nu ai nu ți se poate fura, nu ți se poate cere în instanță și nu trebuie să explici de ce l-ai păstrat. Pentru depozit, asta devine un ciclu cu patru etape. Inventar: unde sunt coloanele cu date personale, marcate prin clasificare (direct identificatoare, cvasi-identificatoare, sensibile), descoperite automat pe cât se poate. Minimizare: colectezi doar ce servește un scop declarat; o dată de naștere completă când ai nevoie de grupa de vârstă e o coloană de prisos. Retenție: fiecare set are un termen și un motiv, iar ștergerea e un job programat, nu o promisiune. Ștergere: demonstrabilă, inclusiv din copii de siguranță și din date derivate. Și aici se împacă imutabilitatea (lecția 10) cu dreptul la ștergere: crypto-shredding. Datele fiecărei persoane se criptează cu propria ei cheie; ștergerea persoanei înseamnă distrugerea cheii. Rândurile rămân fizic în fișierele imuabile și în backup-uri, dar nu se mai pot citi de nimeni, niciodată. Un log care nu se poate rescrie și o ștergere care nu se poate anula, deodată.
Datele sunt un activ toxic. Trebuie să începem să le gândim așa și să le tratăm cum am trata orice altă sursă de toxicitate.Data Is a Toxic Asset, So Why Not Throw It Out? — eseu, CNN / schneier.com, martie 2016
De ce contează Fiecare coloană cu date personale pe care nu o folosești e risc fără beneficiu. Costul ei nu se vede în factura de stocare, ci în ziua incidentului.
Deschide pe YouTube ↗ - 20
Lecția 20 · Fără nume nu înseamnă anonim: codul poștal, data nașterii și sexul identifică majoritatea oamenilor.
k-anonymity: a model for protecting privacy · Latanya Sweeney · 2002
Sweeney a arătat, cu date reale de spital „anonimizate” și o listă electorală cumpărată, că trei coloane banale — codul poștal, data nașterii și sexul — sunt unice pentru cea mai mare parte a populației. Coloanele astea sunt cvasi-identificatori: fiecare e inofensivă singură, împreună sunt un nume. k-anonimitatea e răspunsul ei: publicarea e sigură doar dacă fiecare combinație de cvasi-identificatori apare la cel puțin k persoane. Se obține prin generalizare (data nașterii devine an sau grupă de vârstă, codul poștal devine județ) și suprimare (rândurile prea rare se scot). Nu e sfârșitul poveștii. Dacă toți cei k dintr-un grup au același diagnostic, tot ai aflat ceva despre fiecare — de aici l-diversitatea și t-apropierea, care cer varietate în atributul sensibil. Și orice set „anonim” poate fi legat cu un alt set publicat mâine. Pentru depozit, regula practică: un extras fără nume nu e anonim până nu i-ai numărat cvasi-identificatorii și nu ai verificat k pe fiecare combinație; iar agregarea e prietena ta — un raport pe grupe de vârstă și județ nu are rânduri de persoane deloc.
O publicare oferă protecție de tip k-anonimitate dacă informația despre fiecare persoană conținută în publicare nu poate fi deosebită de cea a cel puțin k-1 alte persoane ale căror informații apar, de asemenea, în publicare.k-anonymity: a model for protecting privacy, International Journal of Uncertainty, Fuzziness and Knowledge-Based Systems 10(5), 2002 — rezumat
De ce contează Extrasul „fără nume” trimis unui partener e cea mai des întâlnită scurgere de date personale și cea mai rar recunoscută ca atare.
Deschide pe YouTube ↗ - 21
Lecția 21 · Confidențialitatea diferențială: adaugi zgomot calibrat la răspuns, nu la date, și promisiunea rezistă la orice alt set publicat.
The Algorithmic Foundations of Differential Privacy · Cynthia Dwork, Aaron Roth · 2014
Slăbiciunea k-anonimității e că depinde de ce altceva există în lume. Confidențialitatea diferențială schimbă întrebarea: nu „poate fi reidentificat setul?”, ci „se schimbă răspunsul la o interogare dacă o singură persoană intră sau iese din set?”. Dacă răspunsul e aproape același cu sau fără tine, nimeni nu învață nimic despre tine anume, orice ar mai ști. Mecanismul: la fiecare răspuns agregat (o sumă, o medie, un număr) se adaugă zgomot aleatoriu calibrat la cât poate mișca un singur individ rezultatul. Parametrul epsilon e bugetul: mai mic înseamnă mai multă protecție și mai puțină precizie, iar fiecare interogare consumă din el — după ce bugetul s-a terminat, nu mai răspunzi. Pentru depozit e o tehnică de ieșire, nu de stocare: datele brute rămân întregi și protejate ca în lecțiile 18–19; ce se protejează e ce pleacă spre exterior — statistici publice, rapoarte către parteneri, seturi de antrenament. Recensămintele moderne o folosesc exact așa. Costul e real și trebuie spus utilizatorilor: pe grupuri mici, zgomotul e comparabil cu semnalul.
„Confidențialitatea diferențială” descrie o promisiune făcută de deținătorul datelor, sau curator, persoanei vizate: „Nu vei fi afectat, negativ sau altfel, dacă permiți ca datele tale să fie folosite în orice studiu sau analiză, indiferent ce alte studii, seturi de date sau surse de informație există.”The Algorithmic Foundations of Differential Privacy (2014), cap. 1 — The Promise of Differential Privacy
De ce contează E singura definiție a confidențialității care nu se prăbușește când apare mâine un alt set de date. Prețul e precizia, iar prețul trebuie spus.
Deschide pe YouTube ↗ - 22
Lecția 22 · Depozitul e cea mai mare țintă din firmă fiindcă adună totul; apărarea e privilegiul minim, aplicat pe rânduri și coloane, cu audit.
The Protection of Information in Computer Systems · Jerome Saltzer, Michael Schroeder · 1975
Un sistem sursă expune un client; depozitul îi expune pe toți, cu istoric. De aceea principiul din 1975 e aici mai concret decât oriunde. Privilegiul minim se aplică pe trei axe: tabele (un rol vede doar schemele de care are nevoie), coloane (mascare dinamică: analistul vede ultimele patru cifre, contabilitatea vede tot) și rânduri (securitate la nivel de rând: un agent de vânzări vede doar regiunea lui, prin aceeași vedere, fără copii ale tabelului). Accesul la seiful de identitate din lecția 18 e un rol separat, acordat separat. Dedesubt, două straturi care nu se negociază: criptare în repaus, cu chei administrate în afara depozitului și rotite, și criptare în tranzit. Deasupra, auditul: fiecare interogare pe tabelele sensibile e înregistrată — cine, când, ce a citit, câte rânduri — păstrată și, mai ales, citită de cineva. Capcanele obișnuite sunt banale și de aceea frecvente: un cont de serviciu pentru instrumentul de BI cu drepturi pe tot depozitul, folosit de toți; secrete lipite în SQL; conturi partajate care fac auditul inutil. Regula lui Saltzer și Schroeder, citită invers: orice privilegiu pe care nu-l poți justifica printr-o sarcină e unul de scos.
Fiecare program și fiecare utilizator al sistemului ar trebui să opereze folosind cel mai mic set de privilegii necesar pentru a-și îndeplini sarcina.The Protection of Information in Computer Systems, Proceedings of the IEEE 63(9), 1975 — §I.A.3, principiul (f), least privilege
De ce contează Un singur cont de serviciu supra-privilegiat transformă o breșă într-un instrument de raportare într-o scurgere a întregului istoric al firmei.
Deschide pe YouTube ↗ - 23
Lecția 23 · Cloud sau on-prem nu e o întrebare despre loc, ci despre cine operează ce și cât costă la gol față de vârf.
All Things Distributed · Werner Vogels · 2008
În cloud, calculul e separat de stocare și elastic: plătești pe secundă sau pe interogare, nu cumperi hardware, actualizările sunt ale furnizorului, regiunile sunt la un click. Reversul: costul de ieșire a datelor (egress), interogări nemărginite care produc facturi surpriză, dependența de furnizor și rezidența datelor, care pentru date reglementate cere regiune europeană și chei administrate de tine. On-prem: cost previzibil la sarcină constantă, control complet, latență mică spre sistemele interne, date sensibile care nu pleacă. Reversul: planificare de capacitate (plătești vârful tot anul), actualizări, oameni, un singur amplasament. Decizia se ia pe câteva variabile, nu pe modă: cât de variabilă e sarcina, ce cere reglementarea, ce știe echipa, ce licențe există deja, cât iese pe egress. Hibridul e legitim: seiful de identitate acasă, faptele pe token în cloud. Iar fraza lui Vogels e valabilă în ambele locuri: discul cedează și în centrul tău de date, furnizorul are și el pene. Deci copii de siguranță testate prin restaurare, nu prin existență; exerciții de refacere; două zone. „Unde” e întrebarea mică; „ce se întâmplă când cedează” e cea mare.
Orice lucru cedează, tot timpul.Werner Vogels — formulare reluată în prezentări și pe blogul All Things Distributed, din 2008
De ce contează Factura cloud crește cu fiecare interogare neglijentă; factura on-prem e fixă și include vârful pe care îl atingi o zi pe an. Ambele se pierd dacă nu ai testat o restaurare.
Deschide pe YouTube ↗ - 24
Lecția 24 · Un join se execută în trei feluri, iar cele 3% critice ale lui Knuth sunt, într-un depozit, aproape întotdeauna un join.
Structured Programming with go to Statements · Donald Knuth · 1974
Trei algoritmi. Nested loop: pentru fiecare rând dintr-o parte, caută în cealaltă — imbatabil când o parte e minusculă sau cealaltă e indexată pe cheie, dezastruos între două tabele mari. Hash join: construiește o tabelă hash din partea mai mică, apoi trece o singură dată prin partea mare și potrivește — calul de povară al depozitului, cu condiția ca partea mică să încapă în memorie; dacă nu, se varsă pe disc și devine de zece ori mai lent. Merge join: ambele părți sortate pe cheie, o singură trecere paralelă — perfect când datele sunt deja ordonate. Planificatorul alege dintre ele pe baza statisticilor; statistici vechi înseamnă plan greșit, deci actualizarea lor e întreținere, nu opțiune. În sistemele distribuite se adaugă o întrebare: unde se întâlnesc rândurile? O dimensiune mică se difuzează (broadcast) pe toate nodurile; două tabele mari se redistribuie (shuffle) după cheie, iar o cheie fierbinte — adesea NULL — trimite jumătate din date pe un singur nod. Și capcana clasică: join-ul pe o cheie neunică multiplică rândurile, tăcut; dimensiunea de tip 2 din lecția 11 e exemplul tipic. Înainte de a optimiza, citește planul. Cele 3% sunt acolo, cu nume.
Ar trebui să uităm de micile eficiențe, să zicem cam 97% din timp: optimizarea prematură e rădăcina tuturor relelor. Și totuși n-ar trebui să ratăm ocaziile din acele critice 3%.Structured Programming with go to Statements, ACM Computing Surveys 6(4), 1974
De ce contează Nouă interogări din zece nu contează; a zecea ține tot raportul de dimineață, și în ea e un join care se varsă pe disc sau multiplică rânduri.
Deschide pe YouTube ↗ - 25
Lecția 25 · Într-un depozit columnar, structura de date e așezarea pe disc: partiția și ordinea decid câte blocuri nu trebuie citite.
Mesaj pe lista de discuții git · Linus Torvalds · 2006
Indexul B-tree e regele bazelor tranzacționale: găsește un rând în câțiva pași. Depozitele columnare merg pe alt principiu: nu caută un rând, sar peste blocuri. Fiecare bloc de coloană poartă un min și un max (zone map); dacă filtrul cere „luna martie” și blocul acoperă „iunie–iulie”, blocul nu se citește deloc. De aici două pârghii. Partiționarea — de regulă pe dată — elimină directoare întregi înainte să înceapă interogarea. Clusterizarea (sau cheia de sortare) grupează valorile apropiate în aceleași blocuri, ca zone map-urile să aibă ce sări; fără ea, fiecare bloc conține de toate și niciunul nu se poate sări. Regulile de proiectare urmează filtrele, nu sosirea datelor: partiționezi pe ce se filtrează în interogări, clusterizezi pe a doua coloană din WHERE. Prea multe partiții înseamnă fișiere mici și metadate mai scumpe decât datele; se compactează. Pentru egalitate pe coloane cu multe valori distincte, filtrele Bloom; pentru agregări repetate, vederi materializate; iar statisticile actualizate rămân condiția ca toate acestea să fie folosite. Torvalds vorbea despre cod, dar regula ține: nu optimiza interogarea; așază datele astfel încât interogarea să nu aibă ce citi.
Programatorii slabi se îngrijorează de cod. Programatorii buni se îngrijorează de structurile de date și de relațiile dintre ele.Mesaj pe lista de discuții git, 27 iunie 2006
De ce contează Diferența dintre o interogare de trei secunde și una de trei minute pe aceleași date e, de obicei, doar ordinea în care au fost scrise pe disc.
Deschide pe YouTube ↗ - 26
Lecția 26 · Fiecare treaptă a memoriei e de o sută de ori mai lentă decât cea de deasupra; o interogare e rapidă exact cât rămâne pe treapta de sus.
Tape is Dead, Disk is Tape, Flash is Disk, RAM Locality is King · Jim Gray · 2006
Cifrele lui Gray sunt tot valabile ca ordine de mărime: RAM în nanosecunde, SSD în zeci de microsecunde, disc rotativ în milisecunde, stocare de obiecte în zeci de milisecunde plus un cost per cerere. Între trepte sunt factori de sută. „Discul e bandă” înseamnă că discul mai merită doar pentru citiri secvențiale — de aici formatul columnar, compresia și așezarea sortată din lecția 25: transformă accesul aleatoriu în parcurgere. „Localitatea în RAM e rege” înseamnă că interogarea rapidă e cea al cărei set de lucru încape în memorie: tabela hash a join-ului, sortarea, agregarea. În clipa în care nu mai încape, se varsă pe treapta următoare și încetinește de zece până la o sută de ori, fără să dea vreo eroare. Depozitele cloud fac ierarhia explicită: stocarea de obiecte e baza, ieftină și nelimitată; nodurile de calcul țin un cache pe SSD local; rezultatele recente stau într-un cache de rezultate. Dimensionarea nu e „câtă memorie are serverul”, ci „câtă memorie primește fiecare slot de interogare” — zece interogări paralele împart același RAM. Compresia columnară e, în termenii lui Gray, cel mai ieftin mod de a urca o treaptă: aceleași date, de cinci ori mai mici, încap acolo unde înainte nu încăpeau.
Banda e moartă, discul e bandă, flash-ul e disc, localitatea în RAM e rege.Prezentare la Storage Guru Gong Show, Redmond, 10 decembrie 2006 (reluată la CIDR 2007)
De ce contează O interogare care „a mers ieri” și azi durează de zece ori mai mult n-a devenit mai grea; setul ei de lucru a coborât o treaptă în ierarhie.
Deschide pe YouTube ↗ - 27
Lecția 27 · La ce să fii atent: depozitele nu mor de tehnologie, mor de lipsa unui model, a unui proprietar și a unui utilizator.
The Mythical Man-Month · Frederick P. Brooks Jr. · 1975
Brooks spunea că tabelele explică sistemul; într-un depozit, tabelele sunt sistemul. Prima cauză de eșec e lipsa modelului: sursele copiate una lângă alta și numite „depozit”, fără fapte și dimensiuni, fără o definiție comună pentru „client” sau „venit” — deci trei rapoarte cu trei cifre. A doua e lipsa proprietarului: seturi fără cineva care răspunde de ele, fără teste, unde o coloană redenumită la sursă trece neobservată o lună. A treia e lipsa utilizatorului: un depozit construit „pentru când o să avem nevoie”, în loc de o decizie concretă de luni dimineață. Restul e o listă de verificat, pe scurt. Cheia surogat și cea naturală confundate, join-uri care multiplică rânduri. Fusuri orare și monede amestecate în aceeași coloană. Ora evenimentului confundată cu ora încărcării. NULL-ul care dispare din agregări și din join-uri fără să anunțe. Interogări nemărginite într-un sistem plătit pe citire. Date personale pe fiecare rând, fără seif și fără termen. Streaming acolo unde o încărcare zilnică era de ajuns. Și o documentație care lipsește — deși, cum spune Brooks, un tabel bine numit și bine cheiat e cea mai bună documentație pe care o vei scrie vreodată.
Arată-mi diagramele tale de flux și ascunde-ți tabelele, și voi rămâne nedumerit. Arată-mi tabelele, și de obicei nu voi mai avea nevoie de diagrame; vor fi evidente.The Mythical Man-Month (1975), cap. 9 — Ten Pounds in a Five-Pound Sack
De ce contează Nicio unealtă nu compensează un model lipsă, un set fără proprietar sau un depozit fără utilizator. Toate trei se văd în tabele, dacă te uiți.
Deschide pe YouTube ↗