Moving from CodeIgniter 3 to CodeIgniter 4: what changes and how to plan it
CodeIgniter 3 is in maintenance mode and CodeIgniter 4 is a rewrite, not a drop-in upgrade. Compare the two versions, see how controllers, models, queries, views and routes translate, and plan a low-risk conversion.
Updated 8 min read

In this article
Thousands of business applications still run on CodeIgniter 3: admin panels, booking systems, small SaaS products and internal tools written years ago and still earning their keep. The official download page describes CodeIgniter 3 as the legacy version, in maintenance and receiving mostly security updates, with 3.1.13 as the current release. It targets PHP 5.6 and later, while CodeIgniter 4.7 requires PHP 8.2.
The hard part is that CodeIgniter 4 is not an upgrade in the usual sense. The official guide says it plainly: version 4 is a rewrite, it is not backwards compatible, and it is "more appropriate to think of converting your app, rather than upgrading it." There is no automatic tool and no twelve-step checklist.
This article compares the two versions side by side, shows how the most common code patterns translate, and sets out a practical plan for moving an application without stopping feature work. It is written for developers doing the work and for founders deciding whether and when to fund it.
CodeIgniter 3 and 4 compared
| Topic | CodeIgniter 3 | CodeIgniter 4 |
|---|---|---|
| Status | Legacy, maintenance mode (3.1.13) | Actively developed (4.7.4, July 2026) |
| PHP | 5.6+ | 8.2+ for 4.7 |
| Installation | Download and copy | Composer (codeigniter4/appstarter) or zip |
| Entry point | index.php in the project root | public/index.php; the rest of the code sits outside the web root |
| Code organisation | No namespaces; the $this->load superobject | PSR-4 namespaces; services, factories and Composer autoloading |
| Routing | Auto-routing by URL segments; $route[] array | Auto-routing off by default; defined routes in app/Config/Routes.php; optional Auto Routing (Improved) |
| Configuration | PHP arrays in application/config | Classes extending BaseConfig, overridable from .env |
| Models | Thin CI_Model | Model with CRUD, validation, timestamps, soft deletes and pagination |
| Hooks | $hook[] configuration | Events plus controller filters |
| Testing | Limited | PHPUnit integration with feature-test helpers |
| Authentication | Third-party libraries | Official CodeIgniter Shield |

Should you migrate, and when?
A conversion costs real time, so be honest about the reasons. The strongest reasons to move are usually these:
- PHP support. Your hosting provider or security policy needs a supported PHP version, and your CodeIgniter 3 code or its libraries do not run cleanly on it.
- Security posture. CodeIgniter 4 keeps application code outside the web root, enables context-aware escaping with
esc(), ships CSRF, secure-headers and Content Security Policy support, and has an official authentication library. - Hiring and maintenance. Developers who know modern PHP expect namespaces, Composer and tests. A framework in maintenance mode becomes harder to staff over time.
- New features. APIs, queues of work and integrations are simpler to build with CI4's REST resources, transformers and Composer packages.
Reasons to wait include an application that is being retired within a year, or one with no tests and no one who understands the business rules. In that second case, spend the first weeks writing characterisation tests or documenting behaviour, not converting code.
Plan the conversion
The official advice is to start with a fresh CodeIgniter 4 project in a new folder and bring your application across component by component. A plan that works for most small and medium applications:
- Inventory. List controllers, models, libraries, helpers, hooks, config files and third-party packages. Mark anything that uses libraries removed in CI4: Calendaring, FTP, Javascript, Shopping Cart, Trackback, XML-RPC and Zip Encoding, plus the CAPTCHA, Email, Path and Smiley helpers.
- Create the new project with
composer create-project codeigniter4/appstarter, and point it at a copy of the production database. - Port configuration first: database, sessions, email and custom config files.
- Port models, because controllers depend on them.
- Port one module at a time (controllers, views and routes together), with tests or a manual checklist for each.
- Replace hooks with events and filters, and authentication with Shield or your chosen library.
- Run both versions side by side on staging, compare outputs, then switch traffic.
Since both versions can read the same database, a gradual move is possible: some teams route new sections of a site to the CI4 application at the web-server level while older sections stay on CI3. That needs care with sessions and shared login, so plan authentication early.
How common code translates
Controllers
Move files to app/Controllers, add a namespace, extend BaseController instead of CI_Controller, and delete the defined('BASEPATH') line. Return output instead of echoing it.
<?php
// CodeIgniter 3: application/controllers/Helloworld.php
class Helloworld extends CI_Controller
{
public function index($name)
{
echo 'Hello ' . html_escape($name) . '!';
}
}<?php
// CodeIgniter 4: app/Controllers/Helloworld.php
namespace App\Controllers;
class Helloworld extends BaseController
{
public function index($name)
{
return 'Hello ' . esc($name) . '!';
}
}Sub-folders become namespaces with capitalised names: application/controllers/users/auth/Register.php becomes app/Controllers/Users/Auth/Register.php in App\Controllers\Users\Auth.
Models and database code
Models move to app/Models, extend CodeIgniter\Model, and declare $table and $allowedFields. Instead of $this->load->model('x'), use model(X::class) or new X(). Query Builder method names move to camelCase and you start from a table builder:
<?php
// CodeIgniter 3
$query = $this->db->select('title')
->where('id', $id)
->limit(10, 20)
->get('mytable');
$rows = $query->result_array();
// CodeIgniter 4
$db = db_connect();
$builder = $db->table('mytable');
$rows = $builder->select('title')
->where('id', $id)
->limit(10, 20)
->get()
->getResultArray();Other renames you will meet often: affected_rows() to affectedRows(), result() to getResult(), get_where() to getWhere() and select_max() to selectMax(). CI3's database result caching does not exist in CI4; use the Caching driver instead. One trap: limit(0) returns all rows in CI4 unless you change the setting described in the user guide (available since 4.5.0).

Views
Views move to app/Views and mostly keep working. Replace $this->load->view('x', $data) with return view('x', $data);, and $this->load->view('x', $data, TRUE) with $html = view('x', $data);. Replace html_escape() with esc(). For page templates, CI4's View Layouts replace the header-and-footer include pattern common in CI3 projects.
Routing
This is the change that most often breaks a converted site. CI4 disables auto-routing by default, so every URL needs a route. The routing upgrade guide also notes that CI3's (:any) wildcard becomes (:segment), because (:any) in CI4 matches several segments.
<?php
// CodeIgniter 3: application/config/routes.php
$route['product/(:any)'] = 'catalog/product_lookup';
// CodeIgniter 4: app/Config/Routes.php
$routes->get('product/(:segment)', 'Catalog::productLookup');The guide uses $routes->add() in its examples for backwards compatibility but recommends HTTP-verb routes such as get() and post() for security. If you have hundreds of URLs, you can enable Auto Routing (Legacy) temporarily, but the safer target is Auto Routing (Improved) or explicit routes.
Configuration, libraries, helpers and hooks
- Config: custom files become classes in
app/ConfigextendingBaseConfig;$this->config->item('site_name')becomesconfig('Site')->siteName. - Libraries: namespace them and instantiate with
neworFactories::libraries('X'). To replace a core class, register your own inapp/Config/Services.phpinstead of creating aMY_class. - Helpers: largely similar, and they can be autoloaded in
app/Config/Autoload.php. String helper functions moved into the Text helper. - Hooks: become
Events::on()calls;pre_controllerandpost_controllerare gone, so use controller filters. - Redirects:
redirect('login/form')becomesreturn redirect()->to('login/form');. It returns a response object instead of exiting. - Input and output: CI3's Input class maps to
IncomingRequest, and Output maps to the Response object.
Pitfalls that catch most teams
The official upgrade notes contain several small changes that cause confusing bugs if you miss them during a conversion:
- Redirects must be returned. In CI3,
redirect()sent headers and stopped the script. In CI4 it returns aRedirectResponse; if you call it withoutreturn, execution carries on and the redirect never happens. Cookies and headers set before the redirect are not carried over automatically either, so callwithCookies()orwithHeaders()when you need them. - HTTP method names are uppercase. Since 4.5.0, CI4 uses
GETandPOSTrather than the lowercase names CI3 used. Code that compares$this->request->getMethod()to'post'will silently fail. - No more
BASEPATH. CI4 does not define the constant, so the olddefined('BASEPATH') OR exit(...)guard at the top of each file will stop it from running. Remove the line everywhere. - Removed hook points.
pre_controllerandpost_controllerno longer exist; move that logic into controller filters.display_overrideandcache_overrideare gone too. - Errors stop execution. In CI4, any error not ignored by
error_reporting()stops the framework, so notices that CI3 logged quietly may now surface as failures. Fix them rather than lowering the error level. - The testing environment is reserved. CI4 uses
testingfor PHPUnit only. If you had a CI3 environment named "testing" for a staging server, rename it and create a matching boot file inapp/Config/Boot/.
Passwords deserve a separate note. If your CI3 application stores hashes created with PHP's password_hash(), any PHP 8 code can still check them with password_verify(), so users can keep their passwords. If it uses an older custom scheme, plan a rehash-on-login step before you switch, and test it with real (anonymised) accounts on staging.
Effort and risk at a glance
| Area | Typical effort | Main risk |
|---|---|---|
| Views | Low | Missed escaping when changing echo syntax |
| Models and queries | Medium | Renamed methods, limit(0) behaviour, missing $allowedFields |
| Controllers | Medium | Code that relied on the superobject or on exit after redirects |
| Routing | Medium to high | Broken URLs and lost SEO if routes are missed |
| Authentication and sessions | High | Locking users out, password hash compatibility |
| Removed libraries | Varies | Finding Composer replacements |
For public sites, export the full list of indexed URLs before you start and test that each one returns the same page, or a 301 redirect, on the new application.
Next steps
Start small: create a CI4 project, connect it to a copy of your database, and port one read-only section end to end. That first module shows the real cost of the conversion far better than any estimate. Once it works, put the remaining modules in a tracked list, port them in order of business value, and keep the CI3 application running until every route has a tested CI4 equivalent. After the switch, upgrades within CodeIgniter 4 are routine: read each release's upgrade notes and run composer update.
Frequently asked questions
Can I upgrade CodeIgniter 3 to 4 with Composer?
No. CodeIgniter 4 is a rewrite and is not backwards compatible. The official guide recommends starting a new CI4 project and converting your application's components into it.
Is CodeIgniter 3 still supported?
The CodeIgniter download page describes version 3 as legacy and in maintenance, receiving mostly security updates. The current CI3 release is 3.1.13.
Can CodeIgniter 3 and 4 use the same database?
Yes. Both work with the same MySQL or other supported database, which allows a gradual migration. Plan sessions and login carefully if both applications serve users at the same time.
Why do my CI3 URLs return 404 in CodeIgniter 4?
Auto-routing is disabled by default in CI4. Define routes in
app/Config/Routes.php, or enable Auto Routing (Improved). Also change(:any)wildcards to(:segment).
Your next customer could start right here.
Introduce your tool, product or business to readers exploring technology.
Explore placements and request a private quote
Sources
- Upgrading from 3.x to 4.x — CodeIgniter Foundation
- Upgrade Controllers — CodeIgniter Foundation
- Upgrade Models — CodeIgniter Foundation
- Upgrade Database — CodeIgniter Foundation
- Upgrade Routing — CodeIgniter Foundation
- Upgrade Views — CodeIgniter Foundation
- Upgrade Configuration — CodeIgniter Foundation
- Download CodeIgniter — CodeIgniter Foundation
Written with substantial AI assistance. Facts checked against cited sources. Editorial policy.
Comments
No comments yet. Be the first to join the conversation.
Leave a comment
Every comment is read by a moderator before it appears. Your email address is never published; it is used only to contact you about this comment.
Related tools
- Directory profile
Docker
Container platform for building, sharing and running applications, including Docker Desktop, Docker Engine, Docker Hub and Docker Scout.
A tool connected to a service selected for this article. Compare its fit for your project; inclusion is not an endorsement.
- Directory profile
Git
Free, open-source distributed version control system for tracking code changes, branching and merging, created in 2005 and used by GitHub, GitLab and Bitbucket.
A tool connected to a service selected for this article. Compare its fit for your project; inclusion is not an endorsement.
More from Cloudsoft Zone

Industry updateCodeIgniter
CodeIgniter 4.7.5 security release: what to patch and how
CodeIgniter 4.7.5 fixes six advisories, including a critical upload bypass and Parser code execution. See who is exposed and how to upgrade safely.
By Cloudsoft Zone

Troubleshooting guideCodeIgniter
CodeIgniter 4 validation not working? 9 causes and fixes
Fix CodeIgniter 4 validation problems fast: wrong input source, lost errors after redirect, permit_empty, strict JSON rules, is_unique placeholders and more.
By Cloudsoft Zone

Industry updateCakePHP
What's new in CakePHP 5.4: ORM changes, DTO mapping and a built-in container
CakePHP 5.4 changes default eager loading, enforces entity-table ownership and adds request-to-DTO mapping, an optional built-in DI container, typed unhydrated queries and JSON streaming. Here is what to check before upgrading.
By Cloudsoft Zone
