Skip to content

Practice: Releases and tags

Answer each in your own words first, then open the answer to check.

Q1. Explain the difference between a tag and a branch in git, structurally and operationally.

Show answer

Structurally, both tags and branches are named pointers to a commit. The difference: branches MOVE when you commit on them (they advance to point to the new commit), while tags are FIXED (always point to the same commit). Operationally: branches are the working surface; tags are the historical markers. You commit and merge to branches; you tag specific moments. Branches are short-lived (mostly) and frequently deleted; tags are intended to be permanent.

Q2. What’s the difference between a lightweight tag and an annotated tag? When should you use each?

Show answer

A lightweight tag is just a pointer with a name (no metadata). An annotated tag is a full git object with tagger info, timestamp, and a message. Use lightweight for private bookmarks (“the commit I’m investigating”) that you’ll delete after. Use annotated for any shipped release, the metadata is part of the audit trail for “who tagged this release and when.”

Q3. Walk through what the three semver numbers (MAJOR.MINOR.PATCH) communicate to a consumer reading the version.

Show answer

The three semver numbers communicate the RISK and TYPE of change since the last release:

  • MAJOR changing (v1.x.x to v2.0.0) signals “this release breaks something; you must update your consuming code.” Users should read release notes carefully before upgrading.
  • MINOR changing (v1.4.x to v1.5.0) signals “new features added, backward-compatible.” Users can upgrade safely; new features available to opt into.
  • PATCH changing (v1.4.2 to v1.4.3) signals “bug fixes only, internal.” Users can upgrade with minimal risk; behavior should be unchanged except the fixed bugs.

This is a CONTRACT with users. Following semver builds trust; bumping casually erodes it.

Q4. A release note can serve four audiences. Name them and give one example of what each cares about.

Show answer

Four audiences and what each cares about:

  • Users: “What’s new that I can use? What’s fixed that was bothering me?”
  • Operators: “What do I need to do during deploy? Migration steps? Rollback concerns?”
  • Developers: “What API changed? What’s deprecated? Any behavior shifts I need to handle in my consuming code?”
  • Auditors: “What changed in this release exactly? Complete record for compliance/regulatory review.”

A canonical release note structure (Highlights / Breaking changes / Features / Bug fixes / Migration notes) addresses each audience in different sections.

Q5. Your team uses GitHub Flow with continuous deploy. You merge a feature on Tuesday at 3pm; it ships immediately. Does your team need formal releases at all? If yes, why and at what cadence?

Show answer

Yes, even with continuous deploy you want formal releases. Reasons:

  • Communication: marketing, sales, support all need to say “we shipped X in v2.3.” Without tags, they can’t.
  • Debugging: “what was deployed at 3pm Tuesday?”, answer requires looking up the commit at that time, which is harder than “v2.3.5 was current then.”
  • Rollback targeting: “roll back to the previous release” needs a previous release to roll back to.
  • Changelog generation: users want a changelog they can read. Tags + release notes give you one for free.

Cadence: depends on your context. Some teams tag every Friday. Some tag every meaningful product milestone (PMs decide). Some tag every Nth merge automatically. The discipline is “we always have tags,” not “we tag on this exact schedule.”

Use a sandbox repository.

Drill 1, create and push a tag:

  1. In your sandbox repo, make a commit (or use an existing commit).
  2. Create an annotated tag: git tag -a v0.1.0 -m "Release v0.1.0: initial alpha"
  3. List tags: git tag. Verify v0.1.0 appears.
  4. Show details: git show v0.1.0. Observe the tag’s message, tagger info, and the commit it points to.
  5. Push the tag: git push origin v0.1.0.
  6. Verify on the remote: visit your GitHub repo’s Releases or Tags page.

Drill 2, tag a specific past commit:

  1. git log --oneline, pick a commit that isn’t the latest.
  2. git tag -a v0.0.9 <commit-sha> -m "Tag earlier commit retroactively as v0.0.9"
  3. git show v0.0.9, verify it points to the older commit, not the current one.

Drill 3, delete a tag (locally and remotely):

  1. git tag -d v0.0.9, deletes locally.
  2. git push origin --delete v0.0.9, deletes on remote (if you pushed it).
  3. Verify on GitHub that the tag is gone.

Drill 4, pre-release tags:

  1. Create v1.0.0-alpha.1, v1.0.0-beta.1, v1.0.0-rc.1 as separate tags on different commits.
  2. List sorted by version: git tag --sort=-v:refname.
  3. Observe pre-release tags sort correctly with version-aware sorting.

Drill 5, create a GitHub Release:

  1. Go to your repo’s Releases page.
  2. Click “Draft a new release.” Pick v0.1.0 as the tag.
  3. Title: “v0.1.0 - 2026-06-10.”
  4. Body: write a short release note with at least a Highlights section.
  5. Publish. Verify the release page renders.
  6. (Optional) Tweet or share the release URL.

For each change description, pick the right semver bump (major / minor / patch) and justify briefly.

  1. You fixed a bug where login redirected to the wrong URL.
  2. You added a new endpoint POST /api/v2/refunds (existing endpoints unchanged).
  3. You removed the deprecated endpoint GET /api/v1/users-old (existed for 2 years; documented as deprecated for 1 year).
  4. You changed the default value of an existing API parameter from false to true.
  5. You improved performance of an internal function (no API change).
  6. You renamed an internal class but kept the public API identical.
  7. You added a new optional field to a JSON response (existing fields unchanged).
  8. You added a new required field to a request body (existing requests now fail).

(See cheatsheet for model decisions.)

Pick a real recent PR you merged (or imagine one). Write a release note entry for it, including:

  • What changed (one sentence)
  • Why it matters (one sentence)
  • Any breaking changes (or “none”)
  • Any migration notes (or “none”)
  • Optional: link to PR/issue

Compare your release note to what you would have written as a commit message (L3). The release note is for end users; the commit message is for engineers. The difference in audience drives the difference in content.

Scenario A. Your team has been continuously deploying for 6 months without any formal release tags. Suddenly someone asks: “what version is in production?” There’s no answer. Without confrontation, describe a path to introducing release tags retroactively + going forward.

Show answer

Path to introducing tags retroactively + going forward:

Retroactive:

  1. Look at git log; identify candidate commits to retroactively tag. “What was the production state on the last day of each month?” is a reasonable starting point.
  2. Tag those commits with retroactive versions (e.g., v0.1.0 for January, v0.2.0 for February). Use annotated tags with a message noting “retroactively tagged.”
  3. Push the tags.
  4. Be transparent: in CHANGELOG.md, note “retroactively tagged historical releases as of YYYY-MM-DD.”

Going forward:

  1. Decide on a cadence (weekly? per milestone? per Nth merge?). Pick whatever fits the team’s rhythm.
  2. Automate tag creation if possible (CI/CD can tag on schedule or merge).
  3. Auto-generate release notes from PR titles since the last tag.
  4. Communicate the new discipline: “Starting next week, we tag every Friday at 5pm. Look here for release notes.”

The retroactive tagging is imperfect (you’re approximating dates) but giving the team and operators a vocabulary for “what version” is worth more than perfect historical accuracy.

Scenario B. A junior developer wants to bump the major version (v1.0.0 to v2.0.0) because “this feature is really big.” The feature is backward-compatible (no breaking changes). Walk through what you’d say without dismissing their enthusiasm.

Show answer

Conversation with the junior developer:

  • “Your feature is great and worth celebrating. Let me explain why we wouldn’t bump major for it: semver isn’t about how big a release feels; it’s a contract with users about upgrade risk.”
  • “A major bump (v1.0.0 to v2.0.0) tells our users ‘this might break your code; test before upgrading.’ If your feature is backward-compatible, meaning users on v1.x can upgrade to your version without changing their code, then it’s a MINOR bump. The version is v1.X+1.0.”
  • “This isn’t about the feature being unimportant. It’s about preserving the major-bump signal for actual breaking changes. If we bump major casually, users stop trusting that ‘major’ means ‘breaking,’ and we lose the contract.”
  • “Here’s the test: would a user on v1.x have to change their code to use your version? If yes, MAJOR. If no, MINOR. Your feature is MINOR; that’s a good thing, it means users get the feature for free without migration work.”

Frame it as preserving a contract with users, not as diminishing the feature.

Scenario C. Your team auto-generates release notes from PR titles. Looking at the auto-generated notes, half the entries say “Fix bug” or “Update dependencies.” Walk through what’s going wrong and how to fix it.

Show answer

What’s going wrong + the fix:

The problem: auto-generated release notes are only as good as the PR titles they’re generated from. “Fix bug” and “Update dependencies” tell users nothing. The team’s PR-title hygiene (L6 discipline) is the upstream issue.

The fix (two layers):

  1. Team-level: improve PR title discipline. PR titles should be user-facing summaries: “Fix login redirect dropping state parameter for OAuth flows” not “Fix bug.” Document the expectation in CONTRIBUTING.md. Reviewers can push back on vague titles before merge.

  2. Release-level: the human edits the auto-generated notes before publishing. The auto-generation is a starting point; the release author rewrites unclear entries, groups related changes, adds a Highlights section. “Generate, then edit” is the correct pattern, not “generate and publish blindly.”

  3. Tooling-level: consider Conventional Commits (L3 + L9 territory), they enforce structured PR titles like fix(auth): handle null state that auto-generation tools can parse better. Worth adopting if PR title quality is chronic.

The lift: better PR titles benefit reviewers (L6), users (release notes), and future debuggers (git blame). The discipline pays compoundingly.

Q. What's a git tag?
A.

A named pointer to a specific commit that never moves. Unlike a branch (which advances on commit), a tag is fixed.

Q. What's the difference between lightweight and annotated tags?
A.

Lightweight tags are just a name + pointer (no metadata). Annotated tags include tagger info, timestamp, and a message (stored as a full git object). For releases, always use annotated.

Q. What does semver MAJOR.MINOR.PATCH mean?
A.

MAJOR = breaking change. MINOR = backward-compatible new feature. PATCH = backward-compatible bug fix. Users read the numbers to gauge upgrade risk.

Q. How do you create an annotated tag for a release?
A.

git tag -a v2.0.0 -m "Release v2.0.0: description" then git push origin v2.0.0.

Q. Why don't tags push by default with git push?
A.

Git’s design choice; tags are typically slower-changing than branches and worth pushing explicitly. Use git push origin <tag> or git push --tags.

Q. What are pre-release identifiers in semver?
A.

Suffixes like -alpha.1, -beta.3, -rc.1 (release candidate) that signal “heading toward this version but not stable yet.” Users opt in for early testing.

Q. What's the canonical structure for release notes?
A.

Highlights / Breaking changes / New features / Improvements / Bug fixes / Migration notes / Contributors. Not every release needs every section; calibrate to the release.

Q. How do releases work in GitHub Flow?
A.

Tag specific main commits as releases. The tag is the formal release; deploys happen continuously on merge regardless. Write release notes; announce.

Q. How does GitFlow handle releases?
A.

A release/* branch is cut from develop. QA happens on the release branch. When ready, merge to main + back to develop. Tag the main commit. Delete the release branch.

Q. What's the relationship between release tags and package managers (npm, PyPI, etc.)?
A.

Package managers typically require git tags matching published version numbers. Your git tag -a v2.0.0 becomes the source-of-truth for [email protected] in the registry.