Live health of every project in this portfolio, checked automatically every 30 minutes by an event-driven pipeline — Step Functions, EventBridge, SQS with a dead-letter queue, and DynamoDB.
Decision: the list of monitored URLs lives in a DynamoDB table, read at the start of every execution — not embedded in Lambda code or the state machine definition.
Why: this pipeline is meant to monitor every future project in this portfolio, not just the ones that existed when it was built. Adding a target means adding a row, not shipping new code. The Step Functions Map state iterates over whatever it finds at runtime, so the orchestration logic never needs to change as the portfolio grows.
Decision: when a health check finds a site unreachable, the workflow routes through
a Choice state to an alerting path — it does not raise an exception caught by
Step Functions' error handling.
Why: a monitored site being down is an expected, normal outcome of a health check — not a failure of the pipeline itself. Retry logic is reserved for genuine infrastructure problems (Lambda throttling, transient AWS errors), never for "the thing I was checking failed the check," which is the entire point of running the check.
Decision: the pipeline runs every 30 minutes, not every 5.
Why: at 5-minute intervals, monthly Step Functions cost was estimated around $1.65 — already low, but unjustified for a portfolio status page with no real SLA to uphold. At 30 minutes, cost drops to roughly $0.20/month. The trade-off is a detection window of up to 30 minutes before an outage is caught, instead of 5 — acceptable here, since this isn't monitoring a system with contractual uptime guarantees.
Decision: individual health check records expire automatically after 7 days via a DynamoDB TTL attribute, rather than accumulating indefinitely.
Why: at this check frequency, unbounded retention would add roughly 100 rows per target per day with no benefit — the status page only ever needs recent history to compute uptime percentage and show current state. TTL deletion isn't instant (up to ~48 hours after expiry), which is irrelevant for this use case.
Decision: the state machine runs as a Standard workflow.
Why: Express workflows are cheaper at high volume, but this pipeline runs infrequently by design. Standard workflows provide a detailed, visual execution history in the console — genuinely useful when debugging why a particular run behaved a certain way — and the cost difference at this volume is negligible.