Juha-Matti Santala
Community Builder. Dreamer. Adventurer.

Track software versions for technical blog posts

Code in this blog post was written with versions: notionhq/client: 2.3.0, Nunjucks: 3.2.4, Node: 20.17.0

When I talk about blogging for developers, a recurring question is how to deal with code example rot. Programming languages, APIs and libraries evolve all the time and a code example written today for version 1.4 might not work anymore in 10 years when software is at version 18.12.

My answer has been: I don’t worry about it too much, I have dates for my posts and I hope people can figure it out. Second part of the answer has been: I’ve been thinking about tracking the versions in my blog post metadata but haven’t implemented it yet.

Following my rule of three is a pattern, I finally decided to start implementing this. I probably won’t be able to retroactively apply this to my old posts because it’s hard to find out which versions I had when I wrote something 6 years ago. But I’ll do my best to do it for any future blog posts that have code examples.

How I implemented it

My current blog CMS is Notion. For my blog post template, I added a new multi-select field and I add my versions in format of software:version.

Notion’s multiselect input for property versions with Python:3.13 selected and Node:20.17.0 as an option.

Over time, I expect it to grow unwieldingly annoying but we’re not there yet so I’ll fix it if I ever get in trouble with managing it.

I use Eleventy for building my static site and I have custom tools to fetch blog posts from Notion and write them as Nunjucks files that then get built into the static site.

To fetch my posts from Notion API, I use their official SDK and the multi-select field is stored there as an object that looks like this:

{
  "id": "post-id",
  "type": "multi_select",
  "multi_select": [ 
    { "id": "entry-id", "name": "Python:3.13", "color": "red" }
  ]
}

I then convert that to something I can use easier:

 // properties is variable that holds all the metadata for the post from Notion
 const versions = properties.versions.multi_select.map(multiselect => {
   const [software, version] = multiselect.name.split(':');
   return { software, version };
 })

And then write it to my post’s frontmatter:

`- ${versions.map((v) => `${v.software}: ${v.version}`).join("\n  - ")}`

When I run it for this blog post, I end up with a YAML frontmatter structure like this:

versions:
  - notionhq/client: 2.3.0
  - Nunjucks: 3.2.4
  - Node: 20.17.0

In my blog post template, I then check if versions are listed and add them after my date and category information:

{% if versions %}
<div class="software-versions">
	<p>Code in this blog post was written with versions:
	{% for entry in versions %}
		{% for software, version in entry %}
		<span>{{software}}: {{version}}</span>
		{% endfor %}
		{% if not loop.last %}
		,
		{% endif %}
	{% endfor %}
	</p>
</div>
{% endif %}

loop.last is a nice way in Nunjucks templates to check if we’re on the last iteration of the loop or not. In my case, I add a comma after each entry as long as we’re not on the last iteration.

On a blog post, it then looks like this

Screenshot of a header of my blog post titled Parsing nginx server logs with regular expressions and at the last line, tetx Code in this blog post was written with versions: python: 3.13.

I will iterate over the design further but right now, the first version is published and I think it’s already an improvement compared to what I had before. What I like about my custom tooling is that I can write and showcase the information in any format I want making it very flexible.

If you have thought about this problem and solved it with something similar (or something completely different), I’d love to hear your thoughts and experiences!


If something above resonated with you, let's start a discussion about it! Email me at juhamattisantala at gmail dot com and share your thoughts. In 2025, I want to have more deeper discussions with people from around the world and I'd love if you'd be part of that.