Een afhankelijkheid zonder onderhoud wordt niet automatisch een incident, maar beperkt wel het vermogen van een applicatie om te evolueren. Een dergelijke afhankelijkheid kan een update van PHP of het framework blokkeren, ongepatchte kwetsbaarheden met zich meebrengen, afhankelijk zijn van verouderde extensies of gegevensformaten opleggen die niet langer aansluiten op andere systemen. Het probleem is niet alleen technisch: elk kwetsbaar pakket verhoogt de kosten en het risico van wijzigingen aan het product.
Het doel bij het vervangen van niet meer onderhouden pakketten in PHP zou niet moeten zijn om de hele repository in één keer te moderniseren. Het doel is om het risico aantoonbaar te verminderen, met behoud van het gedrag dat de bedrijfsvoering nodig heeft en met de mogelijkheid om elke stap terug te draaien.
Behandel gebrek aan onderhoud als een risico voor doorontwikkeling

Een pakket kan niet meer onderhouden zijn, ook als het nog in productie werkt. Het relevante signaal is niet alleen de datum van de laatste wijziging, maar ook het vermogen om met het systeem mee te evolueren. Het is raadzaam te beoordelen of het beveiligingspatches ontvangt, of het compatibiliteit met de huidige PHP-versie aangeeft, of de indirecte afhankelijkheden ervan geblokkeerd zijn of dat het team een fout in het pakket kan diagnosticeren.
Ook de plaats ervan is belangrijk. Een formatteringsbibliotheek die in een interne taak wordt gebruikt, heeft een ander profiel dan een component voor authenticatie, betalingen, het genereren van fiscale documenten of het verwerken van persoonsgegevens. De prioriteit moet de kans op falen, de zakelijke impact en de interventiekosten combineren.
Niet elke oude afhankelijkheid vereist onmiddellijke vervanging. Als deze geïsoleerd is, geen onbetrouwbare invoer verwerkt, stabiel gedrag heeft en noodzakelijke wijzigingen niet blokkeert, kan het redelijk zijn om deze te encapsuleren en de verwijdering ervan te plannen. Daarentegen vereist een component die aan internet is blootgesteld of die het bijwerken van de runtimeomgeving verhindert, een eerdere beslissing.
Maak een inventaris die helpt bij beslissingen
Een overzicht van composer.json en composer.lock is het startpunt, niet de analyse. Een bruikbare inventaris identificeert zowel directe als transitieve afhankelijkheden en beantwoordt operationele vragen:
- Werkelijk gebruik: welke klassen, consoleopdrachten, controllers of processen het pakket aanroepen en hoe vaak.
- Zakelijke functie: welk proces wordt onderbroken als deze faalt: toegang, aankoop, facturatie, import of een ondersteunende taak.
- Blootstelling: of deze gegevens ontvangt van gebruikers, leveranciers, webhooks, bestanden of interne netwerken.
- Koppeling: of de typen, uitzonderingen, geserialiseerde structuren of databasequery's ervan verspreid in de applicatie voorkomen.
- Dekking: welke tests het huidige gedrag beschrijven en welke zones alleen handmatig worden gevalideerd.
- Beperkingen: PHP-versies, extensies, database, wachtrijen, externe API's en wettelijke vereisten.
Statische zoekopdrachten helpen om referenties te vinden, maar vervangen de observatie van het systeem niet. Controleer asynchrone taken, consolescripts, weinig gebruikte routes, via configuratie geactiveerde integraties en dynamisch geladen code. Een ogenschijnlijk marginale afhankelijkheid kan doorslaggevend zijn bij een maandafsluiting of tijdens een operationeel herstel.
Kiezen tussen bijwerken, encapsuleren, vervangen of verwijderen
Er zijn vier hoofdkeuzes, en deze sluiten elkaar tijdens een migratie niet uit.
- Bijwerken: is passend wanneer er een onderhouden versie bestaat waarvan u de interface en vereisten kunt accepteren. Controleer incompatibele wijzigingen, transitieve afhankelijkheden en de vereiste upgrade van de PHP-versie.
- Encapsuleren: creëert een eigen grens rond het huidige pakket. Dit is geschikt wanneer eerst de koppeling moet worden verminderd voordat over de vervanging wordt beslist, of wanneer het alternatief nog niet volwassen is.
- Vervangen: wisselt de component in voor een ander pakket, een externe dienst of een interne implementatie die beperkt is tot het noodzakelijke gebruiksgeval. Dit moet gebaseerd zijn op een expliciet contract, niet op de gelijkenis van methodenamen.
- Verwijderen: verwijdert functionaliteit die geen waarde meer toevoegt, is gedupliceerd of met native functies kan worden opgelost. Dit is vaak de optie met de laagste toekomstige belasting, maar vereist bevestiging dat er geen verborgen componenten zijn die ervan gebruikmaken.
Vermijd het adopteren van een bibliotheek alleen omdat deze populair of compatibel lijkt. Vergelijk licentie, aantoonbaar onderhoud, API-oppervlak, foutmodel, prestaties, ondersteuning van formaten, beveiligingsstrategie en afhankelijkheid van de leverancier. Als de behoefte klein is, kan een eenvoudige interne abstractie stabieler zijn dan het toevoegen van een ander omvangrijk pakket.
Controleer compatibiliteit met contracten en tests
Documentatie legt de bedoeling van een API uit; de code in productie onthult het contract dat werkelijk telt. Bouw vóór het wijzigen van een pakket karakteriseringstests op de huidige gevallen. Deze zijn niet bedoeld om te bewijzen dat het oude ontwerp ideaal is, maar om relevante resultaten vast te leggen en ongewenste wijzigingen te detecteren.
Definieer voorbeelden van invoer en uitvoer, inclusief grensgegevens, null-waarden, coderingen, datums, decimale precisie en foutmeldingen die door andere componenten worden gebruikt. Als het pakket documenten, gebeurtenissen of API-antwoorden produceert, bewaar dan representatieve voorbeelden en valideer hun structuur.
Aspecten die vaak ongemerkt stukgaan
- Persistentie: verschillen tussen ontbrekende en null-waarden, transacties, gegenereerde identificatoren en de volgorde van bewerkingen.
- Serialisatie: veldnamen, tijdzones, datumformaten, Unicode, numerieke typen en achterwaartse compatibiliteit.
- Integraties: authenticatie, herhaalpogingen, tijdslimieten, handtekeningen, paginering en interpretatie van gedeeltelijke antwoorden.
- Fouten: uitzonderingen, codes, registreerbare berichten en voorwaarden die een herhaalpoging of menselijke interventie moeten veroorzaken.
- Prestaties: geheugengebruik, aantal databasequery's, batchgrootte en vertraging in kritieke routes.
Unittests zijn nuttig voor eigen logica, maar volstaan niet wanneer een integratie verandert. Voeg integratietests toe tegen een database of een gecontroleerde omgeving, en contracttests op de grenzen met externe systemen. Voer voor processen met hoge impact vergelijkingen uit met geanonimiseerde of synthetische gegevens voordat de wijziging aan gebruikers wordt blootgesteld.
Ontwerp een adapterlaag vóór de vervanging
Een adapterlaag vertaalt het contract van de applicatie naar het contract van de afhankelijkheid. In plaats van controllers, diensten en wachtrijtaken rechtstreeks een bibliotheek te laten aanroepen, definieert u een interface die gericht is op de zakelijke behoefte. Een dienst voor documentconversie zou bijvoorbeeld eigen bewerkingen moeten aanbieden en domeinobjecten moeten teruggeven, geen interne typen van het pakket.
interface DocumentRenderer
{
public function render(Invoice $invoice): RenderedDocument;
}De huidige implementatie blijft achter die interface. Vervolgens wordt een tweede implementatie met de nieuwe component toegevoegd. Dit beperkt de wijziging tot één punt, vergemakkelijkt vergelijkende tests en voorkomt dat de eigenaardigheden van de vervanging zich door de code verspreiden.
De abstractie moet bewust klein zijn. Een interface die elke methode van de bibliotheek repliceert, vermindert de koppeling niet; deze voegt alleen een laag toe. Modelleer de bewerkingen die de applicatie vandaag nodig heeft en documenteer relevante beslissingen: wat er gebeurt bij ongeldige invoer, welke gegevens behouden blijven en wat de limieten voor grootte of tijd zijn.
Voer een incrementele en omkeerbare migratie uit
- Baken de reikwijdte af: selecteer één proces, één component die ervan gebruikmaakt of één bewerking voordat u alle gebruikssituaties aanpast.
- Karakteriseer het gedrag: voeg tests en voorbeelden toe die normale gevallen, randgevallen en fouten vertegenwoordigen.
- Introduceer de adapter: behoud aanvankelijk de bestaande implementatie achter de nieuwe grens.
- Implementeer het alternatief: vertaal gegevens en fouten zonder het overeengekomen contract te wijzigen.
- Vergelijk resultaten: verwerk, waar dit veilig is, gelijkwaardige invoer met beide implementaties en registreer significante verschillen.
- Migreer componenten die ervan gebruikmaken: wijzig één proces tegelijk totdat directe referenties naar het vorige pakket zijn verwijderd.
- Verwijder tijdelijke code: verwijder de oude implementatie, schakelaars en compatibiliteitspaden wanneer deze niet langer nodig zijn.
Als u gefaseerde activering gebruikt, definieer dan welke metriek de voortgang bepaalt en welke metriek noopt tot terugdraaien. Een configuratieschakelaar kan de implementatie selecteren, maar mag geen twee permanente bronnen van waarheid creëren. Vermijd bij schrijfbewerkingen dat beide paden dezelfde bron wijzigen, tenzij idempotentie en reconciliatie expliciet zijn ontworpen.
Uitrollen met duidelijke diagnostische signalen
Een uitrol staat niet gelijk aan een volledige release: code publiceren is iets anders dan het gedrag ervan voor alle gebruikers inschakelen. Splits beide momenten wanneer het risico dit rechtvaardigt. Rol de nieuwe implementatie inactief uit, verifieer de technische gezondheid en activeer de wijziging beperkt als de architectuur dat toestaat.
Spreek vóór de start observeerbare indicatoren af: foutpercentage per bewerking, responstijden, herhaalpogingen, mislukte taken, verschillen in uitvoer en volume van supportincidenten. Registreer een implementatie-identificator in traceringsgegevens en logboeken om een probleem aan het oude of nieuwe pad toe te schrijven zonder gevoelige gegevens op te nemen.
Het terugdraaien moet getest zijn en compatibel zijn met de gegevens die tijdens de overgang zijn gegenereerd. Teruggaan naar eerdere code lost op zichzelf geen onomkeerbare schemawijziging, gepubliceerde gebeurtenis of verzonden document op. Ontwerp voor die gevallen eerst een compensatie, een additieve migratie of een compatibiliteitsvenster.
Checklist voor een kritieke afhankelijkheid

- Is het werkelijke gebruik en de zakelijke kriticiteit gedocumenteerd?
- Zijn de transitieve afhankelijkheden en platformbeperkingen bekend?
- Bestaat er een eigen contract dat voorkomt dat pakkettypen worden blootgesteld?
- Zijn er relevante karakteriserings-, integratie- en fouttests?
- Zijn gegevens, serialisatie, persistentie, beveiliging en prestaties gevalideerd?
- Kan de activering worden beperkt en houdt het terugdraaien rekening met gegevenswijzigingen?
- Is er een expliciete datum en criterium om compatibiliteit en tijdelijke code te verwijderen?
Veilige vervanging betekent niet dat het nieuwe pakket compileert. Het betekent dat de resultaten die ertoe doen behouden blijven, verschillen zichtbaar worden gemaakt en de afhankelijkheid van componenten die niet langer met de applicatie kunnen evolueren permanent wordt verminderd.



