Can you walk me through the Fareportal migration project? What was the product and what were you trying to achieve?

Fareportal operates CheapOair and OneTravel — two B2C travel booking platforms under Fareportal, serving 100K+ daily active users. I led the frontend migration of the entire customer profiles section from legacy ASP.NET Razor Pages to a modern React architecture.

The profiles section is massive — it covers everything a logged-in user interacts with:

  • My Trips (upcoming, completed, cancelled, recent bookings)
  • Account Settings and My Information
  • Travel Preferences and Co-Traveller Management
  • Payment Methods / Wallet
  • Booking History
  • Loyalty / Rewards and Deals
  • Signup / Signin / Reset Password / OTP Flow
  • Fare Alert Notification Preferences
  • Referral Program

I owned 90% of the frontend for all of these pages — architecture, component design, API integration, testing, and deployment. The remaining 10% was the .NET backend layer, which I also handled when needed.

What was wrong with the existing Razor Pages setup? Why did the migration happen?

The legacy Razor Pages architecture had become a serious bottleneck across multiple dimensions:

Performance was unacceptable. Every user interaction required a full server round-trip. Pages took 4.5 seconds to load — for a travel platform where users check their bookings multiple times before a trip, that's painful.

No component reuse. Razor Pages were monolithic server-rendered templates. UI couldn't be shared across teams or products. CheapOair and OneTravel are separate brands but share functionality — yet every UI element was duplicated and maintained separately.

Frontend locked to .NET deployment cycles. Any frontend change, no matter how small, required going through the entire .NET build and deployment pipeline. This slowed feature delivery significantly.

No modern frontend capabilities. No lazy loading, no async rendering, no independent section updates. Everything loaded synchronously in one massive server response.

Hiring challenges. Finding developers skilled in Razor was increasingly difficult. React had a significantly larger talent pool and ecosystem, making it easier to scale the team.

Multiple teams, no shared architecture. Several teams needed to work on shared UI surfaces, but Razor had no component-driven architecture to support parallel development.

The decision to migrate was made at the organizational level. I was the only engineer on my team familiar with React, so I effectively became the frontend lead for this project from day one.

What was the team structure? How many people worked on this?

The core team was lean — 2–3 engineers total. I handled the entire frontend: architecture decisions, component design, API integration, state management, testing, and deployment. The other engineers supported backend API changes and infrastructure work.

Beyond the migration itself, I also mentored 2–3 junior engineers during this period:

  • Taught React fundamentals — component patterns, hooks, state management, lifecycle methods
  • Established code review practices — writing clean, maintainable code with proper PR workflows
  • Guided debugging in a hybrid codebase — navigating the .NET + React stack and tracing API flows
  • Ran knowledge transfer sessions for the broader team so backend-focused .NET developers could contribute to frontend when needed

KT sessions were critical. In a small team doing a major migration, you can't afford to be a single point of failure. Getting .NET developers comfortable with React meant the project wouldn't stall if I was unavailable.

What was the technical architecture? What stack did you build and why?

The frontend stack was:

  • React with a custom Webpack + Rollup build configuration
  • Context API with Reducer for state management in newer modules
  • Redux was used by other teams and in Signup/Signin components.
  • Custom npm packages published for reusable UI components
  • Module Federation (Webpack 5) for specific/other teams components loaded as remote bundles at runtime

The Component Strategy

The architecture was designed around modularity and independent deployability:

  1. Custom npm packages — Core UI components were published as versioned packages. This meant other teams across Fareportal could consume the same components, and any individual component could be rolled back independently without redeploying the entire application.

  2. Module Federation — Used selectively for components that needed to be loaded as remote bundles at runtime across different apps. This was important for components shared between CheapOair and OneTravel.

  3. Versioning for safety — Every package was versioned, giving us a clear rollback path. If a new version of a component caused issues, we could pin back to the previous version without touching anything else.

Backend Communication

The React frontend consumed the same REST APIs that Razor Pages used. No major backend rewrite was needed — the APIs were already in place. I occasionally created new endpoints or modified existing ones when the React frontend required different data shapes, but the API layer stayed largely intact.

Session Management

Cookie-based sessions shared between Razor and React. The .NET backend managed the session; React read session state via cookies. This ensured users experienced zero authentication disruption during the switchover.

How did you actually execute the migration over 9 months? Was it a gradual page-by-page rollout?

No — it was a big-bang switchover.

We spent the full 9 months building the complete React application in parallel with the existing Razor Pages. When it was ready, we switched all profile pages at once while keeping URLs consistent. The old Razor pages were retired on cutover day.

A big-bang migration is risky — if something breaks, everything breaks. But for our situation, it was the right call. The profiles section was deeply interconnected (shared sessions, cross-page navigation, consistent state). A gradual rollout would have meant maintaining two parallel systems with complex session sharing for months, which would have been more error-prone than a clean cutover.

Why This Worked

Three things made the big-bang approach viable:

  1. Versioned packages — Every component was independently rollbackable. If the Booking History page had issues post-launch, we could roll back just that component without touching My Trips or Account Settings.

  2. Thorough testing — We increased test coverage from 20% to 80% during the migration. By cutover day, the application had been tested extensively.

  3. URL consistency — Users hit the same URLs before and after migration. No redirects, no broken bookmarks, no SEO disruption.

What was the most technically challenging part of the migration?

Two things stood out:

1. Making Components Truly Modular and Versioned

Publishing custom npm packages sounds straightforward, but getting it right at scale is hard. Each component needed to be:

  • Self-contained with its own styles and dependencies
  • Versioned independently without breaking consumers
  • Compatible with both the main profiles app and other teams' applications
  • Loadable at runtime via Module Federation where needed

Setting up the build pipeline for this — Webpack for the main app, Rollup for the npm packages, Module Federation for remote bundles — required careful configuration to avoid dependency conflicts and version mismatches.

2. Faithfully Migrating Business Logic

This was the invisible, painstaking work. Razor pages had years of embedded business logic — edge cases, validation rules, conditional rendering flows, special handling for specific booking types, loyalty tiers, and payment states.

None of this was documented. I had to trace through every code path in the Razor templates, understand the intent, and replicate it faithfully in React while simultaneously improving the architecture.

The hardest bugs to find were the ones that only appeared for specific user states — like a loyalty member with an expired wallet and a cancelled international flight with partial refund. These edge cases were buried deep in Razor template conditionals and only surfaced through thorough QA testing.

What were the measurable results after the migration went live?

We measured performance using multiple tools with before-and-after comparisons:

MetricBefore (Razor)After (React)Improvement
Page Load Time4.5s1.5s70% faster

The improvement was validated across:

  • Lighthouse performance scores
  • Real User Monitoring (RUM) via Application Insights analytics
  • GTmetrix and PageSpeed Insights
  • Before-and-after screenshots documenting the visual and performance differences

Where the Speed Came From

The 70% improvement wasn't just from switching to React — it came from the architectural changes that React enabled:

  • Lazy loading — sections loaded on demand instead of everything at once
  • Async rendering — independent section updates without full page reloads
  • Client-side navigation — no full round-trips for page transitions
  • Optimized bundling — code splitting via Webpack reduced initial bundle size

User and Quality Metrics

  • 100K+ daily active users served by the new profiles section (tracked via Google Analytics and internal analytics dashboards)
  • 150+ bugs identified by the dedicated QA team and resolved before and after launch
  • 1,300 code smells eliminated, tracked via SonarQube static analysis across both React and .NET codebases
  • Test coverage increased from 20% to 80% using Jest + React Testing Library (frontend) and xUnit (.NET backend)
Tell me about the Note API handling 2M+ daily hits.

The Note API is the anonymous session management layer for CheapOair and OneTravel. It handles:

  • Temporary user data storage for non-logged-in users — preferences, search history, selected options
  • Session activity tracking across pages
  • Cart and booking data before login — so users can build their itinerary and only sign in at checkout

Processing 2M+ hits per day with high availability requirements. I maintained this API for a very short time at Fareportal.

The Note API is critical infrastructure — if it goes down, anonymous users can't maintain their booking state across pages. Every flight search, hotel selection, and preference change for non-authenticated users flows through this API.

You also built 3 internal tools at Fareportal. What were those?

All three tools were built in a Turborepo monorepo using React, Next.js, and ASP.NET:

1. Account Management Tool (Legal Team)

Built for the legal team to handle user requests:

  • Account deletion and data access requests
  • Email opt-out management
  • Booking history retrieval for legal inquiries
  • Data export requests — generating complete data packages for users
  • Legal hold functionality — the ability to freeze user accounts during legal proceedings (applied globally, not just EU users)
  • SSO login and user authorization management for the internal tool itself

2. Gift Card & Operations Tools

A suite of operational tools:

  • Gift card generation for specific users with approval workflows
  • Loyalty points management — adjusting, crediting, and auditing loyalty balances
  • Reset password management — administrative password resets with audit trails

3. Internal Rules Dashboard

Handled regulatory compliance:

  • Rewards Program Rules — Rules for different rewards categories and campaigns
  • Other business rules — Rules for different business categories
What about the AI travel planner proof-of-concept?

In mid-2024, I built a proof-of-concept AI travel planner to assess whether LLMs could generate reliable travel itineraries.

The idea: Instead of users manually searching for flights and hotels, let them describe what they want — and have an AI recommend tourist places, hotels, and generate complete itinerary plans based on their booking history and preferences.

LLMs used: Google Gemini and Meta Llama

The outcome: The responses were hallucinated and inaccurate. The AI would recommend hotels that didn't exist, suggest flight routes that weren't available, and generate pricing that had no basis in reality.

This was an honest feasibility study, and the conclusion was clear: in mid-2024, LLMs were not reliable enough for travel planning where factual accuracy is non-negotiable. A user can't book a hotel that doesn't exist. The PoC remained internal and was never deployed to production.

It was still valuable — it helped the organization understand the current limitations of LLM integration and set realistic expectations for future AI features.

How did you approach testing and code quality across the migration?

Testing Strategy

Testing ran in parallel with feature development across both layers:

  • Frontend: Jest + React Testing Library for unit tests on UI components
  • Backend: xUnit for .NET API integration tests
  • QA Team: A dedicated QA team tested the application end-to-end and logged bugs

What Was Hardest to Test

Two areas were particularly challenging:

  1. Complex async API flows — Profile pages had session-dependent logic with multiple chained API calls. Testing these required mocking entire session states and API response sequences.

  2. UI components with deeply embedded business logic — The conditional logic carried over from Razor was complex. A single component might render differently based on loyalty tier, booking type, payment state, and user region. Setting up test fixtures for all these combinations was time-consuming.

Code Quality

  • SonarQube was used for static code analysis across both React and .NET codebases
  • Eliminated 1,300 code smells — redundant code, overly complex functions, and anti-patterns that had accumulated over years in the Razor codebase
  • Resolved 150+ bugs identified by the QA team
  • Increased test coverage from 20% to 80%

The jump from 20% to 80% coverage wasn't just about writing more tests — it was about making code testable in the first place. The modular React architecture with versioned components made unit testing dramatically easier compared to the monolithic Razor templates where everything was entangled.