PHP to Laravel migration — a practical guide
Somewhere in your codebase is a file called functions.php with 4,000 lines in it, three different date-formatting helpers, and a comment from 2016 that says "temporary fix, revisit." You didn't write it. Nobody currently on your team wrote most of it. But it runs your checkout flow, and it works, which is exactly why nobody has touched it in three years. This guide is about moving that code to Laravel without breaking the thing that pays your bills — the strategy, the order of operations, what it costs, and who you need on the team to do it.
In this guide
- 1. Why teams move off legacy PHP
- 2. Two migration strategies, and which one you actually want
- 3. Assess before you touch a single file
- 4. A phased migration plan that ships in stages
- 5. Risks, and how to de-risk each one
- 6. Cost and timeline
- 7. The skills your migration team needs
- 8. Why India for this hire
- 9. How to hire the team
- 10. FAQ
1. Why teams move off legacy PHP
Core PHP without a framework, or an old CodeIgniter 2/3 install, was a perfectly reasonable choice ten years ago. It's fast to write, cheap to host, and gets a product to market quickly. The problem is what happens after year three: the app grows, three or four developers touch it over time with no shared conventions, and the codebase turns into a maze where every new feature risks breaking something unrelated. You've likely felt this already — a "quick fix" to the invoice page that quietly broke the export tool, because both shared a global function nobody remembered existed.
Laravel solves the structural half of that problem. Routes live in one place instead of scattered across if-statements. Eloquent gives you a consistent way to query the database instead of raw SQL strings copy-pasted between files. Middleware handles auth and validation the same way on every route, so you stop finding the one endpoint someone forgot to protect. None of this makes your business logic smarter — it just makes the codebase legible enough that a new developer can be productive in a week instead of a month.
There's a hiring angle too, and it's a practical one. Core PHP developers who can read a decade-old undocumented codebase are getting harder to find and command a premium when you do. Laravel developers are abundant, since it's the most-used PHP framework by a wide margin, which means your applicant pool is larger, your onboarding is faster because Laravel conventions are the same on every project, and you're not permanently dependent on the two people who understand your custom framework.
Security is the third reason, and it's the one that shows up in an incident report rather than a roadmap. Legacy PHP built before parameterized queries were standard practice often has SQL built with string concatenation, no CSRF tokens, and session handling that was reasonable in 2014 and dangerous in 2026. Laravel closes most of that gap by default — Eloquent parameterizes queries automatically, CSRF middleware is on unless you turn it off, and the framework gets patched on a public release cycle instead of relying on whoever wrote your app originally to remember to fix it.
2. Two migration strategies, and which one you actually want
There are really only two ways to do this, and picking the wrong one is the single most common reason migrations stall out or get cancelled halfway.
Strangler fig (incremental, module by module)
Named after the strangler fig vine that grows around a host tree until the tree is no longer needed. You stand up a fresh Laravel application, route new features and one legacy module at a time into it, and let the old codebase shrink over months until there's nothing left to strangle. A reverse proxy decides which app handles which request based on the URL path — your users never see the seam. This is the approach almost every successful migration uses, because the business keeps shipping the whole time and you can stop, pause, or reprioritize at any point without losing what's already been rebuilt.
Full rewrite (build the whole thing in Laravel, then cut over)
You freeze new feature work on the old app, rebuild everything in Laravel in parallel, and switch traffic over on a single cutover date. It's simpler to reason about and produces a cleaner end result with no legacy code left hiding in a corner. The cost is real: feature work stalls for the entire rewrite, you carry the full weight of both systems until cutover day, and if the rewrite runs long (which it usually does), you're stuck maintaining a codebase you've already mentally abandoned.
Default to strangler fig unless one of three things is true: the app is genuinely small (a handful of modules, low complexity), the business logic is simple enough to redocument in a week, or the existing codebase has so little structure that patching around it to run two systems side by side costs more engineering time than a clean rebuild would. If none of those apply, incremental is the safer bet, and it's the approach we recommend to almost every client who asks.
3. Assess before you touch a single file
Every migration that goes badly skips this step, or rushes it. Before any code moves, your team should be able to answer these five questions with confidence, not guesses:
- What are the actual routes? Legacy PHP routing is often implicit — a URL maps to a file on disk, or a switch statement buried in an index file. Map every real route your users hit, including the ones only an internal admin uses once a month, before you decide what "done" looks like.
- What does the database schema actually look like? Not what the original ER diagram from 2018 says — the live schema, including the columns nobody uses anymore and the ones with names that no longer match what they store. A schema dump and a week of query-log review beats any existing documentation.
- Where does the business logic actually live? In a mature core PHP app, logic tends to be scattered across controllers, model-adjacent helper files, and, uncomfortably often, inline in views. Find it before you migrate, because logic buried in a Blade-equivalent template is the easiest thing to lose during a rewrite.
- How does auth currently work? Session-based, token-based, a custom cookie scheme someone built before JWT was common? You need to know exactly how a user stays logged in today, because getting this wrong mid-migration logs out your entire user base at once.
- What do the templates depend on? Old PHP templates often mix HTML, business logic, and direct database calls in the same file. Note which views are simple presentation and which ones are secretly doing work — those need extra care when they become Blade components.
Budget real time for this — a week or two for a mid-size app, longer for anything that's grown past a few hundred thousand lines. It feels slow when you're eager to start writing Laravel code, but every hour spent here saves multiple hours of rework once you discover, three weeks into the migration, that the "simple" invoicing module actually depends on a global variable set by the login page.
4. A phased migration plan that ships in stages
With the assessment done, the actual migration follows a fairly consistent order across most projects we've staffed. Here's the sequence and why it goes in this order.
| Phase | What you build | Why it goes here |
|---|---|---|
| 1. Scaffold + auth | Fresh Laravel install, Eloquent models mapped to the existing schema, Sanctum or Breeze wired to your live users table | Every other module needs a working session. Get this right once, early, instead of retrofitting it later. |
| 2. Reverse proxy routing | Nginx or Cloudflare rules that send specific paths to the new Laravel app, everything else to legacy | This is what lets both apps run side by side. Set it up before migrating a single real feature. |
| 3. Low-risk module first | Pick something with low traffic and no revenue dependency — an internal report, a static content page | Proves the pipeline end to end (routing, deploy, DB access) before you risk anything customers depend on. |
| 4. Core modules, one at a time | Controllers rebuilt with Eloquent models, business logic extracted into services, Blade views replacing old templates | This is the bulk of the work. Each module ships independently and gets tested in production before the next one starts. |
| 5. Background jobs and queues | Cron scripts and long-running tasks moved to Laravel's queue system (Redis or database driver) | Cron-driven PHP scripts are usually the most fragile legacy code. Laravel queues give you retries and monitoring for free. |
| 6. Decommission legacy | Remove the old app once every route is served by Laravel and nothing points at the legacy codebase anymore | The satisfying part. Confirm with real traffic logs, not assumption, before you delete anything. |
Two implementation notes worth calling out. First, Eloquent doesn't require you to rewrite your schema. It maps onto existing tables, and you rename columns or add relationships incrementally rather than all at once. Second, on templates: most teams rebuild legacy views as Blade one to one, but if you're modernizing the frontend at the same time, Inertia.js with Vue or React is a common alternative — just scope that as a separate decision, because bundling a frontend rewrite into a backend migration is how six-month projects become fourteen-month projects.
5. Risks, and how to de-risk each one
Nobody migrates a production app without some risk. The difference between a smooth migration and a painful one is whether you planned for these four failure modes in advance.
- No test coverage on the legacy app. Most core PHP applications built without a framework have little to no automated testing, which means you have no safety net telling you whether a migrated module behaves the same as the original. Write characterization tests against the legacy behavior first, capturing what the code actually does today, bugs included, before you rebuild it, so you have something concrete to compare the Laravel version against.
- Data migration errors. Moving or reshaping data mid-migration is where silent corruption happens — a date field parsed differently, a null handled inconsistently. Run migrations against a full production copy first, diff the row counts and checksums before and after, and never run a schema-altering migration directly against live data without a tested rollback.
- No parallel run before cutover. Don't flip a module from legacy to Laravel and hope. Where the stakes are high (checkout, billing, anything touching money), run both systems in parallel for a stretch, compare outputs on real traffic, and only cut over once the numbers match consistently.
- Scope creep disguised as migration. "While we're in there, let's redesign the checkout flow" is how a three-month migration becomes an eight-month one. Keep the migration a like-for-like translation to Laravel first. Redesign afterward, once you're standing on stable ground, as a separate project with its own budget.
The common thread across all four: the risk isn't Laravel, it's the transition. Every mitigation above is really the same idea applied differently — verify before you trust, and never make the new system and the old system diverge without a way to check the difference.
6. Cost and timeline
Timeline depends heavily on codebase size and how much of it is entangled versus modular, but as a working range: a mid-size application with 40 to 80 routes and a handful of core modules takes 3 to 6 months with a small dedicated team on the strangler-fig approach. Larger, older applications, think a decade of accumulated logic, 150-plus routes, multiple integrations, run 8 to 14 months. A full rewrite of either usually takes 1.5 to 2 times longer than the incremental version of the same project, because nothing ships until everything is done.
On cost, here's what a managed team through TechTeamsOnline runs, all-inclusive of payroll, compliance, and equipment — no separate recruitment or visa fees layered on top:
| Role | Monthly (managed, India) | Hourly equivalent | US market equivalent |
|---|---|---|---|
| Mid-level Laravel developer | from $2,500/mo | ~$16/hr | $7,000-$10,000/mo |
| Senior Laravel / legacy PHP developer | from $3,200/mo | ~$35/hr | $12,000-$18,000/mo |
| Migration tech lead | from $4,500/mo | ~$50/hr | $18,000-$25,000/mo |
| Full migration team (3 engineers + lead) | ~$11,000-$13,000/mo | blended | $40,000+/mo locally |
That blended team cost against a comparable local hire lands at roughly a 65 to 75 percent saving — the same range companies see hiring individual engineers in India, extended to a full migration team. Get exact numbers for your codebase with the cost calculator or the full rate card.
7. The skills your migration team needs
A migration team is a different hire than a greenfield Laravel team, and it's worth being precise about the difference when you're interviewing. You need people who are strong in modern Laravel (Eloquent, routing, middleware, queues, Sanctum for auth), but who can also sit down with a 4,000-line procedural PHP file, no comments, no framework, and figure out what it's actually doing before they translate it. That second skill is rarer than the first, and it's the one that actually determines whether the migration goes smoothly.
Beyond the PHP itself, look for:
- Solid MySQL fundamentals — reading and reshaping an existing schema safely matters more here than in a new build, where you'd just design the schema fresh.
- A test-writing habit — not because the job description says "TDD," but because characterization tests are the only thing standing between your migration and a silent regression in production.
- Comfort with ambiguity — legacy code rarely does what the original developer intended, and a good migration engineer investigates rather than assumes.
- DevOps literacy — someone needs to own the reverse proxy routing rules that keep both apps running side by side. It doesn't have to be a dedicated DevOps hire, but someone on the team needs to be comfortable in Nginx config.
If you're hiring for this specifically, our Laravel developers and PHP backend developers pages break down seniority levels and what to test for in an interview. For teams that need broader backend coverage beyond PHP specifically, see backend engineers.
8. Why India for this hire
PHP has been the default stack for Indian web development for close to two decades — it's how a huge share of Indian agencies and startups built their first products, which means the country's developer pool has an unusually deep bench of engineers who are equally comfortable in modern Laravel and in reading the kind of undocumented legacy PHP you're trying to move away from. That combination is exactly what a migration needs, and it's harder to find in markets where PHP was never the dominant stack to begin with.
The scale backs this up. India has an estimated 4.3 to 5.8 million software developers, roughly one in eight of the world's developer population, and that pool is growing about 11% a year, double the rate in the US. On cost, a US software developer earns a median of $133,080 a year according to the BLS; a senior Laravel developer through a managed India team runs from about $3,200 a month, or roughly $38,400 a year, doing comparable work at a fraction of the price. A five-person team costs around $11,000 a month against roughly $45,000 a month for the same seniority hired locally — about 75% of the budget freed up to extend the migration timeline properly instead of rushing it.
On the quality question people usually ask next: 174 of the Fortune Global 500 run more than 390 engineering centers in India, employing over 950,000 people, and India holds the world's highest concentration of CMMI Level 5 and ISO 27001 certified IT firms. That's not a coincidence, it reflects a market that's been building and maintaining large-scale production software for a long time, migrations included. Time-zone overlap works better than most people expect too: a team on an 11am-8pm IST schedule gives you roughly 2.5 hours of live overlap with US-East and about 4.5 hours with the UK, with the rest of the gap working in your favor — code you hand off at the end of your day is often ready for review the next morning.
On code ownership, standard contracts through a managed provider use work-for-hire and IP-assignment clauses that vest everything built, code, documentation, the migration itself, in you, backed by India's Digital Personal Data Protection Act 2023, which carries penalties up to ₹250 crore for violations and includes an exemption specifically for processing overseas client data under contract. You own what gets built, in writing, the same as you would with a local hire.
9. How to hire the team
A migration is a bounded project with a clear end state, which makes it a good fit for a dedicated team model rather than staff augmentation — you want people who own the migration end to end, not individuals slotted into your existing sprint who each own a slice of it. If you're not sure which model fits your situation, our staff augmentation vs dedicated teams guide walks through the decision in more depth.
Whatever route you take, vet for the migration-specific skills above rather than generic Laravel proficiency — ask a candidate to walk through how they'd approach an undocumented procedural file, not only how they'd structure a new Eloquent model. A short take-home task using a sanitized snippet of your actual legacy code tells you more than any resume will.
Through TechTeamsOnline, you get pre-vetted candidates matched to your stack within 48 hours, with the sourcing, HR, and compliance handled so you're only running the technical interview. See how to build your team in India for the end-to-end process, or go straight to dedicated teams if you already know that's the model you want.
10. Frequently asked questions
Ready to build your migration team?
Chat with Alex — tell him your legacy stack, codebase size, and timeline. You'll have interview-ready Laravel and PHP profiles matched to your migration in 48 hours.
7-day risk-free trial. No commitment. No credit card.