Legacy în dezvoltarea aplicațiilor — ce este, riscuri și strategii de lucru

Autor: IT Sectr Publicat: 2026-07-27 Timp de citire: 7 min

Legacy — nu este doar cod vechi. Este un sistem funcțional care aduce bani afacerii, dar încetinește dezvoltarea. În dezvoltarea aplicațiilor mobile, legacy poate fi scris în Objective-C, poate folosi biblioteci învechite sau pattern-uri arhitecturale. Conform raportului CAST Software (2024), vârsta medie a unei linii de cod în proiectele enterprise depășește 14 ani. Strategia de lucru cu legacy decide dacă acesta va deveni o frână sau va rămâne un activ gestionabil.

Principalele idei

  • Legacy — cod care funcționează în producție, dar folosește tehnologii sau abordări învechite
  • Întreținerea legacy necesită înțelegerea deciziilor istorice și o refactorizare atentă
  • Strategia de migrare — înlocuirea treptată a modulelor fără oprirea produsului prin Strangler Fig
  • Testarea legacy — testele de caracterizare fixează comportamentul curent înainte de refactorizare
  • Vârsta codului în sine nu este o problemă — problema este lipsa testelor și a viziunii arhitecturale

Ce este legacy în dezvoltarea aplicațiilor

Legacy — cod sau sistem care continuă să funcționeze în producție, dar nu mai îndeplinește standardele moderne de calitate. Legacy poate fi scris într-un limbaj învechit (de exemplu, Objective-C în loc de Swift), poate folosi biblioteci nesuportate sau pattern-uri arhitecturale considerate de mult antipattern-uri.

Caracteristica cheie a legacy este lipsa testelor. Conform definiției lui Michael Feathers (2004), codul legacy este cod fără teste. Dacă comportamentul nu poate fi schimbat în siguranță, sistemul este în stare legacy indiferent de vârstă. Cod proaspăt fără teste unitare — este legacy din prima zi.

Legacy nu este neapărat rău. Un sistem bine proiectat în Java 8 poate fi mai fiabil și mai ușor de înțeles decât un cod haotic în Kotlin cu corutine. Vârsta codului nu este un indicator al calității — important este cât de ușor sistemul se pretează la modificări și extindere.

De ce codul legacy este normal

Fiecare sistem de succes devine legacy în timp. Acesta este un proces natural: tehnologiile se dezvoltă mai repede decât poate fi rescris codul. O aplicație scrisă acum 5 ani în Swift 2 este astăzi legacy, deși la momentul creării era modernă.

Valoarea de afaceri a legacy este adesea subestimată. Sistemul funcționează stabil, procesează tranzacții, stochează date — rescrierea implică riscuri. Potrivit Standish Group (2024), 35% din proiectele de rescriere completă eșuează. Din punct de vedere economic, nu scăparea de legacy este justificată, ci învățarea de a lucra cu el.

Cele mai bune strategii — migrare treptată, încapsularea codului vechi în spatele interfețelor noi și testare automatizată. Legacy devine o problemă doar atunci când nu se mai pretează la modificări cu un cost previzibil.

Principalele semne ale unui sistem legacy

Lipsa testelor automate — indicatorul principal. Dacă după modificarea unei linii de cod, programatorul nu poate rula testele și verifica că nimic nu s-a stricat — aveți de-a face cu legacy. Semn suplimentar: procedura de deploy durează ore și necesită acțiuni manuale.

Documentația nu corespunde codului — un alt marker. Diagramele arhitecturale sunt învechite, comentariile descriu un comportament care s-a schimbat deja. Time-to-ramp-up pentru un programator nou depășește o lună — semn al complexității ridicate și al mentenabilității scăzute a sistemului.

Semne suplimentare: arhitectură monolitică fără granițe clare, testarea manuală ca metodă principală de verificare, pipeline CI lung (peste 30 de minute), utilizarea bibliotecilor fără versiuni actuale și imposibilitatea de a actualiza dependențele fără a strica modulele învecinate.

Fenomenul „codului fragil” — o modificare într-un loc strică alte trei. Aceasta este o consecință a cuplării strânse (tight coupling), când modulele știu prea multe unele despre altele. Cu cât cuplarea este mai mare, cu atât sistemul trece mai repede în categoria legacy.

Riscurile lucrului cu cod învechit

Încetinirea vitezei — riscul principal. Adăugarea unei funcționalități simple necesită ore de studiere a codului și zile de testare. Potrivit Stripe (2024), programatorii petrec 33% din timp pentru a depăși datoria tehnică, care este direct legată de prezența modulelor legacy în proiect.

Pierderea expertizei — autorii codului original pleacă din companie, iar documentația este incompletă. Noii programatori se tem să atingă modulele neînțelese, ceea ce duce la efectul „codului înghețat”: modulul nu se dezvoltă, dar continuă să funcționeze. Bus factor al acestor sisteme este critic de scăzut.

Securitatea — bibliotecile învechite conțin vulnerabilități cunoscute. Utilizarea OpenSSL 1.0.2 sau a versiunilor învechite de Jackson în proiecte Java este o cale directă către incidente de securitate care pot costa afacerea reputația și clienții.

Demotivarea echipei — lucrul cu legacy fără o strategie de îmbunătățire scade satisfacția programatorilor. Echipa încetează să fie mândră de produs, fluctuația de personal crește, ceea ce încetinește și mai mult dezvoltarea sistemului.

Strategii de refactorizare legacy

Teste de caracterizare — primul pas înainte de orice modificare a codului legacy. Rulează codul pe date de intrare cunoscute și înregistrează ieșirea așteptată. Aceste teste fixează comportamentul curent ca specificație. Golden master testing — variantă în care datele de ieșire sunt comparate cu un fișier de referință.

Analiza seam — căutarea punctelor în care se poate rupe cuplarea fără a schimba comportamentul. Michael Feathers distinge mai multe tipuri de seam: preprocessor seam, object seam, link seam. Object seam — cel mai frecvent: înlocuirea unui obiect real cu un stub de test printr-o interfață.

Sprout method și Sprout class — tehnici de adăugare a codului nou lângă cel vechi, nu în interiorul lui. În loc să modifici metoda existentă, creează o nouă metodă cu logica necesară și apeleaz-o din cea veche. Astfel se minimizează riscul de a strica codul funcțional.

Exemplu: adăugarea logării în legacy

groovy
class LegacyPaymentProcessor {
    def process(payment) {
        // 200 de linii de cod legacy care nu trebuie atinse
        logPayment(payment) // sprout method
    }
    def logPayment(payment) {
        // cod nou adăugat lângă legacy
    }
}

Migrarea la un stack tehnologic modern

Pattern-ul Strangler Fig — abordarea recomandată pentru migrarea legacy. Noul modul este creat în paralel, traficul este comutat treptat de la vechi la nou. Modulul vechi „moare” natural când nu mai primește cereri. Pattern-ul minimizează riscurile și permite revenirea în caz de probleme.

Branch by Abstraction — tehnică prin care se creează o abstractizare peste implementarea veche și cea nouă. Codul client trece la abstractizare, implementarea veche este înlocuită treptat cu cea nouă. Exemplu: înlocuirea stratului de rețea de la AFNetworking la Alamofire printr-un protocol unic NetworkService.

Migrarea etapizată — împărțirea tranziției în pași mici: încapsularea modulului vechi → scrierea testelor → crearea modulului nou → rularea paralelă → ștergerea modulului vechi. Fiecare pas se încheie cu o stare stabilă a sistemului, ceea ce permite implementarea modificărilor în orice moment.

Întrebări frecvente

Trebuie rescris complet codul legacy?

Rescrierea completă este cea mai riscantă opțiune. Doar 25% dintre proiectele Big Rewrite se finalizează cu succes la timp. Mai bine aplici pattern-ul Strangler Fig: înlocuiește modulele treptat, fără a opri produsul. Fiecare iterație aduce valoare de afaceri, iar riscurile sunt distribuite în timp.

Cum încep refactorizarea legacy fără teste?

Începe cu teste de caracterizare: rulează modulul pe date cunoscute, înregistrează rezultatul. Golden master testing — o modalitate simplă de a fixa comportamentul. Adaugă teste de fiecare dată când atingi o linie de cod. După 6 luni vei avea un schelet care protejează împotriva regresiunilor.

Când este mai profitabil să nu atingi legacy?

Dacă sistemul este stabil, nu necesită modificări frecvente și nu afectează viteza de dezvoltare a altor module — lasă-l. „If it ain’t broken, don’t fix it” — o abordare rezonabilă pentru modulele legacy izolate cu frecvență scăzută de modificări. Atinge codul doar atunci când trebuie să faci modificări de afaceri.

Cum actualizez dependențele într-un proiect legacy?

Folosește versionarea semantică și actualizează în pași: patch → minor → major. Pentru fiecare bibliotecă, scrie teste de compatibilitate. Dependabot sau Renovate automatizează crearea PR-urilor de actualizare. Dacă biblioteca este deprecated — planifică înlocuirea printr-o abstractizare.

Cu ce se deosebește legacy de datoria tehnică?

Datoria tehnică — o metaforă pentru evaluarea costului îmbunătățirilor amânate. Legacy — un sistem sau un cod concret care a îmbătrânit deja. Datoria tehnică se poate acumula într-o lună, legacy necesită timp. Nu orice datorie tehnică devine legacy, dar orice legacy conține datorie tehnică.

Rezumat

  • Legacy — cod fără teste, indiferent de vârstă. Cod proaspăt fără acoperire — legacy din prima zi
  • Vârsta codului — nu este o problemă. Problema este cuplarea strânsă, lipsa testelor și a documentației
  • Testele de caracterizare — primul pas înainte de orice modificare a modulului legacy pentru fixarea comportamentului
  • Pattern-ul Strangler Fig — strategie sigură de migrare cu înlocuirea treptată a modulelor
  • Sprout method — tehnică de adăugare a codului nou lângă cel vechi fără riscul de a strica
  • 35% din rescrierile complete eșuează — migrarea etapizată este mai sigură decât Big Rewrite
  • Legacy izolat cu frecvență scăzută de modificări este mai bine să nu fie atins

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și