Oude systemen hebben twee dingen gemeen: ze werken, en ze maken iedereen nerveus. Ze werken omdat jaren van gebruik in de praktijk ze hebben gevormd rond het daadwerkelijke bedrijf. Ze maken mensen nerveus omdat niemand ze wil aanraken, de documentatie dun is en een van de oorspronkelijke ontwikkelaars drie banen geleden vertrok.
Wat eigenlijk als "legacy" telt
Niet elk oud systeem is legacy. Een 10 jaar oude applicatie die draait op een ondersteunde stack, goed getest, met actief onderhoud, is simpelweg volwassen. Legacy betekent meestal:
- Platform- of frameworkversies die geen beveiligingsupdates meer ontvangen.
- Kennis geconcentreerd bij één of twee mensen.
- Angst om dingen te veranderen omdat kleine wijzigingen verre functies breken.
- Handmatige deploys, handmatige tests, handmatig alles.
- Een groeiende kloof tussen wat het bedrijf nodig heeft en wat het systeem kan.
De twee falende benaderingen
- Niets doen. Risico stapelt zich op. Uiteindelijk breekt er iets kritieks op een slecht moment.
- Vanaf nul herschrijven. Een big-bang-herschrijving duurt bijna altijd langer dan gepland, ontdekt verborgen vereisten te laat en draait zo lang parallel met het oude systeem dat het team het opgeeft.
De benaderingen die daadwerkelijk werken zitten tussen deze twee uitersten.
Strategie 1: Wurg het oude systeem
Houd het legacy-systeem draaiende, maar leid geleidelijk nieuwe functionaliteit door een nieuwe laag. Na verloop van tijd leven steeds meer functies in het nieuwe systeem; het oude wordt kleiner tot het uiteindelijk wordt uitgeschakeld.
Dit werkt voor websites (pagina voor pagina verplaatsen), interne tools (module voor module verplaatsen) en integraties (introduceer een nieuwe API-gateway, leid daarna aanroepers ernaartoe).
Strategie 2: Moderniseer ter plaatse
Soms is de architectuur prima, maar de stack te oud. Upgrade in dat geval één laag tegelijk: taal/runtime, dan bibliotheken, dan framework, dan deployment. Voeg geautomatiseerde tests toe terwijl je bezig bent, zodat elke stap verifieerbaar is. Niet glamoureus, maar zeer effectief.
Strategie 3: Splitsen, dan moderniseren
Grote monolithische systemen zijn vaak gemakkelijker stuk voor stuk te moderniseren zodra de naden duidelijk zijn. Dat betekent eerst grenzen definiëren binnen het bestaande systeem (modules, bounded contexts), en dan modules onafhankelijk vervangen. De grenzen doen er meer toe dan de technologiekeuze.
Wat te doen voordat je code schrijft
- Schrijf op wat het systeem vandaag daadwerkelijk doet, niet wat het verondersteld werd te doen.
- Identificeer de functies waar mensen dagelijks op vertrouwen, die moeten blijven werken tijdens de overgang.
- Identificeer dode functies, ze vormen vaak een betekenisvol deel van de code en moeten stilletjes worden afgevoerd.
- Maak een inventaris van gegevens, integraties, machtigingen en exports. Hier lopen moderniseringsprojecten meestal in de problemen.
- Bepaal wat "klaar" in meetbare termen betekent.
Risicobeheersingen die een echt verschil maken
- Back-ups en geteste herstelacties, vóór elke wijziging.
- Geautomatiseerde tests voor het gedrag waar je om geeft, geleidelijk geïntroduceerd.
- Feature flags zodat wijzigingen in productie kunnen worden teruggedraaid zonder een deploy.
- Observability, logs, foutregistratie, belangrijke bedrijfsstatistieken, zodat je snel ontdekt of er iets is misgegaan.
- Een gefaseerde uitrol: nieuwe code draait eerst voor interne gebruikers, dan een klein percentage echte gebruikers, dan iedereen.
Strangler fig-patroon. In plaats van een riskante big-bang-herschrijving leid je verkeer door een dunne facade die geleidelijk steeds meer verzoeken naar nieuwe services doorstuurt, terwijl het legacy-systeem de rest blijft afhandelen tot het leeg is.
Voorbeeld: een minimale facade-route
Een kleine gateway kan per route beslissen of een verzoek naar de legacy-monoliet of de nieuwe service gaat. Op een framework-neutrale manier geschreven:
app.use((req, res, next) => {
if (req.path.startsWith('/api/v2/invoices')) {
return proxy(req, res, NEW_SERVICE_URL);
}
return proxy(req, res, LEGACY_URL);
});
Elke gemigreerde module verplaatst één prefix van LEGACY_URL naar NEW_SERVICE_URL. Gebruikers zien nooit een big-bang-overgang; het team levert kleine, omkeerbare stappen.
Veelvoorkomende valkuil: het nieuwe systeem en het oude schrijven alsof ze op dag één functioneel gelijkwaardig moeten zijn. Dat hoeft niet. Begin met het kleinste stuk dat echte bedrijfswaarde heeft, bewijs het migratiepad en herhaal dan.
Veelgemaakte fouten
- Modernisering om de technologiekeuze laten draaien. Mooie nieuwe stacks helpen niet als het datamodel en de integraties nog mysterieus zijn.
- Wachten op een volledig herontwerp voordat je de zichtbare problemen oplost. Gebruikers betalen de prijs; momentum sterft.
- Het oude systeem te vroeg uitschakelen. Draai beide lang genoeg parallel om het nieuwe te vertrouwen.
- Institutionele kennis verliezen. Werk samen met de mensen die het systeem kennen. Hun kennis is het echte bezit.
Een realistische tijdlijn
- Stabiliseren: back-ups, monitoring, beveiligingspatches, minimale tests.
- In kaart brengen: documenteer wat er bestaat en wat daadwerkelijk wordt gebruikt.
- Prioriteren: kies het stuk dat nu pijnlijk is en waardevol om als eerste te moderniseren.
- Vervangen: bouw de nieuwe versie van dat stuk naast het oude.
- Omschakelen: leid verkeer, monitor, los problemen op.
- Afvoeren: ontmantel het oude stuk pas nadat het een tijdje rustig is geweest.
- Herhaal voor het volgende stuk.

