惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Martin Fowler
Martin Fowler
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
T
Troy Hunt's Blog
Latest news
Latest news
Vercel News
Vercel News
S
SegmentFault 最新的问题
V
Vulnerabilities – Threatpost
博客园 - Franky
P
Privacy International News Feed
A
Arctic Wolf
T
The Blog of Author Tim Ferriss
S
Schneier on Security
T
The Exploit Database - CXSecurity.com
P
Palo Alto Networks Blog
T
Tor Project blog
Jina AI
Jina AI
GbyAI
GbyAI
The Hacker News
The Hacker News
博客园 - 叶小钗
Google DeepMind News
Google DeepMind News
T
Threatpost
C
CERT Recently Published Vulnerability Notes
P
Proofpoint News Feed
Scott Helme
Scott Helme
WordPress大学
WordPress大学
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Microsoft Azure Blog
Microsoft Azure Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
博客园 - 司徒正美
A
About on SuperTechFans
Recorded Future
Recorded Future
爱范儿
爱范儿
L
LangChain Blog
V
V2EX
C
Cybersecurity and Infrastructure Security Agency CISA
The Cloudflare Blog
阮一峰的网络日志
阮一峰的网络日志
K
Kaspersky official blog
Webroot Blog
Webroot Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
D
DataBreaches.Net
宝玉的分享
宝玉的分享
Google Online Security Blog
Google Online Security Blog
C
Cisco Blogs
L
Lohrmann on Cybersecurity
Help Net Security
Help Net Security
AWS News Blog
AWS News Blog
M
MIT News - Artificial intelligence
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
G
GRAHAM CLULEY

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Serverless Mental Models: What They Don't Tell You Before You Build
Joanne Skiles · 2026-05-26 · via DEV Community

Most serverless tutorials teach you how to deploy a Lambda function.

This article teaches you how to think about serverless, so you can make good architecture decisions instead of just following steps and hoping for the best.

I put together a four-part YouTube mini-series called Serverless Mental Models. This article covers the same ground, with each section anchored to the video if you want to go deeper, see the code, or watch the concepts play out live.


The Four Serverless Mental Models Videos

  1. Serverless has real limits, and you should know them before you commit.
  2. Stateless doesn't mean simple. It means the complexity moves outward, to edges.
  3. Serverless is about failure domains. Having the mental model of the "blast radius" of a failure is the real value proposition.
  4. Build it, then break it on purpose.

Mental model 1: Serverless has real limits

Video 1 of 4: "When Serverless Is the Wrong Choice"

I'm starting with the limits because I'd be doing you a disservice if I didn't.

Serverless is powerful. I've built real production systems with it, and I've given talks about it at AWS re:Invent and AWS Summit. But the honest picture is that it has four real limitations, and if you don't know what they are before you start building, you'll hit them when your system is live and your users are waiting.

Cold Starts

The first one, cold starts, is something you all have probably heard of before. When nobody has called your function in a while, the cloud provider freezes it. The next request has to wake it up (boot the runtime, load your code, initialize dependencies) before it can run. This takes anywhere from 100ms to a couple of seconds, depending on your runtime and bundle size.

For background jobs nobody notices: fine. For a user clicking a button and waiting... well, that's a bad experience.

The workaround is Provisioned Concurrency: you keep N copies of your function always warm. It works. But you're paying for always-on compute, which partially walks back the "pay for what you use" promise. If you need consistent sub-50ms p99 latency, serverless is going to frustrate you.

15-minute ceiling

Lambda functions have a hard maximum execution time of 15 minutes. This rules out video transcoding, large data processing jobs, ML training, and any workflow that needs to hold state across a long sequence of steps.

You can work around some of these with Step Functions, but that adds complexity. Sometimes the honest answer is a container.

Cost at high volume

Serverless is cheap at low scale, but at high scale the economics can flip.

Real numbers from the AWS Cost Calculator for 128MB / 100ms per request, no free tier:

Monthly requests Lambda cost EC2 t3.small cost
1M $0.41 $15
10M $4.08 $15
50M $20.42 $15
100M $40.83 $15

The crossover happens around 30-40 million requests per month. Model your costs with real numbers before you commit.

Stateful workloads

Each Lambda invocation is isolated. When it finishes, its memory is gone. The next invocation starts completely fresh.

This rules out persistent WebSocket connections, in-memory caching that survives across requests, and streaming pipelines that hold rolling buffers. If your workload needs persistent memory, serverless is going to fight you. And honestly, the fight isn't worth it; use the right tool for the job.

When serverless IS the right choice

Serverless is great for so many use cases:

  • Bursty or unpredictable traffic
  • Event-driven processing, think file uploads, DB changes, queue messages
  • Scheduled jobs. I actually use this myself I have a scheduled Lambda that checks my Meetup groups hourly, combines them into a calendar, and drops them into a Discord channel. Costs basically nothing.
  • Glue code connecting services
  • And my favorite, side projects, where you want zero ops overhead.

If your workload fits these patterns, serverless is an excellent choice. And if it doesn't, know that it's going to be a fight.


Mental model 2: Stateless doesn't mean simple

Video 2 of 4: "Stateless Does Not Mean Simple"

The most common misconception I see from those new to serverless, is that when they hear "stateless functions" they think "simple system." But then they build something real and it feels surprisingly complicated (like a Charlie Day diagram), and they don't understand why.

The thing is, stateless doesn't mean the complexity goes away. It means the complexity moves out of your functions and into the infrastructure around them.

Where the complexity goes

Databases. Your primary source of truth. The complexity here is consistency. When multiple Lambda functions run simultaneously, and both try to update the same record, who wins? You need to think about transactions, optimistic locking, and conflict resolution. DynamoDB's conditional writes handle single-item atomicity; multi-item atomicity requires DynamoDB Transactions (2x read/write cost, 100-item limit per call). (P.S - Don't try to simulate transactions with separate conditional writes. I have a story about that for another day.)

Caches. You add Redis or ElastiCache to speed up frequent reads. Now you have two copies of the same data. When the database changes, how does the cache know? Cache invalidation is famously one of the hardest problems in computer science. Not because it's algorithmically complex, but because getting it wrong has subtle, hard-to-detect consequences. Even a well-designed write-through strategy has a timing window between the DB write and the cache update. For prices, inventory counts, or financial data, that window is real.

Queues. Services like SQS let functions communicate asynchronously. The complexity is in delivery guarantees. SQS Standard queues offer at-least-once delivery, meaning a message may arrive more than once. Your function needs to handle duplicates gracefully.

Which brings us to idempotency...

Idempotency: the concept that ties it all together

Idempotent: running the same function twice with the same input produces the same result and causes no additional harm.

This matters because in a distributed system with queues, retries, and at-least-once delivery, your function will be called more than once for the same event. It's not a question of if.

The classic example from the code we build in Video 4:

// Idempotent: each invocation generates a new unique ID
// Two POSTs = two distinct notes, not one overwritten
const noteId = randomUUID();

The classic counterexample:

// NOT idempotent: if this runs twice, the customer is charged twice
await stripe.charges.create({ amount: 4900, currency: 'usd' });

And FYI chargebacks are not free, they penalize the seller. Enough of them, and your payment processor cuts you off.

Design for idempotency before you ship. Use idempotency keys. Design every queue-triggered function so that a duplicate delivery causes no additional harm.

The warm container gotcha

Lambda reuses execution environments between invocations, keeping your function "warm" to avoid cold start overhead. Variables initialized outside your handler persist across invocations on the same container.

This is good for things like the DynamoDB client:

// Initialized once: reused across warm invocations
const client = new DynamoDBClient({});
const docClient = DynamoDBDocumentClient.from(client);

It's a bug for request-specific mutable state:

// Don't do this: state leaks between invocations
let requestCount = 0; // This won't reset between requests!
exports.handler = async (event) => {
  requestCount++; // Undefined behavior on warm containers
  // ...
};

Write your functions as if they always start cold. Put reusable stateless resources outside the handler. Put everything request-specific inside it.

FIFO queues still need idempotency

SQS FIFO queues offer exactly-once delivery within the 5-minute deduplication window. But if your Lambda times out mid-processing without deleting the message, SQS redelivers it. FIFO reduces the frequency of duplicates. It does not eliminate them, so you have to design for idempotency regardless of queue type.


Mental model 3: Serverless is about failure domains

Video 3 of 4: "Why Serverless Is About Failure Domains"

Just so you know, the real reason to use serverless isn't cost or auto-scaling. Those are really nice side effects.

The real reason is failure isolation.

What is a failure domain?

A failure domain is the set of components that will fail together when one thing goes wrong. It's the boundary of what breaks.

Think about circuit breakers in a house. When the kitchen circuit trips (if you're me, this happens when I plug in the food processor and the KitchenAid mixer at the same time), the kitchen goes dark. The bedroom lights stay on. The living room TV keeps running. The failure is contained in one domain.

Without circuit breakers, a single kitchen fault can take out the whole house.

Software works the same way.

Monolith vs serverless failure surface

In a traditional monolith, everything runs in one process. A bug in the email service can crash the entire application. That means the payment processor, the catalog, and the user service. One failure domain equals the whole app.

In a serverless architecture, each Lambda function runs in its own isolated execution environment. They don't share memory. A crash in one doesn't cascade to another.

If your email function crashes, emails fail. Payments keep processing, and the catalog keeps running. The blast radius is just an email function.

Designing for blast radius

There are three principles to design for blast radius:

1. Separate what can fail independently.

If two operations don't need to succeed or fail together, don't connect them synchronously.

// Tightly coupled: analytics down = signup fails
signupHandler() {
  createAccount()     // critical
  sendWelcomeEmail()  // nice to have
  addToAnalytics()    // background work
}

// Decoupled: analytics down = analytics retries, signup succeeds
signupHandler() {
  createAccount()
  publish("user.created") // → queue → async handlers
}

2. Define SLAs per function, not for the whole system.

Critical functions (payments, authentication) need dead-letter queues, alarms on every error, and idempotency keys. Best-effort functions (analytics, recommendation refresh) can tolerate high error rates. Treating them all the same means you're either over-engineering the unimportant or under-engineering the critical.

3. Know your blast radius before you ship.

Draw the dependency graph. Ask yourself, "If this function goes down, what else breaks?" If the answer is "everything," you've accidentally built a serverless monolith: a chain of synchronous Lambda calls with the same cascading failure profile as a monolith, just more expensive and harder to debug.

The latency vs resilience tradeoff

Isolating failure domains with queues adds latency. End-to-end time for a full workflow is longer when there are queue hops.

So keep in mind this pattern that works. Keep the critical synchronous path as short as possible, return a meaningful response to the user, and then push everything else async.

// Synchronous path (user waits): create order, charge card, return order ID
// Async (user doesn't wait): send confirmation email, update analytics, notify warehouse

Common production gotchas

The serverless monolith. You split into Lambda functions but connected them all synchronously. Function A calls B, B calls C, C calls D. B fails = C and D never run, A gets an error. You rebuilt a monolith.

Shared Lambda layers as hidden coupling. A layer used by 12 functions means those 12 functions share a failure domain for anything in that layer. A bad deployment takes down all 12.

Treating dead-letter queues as optional. Without a DLQ, failed async invocations are silently dropped after retries. You find out when a user reports missing data, not from an alert.

Cross-region synchronous dependencies. If your Lambda in us-east-1 calls a service in eu-west-1 synchronously, a regional event becomes your outage. Cross-region calls need a fallback or should be moved off the synchronous path.


Mental model 4: Build it, then break it on purpose

Video 4 of 4: "Build a Minimal Serverless API"

This is the hands-on video. We review a notes API (POST, GET, DELETE) using Lambda, API Gateway, and DynamoDB, deployed with AWS CDK. Then we break it four different ways and debug it "live".

Here's what the architecture looks like:

Browser → API Gateway → Lambda → DynamoDB

Three services, which means three failure domains. If DynamoDB has a bad day, API Gateway is still receiving requests and Lambda is still running. Blast radius = DynamoDB calls fail. Not the whole API is down.

The key decisions in the handler

// Outside the handler: warm containers reuse this
// Ep 2: warm container gotcha
const client = new DynamoDBClient({});
const docClient = DynamoDBDocumentClient.from(client);
const TABLE = process.env.TABLE_NAME || "notes";

exports.handler = async (event) => {
  try {

    if (method === "POST") {
      const body = JSON.parse(event.body || "{}");

      // Validate at the boundary: 400 (caller error) not 500 (our error)
      if (!body.content || typeof body.content !== "string") {
        return respond(400, { error: "content is required" });
      }

      // randomUUID: idempotency by design. Ep 2
      // Retried POST = new note, not an overwrite
      const noteId = randomUUID();

      await docClient.send(new PutCommand({
        TableName: TABLE,
        Item: { noteId, content: body.content.trim(), createdAt: Date.now() },
      }));

      return respond(201, { noteId });
    }

    // ...GET and DELETE...

  } catch (err) {
    // Always log before returning 500
    // console.error → CloudWatch Logs
    // Never return err.message: may leak internals to callers
    console.error("Handler error:", err);
    return respond(500, { error: "Internal server error" });
  }
};

Every decision connects back to a concept from the series.

A note on the comments: the tutorial code is heavily commented because it is a tutorial. That is not how I write production code. In production, I only comment on things that actually need explanation: weird behavior, a non-obvious "why", a gotcha that will bite the next person. The code should explain what it does. Comments explain why it does it when that is not obvious. If anybody has ever seen any of my production code... sorry. Anyways.

The CDK stack

Infrastructure as code, not console clicking. This is what engineers use in production: version-controlled, reproducible, and reviewable in a pull request.

const table = new dynamodb.Table(this, 'NotesTable', {
  partitionKey: { name: 'noteId', type: dynamodb.AttributeType.STRING },
  billingMode: dynamodb.BillingMode.PAY_PER_REQUEST, // Ep 1: no idle cost
  removalPolicy: cdk.RemovalPolicy.DESTROY,
});

const dlq = new sqs.Queue(this, 'NotesDLQ', {
  retentionPeriod: cdk.Duration.days(14),
});

const handler = new lambda.Function(this, 'NotesHandler', {
  runtime: lambda.Runtime.NODEJS_20_X,
  environment: { TABLE_NAME: table.tableName },
  tracing: lambda.Tracing.ACTIVE, // X-Ray: cold start visibility. Ep 1
  deadLetterQueue: dlq,           // Silent failure prevention. Ep 3
});

// Least privilege: limits blast radius of a compromised function. Ep 3
table.grantReadWriteData(handler);

The four breaks

Break 1: IAM permission error. Comment out grantReadWriteData, redeploy, call the API. You get a 500 with no useful information. Navigate to CloudWatch Logs → your function's log group → most recent stream. You'll see:

Handler error: AccessDeniedException: User: arn:aws:sts::...
is not authorized to perform: dynamodb:PutItem on resource: ...table/notes

The log tells you exactly what failed: which principal, which action, which resource. The 500 told you nothing. This is why console.error before every return respond(500) is non-negotiable.

Break 2: Bad input. Send a POST with no content field. Without validation it would silently store a note with content: undefined and return a 201. With it, you get a clean 400. Validate at the boundary. 400 = caller's fault. 500 = your fault. They're different problems for different people.

Break 3: Cold start in X-Ray. Leave the function idle for a few minutes, then invoke it. Navigate to X-Ray traces. You'll see two segments on the cold invocation: Initialization (~300-400ms) and Invocation (<10ms). Warm invocations have no Initialization segment. This is the cold start discussion from Video 1, made visible in real traces.

Break 4: The DLQ and silent failures. Our HTTP API is synchronous, so errors return 500s to the caller. But real production systems gain async triggers over time. Without a DLQ, failed async invocations are silently dropped. The DLQ is already wired in the CDK stack. Navigate to Lambda → Configuration → Asynchronous invocation to verify it's there. Add a CloudWatch alarm on queue depth so you get paged when something fails, not when a user reports it.


The full picture

Four videos. Four mental models. Here's what they give you together:

Mental model The insight
Limits Know when serverless is the wrong tool before you commit
Stateless ≠ simple The complexity moves to the state layer. Design for it.
Failure domains Serverless is fundamentally about blast radius control
Build + break The happy path is easy. Knowing what the errors look like is the real skill

The developers who get really good at serverless aren't the ones who know the most API syntax. They're the ones who have a mental model of the whole distributed system. This means the failure modes, the state management, the blast radius, and making design decisions from that model.

That's what this series is trying to give you.


Watch the series

📺 Serverless Mental Models: full playlist

💻 Code on GitHub