Saturday, March 26, 2005

Ziua unui programator - de la entuziasm la deprimare

10.03
Gmail, hotmail, outlook.
Am terminat toate mailurile de citit.
Si cafeaua de baut.
Nici macar pe cele doua-trei forumuri pe care le monitorizez nu se mai intampla nimic.
Asa ca pana la urma, tot va trebui sa incep sa lucrez.
Si totusi ... daca intre timp o mai fi venit ceva pe Gmail ?

10.27
Tocmai m-a palit IDEEA.
Acum mi-e clar ce am de facut pentru noul feature cerut de client.
Ma apuc furibund de batut in tastatura ca la fasole. Ideeile vin ceva mai repede decat reusesc sa debitez garla asta de property get si set-uri...
E clar, imi trebuie inca o clasa. Deschid un fisier nou si incep sa schitez noua clasa.

12.41
Degetele au inceput sa-mi ezite.
Probabil aici o sa-mi trebuiasca ceva configurari .. Notez in todo list .. Iar bucata asta de cod va trebui revizuita. Nu mi-e prea clar daca o sa ruleze din prima ..
O sa trebuiasca sa-mi aduc aminte sa mai revin peste asta o data... Poate ar trebui sa imi pun un #warning.

12.55
In sfarsit.
Asta a fost.
CTRL+SHIFT+B.
Building...
Una, doua, cinci .. vreo sase erori.
Litere mancate, linii ramase neterminate. O paranteza lipsa. Nimic grav.

12.59
F5 - Run.
Aplicatia porneste.
Si ce daca ?
Am uitat sa adaug noul form in menu.

13.01
F5 din nou.
Deschid nerabdator formul proaspat (inca arata cam "deranjat").
Introduc cateva date la intamplare in textbox-urile required.
Apas OK(Save) pentru a salva in baza de date ..
Error.
Mda.
Stop debugging. O mica corectie.

F5 din nou.
Deschid formul.
Introduc datele la intamplare (oare or fi la fel ca mai devreme?).
Ok(Save)....
Error.
Fir-ar.. Ah .. uitasem. Aici ar mai fi trebuit pus un if pentru cazul asta special...

F5 din nou.
Deschid formul. Introduc datele la intamplare (pe cine pacalesc ? nu mai am atata imaginatie .. e clar ca sunt tot datele de mai devreme).
Ok(save)...
Error.
Ah,da. Cred ca aici de fapt ar fi trebuit i+1.

F5 din nou.
Deschid formul.
Introduc datele mecanic.
OK (save) ..
Error.
Lumea sa prabuseste in jurul meu.
Ce o mai fi si de data asta ?
Mi-e somn.
Si greata.
Simt ca ma paste o depresie.
Urasc tasta F5. Si croncanitul hardului in timp ce face building. Si mesajul ala urta de eroare.

Oare o mai fi venit ceva pe Gmail ?


...
Asa arăta o zi de lucru pentru mine - de la entuziasm la depresie.Am avut intotdeauna dificultati in a pastra un ritm sustinut intr-o activitate (si am toata admiratia pentru cei care reusesc sa faca asta fara un efort deosebit).Pentru mine insa lucrurile nu's chiar roze - ma plictisesc repede, si imi pierd energia. Pentru mine un ritm sustinut de lucru cere eforturi supraomenesti. Si o multime de dulciuri sau alte chestii rontzaibile (chestie foarte usor de constatat dealtfel).
Un ritm constant necesita muzica potrivita.
Si cat mai putine lucruri descurajante.
Sau deprimante.
Precum sedintele lungi de debugging.

Ahh ...sedintele astea lungi de debuging .
Chestiile astea ma ingroapa.
Daca toti programatorii ar iubi la fel de tare ca mine sesinile de debugging probabil ca bransa asta ar fi pe cale de disparitie - am avea toti ori tendinte suicidale, ori de ucigas in serie.
Secventele astea de Run - introducere date - Bang (error) sunt inumane. Si le gasesc cu atat mai enervante cu cat vin din VB6 unde obisnuiam sa modific codul, sa dau "instruction pointerul" cu doua linii mai sus si sa probez imediat modificarea.
Dar nu, in Visual Studio 2003 nu se mai accepta astfel de gesturi de hobbist. VS-ul e "Proffesional" - si profesionistii dat cod impecabil din prima.
Sau trebuie sa opresca sesiunea de debugging , sa modifice codul, sa dea iar F5, sa priveasca iarasi rabdatori mesajele cu building .... sa caute iarasi formul in cauza prin menu-ul aplicatiei, sa introduca iarasi datele de test ...totul in speranta ca "de data asta o fi bine".
In timp insa, am uitat de cat de placut era sa faci Edit and Continue (stiu, o sa-mi aduc aminte iar in VS2005).
Trecerea de la "Edit and Coninue" la "Restart and Pray" era pe cale sa ma determine sa ma apuc de fumat.
Insa am scapat la limita.
Ce ma enerva atat de tare? Pierdeam mult prea mult timp incercand sa reproduc contextul in care aparea eroarea. Imi lua prea mult sa aleg optiuni de menu-uri, sa completez zece textbox-uri pe ecran inainte sa pot da iarasi "salveaza in baza de date" ca sa-mi ajunga iarasi executia la breakpoint-ul care ma interesa. In plus, toata rutina asta cu introducerea acelorasi date in textbox-uri e teribil de monotona - iar eu urasc lucrurile monotone si repetitive.
Chinul de a introduce mereu aceleasi valori in textbox-uri mi-a adus aminte de anii de bajbaiala de prin liceu, cand ma luptam cu cine stie ce algoritm de drum optim ... Lucram in C. Si aveam aceeasi problema enervanta - trebuia sa introduc iar si iar aceeasi parametrii de intrare si sa examinez de fiecare data rezultatele (asta cand nu reuseam sa resetez calculatorul din cauza vreunui pointer neinitializat). Pentru programasele mele scrise in liceu in C (aplicatii de DOS) gasisem insa o metoda foarte practica de a scapa de calvarul datelor de intrare: scriam intr-un fisier text toate valorile de intrare, despartite prin cate un Enter. Apoi redirectam console input-ul spre fisierul asta. Pentru redirectare era suficienta o singura linie de cod - toate scanf-urile alea incepeau sa citeasca din fisier in loc sa astepte input de la tastatura - iar eu scapam de toata corvoada introducerii repetate a datelor de intrare. Stiu, nu-i nimic special in treaba asta - probabil si voi ati folosit sistemul redirectarea consolei.
In VS2003 , cand mi-am dat seama ca trebuie sa restartez aplicatia de prea multe ori si sa introduc de fiecare data nishte valori in textbox-uri... a inceput sa-mi fie dor de redirectarea consolei din aplicatiile de DOS.
Asa ca am pus un buton pe form. Am scris pe el "Date de test" , iar la buton_click am completat din cod toate textbox-urile cu valorile alea plictisitoare. La fiecare pornire a programului - trebuia sa caut formul in cauza prin menu-ul aplicatiei, sa-l deschid si sa apas pe butonul de test si toate campurile se comletau cuminti cu nishte valori. Evident, dupa ce rezolvam bug-ul, stergeam butonul care nu trebuia sa ajunga la client :-).
Mi s-a intamplat de cateva ori insa sa revin dupa o zi-doua la acelasi form - cu un alt bug (evident, mai modificasem cate ceva si pocnise iarasi...).Si uite asa am inceput sa regret repede butoanele sterse .. . In scurt timp am ajuns sa nu mai sterg butoanele cu pricina si codul atasat lor - ci doar sa le pun proprietatea visible pe false. Cand aveam nevoie de ele - era suficient sa le pun visible inapoi pe true.
Rezolvam bug-ul - inapoi cu visible false.
M-am gandit sa folosesc in locul unui buton obisnuit un control derivat din button dar care sa-si seteze singur proprietatea visible in functie de modul in care se compila aplicatia (in Debug sau in Release) sau in functie de vreo variabila statica de la nivelul aplicatiei.Pentru unele formuri mai pretentioase, m-am gandit ca mi-ar putea prinde bine chiar 2-3 butoane pentru a acoperi doua-trei seturi de valori diferite. (Sau poate ca ar fi trebuit sa folosesc in loc de buton un combobox din care sa pot alege setul de date ?).
Dar intre timp - pentru ca nu mai pierdeam timp cu introducerea datelor in textbox-uri - a inceput sa ma sacaie succesiunea de click-uri prin menu-ul aplicatiei si pe butoane de test pe care trebuia sa le dau pentru a deschide formul pentru care faceam debugging.
Si asa mi-am dat seama ca imi lipseste Access-ul - in care poti da click dreapta -> Form view pe orice form aflat in design. Imediat formul trece in Run mode, si-l poti proba si apasa pe toate butonasele - se comporta exact cum se va comporta in fata utilizatorului.
Imi trebuia o modalitate de a porni direct un form, evitand menu-ul aplicatiei si alte formuri intermediare.
Solutia - mai intai am incercat sa schimb form-ul de start-up al aplicatiei. Trebuia insa de fiecare data sa repun lucrurile la loc dupa ce rezolvam bug-ul.Pana la urma mi-a fost mai simplu ca in solution-ul curent sa adaug un alt proiect, cu o referinta la proiectul principal. Puteam modifica de oricate ori vroiam functia main a acestei aplicatii auxiliare (aplicatie de test) - pentru ca asta oricum nu ajungea la client si nu prea conta in ce stadiu ramanea functia ei main.De cate ori era nevoie sa mai rezolv un form marcam proiectul de test ca Start-up project si in functia lui main, apelam formul care facea probleme. Adio splash screen-uri pe care trebuia sa le vad de sute de ori, adio menu-uri. Formul vinovat pornea direct la F5. Un singur lucru mai ramanea de facut - sa apas butonul de test de pe el pentru a completa automat textbox-urile cu valorile predefinite.Si sa dau OK(save) sa vad daca mai obtin vreo eroare.
De click-urile astea am scapat usor - pentru ca din functia main a aplicatiei puteam instantia formul si apela direct buttonTest_click , apoi ButtonSave_Click. Cu conditia ca aceste functii sa fie publice.
Sau sa folosesc reflection (pentru ca poti apela functii private folosind reflection).
Reflection-ul insa mi-a dat o alta perspectiva asupra lucrurilor. Cu Reflection poti cauta toate functiile asociate butoanelor de test dintr-un form (sau dintr-un intreg assembly). Si sa afisezi toate aceste nume de functii intr-o lista (poate chiar un tree). La pornirea aplicatiei auxiliare poti selecta oricare din functiile afisate si sa o invoci cu dublu-click.La drept vorbind nici nu mai aveam nevoie de butoanele de test de pe formuri - ci doar de codul lor din button_click, codul prin care se completau textobox-urile.
Pentru a putea filtra insa functiile de test de celelalte functii ale aplicatiei avem nevoie de o conventie de denumire. (Spre exemplu, toate aceste functii sa se termine in Test). Sau sa fie marcate printr-un atribut folosit ca un marker. Explorer de functii de test cauta si afiseaza toate metodele care se termina in Test sau sunt marcate cu un anumit atribut.
Asadar, la fiecare pornire a aplicatiei de test am afisat lista functiilor de test din aplicatia principala si cu un dublu click am pornit formul si am completat textbox-urile lui cu valorile de test.
In felul asta am scapat de nevoia de a modifica mereu Main-ul aplicatiei auxiliare pentru a porni unul sau altul din formuri. Mai mult - aplicatia auxiliara a devenit suficient de generica pentru a putea fi refolosita cu oricare din proiectele la care lucram.
Ce am obtinut in felul asta?O modalitate foarte complicata de a completa nishte default-uri prin form-uri :-).
Big deal.
Intr-o zi insa listul de selectie a functiei de test s-a transformat intr-un multiselect.
Chestie foarte utila pentru cazul in care faceam modificari in nucleul aplicatiei, sau intr-o functie apelata din mai multe formuri. Puteam modifica linistit codul si apoi sa rulez toate functiile de test din toate formurile pentru a verifica daca aplicatia nu cumva crapa in urma noii modificari. Formurile se deschideau unul cate unul, functiile de test completau datele predefinite, butoanele de save se apelau automat.
Grotesc, nu ?
Daca totul decurgea normal cele 50 de functii se executau una dupa alta iar eu puteam zambi fericit - modificarea din core-ul aplicatiei nu a aruncat in aer nimic de data asta.
Chestie care incurajeaza lucrul la functiile comune (din "core"). Inainte aveam retineri daca era nevoie sa ma ating de o functie din nucleul aplicatiei, care ar putea avea impact in prea multe locuri.

Ati rezistat pana aici ?
Un singur lucru mai lipseste noului noustru mod de lucru.
Un nume.
Si in cautarea unui nume vom afla ca nimic nu e nou sub soare si n-am facut decat sa reinventam roata.
Numele este deja ocupat - povestea mea seamana oarecum a Test Driven Developement.
Cred ca Test Driven Developement nu trebuie impus ca o metodologie de lucru, ca un nou stil in care sa se lucreze. Am ajuns la TDD analizand activitatea zilnica si incercand sa scap de chestiunile chinuitoare. La drept vorbind cu totii facem din prima zi Test Driven Developement - doar dupa fiecare modificare de cod oricum dam un run pentru a proba daca programul merge asa cum trebuie.
Run - Input - Action - Check Results.
Asta e procesul.
Odata automatizata activitatea de "completare de valori de intrare" am tendinta de a rula din ce in ce mai des testul. Dupa orice modificare minora - run test. Treaba asta cu run test imi da un sentiment de siguranta. Chiar daca testul in cauza nu acopera decat o mica parte din scenariile posibile - faptul ca vad inca o data bucatica mea de cod ruland asa cum trebuie imi da incredere si imi insufla un dram de optimism. Ma ajuta sa pastrez un ritm constant. Si tzine loc de dulciuri.
Contrar aparentelor, rularea repetata (obsesiva chiar) a testelor nu diminueaza productivitatea. E chiar invers. Teste mai dese inseamna bucati mici de cod intre ele. Daca testul era ok acum 3 minute si intre timp n-am scris decat 10 linii si acum testul crapa - e clar , bug-ul a fost introdus undeva in cele 10 linii de cod. Mi-e mult mai simplu decat daca au trecut 2 ore si 10 clase de la ultima pornire a aplicatiei.
Se pare ca timpul necesar debugg-ing-ului creste exponential cu cantitatea de cod scrisa de la ultima sesiune de debugging.
Atunci de ce nu dam Run mai des? Pentru ca avem datele alea de introdus mereu. Trebuie sa refacem contextul testului de fiecare data. Si cum asta e o activitate plictisitoare, de durata si care tinde sa rupa ritmul programarii, nu prea o facem des.
Imediat insa ce automatizam procesul de refacere al contextului , rularea unui test dureaza doar cateva secunde. Si in loc sa intrerupa ritmul lucrului, ii formeaza o cadenta.
In scurt timp nici nu mai e nevoie de debugg-ing - adica de breakpoint-uri si inspectarea variabilelor. Pentru ca mereu bug-ul e undeva in ultimele 10 randuri de cod scrise.
Morala povestii ?
Test driven developement nu iti impune cu forta o arhitectura n-tier. Test Driven Developement nu inseamna musai NUnit.
Test Driven Developement inseamna mai mult un mod mai interesant de a aborda problemele.
Nu va ganditi la Test Driven Developement ca la o masura de asigurare a calitatii. (Sa fim seriosi, in clipa in care spui asigurarea calitatii, jumatate de sala se goleste)
Ganditi-va la TDD ca la o metoda de reducere a timpilor de debugging si crestere a eficientei.
Sau ca la o metoda de a mai condimenta putin viata asta plictisitoare de programator :-p .

2 Comments:

Blogger Andrei Ignat said...

SUPER!

11:44 AM  
Blogger Darius Damalakas said...

You said you hate your current employer.

This is what i do:
I promosied myself that I will never work on anything boring (unless my family starves) and in return i'll never do a half-ass job

yes!

10:24 PM  

Post a Comment

<< Home