The bug lived in a file the app never loaded
A PHP OCR parsing fix depended on tracing autoloading to find the live class among duplicate files before changing and deploying the code.
The bug lived in a file the app never loaded
We chased truncated defendant names through a PHP OCR platform and found two copies of the parser class. One copy was dead — wrong namespace, never autoloaded. Every hour spent editing it would have changed nothing on the live site.
The fix was small. Finding the right file to change was the whole job. Before touching a line, we read the autoload config and the controllers' use statements to confirm which class the running app actually loaded.
The problem
A US law firm's legal-intake platform runs OCR over court notice PDFs to pull structured fields — including the defendant's name. On real records, that name came out wrong. Sometimes it was truncated to the first name only. Sometimes it bled across into the adjacent field.
The stack is PHP on CodeIgniter 4. The parsing logic lives in a court-parser class and an OCR-service class. So the plan looked simple: open the parser, fix the name-boundary logic, ship.
Why the obvious fix didn't work
The codebase held two copies each of the court-parser class and the OCR-service class. Same class names, similar code, two locations.
One pair was dead. It sat under a namespace path that did not match its directory, so CodeIgniter's autoloader never resolved it. The class file existed on disk, looked authoritative, and was never loaded at runtime. Editing it would have shipped a "fix" that changed nothing — and sent us chasing a bug that appeared unfixed no matter what we typed.
What we did
We refused to edit anything until we could prove which copy was live. The decision: treat "which file loads?" as a question with a definitive answer in the config, not a coin flip.
We read CodeIgniter's autoload configuration to see how namespaces mapped to directories. Then we read the real controllers and traced their use statements — the exact fully-qualified class names they import. Both pointed to the pair under the app's Libraries directory. The other pair, with the mismatched namespace, was confirmed dead.
That turned an ambiguous two-hit grep into one file with a name on it. Only then did we touch the parser logic.
// The controller's own import decides which class runs.
// Read this BEFORE editing — not the grep results.
use App\Libraries\CourtParser; // live: namespace matches directory
// use App\Services\CourtParser; // dead twin: namespace never resolves
How it works
The path from symptom to deploy was five deliberate steps.
-
1
Investigate
Investigate. Confirm the symptom live: query the platform's API across all 10,727 hearing records, not a cherry-picked few, and characterize the failure — truncation versus field bleed.
-
2
Confirm Live File
Confirm the live file. Read the autoload config and the controllers'
usestatements to identify the one court-parser and one OCR-service class the running app loads. Mark the mismatched-namespace duplicates as dead and leave them untouched. -
3
Reproduce in Docker
Reproduce in Docker. Stand up a local environment that mirrors production — PHP 8.1, MySQL 8.0, and the full 10,727-hearing dataset imported — so the bug reproduces on real data and the fix can be verified against it.
-
4
Back Up
Back up. Before writing to production, copy the live file so there is an instant rollback.
-
5
Deploy
Deploy. Push the fix through a fresh private repo on a branch, merge to main, deploy over SSH, and validate the live file with
php -lto confirm zero syntax errors.
There was one access wrinkle worth naming. The hosting portal's login was gated by a live Cloudflare captcha that can't be automated, so we set up direct SSH to the production server instead, using a cPanel-generated key. The captcha decided the deploy channel.
The diagram below shows the fork that mattered: the dead copy on one branch, the live copy on the other, and every downstream step hanging off the live one.
What broke / what surprised us
The parser fix was the easy part. The full UI end-to-end test in Docker is what surprised us — it surfaced three bugs that existed only in the local setup, not in production.
- A missing writable cache directory, which the app needs to run at all locally.
- A
baseURLhardcoded to the live domain, so local pages pointed back at production. - Upload size limits set too small to accept real court notice PDFs.
Results
We measured the fix the same way we confirmed the bug: against the 10,727-hearing dataset, through the real interface, not a synthetic sample.
- Correct file, first edit. Confirming the live class from autoload and
usestatements meant the first change landed on the code the app actually runs — no wasted deploy cycle on the dead twin. - Reproduction on real data. The Docker mirror imported the full dataset, so the parser fix was verified against the same records that exposed the failure.
- Safe deploy. The live file was backed up before the write and passed
php -lafter it — a syntax-clean deploy with a ready rollback.
Takeaways
- Confirm which file loads before you edit. In a codebase with duplicate classes, the autoload config and the controllers'
usestatements are the source of truth — not a grep, and not the most authoritative-looking file. - Dead code is a trap, not just clutter. A never-loaded duplicate wastes debugging time and can convince you a real fix "didn't work." Verify the runtime path first.
- Reproduce on the real population. Confirming a bug against 10,727 live records, then mirroring that dataset locally, beats reasoning from a few examples.
- A faithful mirror has its own bugs. Cache dirs, hardcoded base URLs, and upload limits are local-only failures — expect them, and run a full UI test to flush them out.
- Infrastructure shapes the plan. An un-automatable captcha rerouted the entire deploy to SSH. Let the constraints pick the channel, then keep the deploy safe with a backup and a
php -lcheck.
Ready to Implement These Strategies?
Let's discuss how to apply these insights to your specific business challenges.
Schedule Consultation