I don't start with "EC2 vs Fargate vs Lambda". I start with constraints: latency and throughput needs, traffic shape, required level of operational control, and how often the system will change. From there the choice between these three is usually straightforward, and my default is almost always Lambda for new APIs and background tasks.
When I use EC2
- • I need full OS-level control or custom agents.
- • I'm running legacy or stateful workloads.
- • I want stable, long-running processes with predictable performance.
Trade-off: maximum flexibility, but I own patching, hardening, autoscaling, and most of the undifferentiated heavy lifting.
When I use Fargate
- • Workload is container-friendly and stateless.
- • I want no EC2 management but full container control.
- • I need 24/7 services with predictable CPU and memory footprints.
Trade-off: less ops than EC2, but I still own image lifecycle, base image security, and orchestrator concepts.
When I use Lambda
- • The workload is naturally event-driven.
- • Traffic is spiky or has a low baseline.
- • The logic fits clean, short-lived functions.
- • I want first-class AWS support and fast iteration.
Trade-off: zero baseline cost and minimal ops, but runtime limits, cold starts, and debugging many small functions can add complexity. In return I get very clean, per-endpoint observability and an easy path into Step Functions when a business process needs orchestration.
In real platforms I rarely pick just one of these. Lambda is my starting point for most new capabilities; I only reach for Fargate or EC2 when the workload doesn't fit the Lambda model (very long-lived, highly specialized networking, or extreme tuning requirements).
Healthy platforms aren't "all serverless" or "all containers". They're portfolios of compute options with clear paved roads and a few well-governed escape hatches. My job as a platform engineer is to make the default path boring and reliable for product teams.
Lambda as the default edge
I treat Lambda as the default entry point for APIs and events: AppSync resolvers, API Gateway handlers, and event-driven glue between managed services. It's ideal for spiky or low-volume workloads where paying only for what you use makes sense, and AWS's support and tooling around Lambda give me very clean observability into each API and task.
Fargate for steady services
For internal platform services that run 24/7 and need predictable performance, I lean on ECS/EKS on Fargate. Teams give me a Docker image and a bit of config; the platform gives them logging, metrics, traces, and a sane networking story.
EC2 for edge cases
EC2 is where I park legacy apps, stateful workloads, or specialized components that really do need a VM. I hide that complexity behind stable APIs and Terraform/CDK modules so product teams don't have to think in terms of instances.
The important part is that contracts stay stable while the compute substrate evolves underneath. That's what lets a platform keep improving without forcing every product team to rewrite their code every time AWS ships a new feature.
I don't treat "DynamoDB vs SQL" as a religion debate. I start with the shape of the data, the access patterns, and what I need from observability and lifecycle management. In that world I usually prefer DynamoDB for operational workloads, and reach for SQL where complex querying is the core of the problem.
Why I often prefer DynamoDB
DynamoDB gives me native integration with CloudWatch, fine-grained metrics per table and index, and clear signals when partitions are hot. Observability is effectively built in. On top of that, TTL and streams make data lifecycle a first-class concept: I can keep hot operational data in DynamoDB and push older records into S3 as a data lake without bolting on a bunch of custom jobs.
Where SQL still shines
SQL is still my go-to when the main value is in complex queries: rich filtering, sorting, joins, and ad-hoc reporting. Mature query planners and relational modeling make it much easier to answer questions like "show me all X where Y, grouped by Z" without redesigning the data model. For some domains, that's exactly what you want.
Mitigating DynamoDB's hard parts
DynamoDB is intentionally not a general-purpose query engine. You get fast key-based access and predictable patterns, but rich, exploratory queries are harder. I usually mitigate that by keeping DynamoDB as the operational source of truth and streaming changes into systems like OpenSearch or Algolia for full-text search and flexible filtering. For longer-term analytics, those same streams feed S3-backed data lakes.
In practice I default to DynamoDB for operational workloads because of its observability, lifecycle controls, and easy path into S3 for long-term storage. I layer SQL, OpenSearch, or Algolia on top when the problem demands richer querying, rather than forcing one database to be everything for everyone.
I use both Serverless Framework and Terraform heavily, but for different layers of the stack. For me it's not "which one is better?" so much as "what am I trying to express: an application, or a platform boundary?"
Where Serverless Framework shines
Serverless Framework is my default for Lambda-centric applications: APIs, background jobs, and event-driven workflows. It lets me express functions, events, IAM, and environment as a single unit of deployment. The DX is great for product teams: short feedback loops, clear linkage between code and infrastructure, and first-class support for patterns like stages and per-function IAM.
Where Terraform is a better fit
Terraform is my choice when I'm defining shared platform primitives: VPCs, subnets, CloudFront distributions, Aurora clusters, cross-account roles, and other things that many applications will consume. Its provider ecosystem and plan/apply model are well-suited to long-lived infrastructure with clear boundaries and fewer day-to-day changes.
How I combine them in practice
In most real systems I use Terraform to lay down the guardrails and shared building blocks, then use Serverless Framework inside those boundaries to ship application logic quickly. Terraform owns the network, base data stores, and global resources; Serverless owns the Lambdas, APIs, and event wiring that sit on top. That separation keeps the platform stable while letting product teams move fast.
I don't need one tool to win outright. I need both to be predictable and automatable so that developers can treat infrastructure as code instead of tickets. Picking the right tool for each layer of the stack is what keeps the platform simple at the edges and powerful at the core.