Skip to main content

Migration strategy

postman-app is in the middle of a long-running architectural migration: moving code from the legacy src/renderer/ monolith into structured Nx packages.

This page explains the strategy, the current state, and exactly how to migrate a package yourself.


The problem with the monolith

src/renderer/ grew organically over many years. It has:

  • ~15,700 files, mostly JavaScript (not TypeScript)
  • 76 top-level directories with no enforced boundaries
  • Thousands of circular imports (A imports B, B imports C, C imports A)
  • No ability to test features in isolation
  • A single massive webpack bundle — slow builds, no tree-shaking

You cannot fix a system like this with a big-bang rewrite. The risk of regressions is too high.


The strategy: strangler fig

The strangler fig pattern comes from botany. A strangler fig grows around a tree over many years — eventually, the fig is self-supporting and the original tree rots away.

Applied to postman-app:

  1. Every new feature goes into an Nx package — never into src/renderer/
  2. Existing monolith code is extracted feature by feature when it needs to be touched
  3. Over time, src/renderer/ shrinks and the Nx packages grow

Migration states

Every Nx package has a migration tag:

TagMeaning
monolith-decoupledNo @postman-app-monolith/* or @@renderer/* imports. Fully independent.
monolith-coupledStill imports from the monolith via bridge aliases. Migration incomplete.
(no tag)New package — should be decoupled from the start

The migration process step by step

Step 1: Generate the package

Use the Nx generator — never create packages manually.

# Create a new ui-feature
npx nx generate @postman/app-generator:library --type=ui-feature --name=my-feature

# Create a new data package
npx nx generate @postman/app-generator:library --type=data --name=my-data

The generator creates the correct directory, package.json, project.json, tsconfig.json, and tags. It also updates tsconfig.json at the repo root.

Step 2: Move the code

Copy the relevant code from src/renderer/ into the new package. At this stage, use bridge imports for anything that still refers to monolith code:

// Temporary bridge — acceptable during migration phase
import { NavigationService } from '@postman-app-monolith/renderer/router/NavigationService';

Tag the package as monolith-coupled in project.json:

{
"tags": ["type:ui-feature", "scope:collaboration", "monolith-coupled"]
}

Step 3: Wire up the new package

Update the entry point in src/renderer/onboarding/src/features/ (or wherever the monolith entry is) to import from the new Nx package instead of the old path.

Step 4: Full decouple

This is a separate PR. Remove all @postman-app-monolith/* and @@renderer/* imports. For each one:

  • If it is a service (like NavigationService), inject it via dependency injection or pass it as a prop
  • If it is a constant, move the constant into a libs/ package
  • If it is a store, move the store into a data/ package

When zero bridge imports remain, change the tag to monolith-decoupled:

{
"tags": ["type:ui-feature", "scope:collaboration", "monolith-decoupled"]
}

Step 5: Update the decouple tracker

Run the refresh script and open a PR with the tracking update:

node .context/scripts/refresh-decouple-migration.js

Common blockers during decoupling

These are the patterns that make full decoupling hard:

BlockerWhy it's hardSolution
NavigationServiceGlobal singleton, tightly coupled to the routerInject via props or React context
Feature flagsInjected at boot time, not importable as a moduleThread through from the entry point
Global singletonsAppState, GlobalStore, etc.Wrap in a context provider at the app level
Transitive monolith depsPackage X imports from monolith, and Package Y imports Package XFix Package X first

Current migration progress

Migration tracking is in Jira under ticket CBR-518. You can also check the state of any package by looking at its project.json tags.

# Find all still-coupled packages
grep -r "monolith-coupled" --include="project.json" -l .

# Find all fully decoupled packages
grep -r "monolith-decoupled" --include="project.json" -l .

As of version 12.8.3: approximately 25% of the codebase (by file count) has been migrated to Nx packages.


PR template for decouple PRs

All decoupling PRs should follow the template in .context/decouple-pr-description-template.md. The title format is:

[CBR-518] decouple <package-name> from monolith

And the checklist includes:

  • yarn validate-modules passes
  • yarn knip shows no new unused exports
  • nx lint and nx test pass on touched packages
  • Root tsc -b passes
  • Decouple tracker refreshed