This site hosts my projects.
commit caf7f02bdf042614b4061e9fac9afceebf3792d6
Author: Rickard Lindberg <rickard@rickardlindberg.me>
Date: Sun Jan 11 20:17:39 2026 +0100
Write
diff --git a/posts/2026/01/01/newsletter-january/post.md b/posts/2026/01/01/newsletter-january/post.md
index 8e90ac1..3323296 100644
--- a/posts/2026/01/01/newsletter-january/post.md
+++ b/posts/2026/01/01/newsletter-january/post.md
@@ -32,7 +32,7 @@ December: posts/2025/12/01/newsletter-december/post.md
[projects2](https://projects.rickardlindberg.me/projects2/)
-* `VCS_ID`, `VCS_SHORT_ID`, and `YMD` for use in CI scripts
+* `VCS_*` variables, for use in CI scripts
* Allow pushing Git tags
* New clone for every `Dockerfile.ci` to simplify build
* Partial support for tagging commits
@@ -62,6 +62,9 @@ December: posts/2025/12/01/newsletter-december/post.md
I just wanted to document my findings and think a little deeper about this
problem by writing about it.
+* [](post:2026/01/11/how-to-assign-version-numbers-to-software/post.md)
+ I wrote to clarify my thinking. It helped in developing changelog2version.
+
## TODO
Here are the things that I'm currently most interested in working on next
@@ -93,5 +96,3 @@ month:
are easier when looping over characters. (Nov 17)
* Write blog post about how to parse offside in markup language
posts/2026/01/09/how-to-parse-off-side-rule-in-meta/post.md
-* How to release software?
- posts/2026/01/08/how-to-release-software/post.md
fatal: Invalid revision range a2088a1702dca3b2b2377bbf18e95f6dfdd56b0a..4ddf9c23a1e793ce8bb2b268f5c87586f1c9eed9
fatal: Invalid revision range 79ce2e6bc16bf4470caebc196f3e42f96b6bfc20..399fafb2c66d27c2c04079af8f0bf48e3c706a13
commit c4ced3f235e8483a8f7e9aaecbef647f01e23738
Author: Rickard Lindberg <rickard@rickardlindberg.me>
Date: Sun Jan 11 20:08:30 2026 +0100
Remove post that instead became how-to-assign-version-numbers-to-software
diff --git a/posts/2026/01/08/how-to-release-software/post.md b/posts/2026/01/08/how-to-release-software/post.md
deleted file mode 100644
index 2e7abc9..0000000
--- a/posts/2026/01/08/how-to-release-software/post.md
+++ /dev/null
@@ -1,128 +0,0 @@
----
-date: 2026-01-08 20:06:45
-title: How to Release Software?
-tags: agile, hidden
-markup: markup
----
-
-My thoughts on software development are highly influenced by agile ideas. When
-I think about how software evolves, I think about small incremental changes
-that are applied to a code repository. Each change goes through a continuous
-integration pipeline, and at every stage of that pipeline we gain confidence
-that the change is good through various automated tests. If the change makes it
-all the way to the end of the pipeline, we can choose to deploy that version to
-production or release it to customers.
-
-```
- | Build | Test | Deploy Test
-version 1 | ok > ok > fail
-version 2 | ok > ok > ok > Deploy Pro?
-version 3 | ok > ok > ok > Deploy Pro?
-```
-
-In this world, every change that passes the pipeline is a release candidate. If
-we choose to release it, it becomes a release.
-
-What is a release candidate?
-
-When I think about a release candiate, I think about a particular version of a
-piece of software that has been built and packaged. There might be different
-builds of that version. There might be a source archive that represents the
-state of the repository at that time. There might be a windows exe. There might
-be an RPM.
-
-What is a release?
-
-When I think about a release, I also think about the artifacts that have been
-produced for a particular version of a piece of software. However, for a
-release, I also expect it to have a version number that makes sense for the
-users of the software. The version is usually represented as a set of numbers
-separated by dots like `1.2.0`.
-
-I expect that when I run the application it can output the version. Maybe in an
-about dialog. Maybe through the command line like this `<program> --version`.
-
-Furthermore, I expect a higher version number to be released later in time. I
-also expect bigger changes if the numbers to the left are incremented. For
-example, I expect there to be fewer changes between `1.2.0` and `1.2.1` than
-between `1.2.1` and `1.3.0`. But any piece of software can use any scheme here.
-
-For a release, I also expect to find a changelog so that I can see what has
-changed. I expect the changelog to be more carefully written than just a list
-of commit messages. I expect the changelog to include only changes that are
-relevant for the users of the software. In an agile way of working, refactoring
-is a common task. That means to improve the code without changing its external
-behavior. The users of the software should never be able to observe that a
-refactoring has taken place. In case they do, for example if the refactoring
-introduced a bug, it was not a refactoring. In any case, those types of changes
-are not relevant to mention in a changelog.
-
-I expect there to be a tag in the repository with the version so that I can get
-the exact commit where the release came from.
-
-## Is agile way of working incopatible with releases?
-
-## Pace
-
-The agile mindset is that more frequent releases are better.
-
-However, as a consumer of a piece of software, you probably don't like spending
-all your time upgrading. You probably favour more stability. And perhaps that
-the bugs that affect you gets fixed. And that the features that you lack get
-implemented. Otherwise, what is the point of upgrading. It can only get worse.
-
-## Notes
-
-* Changelog?
-* Binaries?
-* Branch to stabalize for release? [Version Control that doesn't suck | Nuno
- Afonso](https://www.youtube.com/watch?v=Te2gZc_mOMA&t=4618s)
-
-* AoAD2:
-
- CI: Do it as real as possible. "If it's a desktop app, build an install
- package." But how to release that at will later? What should the version
- number be?
-
- Deployng vs Releasing. (Release means no feature flag is hiding it.)
- This talks about releasing functionality. Not the noun "release".
-
- CD deploy to production
-
-## When to make a release?
-
-* https://www.jamesshore.com/v2/books/aoad2/continuous_integration
-
- > We keep our latest code ready to release.
-
- > The ultimate goal of continuous integration is to make releasing a business
- > decision, not a technical decision. When on-site customers are ready to
- > release, you push a button and release.
-
-Releasing should be a business decision.
-
-In an agile mindset, the releases should be made in a pull base system based on
-demand.
-
-## Internal vs External
-
-Internal: Git hash <version>+<build>
-
-External: semver
-
-## Continuous vs Releases
-
-In some context it might not make sense to have curated releases.
-
-Just a continuous stream of new versions that are deployed might be fine.
-
-## Business decision
-
-We have 5 builds since version 1.0.0 that have passed the CI pipeline?
-
-We decide that build 2/5 should become 1.1.0. The problem is that now we need
-to re-build it, because `<program> --version` outputs something like `1.0.0-2`
-and not `1.1.0`.
-
-And the changelog does not have the release date. Etc. The package does not
-fulfill the "release" requirements.
commit 90b13c8bce212a40d02b38b4540637dfb572826c
Author: Rickard Lindberg <rickard@rickardlindberg.me>
Date: Sun Jan 11 20:05:56 2026 +0100
Write
diff --git a/posts/2026/01/11/how-to-give-software-a-version/post.md b/posts/2026/01/11/how-to-assign-version-numbers-to-software/post.md
similarity index 100%
rename from posts/2026/01/11/how-to-give-software-a-version/post.md
rename to posts/2026/01/11/how-to-assign-version-numbers-to-software/post.md
commit 5179753e1188c28527521b3ee1137bd8cfc5069d
Author: Rickard Lindberg <rickard@rickardlindberg.me>
Date: Sun Jan 11 20:05:15 2026 +0100
Write
diff --git a/posts/2026/01/11/how-to-give-software-a-version/post.md b/posts/2026/01/11/how-to-give-software-a-version/post.md
index 34ffe37..d7b77d7 100644
--- a/posts/2026/01/11/how-to-give-software-a-version/post.md
+++ b/posts/2026/01/11/how-to-give-software-a-version/post.md
@@ -1,7 +1,7 @@
---
-date: 2026-01-11 07:28:19
+date: 2026-01-11 20:05:00
title: How to assign version numbers to software?
-tags: agile, hidden
+tags: agile
markup: markup
---
@@ -11,7 +11,7 @@ that are applied to a code repository. Each change goes through a continuous
integration pipeline, and at every stage of that pipeline we gain confidence
that the change is good through various automated tests. If the change makes it
all the way to the end of the pipeline, we can choose to deploy that version to
-production or release it to customers.
+production or release it to users.
```
Build Test Deploy Test
@@ -90,12 +90,12 @@ For software that is used internally and provided as a service, the version
numbers can probably just be a stream of incrementing numbers like `1`, `2`,
`3` and so on. Since there is no one installing the software, there is no need
to assign a specific meaning to the version number other than an identifier
-that we can talk about. And talking about it is easier if it is incrementing,
-human readable and not some sha1 hash. Here we can also use a scheme based on
-dates. For example `202601.16` might mean the 16th version produced in January 2026.
-This will make the version number more human readable since a single
-incrementing identifier can get quite larger after a while. The date
-component might perhaps also be useful in some situations.
+that we can talk about internally. And talking about it is easier if it is
+incrementing, human readable and not some sha1 hash. Here we can also use a
+scheme based on dates. For example `202601.16` might mean the 16th version
+produced in January 2026. This will make the version number more human
+readable since a single incrementing identifier can get quite larger after a
+while. The date component might perhaps also be useful in some situations.
For software that is released for others to install, we need to think a little
harder about the version number that we give it. First of all, let's tackle the
@@ -183,10 +183,6 @@ like this:
* `1.3.0-beta.1`
* `1.3.0-beta.2`
-And when we choose to make a release, we want to generate just `1.3.0`.
-
-So we also have to encode in the repo if we should make a release or not.
-
For the incrementing part, we can calculate the number of commits since the
previous release. Something like this:
@@ -195,11 +191,18 @@ $ git rev-list HEAD ^1.2.0 --count
1
```
+And when we choose to make a release, we want to generate just `1.3.0`.
+
+So we also have to encode in the repo if we should make a release or not. That
+means that in order to make a release, we have to make a commit to flip this
+switch. Perhaps that commit also includes an update to the changelog with the
+date when the release was made.
+
## What about branches?
The previous schemes do not work for branch builds. Two branches that are the
-same number of commits ahead of the previous commit in a series will get the
-same version number.
+same number of commits ahead of the main branch will get the same version
+number.
Since I am influenced by agile ideas, I don't think that you should do branch
builds. I think you should practice continuous integration and trunk based
@@ -230,5 +233,5 @@ Check it our for more information.
* The build system should generate a version number
* The version number should only be based on the current commit
-* If your software need releases (need the ability to no push all changes to
+* If your software need releases (need the ability to not push all changes to
production) then use a beta/pre-release scheme
commit 56e2b01d56299bd416cd9c9e4eb7812c02801f5a
Author: Rickard Lindberg <rickard@rickardlindberg.me>
Date: Sun Jan 11 15:02:29 2026 +0100
Write
diff --git a/posts/2026/01/11/how-to-give-software-a-version/post.md b/posts/2026/01/11/how-to-give-software-a-version/post.md
new file mode 100644
index 0000000..34ffe37
--- /dev/null
+++ b/posts/2026/01/11/how-to-give-software-a-version/post.md
@@ -0,0 +1,234 @@
+---
+date: 2026-01-11 07:28:19
+title: How to assign version numbers to software?
+tags: agile, hidden
+markup: markup
+---
+
+My thoughts on software development are highly influenced by agile ideas. When
+I think about how software evolves, I think about small incremental changes
+that are applied to a code repository. Each change goes through a continuous
+integration pipeline, and at every stage of that pipeline we gain confidence
+that the change is good through various automated tests. If the change makes it
+all the way to the end of the pipeline, we can choose to deploy that version to
+production or release it to customers.
+
+```
+ Build Test Deploy Test
+ ----- ---- -----------
+version 1: ok > ok > fail
+version 2: ok > ok > ok > Deploy Pro?
+version 3: ok > ok > ok > Deploy Pro?
+```
+
+But what version number do we give it? And how is that version number used?
+
+When I think about a version number of a piece of software, I think about dot
+separated numbers, something like `1.2.0`. The version number might also
+include parts to indicate that it is not an official release, such as
+`1.2.0-beta.3`. These version numbers are separate from the commit identifiers
+in the version control system. They are more human friendly, and usually carry
+some meaning that is useful to the user of the software.
+
+What do I expect from these version numbers?
+
+I expect that when I run the application it can output the version number.
+Maybe in an about dialog. Maybe through the command line like this `<program>
+--version`. That means that we have to pick the version number at the very
+beginning of the continuous integration pipeline so that we can bake it into
+the application.
+
+Furthermore, I expect a higher version number to be released later in time.
+That is one way they are more human friendly compared to version control commit
+identifiers. I also expect bigger changes to the software if the numbers to the
+left are incremented. For example, I expect there to be bigger changes between
+`1.2.0` and `1.3.0` than between `1.2.0` and `1.2.1`. But every piece of
+software can use their own scheme here.
+
+For an official release (a version number without any `beta` indicators or
+such), I also expect to find a changelog so that I can see what has changed. I
+expect the changelog to be more carefully written than just a list of commit
+messages. I expect the changelog to include only changes that are relevant for
+the users of the software. In an agile way of working, refactoring is a common
+task. That means to improve the code without changing its external behavior.
+The users of the software should never be able to observe that a refactoring
+has taken place. In case they do, for example if the refactoring introduced a
+bug, it was not a refactoring. In any case, those types of changes are not
+relevant to mention in a changelog.
+
+## Two types of software
+
+When I think about what version number to assign to a piece of software, I find
+it useful to distinguish between two types:
+
+* Software that is used internally and provided as a service
+* Software that is released for others to install
+
+If our software is used internally and provided as a service, it might make
+sense to deploy every version that passes the continuous integration pipeline
+to production. We want feedback on our changes as soon as possible. And in an
+internal service setting, that is possible.
+
+However if we build a binary installer for example, and users need to install
+it, we impose some kind of work on them every time we produce a new version.
+So now we have to think about if that is worth it or not. As a user of a
+piece of software, you probably don't like spending all your time upgrading.
+You probably favour more stability. And perhaps that the bugs that affect you
+gets fixed. And that the features that you lack get implemented. Otherwise,
+what is the point of upgrading. It can only get worse.
+
+Even in the case of software provided as a service, where you don't impose work
+on the users to install new versions, you might still upset them if you change
+how the software works in a way which is incompatible with how they work. Or
+annoy them with new graphical changes.
+
+## Version number schemes
+
+How to assign version numbers to these two categories of software?
+
+For software that is used internally and provided as a service, the version
+numbers can probably just be a stream of incrementing numbers like `1`, `2`,
+`3` and so on. Since there is no one installing the software, there is no need
+to assign a specific meaning to the version number other than an identifier
+that we can talk about. And talking about it is easier if it is incrementing,
+human readable and not some sha1 hash. Here we can also use a scheme based on
+dates. For example `202601.16` might mean the 16th version produced in January 2026.
+This will make the version number more human readable since a single
+incrementing identifier can get quite larger after a while. The date
+component might perhaps also be useful in some situations.
+
+For software that is released for others to install, we need to think a little
+harder about the version number that we give it. First of all, let's tackle the
+case of when to release. We've already established that it doesn't make sense
+to release every single change. But the continuous integration pipeline should
+still build every change (and produce binaries for it that can be installed).
+To distinguish them, I suggest that intermediate builds are assigned an
+identifier such as `beta` or `pre-release`. For example `1.2.0-beta.3` might be
+the 3rd beta version of the upcoming `1.2.0` release. When we choose to release
+it, we strip the beta suffix and we have the official `1.2.0` release.
+
+We also have to think about what meaning we assign to the components in the
+version number. The version number that we assign communicates something to
+the users of the software.
+
+## How to set a version in the build?
+
+When the continuous integration pipeline receives a new change to build, it
+must choose a version number. That version number should also be written to a
+file and included in the build so that the application can display it.
+
+I think that ideally, the version number should be determined by only looking at
+the given change and not include some external data like a build system id or
+current date and time.
+
+Let's see how we can do that for a number of versioning schemes. We will assume
+that Git is used, but the ideas should be transferable to other version control
+systems.
+
+### Incrementing numbers
+
+Say we have a major version and then we want to increment numbers from there:
+
+* `1.1`
+* `1.2`
+* `1.4`
+
+In this case we put the major version `1` in the repository. We hard code it in
+the build script for example.
+
+For the incrementing part, we could count the number of commits like this: `git
+rev-list HEAD --count`. That will give us an incrementing number. But it will
+increment by the number of commits. So unless you push one commit at a time,
+there will be gaps in the version number.
+
+### Time based
+
+The previous scheme will eventually render very high incrementing numbers. To
+prevent that, and make the version numbers more human friendly, we can
+designate a second component to be `<year><month>` and have the third component
+be incremented. But the third component should only count the number of commits
+done in that year and month. For example:
+
+* `1.202512.13`
+* `1.202512.14`
+* `1.202601.1`
+
+Here we still hard code the `1` in the build script. For the date part, we can
+extract the date from the commit like this:
+
+```
+$ TZ=UTC0 git log -1 --pretty=format:%cd --date=format-local:%Y%m
+202601
+```
+
+For the incrementing part, we can count how many commits are done in January
+2026 something like this:
+
+```
+$ TZ=UTC0 git log --pretty=format:%cd --date=format-local:%Y%m | grep '^202601$' | wc -l
+23
+```
+
+This is a little fragile though because it depends on committer dates being set
+correctly. So it doesn't guarantee that a later commit gets a higher version
+number.
+
+### Release based
+
+In this scenario, the version number of the release is fully encoded in the
+repo somehow. Say we are working on version `1.3.0`. For pre releases, or
+betas, or whatever we want to call them, we want to generate version numbers
+like this:
+
+* `1.3.0-beta.1`
+* `1.3.0-beta.2`
+
+And when we choose to make a release, we want to generate just `1.3.0`.
+
+So we also have to encode in the repo if we should make a release or not.
+
+For the incrementing part, we can calculate the number of commits since the
+previous release. Something like this:
+
+```
+$ git rev-list HEAD ^1.2.0 --count
+1
+```
+
+## What about branches?
+
+The previous schemes do not work for branch builds. Two branches that are the
+same number of commits ahead of the previous commit in a series will get the
+same version number.
+
+Since I am influenced by agile ideas, I don't think that you should do branch
+builds. I think you should practice continuous integration and trunk based
+development. And do [proper
+CI](post:2023/04/06/what-should-a-ci-server-do/post.md).
+
+With that said, the above schemes can work for branches as well if you append
+the commit identifier (like the sha1 hash for Git). For example:
+
+* `1.3.0-beta.3+<sha1>`
+* `1.3.0-branch.3+<sha1>`
+
+## A tool
+
+When I started thinking about how to version my own projects. I created the
+tool
+[changelog2version](https://projects.rickardlindberg.me/changelog2version/). It
+extracts the current version from the changelog in `README.md` as well as the
+last released version. Then it generates version numbers based on the scheme
+
+```
+<version>[-beta.<commits-since-last-release>+<sha1>]
+```
+
+Check it our for more information.
+
+## Summary
+
+* The build system should generate a version number
+* The version number should only be based on the current commit
+* If your software need releases (need the ability to no push all changes to
+ production) then use a beta/pre-release scheme
fatal: Invalid revision range 438e559ba55f4a155c201a5b8f6e60c01bc334f3..a2088a1702dca3b2b2377bbf18e95f6dfdd56b0a
fatal: Invalid revision range ffceef2c3939d7d91cdb4a06f04e3b433d6dc272..9c646625d0745b51637d27c688d5becd0ea5f532
fatal: Invalid revision range 7e5da555e91984c03226e6bf870f258c6af2b8f5..5bc4d8335d791978791a93fc447e4579cc391081
fatal: Invalid revision range 1e048ac4d899922a4b07e3a299bf9547e88748c1..a670beaaf3985f4e774e73b31c12af2873c369e4