Repository activity is abundant, but most of it is not release intelligence. Commits, pull requests, tags, workflow runs, deployments, and published releases each describe a different point in the software delivery process. Sending all of them to a team channel creates noise. A clean release feed selects durable milestones, enriches them with context, and preserves enough identity to support deduplication and audit.
The design begins by separating facts from notifications. A Git tag is a reference. A release object adds a title, notes, assets, draft state, and publication state. A CI run proves that a workflow executed, while a deployment event may indicate that an artifact reached an environment. A useful feed composes these facts into a small number of normalized entries rather than mirroring every provider event.
Define what counts as a release event
Write a policy for each repository class. A library may publish an entry when a non-draft release becomes public. An internal service may publish when a version is deployed to production. A mobile application may publish after a store submission reaches an approved state. Avoid using “new tag” as a universal rule because teams create tags for testing, automation, or rollback points that are not intended for consumers.
Represent the policy as code or configuration with an explicit version. Store which policy produced each feed entry. When the policy changes, do not silently reinterpret history. New events can follow the new rule, while existing entries retain their provenance. This makes a release stream explainable during incident review and prevents a configuration edit from republishing old activity.
Use provider APIs for authoritative metadata
Webhooks are excellent triggers, but the event payload may not include every field needed for a durable entry. After accepting a provider event, fetch the authoritative object using a constrained API client. For GitHub releases, the GitHub Releases REST API documentation defines endpoints and release fields. Capture the immutable provider ID, repository identity, tag name, target commit, draft and prerelease flags, publication time, author, notes, and assets.
Do not make the webhook response wait for API enrichment. Authenticate the webhook, store its provider delivery ID and raw body hash, enqueue a job, and acknowledge quickly. The worker can then retrieve the release, apply policy, and create or update a normalized entry. If the API is temporarily unavailable, retries occur without asking the provider to resend the original webhook.
Normalize repository and version identity
Repository names can change, transfer between organizations, or be mirrored. Use the provider's immutable repository ID as the primary external key and store the current owner/name as display metadata. A release key can combine provider, repository ID, and release ID. Keep the tag name and semantic version as separate fields because not every tag is a valid semantic version and not every product uses the same prefix convention.
Parse semantic versions only when the repository policy says they are meaningful. Record the original tag exactly. A normalized version object can contain major, minor, patch, prerelease, and build metadata when parsing succeeds, plus a boolean indicating compliance. This supports filters such as “stable minor releases” without rejecting date-based or product-specific version labels.
Combine CI evidence without creating duplicates
A release may trigger several workflows across operating systems, architectures, and packaging targets. Those runs are evidence attached to one release, not separate feed entries. Model a release entry with a verification summary: required checks, latest conclusion, artifact provenance, and completion time. Update the existing entry when verification changes, and emit a change event only if subscribers opted into status transitions.
Use stable workflow and run identifiers, not display names alone. Jobs can be rerun, and a later attempt may replace a failure. Store attempts and select the policy-relevant result. If a release is published before all required checks complete, mark verification as pending. Consumers can decide whether to notify immediately, wait for success, or send a follow-up. This is more honest than treating publication and validation as one atomic event.
Represent deployments as environment-specific facts
A deployment is not synonymous with a release. The same version may move through test, staging, canary, and production, and production may have several regions. Normalize deployment events with environment ID, environment class, artifact or commit identity, start and completion times, result, and provider event ID. Then apply a publication rule such as “create one feed update when the first production region succeeds” or “update the entry when global rollout completes.”
Keep sensitive environment details out of public feeds. Internal feeds can include deployment links and runbook references under authorization, while public feeds should expose only customer-relevant status. The custom developer feed model can produce different views from the same normalized event history without duplicating ingestion.
Generate useful release content
A clean entry should answer what changed, who is affected, where to learn more, and what action is required. Preserve the publisher's release notes, but sanitize HTML and apply length limits for feed summaries. Extract structured highlights only when the source follows a documented convention. Do not invent breaking-change labels from keyword guesses. Instead, let maintainers supply categories in release metadata or a checked-in changelog format.
Link to the canonical release page, documentation, migration guide, and downloadable assets when available. Enclosures can represent artifacts in a feed, but avoid listing every asset if the result becomes unwieldy. A concise entry can include the primary package or installer and leave the complete asset list on the release page. Retain checksums and signatures in the API model even if the human-facing feed only links to them.
Handle edits, drafts, prereleases, and deletions
Release objects change. Notes are corrected, assets are replaced, prereleases become stable, and releases can be deleted. Store the latest provider update timestamp and a content fingerprint. When an object changes, update the normalized record and emit an update event with the fields that changed. Keep the stable entry ID so feed readers do not see an edit as a new release.
Draft releases should remain private to authorized workflows. Prereleases should be included only for subscribers who request them. If a public release is deleted, retain a tombstone internally and decide how the feed communicates withdrawal. Removing the item silently can leave consumers with an artifact they believe is current. A withdrawal entry or status update is often clearer, especially for security or compliance-sensitive software.
Control fan-out and notification noise
Let teams subscribe by organization, repository, package, environment, stability channel, version range, or topic. Apply filters before delivery and record the matched rule. Batch related events within a short window when a coordinated release touches many repositories. A digest can group them under a product version while preserving links to each component release.
Use destination-specific formatting. A feed reader benefits from complete notes and stable identifiers. A chat notification needs a concise headline and a few highlights. A webhook consumer needs structured fields and an idempotency key. Generate each representation from the same normalized record so they do not disagree. The syndication layer should own formatting and retries rather than embedding destination logic in CI workflows.
Implementation checklist
- Define release policies separately for libraries, services, applications, and internal environments.
- Verify provider webhook signatures and deduplicate by provider delivery ID.
- Enrich events asynchronously from authoritative APIs with least-privilege credentials.
- Key repositories and releases by immutable provider IDs, while retaining display names and tags.
- Attach CI and deployment evidence to release records instead of creating uncontrolled event streams.
- Preserve draft, prerelease, published, withdrawn, and verification states explicitly.
- Sanitize release content and keep public and private feed views separate.
- Offer filters, batching, idempotent delivery, and replay from normalized history.
Publish milestones, not exhaust
A release feed should help a developer understand software movement without watching the machinery produce it. Provider webhooks and APIs supply detailed facts; your policy turns those facts into durable milestones. Stable identity, asynchronous enrichment, explicit state, and destination-specific delivery are the core design tools.
When the feed is trustworthy, teams can use it for dependency awareness, platform announcements, customer changelogs, and operational coordination. The result is not another firehose. It is a curated record of what shipped and why it matters.


