AlgoInsight

Free lesson · AWS for System Design Interviews

The service-to-primitive map

Easy8 min readawssystem-designinterview-preparation

How To Use This

A working reference for translating in both directions: from a requirement to a primitive to a service, and from a service back to the neutral vocabulary that works in any interview.

Organised by primitive, because that is the direction reasoning actually runs. You do not start with a service and look for a problem.

Tip: The column that matters most in an interview is the third one - what the primitive guarantees.

Naming the primitive is the entry price. Stating what it guarantees is the signal, because it demonstrates the choice was made against requirements rather than by association.

"I need an append-only log - ordered within a partition, retained so consumers can replay, each consumer holding its own offset" is a complete design statement. The service name that follows is almost incidental, which is the point.


Storage Primitives

Need Primitive Guarantees AWS Elsewhere
Store blobs, retrieve whole Object store Durable, HTTP, flat namespace, not a filesystem S3 GCS, Blob Storage, MinIO
A disk for one machine Block storage Low latency, single-attach, AZ-scoped EBS Persistent Disk, Managed Disks
Shared filesystem Distributed filesystem POSIX, multi-attach, higher latency EFS, FSx Filestore, Azure Files, NFS
Fast scratch Local disk Fastest, ephemeral Instance store Local SSD
Cheap long-term retention Archive storage Cheap storage, slow and costly retrieval Glacier tiers Coldline, Archive

Data Primitives

Need Primitive Guarantees AWS Elsewhere
Lookup by key, huge scale Partitioned key-value store Predictable latency, hot-key ceiling, fixed access patterns DynamoDB Cassandra, Bigtable, Cosmos DB
Joins, transactions, ad-hoc queries Relational database ACID, flexible querying, single writer RDS, Aurora PostgreSQL, MySQL, Cloud SQL
Relational at write scale Distributed SQL ACID across partitions, higher latency Aurora Limitless, DSQL Spanner, CockroachDB, Vitess
Sub-millisecond reads In-memory cache Fast, volatile, limited by memory ElastiCache, MemoryDB Redis, Memcached
Full-text and faceted search Search index Inverted index, eventually consistent with the source OpenSearch Elasticsearch, Solr
Time-ordered measurements Time-series store Optimised for time-range queries, downsampling Timestream Prometheus, InfluxDB
Relationships and traversal Graph database Efficient multi-hop traversal Neptune Neo4j, JanusGraph
Analytical scans Columnar warehouse Column pruning, compression, not for point reads Redshift, Athena BigQuery, Snowflake
Similarity search Vector index Approximate nearest neighbour OpenSearch, Aurora pgvector Pinecone, FAISS

Warning: The two rows most often confused are the partitioned key-value store and the relational database, and the discriminator is not scale.

It is whether the access patterns are known and bounded. A key-value store fixes them at design time via the key; adding a new one later means an index or a migration. A relational database lets you ask questions you did not anticipate.

For a system whose queries are still being discovered - most new products - relational is the honest choice, and "it will not scale" is usually premature. A well-indexed relational database handles far more than candidates assume.


Messaging Primitives

Need Primitive Guarantees AWS Elsewhere
Distribute work, absorb bursts Queue Consumed once, at-least-once, no ordering unless FIFO SQS RabbitMQ, Cloud Tasks
Ordered, replayable event stream Append-only log Ordered per partition, retained, per-consumer offsets Kinesis, MSK Kafka, Pub/Sub
One event, many independent consumers Pub/sub Fanout, no retention, at-least-once SNS, EventBridge Pub/Sub, NATS
Multi-step process with state Workflow engine Durable execution state, retries, compensation Step Functions Temporal, Cadence, Airflow
Scheduled work Scheduler Time-based triggering EventBridge Scheduler cron, Cloud Scheduler

Compute Primitives

Need Primitive Guarantees AWS Elsewhere
Run a process per event Function runtime Instant scale, duration and size caps, stateless Lambda Cloud Functions, Knative
Run containers without hosts Managed container runtime No host management, scale-out lag Fargate Cloud Run, Container Apps
Orchestrate containers Container orchestrator Desired-state reconciliation ECS, EKS Kubernetes, Nomad
Full control of the machine Virtual machine Everything, and you operate it EC2 Compute Engine, VMs
Bounded parallel jobs Batch scheduler Queueing, retries, interruptible capacity AWS Batch Slurm, Kubernetes Jobs

Edge, Network, And Coordination

Need Primitive Guarantees AWS Elsewhere
Cache near users CDN Edge caching, invalidation is the hard part CloudFront Cloudflare, Fastly, Akamai
Distribute across servers Load balancer Health checking, L7 routing or L4 forwarding ALB, NLB nginx, Envoy, HAProxy
Name resolution and failover DNS Caching you do not control Route 53 any DNS provider
API front door Reverse proxy / gateway Auth, throttling, transformation API Gateway Kong, Envoy, nginx
Distributed locks, leader election Coordination service Consensus, the hardest thing here DynamoDB conditional writes ZooKeeper, etcd, Consul
Store credentials Secrets store Encrypted, access-controlled, rotatable Secrets Manager Vault, Secret Manager
Manage encryption keys Key management Hardware-backed hierarchy, envelope encryption KMS Cloud KMS, Vault

Important: The coordination row deserves attention because it is the one candidates most often wave at.

"I'd use a distributed lock" is a sentence that hides the hardest problem in distributed systems. Consensus is expensive, lock holders fail while holding locks, and lease expiry versus work completion is a genuine correctness problem - a holder can be paused past its lease and resume believing it still holds the lock.

On AWS the pragmatic answer is usually a conditional write to DynamoDB with a lease and a fencing token, not a general lock service. The stronger answer is usually to avoid needing the lock - partition the work so each partition has a single natural owner, or make the operation idempotent so concurrent execution is safe.

An interviewer who hears "I'd avoid needing a distributed lock by partitioning" has learned more than from any lock implementation.


Choosing Between Two Services For One Primitive

The map gets you to a primitive. Frequently two services implement it, and the discriminator is rarely the primitive itself.

Both provide Discriminator
SQS vs Kinesis Queue or log - will anything replay?
ECS vs EKS Operational cost - can you staff Kubernetes?
RDS vs Aurora Read scaling and failover speed vs cost
ElastiCache vs MemoryDB Durability - is cache loss acceptable?
SNS vs EventBridge Routing sophistication vs simplicity
Lambda vs Fargate Demand shape and duration
DynamoDB vs Cassandra Managed vs control, and where you run

The discriminator is usually operational cost, durability, or demand shape - not capability. Two services implementing the same primitive largely do the same thing; what differs is what they demand of you and what they guarantee at the edges.

In an interview, naming the discriminator is the signal. "Either works - I'd choose ECS because we cannot staff two or three Kubernetes engineers, and that is the actual constraint" demonstrates you evaluated it. "I'd use EKS" does not.


Translating In Both Directions

graph LR
    R[**Requirement**<br/>'users must see<br/>their own posts<br/>immediately'] --> P[**Property**<br/>read-your-writes,<br/>bounded by user]
    P --> PR[**Primitive**<br/>key-value store,<br/>partitioned by user]
    PR --> S[**Service**<br/>DynamoDB,<br/>or Cassandra]
    S -.->|**Reverse direction**:<br/>interviewer names<br/>a service| PR
    PR -.->|state the primitive<br/>and its guarantees| P
This diagram shows translation running in both directions. A requirement, such as users must see their own posts immediately, produces a property - read-your-writes bounded by user. That identifies a primitive, a key-value store partitioned by user. That identifies a service, DynamoDB or Cassandra. In the reverse direction, when an interviewer names a service, translate back to the primitive and state its guarantees rather than accepting the product name as the unit of discussion.

The reverse direction matters more than people expect. When an interviewer says "suppose we use Kafka here", the strong response translates: "so an append-only log - ordered per partition, retained, consumers holding offsets. That means I can add the analytics consumer later without touching the ingestion path."

You have accepted their framing and demonstrated you know what it buys.


What The Map Does Not Contain

Operational cost, which is frequently the deciding factor. Two services implementing the same primitive can differ enormously in what they demand of a team - EKS and ECS are the standing example.

Maturity and ecosystem. The primitive is identical; the tooling around it is not.

The specific limits. Deliberately absent, because they change. Reason about the shape and verify the number - Module 02 Topic 4.


Common Failure Modes

Warning: Memorising the map without the guarantees column. The signal is in the guarantees.

Queue chosen where a log was needed. Not reversible cheaply.

Key-value store chosen for unknown access patterns.

"It will not scale" applied to relational databases prematurely.

Search index treated as a source of truth. Eventually consistent with it.

Distributed locks waved at. The hardest thing in the table.

Columnar warehouse used for point reads.

Only translating one direction. The reverse is common in interviews.

Operational cost ignored. Same primitive, very different burden.

Specific limits quoted from memory.


Why This Matters In An Interview

Interview: What interviewers are listening for:

  • The primitive named with its guarantees, before any product.
  • Translating a service the interviewer named back into neutral terms.
  • The queue/log discrimination.
  • Avoiding coordination rather than implementing it.

Common follow-ups:

  • "What would you use for the event pipeline?" → An append-only log: ordered per partition, retained so consumers replay, per-consumer offsets. That lets analytics be added later without touching ingestion. Kinesis or Kafka.
  • "Suppose we already run Kafka." → Then it is an append-only log with the same properties. The design does not change; ordering is per partition, so the partition key must carry whatever ordering the domain requires.
  • "How do you ensure only one worker processes each job?" → Preferably by partitioning so each key has one natural owner, or by making processing idempotent. If a lock is genuinely required, a conditional write with a lease and a fencing token - and the lease-versus-completion problem is real.
  • "Would a relational database work here?" → Frequently yes, and further than expected. It depends on whether the access patterns are known and bounded. If they are still being discovered, relational is the honest choice.

Practice

Exercise: Starter. Cover the AWS column and, for each primitive, name the service. Then cover the primitive column and do the reverse.

Intermediate. For every service in a design you can already give, write the primitive and its guarantees. Deliver the design using only that vocabulary.

Advanced. Take a design that uses a distributed lock and redesign it so no lock is needed - by partitioning, or by making the operation idempotent.


Related Topics

Back to AWS for System Design Interviews