#AWS#Serverless#Drizzle#FinOps#Optimization

How I Cut AWS Lambda Cold Starts by 70% Through ORM Optimization

6 min read

A data-backed story of identifying performance bottlenecks and optimizing a serverless application.

When My App Didn't Behave as Expected

The first time I tested my recently built full-stack serverless app, I noticed that executions were far slower than anticipated. The delays were consistent and persisted across different endpoints. Network latency and external API calls were minimal and stable, which made it clear that the problem was internal execution overhead, not connectivity issues.

Looking into CloudWatch metrics, several patterns stood out:

  • P95 latency: ~1000ms
  • Cold start: ~1.8s
  • Memory usage: ~85–90% of allocated memory
  • Artifact size: ~250MB (unzipped)

The backend was deployed on AWS Lambda in a modular monolithic architecture, and the weight of dependencies and unoptimized binaries immediately appeared as the main contributors to slow performance.


Trimming the Weight — Cognito SDK

Authentication was initially handled with the Cognito SDK, which added roughly 18MB to the deployment artifact and contributed 120–180ms to cold starts. Since only token verification was necessary, I replaced the SDK with a lightweight JWT validation.

This change immediately showed improvement:

MetricBeforeAfter
Artifact size250MB232MB
Cold start1.8s1.62s

From that point, it was evident that removing unused dependencies could meaningfully reduce initialization overhead.


Optimizing the Bundling Pipeline

Next, I turned to the build process. Using ESBuild with ESM-only builds, explicit externals, dead-code elimination, and minification, I stripped out side-effect-heavy modules and unused imports.

The results were clear:

MetricBeforeAfter
Artifact size232MB160MB
Cold start1.62s1.42s
Memory usage85%72%

Small, disciplined changes compounded, reducing both cold start and memory usage significantly.


Swapping the ORM — From Prisma to Drizzle

Prisma’s Rust-based query engine introduced further cold start latency due to binary initialization. Migrating to Drizzle, which is TypeScript-native, eliminated that overhead entirely.

MetricPrisma StackDrizzle Stack
Artifact size160MB125MB
Cold start (P95)1.42s550ms
Execution duration (P95)1000ms300ms
Memory usage72%56%
Monthly Lambda costBaseline-60%

The trade-off was additional manual schema management and loss of Prisma Studio, but the performance and cost benefits were substantial.


Conclusions

Through this process, several insights became apparent:

  1. Serverless performance is sensitive to artifact weight. Every MB of dependency or binary increases cold start time, memory usage, and cost.
  2. ORM choice affects initialization. Binary-based engines like Prisma are heavy for ephemeral compute, while native TypeScript ORMs like Drizzle eliminate unnecessary overhead.
  3. Incremental improvements add up. Reducing artifact size, trimming dependencies, and optimizing builds all compounded to create significant performance gains.

The Outcome

By methodically identifying the bottlenecks and optimizing both dependencies and ORM choice:

  • Cold starts dropped by ~70%
  • Monthly Lambda costs decreased by ~60% at scale
  • Artifact size was reduced by ~50%

This demonstrates that careful analysis, targeted optimization, and informed trade-offs can achieve measurable, production-ready improvements in serverless applications, independent of network latency.