Skip to content
Udgivet

Modernisér en ældre PHP-applikation uden en fuld omskrivning

Ældre PHP-applikationer bliver ofte beskrevet, som om alderen alene gør dem dårlige. I praksis indeholder mange gamle systemer års nyttige forretningsregler, integrationer, driftsviden og særtilfælde, som ikke er dokumenteret andre steder.

Koden kan være vanskelig at ændre, men applikationen virker stadig. En fuld erstatning kan omdanne kendt teknisk gæld til et langt projekt med usikkert omfang, manglende adfærd og en risikabel afsluttende migrering.

Løbende modernisering vælger en anden tilgang. Den forbedrer applikationens sikkerhed, understøttelse og struktur, samtidig med at den adfærd, forretningen allerede afhænger af, bevares.

En ældre PHP-applikation bliver moderniseret trinvis over en bro af tests og kontrollerede opgraderinger

Start med at forstå den reelle risiko

“Legacy” er ikke i sig selv en nyttig diagnose. Identificér hvad der gør applikationen vanskelig eller farlig at vedligeholde.

Almindelige risici omfatter:

  • En PHP-version, der ikke længere understøttes
  • Gamle eller forladte afhængigheder
  • Manglende pålideligt lokalt miljø eller staging
  • Manuel deployment uden rollback-proces
  • Vigtig adfærd uden automatiserede tests
  • Databaseændringer direkte i produktion
  • Forretningslogik blandet ind i skabeloner og request handlers
  • Integrationer uden logs, retries eller tydeligt ejerskab
  • Sikkerhedsantagelser, som ikke længere matcher den aktuelle brug
  • Viden hos én person eller kun synlig i koden

Rangér risiciene efter driftsmæssig betydning. En ikke-understøttet runtime eksponeret på internettet er mere presserende end inkonsistente navne. En skrøbelig betalingsintegration fortjener opmærksomhed før en bred oprydning i kodestil.

Kortlæg systemet før ændringer

Lav en praktisk oversigt over applikationen:

  • Entry points, routes, planlagte jobs og kommandolinjeopgaver
  • Databaser, tabeller, filer og ekstern lagring
  • Login, rettigheder og administrationsværktøjer
  • API’er, webhooks, e-mail, betaling og tredjepartsintegrationer
  • Deployment, hosting, DNS, køer og background workers
  • Kritiske bruger- og medarbejderflows
  • Afhængigheder og deres versionskrav

Antag ikke, at kode, der ser ubrugt ud, sikkert kan fjernes. Ældre systemer indeholder ofte månedlige jobs, sjældne administrationshandlinger eller partnerintegrationer, som er usynlige ved normal browsing.

Brug logs, serverkonfiguration, databaseaktivitet og samtaler med de personer, der bruger systemet, til at bekræfte hvad der faktisk kører.

Gør applikationen observerbar

Modernisering er sikrere, når ændringer kan forstås i produktion.

Før bred refaktorering bør følgende forbedres:

  • Logning af fejl og exceptions
  • Sammenhæng mellem requests og jobs
  • Overvågning af vigtige endpoints og arbejdsgange
  • Synlighed i planlagte jobs og køer
  • Alarmer ved gentagne fejl
  • Verificering af backups
  • Historik over deployments

Eksponér ikke følsomme data i logs. Målet er at identificere hvad der fejlede, hvor det fejlede, og hvilken forretningshandling der blev påvirket.

Observerbarhed skaber et udgangspunkt. Den afslører også eksisterende fejl, som ellers kunne blive tilskrevet moderniseringsarbejdet.

Beskyt adfærd med tests

En ældre applikation er måske ikke designet til automatiseret test. Det betyder ikke, at test skal vente til efter en omskrivning.

Start med karakteriseringstests omkring eksisterende adfærd. Testene beskriver, hvad applikationen gør nu, inklusive adfærd der senere kan forbedres. Fokusér på arbejdsgange, hvor en utilsigtet ændring vil være dyr:

  • Login og rettighedskontrol
  • Priser og beregninger
  • Oprettelse af ordrer, bookinger eller henvendelser
  • Importer, eksporter og planlagte jobs
  • API- og webhook-adfærd
  • Kritiske rapporter

Tests ved HTTP-, kommando-, service- eller databasegrænser kan give nyttig beskyttelse, selv når den interne kode er tæt koblet. Tilføj unit tests på lavere niveau, efterhånden som ansvar bliver lettere at isolere.

Formålet er ikke at opnå en høj dækningsprocent. Det er at gøre vigtig adfærd synlig før ændringer.

Bevæg systemet mod en understøttet PHP-version

En understøttet PHP-version giver bedre adgang til sikkerhedsrettelser, værktøjer, biblioteker og moderne sprogfunktioner. Kontrollér den aktuelle livscyklus på den officielle side med understøttede PHP-versioner frem for at stole på en gammel projektnote.

Runtime-opgraderinger er lettere, når de håndteres som en rækkefølge:

  1. Registrér nuværende produktionsversion og extensions.
  2. Gennemgå applikationens og afhængighedernes kompatibilitet.
  3. Aktivér streng fejlrapportering i et kontrolleret miljø.
  4. Ret deprecations og inkompatibel adfærd.
  5. Opgradér gennem håndterbare trin, hvor det er nødvendigt.
  6. Kør tests og repræsentative arbejdsgange på målversionen.
  7. Udgiv med overvågning og rollback klar.

Undgå at kombinere runtime-opgraderingen med uvedkommende arkitekturændringer. En fokuseret opgradering gør kompatibilitetsproblemer lettere at finde og reducerer mængden af adfærd, der ændres samtidig.

Få styr på afhængigheder

Nogle ældre PHP-applikationer indeholder kopierede biblioteker, manuelt ændret vendor-kode eller afhængigheder, som ikke er gennemgået i årevis.

Lav en tydelig oversigt over afhængigheder. Flyt håndterbare afhængigheder til Composer, fjern pakker der reelt ikke bruges, og identificér forladte biblioteker, som skal erstattes eller isoleres.

Opdatér i kontrollerede grupper frem for at ændre alt på én gang. Læs opgraderingsnoter, gennemgå indirekte afhængigheder, og test den applikationsadfærd, som bruger hver pakke.

Redigér ikke installerede vendor-filer for at få en opdatering til at virke. En lokal rettelse kan forsvinde ved næste installation og efterlader det reelle kompatibilitetsproblem uløst. Hvor en afhængighed ikke kan erstattes med det samme, bør brugen isoleres bag en tydelig grænse, så resten af applikationen afhænger mindre direkte af den.

Skab grænser omkring vanskelig kode

Ældre kode bliver lettere at modernisere, når ny og ændret adfærd holdes ude af de mest sammenfiltrede områder.

Nyttige grænser kan være:

  • En service omkring et eksternt API
  • Et repository omkring kompleks databaseadgang
  • En dedikeret klasse til priser eller validering
  • En adapter omkring et gammelt bibliotek
  • Et queue job omkring langsomt eksternt arbejde
  • Et nyt endpoint eller modul ved siden af en ældre implementering

Målet er ikke at tvinge alle moderne design patterns ind i applikationen. Det er at give vigtig adfærd ét forståeligt sted og reducere antallet af filer, der skal ændres sammen.

Når en funktion kræver omfattende arbejde, kan den afgrænsede funktion erstattes frem for hele applikationen. Over tid flyttes mere adfærd bag tydeligere interfaces, mens systemet fortsat er i drift.

Ændr databasen forsigtigt

Databaseændringer skaber ofte den største deployment-risiko, fordi gamle og nye applikationsversioner kortvarigt kan køre mod samme schema.

Foretræk bagudkompatible trin:

  1. Tilføj en ny kolonne eller tabel uden at fjerne den gamle struktur.
  2. Udgiv kode, der kan fungere under overgangen.
  3. Migrér eller udfyld data i kontrollerede portioner.
  4. Bekræft at den nye vej er stabil.
  5. Fjern først gammel kode og schema, når de ikke længere bruges.

Store datamigreringer bør være observerbare, kunne genoptages og testes på realistiske datamængder. En migrering, der virker på en lille udviklingsdatabase, kan låse tabeller eller overskride tidsgrænser under deployment i produktion.

Backups betyder noget, men en backup er kun nyttig, når gendannelse er testet, og den forventede gendannelsestid er kendt.

Forbedr deployment før ændringshastigheden øges

Moderne kode er ikke nok, hvis releases fortsat er uforudsigelige.

Byg en gentagelig releaseproces med:

  • Versionsstyring og gennemgåede ændringer
  • Konsistent installation af afhængigheder
  • Automatiserede tests og statisk analyse
  • Miljøspecifik konfiguration
  • Sporing af databasemigreringer
  • Deployment-logs
  • Health checks
  • En praktisk rollback- eller forward-fix-plan

Mindre releases reducerer risikoen og gør problemer lettere at forbinde med en konkret ændring. Feature flags eller kontrolleret aktivering kan hjælpe, når ny adfærd skal testes med en begrænset gruppe.

Gem ikke hemmeligheder i kodebasen, og kopiér ikke produktionsoplysninger til udviklingsmiljøer. Modernisering bør reducere driftsmæssig eksponering samtidig med, at koden forbedres.

Refaktorér omkring aktivt arbejde

Brede oprydningsprojekter bruger ofte tid uden at ændre de dele af systemet, der betyder noget.

Refaktorér hvor der er en grund:

  • En fejl afslører uklart ansvar
  • En ny funktion ville ellers duplikere vanskelig kode
  • En opgradering af en afhængighed kræver isolering
  • En langsom operation har brug for en tydeligere grænse
  • Et sikkerhedsproblem kræver en sikrere implementering

Gør den omkringliggende kode lettere at forstå, mens den nødvendige ændring leveres. Tilføj tests, forbedr navne, adskil ansvar, og dokumentér de beslutninger, der ikke er åbenlyse. Det forbedrer løbende kodebasen uden at kræve en spekulativ omskrivning.

Hvornår et modul bør erstattes

Nogle områder er bedre at erstatte end reparere. En afgrænset omskrivning kan give mening, når:

  • Modulet har et tydeligt interface og begrænsede afhængigheder
  • Eksisterende adfærd kan beskrives og testes
  • Erstatningen løser et konkret vedligeholdelses- eller forretningsproblem
  • Datamigrering og rollback kan håndteres
  • Gammel og ny implementering kan sammenlignes, før den gamle fjernes

Eksempler kan være en importproces, et rapporteringsmodul, en søgefunktion, en administrationsside eller en isoleret integration.

At erstatte ét forstået modul er meget forskelligt fra at genopbygge hele applikationen. Omfang, risici og succeskriterier kan gøres tydelige.

En praktisk plan for modernisering

En nyttig plan starter normalt med sikkerhed og ender med strukturelle forbedringer:

  1. Kortlæg systemet, og rangér driftsrisiciene.
  2. Forbedr backups, logs, overvågning og synlighed i deployment.
  3. Beskyt kritiske arbejdsgange med tests.
  4. Flyt til en understøttet PHP-version.
  5. Få afhængigheder under kontrolleret styring.
  6. Isolér skrøbelige integrationer og forretningsregler.
  7. Refaktorér eller erstat afgrænsede områder, når konkret arbejde når dem.
  8. Mål om vedligeholdelse, stabilitet og levering bliver bedre.

Målet er ikke at få en ældre applikation til at se nybygget ud. Det er at gøre systemet sikrere at drive, lettere at forstå og mere praktisk at ændre. Det kan ofte opnås, samtidig med at de mange års værdifuld adfærd i systemet bevares.

Flere artikler