Moving an unsupported CodeIgniter application onto TypeScript and React one module at a time, with parity proven by tests rather than assumed.
Going from CodeIgniter to a TypeScript and React stack changes both the language and the architecture. Done as a single rewrite, that is two hard problems failing simultaneously, which is why this particular jump has such a poor track record.
We sequence it. A router sits in front of both applications, sessions are shared, and the database stays common. Modules move one at a time, each with characterisation tests written against the legacy behaviour first, so parity is demonstrable.
The compensation for the extra work is real: static typing across the whole stack, a large hiring pool, and a supported runtime. For an application on PHP 5.6 and CodeIgniter 2, those are not luxuries.
Teams that start here often pair it with Laravel to MERN migration, CodeIgniter to Laravel migration and web app development.
Changing language and architecture in one release means two hard problems fail at the same time, and you cannot tell which one caused the incident.
Four things that catch teams attempting it in one jump.
A decade of business rules exists only in the code, and any missed rule becomes a production incident after cutover.
PHP native sessions and Node sessions do not interoperate by default, so users are logged out at the boundary.
PHP loose comparison hides bugs that surface immediately under TypeScript, which is beneficial and needs handling deliberately.
Legacy schemas carry conventions the code depends on implicitly, and reimplementing without capturing them produces subtle data bugs.
Both stacks live, parity proven, rollback always available.
Characterisation tests written against the legacy module before rewriting, so parity is measured rather than assumed.
A proxy layer and shared session store with compatible auth, so users never see the boundary.
Legacy schema modelled explicitly in TypeScript including its implicit conventions, with runtime validation at the edges.
Order chosen by business value and risk, so painful modules improve first and risky ones move once the path is proven.
Interfaces rebuilt route by route with a shared design system during transition.
CI, automated tests and one-command deploys, usually the first time the application has had any of them.
Sixteen to twenty-four weeks, delivering value from month one.
Modules, dependencies, traffic and criticality mapped, with sequence agreed against risk.
Node and React application, routing layer, shared sessions and CI, proven with one low-risk module.
Modules moved in sequence, each with characterisation tests, flagged release and routing rollback.
Schema improvements applied compatibly once traffic for a table sits mostly on the new side.
The legacy application retires when routing shows it serving nothing.
Typed from database to interface.
React introduced per route, sharing design tokens with remaining legacy pages.
Shared sessions and legacy password hash support so nothing is forced on users.
Legacy behaviour captured as tests before it is rewritten.
An education provider ran student enrolment, timetabling and billing on CodeIgniter 2 with PHP 5.6. Nobody currently employed had written the original code, and the enrolment rules were understood mainly through folklore.
We wrote characterisation tests against the enrolment module before touching it. Those tests found four behaviours nobody had documented, including a fee proration rule that had been quietly correct for twelve years and would certainly have been lost in a rewrite.
Modules moved one at a time over five months. The proration rule survived, which the finance team noticed and appreciated more than any of the new features.
Characterisation tests written against the old code are what make this migration safe, because the specification you need does not exist anywhere else.
We move one slice at a time behind a router, with both systems live, so every step is small and every step is reversible.
A working demo and a written note every Friday covering what shipped, what slipped and what it means for the date. No status theatre.
Nothing goes live in one jump. We run in parallel or behind a flag until the numbers say it is safe to switch over.