Skip to content

cache bundled functions across deploys#116

Merged
michaelfward merged 3 commits into
mainfrom
michael/functions-cache
Jul 13, 2026
Merged

cache bundled functions across deploys#116
michaelfward merged 3 commits into
mainfrom
michael/functions-cache

Conversation

@michaelfward

@michaelfward michaelfward commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

two deploy-time fixes that compound:

  1. cache bundled functions across deploys. actions/cache restores .netlify/functions/, deploy-ci re-stamps the manifest (netlify-cli only trusts it for 2 minutes), and netlify deploy reuses the zips. on a miss, netlify build produces the zips + manifest once (no double bundling) and the cache persists them.

  2. bundle uploadthing instead of shipping its node_modules closure. the global external_node_modules = ["uploadthing"] made zisi ship uploadthing's entire dependency closure (the effect ecosystem, ~4900 files, 16-24MB) into every one of the 130 function zips - ~2GB per deploy, minutes of hashing + upload. esbuild bundles + treeshakes it fine today (the april commit that externalized it was a combined shotgun fix with the esbuild switch, never separately needed). zips drop ~60%+.

changes

file change
deploy.yml / deploy-preview.yml actions/cache step for .netlify/functions, exact-match key over every bundling input
scripts/deploy-ci.ts hit: vite build only; miss: netlify build (bundles + writes manifest, falls back to plain vite build on failure); manifest re-stamped right before deploy
netlify.toml drop external_node_modules = ["uploadthing"]

the cache key covers netlify/, scripts/ (guidance.ts inlines scripts/seed-guidance.ts), netlify.toml, lockfile, tsconfig. zisi output is deterministic (verified: bundling the same function twice gives identical sha256), so an exact-key restore is byte-identical to a fresh build. no restore-keys fallback on purpose - a near-miss restore would ship stale function code.

measured on this PR's previews

before (prod baseline) cache miss + slim zips cache hit + slim zips
bundle 130 functions ~234s (inside deploy) 56.5s (netlify build) skipped (22.9s vite only)
hashing ~4min 0.8s ~1s
function upload ~3min ~2.9min ~2.8min (per-file api overhead, not bytes - and netlify never dedupes function uploads even for byte-identical zips)
whole job ~14min 5m58s 5m20s

test plan

  • preview run 1: cache miss, netlify build bundles, deploy logs "Deploying functions from cache"
  • rerun of the same preview: cache hit restored, vite-only build, deploy reused the cached zips
  • slim-zip miss run: bundling 187.9s -> 56.5s, hashing 4min -> 0.8s, job 5m58s
  • slim-zip hit run: job 5m20s end to end
  • preview functions respond: /api/guidance 401 (auth gate), /api/projects 200 with well-formed json (same as prod) - uploadthing module graph loads inlined
  • post-merge: watch the first real image-upload path (thumbnails / og images) since the preview smoke doesn't exercise an actual uploadthing call

@github-actions

Copy link
Copy Markdown

🔎 Loop QA preview (all-k8s backend): https://pr-116--loop-qa-j63k5x.netlify.app

@michaelfward michaelfward merged commit 4d63ba9 into main Jul 13, 2026
4 checks passed
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.

2 participants