DynamoDB mistakes are expensive — not in dollars, but in the hot partitions, full table scans, and impossible access patterns that result from wrong key design. The advanced patterns here — single-table design, GSI overloading, sparse indexes, and conditional writes — are what power the highest-scale production DynamoDB systems.
⚡ TL;DR: Design access patterns first, then choose keys. Single-table: prefix PK/SK for different entity types. GSI for alternate access patterns. Sparse GSI: only items with GSI attribute are indexed. Conditional writes for optimistic locking. DynamoDB Streams trigger Lambda on changes. TTL for automatic expiry.
Single-table design patterns
// All entities in one table: prefix PK and SK
// Users and orders in same table:
// PK: USER#userId SK: METADATA → user entity
// PK: USER#userId SK: ORDER#orderId → user's order
// PK: ORDER#orderId SK: METADATA → order entity
// PK: ORDER#orderId SK: ITEM#itemId → order item
// Query all orders for a user:
const result = await client.send(new QueryCommand({
TableName: 'main',
KeyConditionExpression: 'PK = :pk AND begins_with(SK, :prefix)',
ExpressionAttributeValues: {
':pk': 'USER#'+userId,
':prefix': 'ORDER#'
}
}));
// Query single order:
const order = await client.send(new GetItemCommand({
TableName: 'main',
Key: { PK: 'ORDER#'+orderId, SK: 'METADATA' }
}));
GSI overloading — multiple access patterns from one GSI
// GSI1: GSI1PK, GSI1SK — overloaded for different entity types
// User entity:
// PK: USER#123 SK: METADATA
// GSI1PK: EMAIL#alice@example.com GSI1SK: USER#123
// → GSI1 enables: look up user by email
// Order entity:
// PK: ORDER#456 SK: METADATA
// GSI1PK: STATUS#pending GSI1SK: DATE#2026-04-01
// → GSI1 enables: query all pending orders by date
// Same GSI serves completely different entity lookups!
// Key: different entities use different value formats in GSI1PK
Conditional writes — optimistic locking
// Only update if version matches (prevents lost updates)
const updateResult = await client.send(new UpdateItemCommand({
TableName: 'inventory',
Key: { PK: 'PRODUCT#'+productId, SK: 'STOCK' },
UpdateExpression: 'SET stock = stock - :qty, version = version + :one',
ConditionExpression: 'stock >= :qty AND version = :v',
ExpressionAttributeValues: {
':qty': { N: '1' }, ':one': { N: '1' }, ':v': { N: String(currentVersion) }
}
}));
// If stock < qty or version changed: ConditionalCheckFailedException
// Retry with fresh version if optimistic lock fails
DynamoDB Transactions + TTL
// ACID transactions across up to 25 items:
await client.send(new TransactWriteItemsCommand({
TransactItems: [
{ Update: { // Debit account
TableName:'accounts', Key:{PK:'ACCT#from'},
UpdateExpression:'SET balance = balance - :amt',
ConditionExpression:'balance >= :amt',
ExpressionAttributeValues:{':amt':{N:'100'}}
}},
{ Update: { // Credit account
TableName:'accounts', Key:{PK:'ACCT#to'},
UpdateExpression:'SET balance = balance + :amt',
ExpressionAttributeValues:{':amt':{N:'100'}}
}}
]
}));
// TTL: auto-delete expired items (free!)
// Store TTL as Unix timestamp in 'expiresAt' attribute
// Enable TTL: aws dynamodb update-time-to-live --ttl-attribute expiresAt
- ✅ Single-table design for related entities with multiple access patterns
- ✅ GSI overloading: one GSI serves multiple entity lookup patterns
- ✅ Conditional writes for optimistic concurrency control
- ✅ TTL for automatic session/cache/token expiry
- ✅ Transactions for atomic multi-item updates
- ❌ Never start with DynamoDB before identifying access patterns
- ❌ Never scan large tables — redesign keys instead
External reference: Alex DeBrie Single-Table Design.
Recommended Reading
→ Designing Data-Intensive Applications — The bible of distributed systems and production engineering at scale.
→ The Pragmatic Programmer — Timeless engineering wisdom every senior developer needs.
Affiliate links. We earn a small commission at no extra cost to you.
Free Weekly Newsletter
🚀 Join 2,000+ Senior Developers
Get expert-level JavaScript, Python, AWS, system design and AI secrets every week. Zero fluff, pure signal.
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.
