Who knows what shipped?
When a deployment is invisible, the same questions repeat in chat: Did the fix go out? Which version is staging? Why is the build red? Who is looking at it? Without a shared view, support promises customers a fix that has not deployed, engineers deploy over each other, and a failed release sits unnoticed for an hour.
Notifications and a status dashboard make delivery visible. They are a modest part of a CI/CD pipeline in engineering effort, and a surprisingly large part in how the team feels about it. The risk is doing it badly: a firehose channel that everybody mutes is worse than nothing.
What good looks like
- Actionable. A message either tells someone to do something or tells a group something they care about.
- Routed. Authors hear about their own failures. Production releases go to a team-wide channel. Incidents go to on-call tooling, not general chat.
- Self-contained. Each message links to the run, the diff, the logs and the owner.
- Quiet when healthy. Green builds on feature branches do not post.
- Backed by state. A dashboard shows what is running now, not just what happened.
Designing the messages
The deployment message
A good production deployment message contains: environment, service, version or commit, author, status, a one-line change summary (from the pull request title), and buttons or links to the run and rollback instructions. Failures add the failing stage and the first error.
We use one message per deployment and update it as the release progresses (building, deploying, verifying, done), or thread status replies beneath it. This keeps channels readable. Posting a separate message for every job step quickly buries real events.
A GitHub Actions example
Using a Slack incoming webhook stored as a secret:
- name: Notify Slack
if: always()
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
STATUS: ${{ job.status }}
run: |
curl -sS -X POST -H 'Content-type: application/json' \
--data "{
\"text\": \"Deploy ${STATUS}: ${GITHUB_REPOSITORY} @ ${GITHUB_SHA::7} to production by ${GITHUB_ACTOR}\",
\"attachments\": [{\"text\": \"<${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}|View run>\"}]
}" "$SLACK_WEBHOOK_URL"
This is deliberately minimal. For richer messages with threads and updates, we use a Slack app with a bot token and the Block Kit format, or a maintained community action, packaged as one reusable step so every service posts the same format. Microsoft Teams works similarly through a workflow-based webhook or the Teams app connectors available in your tenant. Webhook mechanisms in Teams have changed over time, so we confirm what your tenant currently supports before building.
The shared step belongs in your reusable workflows, so changing the format happens in one place.
Beyond the pipeline
Not every event comes from CI. With GitOps, the controller knows when a sync succeeded or went out of sync. Argo CD has a notifications component that posts these to Slack or Teams, which covers deployments that happen outside the pipeline, such as manual syncs. Rollback events matter most here; our automated rollback work always posts a message explaining what failed and what was restored.
Routing and escalation
Chat is not an incident system. We separate three lanes:
- Developer feedback: build and test failures, sent to the author or team channel.
- Release awareness: production deployments and rollbacks, sent to an engineering-wide channel and optionally to support and product.
- Incident alerting: production failures after a release, sent to your paging tool (such as PagerDuty or Opsgenie) with acknowledgement and escalation, because a Slack message does not guarantee anyone saw it.
Mixing these lanes is the commonest cause of channel fatigue. Mentions (@here, @channel) are reserved for the second and third lanes.
The deployment status dashboard
Chat answers “what just happened”. A dashboard answers “what is the state, and what is the trend”. We typically build it in Grafana, fed by pipeline events and Prometheus metrics, with panels for:
- Current version per service per environment, so “what is on staging?” has a single answer.
- Recent deployments with status and duration.
- Pipeline success rate and median duration over time.
- The four DORA metrics: deployment frequency, lead time for changes, change failure rate and time to restore.
- Queue time on runners, which shows capacity problems before developers complain.
Getting deployment events into the dashboard is the real work. Options include pushing a metric or annotation at the end of each deployment, using the CI platform’s API, or reading deployment data from the cluster. Our Kubernetes monitoring guide covers the Prometheus and Grafana side. Marking deployments as annotations on service latency and error graphs is a particularly useful trick: when a graph changes shape, you see immediately which release coincided with it.
A caution on DORA metrics
DORA metrics are helpful when definitions are consistent: what counts as a deployment, what counts as a failure, when lead time starts. They are harmful when used to rank people or teams. We set definitions with you and present the numbers as trends, not targets.
Decisions and trade-offs
Webhook versus app. Incoming webhooks are quick but limited: one channel, no threading, and the URL is a bearer secret. A Slack app with a bot token supports threads, updates and multiple channels, at the cost of setup and workspace approval.
Chat noise versus awareness. Posting everything feels transparent and ends up unread. We bias towards fewer messages and let people opt into more.
Build versus buy. Commercial deployment tracking tools exist. For a team with Grafana already running, a few panels often suffice. We recommend buying when you need cross-tool engineering analytics and have budget for it.
When not to bother. A two-person team that sits together does not need elaborate routing. A single channel for production deploys and failures is plenty.
Common failure modes
- Muted channels from noisy notifications.
- Notifications that say “failed” with no link or reason.
- Webhook URLs committed to repositories.
- Messages sent before deployment completes, announcing success too early.
- Dashboards with no owner that quietly go stale.
- Inconsistent service names between chat, dashboard and Git, which makes searching hard.
Delivery and handover
We agree audiences and routing with your team, build the notification step as a shared component, and create the dashboard from your real pipeline data. After a few weeks we review which messages are acted on and prune the rest. You receive documentation of the message format, channel map, credential storage and dashboard queries, and the dashboard definition is kept as code so it can be recreated.
How to tell it is working
People stop asking “did it deploy?” in chat. Failed builds are picked up quickly by their owner, channels are not muted, and the dashboard is opened during retrospectives. Metrics to watch include time from failure to first acknowledgement, the ratio of messages to actions taken, and whether the dashboard’s deployment counts match reality. To get this set up, contact us.