PracHub
QuestionsLearningGuidesInterview Prep

DynamoDB Interview Guide: Data Modeling and Trade-offs

Prepare for DynamoDB interviews with data modeling, capacity planning, indexes, migrations, reliability, retries, and system design trade-offs.

Author: PracHub

Published: 7/6/2026

Home›Knowledge Hub›DynamoDB Interview Guide: Data Modeling and Trade-offs

DynamoDB Interview Guide: Data Modeling and Trade-offs

By PracHub
July 6, 2026
13 min read
0

Quick Overview

This article explains how to discuss DynamoDB in system design interviews, including when to choose it, key design, capacity estimates, and index trade-offs. It also covers migrations, retries, idempotency, hot partitions, Global Tables, backups, and reliability practices.

Free

DynamoDB Interview Guide: Data Modeling, Migrations, Reliability, and System Design Trade-offs

DynamoDB is a managed key-value/document database for operational workloads where the access patterns are known up front. In interviews, don't sell it as "NoSQL that scales." Show the interviewer how you size the workload, design keys, estimate capacity, handle retries, migrate safely, and recover when things fail.

A strong DynamoDB answer calls out:

  • Access patterns, item sizes, RPS, read/write ratio, p95/p99 latency target, cardinality, consistency needs, retention, and cost ceiling.
  • Base table PK/SK, GSIs/LSIs, projections, TTL, streams, backups, and alarms.
  • Hot-key controls, write amplification, idempotency, transactions, pagination, and rollback.

1. DynamoDB in Interview System Design: When to Choose It and When Not To

Use a decision frame before picking DynamoDB:

QuestionDynamoDB-friendly answerWarning sign
QueriesKnown GetItem/Query patternsUnknown filters, joins, arbitrary reporting
Item sizeUsually small, below 400 KBLarge documents/blobs/manifests
TrafficHigh-volume key access with distributed keysOne key gets most traffic
LatencySingle-digit millisecond service-side target for simple key accessp99 depends on retries, throttling, hot partitions, large items, transactions
ConsistencyPer-item conditional writes; regional strong reads where neededStrict cross-item relational constraints everywhere
CostCapacity units and indexes can be estimatedMany GSIs, scans, large items, global replication without budget

Choose DynamoDB for order state, user settings, idempotency records, workflow state, metadata indexes, dedup indexes, counters, and precomputed views. Don't choose it for full-text search, high-cardinality analytical aggregations, full table scans, large documents over 400 KB, ad hoc filtering, unknown query patterns, or complex relational constraints.

Comparison:

StoreUse it forCaveat
DynamoDBDurable key-value/document servingAccess-pattern-first; no joins; GSIs eventual
PostgreSQLrelational workflows, constraints, SQLharder to scale writes horizontally
Rediscache, rate limits, ephemeral countersnetworked Redis is commonly sub-millisecond, not a durable system of record by default
DAXDynamoDB API-compatible read-through cache for hot eventually consistent GetItem/Querydoes not help writes, broad scans, ad hoc queries, rate-limit structures, or non-DynamoDB caching
Search indextext search, faceting, rankingsecondary, eventually consistent
S3/object storageblobs, exports, backups, lake datanot a low-latency metadata query engine alone

For global systems, bring up Global Tables: multi-region active-active replication, asynchronous cross-region propagation, regional failover, and last-writer-wins-style conflict resolution. Strong reads are regional. Cross-region reads can be stale. If conflicts matter, store application versions, request IDs, or state-transition rules.

2. DynamoDB Fundamentals Interviewers Expect You to Know

A table item is a map of attributes and must include the primary key. The max item size is 400 KB, including attribute names. Use vertical partitioning or object storage for large payloads.

Operations:

OperationWhat it doesTrap
GetItemfetch exact primary keyfastest path
Queryrequires partition key equality; optional sort key condition such as between, <, begins_withkey condition narrows reads
Scanreads table/index pagesexpensive; acceptable for controlled backfills/admin jobs
Filter expressionfilters after readingdoes not reduce consumed read capacity
PaginationQuery/Scan return up to 1 MB before filtersuse LastEvaluatedKey cursor

Capacity arithmetic:

  • 1 WCU = one write/sec up to 1 KB; round up by item size.
  • 1 RCU = one strongly consistent read/sec up to 4 KB; two eventually consistent reads/sec up to 4 KB.
  • Transactional reads/writes consume more capacity; model roughly double.
  • GSIs add storage and write capacity for projected index entries.
  • BatchWriteItem: 25 writes, 16 MB request, no conditions; retry unprocessed items.
  • BatchGetItem: 100 items, 16 MB response; retry unprocessed keys.
  • Transactions: up to 100 actions, 4 MB aggregate, same account/region, no two operations on the same item, cancellation reasons must be inspected.

Worked capacity example: 10,000 writes/sec of 2 KB order items = ceil(2/1) * 10,000 = 20,000 WCU on the base table. If two GSIs each project a 2 KB entry, add 20,000 + 20,000, for 60,000 WCU total. If the write is transactional, budget roughly 120,000 WCU. Global Tables multiply replicated write cost per replica region.

Data type caveats: numbers are sent as strings and have finite decimal precision. Maps/lists are convenient for document-style modeling, but they can grow past 400 KB and are hard to query. Empty string/binary values are allowed for non-key attributes; key attributes cannot be empty. Empty sets are not allowed. NULL is different from a missing attribute. Sparse indexes require the GSI key attributes to be absent when the item should not appear.

Expression pitfalls: use expression attribute names for reserved words, nested paths for maps, SET for assignment, ADD for atomic counters/sets`, and conditions for invariants.

Idempotent create in TypeScript:

try {
  await doc.send(new PutCommand({
    TableName: "App",
    Item: {
      PK: `IDEMP#${key}`, SK: "CREATE_ORDER",
      requestHash, status: "COMPLETED",
      response: { orderId }, expiresAt
    },
    ConditionExpression: "attribute_not_exists(PK)"
  }));
  return { orderId };
} catch (e: any) {
  if (e.name !== "ConditionalCheckFailedException") throw e;
  const old = await doc.send(new GetCommand({
    TableName: "App", Key: { PK: `IDEMP#${key}`, SK: "CREATE_ORDER" }
  }));
  if (old.Item?.requestHash !== requestHash) throw new Error("conflicting idempotency key");
  return old.Item.response;
}

LSIs use the same partition key with an alternate sort key. They must be created with the table, can be strongly read, and impose a 10 GB item collection limit per partition key. They are less common than GSIs because they are inflexible. GSIs have their own PK/SK, projections (KEYS_ONLY, INCLUDE, ALL), eventual consistency, backfill when added, hot partitions, and write amplification. In provisioned mode, a throttled GSI can throttle base-table writes.

PartiQL is a SQL-like API, not a relational engine. It does not remove key-design constraints.

3. Access-Pattern-First Data Modeling

Example requirements:

  1. Get order by ID.
  2. List user orders by time, newest first.
  3. List active restaurant orders without hot partitions.
  4. Lookup by payment ID.
  5. Reserve inventory atomically.
  6. Paginate all lists.

Table design:

Table: App
PK: S
SK: S
BillingMode: PAY_PER_REQUEST
GSIs:
  GSI1: { PK: GSI1PK, SK: GSI1SK, Projection: INCLUDE(status,total,eta,userId) }
  GSI2: { PK: GSI2PK, SK: GSI2SK, Projection: KEYS_ONLY }
TTL: expiresAt
Stream: NEW_AND_OLD_IMAGES
PITR: enabled
Encryption: KMS-managed
DeletionProtection: true

Items:

PK=ORDER#o123 SK=META
  type=Order, userId=u9, restaurantId=r7, status=CREATED, version=1,
  GSI1PK=USER#u9, GSI1SK=ORDER#2026-07-06T10:05:00.000Z#o123,
  GSI2PK=PAYMENT#p555, GSI2SK=ORDER#o123

PK=ORDER#o123 SK=EVENT#2026-07-06T10:05:00.000Z#e01
  type=OrderEvent, payloadRef=s3Key

PK=ORDER#o123 SK=DETAILS
  deliveryAddress=..., notes=...     # vertical partitioning

PK=REST#r7#BUCKET#20260706T10#SHARD#03 SK=ETA#2026-07-06T10:30:00.000Z#o123
  type=ActiveRestaurantOrder, status=ACTIVE

Use UTC ISO-8601 fixed-width timestamps for lexical ordering; add IDs as tie-breakers. For reverse order, query ascending keys with inverted timestamps or use ScanIndexForward=false. Bucket and shard monotonically increasing time-series writes so all traffic does not land on the newest key.

Concrete reads:

// user orders, newest first, paginated
const out = await doc.send(new QueryCommand({
  TableName: "App", IndexName: "GSI1",
  KeyConditionExpression: "GSI1PK = :u AND begins_with(GSI1SK, :p)",
  ExpressionAttributeValues: { ":u": "USER#u9", ":p": "ORDER#" },
  Limit: 25, ScanIndexForward: false,
  ExclusiveStartKey: cursor
}));
return { items: out.Items, cursor: out.LastEvaluatedKey };

For restaurant active queues, query all shards for the current buckets, merge by ETA, and keep per-shard cursors. Choose the shard count from peak writes for one restaurant divided by the safe per-partition budget, then add headroom. High cardinality alone is not enough; traffic distribution is what matters.

Uniqueness beyond primary key uses sentinel items in a transaction:

await doc.send(new TransactWriteCommand({ TransactItems: [
  { Put: { TableName:"App", Item:{PK:"EMAIL#a@x",SK:"UNIQUE",userId},
    ConditionExpression:"attribute_not_exists(PK)" } },
  { Put: { TableName:"App", Item:{PK:`USER#${userId}`,SK:"META",email:"a@x"},
    ConditionExpression:"attribute_not_exists(PK)" } }
]}));

Inventory reservation:

await doc.send(new UpdateCommand({
  TableName: "App",
  Key: { PK: "SKU#sku1", SK: "STOCK" },
  UpdateExpression: "SET reserved = reserved + :n",
  ConditionExpression: "available - reserved >= :n",
  ExpressionAttributeValues: { ":n": 2 }
}));

Materialized views are duplicated data. If you update them in a transaction, reads are consistent but writes cost more. If Streams update them, views lag. Store the source version, reconcile missing projections, and rebuild with export/backfill plus stream catch-up.

4. Relational-to-DynamoDB Migration Design

Interview-ready migration plan:

  1. Inventory SQL queries, cardinality, item sizes, RPS, joins, consistency needs, and which reports remain in OLAP/search.
  2. Design versioned DynamoDB items and dual readers that tolerate old/new attributes.
  3. Snapshot with isolation and record the source LSN/binlog position.
  4. Transform via DMS, export to S3, Glue/EMR, or custom workers.
  5. Backfill with parallel scan/range workers, BatchWriteItem, checkpoint items, adaptive rate limits, exponential backoff, and unprocessed-item retries.
  6. Apply CDC after the snapshot position, ordered per source key. Deduplicate snapshot and CDC writes by source version/LSN.
  7. Validate deterministic per-entity checksums, query-result diffs, lag thresholds, mismatch budgets, and business invariants. Quarantine mismatches.
  8. Shadow reads, canary, ramp traffic.
  9. Roll back only if the old system still receives writes or reverse CDC/dual-write reconciliation is proven.

Deletes need an explicit design: soft-delete markers for APIs, tombstones for CDC ordering, deletion of duplicate index items, TTL for eventual cleanup, and referential cleanup workers. TTL is asynchronous, may take days, can emit stream records, and is not a scheduler.

Dual writes are where migrations usually get messy. Prefer one writer plus CDC/outbox. If dual writing cannot be avoided, make writes idempotent, record operation IDs, reconcile partial failures, define ordering, and document which store wins during failover.

Backfills can get expensive fast. Shape the write rate, temporarily use on-demand or provisioned warm capacity, account for GSI backfill, monitor throttling, and avoid starving production.

5. High-Scale Metadata Stores: Object Storage and Deduplication Use Cases

DynamoDB stores metadata, not object bytes. Keep manifests small or split them:

PK=OBJECT#obj1 SK=VERSION#000042  state=FINAL, manifestId=m1
PK=MANIFEST#m1 SK=PART#000000     chunk hashes 0..999
PK=MANIFEST#m1 SK=PART#000001     chunk hashes 1000..1999
PK=TENANT#t1#HASH#h SK=CHUNK      refCount=7, state=LIVE

If a manifest may exceed 400 KB, store the manifest in object storage and keep only pointer, checksum, size, and page count in DynamoDB.

Upload lifecycle: PENDING -> FINALIZING -> FINAL or ABORTED; commit is idempotent by upload ID/request hash. Overwrites create new versions. Deletes create delete markers before garbage collection. Reads define whether they see only the latest committed version.

Safe reference counting protocol: finalize upload in a transaction that creates the object version, links chunks with idempotency keys, and increments counters conditionally. Unlink writes tombstones and decrements once. GC only deletes chunks with refCount=0, no active finalization lease, and grace period elapsed. Workers use leases, retry counts, and crash recovery; object-storage delete failures leave retryable tombstones.

Dedup requires cryptographic hashes, tenant privacy rules, collision handling by verifying size/secondary hash or byte comparison, and encryption implications. Cross-tenant dedup can leak existence unless explicitly allowed. Hot hashes use routing records:

PK=HASH#h SK=ROUTE shardCount=16
PK=HASH#h#S#07 SK=CHUNK refDelta=...

Reads fetch the route, then fan out or read the selected shard. Writes choose the shard by object ID to avoid double counting.

6. Capacity, Partitioning, Hot Keys, and SLA Protection

A common single-physical-partition design ceiling is about 1,000 WCU or 3,000 strongly consistent RCU. Adaptive capacity and split-for-heat help, but not for a single hot item, bad LSI choices, or monotonic latest-bucket writes. On-demand has quotas, maximum throughput settings, previous-peak behavior, and may need warm throughput/pre-warming for launches. Provisioned mode has auto-scaling lag but can use reserved capacity.

SLA control loop:

  • API layer: deadlines, request budgets, idempotency keys.
  • Queue layer: bounded queues and backpressure for non-critical work.
  • DynamoDB client: SDK standard/adaptive retries, exponential backoff with jitter.
  • Capacity guard: shed low-priority writes when throttling or p99 crosses threshold.
  • Cache: Redis/DAX/in-process for hot read-only data with invalidation.
  • Circuit breakers: stop retry storms from degraded upstreams.

Retryable: ProvisionedThroughputExceededException, ThrottlingException, transient 5xx, unprocessed batch items. Usually non-retryable without changing input: ConditionalCheckFailedException, validation errors. TransactionCanceledException requires checking cancellation reasons; some causes are conditional conflicts, others are throttling.

Observability: CloudWatch consumed capacity, throttled requests, latency, system errors, account/table quotas, GSI metrics, stream iterator age, Lambda failures, DLQ depth, PITR/backups, and Contributor Insights for hot keys. Log request IDs, consumed capacity on sampled calls, key prefixes, throttling reason codes, and page counts.

Cost model includes table storage, GSI storage, reads/writes, transactions, streams, Lambda consumers, PITR, backups, exports, restores, global-table replicated writes, data transfer, KMS, and every denormalized item.

7. Consistency, Transactions, Idempotency, and Async Workflows

Strongly consistent reads apply to base tables and LSIs in a region. GSIs and Global Tables are eventually consistent. Use conditions for correctness, not "read then write."

Optimistic locking:

try {
  await doc.send(new UpdateCommand({
    TableName: "App",
    Key: { PK: "ORDER#o123", SK: "META" },
    UpdateExpression: "SET #s=:s, version=version+:one",
    ConditionExpression: "version=:expected",
    ExpressionAttributeNames: { "#s": "status" },
    ExpressionAttributeValues: { ":s": "PICKED_UP", ":one": 1, ":expected": 7 }
  }));
} catch (e:any) {
  if (e.name === "ConditionalCheckFailedException") {/* re-read and decide */}
  else throw e;
}

Exactly-once is not a DynamoDB feature. Build idempotent consumers. Store eventId, source version, status, retry count, and poison reason. Valid state transitions are conditional: QUEUED -> RUNNING -> SUCCEEDED/FAILED. Workers claim jobs with a lease:

Condition: status='QUEUED' OR leaseUntil < now
SET status='RUNNING', workerId=:w, leaseUntil=:t, attempts=attempts+1

Terminal states are idempotent; duplicate workers cannot overwrite them.

Outbox keys should avoid hot dates:

PK=OUTBOX#SHARD#07 SK=2026-07-06T10:05:00Z#eventId
  state=READY, leaseUntil, attempts, publishKey, payloadRef

Choose Streams when table changes themselves are events and 24-hour replay is enough. Choose an explicit outbox when you need publish intent, payload shaping, leases, retries, cleanup, and no double-publish. Use queues/event buses for buffering and fan-out.

8. Data Ingestion, Streams, CDC, and Analytics Pipelines

DynamoDB Streams retain records for 24 hours. They preserve order for modifications to the same item key, not global order. Parallel consumers can reorder across keys, so design per-entity idempotency.

Lambda consumers need partial batch response, bisect-on-error, DLQ/on-failure destination, idempotent downstream writes, checkpoints, and stream-lag alarms.

Derived systems need snapshot plus catch-up: export table/PITR snapshot to S3, build search/lakehouse output, then apply stream records after the export point, validate counts/checksums/query diffs, and switch readers. Analytics options: Export to S3, Glue/Athena-style querying, Kinesis/Firehose pipelines, OpenSearch indexing, Redshift/lakehouse sinks.

Scans are fine for controlled backfills, exports, small admin tables, or offline jobs. Use parallel scan, pagination, rate limits, consumed-capacity monitoring, and remember scans lack snapshot isolation unless using export/PITR.

9. Common DynamoDB Interview Pitfalls and How to Answer Follow-ups

PitfallFix
"Filter by status/date" without an indexmake status/date part of PK/SK or GSI
forgetting paginationreturn cursor from LastEvaluatedKey
assuming GSI strong consistencysay GSIs are eventual
ignoring 400 KBvertical partition or object storage
single global counter/keyuse sharded counters or per-entity counters
overusing GSIschoose: GSI for critical alternate query; duplicate lookup item for unique/direct lookup; separate table for different lifecycle/owner; search index for text/facets; OLAP for aggregations
missing null/absent distinctionsparse index membership needs absent valid key attrs
treating TTL as exactuse workflow scheduler/queue for exact timing
no securityIAM least privilege, KMS encryption, VPC endpoints, audit logs, tenant key prefixes, per-tenant quotas, PII minimization, deletion workflows
no operationsalarms, PITR, restore tests, deletion protection, blue/green tables, GSI backfill monitoring, runbooks

Final interview checklist:

  1. State access patterns with RPS, item size, latency, consistency, retention.
  2. Draw PK/SK and GSIs with projections.
  3. Show one write path, read path, pagination, and capacity math.
  4. Explain hot-key mitigation and retry/backpressure.
  5. Use conditional writes for invariants and idempotency.
  6. Separate operational serving from search/analytics/blob storage.
  7. Cover migration: snapshot, CDC, validation, cutover, rollback.
  8. Cover reliability/security: PITR, backups, restores, Global Tables if needed, IAM, encryption, tenant isolation, observability.

Comments (0)


Related Articles

From Non-CS Major to Software Engineer: A Practical Guide to Cracking the Technical Interview

Prepare for technical interviews with a practical guide to DSA practice, live coding, mock interviews, communication, and interview mindset.

Software Engineer

From Non-CS Major to Software Engineer: A Practical Guide to Cracking the Technical Interview

Prepare for technical interviews with a practical guide to DSA practice, live coding, mock interviews, communication, and interview mindset.

Software Engineer

Design WhatsApp: the presence and receipt problems most candidates ignore

Design WhatsApp-style chat with WebSockets, offline inboxes, Kafka partitions, presence TTLs, receipts, and reliable delivery.

Software Engineer

I Pinned Our Autoscaler for a Month to See What Would Break. Nothing Did.

Learn when Kubernetes autoscaling helps, when CPU-based HPA wastes money, and how capacity planning can cut cloud costs safely.

Software Engineer
PracHub

Master your tech interviews with 8,500+ real questions from top companies.

Product

  • Questions
  • Learning Tracks
  • Interview Guides
  • Resources
  • Premium
  • For Universities

Browse

  • By Company
  • By Role
  • By Category
  • Topic Hubs
  • SQL Questions
  • AI Coding Questions
  • Compare Platforms
  • Discord Community

Support

  • support@prachub.com
  • (916) 541-4762

Legal

  • Privacy Policy
  • Terms of Service
  • About Us

© 2026 PracHub. All rights reserved.