7

Changing Lanes: How Lyft is Migrating 100+ Frontend Microservices to Next.js

 3 years ago
source link: https://eng.lyft.com/changing-lanes-how-lyft-is-migrating-100-frontend-microservices-to-next-js-42199aaebd5f
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Image for post
Image for post
Photo by Joey Kyber on Unsplash

By Josh Callender and Andrew Hao

Introduction

In 2019, Lyft’s frontend architecture needed a reckoning. We were growing quickly as a company, and new teams were creating new software systems daily. At that point in time, we were generating new frontend services from a service generator template — complete with a copy of our bespoke, zero-config frontend build platform. Having such an easy means of service creation led to an explosion of new services with heterogeneous code built upon our React-based frontend architecture.

At the same time, we were running into headwinds trying to maintain our own frontend platform — an internal set of Webpack configurations, ESLint libraries and framework code — and finding ourselves bogged down troubleshooting cryptic build errors and generally finding our productivity sapped by such support requests. Because codebases began to diverge (as they do in microservice architectures), our developers found the task of upgrading to new versions of our frontend platform to be time-intensive and frustrating.

With over 100 frontend services and nearly as many frontend engineers, it was clear something needed to be done in order to ensure that our platform was maintainable for Lyft’s growth.

What problems did we have?

Image for post
Image for post
Photo by ray rui on Unsplash

We sat down and named some of the core issues we were facing:

  • Drifting infrastructure: New platform releases did not see uniform uptake, leaving us with a long tail of services that were left on older platform versions. With time, our frontend infrastructure began to drift, leading to maintainability and code complexity issues.
  • Keeping the entire service fleet up to date is hard: The responsibility of upgrading each service fell upon our product engineering teams, who were often busy and overbooked. This led to services falling behind on security and performance updates.
  • Proliferation (and divergence) of infrastructure code: Each service implemented frontend infrastructure (like Redux, or server-side rendering) in its own special way according to its own needs and team preferences, leading to heterogeneous implementation of common app patterns.
  • Performance bottlenecks: As new technologies like dynamic imports and other bundle size optimizations became available, frontend services that had not been upgraded to our latest platform began to lose out on performance wins offered by newer platform updates.
  • Common tasks are hard to apply at scale: Tasks that would normally be simple were difficult to apply at scale. For example, if we wanted to introduce styled-components to our service bundles, we would need to manually go into each service and add it in its own special way for each service’s implementation approach.
  • Lack of standardization: Sharing code is very difficult due to our heterogeneous codebases. Our engineers must often reinvent the wheel when implementing patterns and modules instead of leveraging shared code and libraries.

Enter Next.js

Image for post
Image for post
Source: https://github.com/vercel/next.js

We made the decision to turn to the open-source community to find a batteries-included framework that would solve these headaches for us. After evaluating different platforms, we landed on Next.js! We liked:

  • Its batteries-included, opinionated philosophy would help unify the divergent architectures of our platform.
  • Its executable wrapper that allowed us to move all central application concerns behind a module interface and remove our need to maintain our own build system architecture.
  • Its strong open-source ecosystem, friendly community, and solid documentation really sold us on the future growth and trajectory of the platform.

We could have just pulled Next.js off the shelf and asked everyone to use it as-is, but we still needed to solve a couple more problems.

Plus a little bit of Lyft special sauce…

Image for post
Image for post
Photo by CHUTTERSNAP on Unsplash

Two problems remained unsolved with Next.js out of the box.

First, we needed to automate platform migrations for the future.

We needed the ability to write easy-to-run and bulletproof service upgrades, and to be able to apply these at scale. To solve this, we designed a migration service with jscodeshift that allows us to ship and run migrations that automatically update service code when upgrades are run.

This means that any future breaking changes in our platform will come with automatic codemods that upgrade code in the host app. This also means that we can open pull requests to upgrade services across the fleet without product engineering intervention.

We needed a way to code share.

We wanted to build an extensible application architecture that would allow developers to write plugins to introduce different state managers and packages with as little configuration or glue code as possible. We designed a plugin service around Webpack Tapable that would allow any of our developers to inject shared Lyft packages into server middleware and the client React app to accomplish different tasks in our ecosystem — from GraphQL clients, Mirage mocking support, UI component libraries to shared libraries around metrics and logging.

Developer communication is key


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK