This content originally appeared on Bits and Pieces - Medium and was authored by James Won
The What, How and Why of Semantic Versioning
Introduction
The world of programming is built upon countless libraries dependent on each other. Semantic Versioning enables programmers to communicate a versioning policy that can be easily understood by consumers of their libraries.
A couple of years ago I was part of a team that maintained an internal JavaScript library of a relatively large organisation. I got to see firsthand the importance of Semantic Versioning both as a maintainer and a consumer.
In this article, I will focus on the what, how and why of Semantic Versioning and delve into some of the lessons I learned during my experience working with it.
Warning: reading discretion is advised for anyone suffering from JavaScript package fatigue.
What is Semantic Versioning?
Semantic Versioning is a set of guidelines that specify how developers set version numbers for their package libraries.
Why use semantic versioning?
The single biggest problem in communication is the illusion that it has taken place.” — George Bernard Shaw
My TL;DR is that it all comes down to simplifying communication of risk.
When using other people’s code we have a tightrope to walk between two extremes.
- We want to ensure that managing packages aren’t done too tightly where you can never upgrade.
- But, on the other hand, we don’t want to be in a situation where we are too loose with upgrades and your code starts to break unexpectedly.
The importance of balancing these two extreme risks is elevated by the sheer size of dependencies that a typical project depends on. And then that is not the full picture. Dependencies are only at the tip of the iceberg, with your dependencies relying on their dependencies (your ‘sub-dependencies’) and so on.
Even the most well-resourced teams just don’t have the ability to thoroughly go through the code and check everything. That’s why a simple versioning system like Semantic Versioning is so important.
It creates a clear and specific set of rules for every developer to follow when versioning their libraries.
How To Use Semantic Versioning
Using Semantic Versioning is relatively straightforward.
There are three sets of numbers separated by two . ‘s. Each communicates different risks and information:
- Major — “This is a breaking change, be careful”.
- Minor — “This is a new addition that shouldn’t break your code”
- Patch — “This is patching a bug”
I’ve created a table below with some examples:
Note. There are nuances between teams and organisations — for example, a popular offshoot of semantic versioning is conventional commits but the principle is very similar.
Who Uses Semantic Versioning?
Pretty much all software developers, knowingly or unknowingly use Semantic Versioning.
Every time you use a package manager for whatever language or ecosystem you are using code that other developers have versioned using Semantic Versioning.
For example, JavaScript developers using Node Package Manager (NPM), and C# developers using Nuget, all rely on packages which have been semantically versioned.
The quality of the Semantic Versioning can vary.
- Small personal projects or half-baked packages (of which there are many) may have wildly inflated version numbers.
- On the other hand, conservative packages such as React Native haven’t even gotten to the first major despite being downloaded more than 1.3 million times (0.71.8 last time I checked).
Personal Learnings
The following quote captures succinctly my experiences using packages.
Hoping for the best, prepared for the worst, and unsurprised by anything in between. — Maya Angelou
Some key lessons I’ve taken away over the years are:
1. The Spec is only as good as the discipline of the person/group of individuals enforcing it.
To err is human, to forgive divine — Alexander Pope
As they say “To err is human”. I’ve both been the cause and recipient of errors with versioning.
Sometimes understanding when a code is considered “Breaking” can be difficult.
Changing a React prop name from oldProp to breakingProp obviously is a breaking change.
But what about if your design system changed the padding of a button by 1px? Is the visual regression enough to be considered “breaking”.
Sometimes decisions can get nuanced by context.
For example. imagine that you maintain an internally hosted design system accessible only to members of your organisation. The consumers of the package are few and you see them every day. In this context, the weight of getting the versioning right doesn’t weigh as heavily as if you were maintaining a big library like React for example.
I totally understand these difficulties as I have faced them before, and I’m not saying that a dogmatic answer is always the solution.
The key though is for developers as much as possible to adhere closely to Semantic Versioning and by default enforce it unless there are good reasons not to, even in the scenario I mentioned above where the library is internal.
2. People really hate change. But you have to fight through it.
Change is inevitable. Growth is optional — John C. Maxwell
StackOverflow, Reddit and Github issues are full of complaints about how changes to a certain package or another have caused them untold pain.
But what is certain is that delaying updating packages is perilous and to keep.
As maintainers, unless you have a package that is self-reliant with no or very few dependencies you will need to update your packages at a certain point or risk security issues or obsolescence if a key dependency moves on.
- An example is Enzyme. Until React 17, Enzyme was an incredibly popular testing library used for React applications. But it just couldn’t keep up and update support. An issue for this has been open since August 2020, and since this time React has moved on even further with 18.
As consumers the longer you wait the harder upgrading will become. You will continue to write potentially redundant code and the snowball is continuing to build into an avalanche of technical debt that will surely arrive.
- For example, I saw the spectre of React upgrades daunting consumers and preventing them from upgrading our library. After teams fell behind more than 1–2 versions, the unknowns become a psychological boogeyman that made it even harder to upgrade.
No matter what you do, the world of packages will move on.
💡 Note: Bit is an open-source tool that provides SemVer-based tagging of different components inside a single project (i.e. library or application). Using the bit tag command each component can be tagged with a SemVer. Upon tagging a version update, Bit prompts you which other components should be updated as well, and lets you auto-tag all of them together. Versioned components can be exported and distributed across teams and projects. When a component is updated with a new version, it can be independently updated by different applications, using SemVer rules.
Learn more about versioning with Bit here:
Versioning Independent Components
3. Even when you do everything right, things can and still will go wrong
Some packages won’t survive, and you may need to deal with it.
For example. until React 17, the design system team I worked on at the time happily relied on Enzyme to do React component behaviour testing. We initially tried to hang on with an unofficial adapter package, but when we realised a proper upgrade wasn’t on its way we had no choice but to bite the bullet and transition off Enzyme.
And while it was painful it led to us using React Testing Library with full gusto which honestly turned out to be a great move for us.
I have nothing but love towards the maintainer of Enzyme, I know firsthand how hard it is to maintain a library (albeit I was on a team with the economic might of a big tech company helping to keep it churning along) and I can only imagine how difficult keeping a project alive open-sourced. But the fact is we had to move on, and we did.
Sometimes everything fails. A web of packages relying on each other means trust is its strength and weakness.
An example is left-pad in 2016. Long story short (the story is absolutely fascinating so definitely have a look if you have time to burn) the developer had deleted all of his NPM repositories including a tiny package composed of 11 lines. This broke a bunch of widely used npm packages including React, leading to an emergency where NPM force reinstated the package.
There are also numerous examples of malicious software disguised as legitimate ones propagating out to infect computers with malware and crypto-mining.
Thankfully it isn’t something I’ve had to personally deal with so far. But it is the small price we pay for being in a world full of free open-source software. Being aware of this is important and having good upgrade practices to avoid the worst.
Some Tips To Make The Road Less Bumpy
- Read release notes — Making a habit of skimming release notes of dependencies really helps identify possible failure points. It also is a great way to learn how other libraries maintain their libraries. I personally advocate this as a ‘should’ for a minor upgrade and a ‘must’ for a major upgrade.
- Sanity check builds in a non-prod environment. Ideally, you should know which areas of the codebase the update affected and you should walk through the user flow to make sure nothing broke. You will already be doing this for majors (I hope!) but it is a good practice to apply for minor and patch updates.
- Over-communicate as a maintainer of a library. We can always do more to communicate better. Write comprehensive release notes, if it isn’t feasible for all releases then do so for at least your major releases. Write clear commits and PRs with good descriptions. This is a good thing to do anyway, but it’s even more important as consumers may need to review the why, what and how of your code. Even if you are a small or closed library, I’d recommend this as good habits have a tendency not to be important until it’s too late 😂.
- Update packages regularly. This goes without saying but having a process to regularly and proactively audit, assess and update packages goes a long way.
- Keep vigilant. Hope for the best but be prepared for the worst!
Conclusion
Semantic Versioning is an important guideline to help developers communicate with consumers of their packages effectively. It is a powerful tool that simplifies communication of risk and enables the huge web of packages that power our world today.
It is important to note that a guideline is only as good as the person or group of people implementing it. Semantic Versioning is no different. At the end of the day we are all human (at least at the moment anyway). It is always important to keep this in mind and to do our best both as consumers and maintainers to proactively apply good practices and be vigilant to reduce the negative impact of errant packages.
Resources For Further Reading
- “Semantic versioning 2.0”
- “About Semantic Versioning” by NPM
- “How one programmer broke the internet by deleting a tiny piece of code” by Quartz — The story of Left-pad
- “Enzyme is dead. Now what?” by Wojciech Maj on Dev.to
Versioning independent components the easy way using Bit
Bit’s open-source tool help 250,000+ devs to build apps with components.
Turn any UI, feature, or page into a reusable component — and share it across your applications. It’s easier to collaborate and build faster.
Split apps into components to make app development easier, and enjoy the best experience for the workflows you want:
→ Micro-Frontends
→ Design System
→ Code-Sharing and reuse
→ Monorepo
Learn more:
- Creating a Developer Website with Bit components
- How We Build Micro Frontends
- How we Build a Component Design System
- How to reuse React components across your projects
- 5 Ways to Build a React Monorepo
- How to Create a Composable React App with Bit
- How to Reuse and Share React Components in 2023: A Step-by-Step Guide
- 5 Tools for Building React Component Libraries in 2023
Navigating Semantic Versioning In An Increasingly Packaged World was originally published in Bits and Pieces on Medium, where people are continuing the conversation by highlighting and responding to this story.
This content originally appeared on Bits and Pieces - Medium and was authored by James Won
James Won | Sciencx (2023-05-31T06:02:17+00:00) Navigating Semantic Versioning In An Increasingly Packaged World. Retrieved from https://www.scien.cx/2023/05/31/navigating-semantic-versioning-in-an-increasingly-packaged-world/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.