Transforming Frontend Architecture: A Journey from Monolith to Micro Frontends at Udemy — Part 3 of 3

Hamza ERBAY
Udemy Tech Blog
Published in
7 min readJan 16, 2024

By: Chris Nienhuis, Hamza Erbay, Matthew Bise, Nathan Chapman, Seth Hodgson, Trey Briggs

Photo by Venti Views on Unsplash

Introduction

In the previous section, we walked through the Udemy Hackathon event, and the decisions and preparations made that laid the foundation for our move toward a more modular frontend architecture. In this section, we will focus on the implementation of our vision post-Hackathon, how we are planning to execute the migration, and the impact it has had so far.

If you missed Part 1 and Part 2 of this series, you can find them here:

Post-Hackathon: Implementing the Vision

Following the green light from the Shark Tank jury, we’ve taken decisive steps to translate our hackathon prototype into a scalable and robust frontend architecture. A dedicated frontend team has been formed at Udemy with the express purpose of driving this evolution.

A screenshot of a Slack message from former Udemy CEO, Gregg Coccari, expressing his appreciation.
“Where the journey began… A throwback to the spark that ignited our incredible voyage. Thanks to Gregg (our former CEO) for recognizing the hard work! #Milestone #Hackathon #OriginStory”

Our commitment to this new direction is reflected in a series of strategic actions:

  • Migration Guidance: We’ve laid out clear guidelines for transitioning (transitioning implies migration) from the monolithic application, ensuring a smooth and structured shift to the new architecture.
  • Collaboration with Design Systems: By partnering with the design system team, we’ve begun decoupling foundational components and design tokens, packaging them into shared npm packages. This initiative is critical for consistency and efficiency across all applications.
  • Education and Alignment: Recognizing the importance of knowledge sharing, we’ve instituted weekly office hours dedicated to educating teams about the new frontend practices.
  • Performance Monitoring: We’ve implemented a comprehensive monitoring dashboard to track our progress and maintain high performance (Datadog & Sentry).
The simplified version of the migration process
The simplified version of the migration process

Local Development and Release Process

The shift to our new architecture has dramatically simplified the local development setup. With a couple of commands — yarn install and yarn dev — developers can have their local environment up and running in under a minute.

Cold Start (blue and orange), Warm Start (only orange boxes)

Our release process mirrors this simplicity. After successful test completion and code review by the owners, GitHub Actions orchestrate the entire release cycle. Developers then perform a final verification post-release, ensuring everything runs smoothly. This streamlined process not only accelerates development but also enhances the reliability of our deployments.

Performance Wins in Production

Reflecting on the strategic shift to a micro frontend architecture, we can see compelling evidence of its success in our production environment. This transition has not only improved our development processes, but has also led to significant performance improvements in our live systems.

System Design — Detailed
System Design — Detailed

Leveraging web Real User Monitoring (RUM) metrics, we’ve quantified the impact of migrating to the new architecture. We have an example from the recent migration of our topic pages, which are used by all Udemy customers — Enterprise, Consumer Subscription, and Marketplace. This insight highlights the progress made in the migration:

What data are we analyzing?

First Contentful Paint (FCP):

At the 75th percentile, we’ve seen a notable ~35% enhancement, significantly reducing the time until users see the first piece of content.

For the 95th and 99th percentiles, there’s an improvement of approximately ~20%, sharpening the experience even in less optimal conditions.

Cumulative Layout Shift (CLS):

The 75th percentile shows an impressive ~400% improvement, greatly increasing visual stability.

At the 95th percentile, the enhancement is around ~300%, further solidifying a seamless visual experience.

Time to First Byte (TTFB):

Frontends are just crushing the monolith when it comes to TTFB, thanks to the edge cache and origin cache.

We’ve observed a ~320% improvement at the 75th percentile, tripling the speed compared to our monolith.

For the 95th percentile, the boost is about ~200%, doubling the responsiveness.

First Input Delay (FID):

In both the monolith and new versions of the page, we maintain an excellent First Input Delay (FID) of less than 0.1 seconds. The frontend update presents a slight edge over the monolith, although the enhancement is insignificant.

Largest Contentful Paint (LCP):

The monolith setup holds a marginal advantage in terms of the Largest Contentful Paint (LCP).

To enhance this metric in the new architecture, we need to strategize on initiating calls for subscription and shared data earlier, minimizing the wait time for the main component to load.

DOM Interactive:

At both the 75th and 95th percentiles, we recorded a ~200% improvement, indicating a much quicker interactive state.

Challenges: Navigating the Trade-offs

Transitioning to a new frontend architecture has its set of challenges and trade-offs. Our goal has been to streamline efforts and maintain a reusable component architecture. However, we’ve encountered several areas that required careful navigation.

Data Serialization

Challenge: The existing monolithic system used Django templates for data serialization implicitly. Transitioning to a microservices architecture required explicit serialization of data.

Solution: We established a new serialization process that translates Django’s context into a RESTful format suitable for communication.

Outcome: Although initially time-consuming, this process enabled us to decouple our data layer from the monolith, resulting in a more flexible and scalable architecture.

Component Versioning

Challenge: Managing the versioning of shared components across monolithic and micro frontend environments posed a significant challenge, especially when ensuring backward compatibility.

Solution: We adopted a rigorous version control strategy and semantic versioning to track changes and dependencies.

Outcome: This allowed smoother transitions and clearer team communication regarding updates and releases.

Parallel Code Maintenance

Challenge: Running A/A tests required maintaining parallel codebases during the transition, which doubled the development effort.

Solution: By applying the container/presentational component pattern, we reduced redundancy and decoupled UI components from business logic.

Outcome: This approach minimized double handling and prepared our codebase for future testing scenarios.

Webpack Compatibility

Challenge: Custom plugins and CSS preprocessor configurations within our monolith were incompatible with Next.js’s expected configurations.

Solution: We refactored our build process, replicating the essential functionalities of our custom configurations within the constraints of Next.js.

Outcome: Though we faced temporary limitations on upgrading Next.js, our solution ensured the continuity of our development processes.

Localization

Challenge: We depend on the translation process that exists with the Django monolith to extract and translate strings.

Solution: We developed a separate new localization pipeline compatible with our micro frontend while ensuring that it aligns with our current translation workflows.

Outcome: The new pipeline maintains the efficiency of our localization process and supports the distributed nature of micro frontends.

Shared Context Provider

Challenge: Another obstacle was ensuring that different frontend platforms could seamlessly integrate and share context.

Solution: We implemented a data provider pattern, which allowed us to manage shared state effectively across our ecosystem.

Outcome: This facilitated the reusability of components and maintained consistency across various platforms.

Conclusion

Decoupling the frontend engineering from the monolith structure could bring significant benefits to Udemy by improving the development and release process efficiency and enhancing the customer experience.

From the development perspective, we’ve managed to accelerate the initial build times for developers, reducing what used to be a 5–8 minute cold start down to less than a minute for frontend activities.

Adopting the new architecture has significantly boosted the speed of our Continuous Integration (CI) process. In the monolith system, the CI processes took around 35–40 minutes to complete, whereas the new setup only takes 3–5 minutes. This improvement has accelerated our development cycle and enables more frequent integrations, enhancing team agility.

We’ve observed dramatic enhancements in web vitals, with First Contentful Paint (FCP), Cumulative Layout Shift (CLS), Time to First Byte (TTFB), and DOM Interactive metrics showing improvements of 200% to 400%. These are not just numbers — they represent real, qualitative changes in how users interact with our platform.

The evolution of Udemy reflects the collaborative culture that thrives within our organization and showcases the potential of our dedicated teams. Despite the challenges of embracing new technologies and refining our methods, I am grateful to my team for their tremendous effort in turning our vision into reality.

--

--