When we first outlined our shift to a monorepo architecture at FlightAware, we had a few straightforward but ambitious goals: break down communication silos, eliminate deployment bottlenecks, and enable code sharing across crews. At the time we had just successfully migrated our beta surface visualization application and were still laying the groundwork for Nx tooling.
What started as one non-production application has grown to nine independent Next.js applications managed by two independent crews and about a dozen shared libraries spanning authentication, logging, internationalization, and more. The monorepo now powers the FlightAware homepage, beta tracking experiences, self-service e-commerce checkout infrastructure, internal administration portals, and others. Getting here was not without its growing pains but the investments in tooling and infrastructure have begun to pay dividends.
Scaffolding Applications & Core Libraries
When we initially adopted Nx, one of our long-term goals was to create custom generators to eliminate the engineering toil of creating new projects. About a year ago, we implemented a generator that creates a fully integrated Next.js application pre-configured with Tailwind, Vitest, and standardized targets for formatting, linting, and containerization with a single CLI command. This drastically reduced the friction involved with spinning up a new application, and also ensures new applications adhere to our architectural standards from day one, regardless of who starts them.
Eliminating the bottlenecks of cross-team code sharing was one of our primary goals. Today the core authentication library provides server-side session resolution, automated token management, and robust state synchronization using the browser’s BroadcastChannel API. By centralizing this logic into a shared monorepo library, any team or engineer can implement secure, unified authentication without any custom code.
We’ve also tested the efficacy of our strict single-version policy across the workspace multiple times. We’ve implemented major version upgrades of React, Next.js, Nx, and Vite. While this did require some coordination it was largely asynchronous and it did have the intended goal of guaranteeing seldom-used applications and libraries do not fall behind.
Outside Applications - Routing, Observability, Monitoring
Beyond our initial roadmap, this architecture enabled several major technical enhancements. We migrated away from Ingress NGINX and adopted Envoy Gateway. This allows us to define clean, environment-specific HTTPRoutes, enforce rate-limiting where desired, and route static asset requests directly to S3.
As new and larger applications began appearing in the monorepo, deployment frequency started increasing. As deployment frequency increased, so did our need for rigorous observability. We moved past basic logging and integrated the OpenTelemetry Node SDK and Prometheus exporters directly into libraries and applications. We deployed comprehensive Prometheus recording rules and Service Level Objective alerts across beta and production environments, tracking critical metrics like homepage latency, CPU usage, and event loop. These alerts are routed directly to the relevant crew’s Slack channel with embedded runbooks to make incident response quick and easy.
To proactively protect against UX regressions, we run Playwright tests every 15 minutes and check web vitals using Lighthouse CI with every pull request.
Finally, the developer experience received a massive quality of life upgrade through our adoption of inferred tasks. This has let us eliminate or avoid hundreds of lines of boilerplate target configurations from our project.json files. This as well as the workspace-level Vitest configuration makes the codebase cleaner, smaller, and easier to maintain.
Next Steps
Two years after initial implementation we’ve grown the monorepo from this:

to this:

(Flock was our internal name for the app that is now live).
We have both user-facing and internal developer experience related changes we’re focusing on today. On the user facing side, we will continue to migrate legacy applications into the monorepo while also adding features to the applications already there. On the internal side, as the number of applications and libraries grow, the need for running multiple applications locally at the same time increases exactly as the ease of doing so decreases. In the short term we’re investigating Tilt, nix, and other ways to run multiple applications, each with their own SSL local-ssl-proxy, within a local development environment. Additionally, streamlining our CI/CD pipelines and making build, lint, and test steps as performant as possible is a constant goal.
Closing Thoughts
The shift to a monorepo has fundamentally changed how the web team builds and ships software. It’s not perfect and it’s not right for every organization or for any project, but if you’re facing the same challenges as FlightAware, it’s worth looking into. As we tackle our next set of challenges - local container orchestration, streamlining CI/CD pipelines, and increasing velocity of new application development - we’ll be continually refining this ecosystem to keep the engineering toil to a minimum.
Look for the next monorepo update in 2028!
