Keeping a four-year-old CodeIgniter app healthy
clinicxo started in 2022 on CodeIgniter 3 and is still on it. What changed underneath it so that it could stay there.
clinicxo is the oldest product here. It runs clinics: prescriptions, appointments, patients, branches, invoices. It started in January 2022 on CodeIgniter 3 and PHP 7.4, and today it is on PHP 8.2 and still CodeIgniter 3. We have been asked a few times why it was not rewritten. It was not rewritten because the clinics using it would gain nothing from a rewrite and lose a lot from a migration.
What did change is everything around the framework.
Configuration out of the code
The 2022 version had database credentials, SMS gateway keys and payment keys in config files that were committed. That was fine when one person ran one copy. It stopped being fine when there was a staging environment and a second developer. The first change was moving every secret to environment variables, with a small loader that reads a .env file in development and the container environment in production. Boring work, done over a few weeks, with no user-visible change.
One container
The app used to run on a VPS with nginx, PHP-FPM and MySQL installed by hand. It now runs as a single Docker image built from the repository, with nginx and PHP-FPM inside it, deployed by dockxo. The database is a managed MariaDB that dockxo runs on the same server. A deploy takes about ninety seconds and rolling back is picking an older image. This was the change that made every later change cheap.
Queues
SMS reminders, invoice generation and report exports used to run inside the HTTP request that triggered them. A clinic with four hundred appointments tomorrow would wait a long time for the reminder page to load. Those now go into a Redis list, and a worker container processes them. The worker is supervised by cronxo in service mode, which restarts it if it dies and alerts us if it keeps dying.
Search
Patient lookup was LIKE '%name%' over a table that had grown to a few hundred thousand rows. It was slow and it could not handle Turkish characters sensibly: a search for “Çelik” would miss “Celik” and the other way round. Meilisearch indexes patients now, with the normalisation handled by the engine, and the search box returns results as you type. The index is rebuilt nightly and updated on every patient save.
What stayed
The framework, the controllers, most of the views. CodeIgniter 3 on PHP 8.2 needs a maintained fork and a handful of patches, and we carry those. The clinics see the same screens they learned in 2022, with faster search and reminders that arrive on time. A rewrite would have produced a nicer codebase and a year of regressions. We would rather have the clinics.