The Challenge of Legacy Enterprise Systems
This isn't a simple brochure site migration. We're talking about a full-featured enterprise ERP platform.
When handling mission-critical data across all these modules, migration isn't just about changing syntax. It's about ensuring data integrity, business continuity, and zero regression across dozens of interconnected features.
Strategic Note: TDD as Your Safety Net
With PHPUnit already in your stack, you're ahead of the game. We wrapped legacy logic in tests before the move, ensuring the new CI4 codebase produced identical outputs across invoicing, payroll calculations, and iXBRL generation.
The 6-Phase Enterprise Migration Roadmap
For a system this complex, a phased strangler pattern is non-negotiable.
Phase 1: Audit & Module Inventory
Map all 12+ modules, their dependencies, and data flows. Identify which modules have React frontends (API-only migration) vs. server-rendered views.
Phase 2: TDD Wrapper Layer
Write PHPUnit tests for every critical calculation: tax breakdowns, payroll math, invoice totals, iXBRL number formatting.
Phase 3: API-First Migration
Your React components already hit endpoints. Migrate those endpoints to CI4 first - the frontend won't know the difference.
Phase 4: Core Modules
Migrate auth, clients, invoicing, and time tracking - the most frequently used paths. Test in production with feature flags.
Phase 5: Complex Calculations
Payroll, multi-country tax, iXBRL submission. Run parallel mode: both systems calculate and compare results before saving.
Phase 6: Reporting & Documents
Template-based document generation, payslips, CRM exports. Validate output format parity (PDF, CSV, iXBRL).
Core Function Mapping Reference
Use this table to quickly identify standard CI3 calls and their modern CI4 equivalents.
| Legacy (CI3) | Modern (CI4) |
|---|---|
$this->load->library('session') | Auto-loaded, use service('session') |
$this->session->userdata | $this->session->get |
$this->session->set_userdata | $this->session->set |
$this->form_validation->set_rules | $this->form_validation->setRule |
validation_errors() | $this->form_validation->listErrors() |
$this->input->post | $this->request->getPost |
$this->input->get | $this->request->getGet |
$this->input->json | $this->request->getJSON() |
redirect('dest') | return redirect()->to('/dest') |
$this->input->is_ajax_request() | $this->request->isAJAX() |
$query->row_array() | $query->getRowArray() |
$query->result_array() | $query->getResultArray() |
$query->num_rows() | $query->resultID->num_rows |
order_by | orderBy |
where_in | whereIn |
$this->output | $this->response |
set_content_type | setContentType |
$this->encrypt->encode | $this->encrypter->encrypt |
force_download | $this->response->download |
$this->db->query() | $this->db->query() (still works) |
$this->load->view() | return view() |
Module-by-Module Migration Strategy
Your system has distinct modules. Here's the priority order and approach for each:
| Module | Priority | Migration Approach | Key Considerations |
|---|---|---|---|
| Authentication | High | Direct migration | Session handling differences |
| Client Management | High | CRUD + validation | Form validation rules |
| Invoicing | High | Parallel calculation testing | Math precision, PDF generation |
| Multi-Country Tax | High | Parallel calculation | Different tax rules per country |
| Payroll Management | High | Parallel with validation | Math precision, compliance |
| Time Tracking | Medium | API endpoint migration | React integration, real-time updates |
| Task Tracking | Medium | API endpoint migration | React integration |
| Project Management | Medium | Template system update | Template parsing engine |
| Payslips/Timesheets | Medium | Report generation | PDF/CSV output |
| CRM/Leads | Medium | CRUD migration | Email integration |
| Document Generation | Low | Template engine swap | Output format parity (PDF/DOCX) |
| iXBRL Submission | Low | API/library swap | Companies House compliance |
Model & Database Layer: Key Shifts
CI4's model system is more powerful. Here's what changes for your data-heavy modules:
| Legacy (CI3) | Modern (CI4) | Impact on Your Modules |
|---|---|---|
$this->load->model('invoice_model'); | $model = model('InvoiceModel'); | Invoicing, CRM, Projects |
$query->result_array() | $query->getResultArray() | All reporting modules |
$this->db->insert_id() | $model->insertID() | Time tracking, tasks |
$this->db->trans_start() | $this->db->transBegin() | Payroll, invoicing (critical!) |
$this->db->affected_rows() | $model->affectedRows() | Batch operations |
Pro Tip: Entities for Financial Data
Use CI4 Entity classes for Invoice, Payroll, and TaxCalculation. This encapsulates business rules (e.g., $invoice->calculateTotal() or $tax->applyVAT()) ensuring consistency across controllers and API endpoints.
React.js + CI4: Migration Strategy
Your React frontend already communicates via API endpoints. This is your secret weapon for a smooth migration.
⚡ API-First Approach
Your React components don't care if the backend is CI3 or CI4 - they only see JSON. Migrate API endpoints first with identical request/response structures. The frontend won't know anything changed.
🔄 Parallel API Testing
Run both CI3 and CI4 API endpoints simultaneously. Compare JSON responses for the same request. Any difference is a bug. Once validated, switch the React app to point to CI4.
📡 CORS & Headers
CI4 has built-in CORS configuration. Set $filters->aliases['cors'] and enable for your API routes. No more custom middleware hacks.
🔐 API Authentication
If using JWT or token-based auth for React, CI4's Authentication and JWT libraries are drop-in replacements for your existing patterns.
Multi-Country Tax & Payroll: Critical Path
This is where financial accuracy is non-negotiable. Your TDD approach is essential.
💰 Tax Calculation Migration Checklist
- Extract all tax rules (VAT, GST, Sales Tax per country) into a TaxCalculator service - not scattered across controllers
- Write PHPUnit tests with known good inputs/outputs from CI3 before touching code
- Run parallel mode: calculate tax in both CI3 and CI4, log differences, fix until identical
- Payroll same logic: gross pay → deductions → net pay → employer contributions
- Use decimal/bcmath for currency math, never floats. CI4 supports this natively
📊 Time Tracking for Invoicing
- Time entries → billable amounts → invoice line items
- Test rounding rules (15-min increments? 6-min?)
- Verify rate overrides (client-specific, project-specific, staff-level)
- Test date ranges and timezone handling (staff in different countries)
Template-Based Document Generation
Invoices, payslips, reports, and Companies House submissions.
📄 Template Engine Options
CI3 likely used a custom parser or simple str_replace(). CI4 offers multiple paths:
- Keep existing: Your custom parser still works with minor adjustments
- Twig/Blade: More powerful template inheritance
- View Parser: CI4's built-in parser for simple templates
🏛️ iXBRL for Companies House
This is likely a dedicated library. Migration approach:
- Check if the iXBRL library has a CI4-compatible version
- If not, wrap it in a service class that both CI3 and CI4 can use
- Validate output XML against Companies House schema
Comprehensive Testing Strategy
With PHPUnit already in your stack, here's how to structure tests for your modules:
| Module | Test Type | Key Assertions |
|---|---|---|
| Invoicing | Golden Master | Totals, tax, line items match CI3 exactly |
| Payroll | Parameterized | Test 50+ salary/rate/deduction combinations |
| Tax | Country-specific | Each country's tax rules produce correct output |
| Time Tracking | Integration | Hours → billable amounts → invoice creation |
| iXBRL | Schema validation | XML validates against Companies House XSD |
| API Endpoints | Response comparison | CI3 vs CI4 JSON identical |
Security & Configuration: What's New
🔐 .env Files are Mandatory
Database credentials, API keys for Companies House, tax lookup services, payroll processors - all go in .env.
🛡️ CSRF for Forms
Your React app uses token-based auth, but server-rendered forms (admin panels, reports) need <?= csrf_field() ?>.
🔑 Encryption Keys
Payroll data, client financials - all need encryption. Set a strong encryption.key in .env.
📁 Public Folder
Move all public assets (React build files, CSS, JS) to /public. Better security separation.
Ready to Start Your Enterprise Migration?
Every ERP system has unique challenges. Let's discuss your specific CI3 to CI4 migration path - invoicing, payroll, tax, React integration, and all.
Let"s discuss a zero-downtime strategy tailored to your system.