ABONAMENTE VIDEO REDACȚIA
RO
EN
NOU
Numărul 147
Numărul 146 Numărul 145 Numărul 144 Numărul 143 Numărul 142 Numărul 141 Numărul 140 Numărul 139 Numărul 138 Numărul 137 Numărul 136 Numărul 135 Numărul 134 Numărul 133 Numărul 132 Numărul 131 Numărul 130 Numărul 129 Numărul 128 Numărul 127 Numărul 126 Numărul 125 Numărul 124 Numărul 123 Numărul 122 Numărul 121 Numărul 120 Numărul 119 Numărul 118 Numărul 117 Numărul 116 Numărul 115 Numărul 114 Numărul 113 Numărul 112 Numărul 111 Numărul 110 Numărul 109 Numărul 108 Numărul 107 Numărul 106 Numărul 105 Numărul 104 Numărul 103 Numărul 102 Numărul 101 Numărul 100 Numărul 99 Numărul 98 Numărul 97 Numărul 96 Numărul 95 Numărul 94 Numărul 93 Numărul 92 Numărul 91 Numărul 90 Numărul 89 Numărul 88 Numărul 87 Numărul 86 Numărul 85 Numărul 84 Numărul 83 Numărul 82 Numărul 81 Numărul 80 Numărul 79 Numărul 78 Numărul 77 Numărul 76 Numărul 75 Numărul 74 Numărul 73 Numărul 72 Numărul 71 Numărul 70 Numărul 69 Numărul 68 Numărul 67 Numărul 66 Numărul 65 Numărul 64 Numărul 63 Numărul 62 Numărul 61 Numărul 60 Numărul 59 Numărul 58 Numărul 57 Numărul 56 Numărul 55 Numărul 54 Numărul 53 Numărul 52 Numărul 51 Numărul 50 Numărul 49 Numărul 48 Numărul 47 Numărul 46 Numărul 45 Numărul 44 Numărul 43 Numărul 42 Numărul 41 Numărul 40 Numărul 39 Numărul 38 Numărul 37 Numărul 36 Numărul 35 Numărul 34 Numărul 33 Numărul 32 Numărul 31 Numărul 30 Numărul 29 Numărul 28 Numărul 27 Numărul 26 Numărul 25 Numărul 24 Numărul 23 Numărul 22 Numărul 21 Numărul 20 Numărul 19 Numărul 18 Numărul 17 Numărul 16 Numărul 15 Numărul 14 Numărul 13 Numărul 12 Numărul 11 Numărul 10 Numărul 9 Numărul 8 Numărul 7 Numărul 6 Numărul 5 Numărul 4 Numărul 3 Numărul 2 Numărul 1
×
▼ LISTĂ EDIȚII ▼
Numărul 30
Abonament PDF

Riscurile operării cu branch-uri

Anghel Conțiu
Design Lead
@Endava



PROGRAMARE


Folosirea instrumentelor de control al versiunii a devenit un obicei în modul nostru de lucru, iar una dintre proprietățile lor, aceea de a folosi branch-uri, poate fi fructificată după cum consideră fiecare echipa că aduce valoare. S-au scris multe articole despre strategiile de branching și în timp ce unele dintre ele au devenit foarte populare, aproape toate au o problemă comună care trebuie abordată. Aceasta este problema procesului merging între branch-uri. Există un motiv pentru care se discută nu doar în termeni de strategie de branching, ci in termeni de strategie de branching și merging.

Fuziunea se poate transforma ușor în coșmar atunci când strategia abordată este greșită. Putem să luăm ca exemplu una dintre cele mai populare strategii de fuziune, strategia Vincent Driessen prezentată în Figura 1. Această strategie poate să nu fie cea mai potrivită în anumite condiții întâlnite destul de des.

Figura 1: modelul Vincent Driessen

Ne concentrăm pe cele două branch-uri care sunt intens folosite: “feature” și “develop”.

Branch-ul “feature” este creat din branch-ul “develop” și este folosit de programator pentru a implementa o nouă funcționalitate.

Când termină implementarea, va aduce modificările de pe branch-ul “feature” în branch-ul “develop” astfel încât acestea vor deveni disponibile pentru restul echipei.

De urmărit următoarea situație:

Acesta pare a fi un workflow sănătos, însă ascunde câteva riscuri care în final vor duce la un efort adițional atât pe partea de dezvoltare, cât și pe cea de testare. Iată câteva dintre aceste riscuri:

Riscul retestării funcționalității

Dan își petrece o zi testând toate particularitățile implementării Mariei și în final își dă acordul. Totul funcționează bine. Ceea ce el încă nu știe este faptul că Andrei lucrează cu două dintre fișierele modificate de Maria. Andrei își efectuează un merge al branch-ului său trei zile mai târziu în branch-ul develop și e bucuros că nu întâmpină conflicte. Dan începe testarea dar observă că funcționalitatea Mariei nu se mai comportă bine. Dan creează un tichet pentru Maria, Maria rezolvă problema folosind de această dată și modificările aduse de Andrei, iar Dan urmează să testeze din nou atât funcționalitatea Mariei, cât și pe cea a lui Dan.

Așadar Maria a pierdut timp pentru reparația respectivă, iar Dan a pierdut timp deoarece a trebuit să testeze cel puțin de două ori ambele funcționalități.

Riscul fuzionării

Se întâmplă des ca dezvoltarea funcționalităților și scrierea de teste unitare pentru ele să dureze câteva zile. Pentru a micșora riscul apariției de conflicte la fuziune programatorii încearcă să își aducă branch-ul “develop” cât mai des (probabil în fiecare zi) în branch-ul pe care ei dezvoltă separat funcționalitatea. Faptul că se implementează simultan mai multe funcționalități diferite pe branch-uri diferite este cert, iar riscul de conflicte la fuziune este oricum ridicat, iar cu cât programatorii petrec mai mult timp pe branch-ul lor specific, cu atât riscul crește.

Riscul pentru integrarea continuă

Programatorii ar trebui să scrie teste unitare, teste între componente și teste de integrare. Atunci când aceste teste sunt dezvoltate împreună cu funcționalitatea, în izolare, pe branch-ul de implementare a funcționalității, și în același timp o altă funcționalitate este dezvoltată împreună cu testele corespunzătoare pe un alt branch, la momentul efectuării operației de merge șansele ca testele scrise să pice sunt ridicate. Testele nu au fost scrise luând în considerare comportamentul dezvoltat în paralel pe celelalte branch-uri. Ele vor trebui revizuite și reparate, ceea ce aduce un efort în plus.

Riscul pentru rularea script-urilor pe baza de date

Multe echipe încearcă să își automatizeze procesul de instalare al produsului. La nivel de baze de date rulează în mod automat script-uri care aduc starea bazei de date la versiunea corectă. Din cauza faptului că programatorii vor crea aceste script-uri iîn izolare, pe branch-uri diferite, există riscul de a strica ordinea în care ele trebuie rulate.

Dacă privim cu atenție rădăcina acestor probleme, observăm izolarea în care lucrează programatorii pe branch-uri diferite. O alternativă pentru acest mod de lucru este folosirea tehnicii de branching prin abstractizare.

Branching prin abstractizare

Este definită de Martin Fowler ca fiind “o tehnică de a face o schimbare pe scară largă unui sistem software într-un mod gradual care permite lansarea sistemului în mod regulat în timp ce schimbarea e încă în desfășurare”.

Tehnica implică adăugarea unui nivel de abstractizare deasupra funcționalității la care se lucrează astfel încât codul client va comunica doar cu acest nivel de abstractizare, nefiind conștient dacă folosește versiunea originală sau nouă a implementării.

Oricare ar fi modul în care este folosită tehnica, există câteva puncte comune:

În timp ce programatorul scrie teste pentru funcționalitatea lui, el este sigur ca testele sale folosesc ultima versiune a codului deoarece colegii lui își adaugă modificările pe același branch. În acest fel nu vor exista operații de merge ulterioare care să strice testele, deoarece alte branch-uri nu există.

Este importantă plasarea corectă a nivelului de abstractizare și modul în care obiectele dintr-o versiune sau alta ale aceleiași funcționalități sunt instanțiate. Din acest punct de vedere există mult loc pentru creativitate deoarece această decizie depinde și de contextul problemei. Pot exista mai multe variante, dintre care vor fi prezentate două.

Branching prin abstractizarea componentei

Această tehnică se aplică acolo unde este necesară modificarea sau rescrierea unei componente mari.

Un nivel de abstractizare trebuie adăugat astfel încât codul client să nu mai depindă de componentă ci de abstractizare. Această acțiune ar putea implica unele modificări (refactoring) la nivel de componentă și teste adiționale ar putea fi scrise. Modificările necesare în vederea extragerii nivelului de abstractizare ar putea fi costisitor, de aceea acesta este un punct important pentru a lua o decizie asupra strategiei de branching.

Noua implementare a componentei va fi făcută pas cu pas, astfel funcționalitățile vor fi adăugate în funcție de priorități. Când un set de funcționalități e finalizat, clientul codului poate schimba legăturile cu instanțele folosite și poate migra la noua componentă. Este important de reținut că aplicația trebuie să poată fi construită și rulată în oricare moment. În final nu vor mai exista dependențe către vechea implementare.

Figura 2: Branching prin abstractizarea componentei. ClientCode este într-un process de migrare pentru a folosi NewComponent.

Branching prin abstractizare la nivel de punct de folosire

Să luăm în considerare cazul în care abstractizarea la nivel de componentă nu este cea mai bună soluție. Motivele ar putea consta în efort prea mare de modificări pentru a extrage nivelul de abstractizare la nivel de componenta. Pentru astfel de situații putem lăsa codul așa cum este și să alegem o altă tehnică.

Figura 3 face referință la codul care folosește clasa (grupul de clase) ce trebuie actualizată. Descrierea claselor din figura:

  1. Versiunea veche și versiunea nouă a implementării clasei care folosește codul ce trebuie actualizat:

    • Punctul de abstractizare este la nivel de clasă care folosește codul ce trebuie actualizat.
    • Inițial vom avea versiunea originală a clasei client și o copie a ei care va deveni noua versiune (da, nu trebuie să copiem o clasă, aceasta va fi o situație temporară până când noua versiune devine stabilă; vom avea apoi posibilitatea de a folosi versiunea originală dacă vor fi probleme în timpul folosirii noii versiuni; versiunea originală în final va fi ștearsă).
    • Vom avea o interfață nouă pe care atât clasa client originală, cât și noua versiune a acestei clase client o vor implementa („Interface” în Figura 3). Interfața va conține probabil metodele publice din implementarea originală a clasei client.
    • Noua versiune a clasei client va fi modificată treptat pentru a aduce noile funcționalități.
    • Versiunea originală va fi ștearsă la final.
  2. „The Factory”

    • Va avea responsabilitatea de a instanția versiunea originală sau versiunea nouă a clasei client; Responsabilitatea verificării dacă una sau cealaltă dintre versiuni trebuie instanțiată se face în interiorul componentei „factory”.
    • Poate deveni mai inteligentă în măsura în care este nevoie; spre exemplu, poate alege între cele două versiuni la pornirea aplicației sau în timpul rulării acesteia, poate să fie conștientă de mai multe informații din mediul în care există și să ia măsuri în funcție de acestea, etc. .
    • Se va folosi interfața clasei client astfel încât implementarea originală sau cea nouă va fi instanțiată și injectată acolo unde este nevoie de ea;
    • Va constitui garanția faptului că acolo unde e nevoie de o implementare sau de alta, modul de alegere între ele va fi unul consistent, și nu vor fi condiții diferite în locuri diferite.
  3. Spațiul de activitate al clasei client („the scope”)
    • Având componenta „factory” ca responsabilă pentru instanțierea clasei client, avem oportunitatea să stabilim spațiul de activitate al instanței;
    • În general noua versiune ar trebui să aibă același spațiu de activitate ca și cea originală, iar aceasta decizie poate fi specificată la nivelul „factory”.
  4. Rularea aplicației în medii diferite
    • Componenta „factory” poate deveni conștientă de mediul în care activează și va instanția versiunea clasei client adecvată mediului respectiv;
    • Posibilitatea de a putea instanția o implementare sau alta constituie și un mecanism de rezervă în cazul în care noua versiune nu funcționează la nivelul așteptărilor. Dezactivarea ei și activarea instanței originale se efectuează ușor si rapid.

Figura 3: Ramificarea prin absractizare la nivel de punct de folosire. Versiunea originală și cea nouă a clasei care folosește codul ce trebuie actualizat.

Sintetizăm afirmațiile de mai sus privitoare la aspectele asupra cărora se vor concentra membrii echipei:

Programatorii:

Persoanele care testează:

De îndată ce nivelul de încredere în noua funcționalitate devine suficient mecanismul de schimbare între instanțe și instanța originală pot fi șterse. Important este de luat în considerare faptul că sistemul trebuie să poată fi construit și rulat cu succes în oricare moment.

Conferință TSM

NUMĂRUL 147 - Automotive

Sponsori

  • Accenture
  • BT Code Crafters
  • Accesa
  • Bosch
  • Betfair
  • MHP
  • BoatyardX
  • .msg systems
  • P3 group
  • Ing Hubs
  • Cognizant Softvision
  • Colors in projects