Shawn Mervin

Moving a CodeIgniter app to React without a rewrite.

Your CodeIgniter application works, the business depends on it, and the front end is showing its age. This is the order I do the move in, learned replacing the system behind a working rental yard.

From a real migration: a CodeIgniter 3 system with roughly 78 controllers, now serving a React web app and a React Native field app.

First, decide what you're actually replacing

Probably not the back end

Most CodeIgniter apps that need help need it in the browser: jQuery screens, page reloads, nothing that works on a phone. The models, the queries and the business rules underneath usually still work. Throwing them out to fix the front end is the expensive way to get a new UI.

So the plan is: keep CodeIgniter as the engine, turn it into an API, and build the new front end on top of it.

Why not just upgrade to CodeIgniter 4?

CodeIgniter 4 is a rewrite of the framework, not an upgrade. Namespaces, a different loader, a changed query builder, filters instead of hooks. A large CI3 app moving to CI4 is months of work and you end up with the same screens on a newer framework. If the problem is the UI, fix the UI. CI3 runs on PHP 8.2, and an API cares far less about its framework version than a page-rendering app does.

Before and after. Before: the browser talks to one CodeIgniter app holding views, controllers and models, on one MySQL database. After: a React web app and a React Native field app both talk to the same CodeIgniter code acting as a JSON API, on the same database. The old views are gated off. BEFORE Browser CodeIgniter views (HTML) controllers models + rules MySQL AFTER React web app React Native field app The same CodeIgniter code, now an API old views: gated JSON models + rules tokens for web, session cookie for the phone same MySQL
Keep the engine and the database; replace what sits on top of them.

The steps, in order

1. Read the old code for the rules

Before writing anything new, find where the money happens: invoicing, credits, taxes, status changes. Note every rule you find, especially the odd ones. Those are usually somebody's hard-won fix for a real customer problem.

2. Give the controllers a JSON voice

Add API endpoints that call the same models the views already use. No second copy of the logic, no second database. Where the old code put logic in the view or controller, move it down into the model so both the old screen and the API get it.

3. Sort out authentication for two kinds of client

The old app uses PHP sessions. A React web app is happier with bearer tokens; a React Native app can keep using a session cookie. Both can be served by the same API if each request is checked for the user and the tenant the same way. Get this right once, in one place, before building screens.

4. Build the new screens a workflow at a time

Pick a whole job someone does, such as creating an agreement or closing out a delivery, and rebuild it end to end in React. Ship it next to the old screen. Let people use either. Then pick the next one.

5. Test on a scrubbed copy of production

Refresh a sandbox database from a production snapshot and scrub every outside email address before anything runs against it. Test data that doesn't look like production hides the bugs production has.

6. Check parity on every surface

When a behaviour exists in the old UI, the web app and the phone app, compare all three before calling a workflow moved. Open the screen and look at it. Don't trust a green build.

7. Freeze, then gate, then retire

Once nothing needs the old screens, freeze them: no more changes. Then gate them: browser visits to old pages redirect to their new equivalents, other callers get a clear error, and anything the mobile app still calls in the background is allowed through. When the logs go quiet, retire the old UI and keep the repository as a read-only reference. Archive before you delete anything on disk.

Traps I hit so you don't have to

Hard-coded database names

Old code written for one company often names its database directly. The day a second company shares the system, those lines quietly read the wrong data. Search for every literal schema name before you go multi-tenant.

Models that switch connections

A CodeIgniter model that changes $this->db in its constructor changes it for whoever loaded it next. That's a cross-tenant leak waiting for the right request order. Give each model its own connection handle instead.

Queries that fail silently

CI3's query builder can return false on a bad column and the next line calls row_array() on it. Old code is full of this. Turn on error logging that actually reaches a person before you start moving things around.

Parity drift on mobile

Web ships the moment you deploy; a phone app waits on app store review. Plan for the API to serve the old and new versions of the field app at the same time.

Questions people ask first

Can CodeIgniter 3 run on modern PHP?

CI3 runs on PHP 8.2 with some deprecation notices suppressed. PHP 8.4 is the point where the deprecations get serious, which is a good deadline for moving new endpoints off it.

Do we have to replace the database?

No. Keeping one database through the whole move is what makes it safe. Schema changes can come afterwards, one at a time.

React or React Native?

Both, if your staff work in the field. They share a language and a lot of thinking, and one API can serve them both.

Read next

Sitting on a CodeIgniter app?

Send me what it does and roughly how big it is. I'll tell you honestly whether it's a migration, a rescue, or best left alone.