When a citizen has to transfer a contract or bring a document, they book an appointment at the counter of the company supplying their water, gas or energy. The portal is the shared calendar of operators and the client's staff: over a hundred thousand appointments since 2020, always with slots already set for the coming weeks. There was no moment to 'switch off and on'.
01The starting point
The old portal worked, but surprises abounded: the availability rules (hours, capacity, hundreds of exceptions and holidays) lived in compiled programs whose source no longer existed — we rebuilt them by reading the database queries left inside. One rule was even in the web page code. The SMS notifications queried tables that no longer existed: they'd been dead for a while and no one had noticed. And the appointment didn't record which company it was for, a datum simply missing on shared counters.
02The choices
A new application, not a refit. Written in Python with a mature open-source framework, on a cloud machine in an Italian region, registered to the client and administered by us: the infrastructure is theirs, we run it. The client is a line of configuration: adding a company no longer needs a release. Strict separation: on shared counters, the other company's appointments don't exist for the operator, not in the calendar nor in search.
03The migration: with people already in line
The switch happened counter by counter, with explicit authorization for each. Over twenty-seven thousand past and future appointments were migrated, repeatably (each row remembers its origin, no duplicates) and silently (no confirmation emails sent for appointments from years back). For a few days the two portals ran in parallel, with a probe every half hour bringing across appointments booked on the old one; then a notice, finally the old one read-only. Where a datum wasn't there, we didn't invent it.
04Security, verified from outside
A portal on the internet must be checked as an outsider would. The review found one serious problem: no limit on login attempts. Closed the same day, applied only to the login page. The rest was in order: modern encryption, security headers, database reachable only from the machine itself, admin access from a single address. And the data isn't only in the cloud: every night a full copy comes down to an on-site server.
05What we learned
1. When the rules are in lost code, the data still tells the truth. 2. You don't invent missing data: better an empty, declared field than a plausible false value. 3. Migrations are done with people inside: parallel, probe, notice, read-only. Never a leap in the dark. 4. Security is verified from outside, and automated checks must themselves be checked.