Skip to content
Rohit Anand

Engineering case study

Modernizing Birdeye's Frontend Platform

Platform EngineeringArchitectureModernization

Led the phased modernization of Birdeye's legacy PHP/jQuery frontend into a scalable React platform without disrupting product delivery—enabling faster development, improved consistency, and long-term platform evolution.

Migrating from PHP/jQuery to React at enterprise scale—platform transformation across engineering, organization, and business delivery.

The biggest challenge wasn't React—it was modernizing a live product without slowing 40+ engineers.

Why the business funded this

Legacy PHP/jQuery architecture had become the primary bottleneck for feature velocity, frontend consistency, and engineering productivity. Modernizing the platform was essential to continue scaling product development on $100M+ ARR surfaces.

My role

Engineering Lead → Engineering Director

Team

40+ engineers across product teams

Duration

Multi-year platform modernization

Responsible for

  • Led migration strategy
  • Managed frontend / UI platform direction
  • Worked with Product & Design
  • Reviewed architecture
  • Planned phased rollout
  • Mentored engineers
  • Hiring
  • Stakeholder communication
  • Delivery ownership

$100M+ ARR

Business scale

40+

Engineers

0

Rewrite freeze

Weekly

Delivery

PHP → React

Platform

React SPA

End state

Engineering timeline

  1. Discovery
  2. Architecture
  3. Prototype
  4. Shared components
  5. Migration
  6. Dual runtime
  7. Legacy removal

Company

Birdeye

From

PHP / jQuery

To

React SPA

Strategy

Incremental dual runtime

Products

Social · Insights · Campaigns

Live

birdeye.com

ReactJavaScriptTypeScriptPHPjQuerySPAFrontend ArchitectureDual Runtime
Modernizing Birdeye's Frontend Platform visual
Modernizing Birdeye's Frontend Platform visual
Modernizing Birdeye's Frontend Platform visual

Business

Business impact

  • Kept $100M+ ARR product delivery moving—no multi-quarter rewrite freeze
  • Raised developer productivity with one shared React platform
  • Faster onboarding onto a single frontend stack and pattern language
  • Enabled independent team ownership of product surfaces
  • Unlocked later UI/UX consistency across Social, Insights, and Campaigns

Why this problem mattered

Legacy PHP/jQuery had become the primary bottleneck for feature velocity, consistency, and productivity. Modernizing was essential to keep scaling product development.

We evaluated rewrite, wrap, or incremental migration. A full rewrite would have frozen delivery on revenue products. We chose incremental dual runtime so teams could keep shipping while replacing surfaces—one at a time.

Overview

Birdeye’s enterprise SaaS needed a frontend that could keep pace with product velocity. PHP/jQuery made UI slower to change, harder to share across Social, Insights, and Campaigns, and poorly suited to a modern SPA.

Goals

  • Replace PHP/jQuery UI with a maintainable React SPA foundation
  • Migrate without freezing the product roadmap
  • Keep dual runtime explicit, time-boxed, and owned—not accidental forever
  • Give product teams shared patterns and clearer UI ownership
  • Improve UI iteration speed for enterprise customer-facing products

Architecture

Legacy PHP/jQuery did not jump straight to React. A shared component layer made dual runtime finishable; SPA journeys then shared the same API layer and services.

Shared components and contracts were the difference between temporary dual runtime and permanent fragmentation.

  1. Browser
  2. CDN
  3. Legacy PHP / jQuery
  4. Shared componentsPrimitives, patterns, standards
  5. React SPAMigrated product journeys
  6. API layer
  7. Backend services

Organization

Team organization

Not a single-squad rewrite. Success required Frontend, Backend, Platform, QA, Product, and Design shipping in parallel—coordination was the hard problem.

Engineering (40+)
├── Frontend / UI platform
├── Product engineering teams
├── Backend services
├── QA
├── Product
└── Design

Engineering organization

Scaled to 40+ engineers across product teams

Product engineering teams

Owned customer-facing features on Social, Insights, Campaigns, and related surfaces

Frontend / UI platform

Architecture, shared components, migration standards, and dual-runtime guardrails

Backend services

API contracts and service ownership that both stacks depended on

QA

Regression coverage and release confidence for cutovers

Product & Design

Journey prioritization, UX consistency, and acceptance of React as the product surface

  • Communication: migration status was framed as delivery continuity and cleanup progress—not “more React pages.”
  • Releases: cutovers used controlled rollout and explicit rollback ownership.
  • Ownership: every dual-runtime surface had a clear owner and a cleanup definition of done.

Migration

Migration phases

  1. Phase 1

    Component library

    Shared UI primitives so migrated screens did not invent React differently every time.

  2. Phase 2

    Shared routing

    Stable SPA boundaries and navigation contracts for dual-runtime coexistence.

  3. Phase 3

    Feature migration

    Journey-by-journey cutover of customer-facing product surfaces.

  4. Phase 4

    Legacy removal

    Retire PHP/jQuery once React journeys were stable—cleanup as a milestone, not a wish.

Engineering

Highlights

PHP / jQuery → ReactIncremental dual runtimeShared componentsControlled rolloutNo roadmap freezeEnterprise SaaS UILeadership + architecture

A typical surface migration

Discover legacy behavior → prototype React → controlled rollout → clean up PHP/jQuery once stable.

  1. Discovery — inventory PHP/jQuery behavior & risks
  2. Architecture — route, ownership, and contracts
  3. Prototype — prove React on a thin slice
  4. Migration — dual runtime by surfaceShared components where possible
  5. Rollout — controlled release + monitoring
  6. Cleanup — retire legacy once stable

Judgment

What didn't work

Big-bang migration

Problem

Would have blocked feature teams and frozen delivery on revenue products.

Decision

Abandoned after architecture review—blast radius was unacceptable.

Moved to

Incremental migration, shared components, dual runtime, explicit cleanup.

Why not rewrite everything at once?

Full rewrite: freezes delivery on $100M+ ARR products.

Thin wrap: looks modern, fixes nothing.

Incremental dual runtime: continuous releases while replacing surfaces. Temporary complexity was the price of uninterrupted value—and cleanup was a milestone, not a wish.

Judgment

Engineering decisions I was proud of

Shared component layer over a freeze rewrite

Instead of

Stopping product development to rewrite the app end-to-end.

Why

Revenue teams could not pause. Shared components let us migrate incrementally while converging on one UI platform.

Trade-off

Temporary dual-runtime complexity—and the discipline to finish cleanup.

Benefit

Weekly releases continued; React adoption compounded instead of fragmenting.

Temporary complexity is a tool. Permanent complexity is a leadership failure.

Key engineering decisions

Migration strategy

Problem

We needed a modern React platform without stopping feature delivery on live enterprise products.

Options

Option A — Full rewrite

Pros

  • Clean end-state architecture
  • No long-term dual stack

Cons

  • Freezes product development
  • High blast radius if wrong
  • Hard to show incremental value

Rejected because: Delivery risk was unacceptable for enterprise customers and revenue product roadmaps.

Option B — Wrap legacy and move on

Pros

  • Faster short-term cosmetics
  • Less migration work upfront

Cons

  • Doesn’t fix maintainability
  • Hiring and velocity stay stuck on jQuery

Rejected because: It deferred the real problem and would recreate inconsistency under a new skin.

Option C — Incremental dual-runtime migration· Chosen

Pros

  • Weekly releases continue
  • Lower blast radius per cutover
  • Learning compounds on real traffic

Cons

  • Temporary PHP/jQuery + React coexistence
  • Needs strict ownership and cleanup discipline

Chosen because: It protected customer value while still finishing the platform change.

Decision

Ship React journey by journey, keep delivery moving, retire legacy on evidence—not optimism.

Trade-offs

Temporary dual-runtime complexity for uninterrupted releases.

Result

Teams kept shipping Social, Insights, and Campaigns while the platform changed underneath.

Shared components during dual runtime

Problem

Without shared primitives, every migrated screen would invent React differently—and dual runtime would become permanent fragmentation.

Why

Shared components and standards so React adoption compounded instead of fragmenting.

Pros

  • Lower rewrite cost per surface
  • More coherent UI
  • Path into later UI/UX consistency

Cons

  • Upfront coordination
  • Friction vs one-off screens

Trade-offs

Slower first migrations; faster later ones.

Result

A reusable foundation—not one-off React pages.

Release and rollback as architecture

Problem

Migrating customer-facing UI without a rollback story is not modernization—it’s gambling.

Why

Every cutover needed rollout, monitoring, compatibility, and a path back.

Pros

  • Lower production risk
  • Stakeholder confidence
  • Cleanup after stability

Trade-offs

More process per cutover—worth it for enterprise trust.

Result

Dual runtime stayed intentional and finishable.

Challenges I had to solve

Challenge

Legacy PHP/jQuery couldn’t support velocity, consistency, or hiring as products multiplied.

Solution

React as target architecture; migrate surface by surface with shared components.

Result

A modern SPA foundation without a greenfield freeze.

Challenge

Dual runtime confuses ownership if contracts and cleanup rules are vague.

Solution

Explicit surface owners, shared boundaries, standards, and deliberate legacy retirement.

Result

React adoption compounded instead of fragmenting.

Challenge

Feature work, QA, and migration colliding across product teams.

Solution

Sequenced cutovers; release strategy part of engineering design.

Result

Modernization advanced alongside Social, Insights, and Campaigns.

Challenge

Architecture loses to feature pressure unless risk and communication stay visible.

Solution

Stakeholder communication, mentoring, and hiring—not just a slide deck.

Result

Modernization stayed funded, staffed, and finishable.

Delivery

Risk mitigation

RiskMitigation
Broken releasesFeature flags
RegressionShared components + standards
Downtime / freezeDual runtime
Slow rolloutIncremental migration
Forever dual-stackCleanup milestones + owners

Feature flags

New React surfaces could be enabled gradually and disabled quickly without a full rollback drama.

Canary / controlled releases

Migrated journeys shipped behind controlled release patterns so blast radius stayed small.

Rollback strategy

Each cutover needed a defined path back if a React surface failed acceptance or production health checks.

Monitoring

Watched production signals during dual runtime so cleanup decisions were evidence-based.

Performance budgets

Migrated UI had to meet interaction and load expectations—not merely “render in React.”

Dual runtime

Treated dual runtime as explicit, owned, and time-boxed—with cleanup as a milestone.

Organization

Engineering leadership

Roughly as much leadership as architecture. Migrations fail from communication and ownership—not frameworks.

40+

Engineers

Multi-team

Product teams

Weekly

Architecture reviews

Cross-functional

Planning

Incremental delivery mattered more than architectural purity.

Weekly architecture reviews

Standards, shared components, and dual-runtime boundaries stayed visible.

Migration governance

Done meant React live, monitored, and legacy retired—or not done.

Cross-team planning

Sequenced cutovers so feature and migration work did not thrash.

Hiring & mentoring

Grew owners for React surfaces; standards compounded through reviews.

Stakeholder communication

Progress = delivery continuity and cleanup—not “more React pages.”

Product alignment

React journeys treated as the product surface, not a side quest.

Outcomes

Outcomes

Business outcomes

Feature velocity

No multi-quarter rewrite freeze on $100M+ ARR product lines

Product continuity

Weekly releases continued while the platform changed underneath

Follow-on leverage

Unlocked later UI/UX consistency across Social, Insights, Campaigns

Engineering outcomes

Platform

PHP/jQuery surfaces migrated onto a React SPA foundation

Architecture

Shared component layer + dual runtime, then cleanup to React-only

Delivery system

Controlled rollout, monitoring, and rollback as first-class design

Team outcomes

Ownership

Independent product teams could own React surfaces on shared standards

Onboarding

One frontend stack and pattern language instead of one-off jQuery screens

Leadership cadence

Architecture reviews, migration governance, hiring, and mentoring

Transformation

What changed after migration

BeforeAfter
PHP / jQueryReact platform
Inconsistent UIShared components
Slow feature delivery on legacy UIFaster releases on a shared stack
Duplicate one-off screensReusable architecture
Hard-to-hire frontend stackModern React ownership model
Ambiguous dual-stack riskExplicit dual runtime → cleanup

Lessons learned

The hardest part wasn’t React—it was coordinating teams without slowing delivery.

  • Write down why you rejected rewrite and wrap—not only what you chose.
  • Dual runtime is fine; ambiguous ownership is not.
  • Shared components make incremental migrations compound.
  • Release and rollback are architecture decisions.
  • Tie modernization to customer journeys so it stays finishable.

Leadership

Leadership takeaways

  • Technology rarely causes migrations to fail. Communication does.
  • Keeping Product, QA, Engineering, and Design aligned was as important as the React code itself.
  • Temporary complexity is a tool. Permanent complexity is a leadership failure.

What I'd do differently

  • I’d publish cutover and cleanup checklists earlier for every legacy surface.
  • I’d invest even sooner in shared React primitives before more screens migrated.
  • I’d pair migration progress more tightly with delivery dashboards stakeholders already trusted.

If I were starting today

Same incremental strategy—stronger platform earlier:

  • Stronger module boundaries where team ownership matters
  • Design tokens alongside shared components
  • Automate migration checklists and regression coverage
  • Storybook earlier for shared UI
  • DORA-style signals on cutovers and cleanup

Biggest takeaway

Not a framework swap
a judgment call to protect delivery while finishing the platform change.

Key takeaways

  • Modernization succeeds because of execution, not frameworks.
  • We optimized for incremental delivery, alignment, compatibility, and adoption.
  • Temporary complexity is a tool. Permanent complexity is a leadership failure.

Final reflection

Looking back, the migration succeeded not because React was a better framework, but because we optimized for incremental delivery, organizational alignment, and long-term maintainability. The technical solution was only one part of the transformation—the execution strategy mattered just as much.


← All projects