Install any skill in seconds. Free to start, no credit card required.
Get Started Free →AWS serverless and event-driven architecture expert based on Well-Architected Framework. Use when building serverless APIs, Lambda functions, REST APIs, microservices, or async workflows. Covers Lambda with TypeScript/Python, API Gateway (REST/HTTP), DynamoDB, Step Functions, EventBridge, SQS, SNS, and serverless patterns. Essential when user mentions serverless, Lambda, API Gateway, event-driven, async processing, queues, pub/sub, or wants to build scalable serverless applications with AWS best
.claude/skills/microck-aws-serverless-eda/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-03 | ✗→✓ | ▲ Improved | 208% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 222% | 0% |
| case-01 | ✓→✓ | = Same ✓ | 245% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 281% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 202% | 0% |
This skill provides comprehensive guidance for building serverless applications and event-driven architectures on AWS based on Well-Architected Framework principles.
This skill includes 5 MCP servers for serverless development:
When to use: Always verify AWS service information before implementation
Purpose: Complete serverless application lifecycle with SAM CLI
Purpose: Execute Lambda functions as tools
Purpose: Execute complex workflows and orchestration
Purpose: Event-driven messaging and queue management
Use this skill when:
Functions should be concise and single-purpose
typescript// ✅ GOOD - Single purpose, focused function export const processOrder = async (event: OrderEvent) => { // Only handles order processing const order = await validateOrder(event); await saveOrder(order); await publishOrderCreatedEvent(order); return { statusCode: 200, body: JSON.stringify({ orderId: order.id }) }; }; // ❌ BAD - Function does too much export const handleEverything = async (event: any) => { // Handles orders, inventory, payments, shipping... // Too many responsibilities };
Keep functions environmentally efficient and cost-aware:
Design for concurrency, not volume
Lambda scales horizontally - design considerations should focus on:
typescript// Consider concurrent Lambda executions accessing DynamoDB const table = new dynamodb.Table(this, 'Table', { billingMode: dynamodb.BillingMode.PAY_PER_REQUEST, // Auto-scales with load }); // Or with provisioned capacity + auto-scaling const table = new dynamodb.Table(this, 'Table', { billingMode: dynamodb.BillingMode.PROVISIONED, readCapacity: 5, writeCapacity: 5, }); // Enable auto-scaling for concurrent load table.autoScaleReadCapacity({ minCapacity: 5, maxCapacity: 100 }); table.autoScaleWriteCapacity({ minCapacity: 5, maxCapacity: 100 });
Function runtime environments are short-lived
typescript// ❌ BAD - Relying on local file system export const handler = async (event: any) => { fs.writeFileSync('/tmp/data.json', JSON.stringify(data)); // Lost after execution }; // ✅ GOOD - Use persistent storage export const handler = async (event: any) => { await s3.putObject({ Bucket: process.env.BUCKET_NAME, Key: 'data.json', Body: JSON.stringify(data), }); };
State management:
Applications must be hardware-agnostic
Infrastructure can change without notice:
Design for portability:
Use Step Functions for orchestration
typescript// ❌ BAD - Lambda function chaining export const handler1 = async (event: any) => { const result = await processStep1(event); await lambda.invoke({ FunctionName: 'handler2', Payload: JSON.stringify(result), }); }; // ✅ GOOD - Step Functions orchestration const stateMachine = new stepfunctions.StateMachine(this, 'OrderWorkflow', { definition: stepfunctions.Chain .start(validateOrder) .next(processPayment) .next(shipOrder) .next(sendConfirmation), });
Benefits of Step Functions:
Event-driven over synchronous request/response
typescript// Pattern: Event-driven processing const bucket = new s3.Bucket(this, 'DataBucket'); bucket.addEventNotification( s3.EventType.OBJECT_CREATED, new s3n.LambdaDestination(processFunction), { prefix: 'uploads/' } ); // Pattern: EventBridge integration const rule = new events.Rule(this, 'OrderRule', { eventPattern: { source: ['orders'], detailType: ['OrderPlaced'], }, }); rule.addTarget(new targets.LambdaFunction(processOrderFunction));
Benefits:
Operations must be idempotent
typescript// ✅ GOOD - Idempotent operation export const handler = async (event: SQSEvent) => { for (const record of event.Records) { const orderId = JSON.parse(record.body).orderId; // Check if already processed (idempotency) const existing = await dynamodb.getItem({ TableName: process.env.TABLE_NAME, Key: { orderId }, }); if (existing.Item) { console.log('Order already processed:', orderId); continue; // Skip duplicate } // Process order await processOrder(orderId); // Mark as processed await dynamodb.putItem({ TableName: process.env.TABLE_NAME, Item: { orderId, processedAt: Date.now() }, }); } };
Implement retry logic with exponential backoff:
typescriptasync function withRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> { for (let i = 0; i < maxRetries; i++) { try { return await fn(); } catch (error) { if (i === maxRetries - 1) throw error; await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 1000)); } } throw new Error('Max retries exceeded'); }
Use EventBridge for event routing and filtering:
typescript// Create custom event bus const eventBus = new events.EventBus(this, 'AppEventBus', { eventBusName: 'application-events', }); // Define event schema const schema = new events.Schema(this, 'OrderSchema', { schemaName: 'OrderPlaced', definition: events.SchemaDefinition.fromInline({ openapi: '3.0.0', info: { version: '1.0.0', title: 'Order Events' }, paths: {}, components: { schemas: { OrderPlaced: { type: 'object', properties: { orderId: { type: 'string' }, customerId: { type: 'string' }, amount: { type: 'number' }, }, }, }, }, }), }); // Create rules for different consumers new events.Rule(this, 'ProcessOrderRule', { eventBus, eventPattern: { source: ['orders'], detailType: ['OrderPlaced'], }, targets: [new targets.LambdaFunction(processOrderFunction)], }); new events.Rule(this, 'NotifyCustomerRule', { eventBus, eventPattern: { source: ['orders'], detailType: ['OrderPlaced'], }, targets: [new targets.LambdaFunction(notifyCustomerFunction)], });
Use SQS for reliable asynchronous processing:
typescript// Standard queue for at-least-once delivery const queue = new sqs.Queue(this, 'ProcessingQueue', { visibilityTimeout: Duration.seconds(300), retentionPeriod: Duration.days(14), deadLetterQueue: { queue: dlq, maxReceiveCount: 3, }, }); // FIFO queue for ordered processing const fifoQueue = new sqs.Queue(this, 'OrderedQueue', { fifo: true, contentBasedDeduplication: true, deduplicationScope: sqs.DeduplicationScope.MESSAGE_GROUP, }); // Lambda consumer new lambda.EventSourceMapping(this, 'QueueConsumer', { target: processingFunction, eventSourceArn: queue.queueArn, batchSize: 10, maxBatchingWindow: Duration.seconds(5), });
Implement fan-out pattern for multiple consumers:
typescript// Create SNS topic const topic = new sns.Topic(this, 'OrderTopic', { displayName: 'Order Events', }); // Multiple SQS queues subscribe to topic const inventoryQueue = new sqs.Queue(this, 'InventoryQueue'); const shippingQueue = new sqs.Queue(this, 'ShippingQueue'); const analyticsQueue = new sqs.Queue(this, 'AnalyticsQueue'); topic.addSubscription(new subscriptions.SqsSubscription(inventoryQueue)); topic.addSubscription(new subscriptions.SqsSubscription(shippingQueue)); topic.addSubscription(new subscriptions.SqsSubscription(analyticsQueue)); // Each queue has its own Lambda consumer new lambda.EventSourceMapping(this, 'InventoryConsumer', { target: inventoryFunction, eventSourceArn: inventoryQueue.queueArn, });
Implement distributed transactions:
typescriptconst reserveFlight = new tasks.LambdaInvoke(this, 'ReserveFlight', { lambdaFunction: reserveFlightFunction, outputPath: '$.Payload', }); const reserveHotel = new tasks.LambdaInvoke(this, 'ReserveHotel', { lambdaFunction: reserveHotelFunction, outputPath: '$.Payload', }); const processPayment = new tasks.LambdaInvoke(this, 'ProcessPayment', { lambdaFunction: processPaymentFunction, outputPath: '$.Payload', }); // Compensating transactions const cancelFlight = new tasks.LambdaInvoke(this, 'CancelFlight', { lambdaFunction: cancelFlightFunction, }); const cancelHotel = new tasks.LambdaInvoke(this, 'CancelHotel', { lambdaFunction: cancelHotelFunction, }); // Define saga with compensation const definition = reserveFlight .next(reserveHotel) .next(processPayment) .addCatch(cancelHotel.next(cancelFlight), { resultPath: '$.error', }); new stepfunctions.StateMachine(this, 'BookingStateMachine', { definition, timeout: Duration.minutes(5), });
Store events as source of truth:
typescript// Event store with DynamoDB const eventStore = new dynamodb.Table(this, 'EventStore', { partitionKey: { name: 'aggregateId', type: dynamodb.AttributeType.STRING }, sortKey: { name: 'version', type: dynamodb.AttributeType.NUMBER }, stream: dynamodb.StreamViewType.NEW_IMAGE, }); // Lambda function stores events export const handleCommand = async (event: any) => { const { aggregateId, eventType, eventData } = event; // Get current version const items = await dynamodb.query({ TableName: process.env.EVENT_STORE, KeyConditionExpression: 'aggregateId = :id', ExpressionAttributeValues: { ':id': aggregateId }, ScanIndexForward: false, Limit: 1, }); const nextVersion = items.Items?.[0]?.version + 1 || 1; // Append new event await dynamodb.putItem({ TableName: process.env.EVENT_STORE, Item: { aggregateId, version: nextVersion, eventType, eventData, timestamp: Date.now(), }, }); }; // Projections read from event stream eventStore.grantStreamRead(projectionFunction);
REST APIs with Lambda backend:
typescriptconst api = new apigateway.RestApi(this, 'Api', { restApiName: 'microservices-api', deployOptions: { throttlingRateLimit: 1000, throttlingBurstLimit: 2000, tracingEnabled: true, }, }); // User service const users = api.root.addResource('users'); users.addMethod('GET', new apigateway.LambdaIntegration(getUsersFunction)); users.addMethod('POST', new apigateway.LambdaIntegration(createUserFunction)); // Order service const orders = api.root.addResource('orders'); orders.addMethod('GET', new apigateway.LambdaIntegration(getOrdersFunction)); orders.addMethod('POST', new apigateway.LambdaIntegration(createOrderFunction));
Real-time data processing with Kinesis:
typescriptconst stream = new kinesis.Stream(this, 'DataStream', { shardCount: 2, retentionPeriod: Duration.days(7), }); // Lambda processes stream records new lambda.EventSourceMapping(this, 'StreamProcessor', { target: processFunction, eventSourceArn: stream.streamArn, batchSize: 100, maxBatchingWindow: Duration.seconds(5), parallelizationFactor: 10, startingPosition: lambda.StartingPosition.LATEST, retryAttempts: 3, bisectBatchOnError: true, onFailure: new lambdaDestinations.SqsDestination(dlq), });
Background job processing:
typescript// SQS queue for tasks const taskQueue = new sqs.Queue(this, 'TaskQueue', { visibilityTimeout: Duration.minutes(5), receiveMessageWaitTime: Duration.seconds(20), // Long polling deadLetterQueue: { queue: dlq, maxReceiveCount: 3, }, }); // Lambda worker processes tasks const worker = new lambda.Function(this, 'TaskWorker', { // ... configuration reservedConcurrentExecutions: 10, // Control concurrency }); new lambda.EventSourceMapping(this, 'TaskConsumer', { target: worker, eventSourceArn: taskQueue.queueArn, batchSize: 10, reportBatchItemFailures: true, // Partial batch failure handling });
Periodic processing with EventBridge:
typescript// Daily cleanup job new events.Rule(this, 'DailyCleanup', { schedule: events.Schedule.cron({ hour: '2', minute: '0' }), targets: [new targets.LambdaFunction(cleanupFunction)], }); // Process every 5 minutes new events.Rule(this, 'FrequentProcessing', { schedule: events.Schedule.rate(Duration.minutes(5)), targets: [new targets.LambdaFunction(processFunction)], });
Handle external webhooks:
typescript// API Gateway endpoint for webhooks const webhookApi = new apigateway.RestApi(this, 'WebhookApi', { restApiName: 'webhooks', }); const webhook = webhookApi.root.addResource('webhook'); webhook.addMethod('POST', new apigateway.LambdaIntegration(webhookFunction, { proxy: true, timeout: Duration.seconds(29), // API Gateway max })); // Lambda handler validates and queues webhook export const handler = async (event: APIGatewayProxyEvent) => { // Validate webhook signature const isValid = validateSignature(event.headers, event.body); if (!isValid) { return { statusCode: 401, body: 'Invalid signature' }; } // Queue for async processing await sqs.sendMessage({ QueueUrl: process.env.QUEUE_URL, MessageBody: event.body, }); // Return immediately return { statusCode: 202, body: 'Accepted' }; };
Implement comprehensive error handling:
typescriptexport const handler = async (event: SQSEvent) => { const failures: SQSBatchItemFailure[] = []; for (const record of event.Records) { try { await processRecord(record); } catch (error) { console.error('Failed to process record:', record.messageId, error); failures.push({ itemIdentifier: record.messageId }); } } // Return partial batch failures for retry return { batchItemFailures: failures }; };
Always configure DLQs for error handling:
typescriptconst dlq = new sqs.Queue(this, 'DLQ', { retentionPeriod: Duration.days(14), }); const queue = new sqs.Queue(this, 'Queue', { deadLetterQueue: { queue: dlq, maxReceiveCount: 3, }, }); // Monitor DLQ depth new cloudwatch.Alarm(this, 'DLQAlarm', { metric: dlq.metricApproximateNumberOfMessagesVisible(), threshold: 1, evaluationPeriods: 1, alarmDescription: 'Messages in DLQ require attention', });
Enable tracing and monitoring:
typescriptnew NodejsFunction(this, 'Function', { entry: 'src/handler.ts', tracing: lambda.Tracing.ACTIVE, // X-Ray tracing environment: { POWERTOOLS_SERVICE_NAME: 'order-service', POWERTOOLS_METRICS_NAMESPACE: 'MyApp', LOG_LEVEL: 'INFO', }, });
Lifecycle management:
Function execution:
Workflow orchestration:
Messaging operations:
This skill includes comprehensive reference documentation based on AWS best practices:
references/serverless-patterns.mdreferences/eda-patterns.mdreferences/security-best-practices.mdreferences/observability-best-practices.mdreferences/performance-optimization.mdreferences/deployment-best-practices.mdExternal Resources:
For detailed implementation patterns, anti-patterns, and code examples, refer to the comprehensive references in the skill directory.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | pass→pass | 11,286 | 14,516 | +29% | 1 | 1 | 0% | 2,210 | 7,618 | +245% | 0 | 0 | — |
case-20 | fail→fail | 20,008 | 43,306 | +116% | 1 | 1 | 0% | 3,339 | 9,543 | +186% | 0 | 0 | — |
case-02 | pass→pass | 9,961 | 10,762 | +8% | 1 | 1 | 0% | 1,911 | 7,274 | +281% | 0 | 0 | — |
case-03 | fail→pass | 22,045 | 15,403 | -30% | 1 | 1 | 0% | 2,505 | 7,711 | +208% | 0 | 0 | — |
case-04 | pass→pass | 14,975 | 13,545 | -10% | 1 | 1 | 0% | 2,424 | 7,331 | +202% | 0 | 0 | — |
case-05 | pass→pass | 13,289 | 13,104 | -1% | 1 | 1 | 0% | 2,313 | 7,557 | +227% | 0 | 0 | — |
case-06 | pass→pass | 5,531 | 8,199 | +48% | 1 | 1 | 0% | 816 | 6,623 | +712% | 0 | 0 | — |
case-07 | pass→pass | 4,108 | 8,050 | +96% | 1 | 1 | 0% | 656 | 6,520 | +894% | 0 | 0 | — |
case-08 | fail→pass | 15,433 | 17,805 | +15% | 1 | 1 | 0% | 2,608 | 8,396 | +222% | 0 | 0 | — |
case-09 | pass→pass | 15,652 | 10,107 | -35% | 1 | 1 | 0% | 2,585 | 6,864 | +166% | 0 | 0 | — |
case-10 | pass→pass | 12,662 | 9,889 | -22% | 1 | 1 | 0% | 1,699 | 6,980 | +311% | 0 | 0 | — |
case-11 | pass→pass | 7,949 | 8,675 | +9% | 1 | 1 | 0% | 1,226 | 6,520 | +432% | 0 | 0 | — |
case-12 | pass→pass | 13,374 | 10,433 | -22% | 1 | 1 | 0% | 2,076 | 6,900 | +232% | 0 | 0 | — |
case-13 | pass→pass | 15,281 | 15,856 | +4% | 1 | 1 | 0% | 2,571 | 7,974 | +210% | 0 | 0 | — |
case-14 | pass→pass | 3,593 | 5,054 | +41% | 1 | 1 | 0% | 536 | 5,963 | +1013% | 0 | 0 | — |
case-15 | pass→pass | 10,677 | 8,636 | -19% | 1 | 1 | 0% | 1,796 | 6,713 | +274% | 0 | 0 | — |
case-16 | pass→pass | 8,659 | 6,884 | -20% | 1 | 1 | 0% | 1,389 | 6,396 | +360% | 0 | 0 | — |
case-17 | pass→pass | 4,035 | 7,281 | +80% | 1 | 1 | 0% | 594 | 6,339 | +967% | 0 | 0 | — |
case-18 | pass→pass | 5,406 | 9,481 | +75% | 1 | 1 | 0% | 822 | 6,994 | +751% | 0 | 0 | — |
case-19 | pass→pass | 9,059 | 9,250 | +2% | 1 | 1 | 0% | 1,596 | 6,748 | +323% | 0 | 0 | — |
case-21 | pass→pass | 15,750 | 30,676 | +95% | 1 | 1 | 0% | 2,518 | 8,614 | +242% | 0 | 0 | — |
case-22 | pass→pass | 14,598 | 16,621 | +14% | 1 | 1 | 0% | 2,782 | 8,493 | +205% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +9 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.