Blog

Why Version Numbers Drifted From Their Meaning

Version numbers inside a user agent string once corresponded to a release a user could identify. Several changes to how browsers ship have loosened that correspondence considerably.

Rapid release cycles changed the unit

When browsers shipped major versions rarely, the number carried information: it implied a feature set, a rendering behaviour and an approximate age. Users and developers both understood the scale.

Frequent releases turned the major number into something closer to a counter. It increments on a schedule rather than when the software changes meaningfully.

A gap of several versions no longer implies a large behavioural difference. Code that treats version distance as capability distance is reading a scale that no longer exists.

Minor components stopped varying

Many browsers now emit fixed values in the minor and patch positions regardless of the actual build. The positions remain for format compatibility with parsers that expect them.

This is deliberate. Detailed build numbers distinguished individual installations more finely than any site needed, so vendors reduced what the header exposes.

A parser that splits on dots and compares four components will find three of them constant. The comparison still runs and still returns an answer, which is what makes the situation awkward.

Platform versions were capped

Operating system versions inside the comment section have been pinned at a value on several platforms. Newer systems continue to report the older number.

The pinning avoided a class of breakage where sites compared platform versions numerically and mishandled values above the range they were written against. Holding the number constant was safer than advancing it.

It also removed a signal that some code genuinely used, such as gating on features tied to a system release. That code now needs a different source.

Different components froze at different times

The various numbers in one string are not on the same footing. Some advance with each release, some advance rarely, some have not moved in years.

Nothing in the string marks which is which. A number that looks like a version and sits where a version belongs may be a literal that will never change.

This is the strongest argument for treating the header as a lookup key rather than a data structure. Its components mean what a maintained dataset says they mean.

Where an accurate version still comes from

Structured client hints report browser version as an explicit field, with the full value available separately from the reduced one. The distinction is part of the interface rather than something to infer.

Where the exact version matters — a known defect in one release, for instance — that channel is the reliable source. Parsing it out of the legacy header is guesswork with good odds, not certainty.

For most decisions the version is not what the code needs anyway. Whether a feature exists is a question the runtime can answer directly.