Skip to content

perf(docker): install dependencies before copying source - #884

Merged
vilenarios merged 1 commit into
developfrom
perf/dockerfile-layer-caching
Sep 11, 2026
Merged

perf(docker): install dependencies before copying source#884
vilenarios merged 1 commit into
developfrom
perf/dockerfile-layer-caching

Conversation

@vilenarios

Copy link
Copy Markdown
Contributor

A one-line source edit currently costs a full dependency reinstall — twice.

COPY . . sits above yarn install, so any source change invalidates that layer and everything below it. The build then re-resolves the whole tree for the compile, and again for the production prune that runs after the source copy.

Measured

Same machine, both with warm caches, identical one-line source edit:

source-only rebuild
current 3m37s
this PR 14.5s

Most of the saving comes from moving the production install into its own stage. It depends only on the manifests, so editing source no longer re-resolves it — previously it ran as rm -rf node_modules && yarn install --production below the source copy and was rebuilt every time.

Output is unchanged

Built from identical source, both Dockerfiles produce:

  • compiled dist: 721e6960bb4659a7, 469 files — byte-identical
  • runtime node_modules: 04d87018b84258a5, 1010 packages — identical

No new requirements

Deliberately avoids BuildKit-only features. A yarn cache mount would also speed up genuine dependency changes, but the Dockerfile currently builds under the legacy builder and --mount=type=cache would make BuildKit a hard requirement for anyone building locally. Verified this still builds with DOCKER_BUILDKIT=0. CI is unaffected either way — it uses docker/setup-buildx-action.

One trap worth knowing

.yarnrc has to be copied alongside the manifests. It carries ignore-engines true, which some transitive dependencies need to install at all (@permaweb/aoconnect declares "engines": {"yarn": "please-use-npm"}). It was previously swept in by COPY . .; copying the manifests without it fails the install outright. That cost me one build to discover.

🤖 Generated with Claude Code

https://claude.ai/code/session_015R5EBQvBycT5dbNq2YRAgJ

`COPY . .` sat above `yarn install`, so any source edit invalidated that
layer and everything below it. Every build therefore re-resolved and
rebuilt the entire dependency tree from scratch -- twice, once for the
compile and again for the production prune below the source copy.

Copying only the manifests first confines dependency work to layers that
change when package.json or yarn.lock change. The production tree also
moves into its own stage rather than running as `rm -rf node_modules &&
yarn install --production` after the build: it depends solely on the
manifests, so editing source no longer re-resolves it. That split is where
most of the saving comes from.

Measured on the same machine, both with warm caches, for a one-line source
edit: 3m37s before, 14.5s after.

`.yarnrc` is copied alongside the manifests. It carries `ignore-engines
true`, which some transitive dependencies require to install at all; it was
previously swept in by `COPY . .`, and copying the manifests without it
fails the install outright.

Deliberately uses no BuildKit-only features. A yarn cache mount would speed
up dependency changes too, but the current Dockerfile builds under the
legacy builder and adding `--mount=type=cache` would make BuildKit a hard
requirement for anyone building locally. Verified this still builds with
DOCKER_BUILDKIT=0. CI is unaffected either way -- it uses
docker/setup-buildx-action.

Output is unchanged: built from identical source, the original and
restructured Dockerfiles produce a byte-identical compiled dist
(721e6960bb4659a7, 469 files) and an identical runtime dependency tree
(04d87018b84258a5, 1010 packages).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015R5EBQvBycT5dbNq2YRAgJ
@coderabbitai

coderabbitai Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Team

Run ID: 6f7377af-70ef-41c5-ab97-d2de28193ad2

📥 Commits

Reviewing files that changed from the base of the PR and between 2bedbb1 and b429463.

📒 Files selected for processing (1)
  • Dockerfile

Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review.


📝 Walkthrough

Walkthrough

The Dockerfile now separates cached dependency installation, production dependency installation, application compilation, and runtime packaging into distinct stages.

Changes

Docker build pipeline

Layer / File(s) Summary
Dependency and build stages
Dockerfile
The Dockerfile adds base, deps, proddeps, and builder stages. Manifest files and .yarnrc support dependency caching. Source is copied only into the builder stage.
Runtime dependency handoff
Dockerfile
The runtime image copies production-only node_modules from proddeps.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to b4294

This change reorganizes Docker dependency and build stages to improve caching while retaining the production runtime dependency handoff. No concrete merge-blocking risk remains.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description check ✅ Passed The description clearly explains the Dockerfile caching changes, measured rebuild-time improvement, unchanged outputs, and legacy-builder compatibility.
Title check ✅ Passed The title clearly and concisely describes the main change: installing dependencies before copying source to improve Docker layer caching.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf/dockerfile-layer-caching

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Sep 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 80.69%. Comparing base (428656b) to head (b429463).
⚠️ Report is 7 commits behind head on develop.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop     #884      +/-   ##
===========================================
- Coverage    80.70%   80.69%   -0.02%     
===========================================
  Files          143      143              
  Lines        57785    57785              
  Branches      4496     4495       -1     
===========================================
- Hits         46634    46627       -7     
- Misses       11096    11103       +7     
  Partials        55       55              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@vilenarios
vilenarios merged commit 7e1e3d1 into develop Sep 11, 2026
4 checks passed
@vilenarios
vilenarios deleted the perf/dockerfile-layer-caching branch September 11, 2026 03:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant