Approach

How I work, shown on one real project

Anyone can promise careful work. This is what it looked like when I upgraded a live order system that a company runs on every day, from CakePHP 3.6 on PHP 7.4 to CakePHP 5.4 on PHP 8.5.

Six rules I work by

Safety net first

Before I change a line, I write automated tests that record how your system behaves today. Every step only goes ahead when they still pass.

Prove nothing changed

Old and new are compared on real data: PDFs, exports, imports, totals. "It seems to work" is not a test.

Fix what I find

If I come across a security hole or an old bug, I don't walk past it because it wasn't in the quote. I fix it and tell you.

Production is sacred

Staging first, go-live outside office hours, a written rollback plan, and a database copy from right before the switch.

Keep watching after go-live

After a major update I read the logs myself for days. Problems are fixed before you or your users know there was one.

Call before it breaks

When something threatens your system, you hear it from me first, together with what I've already done about it.

The project

A Dutch e-commerce company runs its whole operation on a custom order management system: webshop orders come in by webhook, the office handles orders and quotes, the warehouse manages stock, and couriers plan and drive their routes from their phones. It ran on CakePHP 3.6 and PHP 7.4, both without security updates for years.

353
automated tests, the first ones written before the upgrade started
581
files changed in 102 commits
6 days
from first commit to live
0
errors for users since go-live
0

Before the upgrade: keeping it running when others went offline

Just before the upgrade, a critical PHP vulnerability was published that hit servers still running old PHP versions, and no fix was available for PHP 7.4 at that moment. A common reaction was to take the affected sites offline until they could be patched.

For this company, offline means no orders, no routes and no deliveries. So I called the client before they heard about it elsewhere, and put a temporary lock in front of the system: staff logged in with one extra password, and only the webshop platform's own network could still deliver orders. The business kept working and no order was lost.

That bought the time to do the real fix properly: the upgrade below. The lock was removed once the new version was live.

1

A safety net before touching the code

The first tests were written and run on the old version, to capture how the system really behaved: the order webhook, the admin screens, PDFs, Excel exports, stock and reservations. The suite grew with every step.

  • A separate test database with the production structure but fictitious orders and customers
  • The order webhook tested field by field, including duplicates and failures
  • Full business flows: webhook order → stock reservation → courier → route → delivered
  • Rights per role, every scheduled job, every export
2

Upgrade in steps, not in one jump

CakePHP 3.6 → 4.6 → 5.4, and PHP 7.4 → 8.4 → 8.5. Each step only went ahead when the same tests still passed.

  • Every library updated; abandoned ones replaced by supported ones or by small code of my own
  • Login rebuilt on the new authentication library, with existing passwords kept valid
  • 144 screen templates, 13 scheduled commands and 5 in-house plugins ported
  • 223 validation rules rewritten by hand
  • Static analysis (PHPStan) and code style checks clean
  • The front end deliberately left alone, so every screen looks and works the same
3

Prove that nothing changed

On staging, with a fresh copy of the production data, old and new were put side by side:

  • PDFs for orders, quotes, routes and labels: identical text
  • A real webshop order import: 56 fields identical
  • The supplier price sync: 618 products identical
  • Stock counters of all 1,535 products, dashboard and statistics totals
  • All 189 menu pages opened, all 31 saved filters counted against the database
  • Every role logged in and every menu link clicked, plus a test day by the client
4

Go-live at night, with a way back

Staging went first. Production switched on a Monday at 23:34, following a written go-live order, with a full database copy from five minutes before and the old version ready to restore. Users only had to log in once more the next morning.

Three days later the server moved from PHP 8.4 to PHP 8.5 in the middle of a working day. The office kept saving orders while it happened.

5

Keep watching, and fix it before anyone knows

Go-live is not where I stop. During the first week I read the logs every 30 minutes: who was working, whether anyone got an error, whether orders and night jobs came through.

Three problems showed up that way. Each was fixed, covered by a new test, tried on staging and put live the same day, before a user ran into it. One was a warning in a log file that nobody would ever have seen on screen. The client heard about them from me, in the report, as things already solved.

After that week, monitoring takes over: the system mails me when an integration stops working and again when it recovers.

What I fixed that nobody asked for

The job was "upgrade the framework". Reading every file of a system means you see what is wrong with it. None of this was in the quote, and all of it was fixed.

Security holes

  • Password hashes were sent to the browser by the data grids
  • Search filters ran as raw SQL from the browser: anyone logged in could read or change any data
  • Stored XSS through shared saved filters
  • No CSRF protection on forms and buttons
  • Pages could be opened by URL without the right to see them
  • The order webhook accepted calls from anyone, and its log stored customer data
  • A file upload that could place a script on the server
  • An API key leaking into Excel exports

Bugs that had been there for years

  • The same webhook arriving twice created the order twice or reserved stock twice. Now blocked by the database itself
  • Deleting a setting could silently delete every order that used it
  • Courier prices lost their cents: 9.99 became 10
  • Success and error messages had not been shown since an earlier half-finished upgrade
  • Excel exports were missing their date columns
  • Very large exports crashed the server
  • Several pages that had been broken for years work again

So it doesn't happen again

  • A supplier API had once been down for two months without anyone noticing. It is now checked every 30 minutes, with a mail when it fails and when it recovers
  • A sync that receives nothing now counts as a failure, not as "nothing changed"
  • Staging can never mail real customers
  • Rights follow "deny unless allowed", checked role by role against the old situation: 0 differences
  • Deprecation warnings cleaned up, so the next upgrade is a small one
A small first step

Code review for a fixed €950

Not sure what state your system is in, or what an upgrade would take? I read the code and tell you, in writing, before you commit to anything bigger.

  • I read the code and the dependencies of one application
  • A written report in plain language: what is out of date, what is risky and what is fine
  • Security findings, ordered by how urgent they are
  • A step-by-step plan for the upgrade or repair, with a fixed quote
  • A call to walk you through it
€950
excl. VAT, one application

If you order the work from the report, the €950 is deducted from that invoice.

Is your system very large? Then I tell you before I start, not afterwards.

Ask for a code review