Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Lambda best practices, S3 event patterns, SQS/SNS fanout, and DynamoDB access patterns for serverless AWS architectures.
.claude/skills/aws-patterns/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-05 | ✗→✓ | ▲ Improved | — | — |
| case-01 | ✗→✓ | ▲ Improved | — | — |
| case-03 | ✗→✓ | ▲ Improved | — | — |
| case-07 | ✓→✓ | = Same ✓ | — | — |
| case-10 | ✗→✗ | = Same ✗ | — | — |
Serverless and managed service patterns for AWS production workloads.
typescript// Cold start optimization: keep handler thin, initialize outside handler import { DynamoDBClient } from '@aws-sdk/client-dynamodb' import { DynamoDBDocumentClient, GetCommand } from '@aws-sdk/lib-dynamodb' // Initialized ONCE per container (reused across invocations) const client = DynamoDBDocumentClient.from(new DynamoDBClient({})) export const handler = async (event: APIGatewayProxyEvent) => { try { const userId = event.pathParameters?.id if (!userId) { return { statusCode: 400, body: JSON.stringify({ error: 'Missing user ID' }) } } const result = await client.send(new GetCommand({ TableName: process.env.USERS_TABLE!, Key: { pk: `USER#${userId}`, sk: `PROFILE` } })) if (!result.Item) { return { statusCode: 404, body: JSON.stringify({ error: 'User not found' }) } } return { statusCode: 200, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(result.Item) } } catch (err) { console.error('Handler error:', err) return { statusCode: 500, body: JSON.stringify({ error: 'Internal server error' }) } } }
typescript// S3 → Lambda: process uploaded files import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3' const s3 = new S3Client({}) export const handler = async (event: S3Event) => { for (const record of event.Records) { const bucket = record.s3.bucket.name const key = decodeURIComponent(record.s3.object.key.replace(/\+/g, ' ')) const size = record.s3.object.size // Guard: skip non-image files or oversized uploads if (size > 10_000_000) { console.warn(`Skipping oversized file: ${key} (${size} bytes)`) continue } const response = await s3.send(new GetObjectCommand({ Bucket: bucket, Key: key })) const body = await response.Body!.transformToByteArray() await processImage(body, key) console.log(`Processed ${key} (${size} bytes)`) } }
typescript// SNS → multiple SQS queues (fanout to parallel consumers) // Publisher: send to SNS topic import { SNSClient, PublishCommand } from '@aws-sdk/client-sns' const sns = new SNSClient({}) async function publishOrderEvent(order: Order): Promise<void> { await sns.send(new PublishCommand({ TopicArn: process.env.ORDER_EVENTS_TOPIC!, Message: JSON.stringify(order), MessageAttributes: { eventType: { DataType: 'String', StringValue: 'order.created' }, region: { DataType: 'String', StringValue: order.region } } })) } // Consumer: SQS Lambda (one per subscriber: email, analytics, inventory) export const emailHandler = async (event: SQSEvent) => { for (const record of event.Records) { const order = JSON.parse(record.body) as Order try { await sendOrderConfirmation(order) } catch (err) { console.error(`Failed to send email for order ${order.id}:`, err) throw err // Message returns to queue for retry (DLQ after maxReceiveCount) } } }
typescript// Access patterns drive table design, not entity relationships // Table: pk (partition key) + sk (sort key) + GSI1PK + GSI1SK // Entities: User, Order, OrderItem - all in one table const AccessPatterns = { // Get user profile getUserProfile: (userId: string) => ({ pk: `USER#${userId}`, sk: `PROFILE` }), // Get user's orders (sorted by date) getUserOrders: (userId: string) => ({ pk: `USER#${userId}`, sk: { begins_with: 'ORDER#' } // sk: ORDER#2025-01-15#orderId }), // Get order with items getOrderWithItems: (orderId: string) => ({ pk: `ORDER#${orderId}`, sk: { begins_with: '' } // sk: METADATA, ITEM#productId }), // Get orders by status (GSI1) getOrdersByStatus: (status: string) => ({ GSI1PK: `STATUS#${status}`, GSI1SK: { begins_with: '' } // GSI1SK: date#orderId }) } // Write: transactional multi-item write import { TransactWriteCommand } from '@aws-sdk/lib-dynamodb' async function createOrder(order: Order): Promise<void> { const items = [ // Order metadata { Put: { TableName: process.env.TABLE!, Item: { pk: `ORDER#${order.id}`, sk: 'METADATA', GSI1PK: `STATUS#${order.status}`, GSI1SK: `${order.createdAt}#${order.id}`, ...order } } }, // User's order reference { Put: { TableName: process.env.TABLE!, Item: { pk: `USER#${order.userId}`, sk: `ORDER#${order.createdAt}#${order.id}`, orderId: order.id, total: order.total, status: order.status } } } ] await client.send(new TransactWriteCommand({ TransactItems: items })) }
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-10 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
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 +14 percentage points is the difference between those two pass rates over the 22 comparable cases.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.