Werkwijze

Hoe ik werk, aan de hand van één echt project

Iedereen kan zorgvuldig werk beloven. Zo zag het eruit toen ik een live ordersysteem waar een bedrijf elke dag op draait upgradede van CakePHP 3.6 op PHP 7.4 naar CakePHP 5.4 op PHP 8.5.

Zes regels waar ik me aan houd

Eerst een vangnet

Voordat ik één regel verander, schrijf ik geautomatiseerde tests die vastleggen hoe je systeem zich nu gedraagt. Elke stap gaat pas door als ze nog slagen.

Bewijzen dat niets veranderd is

Oud en nieuw worden vergeleken op echte data: PDF's, exports, imports, totalen. "Het lijkt te werken" is geen test.

Oplossen wat ik tegenkom

Kom ik een beveiligingslek of een oude bug tegen, dan loop ik er niet langs omdat het niet in de offerte stond. Ik los het op en meld het.

Productie is heilig

Eerst staging, livegang buiten kantooruren, een uitgeschreven terugvalplan en een kopie van de database van vlak voor de overstap.

Blijven meekijken na livegang

Na een grote update lees ik dagenlang zelf de logs. Problemen zijn opgelost voordat jij of je gebruikers weten dat er een was.

Bellen voordat het misgaat

Als iets je systeem bedreigt, hoor je het eerst van mij, samen met wat ik er al aan gedaan heb.

Het project

Een Nederlands e-commercebedrijf draait zijn hele operatie op een eigen ordersysteem: webshoporders komen binnen via een webhook, het kantoor verwerkt orders en offertes, het magazijn beheert de voorraad, en koeriers plannen en rijden hun routes vanaf hun telefoon. Het draaide op CakePHP 3.6 en PHP 7.4, beide al jaren zonder beveiligingsupdates.

353
geautomatiseerde tests, de eerste geschreven vóór de upgrade begon
581
bestanden gewijzigd in 102 commits
6 dagen
van eerste commit tot live
0
fouten voor gebruikers sinds de livegang
0

Vóór de upgrade: in de lucht houden terwijl anderen offline gingen

Vlak voor de upgrade werd een kritiek PHP-lek bekend dat servers met oude PHP-versies raakte, en voor PHP 7.4 was er op dat moment geen oplossing. Een veelvoorkomende reactie was om de getroffen sites offline te halen tot ze gepatcht konden worden.

Voor dit bedrijf betekent offline: geen orders, geen routes en geen leveringen. Dus heb ik de klant gebeld voordat ze het ergens anders hoorden, en een tijdelijk slot voor het systeem gezet: medewerkers logden in met één extra wachtwoord, en alleen het netwerk van het webshopplatform kon nog orders afleveren. Het bedrijf werkte gewoon door en er is geen order verloren gegaan.

Dat gaf de tijd om het echte probleem goed op te lossen: de upgrade hieronder. Het slot is verwijderd toen de nieuwe versie live stond.

1

Een vangnet voordat ik de code aanraak

De eerste tests zijn geschreven en gedraaid op de oude versie, om vast te leggen hoe het systeem zich werkelijk gedroeg: de order-webhook, de beheerschermen, PDF's, Excel-exports, voorraad en reserveringen. Met elke stap kwamen er tests bij.

  • Een aparte testdatabase met de structuur van productie, maar met fictieve orders en klanten
  • De order-webhook veld voor veld getest, inclusief dubbele en mislukte aanroepen
  • Complete bedrijfsprocessen: webhook-order → voorraadreservering → koerier → route → afgeleverd
  • Rechten per rol, elke geplande taak, elke export
2

Upgraden in stappen, niet in één sprong

CakePHP 3.6 → 4.6 → 5.4, en PHP 7.4 → 8.4 → 8.5. Elke stap ging pas door als dezelfde tests nog slaagden.

  • Elke library bijgewerkt; verlaten libraries vervangen door ondersteunde, of door een klein stuk eigen code
  • Inloggen opnieuw gebouwd op de nieuwe authenticatie-library, waarbij bestaande wachtwoorden geldig bleven
  • 144 schermtemplates, 13 geplande commando's en 5 eigen plugins overgezet
  • 223 validatieregels met de hand herschreven
  • Statische analyse (PHPStan) en codestijlcontroles zonder meldingen
  • De frontend bewust ongemoeid gelaten, zodat elk scherm er hetzelfde uitziet en hetzelfde werkt
3

Bewijzen dat er niets veranderd is

Op staging, met een verse kopie van de productiedata, zijn oud en nieuw naast elkaar gelegd:

  • PDF's van orders, offertes, routes en labels: identieke tekst
  • Een echte webshoporder geïmporteerd: 56 velden identiek
  • De prijssynchronisatie met de leverancier: 618 producten identiek
  • De voorraadtellers van alle 1.535 producten, en de totalen van dashboard en statistieken
  • Alle 189 menupagina's geopend, alle 31 opgeslagen filters nageteld tegen de database
  • Met elke rol ingelogd en elke menulink aangeklikt, plus een testdag door de klant
4

Live in de nacht, met een weg terug

Staging ging eerst. Productie is op een maandag om 23:34 overgezet volgens een uitgeschreven draaiboek, met een volledige kopie van de database van vijf minuten ervoor en de oude versie klaar om terug te zetten. Gebruikers hoefden de volgende ochtend alleen één keer opnieuw in te loggen.

Drie dagen later ging de server midden op een werkdag van PHP 8.4 naar PHP 8.5. Het kantoor bleef ondertussen orders opslaan.

5

Blijven meekijken, en oplossen voordat iemand het merkt

Bij de livegang houdt het voor mij niet op. In de eerste week las ik elke 30 minuten de logs: wie er werkte, of iemand een fout kreeg, of orders en nachtelijke taken goed doorkwamen.

Zo kwamen drie problemen boven. Elk is dezelfde dag opgelost, afgedekt met een nieuwe test, uitgeprobeerd op staging en live gezet, voordat een gebruiker ertegenaan liep. Eén ervan was een waarschuwing in een logbestand die niemand ooit op het scherm had gezien. De klant hoorde het van mij, in het rapport, als iets dat al was opgelost.

Na die week neemt monitoring het over: het systeem mailt me als een koppeling uitvalt en opnieuw als die weer werkt.

Wat ik heb opgelost waar niemand om vroeg

De opdracht was "upgrade het framework". Wie elk bestand van een systeem leest, ziet wat eraan mankeert. Niets hiervan stond in de offerte, en alles is opgelost.

Beveiligingslekken

  • Wachtwoordhashes werden door de overzichten naar de browser gestuurd
  • Zoekfilters werden als ruwe SQL vanuit de browser uitgevoerd: iedereen die was ingelogd kon alle data lezen of wijzigen
  • Stored XSS via gedeelde opgeslagen filters
  • Geen CSRF-bescherming op formulieren en knoppen
  • Pagina's waren via de URL te openen zonder het recht om ze te zien
  • De order-webhook accepteerde aanroepen van iedereen, en de log bewaarde klantgegevens
  • Een bestandsupload waarmee een script op de server kon worden geplaatst
  • Een API-sleutel die in Excel-exports terechtkwam

Bugs die er al jaren zaten

  • Kwam dezelfde webhook twee keer binnen, dan werd de order twee keer aangemaakt of de voorraad twee keer gereserveerd. Nu geblokkeerd door de database zelf
  • Het verwijderen van een instelling kon ongemerkt elke order wissen die haar gebruikte
  • Koeriersprijzen verloren hun centen: 9,99 werd 10
  • Succes- en foutmeldingen werden niet meer getoond sinds een eerdere, half afgemaakte upgrade
  • In Excel-exports ontbraken de datumkolommen
  • Zeer grote exports lieten de server crashen
  • Meerdere pagina's die al jaren kapot waren, werken weer

Zodat het niet opnieuw gebeurt

  • Een API van een leverancier had ooit twee maanden plat gelegen zonder dat iemand het merkte. Die wordt nu elke 30 minuten gecontroleerd, met een mail als hij uitvalt en als hij weer werkt
  • Een synchronisatie die niets ontvangt telt nu als fout, niet als "niets gewijzigd"
  • Staging kan nooit echte klanten mailen
  • Rechten werken volgens "geweigerd tenzij toegestaan", per rol gecontroleerd tegen de oude situatie: 0 verschillen
  • Deprecation-waarschuwingen opgeruimd, zodat de volgende upgrade een kleine is
Een kleine eerste stap

Code review voor een vaste prijs van €950

Weet je niet goed hoe je systeem ervoor staat, of wat een upgrade zou vragen? Ik lees de code en vertel het je, op papier, voordat je aan iets groters begint.

  • Ik lees de code en de dependencies van één applicatie
  • Een rapport in gewone taal: wat verouderd is, wat risico geeft en wat in orde is
  • Beveiligingsbevindingen, op volgorde van urgentie
  • Een stappenplan voor de upgrade of reparatie, met een vaste offerte
  • Een gesprek waarin ik het met je doorloop
€950
excl. btw, één applicatie

Laat je het werk uit het rapport door mij uitvoeren, dan gaat de €950 van die factuur af.

Is je systeem erg groot? Dan zeg ik dat voordat ik begin, niet achteraf.

Vraag een code review aan