What is a Go pseudo-version like v0.0.0-20060102150405-abcdef123456, and when does the go command create one?
answer
- a synthetic version for an untagged commit
- three fields joined by hyphens
- the middle field is a UTC commit time
- twelve hex characters identify the commit
- sorts below the next real release tag
basics
~20 sA pseudo-version is a synthetic semantic version the go command derives for a commit that has no suitable version tag. It encodes a base version, the commit's UTC timestamp, and a 12-character commit hash prefix, so untagged commits still order correctly.
solid answer
~40 sA pseudo-version lets minimal version selection work on a commit that carries no usable semantic-version tag. It has three parts: a **base version**, the commit's **UTC timestamp** in `yyyymmddhhmmss` form, and the first **12 hex characters of the commit hash**. The go command synthesises one when you ask for a commit or branch directly — `go get example.com/lib@main` or `@abcdef1` — or when you depend on a repository that has never been tagged. Because the timestamp sits in the pre-release field, ordering is well defined: pseudo-versions derived from later commits sort above earlier ones, and all of them sort below the next real release tag. You should never hand-write one; the go command verifies that the encoded base, timestamp and hash actually match the commit and rejects a fabricated string.
code
text · 8 linesv0.0.0-20060102150405-abcdef123456
// no usable tag in the commit's ancestry
v1.2.4-0.20060102150405-abcdef123456
// nearest tag was the release v1.2.3
v1.3.0-beta.0.20060102150405-abcdef123456
// nearest tag was the pre-release v1.3.0-betago deeper
Recognise the shape and know it means 'a specific commit, no release tag'. Be able to read the middle field as a commit time and the last as a commit hash prefix.
Explain why the parts are ordered as they are: the timestamp sits in the pre-release field so the commit sorts above its base tag and below the next release. Know which situations make the go command derive one.
Talk about pseudo-versions as a temporary state in a real codebase: how they get in, why an unreviewed branch tip in a build list is a risk, and what you do to get back onto tagged releases.
Take a position on whether pseudo-versions are acceptable in production module files for your organisation, and what the alternative costs — requiring upstream tags, or maintaining forks that publish real versions.
## The problem it solves Minimal version selection compares versions and takes a maximum. That only works if every module version in the graph *has* a version — an ordered, semantic-version-shaped string. But real dependencies are sometimes an untagged repository, a fork whose fix has not been released, or a specific commit on `main`. A **pseudo-version** is the synthetic semantic version the go command derives for such a commit so it can take part in ordering and selection like any tagged release. ## Anatomy A pseudo-version has three fields: 1. **A base version** — derived from the nearest version tag in the commit's ancestry, or `v0.0.0` when there is none. 2. **A UTC timestamp** — the commit time formatted `yyyymmddhhmmss`. In `v0.0.0-20060102150405-abcdef123456` that field is `20060102150405`. 3. **A commit identifier** — the first 12 hexadecimal characters of the revision hash. The timestamp and hash live in the *pre-release* part of the semantic version, after the hyphen, which is what makes the ordering work out. ## The three forms Which form the go command produces depends on what tag precedes the commit: - **No relevant tag in the ancestry** → `v0.0.0-yyyymmddhhmmss-abcdefabcdef`. (For a module whose path carries a major-version suffix, the base is that major version rather than `v0`.) - **The nearest tag is a release, say v1.2.3** → `v1.2.4-0.yyyymmddhhmmss-abcdefabcdef`. The patch number is incremented and `0.` is prefixed to the pre-release field. That places the commit *above* v1.2.3 — it contains v1.2.3's work plus more — and *below* the eventual v1.2.4 release, because any pre-release sorts below the release of the same version. - **The nearest tag is itself a pre-release, say v1.3.0-beta** → `v1.3.0-beta.0.yyyymmddhhmmss-abcdefabcdef`. The suffix is appended to the existing pre-release, keeping it above the beta and below v1.3.0. The common design goal across all three: a pseudo-version must sort **above the work it is built on** and **below the next real release**, so that a later genuine tag automatically supersedes it under maximum-of-minimums. ## When one appears - You resolve a branch, tag-less repository or explicit revision: `go get example.com/lib@main`, `@master`, `@abcdef1`. - You depend on a module that has never published a semantic-version tag — common for early-stage internal libraries. - A tool or a teammate temporarily points at a commit containing an unreleased fix. In every case the go command writes the derived pseudo-version into `go.mod`, and from then on it is an ordinary version string as far as resolution is concerned. ## Rules that catch people out - **They are derived, not authored.** The go command validates a pseudo-version against the repository: the hash must exist, the timestamp must be that commit's, and the base must be consistent with the commit's ancestry. A hand-edited pseudo-version is rejected rather than silently used. - **The timestamp is the commit time, in UTC.** Not the time you ran `go get`, and not a publication time. This is what makes two people resolving the same commit produce the same string. - **They order by time, not by branch.** Two pseudo-versions from unrelated branches still compare by their timestamp field, so the newer commit wins under MVS even if it is on a branch you did not mean to prefer. That is a genuine hazard of pinning to commits. - **A real release supersedes them automatically.** Because `v1.2.4-0.…` sorts below `v1.2.4`, once the fix you were tracking is tagged, requiring the tag raises the minimum above your pseudo-version cleanly — no special casing needed. - **A `v0.0.0-…` pseudo-version is the lowest thing in its major version.** It will lose to any tagged release of that module that anything else in the graph requires. ## Reading one at a glance Given `v0.0.0-20060102150405-abcdef123456`, you can say immediately: the module had no usable tag in that commit's ancestry, the commit was made at 2006-01-02 15:04:05 UTC, and it is commit `abcdef123456`. That is enough to go look at the repository and see exactly what code is in your build — which is the real point: a pseudo-version is a human-readable, orderable name for a commit.
- Why does a pseudo-version built on v1.2.3 use v1.2.4-0 rather than v1.2.3-something?Because the commit contains everything in v1.2.3 plus more, so it must sort *above* v1.2.3, and a pre-release of v1.2.3 would sort below it. Bumping the patch to v1.2.4 and putting the timestamp in the pre-release field of that version puts the commit above v1.2.3 and still below the eventual v1.2.4 release.
- Can I write a pseudo-version by hand to pin a commit?No. The go command validates the base version, timestamp and hash against the actual repository and rejects a string that does not match the commit. The supported way is to ask for the revision — `go get example.com/lib@abcdef1` or `@main` — and let the go command derive and record the canonical form.
- What is the risk of leaving a pseudo-version in a go.mod long-term?It names a commit, not a release, so nobody reviewed or tagged what you are shipping, and it can silently be a branch tip that has since been rewritten or abandoned. It also orders purely by commit time, so an unrelated branch's commit can outrank the one you meant. Treat it as a temporary pin with a ticket attached.
It is a timestamped serial number stamped on an unlabelled part, chosen so it files just after the last labelled part and just before the next one.
saying these in an interview costs you the question
- Thinks the timestamp is when go get was run
- Believes a pseudo-version outranks a later tagged release
- Says the hash field is a module content checksum
- Hand-edits a pseudo-version string in go.mod
- Assumes v0.0.0 means the module is version zero