Ward Cunningham a introdus această metaforă, explicând că "livrarea codului pentru prima dată este ca și cum ai lua un credit", apoi imediat scoțând în evidență că neplătirea acestei datorii curând "se contabilizează ca o dobândă la acel credit". Metafora folosită este extrem de sugestivă, deoarece puteți vizualiza imediat dobânda care se adaugă în timp la datoria inițială. Același autor remarcă, mai bine de 20 ani mai târziu, că "ironia în industria noastră este că se dovedește mai 'greu' codul decât partea hardware atunci când produsul este terminat și programatorii eliberați."
Un alt autor tehnic celebru, Uncle Bob, face o distincție clară între datoria tehnică și codul scris delăsător, atunci când notează că "dezordinea nu este datorie tehnică". În continuarea acestui articol, ne vom referi la acele proiecte care sunt create și dezvoltate corect.
Există o multitudine de moduri în care datoria tehnică se adaugă unui proiect. Iar Martin Fowler duce explicația mai departe atunci când face diferența între datoria prudentă și cea nechibzuită.
Pe 31 iulie 2012, Knight Capital Group, cel mai mare trader de capital din SUA a falimentat în doar 45 minute, din cauza unei serii de evenimente care a dus la executarea unui cod nefolosit de 8 ani care nu a fost eliminat din codebase, cunoscut și ca funcționalitatea Power Peg.
Algoritmul lor de bază se numea SMART, fiind un router automat care trimitea comenzi în piață. Atunci când a scris un update pentru acesta, echipa a refolosit un flag vechi, care până în acel moment activa Power Peg. Codul trebuia să fie instalat pe 8 mașini, un proces manual derulat în câteva zile, însă doar 7 din acestea l-au recepționat. Când piața a început tranzacționarea și codul a început să fie executat cele 7 mașini care aveau codul corect au funcționat așa cum trebuia, însă cea de-a 8-a mașină a început să execute codul Power Peg (considerat 'mort'), iar acesta a început să inunde piața cu comenzi neobișnuite; în total au fost peste 4 milioane de tranzacții care au condus la pierderi de \$460 milioane de dolari.
Cauza nu este lipsa testării, nici deployment-ul incorect luate individual, ci mai degrabă lanțul de evenimente care a condus la executarea codului Power Peg într-un mediu de producție fără posibilitatea de a-l dezactiva, așa cum arată raportul SEC. Datoria tehnică și dobânda acesteia au fost plătite simultan. Așa cum un medic a explicat "atribuirea unui accident unei singure 'cauze originare' este în mod fundamental greșită [...] nu reflectă o înțelegere tehnică a naturii eșecului, ci mai degrabă o nevoie socială, culturală, de a da vina pe forțe specifice, localizate, sau pe evenimente pentru anumite rezultate."
Este important să înțelegem mentalitatea intrinsecă a fiecărui membru al echipei, deoarece aduce într-o lumină specială felul în care ei abordează procesul de construcție software. Ne preocupă acest aspect datorită legii lui Conway care spune că "orice organizație care proiectează un sistem va produce inevitabil unul a cărui structură este o copie a structurii de comunicare din interiorul acelei organizații".
Chiar dacă nu aveți persoane specializate pentru fiecare din aceste roluri, fiind posibil ca o persoană să îndeplinească mai multe simultan, este important să fiți conștienți de importanța fiecăruia dintre aceste roluri și să le folosiți în avantajul vostru.
Trebuie să integrăm testarea în modul de lucru al dezvoltatorilor, fără să o lăsăm până când codul e deja scris. Veți întâmpina rezistență, deoarece modificarea status quo-ului nu e niciodată un lucru ușor. Chiar dacă echipa dorește să facă testare, nivelele lor de competență sunt diferite, iar armonizarea acestora într-o strategie comună va prezenta câteva probleme.
Care sunt cel mai des întâlnite scuze pe care le folosesc inginerii software atunci când nu vor să includă testarea în fluxul de lucru?
"Avem o echipă de testare bine pregătită. Este treaba lor să semnaleze problemele posibile."
"Clientul nostru a cerut această funcționalitate în producție până lunea viitoare. Nu este timp pentru altceva decât implementare. Nu avem timp pentru altfel de teste decât cele pe cale oricum le face testerul nostru manual."
"Codul meu funcționează, am un istoric foarte bun. De ce m-aș apuca acum de testare?"
"Clientul nu plătește decât pentru software funcțional. Programatorii sunt scumpi, nu putem vinde acest timp suplimentar clientului, deoarece va alege altă firmă cu care să colaboreze."
"Fac doar o modificare cosmetică minoră, n-are ce să se întâmple."
Să ne uităm puțin la cele mai importante tipuri de teste pe care sistemul nostru trebuie să le primească. Mai jos le vom menționa doar pe cele mai comune, vă recomand să consultați clasificarea extinsă și descrierile asociate
Este cel mai cunoscut tip de testare și de obicei se execută fără neapărat ca echipa să fie conștientă de aceasta. Scopul său este să determine dacă cerințele sunt îndeplinite, fără a merge în detaliu pentru fiecare funcționalitate în parte.
Testul simplu de acceptanță funcțională (FAST) ar trebui să fie executat la fiecare release, pentru a verifica dacă funcționalitățile majore sunt accesibile și funcționale pe o configurație cunoscută (de obicei minimală) a sistemului. Permite înlocuirea frazei "la mine merge" cu un mult mai relevant "merge în QA".
Testarea de acceptanță de conformitate verifică dacă software-ul este conform cu anumite regulamente. Pentru proiectele care au nevoie de a fi conforme cu un set de reguli, este deosebit de important și trebuie verificat des.
Testarea de acceptanță a instalării ia un release și îl instalează complet într-un mediu care este foarte apropiat (recomandabil identic) cu producția.
Testele funcționale orientate pe task (TOFTs) se adresează urmăririi happy flow-ului fiecărui feature în parte și verificării validității acestora.
Testele de forțare a erorilor (FOTs) obligă programul să intre pe condiții de eroare și verifică faptul că sunt tratate corespunzător.
Testarea exploratorie indică problemele care altfel nu ar fi găsite, deoarece nu urmează un plan stabilit dinainte.
Celelalte tipuri de verificări sunt la fel de importante, mai jos le vom enumera doar: testarea încărcării, stresului, performanței, securității, scalabilității, regresiei, funcționalității (usability), a documentației, testarea instalării/dezinstalării.
Introdus în 2006 de către Dan North, modul de dezvoltare bazat pe comportament (BDD) solicită folosirea unui vocabular comun atât de către oamenii de business cât și de dezvoltatori, pentru a descrie comportamentul sistemului prin intermediul criteriilor de acceptanță. Gherkin este limbajul folosit pentru această descriere, acesta permițând o descriere amănunțită a comportamentului dorit. Stilul Given-When-Then a devenit mult mai popular decât ar fi crezut creatorii săi.
Scrierea de teste care conduc sistemul automat prin scenarii verificate de părțile interesate asigură claritatea specificațiilor și previne introducerea regresiilor.
Pe scurt: nu este mai bun. TDD este mai granular, iar disciplina sa adaugă o multitudine de beneficii codului. Dar aceste metode abordează nivele diferite de testare, de aceea nu le putem compara între ele. Ceea ce dorim să sugerăm este să încercați BDD primul.
Modul în care BDD a apărut a fost tocmai nefericirea lui Dan North cu adoptarea TDD în cazul său personal. Așa că a mers un nivel mai sus la procesul de analiză, unde a găsit o metodă de a-l simplifica. De aceea, a câștigat atât de multă popularitate într-o sumedenie de limbaje.
Înțelegem faptul că modificarea întregului mod în care livrați sofware astăzi este o schimbare drastică ce nu poate avea loc peste noapte. De aceea, am extras câteva practici pe care le puteți încorpora chiar astăzi, pentru a vedea beneficiile imediat.
Testarea nu ar trebui să se petreacă după ce codul a fost deja scris. Programatorii care învață conceptele de testare încep să gândească și distructiv, chiar înainte de a scrie cod, astfel că vor produce un cod superior calitativ.
Pentru o anumită funcționalitate, o persoană poate fie să scrie scenariile de test, fie codul, dar nu ambele, deoarece "un programator nu ar trebui să își testeze niciodată propriul cod". Poate sună puțin ca pair-programming, dar perechea lucrează în paralel cu scopuri diferite.
Notă: chiar dacă unul din programatori devine foarte îndemânatic la a scrie scenariile, asigurați-vă că la selecția următoare va scrie cod, pentru a-i păstra îndemânarea.
Partiționarea datelor de intrare. Divizați datele de intrare în mulțimi care au același comportament pentru a putea scrie doar un singur test pentru fiecare și a elimina astfel testele redundante care parcurg același cod. Partiționați în: date goale, date singulare, date multiple.
Testarea limitelor: Identificați elementele de la limite și scrieți câte un test pentru fiecare. Exemple: zero; null; valori minime/maxime; mulțimea vidă; șirul de caractere gol.
Tratați modulele de cod drept componente independente. Descrieți comportamentul acestora folosind intrări și ieșiri statice care pot fi alimentate testelor automate. Dacă există o componentă care nu se poate mula pe această abordare, înseamnă că aceasta nu a fost corect specificată în cerințe.
Această metodă face ca testele să fie mai rapide, pentru că nu e nevoie să pregătim întregul sistem, iar acesta poate să revină la starea anterioară după ce testul a fost finalizat.
Un beneficiu suplimentar este acela că testele devin modulare, astfel că ele depind de (și descriu) specificațiile.
Introducerea de erori în funcționalitățile existente nu este de dorit. BDD ne ajută să prevenim acest lucru prin faptul că avem testele scrise deja. Dar ele trebuie să poată rula cât mai ușor pentru a fi folosite.
Asigurați-vă că fiecare programator poate rula întreaga suită de teste fără prea mare efort. Cu cât e mai mare efortul necesar executării acesteia, cu atât e mai mică probabilitatea ca acest lucru să se petreacă în realitate.
de Ovidiu Mățan
de Vlad But
de Maria Revnic
de Delia Mircea