Are you using the latest-tag? Well.. Stop it.

For the longest time I’ve been fervently against the use of any sort of “implicit versioning” in dependency-management. Today it occurred to me I have never clearly outlined to myself “why” I’m so fervently against its use. Of course I have my intuitions and a range of experiences surrounding implicit versioning. Suffice to say that intuitions are hardly relevant when your intent is to develop software that’s (mostly) free of bugs. I realize that many others have written on the subject. Yet, I felt like most were lacking a general view and substantial reasoning behind avoiding implicit versioning.
In this article I outline my thoughts on implicit-versioning, specifically version-tags and version-ranges. In doing so I first give a general description of tags and their use for either identification or categorization, I describe what version-tags are not and what they are concretely. Second, I describe why you should avoid using version-tags for categorization. Third, I describe version-ranges and why you should avoid them as well. Last, I dig a bit deeper for those who remain unconvinced.
TL:DR?; Check out the summary at the bottom of the article.
Tags? Versions?
For those who are unfamiliar with the concept of version-tags consider this real-world analogue. Imagine going to an English auction with a “not-so-sophisticated” auctioning method, wanting to buy that amazing mint-condition star wars collectible you’ve had your eye on for years.
Before the auction starts all potential bidder signs up and is given a sign with a unique number on it.
The bidding commences and you hold up your sign at an amount you’re willing to pay. Other bidders out-bid you and you out-bid them. At a certain point no other bidders are out-bidding you. The auction is over and you go to the auctioneer to take home your coveted collectible. They check if the unique number on your sign matches the one they jotted down when the auction ended and tell you were you can retrieve the collectible.
The only reason this auctioning-system is working properly is due to the relation between the unique number, the sign and your ownership of said sign. The three together provide a means of identification through association.
- If some-how the unique number changed after your successful bid, and before retrieval of your collectible, you can say good-bye to Mr. Solo due to incorrect identification.
- If you lose the sign, someone else can pick it up and retrieve the collectible. Because he/she is now associated with the sign and the unique number.
In version-control systems version-tags are similar to the signs used in this auctioning example. As they:
- Can, but are not required to, be used as a means of identification through association for an object, e.g. a bidder with a sign, or a historic state of your source-code with a version-tag.
- Mean something in one and one context only, e.g. the auction you attended, or the source-code you’re working on.
Based on this example I define tags as: A means to identify or categorize an object, as does Miriam-Webster [ 1, See: 5a, 5b].
There is an important distinction to make between identification and categorization. When using a tag as a means of identification it should never be used in the same context again. When using a tag for categorization the tag may be used for many objects in the same context.
Version-tags are not..
Let clarify a distinction between a “version” and a “tag”. In most version-control systems a version IS-NOT a tag. A version is the historic state of your source-code. A tag is any sign that is associated with a historic state of your source-code.
For instance, in git a historic state of your source-code is always associated with one-and-only-one unique SHA-hash. This hash can only represent a historic-state because of its uniqueness, as this allows for identification by association. Unfortunately, as with most unique-identifiers, a hash doesn’t provide any meaningful information to a human-being. So git allows developers to add meaningful tags that refer to a hash. This gives tags their real strength. The capacity to give a clear meaning to an otherwise, for humans, meaningless identifier.
So when I use the term “version-tag” I mean the tag used to identify or categorize a “version”. The two are not to be interpreted as interchangeable.
This gives tags their real strength. The capacity to give a clear meaning to an otherwise, for humans, meaningless identifier.
Version-tags are..
So what are version-tags? Consider some version-tag examples of the two types of tags I discussed earlier. In the examples I use SemVer [4] as the versioning-schema. The following version-tags are what I consider to be identification-tags:
- Tags strictly following the
X.Y.Zform, whereX,Y, andZare non-negative integers, i.e.0.2.3,1.3.5,6.3.6etc. - Tags that, SHOULD always resolve to the same version. Here I state “SHOULD” instead of “MUST” because some version-control systems allow developers to add a tag more than once.
Whereas the following version-tags are categorization-tags:
- Pre-release versions of the form
X.Y.Z-abcform, whereX,Y, andZare non-negative integers with a hyphen and an alphabetic[A-Za-z]post-fix, e.g.:1.0.0-alpha,1.0.0-beta,2.3.4-omega. Since some dependencies stay on the1.0.0-alpha-tag for ages, even-though lots of code might have changed. - Alphanumeric tags of the form
[:.-A-Za-z0-9]e.g.:latest,default,node:8,wheezy,lts,latest-beta, etc. - Tags that MAY NOT always resolve to the same version.
Why you should avoid categorization-tags
Imagine that you’re attending an auction where the auctioneer messed up. The auctioneer gave another bidder a sign with a “unique number” identical to yours! The auction might end up billing you for items you never even bid on! In this case we’re using the sign more than once in the same context when our intent is to use the sign for identification. This means that we ended up using the sign for categorization instead!
Unfortunately, there seems to be a rampant use of version-tags that are used for categorization in Software Engineering. Whereas these version-tags end up being interpreted as tags meant for identification.
The latest catastrophe
One example to illustrate why you should avoid categorization-tags in your dependencies is the use of the latest-tag. This tag is used in the dependency-management systems of Maven [2], NPM [6] and Docker [3]. The latest-tag is one particular seed of evil that can cause grievance to many a developer.
When someone uses the latest-tag what is expected is the latest version of some dependency. What often ends up happening is they get a version of the dependency that “just so happens to be tagged with latest” without any guarantees it actually is the “latest” version. This especially applies to Docker, where the latest-tag has become part of “standard” procedures and you can end up with implicit usage of the tag. Consider the following quote from the Docker Command Line Interface documentation:
To download a particular image, or set of images (i.e., a repository), use
docker pull. If no tag is provided, Docker Engine uses the:latesttag as a default. [3]
If you forget to supply a tag, Docker simply assumes you require a version with the latest-tag. In this case you have no clue whether or not this “latest version” will break your system. In effect the dependency-management system has taken away control from developers assuming that they want to use the latest-tag. Even-though it has no reason to assume this! You might just have forgotten to supply a desired tag!
What’s even worse however, is that the latest-tag can be moved to a different version and it is not limited to any specific type of version. It could be any version, even a completely new major-version. This means you could now be facing breaking changes or bugs since you’ve last had to download a dependency using that ow-so-useful latest-tag. The latest-tag is a prime example of a version-tag used for categorization which is prone to be interpreted as a means of identification.
Obviously this is not a problem when you’re pulling an image for the first time in a fresh project. It is however problematic when you’re working within a larger project and you don’t add a version or fix the version with the latest tag. Most likely this issue will only be caught after you’ve created a new release for your project. If you’re lucky, if you’re not you’ll find out when it’s already in production.
Surprise! Since your last download
latest-tag has been moved from version-tag 2.10.3 to version-tag 4.3.1! — Dependency developer
The latest-tag illustrates that using categorization-tags can negatively impact your applications. As it increases the potential for incorrect outcomes of your source-code without your knowledge.
Using categorization-tags for your dependencies implies you’re no longer in control of the correctness of your code. Instead you’re hoping that, god-be-willing, your dependencies haven’t introduced bugs in the version your dependency-management system has selected.. This in itself should be enough of a reason to avoid categorization-tags and use identification-tags as much as possible!
Version-ranges are (almost always) just as evil!
Dependency-management systems often allow you to specify “version-ranges”. For example, Maven allows the usage of [1.2, 1.3] which is equivalent to SemVer syntax 1.2.0 <= x <= 1.3.0[5] whereas NPM supports syntax such as 2.x [6] which is equivalent to 2.0.0 <= x <= 3.0.0 . Both of these version-ranges are prone to incorrect outcomes across builds because like categorization-tags, the dependency-management MAY NOT always resolve to the same version.
For this very reason NPM even has a package-lock.json which they recommend you commit to your version-control system. This file contains all the information needed to ensure that anyone pulling a version from your version-control system will have the exact same versions of its dependencies. In essence they’ve added a new file to solve a problem that would never have existed if they didn’t allow version-tags or version-ranges.
Now, version-ranges are not as “bad” as using the latest-tag because in most cases they don’t allow major version changes. However, unless you’ve specified a version-range for a very , very, very good reason, they can be just as evil. Most of the time there is no good reason to use a version-range for a dependency and you should just use a fixed version. Version-ranges should be an exception, not a rule.
Please, just avoid categorization-tags & version-ranges
Hoare logic applies
In Hoare logic so-called Hoare-triples are used to reason about the correctness of a computer program. A Hoare-triple is constructed as {P}C{Q}where P and Q are assertions and Cis a command. Command C is executed when precondition P is met. When we use Hoare-triples in determining the correctness of a program we want to examine whether post-condition Q holds after executing command C for precondition P.
Without going into to much detail, assume we have a desired post-condition Q and we want to ensure that precondition P is met. For this to occur we need to ensure that command C is known. Assume that command C is a method from a dependency where, the dependency-version has been specified using a version-range or a categorization-tag of the form >= 1.x. This means we can not be sure of the program’s correctness as time proceeds. As we cannot guarantee the program will use command C1.1.2 , C1.34.1 or C1.5.3! This situation should be avoided at all times!
Version-tags convey meaning
Version-tags do more than just point to a historic state of a dependency. Often they convey a very specific meaning. Assuming your dependencies adhere to a versioning-schema like SemVer [4], version-tags allow any developer to identify whether there are any breaking changes, added features, etc. in this version of the dependency. This information no longer available at first glance when you’re using categorization-tags like the latest-tag.
Instead you’re now required to go to check all the versions of a dependency to find the latest-tag and the version it is associated with. As a Software Engineer you should prevent yourself and your users from getting into situations where you are uncertain about the correctness of your source-code. Especially if no conscious preceding change was made by you or them!
Your team-mates will thank you for it!
If you’re using categorization-tags or version-ranges in your dependencies you will eventually get incorrectness among your team-mates without having made any changes to the underlying code at all! Imagine helping a newly recruited team-mate to get started with your development environment. You’ve cloned the repository you need, your build-tooling has just finished, you run it.. Everything is running! Nice! Your new team-mate starts getting familiar with your application. Then after trying it out for a bit, suddenly, it crashes! POOF! Magical isn’t it? Now you’re stuck trying to find-out what happened for 10–20 minutes. Eventually realizing the version of a dependency got changed to a new minor-version during the build. A version which, unfortunately, introduced a bug. All because your dependency-management system had your permission to retrieve the version it felt like!
To summarize
When specifying the version-tags of your dependencies (and, where possible, when versioning your own source-code) you should:
Avoid
- Any type of versioning that, over time, MAY NOT resolve to the same version. This can lead to an incorrect application due to changes in its dependencies.
- Categorization-tags, as over time they MAY NOT resolve to same version.
- Version-ranges, as over time they MAY NOT resolve to same version. Unless you’re really, really sure they won’t, in which case — use the fixed version they’re resolving to.
- I use, MAY NOT, because version-tags and -ranges can but don’t have to refer to the same version over time.
- The
latest-tag. For the love of all that is sacred, don’t use it!
Use
- Any type of versioning that, regardless of point-in-time, SHOULD resolve to the same version. This ensures your application stays correct, unless altered by you.
- Identification-tags, as they SHOULD resolve to the same version, regardless of point-in-time.
- Explicit versions, as they SHOULD, resolve to the same version regardless of point-in-time.
- I use, SHOULD, because humans make mistakes and version-control systems are way to flexible concerning version-tags.
Agree? Disagree? Let me know and leave a comment!

![[GUIDE] Running MicroK8s & Nginx-ingress on CentOS 7](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1650647049653%2FW-ozq9MDf.png&w=3840&q=75)