Skip to content

Monorepos and workspace boundaries

One repository, many deployables: the declared dependency graph decides what each change rebuilds.

Predict first

Before you read: predict

ExamHall keeps apps/candidate, apps/proctor, apps/admin, apps/api and packages/contracts in one pnpm workspace. Each app lists @exam/contracts as a dependency (workspace:*), and the candidate, proctor and admin apps call api over HTTP. A merge request changes only files in apps/admin. CI runs pnpm --filter "...[origin/main]" run build and deploys each app it built.

Which packages does CI build?

Pick one

The 30-second version

A monorepo holds many apps and packages in one repository, yet each app still builds and deploys on its own. A workspace tool turns the declared dependencies into a graph. CI builds what a change touched and everything that depends on it, and restores unchanged work from a shared cache. Each project must declare every package it imports.

One repository, many deployables: the declared dependency graph decides what each change rebuilds.

How it works

Step 1 of 12

One repository, many deployables. Apps and shared packages live in one repository with one history. Each app still builds and deploys on its own.
In the kitchen
  • dozens of papers from one mill, each sold on its own → one repository, many deployables (step 1)

Analogy: A wallpaper mill's printing blocks

One mill prints dozens of wallpapers from a shared rack of carved blocks, and sells each paper on its own.

At the mill In the repository
dozens of papers from one mill, each sold on its own one repository, many deployables (step 1)
each paper's job card lists the blocks it is printed with declared dependencies (steps 4–5)
after a block is recut, the printers look up the job cards that list it affected projects from the graph (step 6)
one rack in the hall for every printer, or a tray per printer holding the blocks his card lists npm's root node_modules, or pnpm's per-project links (step 9)
a recut block and the new cards of the papers it serves, issued together one change, every caller (step 11)

Breaks down when: a reprint costs paper and ink, while a build whose inputs are unchanged costs almost nothing once its output is stored.

Contrasting case: a mill that sells its papers only as one bound sample book, and reprints the whole book for any change: one deployable.

In real terms: keep the declarations honest, since the graph and pnpm's links are both built from them.

Examples, run for real

4.1 ExamHall EXH-4760: forty minutes for every merge request

ExamHall's CI built and tested all five packages on every merge request, 8 minutes each by the team's own estimate (a stated cost, not a timing). MR !5060 switches CI to pnpm --filter "...[origin/main]".

4.1 before MR !5060: every merge request builds 5 packages · 5 x 8 = 40 minutes
4.1 MR !5060, a merge request to apps/admin: builds 1 (admin) · 8 minutes
4.1 MR !5060, a merge request to packages/contracts: builds 5 (contracts, admin, api, candidate, proctor) · 40 minutes

Most merge requests touch one app, and only that app builds (step 7). A change to contracts still builds every app; the cache is for that (step 8).

4.2 Media streaming: one commit across three packages

A video platform renames posterUrl to artworkUrl in packages/catalog-schema, which apps/player and apps/api read.

4.2 one commit changes 3 folders (apps/api, apps/player, packages/catalog-schema) and selects 3 packages (api, player, catalog-schema) · builds before it: 3 of 3 pass · after it: 3 of 3 pass · package versions changed: 0
4.2 the old player, not yet redeployed, renders the new API's card as <img alt="Night Train" src="undefined">

One commit changed all three, and every build passed before and after it (step 11). The two apps still deploy separately, so the old player meets the new API until it is replaced (step 12).

Try it

Answer first, then check. Nothing is saved.

Question 1 of 7

A video platform's pnpm workspace has the packages drm, player-core and captions and the apps web-player, tv-app, studio and ingest-worker. Their package.json files declare these workspace dependencies, and no others:

packages/player-core  dependencies     @vid/drm
apps/web-player       dependencies     @vid/player-core
apps/tv-app           devDependencies  @vid/player-core
apps/ingest-worker    dependencies     @vid/drm
apps/studio           dependencies     @vid/captions

A merge request changes only files in packages/drm. CI runs pnpm --filter "...[origin/main]" test.

Which apps does it test?

Pick one

Question 2 of 7

A chat app's pnpm workspace has the packages emoji and protocol and the apps web, desktop, mobile and push-service. web and desktop list @chat/emoji and @chat/protocol; mobile and push-service list only @chat/protocol. On each commit to main, CI runs nx affected -t build against the previous commit, with Nx's remote cache on (in this item the cache runs as a model). On commit A, CI built every package. Commit B changed only packages/emoji, and CI built emoji, web and desktop. Commit C is git revert of B, and CI picks the same three.

On commit C, which of the three run their build instead of restoring it from the cache?

Pick one

Question 3 of 7

A ticketing company's repository has apps/box-office and packages/admin-reports, named @tix/admin-reports. apps/box-office/index.js starts with import { refundTotals } from '@tix/admin-reports';, but neither box-office's package.json nor the root's lists any dependencies. The team sets the repository up once as an npm workspace and once as a pnpm workspace, each with default settings. Each time it installs, then runs node apps/box-office/index.js.

In which setup does the import load?

Pick one

Question 4 of 7

A payments ledger's pnpm workspace has the package money and the apps ledger-api, statements and payouts, each of which lists @pay/money. On every commit to main, CI runs pnpm --filter "...[HEAD~1]" run build; a build loads the package's entry with Node. Commit X changed only apps/statements, and its CI went red: the statements build failed. Nobody has fixed it. The next commit, Y, changes only apps/payouts.

Which packages does CI build for commit Y?

Pick one

Question 5 of 7

Write three to five sentences answering an interviewer who says ExamHall's candidate frontend and its API share a Submission type: monorepo or polyrepo?

Question 6 of 7

A video platform's pnpm workspace has the apps web, tv and studio and the package packages/catalog, named @vid/catalog. The root package.json lists "@vid/catalog": "workspace:*" for a seeding script. web and tv list it too; studio does not, yet apps/studio/index.js imports formatRuntime from it, and studio's build passes. A merge request renames formatRuntime to runtimeLabel in catalog and updates web, tv and the seeding script. CI runs pnpm install --offline, then pnpm --filter "...[origin/main]" run build; a build loads the package's entry with Node.

What happens?

Pick one

Question 7 of 7

A chat app's pnpm workspace, on pnpm 10.34.6 with default settings, has packages/protocol, named @chat/protocol, at version 3.1.0. apps/web lists it as "@chat/protocol": "^3.0.0". CI runs pnpm install --offline on a runner with an empty package store and no registry access.

What happens?

Pick one

Go deeper

Pro tips
  1. Make CI affected-only before the repository grows. When: an interviewer asks how you would keep a monorepo's pipeline fast (the row's typical question 2; steps 7–8). Verify:
    tip 20 packages x 8 minutes = 160 minutes for every merge request when CI builds everything; affected-only, a merge request to one app that nothing depends on builds 1 package, 8 minutes
    Status: RAN.
Source

quote-check.mjs finds every passage below verbatim in its saved text. Valid as of 2026-10-17.

  • Nx, monorepo.tools (the row's first source): https://monorepo.tools/ (monorepo-tools.txt). Quoted: "A monorepo is a single repository containing multiple distinct projects, with well-defined relationships."; "A good monorepo is the opposite of monolithic!"; "A breaking change in a shared library and the fix in every consumer land in the same PR."; "Determine which projects are affected by a given change and only run the relevant tasks. Skip everything else." Supports steps 1, 2, 7 and 11.
  • Nx docs, Run Only Tasks Affected by a PR (the row's second source): https://nx.dev/docs/features/ci-features/affected (nx-affected.txt). Quoted: "Use the project graph to determine which projects the files belong to."; "Determine which projects depend on the projects you modified." Supports step 6.
  • pnpm docs, Workspace (the row's third source): https://pnpm.io/workspaces (pnpm-workspaces.txt). The page describes the current pnpm (12.x on the day read); what is used here was also executed on 10.34.6. Quoted: "A workspace must have a pnpm-workspace.yaml file in its root."; "When this protocol is used, pnpm will refuse to resolve to anything other than a local workspace package." Supports steps 3 and 4.

Checked against the tools and docs as of 2026-10-17.