How a CodeIgniter 3 application becomes a CodeIgniter 4 one
Inventory before estimate
We start by counting. A script walks the application folder and lists every controller and public method, every model, library, helper, hook and view, along with which third-party libraries are loaded where. Web server logs tell us which URLs are really visited. It is common to find that a meaningful part of the code is dead: old reports, abandoned modules, test controllers. Dead code is not ported. This inventory is what makes the estimate honest, and it is yours to keep.
What maps cleanly and what does not
Some things translate almost mechanically. Routes become route definitions. Config arrays become config classes, with secrets moved to the .env file. Views mostly survive, apart from how they are loaded and escaped. Other things need rethinking. The CodeIgniter 3 loader and the get_instance() super-object have no equivalent, so every library that reached into the controller must be given its dependencies properly. MY_Controller logic usually splits into a base controller and filters. Hooks become events or filters. The input class is replaced by the request object, and global XSS filtering is gone, which means output escaping has to be done in views where it belongs. Form validation rules are similar in spirit but differ in details, so each rule set is checked individually.
The database layer
The schema does not need to change, and we do not change it during the port. Queries do. The Query Builder in CodeIgniter 4 is close to the old one but not identical, results are returned differently, and models gain features such as allowed fields and built-in validation. We port models one at a time and run old and new versions against the same data to compare results. Long hand-written SQL that works is kept as it is and wrapped in a model method.
Running both at once
While the port is in progress, the CodeIgniter 3 application stays live and continues to receive fixes. Each fix is logged so it can be mirrored in the new code. On staging, both versions point at copies of the same database. We compare page output, exports and calculated figures for a list of scenarios you help us write. Differences are either bugs in the port or bugs in the original that the port exposed. You decide which behavior is correct.
Cutover and what follows
Because both applications use the same schema, the switch is a deployment, not a data migration. Sessions are the one exception: users log in again unless we bridge them. The old code is kept deployable for an agreed period. Once you are on CodeIgniter 4, features such as a proper REST API or automated tests become straightforward additions instead of workarounds.