ADR-113: Lambda Packaging¶
Status: Accepted Date: 2026-01-27 Issue: #154
Context¶
The Lambda aggregator previously bundled the entire zae_limiter package with all runtime dependencies into the Lambda zip. This resulted in a ~17 MB deployment package containing libraries like aioboto3, aiohttp, click, pip, and other packages that the aggregator never uses. The aggregator only needs boto3 (provided by the Lambda runtime), aws-lambda-powertools, and zae_limiter.schema.
The project needed a packaging approach that: - Produces a small Lambda zip (~1-2 MB instead of ~17 MB) - Only bundles what the aggregator actually uses - Works cross-platform (macOS/Windows host building for Linux Lambda) - Does not require Docker - Is compatible with LocalStack free tier (no Lambda Layers)
Decision¶
-
Separate the aggregator into its own top-level package
zae_limiter_aggregator(installed alongsidezae_limiterfrom the same wheel). -
Minimal Lambda zip — The Lambda builder copies only:
zae_limiter_aggregator/(all.pyfiles)zae_limiter/__init__.py(empty stub — avoids importingaioboto3)zae_limiter/schema.py(full copy — only importstyping, no external deps)-
[lambda]extra dependencies viaaws-lambda-builders(onlyaws-lambda-powertools) -
Empty
__init__.pystub — Python's import system executeszae_limiter/__init__.pywhen the aggregator doesfrom zae_limiter.schema import .... The real__init__.pyimportsaioboto3which isn't available in Lambda. The empty stub avoids this. -
aws-lambda-buildersremains a base dependency for cross-platform pip installs of the[lambda]extra.
Consequences¶
Positive:
- Lambda zip size reduced from ~17 MB to ~1-2 MB (aws-lambda-powertools + source files only)
- No aioboto3, aiohttp, botocore, click, pip in the Lambda zip
- boto3 provided by Lambda runtime — no need to bundle
- Cross-platform builds work without Docker
- Compatible with LocalStack free tier (no Lambda Layers required)
- Dev/unreleased versions work because local packages are copied, not downloaded from PyPI
- Clean separation of concerns: aggregator is independently importable
Negative:
- Two packages to manage in the wheel (zae_limiter + zae_limiter_aggregator)
- Empty __init__.py stub is a packaging workaround (not a true standalone package)
- aws-lambda-builders and pip remain as base dependencies
Alternatives Considered¶
Lambda Layers¶
Rejected because: LocalStack free tier does not support Lambda Layers, breaking local development.
Separate PyPI package (zae-limiter-lambda)¶
Rejected because: Requires shipping a zip file artifact and keeping two packages in sync, adding maintenance burden.
Docker-based builds (SAM/CDK)¶
Rejected because: Requires Docker installed on the build machine, adding a heavy dependency for what should be a lightweight CLI operation.
pip --platform with --only-binary¶
Rejected because: Only works for packages with pre-built wheels; fails for packages requiring compilation, and aws-lambda-builders already wraps this approach with better error handling.
Bundle full zae_limiter with all deps¶
Rejected because: Produces ~17 MB zip with unnecessary packages (aioboto3, aiohttp, click, pip) that the aggregator never imports.