toss4 min read

Curated summary

The Monorepo Hope Edition: One Year to Bring a Despairing Repo Back to Hope

Read original(opens in new tab)

Toss argues that a monorepo alone does not guarantee a consistent or efficient frontend development experience. The real problem was dependency-version fragmentation across services, which made installations slow, platform changes risky, and upgrades difficult. Toss addressed this by introducing shared dependency “catalogs,” standardizing core libraries while preserving controlled, gradual upgrades.

Toss’s Frontend Development Environment

  • More than 100 frontend engineers maintain products inside and outside the Toss app.
  • Despite the large number of products, services use nearly identical versions of React 19, Next.js 15, TypeScript, bundlers, and linters.
  • A shared monorepo makes it easier to:
    • Maintain a consistent development environment.
    • Share code across services.
    • Propagate platform-wide changes.
    • Give users access to features such as React Concurrent Mode and Server Components.

Problems with an Unmanaged Monorepo

  • Services used widely different dependency versions, including outdated React and supporting libraries.
  • This created fragmented developer experiences:
    • Some services had fast development servers and modern APIs.
    • Older services were slower and harder to develop.
  • Dependency installation could take more than a minute even with caching.
  • Platform teams struggled to test shared libraries across many React and library versions.
  • Service developers avoided upgrades because compatibility risks and migration costs were high.
  • Older services consequently became locked into outdated dependencies.

Why Toss Rejected a Polyrepo Strategy

  • Splitting the monorepo into separate repositories could reduce the size of each individual project and improve installation times.
  • However, polyrepos would not solve the main issues:
    • Development environments would remain fragmented.
    • Shared-code development and updates would become more expensive.
    • Differences between services could become even more pronounced.
  • Toss concluded that improving dependency management within the monorepo was preferable to abandoning it.

Simplifying the Dependency Tree

  • The central issue was that services selected different versions of the same core libraries.
  • Toss identified roughly 10–20 commonly used libraries, including:
    • React
    • Component libraries such as TDS
    • Jotai
    • TypeScript
    • ESLint
  • Standardizing these dependencies could:
    • Reduce installation time.
    • Provide a consistent developer experience.
    • Make platform-library testing more predictable.
    • Enable automated migration scripts and compatibility layers.
    • Lower the cost of adopting breaking changes.
  • In practice, developers usually chose libraries rather than requiring specific versions, making centralized versions practical.

Dependency Catalogs

  • Toss defined recommended versions as a shared Catalog using pnpm or Yarn workspace configuration.
  • Services reference catalog-managed dependencies with the catalog: protocol instead of specifying independent versions.
  • Named catalogs can support different release channels, such as:
    • stable
    • beta
  • Toss initially included essential dependencies such as React, Next.js, TypeScript, TDS, and the Toss App SDK.
  • Catalog packages had to be tested in representative service environments before release.
  • New services automatically referenced the latest catalog.
  • CI detected cases where developers accidentally bypassed catalog versions.
  • Existing services were migrated collaboratively with their code owners.
  • Catalog changes were released as new versions and rolled out gradually rather than modifying a shared version in place.
  • Upgrade scripts and AI Skills reduced the effort required to migrate services.

Results After Full Adoption

  • Dependency duplication fell substantially:
    • .pnp.cjs shrank from 96 MB to 15 MB, an approximately 84% reduction.
    • Development-server startup improved from 26.7 to 20.3 seconds, about 23% faster.
    • Full dependency installation decreased from 528.4 to 249.9 seconds, about 52% faster.
  • Developers gained greater confidence that catalog packages had already been tested in real services.
  • Centralized version control reduced incompatible transitive dependencies, such as one package requiring version 1 while a service used version 2.
  • Better dependency visibility made large architectural improvements safer, including work involving RSC, TypeScript 7, Rspack, and end-to-end testing.
  • Services could adopt improved platform packages more consistently and with less upgrade friction.

The practical recommendation is to retain the monorepo, but enforce a curated set of shared dependency versions through catalogs, CI checks, staged releases, and automated migration tooling. This combines the sharing benefits of a monorepo with a more predictable and maintainable development environment.

Continue with another curated summary.