Serverless #

Nama “serverless” adalah salah satu nama yang paling menyesatkan di dunia teknologi. Server masih ada — kamu hanya tidak mengelolanya. Yang benar-benar hilang adalah tanggung jawab untuk menyediakan, mengkonfigurasi, men-scale, dan memelihara infrastruktur server. Kamu cukup menulis fungsi, deploy, dan platform yang mengurus sisanya: scaling, availability, patching sistem operasi, dan resource allocation.

Model ini mengubah cara berpikir tentang backend: bukan lagi tentang “berapa server yang dibutuhkan” tapi tentang “fungsi apa yang perlu dieksekusi dan kapan”. Sebuah fungsi yang hanya dibutuhkan saat ada event tertentu — upload file, webhook masuk, jadwal harian — tidak perlu server yang selalu berjalan. Di serverless, kamu membayar hanya untuk eksekusi yang sebenarnya terjadi, bukan untuk kapasitas yang menunggu.

Tapi serverless bukan silver bullet. Ia memperkenalkan constraint baru — cold start, execution timeout, statelessness yang ketat, debugging yang lebih sulit — yang perlu dipahami sebelum memilihnya sebagai arsitektur.

Cara Kerja Function as a Service (FaaS) #

FaaS adalah inti dari serverless. Setiap “fungsi” adalah unit deployment yang independen — bisa ditulis dalam berbagai bahasa, di-trigger oleh berbagai event, dan di-scale secara independen.

sequenceDiagram
    participant E as Event Source
    participant P as Platform\n(AWS Lambda, GCP Functions)
    participant C as Container
    participant F as Function Code

    E->>P: Event masuk\n(HTTP request, S3 upload, SQS message)

    alt Cold Start (tidak ada container aktif)
        P->>C: Provision container baru
        C->>C: Download dan init runtime
        C->>F: Load function code
        F->>F: Inisialisasi (global scope)
        Note over C,F: Cold start: 100ms - 3 detik
    else Warm Start (container sudah ada)
        P->>C: Route ke container yang ada
        Note over C: Warm start: < 10ms
    end

    F->>F: Eksekusi handler function
    F->>E: Return response
Perbedaan model traditional server vs serverless:

  Traditional Server:
  Server selalu berjalan 24/7
  → Bayar untuk waktu server aktif, bukan hanya saat digunakan
  → Scale manual atau auto-scaling yang lambat
  → Tanggung jawab: OS update, security patch, capacity planning
  → Cocok untuk: workload yang konstan dan prediktabel

  Serverless (FaaS):
  Fungsi hanya berjalan saat ada event
  → Bayar per eksekusi (dan per GB-seconds memory yang digunakan)
  → Scale otomatis dari 0 ke ribuan concurrent dalam detik
  → Tidak ada tanggung jawab infrastruktur
  → Cocok untuk: workload yang spiky, intermittent, atau event-driven

Cold Start: Tantangan Utama Serverless #

Cold start terjadi ketika tidak ada container yang sudah “warm” untuk menangani request. Platform harus menyiapkan environment baru dari awal — ini butuh waktu yang bisa terasa oleh user jika tidak ditangani dengan baik.

Anatomi cold start:

  1. Platform provision container baru       (~50-200ms)
  2. Download dan extract deployment package (~50-500ms tergantung ukuran)
  3. Inisialisasi runtime (Node.js, Python, Java, dll)  (~50-500ms)
  4. Eksekusi kode di luar handler (global scope)       (~10-1000ms)
  5. Eksekusi handler function                          (varies)

  Total cold start:
  → Node.js/Python: biasanya 100-500ms
  → Java/C#: bisa 1-5 detik (JVM startup yang berat)
  → Go/Rust: biasanya < 100ms (binary yang compiled)

  Warm start (container yang sudah ada):
  → Langsung ke langkah 5
  → Biasanya < 10ms overhead

Strategi Mitigasi Cold Start #

// Go: padanan handler serverless
// ✗ Anti-pattern: inisialisasi mahal di dalam handler
func handler(event map[string]any, context map[string]any) {
    // Ini dieksekusi SETIAP kali handler dipanggil
    // Bahkan untuk warm invocation yang sudah punya connection
    dbClient := connectDynamoDB()  // overhead setiap call
    s3Client := connectS3()
    config := loadConfigFromS3()   // network call setiap kali!

    // ... proses event ...
}

// ✓ Benar: inisialisasi di luar handler (global scope)
// Dieksekusi SEKALI saat cold start, di-reuse di warm invocation

// Global scope — dieksekusi saat container diinisialisasi
var (
    dbClient = connectDynamoDB()
    s3Client = connectS3()
    config   map[string]any // lazy load
)

func getConfig() map[string]any {
    if config == nil {
        config = loadConfigFromS3()
    }
    return config
}

func handler(event map[string]any, context map[string]any) {
    // Handler dipanggil setiap invocation
    // Tapi dbClient dan s3Client sudah ada dari global scope
    cfg := getConfig() // hanya load pertama kali per container

    // ... proses event menggunakan client yang sudah ada ...
    return processEvent(event, cfg, dbClient)
}
// Go: teknik mitigasi cold start lainnya

// 1. Kurangi ukuran deployment package
// Semakin kecil package → download lebih cepat → cold start lebih pendek
// go.mod: hanya include yang benar-benar dibutuhkan
// Gunakan Lambda Layers untuk dependency yang jarang berubah

// 2. Provisioned Concurrency (AWS Lambda)
// Pre-warm N container sebelum ada request
// Bayar extra tapi cold start dihilangkan untuk traffic yang bisa diprediksi
// aws lambda put-provisioned-concurrency-config \
//     --function-name my-function \
//     --qualifier LIVE \
//     --provisioned-concurrent-executions 5

// 3. Scheduled ping untuk keep-alive (workaround murah)
// CloudWatch Event setiap 5 menit → invoke Lambda
// → Container tetap warm
// Tapi ini tidak scale untuk fungsi yang butuh banyak concurrent instance

// 4. Pilih runtime dengan cold start lebih cepat
// Cold start ranking (dari tercepat):
// Go/Rust > Node.js ≈ Python > C# > Java
// Untuk fungsi yang latency-sensitive, hindari Java tanpa GraalVM native image

Stateless by Design #

Serverless memaksa statelessness yang ketat — tidak ada jaminan bahwa invocation berikutnya akan menggunakan container yang sama. Setiap invocation harus memperlakukan state lokal sebagai sementara.

// Go: padanan serverless stateless
// ✗ Anti-pattern: menyimpan state di memory container

// State ini tidak konsisten antar invocation!
var requestCounter int // container A: 5, container B: 3, container C: 0

func handler(event map[string]any, context map[string]any) {
    requestCounter++
    // requestCounter berbeda di setiap container
    // Tidak bisa diandalkan untuk apapun
}

// ✓ Benar: semua state di external storage
// Untuk counter yang akurat
var dynamodb = connectDynamoDB()

func incrementRequestCounter(functionName string) int {
    // Atomic ADD pada counter DynamoDB
    resp := dynamodb.UpdateItem(map[string]any{
        "TableName": "counters",
        "Key":       map[string]any{"name": map[string]any{"S": functionName}},
        "UpdateExpression":          "ADD #count :one",
        "ExpressionAttributeNames":  map[string]any{"#count": "count"},
        "ExpressionAttributeValues": map[string]any{":one": map[string]any{"N": "1"}},
        "ReturnValues":              "UPDATED_NEW",
    })
    return int(resp["Attributes"].(map[string]any)["count"].(map[string]any)["N"].(float64))
}

// Untuk session/cache
var redisClient = redis.NewClient(&redis.Options{
    Addr: os.Getenv("REDIS_HOST") + ":6379",
})

func handler(event map[string]any, context map[string]any) {
    // State selalu dari external storage
    counter := incrementRequestCounter("my-function")
    session, _ := redisClient.Get("session:" + event["session_id"].(string)).Result()
    // ... proses ...
}
Di mana menyimpan state di serverless:

  Temporary (dalam satu invocation saja):
  → Local variable di function
  → /tmp filesystem (max 512MB di AWS Lambda, tapi hilang saat container recycle)

  Persistent state:
  → Database (DynamoDB, RDS via connection pooling)
  → Cache (ElastiCache/Redis via koneksi yang di-reuse di global scope)
  → Object storage (S3 untuk file)
  → Message queue (SQS, SNS untuk komunikasi async)

  State yang harus dihindari:
  → File lokal yang diharapkan persist antar invocation
  → In-memory cache yang diharapkan konsisten antar container
  → Connection yang tidak di-cleanup (connection leak di database)

Event-Driven Architecture di Serverless #

Serverless bersinar paling terang dalam arsitektur event-driven — fungsi yang dipicu oleh event dari berbagai sumber.

// Go: contoh Lambda function yang menangani berbagai trigger
// 1. HTTP request via API Gateway
func apiHandler(event map[string]any, context map[string]any) map[string]any {
    // Handler untuk HTTP request
    path := event["path"]
    method := event["httpMethod"]
    body := json.Unmarshal([]byte(event["body"].(string)))

    if method == "POST" && path == "/orders" {
        return createOrder(body)
    }

    return map[string]any{
        "statusCode": 404,
        "body":       `{"error": "Not found"}`,
    }
}

// 2. S3 event — ketika file di-upload
func s3EventHandler(event map[string]any, context map[string]any) {
    // Proses file yang baru di-upload
    for _, record := range event["Records"].([]any) {
        rec := record.(map[string]any)
        bucket := rec["s3"].(map[string]any)["bucket"].(map[string]any)["name"]
        key := rec["s3"].(map[string]any)["object"].(map[string]any)["key"]

        // Process file: resize gambar, parse CSV, scan virus, dll
        processUploadedFile(bucket, key)
    }
}

// 3. SQS queue — proses pesan dari antrian
func sqsHandler(event map[string]any, context map[string]any) map[string]any {
    // Proses pesan dari SQS queue
    var failedMessages []map[string]any

    for _, record := range event["Records"].([]any) {
        rec := record.(map[string]any)
        messageID := rec["messageId"]
        body := json.Unmarshal([]byte(rec["body"].(string)))

        if err := processMessage(body); err != nil {
            // Tandai sebagai gagal — akan kembali ke queue atau ke DLQ
            failedMessages = append(failedMessages, map[string]any{"itemIdentifier": messageID})
            log.Printf("Failed to process message %v: %v", messageID, err)
        }
    }

    // Return batch item failures — hanya yang gagal yang di-retry
    return map[string]any{"batchItemFailures": failedMessages}
}

// 4. CloudWatch Events (scheduled)
func scheduledHandler(event map[string]any, context map[string]any) map[string]any {
    // Dijalankan setiap hari jam 08:00
    sendDailyReport()
    cleanupExpiredSessions()
    return map[string]any{"status": "completed"}
}

// 5. DynamoDB Stream — react terhadap perubahan database
func dynamodbStreamHandler(event map[string]any, context map[string]any) {
    // Trigger ketika ada record baru atau berubah di DynamoDB
    for _, record := range event["Records"].([]any) {
        rec := record.(map[string]any)
        if rec["eventName"] == "INSERT" {
            newItem := rec["dynamodb"].(map[string]any)["NewImage"].(map[string]any)
            userID := newItem["user_id"].(map[string]any)["S"]
            sendWelcomeEmail(userID)
        }
    }
}

Batasan Serverless yang Perlu Dipahami #

Serverless bukan tanpa keterbatasan. Memahami ini penting sebelum memilih serverless untuk use case tertentu.

Batasan utama AWS Lambda (angka bisa berbeda per provider):

  Execution timeout:
  → Maksimum 15 menit per invocation
  → Tidak cocok untuk long-running process (video encoding, ML training)

  Memory:
  → 128MB sampai 10GB (lebih banyak memory = lebih banyak CPU)
  → Tidak ada GPU (untuk workload ML/AI)

  Storage:
  → /tmp: 512MB (bisa dikonfigurasi sampai 10GB)
  → Bukan persistent storage

  Concurrent executions:
  → Default limit: 1000 concurrent per region (bisa dinaikkan)
  → Penting untuk dipahami untuk workload burst

  Deployment package:
  → 50MB compressed, 250MB uncompressed
  → Layer membantu tapi ada limit total juga

  Networking:
  → Cold start lebih lama jika di dalam VPC (ENI provisioning)
  → Perlu NAT Gateway untuk akses internet dari VPC

  Payload:
  → API Gateway: 10MB request/response
  → Synchronous invocation: 6MB payload
  → SQS: 256KB per message
Kapan serverless TIDAK cocok:

  ✗ Long-running workload (> 15 menit)
    → ETL yang memproses dataset besar
    → Video transcoding
    → ML model training
    → Solusi: ECS Fargate, EC2, AWS Batch

  ✗ Latency-sensitive yang tidak toleran cold start
    → Gaming backend real-time
    → HFT (High Frequency Trading)
    → Solusi: Provisioned Concurrency atau traditional server

  ✗ Heavy stateful workload
    → WebSocket connection yang persist lama
    → Stateful streaming (biasanya butuh connection persist)
    → Solusi: EC2/ECS dengan WebSocket server

  ✗ Workload yang butuh GPU
    → Deep learning inference
    → Solusi: EC2 GPU instances, SageMaker

  ✗ Very high constant traffic
    → Jika selalu 1000+ concurrent executions 24/7
    → Cost bisa lebih mahal dari EC2
    → Hitung dahulu sebelum memutuskan

Cost Model Serverless #

Cost model serverless berbeda fundamental dari server tradisional — ini bisa sangat hemat untuk beberapa use case, tapi juga bisa lebih mahal dari ekspektasi.

AWS Lambda pricing (2025):

  Compute:
  → $0.0000166667 per GB-second
  → Contoh: fungsi 256MB yang berjalan 1 detik
    = 0.25 GB × 1 detik × $0.0000166667 = $0.0000041667

  Request:
  → $0.20 per 1 juta request

  Free tier (per bulan):
  → 1 juta request gratis
  → 400.000 GB-seconds gratis

  Kalkulasi biaya:

  Skenario A — Low traffic:
  100 request/hari × 30 hari = 3.000 request/bulan
  Masih dalam free tier → GRATIS
  vs EC2 t3.micro: ~$8.5/bulan

  Skenario B — Medium traffic:
  100.000 request/hari × 30 hari = 3.000.000 request/bulan
  Fungsi 256MB, rata-rata 100ms
  Compute: 3M × 0.25GB × 0.1s = 75.000 GB-s × $0.0000166667 = $1.25
  Request: (3M - 1M) × $0.2/1M = $0.40
  Total: ~$1.65/bulan vs EC2 t3.micro $8.5/bulan → HEMAT

  Skenario C — High traffic:
  10 juta request/hari = 300 juta request/bulan
  Fungsi 512MB, rata-rata 200ms
  Compute: 300M × 0.5GB × 0.2s = 30M GB-s × $0.0000166667 = $500
  Request: 300M × $0.2/1M = $60
  Total: ~$560/bulan vs fleet EC2 yang bisa jadi lebih murah
  → Perlu kalkulasi lebih detail sebelum memutuskan

Observability di Serverless #

Debugging dan monitoring di serverless lebih menantang dari traditional server — tidak ada SSH ke server, log tersebar per fungsi, dan distributed tracing jadi lebih penting.

// Go: best practices untuk observability di Lambda
// Structured logging — lebih mudah di-parse di CloudWatch Logs Insights
// logEvent mencatat event terstruktur untuk observability
func logEvent(eventType string, fields map[string]any) {
    entry := map[string]any{
        "event_type": eventType,
        "timestamp":  time.Now().Unix(),
    }
    for k, v := range fields {
        entry[k] = v
    }
    logger.Info(entry)
}

// tracedHandler membungkus handler dengan tracing dan error handling
// (padanan decorator Python di Go)
func tracedHandler(handler func(map[string]any, map[string]any) map[string]any) func(map[string]any, map[string]any) map[string]any {
    return func(event map[string]any, context map[string]any) map[string]any {
        start := time.Now()

        // Log invocation metadata
        logEvent("invocation_start", map[string]any{
            "function_name":     context["function_name"],
            "request_id":        context["aws_request_id"],
            "remaining_time_ms": context["get_remaining_time_in_millis"],
            "memory_limit_mb":   context["memory_limit_in_mb"],
        })

        result := handler(event, context)

        durationMs := float64(time.Since(start).Microseconds()) / 1000
        logEvent("invocation_success", map[string]any{
            "duration_ms": math.Round(durationMs*100) / 100,
            "request_id":  context["aws_request_id"],
        })

        return result
    }
}

// handler adalah fungsi handler utama
var handler = tracedHandler(func(event map[string]any, context map[string]any) map[string]any {
    // Fungsi handler utama
    return nil
})
// Go: AWS X-Ray untuk distributed tracing
// Patch semua AWS SDK call untuk auto-tracing
// (xray-go meng-instrumentasi HTTP client dan AWS SDK secara otomatis)
func init() {
    xray.Configure(xray.Config{DaemonAddr: "xray:2000"})
}

// processOrder muncul sebagai subsegment di X-Ray trace
func processOrder(orderData map[string]any) map[string]any {
    ctx, seg := xray.BeginSubsegment(context.Background(), "process_order")
    defer seg.Close()

    // validate_order
    ctx, sub := xray.BeginSubsegment(ctx, "validate_order")
    sub.AddAnnotation("order_id", orderData["id"])
    validateOrder(orderData)
    sub.Close()

    // save_to_database
    ctx, sub2 := xray.BeginSubsegment(ctx, "save_to_database")
    order := saveOrder(orderData)
    sub2.AddMetadata("saved_order", order)
    sub2.Close()

    return order
}

Infrastructure as Code untuk Serverless #

# serverless.yml — menggunakan Serverless Framework

service: my-app

provider:
  name: aws
  runtime: python3.12
  region: ap-southeast-1
  memorySize: 256  # MB, default untuk semua fungsi
  timeout: 30      # detik, default
  environment:
    APP_ENV: ${opt:stage, 'development'}
    DATABASE_URL: ${env:DATABASE_URL}

  # IAM role — least privilege
  iam:
    role:
      statements:
        - Effect: Allow
          Action:
            - dynamodb:GetItem
            - dynamodb:PutItem
            - dynamodb:UpdateItem
          Resource: !GetAtt OrdersTable.Arn

functions:
  # HTTP API
  api:
    handler: src/api.handler
    events:
      - httpApi:
          path: /orders
          method: POST
      - httpApi:
          path: /orders/{id}
          method: GET
    memorySize: 512  # override default untuk fungsi yang butuh lebih

  # Scheduled job
  dailyReport:
    handler: src/reports.daily_handler
    timeout: 300  # 5 menit untuk proses report
    events:
      - schedule:
          rate: cron(0 8 * * ? *)  # Setiap hari jam 08:00 UTC
          enabled: true

  # SQS consumer
  orderProcessor:
    handler: src/processor.sqs_handler
    reservedConcurrency: 10  # Limit concurrency untuk melindungi downstream
    events:
      - sqs:
          arn: !GetAtt OrderQueue.Arn
          batchSize: 10
          functionResponseType: ReportBatchItemFailures

resources:
  Resources:
    OrdersTable:
      Type: AWS::DynamoDB::Table
      Properties:
        TableName: ${self:service}-${opt:stage}-orders
        BillingMode: PAY_PER_REQUEST  # On-demand — no capacity planning
        AttributeDefinitions:
          - AttributeName: id
            AttributeType: S
        KeySchema:
          - AttributeName: id
            KeyType: HASH

    OrderQueue:
      Type: AWS::SQS::Queue
      Properties:
        QueueName: ${self:service}-${opt:stage}-orders
        VisibilityTimeout: 60
        RedrivePolicy:
          deadLetterTargetArn: !GetAtt OrderDLQ.Arn
          maxReceiveCount: 3  # Retry 3 kali sebelum ke DLQ

    OrderDLQ:
      Type: AWS::SQS::Queue
      Properties:
        QueueName: ${self:service}-${opt:stage}-orders-dlq
        MessageRetentionPeriod: 1209600  # 14 hari

Anti-Pattern yang Harus Dihindari #

// Go: anti-pattern
// ✗ Anti-pattern 1: inisialisasi mahal di dalam handler
func handler(event map[string]any, context map[string]any) {
    db := connectDatabase()  // connection baru setiap invocation!
    config := loadFromS3()   // network call setiap invocation!
}

// ✓ Solusi: global scope untuk inisialisasi yang bisa di-reuse
var (
    db     = connectDatabase() // sekali per container
    config = loadFromS3()      // sekali per container
)

func handler(event map[string]any, context map[string]any) {
    return process(event, db, config)  // reuse dari global
}

// ✗ Anti-pattern 2: menyimpan state di /tmp dan mengandalkannya persist
func handler(event map[string]any, context map[string]any) {
    os.WriteFile("/tmp/data.json", data, 0644)
    // Invocation berikutnya mungkin container berbeda — file tidak ada!
}

// ✓ Solusi: gunakan S3 atau database untuk persistent state

// ✗ Anti-pattern 3: timeout yang terlalu pendek tanpa retry logic
// Default Lambda timeout: 3 detik
// Jika downstream service lambat → timeout sebelum selesai
// ✓ Solusi: set timeout yang realistic + implementasi retry di SQS

// ✗ Anti-pattern 4: tidak ada Dead Letter Queue
// Message gagal diproses → hilang begitu saja
// ✓ Solusi: selalu konfigurasi DLQ untuk SQS consumer dan async invocation

// ✗ Anti-pattern 5: concurrency tidak di-limit untuk downstream protection
// Lambda scale ke ribuan concurrent → flood database atau third-party API
// ✓ Solusi: set reservedConcurrency atau gunakan SQS untuk throttling alami

// ✗ Anti-pattern 6: fungsi yang melakukan terlalu banyak (monolithic Lambda)
// Satu fungsi handle semua: auth + business logic + data access + notification
// ✓ Solusi: single responsibility per fungsi, composisi via event/message

Checklist Serverless #

DESAIN FUNGSI:
  □ Setiap fungsi punya satu tanggung jawab yang jelas
  □ Handler function tipis — logika bisnis di module terpisah
  □ Inisialisasi mahal di global scope, bukan di dalam handler
  □ Tidak ada state yang disimpan di memory atau /tmp untuk persist antar invocation

COLD START:
  □ Deployment package sekecil mungkin
  □ Dependency yang tidak perlu tidak di-include
  □ Runtime dipilih berdasarkan kebutuhan latency (Go/Node.js untuk latency rendah)
  □ Provisioned Concurrency dikonfigurasi untuk fungsi yang latency-critical

ERROR HANDLING:
  □ Dead Letter Queue dikonfigurasi untuk async invocation dan SQS consumer
  □ Retry logic yang tepat (exponential backoff untuk transient error)
  □ Idempotent handler — eksekusi dua kali menghasilkan hasil yang sama
  □ Partial batch failure di-handle dengan benar (reportBatchItemFailures)

KEAMANAN:
  □ IAM role dengan least privilege — hanya permission yang diperlukan
  □ Secret dari AWS Secrets Manager atau Parameter Store, bukan env var hardcode
  □ Tidak ada sensitive data di log
  □ VPC jika fungsi perlu akses resource private

OBSERVABILITY:
  □ Structured logging (JSON) untuk kemudahan query di CloudWatch Logs Insights
  □ Custom metrics untuk business event penting
  □ X-Ray tracing diaktifkan untuk distributed tracing
  □ Alert dipasang untuk error rate, duration, throttling

COST:
  □ Memory size dikonfigurasi sesuai kebutuhan (lebih banyak memory = lebih mahal)
  □ Timeout dikonfigurasi sesuai kebutuhan (bukan set ke maksimum)
  □ Reserved concurrency di-set untuk mencegah runaway cost
  □ Cost estimate dibuat sebelum go-live

OPERASIONAL:
  □ Infrastructure as Code (Serverless Framework, AWS CDK, SAM)
  □ CI/CD pipeline untuk deployment
  □ Environment terpisah (dev, staging, production)
  □ Rollback bisa dilakukan dengan cepat

Ringkasan #

  • Serverless bukan tanpa server — ia tanpa manajemen server — platform menangani provisioning, scaling, dan availability. Kamu fokus pada kode dan logika bisnis.
  • Cold start adalah trade-off utama — kontainer baru butuh waktu untuk diinisialisasi. Minimalkan dengan inisialisasi di global scope, package yang kecil, dan runtime yang tepat.
  • Stateless adalah syarat, bukan pilihan — tidak ada jaminan invocation berikutnya pakai container yang sama. Semua state harus di external storage (DynamoDB, Redis, S3).
  • Event-driven adalah konteks terbaik serverless — file upload, webhook, pesan dari queue, jadwal periodik — semua cocok. Workload yang constant dan latency-critical lebih cocok dengan traditional server.
  • Inisialisasi di global scope untuk warm invocation — connection database, konfigurasi, dan client SDK yang di-inisialisasi di luar handler function di-reuse antar invocation dalam container yang sama.
  • Dead Letter Queue adalah safety net yang wajib — pesan yang gagal diproses tidak boleh hilang begitu saja. DLQ memungkinkan investigasi dan reprocessing.
  • IAM least privilege adalah wajib — setiap fungsi hanya boleh punya akses ke resource yang benar-benar dibutuhkan. Satu fungsi dikompromikan tidak boleh bisa mengakses semua resource.
  • Idempoten adalah keharusan — Lambda bisa mengeksekusi fungsi lebih dari sekali (exactly-once delivery tidak dijamin). Handler yang idempoten aman untuk di-retry.
  • Cost model berbeda dari tradisional — sangat hemat untuk low-to-medium traffic, bisa lebih mahal dari EC2 untuk traffic sangat tinggi yang konstan. Kalkulasi selalu sebelum memutuskan.
  • Observability lebih menantang — structured logging, X-Ray tracing, dan alert yang tepat adalah wajib karena tidak ada SSH dan log tersebar di banyak fungsi.

← Sebelumnya: Configuration Manager   Berikutnya: Microservices →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact