The catalogue is not the thing to learn
AWS publishes something north of two hundred services, and the reflex when learning it is to start memorising them. That reflex is wrong. The catalogue churns, half of it will never touch your system, and the parts you do use will be re-read from the documentation anyway. What does not churn is the substrate: the way the platform is carved up geographically, the boundary that contains a mistake, the split between the API that changes your infrastructure and the traffic that flows through it, and the billing model that quietly decides whether a design is affordable.
Those things are true across every service. They are also the things a newcomer reliably gets wrong, usually in the same order - a single-zone deployment, one account holding production and experiments, retry code that assumes a create call is immediately visible, and a bill dominated by a line item nobody budgeted for. This article is a map of that substrate. Each section stays at one altitude and hands the detail to the article that owns it: the sibling pieces on IAM conditions, Kinesis, data lakes, PrivateLink and the rest go deep where this one deliberately does not.
Regions, zones, and edge - the geography you cannot abstract away
The top-level unit is the region: an independent installation of the platform in one part of the world, with its own control plane, its own service endpoints, and its own copy of your resources. Regions are deliberately isolated from each other. A bucket in Frankfurt is not visible to an API call aimed at Oregon; a queue does not span the two. Nothing replicates across regions unless you explicitly configure a feature that does it, and each of those features - S3 replication, DynamoDB global tables, cross-region read replicas - is a separate opt-in with its own consistency story.
Inside a region sit availability zones: groups of data
centres with independent power, cooling and network paths, close enough for
synchronous replication and far enough apart that a flood or a substation
failure should not take two of them. A subtlety that bites multi-account teams:
the zone name you see, us-east-1a, is mapped per account, so two
accounts naming the same letter may be pointing at different physical zones.
The stable identifier is the AZ ID, such as
use1-az1, and that is what you compare when you are pinning
workloads together or apart across accounts.
Outside the regions is the edge: a much larger set of points of presence that terminate connections close to users and forward over the AWS backbone. That is where CloudFront, Route 53 and Global Accelerator live. Edge locations run no general compute for you; they shorten the first hop and absorb traffic, which is a different job from running your application.
A handful of services are global rather than regional - IAM, Route 53, CloudFront, Organizations. They look convenient until you notice their control planes are homed in a single region, so the blast radius of a problem there is wider than the region boundary suggests. Design for it rather than being surprised by it.
AZ awareness is the single most consequential architectural fact
If you take one structural idea away, take this one: the availability zone is the failure domain that your architecture has to be explicit about, and almost every AWS primitive is either scoped to a zone or spread across them. Subnets belong to exactly one zone. An EBS volume lives in one zone and can only attach to an instance in that zone. An instance is in one zone. A load balancer, by contrast, places nodes in each zone you enable and only sends traffic to zones it has been told about. EFS exposes a mount target per zone. A Multi-AZ database keeps a standby in a second zone and fails over by moving a DNS name.
Get this wrong and the failure is not subtle. A three-instance cluster that happens to land in one zone has the availability of one data centre no matter how many replicas it runs. An autoscaling group with subnets in a single zone cannot recover when that zone is impaired, because there is nowhere else to launch. A quorum system spread over two zones loses quorum when either one goes - which is why quorum services want three.
The counterweight is cost and latency. Traffic between zones is billed per gigabyte and charged on both sides of the conversation, so a chatty service mesh that ignores topology can turn zone redundancy into a large recurring line item. Latency between zones is small but not zero, which matters for synchronous replication and for anything doing many sequential round trips. The design question is therefore never "multi-AZ or not" but "which tier replicates synchronously across zones, which one keeps its traffic zone-local, and where does the boundary sit". See VPC subnets and AZs for the networking mechanics and multi-region architecture for the tier above this one.
The account is the blast radius
An AWS account is not an administrative convenience. It is the hardest isolation boundary the platform offers short of a separate region, and it is also the unit that shows up on the bill and the unit that owns service quotas. Everything inside one account shares a default trust relationship, a quota pool, and a single place where a wrong policy can reach further than intended. Two accounts share nothing until you deliberately connect them.
That is why the modern default is many accounts, not one. Production, staging, security tooling, the data platform, and each team's sandbox get their own, gathered under Organizations so that billing consolidates and guardrails apply centrally. Control Tower automates the provisioning of that structure. Service control policies set the outer edge of what any principal in a member account can do - they grant nothing, they only subtract - which is what makes them the right place for rules like "no one may disable logging" or "nothing runs outside these regions".
The mechanics of who-can-do-what within and across those accounts belong to the identity articles, not here: start with IAM for principals, roles and policy evaluation, and IAM conditions for the context keys that make cross-account and tag-scoped access precise. The framing point for this map is simply that account layout is an architectural decision made early and expensive to change late, because moving a live resource between accounts usually means recreating it.
Control plane and data plane fail differently
Every AWS service has two halves. The control plane is the API that creates, modifies and describes resources: launching an instance, creating a table, changing a security group. The data plane is what the resource does once it exists: packets flowing to that instance, reads and writes against that table, requests traversing that load balancer. They are built differently. Control planes are complex, comparatively centralised, and low-volume. Data planes are simple, massively distributed, and carry orders of magnitude more traffic.
The practical consequence is that the two produce different outages. A control-plane impairment often leaves running systems completely healthy - your instances keep serving, your queues keep draining - while nothing new can be created or changed. A data-plane impairment is the one your users notice immediately. Most large regional events start as the former, and what turns them into user-visible outages is architecture that depends on the control plane to stay up.
This is the reasoning behind static stability: build so the steady state survives without control-plane calls. Pre-provision the standby capacity in the second zone instead of assuming you can scale into it during the event. Keep enough headroom that recovery does not require launching anything. Avoid a health check that calls a describe API on the hot path. Cache the credentials and configuration your instances need at start-up rather than fetching them per request. The system that has already made its decisions is the one that survives the hour when new decisions cannot be made.
Control-plane APIs are eventually consistent, and your code must know it
Newcomers write infrastructure code as though the API were a database transaction: create the role, then immediately assume it; create the bucket, then immediately write a policy referring to it; tag a resource, then filter on that tag. All of these can fail intermittently, because control-plane state propagates asynchronously across the fleet that serves the API. A successful create means the request was accepted and durably recorded, not that every endpoint in the region can already see the result.
Identity changes are the classic case - a newly created role or a modified
policy takes a moment to be visible everywhere, so the first
AssumeRole after creation may be denied while the second succeeds.
Describe calls behave similarly: a resource you just made may be missing from a
list for a short while. The correct response is never a fixed sleep. It is to
retry on the specific transient errors with exponential backoff and jitter, and
to poll for the state you actually need rather than for wall-clock time. The
AWS SDKs ship waiters that do exactly this, and their retry modes will handle
throttling for you if you let them.
Retrying safely requires idempotency, which is why so many mutating APIs accept a client-supplied token: send the same token on the retry and a duplicated request is recognised rather than executed twice. Without it, a timeout that was actually a success becomes two instances, two orders, two charges. The discipline is simple to state and easy to skip: generate the token once per logical operation, not once per attempt, and make every automated action either idempotent or safely repeatable.
Unmanaged, managed, serverless - the taxonomy that decides who is on call
The useful way to classify AWS services is not by what they do but by how much of the operational job they take. Three bands cover almost everything.
Unmanaged - you get a machine
EC2 and friends hand you compute, storage and a network and step back. You choose instance types, patch the guest OS, install and tune the software, build the AMI pipeline, and own capacity planning. This is maximum control and maximum operational surface, and it is the right answer when the software has requirements no managed offering matches.
Managed - you get a running system with knobs
RDS, MSK, OpenSearch and EKS run a known piece of software for you. Provisioning, replication, backups and version upgrades become API calls, but you still size the cluster, choose a maintenance window, and own the workload's behaviour on top. Capacity is still a decision you make in advance.
Serverless - you get an interface
Lambda, SQS, DynamoDB on-demand and Fargate expose behaviour without exposing a machine. There is no instance to patch and often no capacity to choose; you pay per request or per unit of work and scale is the platform's problem within its quotas. The cost is a narrower contract: you live inside the service's limits, its execution model, and its cold-start behaviour. See the serverless architecture piece for how these compose.
Choosing a band is choosing who carries the pager at three in the morning. It is a staffing decision as much as a technical one, and it is usually the first thing to get right.
Shared responsibility, and where the line actually falls
The shared responsibility model is usually summarised as AWS securing the cloud and you securing what you put in it. True, and too vague to act on. The line moves with the service class described above, and knowing where it sits for each thing you run is the whole point of the model.
On an instance, AWS owns the hardware, the hypervisor - the Nitro system is where that boundary is implemented - and the physical network. You own the guest OS and every package on it, which means kernel CVEs are your patching problem. On a managed database, engine patching becomes AWS's job within a window you choose, but the major-version upgrade is still your project, and the parameter group, the network exposure and the credentials are entirely yours. On an object store, durability and the physical media are AWS's; whether the bucket is public is 100% yours.
Two responsibilities never move regardless of service class. Configuration is always yours - nothing in the platform prevents you from making a private dataset world-readable, and the platform will carry out that instruction perfectly. And identity is always yours: no managed service will stop you granting a wildcard action to a wildcard resource. That is why the guardrail layer - Config for continuous evaluation, CloudTrail for the audit record, SCPs for the hard edges, and KMS key policies for the cryptographic one - is not optional decoration. It is the customer half of the model, implemented.
Quotas are a design constraint, not a support ticket
Every service enforces limits, and they come in two flavours that people conflate. Resource quotas cap how much of a thing an account may hold in a region - instances of a family, VPCs, rules per security group, concurrent function executions. Rate quotas cap how often you may call an API, and they are usually token-bucket shaped: a sustained refill rate plus a burst allowance, so a workload can look fine until it bunches its calls. Exceeding either returns a throttling error, not a queue.
Some quotas are adjustable through Service Quotas or support, and some are architectural constants that will never move because they reflect how the service is partitioned. Treating the second kind as a paperwork problem is the mistake. When a limit is per-shard, per-partition or per-connection, the fix is to reshape the workload - more partitions, better key distribution, batching - not to ask for a bigger number. The Kinesis article works through exactly this for per-shard throughput, and the pattern generalises.
Three habits keep quotas from becoming incidents. Find the binding limits for your critical services before launch, not during the first traffic spike. Alarm on approach rather than on breach, since quota metrics are published and the useful signal is the trend. And remember quotas are scoped per account and per region, so consolidating workloads into one account concentrates quota pressure the same way it concentrates blast radius - another argument for the multi-account layout.
The shape of the bill, and the line item people miss
Pricing has a repeating structure once you stop reading it service by service. Compute is sold on a spectrum of commitment: on-demand at the top, reserved capacity and savings plans in exchange for a one- or three-year commitment, and spot at the bottom for spare capacity that can be reclaimed with short notice. Storage is priced per gigabyte-month, usually with tiers that trade retrieval latency and retrieval fees for a lower resting price - see S3 storage classes. Serverless and API-shaped services price per request plus per unit of work, which makes them close to free when idle and worth modelling carefully when busy.
The line item that surprises people is data transfer. Traffic in from the internet is generally free; traffic out is not. Traffic between availability zones is billed, in both directions, which is why a badly-placed replica or a cross-zone chatty service can dominate a bill that was budgeted purely on compute. Traffic between regions is billed and costs more. A NAT gateway charges per hour and per gigabyte processed, so private subnets that pull large container images or talk to public service endpoints through NAT accumulate cost invisibly - one reason VPC endpoints and PrivateLink often pay for themselves. Model the network path, not just the boxes at either end.
The structural point: architecture and bill are the same document. Choosing where data lives, how often it crosses a boundary, and how much commitment you can make is a design activity, and doing it after the fact is far harder than doing it up front.
Infrastructure as code is the only interface that scales
The console is a fine place to learn a service and a terrible place to run one. Anything clicked into existence has no history, no review, no reproduction in a second region, and no way to tell six months later whether its current shape was intended. Once a system spans more than a handful of resources - or more than one account, which the earlier section argues it should - a declarative definition stops being good practice and becomes the only workable interface.
The options divide into template-first and program-first. CloudFormation is the native engine that understands stacks, rollbacks and drift; the CDK lets you generate those templates from a real programming language, which is a large ergonomic win and a real abstraction risk when the generated resources stop being obvious. Terraform occupies the same space with its own state model and multi-provider reach. The choice matters less than the commitment: one system of record, per environment, applied by a pipeline rather than by a laptop.
The failure mode to plan for is drift - the divergence between the declared state and the real one, created by an emergency console change that nobody backported. Drift is not an accident to be scolded away; it is a certainty, because emergencies happen. Detect it continuously with stack drift detection and Config rules, make the backport a routine part of incident cleanup, and restrict who can mutate production outside the pipeline so the exceptions are few enough to notice.
Choosing among the four services that all seem to fit
AWS almost never gives you one obvious answer. You want a queue and find SQS, Kinesis, MSK and EventBridge. You want containers and find ECS, EKS, Fargate, App Runner and Beanstalk. You want a relational database and find RDS, Aurora, Aurora Serverless and, if you squint, DynamoDB. The overlap is real - these are different points on a set of tradeoffs, not marketing duplication - and the way through is to ask a fixed set of questions rather than to compare feature tables.
What is the unit of scaling and of billing? A shard, a provisioned instance, a request, a gigabyte-month. That single answer usually tells you which service fits the traffic shape you expect, especially at the extremes of spiky and idle.
Who owns the state, and what happens to it in a failover? Services that hold your data have far higher switching costs than services that route it. Spend the analysis budget there.
What semantics do you actually need? Ordering, exactly-once delivery, replay of history, and fan-out to many consumers are the axes that separate the messaging services from each other, and they are the questions the SQS, Kinesis and EventBridge articles each answer for their own service.
What operational surface are you signing up for, and what is the exit? A deeply proprietary managed service can be exactly right; just make the decision with the migration cost visible rather than discovering it later. ECS versus EKS is the canonical worked example of this comparison.
Where the map goes next
Follow the layer you are working in. For identity and the account structure: IAM, IAM conditions, Organizations and SCPs, Control Tower. For networking: VPC, subnets and AZs, Transit Gateway, PrivateLink, the load balancer family. For compute and containers: EC2, ECS and Fargate, ECS versus EKS, Lambda. For storage and data: S3, EBS, EFS, data lake architecture, Kinesis. For resilience beyond the zone: multi-region architecture and global tables. For the guardrails: Config, CloudTrail, secrets rotation.
Read those for depth. Come back to this page when a design decision feels service-specific but is actually one of the cross-cutting choices above - which is more often than it looks.
AWS is easier to learn as four cross-cutting facts than as two hundred services. The availability zone is the failure domain your architecture must name explicitly. The account is the blast radius and the quota pool, which is why multi-account under Organizations is the default. The control plane is a separate, more fragile system from the data plane, so build steady states that survive without it and write retries that are idempotent and backoff-aware. And the bill is a design artefact, with data transfer across zones, regions and NAT as the line item that most often escapes the estimate. Every service you pick sits somewhere on the unmanaged-managed-serverless spectrum, and that position - not the feature list - decides who is on call.