Skip to main content
  1. Posts/

How to Build a CloudWatch Dashboard for an E-Commerce Platform

Table of Contents
Production Operations - This article is part of a series.
Part 3: This Article

A CloudWatch dashboard can be technically correct and still be almost useless during an incident.

I’ve seen plenty of dashboards that amount to a collection of infrastructure graphs:

  • EC2 CPU
  • RDS CPU
  • ECS memory
  • Redis memory
  • OpenSearch CPU

Those metrics aren’t wrong.

The problem is that they don’t answer the first question I care about when something goes wrong:

Are customers actually being affected?

And if they are, I want the rest of the dashboard to help me answer the next question:

Where is the request failing?

That distinction completely changes how I build operational dashboards.

For an e-commerce platform, I don’t want CloudWatch organized like the AWS console. I want it organized like the application.

The general flow I use looks more like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
Customer
Cloudflare
Application Load Balancer
   ├────► Next.js / ECS Fargate
   └────► Magento / EC2 Auto Scaling
                    ├────► RDS
                    ├────► ElastiCache / Valkey
                    ├────► OpenSearch
                    └────► SQS / Background Processing

The dashboard should follow essentially the same path.

In this guide, I’m going to build one from that perspective.

The finished dashboard will be organized roughly like this:

  1. Customer impact
  2. Traffic, errors, and latency
  3. Next.js / ECS
  4. Magento / EC2
  5. Database
  6. Cache
  7. Search
  8. Queues and asynchronous processing
  9. Application logs
  10. Distributed traces

This isn’t intended to be a pretty wallboard that looks impressive on a television.

It’s intended to be something I can open during an incident and immediately start narrowing down the failure.

The Architecture We’re Monitoring
#

The example throughout this article is based on the kind of architecture I deal with in production.

The frontend is Next.js running on ECS Fargate.

The backend is Magento 2 running across an EC2 Auto Scaling Group behind an Application Load Balancer.

Supporting services include:

  • Amazon RDS
  • ElastiCache running Valkey
  • Amazon OpenSearch Service
  • Amazon SQS
  • CloudWatch Logs
  • AWS X-Ray

Cloudflare sits in front of the platform, but this article focuses primarily on the AWS side of observability.

That distinction matters because CloudWatch cannot tell me everything Cloudflare knows about the request. Likewise, Cloudflare doesn’t necessarily understand what’s happening inside Magento, PHP-FPM, RDS, Valkey, or OpenSearch.

I still want a few edge signals nearby when investigating an incident — Cloudflare request volume, origin error rate, cache hit ratio, WAF activity, and rate-limiting events are especially useful. I generally leave those in Cloudflare rather than trying to force them into this CloudWatch dashboard unless there is a strong operational reason to centralize them.

Cloudflare tells me what is happening at the edge.

CloudWatch tells me what is happening inside the application.

Request-path architecture from Cloudflare through ALB into Next.js on ECS Fargate and Magento on EC2, with RDS, Valkey, OpenSearch, and SQS

Don’t Start With Infrastructure
#

The easiest way to build a CloudWatch dashboard is to open the console and start clicking metrics.

That is also how you end up with 40 graphs and no idea which one matters.

Instead, I start with the questions I want the dashboard to answer.

For an e-commerce platform, mine are roughly:

Is the site working?

Can customers browse products?

Can they add products to their cart?

Can they check out?

Are orders being created?

Is the application getting slower?

Are errors increasing?

Only after that do I care whether CPU utilization happens to be 73%.

Row 1: Customer Impact
#

At the very top of the dashboard, I want a handful of large numbers.

For this platform, that means:

  • Orders per minute
  • Checkout failures
  • HTTP 5xx rate
  • p95 latency

This gives me immediate situational awareness.

If an alarm fires at 2:00 PM and I open this dashboard, I can quickly distinguish between:

“CPU is elevated.”

and:

“Customers cannot check out.”

Those are very different incidents.

Customer-impact CloudWatch widgets for orders per minute, checkout failures, HTTP 5xx rate, and p95 latency

Infrastructure Metrics Are Not Business Metrics
#

AWS gives me request counts, errors, CPU, database connections, queue depth, and dozens of other infrastructure metrics.

AWS does not inherently know what an order means.

For important application events, I prefer publishing custom metrics.

A simple namespace could look like:

1
Ecommerce/Production

With metrics such as:

1
2
3
4
5
6
7
OrdersPlaced
CheckoutAttempts
CheckoutFailures
GraphQLRequests
GraphQLErrors
CartFailures
PaymentFailures

Dimensions should remain deliberately low-cardinality.

For example:

1
2
Environment=production
Store=us

I would not create dimensions such as:

1
2
3
4
CustomerId
OrderId
SessionId
RequestId

Those belong in logs and traces, not CloudWatch metric dimensions.

Once the application publishes useful business metrics, the dashboard becomes significantly more valuable.

A sudden RDS CPU spike is interesting.

A simultaneous drop in OrdersPlaced makes it urgent.

Row 2: Traffic, Errors, and Latency
#

The next row answers:

What is happening to requests entering the application?

For an Application Load Balancer, my baseline widgets would include:

  • Request count
  • Target 4xx
  • Target 5xx
  • Load balancer 5xx
  • p50 response time
  • p95 response time
  • p99 response time
  • Healthy targets

Why Percentiles Matter
#

Average latency is one of the easiest metrics to misuse.

Suppose most requests complete in 150 ms but a meaningful percentage of customers are hitting requests that take several seconds.

The average can still look surprisingly healthy.

That’s why I want at least:

1
2
3
p50
p95
p99

Think of them roughly as:

1
2
3
p50 → what a normal request experiences
p95 → what a slow customer experiences
p99 → what the tail of the system is experiencing

When p50 remains flat but p99 explodes, I start looking for contention, slow queries, downstream services, exhausted pools, garbage collection, or other bottlenecks affecting only part of the workload.

Calculate Error Rate Instead of Just Error Count
#

A graph showing 500 errors sounds alarming.

But:

1
500 errors out of 1,000 requests

is very different from:

1
500 errors out of 10,000,000 requests

Metric math lets me turn those raw counters into something much more meaningful:

1
Error Rate = Target 5xx / Request Count × 100

Conceptually:

1
IF(requests > 0, (errors / requests) * 100, 0)

This is one of the first derived metrics I add.

Row 3: Frontend — Next.js on ECS Fargate
#

For the frontend, I care about both resource saturation and service capacity.

At minimum:

1
2
CPUUtilization
MemoryUtilization

If I enable Container Insights, I can go considerably deeper into task-level and container-level behavior.

For a production Next.js service, I want to see:

  • CPU utilization
  • Memory utilization
  • Running versus desired task count
  • Requests per target
  • ALB latency for the frontend target group

The important thing here is correlation.

Suppose traffic increases and I see:

1
2
3
4
Request volume       ↑
ECS CPU              ↑
Running task count   ↑
p95 latency           stable

Autoscaling is probably doing exactly what I want.

Now imagine:

1
2
3
4
Request volume       ↑
ECS CPU              ↑
Running task count   flat
p95 latency           ↑↑↑

That tells a very different story.

Maybe autoscaling hasn’t reacted.

Maybe a scaling limit has been reached.

Maybe task placement is failing.

Maybe the application itself has become CPU-bound.

Graphs become much more useful when they are designed to be interpreted together.

Row 4: Backend — Magento on EC2
#

The Magento tier is different.

Our backend is built into immutable AMIs and deployed through an EC2 Auto Scaling Group.

That means I want visibility into the fleet rather than obsessing over one specific EC2 instance.

Instances are disposable.

The service is what matters.

I generally care about:

  • EC2 CPU
  • instance count
  • unhealthy instances
  • status check failures
  • target response time
  • requests per target
  • target 5xx responses
  • PHP/application errors

Avoid Hardcoding Instance IDs
#

This is a perfect use case for CloudWatch search expressions.

Instead of building a dashboard around:

1
i-0123456789abcdef0

I want the dashboard to find whatever instances currently belong to the workload.

Conceptually:

1
2
3
4
5
6
SEARCH(
  '{AWS/EC2,AutoScalingGroupName} MetricName="CPUUtilization"
   AutoScalingGroupName="magento-production"',
  'Average',
  60
)

If an instance disappears tonight and another replaces it, I don’t want to edit my dashboard tomorrow.

The observability model needs to understand disposable infrastructure.

One important limitation is that a CloudWatch alarm cannot be based directly on a SEARCH expression because the expression may return multiple time series. That’s fine for dashboards, where dynamic discovery is exactly what I want, but alerting needs a separately defined operational condition.

Row 5: RDS — Where E-Commerce Problems Get Expensive
#

Once the application tier looks healthy, one of my next stops is almost always the database.

Magento in particular can make database behavior extremely important to overall application performance.

My basic RDS section includes:

  • CPU utilization
  • database connections
  • freeable memory
  • read latency
  • write latency
  • read IOPS
  • write IOPS
  • disk queue depth
  • free storage

Database Connections Deserve Their Own Graph
#

Connection growth is one pattern I always want visible.

A connection leak, badly tuned pool, sudden application scaling event, or slow queries holding connections open can create a cascading failure.

CPU may eventually rise as well.

But connections may tell me what happened first.

Correlate RDS With Application Latency
#

I particularly like putting these graphs close together:

1
2
3
4
ALB p95 response time
RDS read/write latency
Database connections
RDS CPU

If all four move at the same time, I have a much stronger lead than simply seeing “RDS CPU is high.”

If the problem requires deeper SQL analysis, that’s when I move beyond the dashboard into Database Insights or the database itself.

The dashboard should tell me where to investigate, not replace every specialized diagnostic tool.

Four correlated time-series panels showing simultaneous rises in application p95 latency, RDS connections, RDS read latency, and RDS CPU

Row 6: ElastiCache / Valkey
#

Cache problems are deceptive because the cache itself can appear operational while application performance deteriorates dramatically.

For Valkey, my key metrics include:

  • CacheHitRate
  • EngineCPUUtilization
  • Evictions
  • CurrConnections
  • memory pressure/capacity
  • read/write latency where applicable
  • replication lag where applicable

CacheHitRate is particularly valuable because it tells me something infrastructure utilization alone cannot.

Conceptually:

1
hits / (hits + misses)

Imagine this incident:

1
2
3
4
5
6
7
Valkey CPU              normal
Valkey memory           normal
Valkey connections      normal
Cache hit rate          ↓↓↓
RDS queries             ↑↑↑
RDS CPU                 ↑↑↑
Application latency     ↑↑↑

Valkey didn’t “go down.”

But the cache stopped doing its job effectively.

That is why behavioral metrics are often more valuable than simple resource-health metrics.

Row 7: OpenSearch
#

Search is another subsystem where resource utilization alone doesn’t tell the whole story.

For OpenSearch, I care about:

1
2
3
4
5
6
7
8
9
ClusterStatus.red
ClusterStatus.yellow
CPUUtilization
JVMMemoryPressure
FreeStorageSpace
SearchLatency
SearchRate
ThreadpoolSearchQueue
ThreadpoolSearchRejected

Depending on the workload I may also include indexing metrics.

Queue and Rejection Metrics Are Extremely Useful
#

CPU can look fine while requests are backing up internally.

That’s why I like:

1
2
ThreadpoolSearchQueue
ThreadpoolSearchRejected

If search latency increases and the search queue starts building, that’s actionable.

If rejections start increasing, even more so.

For an e-commerce site, that can quickly become:

1
2
3
4
5
6
7
search results slow
category pages slow
product discovery suffers
conversion suffers

That is much more useful context than simply knowing an OpenSearch node is using 60% CPU.

Row 8: SQS and Background Processing
#

Queues need a slightly different mental model.

Queue depth by itself is useful, but I care at least as much about message age.

For SQS, my standard widgets include:

1
2
3
4
ApproximateNumberOfMessagesVisible
ApproximateAgeOfOldestMessage
ApproximateNumberOfMessagesNotVisible
DLQ ApproximateNumberOfMessagesVisible

Imagine a queue normally processes thousands of messages per minute.

Seeing:

1
Queue depth: 2,000

may be completely normal.

But:

1
Oldest message: 47 minutes

probably isn’t.

The combination tells the story.

1
2
Depth    ↑
Age      flat

could simply mean traffic increased and consumers are keeping pace.

But:

1
2
Depth    ↑↑↑
Age      ↑↑↑

looks like backpressure.

And then there is the metric I never want to see:

1
DLQ Messages > 0

For important order, inventory, fulfillment, or integration queues, that gets prominent placement.

Side-by-side comparison of healthy queue depth growth versus queue backpressure with rising oldest-message age and DLQ growth

Row 9: Logs Insights — Put Errors on the Dashboard
#

Metrics tell me that something is wrong.

Logs often tell me what is wrong.

CloudWatch Logs Insights queries can live directly on a dashboard, which makes the dashboard a jumping-off point for investigation instead of just a collection of graphs.

If application logs are structured JSON, I can build a widget showing errors over time:

1
2
3
fields @timestamp, level, message
| filter level in ["error", "critical"]
| stats count(*) as errors by bin(5m)

Or show the most common failures:

1
2
3
4
5
fields message
| filter level in ["error", "critical"]
| stats count(*) as occurrences by message
| sort occurrences desc
| limit 20

For a GraphQL API, structured logging becomes especially useful.

If I’m logging fields such as:

1
2
3
4
5
6
{
  "level": "error",
  "operationName": "AddProductsToCart",
  "status": 500,
  "durationMs": 1832
}

I can build a query like:

1
2
3
4
5
fields operationName, status
| filter status >= 500
| stats count(*) as failures by operationName
| sort failures desc
| limit 20

Now the dashboard can tell me something far more useful than:

1
5xx errors increased.

It can tell me:

1
2
3
AddProductsToCart      1,482
PlaceOrder               219
CustomerLogin             87

That’s immediately actionable.

Structured Logging Makes Everything Better
#

If my logs look like this:

1
Something broke while processing request

there is only so much CloudWatch can do.

I want structured fields.

For example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
  "timestamp": "2026-08-13T16:04:31Z",
  "level": "error",
  "service": "magento",
  "environment": "production",
  "route": "/graphql",
  "operationName": "PlaceOrder",
  "status": 500,
  "durationMs": 2411,
  "requestId": "example-request-id",
  "errorType": "DatabaseTimeout"
}

Now I can ask useful questions.

1
stats count(*) by errorType

Or:

1
2
stats avg(durationMs), pct(durationMs, 95), pct(durationMs, 99)
by operationName

Or:

1
2
filter operationName = "PlaceOrder"
| stats count(*) by status

Good dashboards start with good telemetry.

No dashboard layout can compensate for an application that emits poor data.

Row 10: X-Ray and Distributed Tracing
#

Metrics tell me something became slow.

Logs may tell me an error occurred.

Distributed traces tell me where the request spent its time.

For an e-commerce request, I want to be able to follow something conceptually like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
Customer Request
   Next.js
   GraphQL
   Magento
      ├──── 42 ms ───► Valkey
      ├─── 610 ms ───► RDS
      └── 1,840 ms ──► OpenSearch

Now the investigation changes from:

“Magento is slow.”

to:

“Magento spent 1.8 seconds waiting on search.”

That’s a dramatically better problem statement.

I don’t need an enormous trace visualization permanently occupying half of the dashboard.

I need enough tracing context that once the high-level metrics point toward an application path, I can jump into X-Ray and inspect representative slow or failed requests.

Build the Dashboard as Code
#

Once a dashboard becomes operationally important, I don’t want its only copy living in somebody’s AWS console.

I want it in Git.

I normally keep something like this in the infrastructure repository:

1
2
3
4
5
observability/
├── dashboards/
│   └── ecommerce-production.json
└── terraform/
    └── cloudwatch.tf

That gives me:

  • code review
  • Git history
  • reproducibility
  • rollback
  • consistent environments
  • no mystery console edits

If somebody destroys the dashboard layout while investigating something at 3 AM, I can restore it from source control.

A Smaller Dashboard Definition
#

The full dashboard JSON gets large quickly, so I don’t think dumping the entire production definition into an article helps much.

Instead, here is a representative section showing the patterns that matter: a business KPI, a derived error rate, and a dynamic EC2 fleet metric.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
{
  "start": "-PT3H",
  "periodOverride": "inherit",
  "widgets": [
    {
      "type": "metric",
      "x": 0,
      "y": 0,
      "width": 6,
      "height": 5,
      "properties": {
        "title": "Orders Placed",
        "view": "singleValue",
        "region": "us-east-1",
        "period": 60,
        "metrics": [
          [
            "Ecommerce/Production",
            "OrdersPlaced",
            "Environment",
            "production",
            {
              "stat": "Sum"
            }
          ]
        ]
      }
    },
    {
      "type": "metric",
      "x": 6,
      "y": 0,
      "width": 6,
      "height": 5,
      "properties": {
        "title": "Target 5xx Rate",
        "view": "singleValue",
        "region": "us-east-1",
        "period": 60,
        "metrics": [
          [
            {
              "expression": "IF(requests>0,(errors/requests)*100,0)",
              "label": "5xx %",
              "id": "errorRate"
            }
          ],
          [
            "AWS/ApplicationELB",
            "RequestCount",
            "LoadBalancer",
            "app/example-alb/1234567890",
            {
              "stat": "Sum",
              "id": "requests",
              "visible": false
            }
          ],
          [
            ".",
            "HTTPCode_Target_5XX_Count",
            ".",
            ".",
            {
              "stat": "Sum",
              "id": "errors",
              "visible": false
            }
          ]
        ]
      }
    },
    {
      "type": "metric",
      "x": 0,
      "y": 5,
      "width": 12,
      "height": 6,
      "properties": {
        "title": "Magento Fleet CPU",
        "region": "us-east-1",
        "period": 60,
        "metrics": [
          [
            {
              "expression": "SEARCH('{AWS/EC2,AutoScalingGroupName} MetricName=\"CPUUtilization\" AutoScalingGroupName=\"magento-production\"', 'Average', 60)",
              "id": "magentoCpu"
            }
          ]
        ]
      }
    }
  ]
}

The complete definition belongs in the repository alongside the infrastructure that owns it, not buried in the middle of a blog post.

I can deploy that dashboard directly with the AWS CLI:

1
2
3
aws cloudwatch put-dashboard \
    --dashboard-name ecommerce-production \
    --dashboard-body file://ecommerce-production.json

Or let Terraform own it:

1
2
3
4
resource "aws_cloudwatch_dashboard" "ecommerce_production" {
  dashboard_name = "ecommerce-production"
  dashboard_body = file("${path.module}/../dashboards/ecommerce-production.json")
}

The exact metrics will change from platform to platform.

The structure is what matters.

Use Dashboard Variables Where They Actually Help
#

CloudWatch dashboard variables can change metric dimensions or other properties throughout a dashboard.

That makes it possible to reuse the same dashboard for things such as:

1
2
production
staging

or:

1
2
us-east-1
us-west-2

Property variables are generally the cleaner option when a resource is identified by a specific JSON property or metric dimension.

Pattern variables are more powerful but also easier to abuse because they perform substitutions against the dashboard definition itself.

My preference is to use variables where the architecture is genuinely identical.

I don’t try to create one magical dashboard that represents every application, environment, AWS account, and Region.

At some point reuse becomes obscurity.

How I Choose Widget Sizes
#

I intentionally vary widget sizes.

Important single values belong at the top.

1
6 × 5

Trend graphs usually need more horizontal space.

1
12 × 6

Dense diagnostic graphs may deserve the entire row.

1
24 × 6

The layout itself communicates importance.

I don’t want a CPU utilization graph occupying the same visual priority as checkout failure rate.

Set the Default Time Range for Incidents
#

For operational dashboards, I usually prefer opening with a relatively short recent window.

For example:

1
2
3
{
  "start": "-PT3H"
}

Three hours gives me enough context to see what normal looked like before an incident while still keeping short-lived spikes visible.

During an investigation I may switch to:

1
2
3
4
5
15 minutes
1 hour
6 hours
24 hours
7 days

depending on the problem.

A 30-day graph is excellent for capacity planning.

It’s terrible for finding a 90-second production failure.

Decide Separately What Deserves an Alarm
#

A useful dashboard and a useful alarm strategy overlap, but they are not the same thing.

A dashboard helps me investigate.

An alarm tells me that a condition deserves attention.

For this architecture, I would strongly consider alarms around:

  • sustained HTTP 5xx rate
  • unexpectedly low order volume during normally active periods
  • elevated checkout failure rate
  • unhealthy ALB targets
  • ECS service capacity problems
  • RDS capacity or latency problems
  • critically low database storage
  • Valkey evictions or severe memory pressure
  • OpenSearch red cluster state
  • OpenSearch search rejections
  • SQS oldest-message age
  • DLQ message count
  • failed smoke tests after deployment

But thresholds need to come from the application.

There is no universal:

1
CPU > 80% = production emergency

rule.

A service designed to efficiently run at 80% CPU might be perfectly healthy.

A checkout service returning 15% errors at 12% CPU is definitely not.

Gotcha: Too Many Metrics Can Make the Dashboard Worse
#

Once you discover how many metrics CloudWatch exposes, there is a temptation to add all of them.

Don’t.

A dashboard with 70 graphs means I effectively have no dashboard.

I have another monitoring console.

Every widget should answer a question.

For example:

1
2
3
4
5
Widget:
RDS DatabaseConnections

Question:
Are application connections accumulating or approaching database capacity?
1
2
3
4
5
Widget:
ApproximateAgeOfOldestMessage

Question:
Are consumers keeping up with queue producers?
1
2
3
4
5
Widget:
OpenSearch ThreadpoolSearchRejected

Question:
Is search rejecting work under load?

If I cannot explain why I need a graph, I remove it.

Gotcha: Averages Hide Bad Behavior
#

This deserves special attention in high-volume systems.

If I graph only:

1
Average TargetResponseTime

I can hide painful customer experiences inside millions of otherwise fast requests.

Use percentiles.

Look at distributions.

Separate important application paths when possible.

A fast product-image request should not make a painfully slow checkout operation appear acceptable.

Gotcha: Dynamic Infrastructure Requires Dynamic Dashboards
#

Hardcoding EC2 instance IDs into an Auto Scaling environment is a maintenance problem waiting to happen.

The same principle applies anywhere resources are ephemeral.

Use:

  • meaningful dimensions
  • service-level metrics
  • Container Insights
  • SEARCH expressions
  • resource tags where appropriate
  • stable workload identifiers

Monitor the service, not yesterday’s server.

Gotcha: Metrics Without Context Create False Leads
#

Suppose this happens:

1
RDS CPU: 92%

Is that bad?

Maybe.

Now add:

1
2
3
4
5
Order rate:              normal
Checkout error rate:     normal
p95 latency:             normal
Database connections:    normal
Read latency:            normal

I’m interested, but I’m probably not declaring a production incident yet.

Now change the surrounding metrics:

1
2
3
4
5
6
RDS CPU:                 92%
Order rate:              ↓↓↓
Checkout error rate:     ↑↑↑
p95 latency:             ↑↑↑
Database connections:    ↑↑↑
Read latency:            ↑↑↑

That’s a completely different situation.

Dashboards create value through correlation, not individual graphs.

The Dashboard Should Mirror How You Troubleshoot
#

When something breaks, I don’t troubleshoot alphabetically by AWS service.

I start with the symptom and follow the request down the stack.

Is the customer affected?

Are requests failing or becoming slow?

Is the frontend healthy?

Is the backend healthy?

Is the problem in RDS, Valkey, OpenSearch, or asynchronous processing?

What do the logs say?

What does the trace show?

Vertical troubleshooting flow from customer impact through traffic, application tiers, data stores, queues, logs, and X-Ray traces to root cause

That investigation order is what turns CloudWatch from a pile of AWS graphs into an operational tool.

Final Thoughts
#

CloudWatch gives us an enormous amount of telemetry.

The difficult part isn’t collecting graphs.

It’s deciding which signals matter and arranging them so an engineer can understand the health of a complicated system quickly.

A useful dashboard should be able to turn:

“The website is slow.”

into something closer to:

“Search is saturated, OpenSearch requests are being rejected, and that is driving backend latency.”

That is the standard I use.

Start with what the customer experiences, follow the request through the system, and use each widget to eliminate possibilities until the failure has somewhere specific to live.

If a dashboard can’t help me narrow an incident down, it doesn’t need another graph.

It needs a better design.

David Cajio
Author
David Cajio
I design and operate AWS e-commerce infrastructure, CI/CD pipelines, and reproducible Linux workstations. AWS Certified Solutions Architect – Associate. I write about the systems, trade-offs, and lessons from keeping high-traffic platforms running under real-world pressure.
Production Operations - This article is part of a series.
Part 3: This Article

Share this post

Related