Skip to content

Deploy shared-code changes directly, without CloudFormation - #211

Merged
matuscongrady merged 2 commits into
mainfrom
feat/fast-deploy-shared-layer
Oct 18, 2026
Merged

matuscongrady merged 2 commits into
mainfrom
feat/fast-deploy-shared-layer

Conversation

@matuscongrady

Copy link
Copy Markdown
Contributor

Why

A change to code several Lambda functions share (the benchmark's packages/service) changes their shared chunk layer. The deploy treated any change to the AWS::Lambda::LayerVersion resource as a template change and went through CloudFormation: every shared-change run on main logged Deploying through CloudFormation: StpSharedChunkLayer1LayerVersion changed. On the EC2 benchmark that was 29.9 s (lambda-5) and 37.7 s (lambda-25) for a shared change, against 9.8 s and 11.9 s for a change to the functions' own code.

What changes

  • A content-only change of a shared chunk layer no longer forces a CloudFormation deployment (isContentOnlyLayerChange).
  • The fast deployment publishes the layer version itself before it touches any function, or reuses CloudFormation's version or one an earlier fast deployment published with the same content. Then every function that uses the layer gets the new version together with its own code, in parallel, through the existing update path (code, wait, layers, wait, tag).
  • Each function records the layer content it runs in a new stp:shared-layers tag, next to stp:code-digest. CloudFormation writes it from the template, fast deployments write it after a direct update, and a stack deployed before this change is read from the template CloudFormation holds. The next deployment, --fullDeploy, rollback and the full-deployment drift check all use it.
  • CloudFormation does not re-apply a function's layers when only its code changes (checked on real AWS: after a rollback the functions kept the published version). So when a function runs other layer content than the template gives it and the layer's content equals CloudFormation's, the template points the layer at a copy of the same object under the release's key. CloudFormation then republishes the layer and updates code and layers in one resource update. The same copy is made for rollback templates.
  • After a full deployment, tags CloudFormation left alone are repaired, and layer versions that fast deployments published and nothing runs any more are deleted. delete removes the rest. Versions still in use are never deleted; the deletion reads every function's layers first.
  • A fast deployment refuses the direct path when a function invoked through an alias uses a changed layer (the existing rule) or when a Lambda outside the direct path would keep a changed layer.

Measured

Live, from this machine against stacktape-e2e with the benchmark harness (node bench.mjs deploy stacktape <app> --change shared, 3 runs, medians of time until the new code answered):

main (a0c557b) shared this branch shared main handlers this branch handlers
lambda-5 30.6 s 20.4 s 15.0 s 9.7 s
lambda-25 45.5 s 22.3 s 13.4 s 12.6 s

The shared change is still about 10 s behind a handler change. An instrumented run shows where: PublishLayerVersion takes 5.5-5.8 s on the AWS side for the 2 MiB layer (measured the same with the AWS CLI, from S3 or from a direct upload), and each function then needs two sequential updates, code and then layers, with a wait after each, because Lambda takes one update at a time. The layer is published before any function changes so that a failed publication leaves every function as it was. Publishing it in parallel with the code updates would save up to 4 s but would leave the functions on new code without the new layer for the whole publication.

Tests

Failing on main, passing here:

  • apps/cli/src/domain/deployment-artifact-manager/shared-layers.spec.ts: identity, tag format, template reading, version selection, republish rule.
  • hotswap-utils.spec.ts: content-only layer changes, including a diff from the real template differ.
  • fast-deploy.spec.ts: layers are published before any function, a publication failure changes no function, cleanup after success.
  • tests/ministack/fast-deploy.spec.ts: a two-function split project through the source CLI on MiniStack. Shared change deployed directly with the ZIPs executed in the official runtime image, unchanged redeploy, own-code change, rollback, redeploy, --fullDeploy, return to CloudFormation's content, rollback to the held release while running a published version, full deployment for an unrelated change while drifted, and a no-op deploy at the end. Every step checks the functions' layers, tags, the layer versions left and the recorded releases.

Lanes run: deployment-rules, naming, synthesis (characterization), synthesis-families, data-safety, cli-operation-rules, workspace-tools, ministack-deploy, pnpm check:integrated.

Live: the series above, plus a rollback of the lambda-5 stack to the release CloudFormation holds while its functions ran a published layer version. The restored template's layer resource was republished from a copy of the release's object (CloudFormation created version 12 from cc604c182c29-v000020.zip), the functions came back on it with their restored code, their tags were corrected, and the published version 11 was deleted.

🤖 Generated with Claude Code

matuscongrady and others added 2 commits October 18, 2026 01:27
A change to code several functions share changes their shared chunk layer.
The deploy treated any layer change as a template change and fell back to
CloudFormation, so a shared change took 30-55 s on 5-25 functions where a
handler change took 10 s.

A content-only change of a shared chunk layer is now a code change. The fast
deployment publishes the layer version itself (or reuses CloudFormation's or
an earlier published version with that content), then gives every function
that uses it the new version together with its code. Each function records
the layer content it runs in a `stp:shared-layers` tag, written by
CloudFormation from the template and by fast deployments, so the next
deployment, a rollback and a full deployment all know what the function runs.
Full deployments re-key drifted functions so CloudFormation updates them,
repair tags CloudFormation leaves alone, and delete the layer versions fast
deployments published once nothing runs them; `delete` removes the rest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mark shared-layer content unknown before changing layers so deploy and
rollback restore it after a failed tag write. Confirm function tags only
after recording the fast release, so a failed release write cannot make
the retry skip the running code. Preserve source-map activation for
successful functions when another workload fails.

Cover failed tag and release writes through the CLI against MiniStack,
including normal deploy, full deploy, rollback and layer cleanup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@matuscongrady

Copy link
Copy Markdown
Contributor Author

Fixed both findings in f7e5ee8. Layer updates now mark the layer tag unknown first, so deploy, full deploy and rollback restore the correct layers after a failed final tag write. Cleanup keeps versions still in use. Function tags are confirmed after the release is stored, so a failed release write is retried on the next deploy.

The new CLI failure-recovery tests, selected offline lanes and pnpm check:integrated passed. The full MiniStack lane had 23 passes and two documented emulator skips. No AWS used.

@matuscongrady
matuscongrady merged commit 8f4a4c1 into main Oct 18, 2026
2 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.

1 participant