Skip to main content

Mistakes we made

Every DevOps mistake from this module on one page. Each one cost real time, an outage, or a security exposure. Read it before your first production release, then follow the link for the full story and the fix.

How it works​

The pattern behind most of them: we trusted a signal that looked like the truth (a tag list, a status code, a green fallback, a console change) instead of checking the real state.

Releases​

What happenedWhyDo insteadPage
Two releases reverted live features, twice in one dayProduction state was read from the tag list; a teammate deployed by manual runRead ECS, run git merge-base --is-ancestorRelease safety
A tag landed on an old parked branchA merge failed, an empty variable tagged the local checkoutCheck the merge, print the commit, then tagRelease safety
A tag went out mid-way through a teammate's deployOnly one repo's pipeline was checkedCheck every production pipelineRelease safety
Whole-branch promotion would have broken stagingBranches carried drifted per-env config and missing hotfixesCherry-pick onto the running commitRelease safety
Screens failed during a deployA migration was not backward compatibleExpand and contractRelease safety
Merged code sat undeployed for hoursA merge does not deploy; the tag was missingPush the tag, check the tag list firstPipeline
CodeDeploy said success, traffic did not moveIt swaps the listener default, not host-header rulesCheck where traffic really goesPipeline
A deploy looked like a crash loop for 25 minutesBoot migrations waited on table locks held by the old tasksRun migrations as a separate task, or a longer start periodPipeline
A hand-registered fix was reverted by the next buildThe build re-registers task definitions from the repoCommit the change, never fix by handPipeline
Every build failed at installLockfile regenerated without a package.json changeDiff both against the last good tagPipeline
A web app crash-looped on stagingHealth check got a 307; matcher allowed only 2xxReal /health route, matching matcherPipeline

Observability​

What happenedWhyDo insteadPage
Every deploy was one Sentry releaseRelease was a static SSM valuePer-build release baked into the task definitionSentry
Validation errors were invisiblebeforeSend dropped everything under 500; GraphQL returns 200Log client errors with field paths, tag themSentry
One attacker made 600 Sentry eventsEvery rejected request became an eventSample client errorsSentry
Stack traces pointed at bundled codeSource maps never uploadedUpload per releaseSentry
A 79% failure rate went unseenSilent fallback plus statusCode: null in logsRead ALB metrics, fix the serializerDaily check
Seventeen app builds all showed one versionOnly the build number movedBump the version name tooCrashlytics
A shipped build number differed from the archiveThe upload tool renumbered itCheck which archive you sentCrashlytics
Dev was down for hours, unnoticedNo traffic, no alarmHealth alarms in every environmentIncidents

Security and secrets​

What happenedWhyDo insteadPage
User tokens and a service key were in logs?token= and an unlisted header were loggedRedact params, headers and the URL; test it; rotateCloudWatch and security
A token leaked through the wrong headerRedaction matched header names onlyScrub token-shaped values in every headerCloudWatch and security
No CloudTrail, no GuardDuty, no MFANever set up per accountSecurity baseline in every accountCloudWatch and security
Attackers found internal route namesAPI docs served in productionTurn docs off; block internal paths at the WAFCloudWatch and security
A form's spam check failed for six hoursA secret was dropped; code fell back to another oneFail at startup, no fallbacksSecrets
A fixed test login code was publicIt was in a repo doc and the app binaryBypasses off in prod, rotate burned codesSecrets
A console change would have been revertedThe parameter was Terraform-ownedChange it in TerraformSecrets
A temporary bastion ran for a monthNo end date, no ownerTemporary access expiresIncidents

AI providers and capacity​

What happenedWhyDo insteadPage
Every realtime AI session died at onceProvider spend cap trippedCheck spend first; alert on quota errorsIncidents
Chat failed with auth errors in productionPer-feature keys were dead; only the base key was testedTest every keySecrets
A new key could not reach our modelProvider retired the model for new projectsMint keys in the same project, or move modelsSecrets
One browser opened 160,000 socketsBackoff reset on the error frame; close code ignoredAuth close is final; refresh onceIncidents
API tasks killed with exit code 137Unbounded sockets, each holding an AI sessionCaps, load shedding, heap limit, back-pressureIncidents

Rules​

Rule

When you hit a new mistake, add it to the module page and to this table in the same change. A lesson that lives only in chat will be paid for again.

Checklist​

  • Read this page before your first production release.
  • Read it again before your first week of production duty.
  • Added any new mistake from your last incident.