Versioning Your 100+ Controllers in CI3 Without Changing a Single Route

Rutvi DhameliyaRutvi Dhameliya
3 min read

Introduction

Ever faced this?

"How can I refactor 100+ controllers in a live CI3 app without touching routes or breaking frontend code?"

This was my reality on a legacy CodeIgniter 3 project. I had to add new features and restructure logic without breaking thousands of AJAX/API calls already in production.

Here’s how I did it using custom router logic and a versioned controller directory, all without touching the routes file.


🧠 The Problem

I had to:

  • ✨ Refactor 100+ controller methods

  • βž• Add new features

  • πŸ”’ Preserve all existing frontend AJAX/API endpoints

But without:

  • ❌ Editing 1000+ frontend calls
  • ❌ Manually redirecting each old method
  • ❌ Renaming existing controller files

I needed a smarter, safer way to version my logic β€” just like APIs, but at the controller level.


❌ What I Didn’t Want to Do

  • Update every route or endpoint manually

  • Add redirect() logic inside hundreds of methods

  • Rename controllers and risk breaking everything

Too risky. Too time-consuming. Too boring.


βœ… The Solution: Router-Level Controller Versioning

I created a localized/ folder inside the controllers/ directory and duplicated all controllers that needed changes into it. Then, I modified the router to check this versioned folder first.


πŸ—‚οΈ Folder Structure

Before:

application/
└── controllers/
    β”œβ”€β”€ Dashboard.php
    β”œβ”€β”€ Automation.php
    └── ... (100+ controllers)

After:

application/
└── controllers/
    β”œβ”€β”€ Dashboard.php
    β”œβ”€β”€ Automation.php
    └── localized/
        β”œβ”€β”€ Dashboard.php
        β”œβ”€β”€ Automation.php
        └── ... (100+ controllers)

πŸ› οΈ Custom Router (MY_Router.php)

Inside application/core/, I created MY_Router.php and added the following:

<?php defined('BASEPATH') OR exit('No direct script access allowed');

class MY_Router extends CI_Router {
    protected function _validate_request($segments) {
        if (empty($segments)) {
            return $segments;
        }

        // Get controller segments (ignore method name)
        $controller_segments = $segments;
        if (count($segments) >= 2) {
            array_pop($controller_segments);
        }

        // Build the file path for the versioned controller
        $localized_file = APPPATH . 'controllers/localized/' . implode('/', $controller_segments) . '.php';

        // If versioned controller exists, load it
        if (file_exists($localized_file)) {
            array_unshift($segments, 'localized');
            log_message('debug', 'Using localized controller: '.$localized_file);
        } else {
            log_message('debug', 'Localized controller not found: '.$localized_file);
        }

        return parent::_validate_request($segments);
    }
}

πŸ” How It Works

  • πŸ”Ž CI3 checks if the controller exists in the `localized/` folder

  • βœ… If yes, it uses the updated controller

  • πŸ” If no, it falls back to the original

  • πŸ›‘οΈ No route changes required β€” the frontend still uses the same URLs


πŸ§ͺ Real-World Impact

  • βš™οΈ Easy rollout of new features controller-by-controller

  • πŸ” Seamless fallback to old code during testing

  • 🚫 Zero downtime or broken frontend calls

  • πŸ” Safer QA and debugging in production

  • ⏱️ No need to touch thousands of routes or AJAX calls


🧰 Tech Stack Used

  • PHP (CodeIgniter 3)

  • MySQL

  • jQuery & AJAX frontend

  • Webhook/Event-based triggers


πŸ“š Lessons Learned

  • CI3 may be old, but it's still flexible when used smartly

  • Versioning isn't just for APIs β€” it's great for controllers too

  • A clean router design can help prevent future tech debt

  • Avoiding unnecessary redirects reduces debugging headaches


🏁 Final Thoughts

If you're working on a large CI3 codebase and need to modernize without disrupting the existing system, this router-based controller versioning approach could save you hours β€” or even days β€” of work.

It’s simple, non-intrusive, and gives you a safe playground for upgrades while keeping production stable.

Happy coding! πŸš€


βœ‰οΈ Let me know in the comments if you’ve faced similar challenges β€” or if you’ve found other clever ways to handle large-scale controller refactoring in CI3!

20
Subscribe to my newsletter

Read articles from Rutvi Dhameliya directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Rutvi Dhameliya
Rutvi Dhameliya