0x55aa
← Back to Blog

EventBridge: Stop Polling and Start Reacting (Your APIs Will Thank You) āš”šŸŽÆ

•18 min read

EventBridge: Stop Polling and Start Reacting (Your APIs Will Thank You) āš”šŸŽÆ

Real talk: The first serverless backend I architected was a beautiful mess. Lambda functions polling DynamoDB every 30 seconds. SQS queues being checked by scheduled Lambdas. API calls firing every minute "just in case" something changed. My AWS bill was $380/month for a system with 50 active users. šŸ˜…

Then I discovered EventBridge. Three weeks later, the same system cost $42/month, responded instantly to changes, and I actually understood what was happening. No more polling. No more "check every X seconds." Just pure, beautiful event-driven architecture! ✨

Welcome to EventBridge - the AWS service that turns your polling nightmare into reactive bliss!

What Even Is EventBridge? (Beyond "Event Bus") šŸ¤”

EventBridge = AWS's serverless event bus - Routes events from your apps, AWS services, and SaaS apps to targets.

Think of it like: A smart postal service where mail (events) automatically gets routed to the right destination based on rules you define!

Real components:

  • Event Bus: The main highway for events
  • Rules: Traffic cops that route events to destinations
  • Targets: Where events end up (Lambda, Step Functions, SQS, etc.)
  • Event Patterns: Filters that match specific events
  • Schemas: Documentation for event structure

Why it's everywhere: When you stop polling and start reacting, everything gets faster, cheaper, and more maintainable!

Why it's confusing: Event-driven thinking is BACKWARDS from traditional programming. Instead of "check if X happened," you say "when X happens, do Y!" šŸ”„

The $380 Polling Bill: My Event-Driven Wake-Up Call šŸ’€

When architecting our e-commerce backend, I needed to process orders, send notifications, update inventory, and trigger fulfillment workflows. "Easy!" I thought.

What I naively built (polling hell):

// ORDER PROCESSOR - Runs every minute! 😱
exports.checkForNewOrders = async () => {
  const orders = await dynamodb.scan({
    TableName: 'Orders',
    FilterExpression: 'orderStatus = :new',
    ExpressionAttributeValues: { ':new': 'NEW' }
  }).promise()

  for (const order of orders.Items) {
    await processOrder(order)
    // Update status so we don't process again
    await updateOrderStatus(order.id, 'PROCESSING')
  }
}

// INVENTORY CHECKER - Runs every 30 seconds! 😱
exports.checkInventory = async () => {
  const products = await dynamodb.scan({
    TableName: 'Products',
    FilterExpression: 'stock < :threshold',
    ExpressionAttributeValues: { ':threshold': 10 }
  }).promise()

  for (const product of products.Items) {
    await sendLowStockAlert(product)
  }
}

// EMAIL QUEUE PROCESSOR - Runs every minute! 😱
exports.processEmailQueue = async () => {
  const messages = await sqs.receiveMessage({
    QueueUrl: emailQueueUrl,
    MaxNumberOfMessages: 10
  }).promise()

  for (const message of messages.Messages) {
    await sendEmail(JSON.parse(message.Body))
    await sqs.deleteMessage({
      QueueUrl: emailQueueUrl,
      ReceiptHandle: message.ReceiptHandle
    }).promise()
  }
}

// Scheduled via CloudWatch Events:
// - checkForNewOrders: Every 1 minute = 43,200 invocations/month
// - checkInventory: Every 30 seconds = 86,400 invocations/month
// - processEmailQueue: Every 1 minute = 43,200 invocations/month
// Total: 172,800 Lambda invocations/month (just for polling!)

The disaster:

  1. DynamoDB scans: 172,800 scans/month Ɨ expensive!
  2. Lambda invocations: 172,800 runs (most finding NOTHING!)
  3. Latency: Orders took 30-60 seconds to process (waiting for next poll)
  4. Waste: 95% of Lambda runs found zero new items!
  5. Bill: $380/month for a tiny system!

The breakdown:

Polling Architecture Costs:
- DynamoDB scans: 172,800 Ɨ 1KB avg = 172GB scanned/month
  Cost: 172,000 / 4 (KB per RCU) = 43,000 RCUs Ɨ $0.00013/hour Ɨ 730 = $4.08/month
- Lambda invocations: 172,800 Ɨ $0.0000002 = $0.03/month
- Lambda duration: 172,800 Ɨ 500ms Ɨ 512MB = 44,236 GB-seconds
  Cost: 44,236 Ɨ $0.0000166667 = $0.74/month
- Lambda polling mostly empty: WASTED TIME AND MONEY
- Average order processing delay: 30-60 seconds (UX suffers!)

Reality Check: Most expensive parts were:
- Scan costs: Reading 1M items to find 50 new orders
- Wasted compute: 95% of runs found nothing
- SQS polling: Charges even when queue is empty

In production, I've deployed dozens of serverless apps. The polling pattern ALWAYS becomes the bottleneck and the biggest cost! āš ļø

Enter EventBridge: The Event-Driven Revolution šŸš€

What I rebuilt with EventBridge:

// NO POLLING! Events trigger Lambda automatically! ✨

// 1. When order is created, emit event
async function createOrder(orderData) {
  const order = {
    id: generateId(),
    ...orderData,
    status: 'NEW',
    createdAt: Date.now()
  }

  // Save to DynamoDB
  await dynamodb.put({
    TableName: 'Orders',
    Item: order
  }).promise()

  // Emit event to EventBridge
  await eventbridge.putEvents({
    Entries: [{
      Source: 'ecommerce.orders',
      DetailType: 'OrderCreated',
      Detail: JSON.stringify(order),
      EventBusName: 'default'
    }]
  }).promise()

  return order
}

// 2. EventBridge routes event to Lambda (INSTANTLY!)
exports.processOrder = async (event) => {
  const order = JSON.parse(event.detail)

  console.log('Order received INSTANTLY:', order.id)

  // Process order
  await chargePayment(order)
  await reserveInventory(order)
  await notifyWarehouse(order)

  // Update status
  await updateOrderStatus(order.id, 'PROCESSING')
}

// 3. EventBridge rule (defined once, works forever!)
// NO CODE! Just configuration:
{
  "EventPattern": {
    "source": ["ecommerce.orders"],
    "detail-type": ["OrderCreated"]
  },
  "Targets": [{
    "Arn": "arn:aws:lambda:us-east-1:123456789:function:processOrder",
    "Id": "OrderProcessor"
  }]
}

The magic:

Event-Driven Architecture Results:
- Lambda invocations: ONLY when events occur (not polling!)
- With 1000 orders/month: 1000 invocations (not 43,200!)
- Latency: <100ms (instant reaction, not 30-60 seconds!)
- DynamoDB scans: ZERO! (just targeted reads)
- Cost: $0.05/month for EventBridge + $0.10 for Lambda
- Total cost reduction: $380 → $42/month (91% cheaper!)
- Response time improvement: 30-60 seconds → <100ms (99.8% faster!)

A serverless pattern that saved us: Emit events, don't poll for changes. Your architecture becomes reactive, scalable, and dirt cheap! šŸŽÆ

EventBridge Mistake #1: Still Using CloudWatch Events 🚨

The old way (CloudWatch Events - deprecated!):

# Create scheduled Lambda (old way)
aws events put-rule \
  --name check-orders \
  --schedule-expression "rate(1 minute)"

aws events put-targets \
  --rule check-orders \
  --targets "Id=1,Arn=arn:aws:lambda:..."

# Problems:
# - Limited event patterns
# - Only AWS events
# - No schema registry
# - No SaaS integration

The new way (EventBridge):

# Create event-driven rule (new way)
aws events put-rule \
  --name order-created \
  --event-bus-name default \
  --event-pattern '{
    "source": ["ecommerce.orders"],
    "detail-type": ["OrderCreated"]
  }'

aws events put-targets \
  --rule order-created \
  --targets "Id=1,Arn=arn:aws:lambda:..."

# Benefits:
# - Rich event patterns
# - Custom events
# - Schema registry
# - SaaS integrations (Stripe, Shopify, etc.)
# - Archive & replay

Why EventBridge is better:

CloudWatch Events:
āŒ Only AWS service events + scheduled
āŒ Basic pattern matching
āŒ No schema discovery
āŒ No SaaS integration

EventBridge:
āœ… Custom events from your apps!
āœ… Advanced pattern matching (content filtering!)
āœ… Schema registry (auto-documentation!)
āœ… 35+ SaaS integrations out of the box!
āœ… Archive & replay events
āœ… API destinations (webhook any HTTP endpoint!)

When architecting on AWS, I learned: CloudWatch Events is legacy. Use EventBridge for EVERYTHING! It's backwards compatible but way more powerful! šŸŽÆ

EventBridge Mistake #2: Not Using Event Patterns Effectively šŸ“‹

The lazy pattern (matches too much):

{
  "source": ["ecommerce.orders"]
}

Result: ALL order events trigger your Lambda! Created, updated, deleted, cancelled - EVERYTHING! 😱

The smart pattern (specific filtering):

{
  "source": ["ecommerce.orders"],
  "detail-type": ["OrderCreated"],
  "detail": {
    "status": ["NEW"],
    "total": [{ "numeric": [">", 100] }],
    "country": ["US", "CA"]
  }
}

Translation: Only trigger Lambda for NEW orders over $100 in US/Canada!

Advanced patterns (content-based filtering):

// Match orders with specific product categories
{
  "source": ["ecommerce.orders"],
  "detail": {
    "items": {
      "category": ["electronics"]
    }
  }
}

// Match high-value VIP customer orders
{
  "source": ["ecommerce.orders"],
  "detail": {
    "customer": {
      "tier": ["VIP", "PLATINUM"]
    },
    "total": [{ "numeric": [">=", 500] }]
  }
}

// Match orders that need fraud review
{
  "source": ["ecommerce.orders"],
  "detail": {
    "riskScore": [{ "numeric": [">", 0.7] }],
    "isFirstPurchase": [true]
  }
}

Why this matters:

Without filtering:
- 10,000 orders/month
- ALL trigger Lambda
- 10,000 invocations
- Cost: 10,000 Ɨ $0.0000002 = $0.002 (plus processing time!)
- Many invocations wasted (wrong event type)

With smart filtering:
- 10,000 orders/month
- Only 500 match pattern (high-value VIP orders)
- 500 invocations
- Cost: 500 Ɨ $0.0000002 = $0.0001
- ONLY relevant events trigger Lambda!
- 95% reduction in noise!

In production, I've deployed EventBridge rules that filter 99% of events. Only the 1% that matter trigger actions. Cost and noise both plummet! šŸ’°

EventBridge Mistake #3: Not Using Multiple Targets šŸŽÆ

The inefficient way (one target per rule):

# Rule 1: Order created → Send email
aws events put-rule --name order-email ...
aws events put-targets --rule order-email --targets Lambda:SendEmail

# Rule 2: Order created → Update inventory
aws events put-rule --name order-inventory ...
aws events put-targets --rule order-inventory --targets Lambda:UpdateInventory

# Rule 3: Order created → Notify warehouse
aws events put-rule --name order-warehouse ...
aws events put-targets --rule order-warehouse --targets Lambda:NotifyWarehouse

# Problems:
# - 3 rules for same event!
# - Hard to maintain
# - Scattered logic

The efficient way (one rule, multiple targets):

# ONE rule with multiple targets!
aws events put-rule \
  --name order-created \
  --event-pattern '{
    "source": ["ecommerce.orders"],
    "detail-type": ["OrderCreated"]
  }'

aws events put-targets \
  --rule order-created \
  --targets \
    "Id=1,Arn=arn:aws:lambda:...:function:SendEmail" \
    "Id=2,Arn=arn:aws:lambda:...:function:UpdateInventory" \
    "Id=3,Arn=arn:aws:lambda:...:function:NotifyWarehouse" \
    "Id=4,Arn=arn:aws:states:...:stateMachine:OrderWorkflow" \
    "Id=5,Arn=arn:aws:sqs:...:order-archive-queue"

# ONE event → 5 targets triggered IN PARALLEL! ✨

What happens:

Order created (single event)
    ↓
EventBridge (fanout to all targets)
    ā”œā”€ā”€ā†’ Lambda: SendEmail (email notification)
    ā”œā”€ā”€ā†’ Lambda: UpdateInventory (reserve stock)
    ā”œā”€ā”€ā†’ Lambda: NotifyWarehouse (fulfillment)
    ā”œā”€ā”€ā†’ Step Functions: OrderWorkflow (complex orchestration)
    └──→ SQS: order-archive-queue (audit log)

ALL TRIGGERED SIMULTANEOUSLY! ⚔

Benefits:

  • āœ… One event → Multiple reactions
  • āœ… Targets run in PARALLEL (not sequential)
  • āœ… Decoupled services (each target is independent)
  • āœ… Easy to add/remove targets
  • āœ… One rule to maintain

Real example from production:

// When user signs up (1 event)
{
  "source": "users.auth",
  "detail-type": "UserSignedUp",
  "detail": {
    "userId": "12345",
    "email": "[email protected]",
    "plan": "premium"
  }
}

// Triggers (6 targets simultaneously):
1. Lambda → Send welcome email
2. Lambda → Create user profile in DynamoDB
3. Lambda → Add to email marketing list
4. Step Functions → Onboarding workflow (multi-step)
5. SQS → Analytics queue
6. EventBridge API Destination → Webhook to Slack

// All happen automatically, in parallel, instantly! šŸš€

EventBridge Mistake #4: Not Using Schema Registry šŸ“š

The problem:

// You emit an event:
await eventbridge.putEvents({
  Entries: [{
    Source: 'ecommerce.orders',
    DetailType: 'OrderCreated',
    Detail: JSON.stringify({
      orderId: '12345',  // Wait, was it orderId or order_id?
      userId: 'abc',     // Or user_id?
      total: 99.99,      // Number or string?
      items: [...]       // What's the structure?
    })
  }]
}).promise()

// Another developer consuming the event:
exports.handler = async (event) => {
  const order = event.detail

  // Is it orderId, order_id, OrderId, or id?
  const orderId = order.orderId ?? order.order_id ?? order.id

  // 😭 No documentation! Just guessing!
}

The solution - Schema Registry:

# EventBridge auto-generates schema from events!
aws schemas create-schema \
  --schema-name ecommerce.orders@OrderCreated \
  --type OpenApi3 \
  --content '{
    "openapi": "3.0.0",
    "info": {
      "title": "OrderCreated",
      "version": "1.0.0"
    },
    "components": {
      "schemas": {
        "OrderCreated": {
          "type": "object",
          "properties": {
            "orderId": { "type": "string" },
            "userId": { "type": "string" },
            "total": { "type": "number" },
            "items": {
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "productId": { "type": "string" },
                  "quantity": { "type": "number" },
                  "price": { "type": "number" }
                }
              }
            }
          },
          "required": ["orderId", "userId", "total"]
        }
      }
    }
  }'

# Generate code from schema!
aws schemas get-code-binding-source \
  --schema-name ecommerce.orders@OrderCreated \
  --language TypeScript \
  --output-path ./src/types/

Now you have types:

// Auto-generated TypeScript types!
interface OrderCreated {
  orderId: string
  userId: string
  total: number
  items: Array<{
    productId: string
    quantity: number
    price: number
  }>
  createdAt: string
}

// Use with full type safety!
export const handler = async (event: EventBridgeEvent<'OrderCreated', OrderCreated>) => {
  const { orderId, total, items } = event.detail

  // TypeScript knows the structure!
  // Auto-complete works!
  // No guessing! šŸŽ‰
}

Why Schema Registry is amazing:

  • āœ… Auto-generated documentation
  • āœ… Type-safe code generation (TypeScript, Python, Java, etc.)
  • āœ… Schema versioning
  • āœ… Discovery (find all events in your system!)
  • āœ… No more guessing event structures!

EventBridge Mistake #5: Not Using Input Transformation šŸ”„

The problem (passing entire event):

// EventBridge sends this to Lambda:
{
  "version": "0",
  "id": "abc-123",
  "detail-type": "OrderCreated",
  "source": "ecommerce.orders",
  "account": "123456789",
  "time": "2024-01-01T12:00:00Z",
  "region": "us-east-1",
  "resources": [],
  "detail": {
    "orderId": "12345",
    "total": 99.99,
    "items": [...],
    "customer": {...},
    "shipping": {...}
    // 10KB of data!
  }
}

// Lambda only needs orderId and total!
// Wasting 9.9KB of data transfer!

The solution (input transformation):

// EventBridge rule with input transformer
{
  "InputTransformer": {
    "InputPathsMap": {
      "orderId": "$.detail.orderId",
      "total": "$.detail.total",
      "customer": "$.detail.customer.email"
    },
    "InputTemplate": "{\"orderId\": <orderId>, \"total\": <total>, \"customerEmail\": <customer>}"
  }
}

// Lambda receives just this:
{
  "orderId": "12345",
  "total": 99.99,
  "customerEmail": "[email protected]"
}

// 10KB → 0.1KB (99% reduction!)

Advanced transformation:

// Add custom fields, format data, enrich events
{
  "InputTransformer": {
    "InputPathsMap": {
      "orderId": "$.detail.orderId",
      "total": "$.detail.total",
      "timestamp": "$.time"
    },
    "InputTemplate": "{
      \"orderId\": <orderId>,
      \"total\": <total>,
      \"isHighValue\": <total> > 100,
      \"processedAt\": <timestamp>,
      \"environment\": \"production\"
    }"
  }
}

Why this matters:

  • āœ… Smaller Lambda payloads = faster cold starts
  • āœ… Less data parsing in Lambda = faster execution
  • āœ… Custom fields = cleaner Lambda code
  • āœ… Filter sensitive data before sending to target

EventBridge Patterns That Scale šŸš€

Pattern #1: Event-Driven Microservices

// Service A: Order Service
async function createOrder(orderData) {
  const order = await saveOrder(orderData)

  // Emit event (fire and forget!)
  await eventbridge.putEvents({
    Entries: [{
      Source: 'orders',
      DetailType: 'OrderCreated',
      Detail: JSON.stringify(order)
    }]
  }).promise()

  return order  // Don't wait for other services!
}

// Service B: Inventory Service (subscribes to events)
exports.handler = async (event) => {
  const order = event.detail
  await reserveInventory(order.items)
}

// Service C: Notification Service (subscribes to events)
exports.handler = async (event) => {
  const order = event.detail
  await sendOrderConfirmation(order.customer.email, order)
}

// Service D: Analytics Service (subscribes to events)
exports.handler = async (event) => {
  const order = event.detail
  await trackOrderEvent(order)
}

// ZERO coupling! Each service is independent! ✨

Pattern #2: CQRS (Command Query Responsibility Segregation)

// Write model: Handle commands
async function createOrder(command) {
  const order = {
    id: generateId(),
    ...command,
    createdAt: Date.now()
  }

  // Write to DynamoDB (source of truth)
  await dynamodb.put({
    TableName: 'Orders',
    Item: order
  }).promise()

  // Emit event
  await eventbridge.putEvents({
    Entries: [{
      Source: 'orders',
      DetailType: 'OrderCreated',
      Detail: JSON.stringify(order)
    }]
  }).promise()

  return order
}

// Read model: Update projections (triggered by events)
exports.updateOrderProjection = async (event) => {
  const order = event.detail

  // Update read-optimized views
  await Promise.all([
    // View 1: Orders by customer
    dynamodb.put({
      TableName: 'OrdersByCustomer',
      Item: {
        PK: `CUSTOMER#${order.customerId}`,
        SK: `ORDER#${order.id}`,
        ...order
      }
    }).promise(),

    // View 2: Orders by date
    dynamodb.put({
      TableName: 'OrdersByDate',
      Item: {
        PK: formatDate(order.createdAt),
        SK: order.id,
        ...order
      }
    }).promise(),

    // View 3: Search index
    elasticsearch.index({
      index: 'orders',
      id: order.id,
      body: order
    })
  ])
}

// Queries read from projections (optimized for each use case!)

Pattern #3: Saga Pattern (Distributed Transactions)

// EventBridge orchestrates saga across services

// Step 1: Order created
await eventbridge.putEvents({
  Entries: [{
    Source: 'orders',
    DetailType: 'OrderCreated',
    Detail: JSON.stringify(order)
  }]
}).promise()

// Step 2: Payment service processes (triggered by event)
exports.processPayment = async (event) => {
  const order = event.detail

  try {
    await chargeCard(order.paymentMethod, order.total)

    // Success → Emit success event
    await eventbridge.putEvents({
      Entries: [{
        Source: 'payments',
        DetailType: 'PaymentSucceeded',
        Detail: JSON.stringify({ orderId: order.id })
      }]
    }).promise()
  } catch (error) {
    // Failure → Emit failure event
    await eventbridge.putEvents({
      Entries: [{
        Source: 'payments',
        DetailType: 'PaymentFailed',
        Detail: JSON.stringify({ orderId: order.id, reason: error.message })
      }]
    }).promise()
  }
}

// Step 3: Inventory service reserves stock (on payment success)
exports.reserveInventory = async (event) => {
  const { orderId } = event.detail

  try {
    await reserveStock(orderId)

    await eventbridge.putEvents({
      Entries: [{
        Source: 'inventory',
        DetailType: 'InventoryReserved',
        Detail: JSON.stringify({ orderId })
      }]
    }).promise()
  } catch (error) {
    // Failure → Trigger compensation (refund payment)
    await eventbridge.putEvents({
      Entries: [{
        Source: 'inventory',
        DetailType: 'InventoryReservationFailed',
        Detail: JSON.stringify({ orderId })
      }]
    }).promise()
  }
}

// Step 4: Compensation handler (triggered by failure events)
exports.compensate = async (event) => {
  const { orderId } = event.detail

  // Rollback payment
  await refundPayment(orderId)

  // Cancel order
  await cancelOrder(orderId)
}

// Distributed transaction managed by events! šŸŽ­

EventBridge Cost Optimization šŸ’°

Pricing:

Custom events: $1.00 per million events
AWS service events: FREE!
Schema discovery: $0.10 per million ingested events
Archive: $0.023 per GB per month
Replay: $0.023 per GB replayed

How I keep costs low:

1. Batch Events

// āŒ BAD: One event at a time
for (const order of orders) {
  await eventbridge.putEvents({
    Entries: [{
      Source: 'orders',
      DetailType: 'OrderCreated',
      Detail: JSON.stringify(order)
    }]
  }).promise()
  // 1000 orders = 1000 API calls!
}

// āœ… GOOD: Batch up to 10 events per request
const batches = chunk(orders, 10)
for (const batch of batches) {
  await eventbridge.putEvents({
    Entries: batch.map(order => ({
      Source: 'orders',
      DetailType: 'OrderCreated',
      Detail: JSON.stringify(order)
    }))
  }).promise()
}
// 1000 orders = 100 API calls (10Ɨ fewer!)

2. Use AWS Service Events (Free!)

// DynamoDB Streams → EventBridge (FREE!)
// S3 notifications → EventBridge (FREE!)
// Step Functions → EventBridge (FREE!)

// Instead of Lambda polling DynamoDB, use streams!
{
  "EventPattern": {
    "source": ["aws.dynamodb"],
    "detail-type": ["DynamoDB Stream Record"],
    "detail": {
      "eventName": ["INSERT"],
      "dynamodb": {
        "Keys": {
          "PK": {
            "S": [{ "prefix": "ORDER#" }]
          }
        }
      }
    }
  }
}

// Now DynamoDB writes automatically trigger EventBridge (FREE!)

3. Filter Events Aggressively

// Only match high-value events
{
  "detail": {
    "total": [{ "numeric": [">", 100] }]
  }
}

// Reduces downstream Lambda invocations by 90%!

EventBridge vs Alternatives šŸ†š

EventBridge vs SQS

EventBridge:
āœ… Pub/sub (1 event → many targets)
āœ… Content-based routing
āœ… AWS service integration
āœ… SaaS integration
āŒ No guaranteed order
āŒ No exactly-once delivery

SQS:
āœ… Guaranteed delivery
āœ… Exactly-once processing (FIFO)
āœ… Message retention (14 days)
āœ… Dead letter queues
āŒ Point-to-point only (1 message → 1 consumer)
āŒ No content filtering

Use EventBridge for: Fanout, event routing, AWS integration
Use SQS for: Reliable queuing, ordered processing
Use BOTH: EventBridge routes to multiple SQS queues! šŸŽÆ

EventBridge vs SNS

EventBridge:
āœ… Advanced filtering (content-based!)
āœ… Schema registry
āœ… Archive & replay
āœ… Targets: Lambda, Step Functions, SQS, Kinesis, API Gateway, etc.
āŒ Max 5 targets per rule

SNS:
āœ… Simple pub/sub
āœ… FIFO topics
āœ… SMS, email, mobile push
āŒ Basic filtering only
āŒ No schema registry

Use EventBridge for: Complex event routing, microservices
Use SNS for: Simple notifications, mobile push

The EventBridge Checklist āœ…

Before going to production:

  • Stop polling! (Convert to event-driven)
  • Define event patterns (Specific, not greedy)
  • Use multiple targets (Fanout for free!)
  • Enable schema registry (Self-documenting events)
  • Use input transformers (Minimize Lambda payloads)
  • Archive important events (Compliance, debugging)
  • Set up dead letter queues (Catch failed deliveries)
  • Monitor event delivery (CloudWatch metrics)
  • Batch events (Reduce API calls)
  • Use AWS service events (They're FREE!)

The Bottom Line šŸ’”

EventBridge transforms how you architect on AWS!

The essentials:

  1. Stop polling (Emit events instead!)
  2. Use event patterns (Filter smartly)
  3. Multiple targets (Fanout for free)
  4. Schema registry (Type safety)
  5. Input transformers (Optimize payloads)

The truth about EventBridge:

It's not "just another messaging service" - it's a PARADIGM SHIFT! Stop asking "did something happen?" Start saying "when something happens, do this!"

When architecting our serverless backend, I learned: Event-driven architecture is harder to design upfront but pays MASSIVE dividends. Your system becomes reactive, scalable, and maintainable. Stop polling. Start reacting. Your AWS bill (and your sanity) will thank you! šŸ™

You don't need perfect event-driven design from day one - you need to START reacting and STOP polling! šŸš€

Your Action Plan šŸŽÆ

This week:

  1. Identify ONE polling Lambda (runs on a schedule)
  2. Convert it to event-driven (emit events instead!)
  3. Set up EventBridge rule to trigger Lambda
  4. Measure cost savings (probably 80-90%!)

This month:

  1. Audit ALL scheduled Lambdas (find polling patterns)
  2. Enable DynamoDB Streams → EventBridge
  3. Enable S3 notifications → EventBridge
  4. Set up Schema Registry for your events

This quarter:

  1. Design event-driven microservices architecture
  2. Implement event sourcing for critical workflows
  3. Set up event archiving for compliance
  4. Become the event-driven guru on your team! šŸ†

Resources Worth Your Time šŸ“š

Tools I use daily:

Reading list:

Real talk: The best EventBridge strategy is starting simple. One polling Lambda → event-driven. Then another. Then another. Before you know it, your entire architecture is reactive!


Still polling DynamoDB every 30 seconds? Connect with me on LinkedIn and share your event-driven wins!

Want to see my EventBridge architectures? Check out my GitHub - production event patterns!

Now go forth and stop polling! āš”šŸŽÆ


P.S. If you're running Lambda on a schedule "just to check if something happened," you're doing it wrong! Emit events. React to events. Stop wasting money polling! šŸ’ø

P.P.S. I once had a system with 47 scheduled Lambdas (all polling!). After converting to EventBridge, we had 3 scheduled Lambdas and 200+ event-driven Lambdas. Bill dropped 85%. Response time improved 95%. Event-driven architecture is MAGIC! ✨