Overview
At 2 AM, I got a phone call.
Users of a ride-hailing project reported that opening the App to view trip history during late-night hours caused a 3-5 second blank screen. After investigation, I found that this API group used Serverless function compute. During the day, traffic was normal, but after traffic dropped off at night, function instances were reclaimed. The first request triggered a cold start, pushing P99 latency to 4.8 seconds.
This is not an isolated case. I’ve seen Serverless projects fail repeatedly when moving from POC to production: cold starts causing API timeouts, monthly bills 3x higher than traditional servers, production incidents with no logs to trace, and security policies growing so tangled that nobody dares to touch them.
Serverless marketing is seductive—pay-per-use, auto-scaling, no operations. But anyone who has run it in production knows “no-ops” is a myth. Serverless doesn’t eliminate operations; it shifts the battlefield from server management to cold start mitigation, cost control, observability engineering, and security policy governance. And these new battlefields are just as complex as traditional operations.
This article skips Serverless basics and dives straight into 5 production-level pitfalls and governance decisions. Every pitfall comes from a real project, every decision backed by data.
1. Cold Start Governance: Four-Layer Optimization from 5 Seconds to 200ms
1.1 Where Exactly Does Cold Start Get Stuck
Many people know “cold start is slow” but can’t articulate where. Let’s break down a complete cold start process:
| Stage | Time Share | What Happens | Optimizable? |
|---|---|---|---|
| Resource allocation | 15-20% | Platform allocates CPU/memory, mounts filesystem | Platform-side, not user-controllable |
| Runtime init | 20-30% | Loads language runtime (Java slowest, Node.js fastest) | Choose runtime |
| Code loading | 15-25% | Downloads deployment package, unzips, loads dependencies | Slim down package |
| Init logic | 30-40% | Executes global code: DB connections, SDK init, config loading | User-controllable, largest optimization space |
Key finding: the init logic stage accounts for the largest share (30-40%), and it’s the only part users can deeply optimize. Many teams have slow cold starts not because the platform is bad, but because they stuffed the global init with too much—database connection pools, Redis clients, config files, service discovery registration. In traditional servers these run once. In Serverless, they run on every cold start.
1.2 Layer One: Runtime Selection—Don’t Use Java for Functions
This is the most brutal but most effective optimization. Cold start differences across runtimes are massive:
| Runtime | Typical Cold Start | Reason | Use Case |
|---|---|---|---|
| Node.js | 100-300ms | V8 engine starts fast, short JIT warmup | API gateways, lightweight compute |
| Python | 200-500ms | Interpreter starts fast, but dependency loading is slow | Data processing, scripts |
| Go | 50-150ms | Compiled to single binary, no runtime | Performance-sensitive scenarios |
| Java | 800-3000ms | JVM startup + class loading + GC init | Legacy system migration |
In the ride-hailing project, switching the same API group from Java to Go cut cold start from 2.8 seconds to 180ms. No business logic changed—just the runtime.
My recommendation: Use Go or Node.js for new projects. If you must use Java (legacy systems), enable AWS SnapStart or Alibaba Cloud’s init optimization, which can compress JVM cold start from 2-3 seconds to around 500ms. But SnapStart has its own pitfalls—it restores memory state from a snapshot, so if your init logic includes time-sensitive operations (like fetching temporary tokens), the token may have expired after snapshot restoration.
1.3 Layer Two: Code Slimming—Cut What You Don’t Need
Deployment package size directly impacts cold start. I saw a Python function at an e-commerce platform with an 85MB package containing full Pandas, NumPy, and SQLAlchemy—but it only used json and requests. Cold start was 3.2 seconds, with 1.8 seconds spent loading dependencies.
Slimming strategy:
# ❌ Wrong: initialize all clients at function entry
import pandas as pd # 80MB, adds 800ms to cold start
import numpy as np # 30MB, adds 300ms
from sqlalchemy import create_engine # adds 200ms
def handler(event, context):
# Actually only uses json and requests
import json
import requests
# ...business logic
# ✅ Right: import only what's needed, lazy load
def handler(event, context):
import json
import requests
# Import what you actually use in the business logic
# Put heavy dependencies in a separate layer, reference on demand
Measured data: Same Python function, package slimmed from 85MB to 3.2MB, cold start dropped from 3.2 seconds to 480ms. Go functions, being compiled, have 8MB binaries with cold starts naturally under 100ms.
1.4 Layer Three: Warmup Strategies—Buying Time with Money
When cold start optimization hits its limit, warmup is the answer. The idea: have the platform pre-stage function instances so requests are served immediately without waiting for initialization.
| Warmup Approach | Mechanism | Cost | Effect | Use Case |
|---|---|---|---|---|
| Provisioned concurrency | Platform keeps N instances warm | Billed by instance count × time | Cold start = 0 | High-frequency core APIs |
| Scheduled trigger | Invoke function every N minutes | Billed per invocation | Reduces cold start probability | Low-frequency APIs with SLA requirements |
| Event-based warmup | Upstream sends a lightweight request before actual call | Nearly zero | Requires upstream coordination | Scenarios with predictable call chains |
In the ride-hailing project, I used provisioned concurrency of 5 instances for core APIs. Monthly cost increased by about 120 yuan, but P99 dropped from 4.8 seconds to 200ms. Worth every penny—late-night complaints dropped 90%, and the customer service savings alone exceeded 120 yuan.
But provisioned concurrency isn’t a panacea. I saw a team configure provisioned concurrency of 10 instances for all 20 functions. Monthly bill jumped by 8000 yuan. Turns out 15 of those functions had fewer than 100 daily invocations—they didn’t need warmup at all. Provisioned concurrency should only be used for high-frequency functions on the critical path.
1.5 Layer Four: Init Logic Optimization—Move Heavy Work to Global Scope
Serverless functions have a key execution model property: global scope code executes only once during cold start; subsequent requests reuse the same instance and skip it. This means placing init logic in the global scope lets subsequent requests avoid repeated initialization.
// Go example: global init, executes only once
var (
dbConn *sql.DB
redisCli *redis.Client
config *Config
)
func init() {
// These operations execute only once during cold start
// Subsequent requests reuse the same instance, skipping this
config = loadConfig()
dbConn = createDBPool(config.DB)
redisCli = createRedisClient(config.Redis)
}
func Handler(event Event) (Response, error) {
// Use already-initialized clients directly
// Hot starts have near-zero latency here
result, err := dbConn.Query("SELECT ...")
// ...
}
Pitfall warning: Global connection pools have a trap. When function instances are reclaimed, TCP connections in the pool break. On the next cold start, if you reuse the old connection pool object, the first query will timeout. The fix: rebuild connection pools in init(), or auto-reconnect on query failure. I hit this in the ride-hailing project—at 3 AM the instance was reclaimed, and at 4 AM when traffic resumed, the first batch of requests all timed out because the connection pool’s connections were stale.
1.6 Cold Start Optimization Summary
After four-layer optimization, the ride-hailing project’s core API cold start evolution:
| Optimization Stage | P99 Latency | Method | Monthly Cost Change |
|---|---|---|---|
| Initial state | 4800ms | None | Baseline |
| Runtime switch | 1800ms | Java → Go | None |
| Code slimming | 320ms | 85MB → 3.2MB package | None |
| Provisioned concurrency | 200ms | 5 warm instances | +120 yuan |
| Init optimization | 200ms | Connection pool reuse | None |
From 4.8 seconds to 200ms—core methods were runtime switching and code slimming, with provisioned concurrency as the final safety net. Cost increased only 120 yuan/month, but user experience transformed.
2. Cost Traps: Three Root Causes of Monthly Bills Surging from 50 to 5,000 Yuan
2.1 Trap One: Pay-Per-Use Does Not Mean Cheaper
“Pay-per-use” is Serverless’s most attractive selling point—and its most dangerous trap.
Real case from an e-commerce platform: A group of internal management APIs migrated from 4 ECS instances (monthly cost 3,000 yuan) to function compute. The first month’s bill was only 50 yuan—because daily invocations were under 100. The team was thrilled, thinking they saved 98%.
Then they migrated another business—a product search suggestion API. This API had 2 million daily invocations with peak QPS of 5,000. After migration, the first month’s bill was 5,200 yuan—73% more expensive than the original ECS solution.
Root cause: Serverless billing formula is request_count × unit_price + execution_duration(GB-seconds) × unit_price + outbound_traffic × unit_price. In low-frequency scenarios, request counts are low, so costs are genuinely low. But in high-frequency scenarios, request costs scale linearly, and execution duration costs accumulate. Moreover, memory configuration directly affects billing—a 1GB function executing for 1 second costs 1 GB-second; a 256MB function executing for 1 second costs only 0.25 GB-seconds.
Cost governance strategies:
| Strategy | Effect | Implementation Difficulty |
|---|---|---|
| Memory right-sizing | 30-50% GB-second cost reduction | Medium |
| Execution timeout | Prevents zombie functions from burning money | Low |
| Concurrency limits | Prevents traffic spikes from exploding bills | Low |
| Hot/cold separation | Low-frequency → Serverless, high-frequency → ECS | High |
Memory right-sizing is the most easily overlooked optimization. Many teams assign 1GB to all functions uniformly, when most only need 256MB. In the ride-hailing project, I right-sized 20 functions from uniform 1GB to 128MB-512MB on-demand, cutting monthly costs by 42%.
But here’s a counterintuitive point: reducing memory doesn’t always save money. Function execution duration is strongly correlated with memory size—more memory means more CPU (Serverless platforms typically allocate CPU proportional to memory), which means faster execution. A function with 256MB might take 2 seconds, while 1GB might finish in 0.5 seconds. Final GB-seconds: 256MB × 2s = 0.5 GB-seconds vs 1GB × 0.5s = 0.5 GB-seconds. Same. But if 512MB takes only 0.8 seconds, that’s 0.4 GB-seconds—actually cheaper.
My recommendation: Run a memory stress test. Configure the same function at 128MB/256MB/512MB/1GB, run identical workloads, record execution time and GB-seconds, find the inflection point. The inflection point is typically around 512MB—beyond that, execution time reduction can’t keep up with memory cost growth.
2.2 Trap Two: The Cost Multiplier Effect of Function Chaining
Serverless architecture encourages “one function per feature,” but chained function calls create a cost multiplier effect.
A ride-hailing project search API was split into 5 functions:
API Gateway → Auth function → Param validation function → Search function → Result sorting function → Logging function
One user request = 5 function invocations. 1 million daily searches = 5 million function invocations. At 0.0001 yuan per invocation, request costs alone = 500 yuan/day = 15,000 yuan/month.
After merging into 2 functions (auth+validation combined, search+sort+logging combined), invocations dropped to 2 million/day, cutting monthly costs by 60%.
My recommendation: Don’t make function granularity too fine. One function should handle a complete business logic, not split one business operation into multiple chained functions. Serverless best practice is “coarse-grained functions”—each function handles a complete business operation. Only split when independent scaling is genuinely needed.
2.3 Trap Three: Hidden Costs—Everything You Didn’t Think About Is Charging You
Beyond explicit function invocation costs, Serverless has significant hidden costs:
| Hidden Cost | Cause | Monthly Average | Controllability |
|---|---|---|---|
| Log storage | Every invocation writes logs; CloudWatch/SLS bills by storage | 200-800 yuan | High (set retention) |
| Observability tools | Extra function calls for tracing and metrics | 100-500 yuan | Medium |
| API Gateway | Billed per request, stacks with function costs | 100-300 yuan | Low |
| Data transfer | Internal VPC calls between functions | 50-200 yuan | Medium |
| Test environments | Each environment deploys a full set of functions | 100-400 yuan | High |
During a cost audit at an e-commerce platform, I found a function with 500K daily invocations: function call costs were only 350 yuan/month, but CloudWatch log costs hit 1,200 yuan/month—because each request wrote 3 log entries averaging 2KB each, generating 3GB of log data daily, and CloudWatch charges 0.5 yuan/GB for storage.
Governance actions:
- Set log retention to 7 days (production), 1 day (test environments)
- Change DEBUG logs to sampled output (10:1 sampling ratio)
- Structured logs, reduce redundant fields
After governance, log costs dropped from 1,200 yuan/month to 180 yuan/month.
For more on cloud cost governance, see Related: SRE Perspective on FinOps: Cloud Cost Visibility and Optimization Strategies, which covers cloud cost visibility building and optimization strategy systems.
3. Observability Blind Spots: Why Traditional Monitoring Completely Fails
3.1 Three Failures of Traditional Monitoring
Porting traditional server monitoring to Serverless reveals three complete failures:
IP and hostname failure: Function instances have no fixed IP—every cold start is a new instance. IP-based alerting rules and host-dimension dashboards are useless. You can’t say “alert if this IP’s CPU exceeds 80%” because a function instance’s average lifetime might be 30 seconds.
Process-level metrics failure: Serverless platforms don’t expose underlying CPU, memory, or disk I/O process-level metrics. You can’t access function instance /proc info, can’t use Node Exporter. You only get platform-provided aggregate metrics: invocation count, error rate, execution duration, cold start count.
Log aggregation failure: On traditional servers, logs go to local files and get collected by Filebeat/Vector. Serverless logs go through platform APIs (AWS CloudWatch, Alibaba Cloud SLS). When instances are destroyed, local data is gone. If you didn’t proactively log in code, you have nothing to investigate after a failure.
3.2 The Serverless Observability Trio
To address these failures, Serverless observability requires rebuilding three components:
Structured logging: Every log entry must include request_id, function_name, timestamp, duration, status. These are the minimum set for troubleshooting. Don’t use print() or console.log() for unstructured text—use the platform’s structured logging API.
// Go example: structured log output
func Handler(event Event) (Response, error) {
start := time.Now()
requestID := event.RequestContext.RequestID
log.Printf(`{"request_id":"%s","function":"search","action":"start","ts":"%s"}`,
requestID, start.Format(time.RFC3339))
result, err := doSearch(event)
duration := time.Since(start).Milliseconds()
status := "success"
if err != nil {
status = "error"
}
log.Printf(`{"request_id":"%s","function":"search","action":"end","duration_ms":%d,"status":"%s","error":"%v"}`,
requestID, duration, status, err)
return Response{Result: result}, err
}
Distributed tracing: Chained function calls are key to finding performance bottlenecks. Use AWS X-Ray or OpenTelemetry to tag each request with a trace_id that spans all function calls.
In the ride-hailing project, the search API went through 4 functions. Without distributed tracing, diagnosing “why is P99 2 seconds” meant checking each function’s logs individually—2 hours. With X-Ray, the Trace graph directly showed the third function (result sorting) taking 1.5 seconds. Diagnosis time dropped to 5 minutes.
Custom metrics: Platform default metrics aren’t enough. You need custom business metrics like “search result count,” “cache hit rate,” “degradation trigger count.” Use CloudWatch Custom Metrics or Prometheus + Gateway.
For systematic observability building, see Related: Monitoring Data Governance: From Metrics Explosion to Precision Observability, which covers metric naming conventions and alert noise reduction.
3.3 Controlling the Cost of Observability Itself
Observability infrastructure itself costs money. A function with 1 million daily invocations outputting 3 logs + 1 Trace + 5 custom metrics per call can have observability costs matching or exceeding function invocation costs.
My governance approach:
- Log tiering: ERROR logs full output, INFO logs 10% sampled, DEBUG logs disabled by default
- Trace sampling: Don’t use 100% tracing—5% sampling is sufficient. The ride-hailing project dropped from 100% to 5% sampling, cutting Trace costs by 95% with minimal diagnostic capability loss
- Metric aggregation: Don’t report metrics per request—aggregate locally in the function, batch-report every minute
4. Security and Permissions: IAM Policy Explosion Is a Ticking Bomb
4.1 The Practical Dilemma of Least Privilege
Serverless security is based on IAM policies—each function has its own execution role, accessing only resources allowed by policy. Perfect in theory, chaotic in practice.
Real situation at an e-commerce platform: In 3 months, the function count grew from 8 to 60, each with its own IAM role. Developers copy-pasted existing function policy templates for convenience, resulting in a pile of functions with permissions they shouldn’t have—a function that only needed to read S3 had DynamoDB read/write, SQS send, and even Lambda invoke permissions.
Risk: If this function is compromised (via SSRF or code injection), the attacker can use its IAM role to access DynamoDB, send SQS messages, invoke other Lambda functions—horizontal movement scope is massive.
4.2 IAM Policy Governance
| Governance Action | Approach | Tools |
|---|---|---|
| Policy audit | Periodically scan all function IAM roles, list permissions | Cloud platform API + custom scripts |
| Permission convergence | Remove unnecessary permissions, keep only minimum required | Manual + approval workflow |
| Policy templates | Preset templates by function type (read-only, read-write, compute) | IAM Policy Template |
| Access analysis | Use platform Access Analyzer to detect over-permissioned roles | Cloud platform native tools |
In the ride-hailing project, an IAM audit found 42 of 60 functions had excessive permissions. After convergence, total policy entries dropped from 380 to 95. More importantly, the security blast radius shrank significantly—even if one function is compromised, the resources an attacker can laterally access are very limited.
4.3 Security Boundaries for Inter-Function Calls
When multiple functions call each other, security boundaries are easily overlooked. Common issues:
- Internal calls skip authentication: Function A calls Function B, assuming “internal network is secure,” without adding auth to B. In reality, other services in the same VPC can also call B.
- Shared database connections: Multiple functions use the same database account. If one function is compromised, it can read/write all tables.
- Tokens hardcoded in environment variables: Function env vars store database passwords and API keys. Although cloud platforms encrypt storage, function logs might accidentally print them.
My recommendation: Function-to-function calls should go through API Gateway with authentication, not direct internal calls. Database accounts should be per-function, each accessing only its own tables. Sensitive credentials should use cloud platform Secrets Manager (AWS Secrets Manager / Alibaba Cloud KMS), not environment variables.
5. Selection Decision Tree: When to Use Serverless and When to Avoid It
5.1 Scenarios Suited for Serverless
Based on multiple project experiences, here are the scenarios where Serverless truly shines:
| Scenario | Characteristics | Why It Fits | Real Case |
|---|---|---|---|
| Low-frequency APIs | < 1,000 daily invocations | ECS idle costs are high; pay-per-use advantage is clear | Internal management APIs, report exports |
| Event-driven | Triggered by events, not continuously running | Natural fit for Serverless event model | File upload triggers processing, scheduled tasks |
| Bursty traffic | Large traffic swings, unpredictable peaks | Auto-scaling without capacity planning | Marketing pages, flash sale assistants |
| Batch processing | Short compute tasks, parallelizable | Pay by execution duration, release when done | Image thumbnail generation, log ETL |
| Webhook handling | External callbacks, irregular frequency | Low-frequency high-burst, pay-per-use is ideal | Payment callbacks, CI/CD triggers |
5.2 Scenarios Not Suited for Serverless
| Scenario | Why Not | Alternative |
|---|---|---|
| Long-connection services | Function max execution time is limited (typically 15 min) | ECS + load balancer |
| High-frequency core APIs | > 1M daily invocations, cost may exceed ECS | ECS + auto-scaling |
| Latency-sensitive < 50ms | Cold start can’t be eliminated, P99 not guaranteed | ECS persistent service |
| Stateful services | Functions are stateless, state management is complex | ECS + Redis |
| Heavy compute tasks | CPU-intensive tasks have long duration, high cost | GPU instances or batch compute |
5.3 Selection Decision Tree
┌─ Daily invocations < 1,000?
│ ├─ Yes → ✅ Serverless
│ └─ No → ┌─ Latency requirement < 50ms?
│ │ ├─ Yes → ❌ ECS persistent
│ │ └─ No → ┌─ Burst traffic > 10x?
│ │ │ ├─ Yes → ✅ Serverless + provisioned concurrency
Daily invocations │ │ └─ No → ┌─ Monthly cost comparison
│ │ │ ├─ Serverless < ECS × 0.7 → ✅ Serverless
│ │ └─ Serverless > ECS × 0.7 → ❌ ECS
└─ Stateful? → ❌ ECS
Core decision criterion: Do a monthly cost comparison. Estimate Serverless monthly cost using daily_invocations × avg_duration × memory_config, compare with equivalent ECS solution. If Serverless monthly cost is below 70% of ECS, use Serverless; above 70%, use ECS. This 70% threshold comes from my field experience—Serverless hidden costs (logs, observability, debugging) run about 30-40% of explicit costs, so 70% on paper is the real break-even point.
For more on cloud platform selection, see Related: Don’t Get Fooled by Multi-Cloud: Architecture Decisions and Lessons from Vendor Lock-in to Cross-Cloud Disaster Recovery, which covers vendor lock-in issues and disaster recovery design in multi-cloud architectures.
6. Serverless Deployment and Version Management Engineering Practices
6.1 Don’t Deploy Manually from the Console
I’ve seen the same problem across multiple teams: developers manually create functions, upload code packages, and configure triggers through the cloud platform console. One environment is manageable, but with multiple environments (dev/test/staging/prod), it becomes chaos—nobody can tell what differs between production and test function configurations.
Correct approach: Infrastructure as Code. Use Serverless Framework, AWS SAM, or Terraform to manage function definitions.
# serverless.yml example
service: search-api
frameworkVersion: '3'
provider:
name: aliyun
runtime: go1
memorySize: 512
timeout: 30
logRetentionDays: 7 # 7-day log retention, controls storage cost
functions:
search:
handler: bin/search
events:
- http:
path: /search
method: get
cors: true
environment:
DB_HOST: ${env:DB_HOST}
REDIS_HOST: ${env:REDIS_HOST}
provisionedConcurrency: 5 # Core function provisioned concurrency
suggest:
handler: bin/suggest
events:
- http:
path: /suggest
method: get
memorySize: 256 # Non-core function reduced memory
6.2 Versioning and Traffic Shifting
Serverless version management has a unique advantage: multiple versions can be deployed simultaneously, with traffic shifting via aliases. This is simpler than traditional blue/green or canary deployments.
| Release Strategy | Implementation | Use Case |
|---|---|---|
| Full release | Alias points 100% to new version | Low-risk changes |
| Canary | Alias 10% → new version, 90% → old version | Risky changes |
| Linear rollout | Alias gradually 10% → 25% → 50% → 100% | Core API changes |
| Quick rollback | Alias switches back to old version | Production incidents |
Pitfall warning: Version switching only changes code—it doesn’t switch environment variables or IAM roles. If the new version needs new env vars or permissions, update the config before publishing the version. I hit this in the ride-hailing project—new version code needed to read a new DynamoDB table, but the IAM role wasn’t updated. After version switch, all requests got permission errors. Because it was a canary release, only 10% of users were affected, but it took 20 minutes to diagnose the IAM issue.
6.3 CI/CD Pipeline Design
Serverless CI/CD is simpler than traditional applications, but has its own pitfalls:
#!/bin/bash
# Serverless CI/CD pipeline core steps
# 1. Unit tests
go test ./... -coverage
# 2. Build (Go cross-compile for Linux binary)
GOOS=linux GOARCH=amd64 go build -o bin/search cmd/search/main.go
# 3. Deploy to test environment
serverless deploy --stage test
# 4. Integration test (invoke test environment function)
serverless invoke --stage test --function search --data '{"keyword":"test"}'
# 5. Deploy to production (canary)
serverless deploy --stage prod --concurrent 10
Key points:
- Separate build and deploy—compile binary first, then deploy. Avoid installing dependencies in CI environment
- Run integration tests after test environment deployment—invoke the function directly to verify, not just check deployment success
- Use canary for production—shift 10% traffic first, observe 5 minutes for errors, then full rollout
7. Production Checklist
Before every Serverless function goes live, run through this checklist:
7.1 Performance and Cost
- Cold start P99 < 500ms (core API < 200ms)
- Deployment package < 50MB (Go < 15MB)
- Memory configuration stress-tested, not guessed
- Execution timeout set (default 30s, adjust per business)
- Concurrency limit set (prevent traffic spikes from burning money)
- Log retention ≤ 7 days (test env ≤ 1 day)
7.2 Observability
- Structured logs (with request_id)
- Distributed tracing (Trace sampling ≥ 5%)
- Custom metrics (invocation success rate, key business metrics)
- Alert rules (error rate > 1%, P99 > threshold, cold start ratio > 30%)
- Dashboard (invocations, latency, errors, cost)
7.3 Security
- IAM policies least privilege (no excessive permissions)
- Sensitive credentials in Secrets Manager, not env vars
- Inter-function calls authenticated, no naked calls
- Dependencies scanned for known vulnerabilities (regular scans)
- Logs don’t output sensitive info (passwords, tokens, user data)
7.4 Deployment and Operations
- Function definitions managed by IaC (serverless.yml / Terraform)
- CI/CD pipeline includes integration tests
- Production deployment uses canary or gradual rollout
- Rollback plan verified (Alias switch < 30 seconds)
- Environment variables consistent across environments (same keys, different values)
8. Anti-Patterns: Pitfalls I’ve Stepped On So You Don’t Have To
Anti-Pattern One: Splitting a Monolith into 50 Micro-Functions
In the early days of a ride-hailing project, the team split a Spring Boot monolith into 50 Lambda functions, one per API endpoint. Results:
- Deployment complexity exploded: 50 functions × 4 environments = 200 deployment units
- Call chains too long: one request went through 6-8 functions, latency accumulated
- Cost multiplier effect: one request = 6-8 function invocations
- Operations difficulty doubled: monitoring, alerting, and logs for 50 functions scattered everywhere
Correct approach: Aggregate by business domain—one function handles all operations in a domain. 50 functions merged to 8, costs dropped 65%, deployment complexity dropped 80%.
Anti-Pattern Two: Running Scheduled Batch Jobs Without Timeout
An e-commerce platform used a function to run daily data sync at midnight. As data volume grew, execution time increased from 5 minutes to 20 minutes, but the function timeout was set to 15 minutes. The task was forcibly interrupted at 15 minutes, syncing only half the data. Worse, there was no retry logic—the next day’s sync continued based on incomplete data, and data drift grew daily.
Correct approach: Set timeout to 2x estimated execution time, add idempotent retry logic, use Step Functions for multi-step task orchestration.
Anti-Pattern Three: Writing Files to /tmp and Expecting Them to Persist
Function /tmp directories disappear when instances are reclaimed. A developer cached config files in /tmp, expecting to read them on the next call to save loading time. During the day, high traffic kept instances alive and the cache worked. After traffic dropped at night and instances were reclaimed, the first batch of requests the next day all failed because cache files were gone.
Correct approach: Use Redis or the platform’s caching layer—never rely on the local filesystem.
Summary
Serverless isn’t “no-ops”—it’s “different ops.” Traditional operations manages servers, networks, and operating systems. Serverless operations manages cold starts, costs, observability, and security policies. The battlefield has changed shape, but the essence of the fight remains the same: keep systems stable, control costs, and diagnose failures fast.
Back to my field data from the ride-hailing project: core API cold start optimized from 4.8 seconds to 200ms, achieved through runtime selection + code slimming + provisioned concurrency. Monthly cost dropped from 3,000 yuan (ECS) to 170 yuan (Serverless, including 120 yuan provisioned concurrency + 50 yuan invocation)—a 94% savings. But observability added another 80 yuan/month (logs + Trace + metrics), bringing the actual total to 250 yuan. Still 92% cheaper than ECS, but not as dramatic as the “50 yuan” headline.
If you’re considering migrating to Serverless, my advice:
- Do a cost estimate first—use real invocation volumes and execution times to estimate monthly costs, compare with ECS. Don’t be seduced by “pay-per-use”
- Start with low-frequency scenarios—internal management APIs, webhook handling carry the lowest risk. Validate before migrating core business
- Observability first—design your logging, tracing, and metrics approach before going Serverless. Otherwise, when something breaks, you’re flying blind
- IAM governance from day one—enforce least privilege from the start. Don’t wait until function count explodes to converge permissions—you won’t know who added what
Serverless is a good tool, but it’s not a silver bullet. Use it in the right scenario and it saves money and effort. Use it in the wrong scenario and your bills and incidents will explode simultaneously.
References & Acknowledgments
The following resources were referenced during the writing of this article. Thanks to the original authors for their contributions:
- Serverless 2.0 in Practice: When SnapStart, Firecracker, and WASI All Land — Programmer Eggplant, deep analysis of Serverless cold start’s four stages and optimization approaches across platforms
- Serverless Backend Cold Start Optimization: Full-Chain Solution — OSCHINA, production-grade cold start optimization code and configuration examples
- Three Years of Serverless: What I Finally Understood About Moving from POC to Production — NetEase, analysis of cognitive traps and cost issues in Serverless POC-to-production transitions
- Cloud Native Death Report: Serverless’s Fatal Cost Traps — CSDN, deconstructing Serverless explicit and hidden costs from a testing perspective
- Function Compute + API Gateway in Practice: Serverless from POC to Production — Alibaba Cloud Developer Community, full-chain architecture design and 5 production-level pitfall cases for FC + API Gateway
- Serverless Cold Start Optimization: Complete Performance Improvement Guide — CSDN, systematic breakdown of cold start causes and optimization strategies
- DeepSeek Serverless Cold Start Optimization Record: 7 Iterations from 1200ms to 47ms — CSDN, Go/Rust dual-language Runtime tuning parameter table and measured data
- The Cost of Serverless Decoupling: What We Lost When Infrastructure Became a Black Box — CSDN, analysis of control transfer and observability challenges from Serverless decoupling
- Serverless Architecture Pros and Cons in Enterprise Applications in 2026 — Zhongpei Weiye, evaluating Serverless’s applicability boundaries in enterprise applications in 2026
- Huawei Cloud FunctionGraph Performance Optimization Best Practices — Huawei Cloud, official function performance optimization recommendations and stress testing methods