Why I created yet another versioning tool and what I learned doing so
Daniel Richter
Posted on July 15, 2020
TL;DR
There are already existing tools for versioning your software like
But they didn't quite fit my use case so I created my own tool bumpup
Whats wrong with the existing tools?
There is nothing particularily wrong with the existing tools, but they didn't quite do the job for me.
semantic-release
semantic-release did too many things for me, the most important thing I didn't want semantic-release to do is pushing the updated package.json to git and publish the package to npm because at work we have specific configurations for pushing to git and publish packages that I coulnd't pass to semantic-release. Also not a problem for the project I was working on but maybe for other projects is that it's very opinionated. It only supports npm, semver and git. But what if you are writing a Java project versioned in svn?
release-it
release-it states in their documentation
Use --no-npm (or "npm": false) to ignore and skip bumping package.json and skip npm publish altogether.
So it was not possible to skip publishing but bump the version in the package.json
standard-version
standard-version seemed like the right tool at first glance. But when I installed it and used it in our CI pipeline I noticed some weird behaviour. The project I was working on is a monorepo, that means many different packages with different versions in one repository. When I committed changes to both packages in that monorepo everything worked fine, but when I committed to only one of the packages standard-version tried to version the other package leading to a weird CHANGELOG.md and git commit history with duplicate commits.
With these drawbacks in mind I decided to write my own tool for the job.
What did I do differently?
While thinking about the steps necessary to version software I first came up with the following pattern:
The core library has 4 steps: read-version, determine-type, determine-new-version and write-version. Each step depends on the data from the step before and could look something like this in code
writeVersion(determineVersion(determineType(readVersion)));
But then I remebered a pattern from functional programming: composition
This lead me to the conclusion that I didn't need to have explicit steps, I could just take an arbitrary number of plugins and compose
them together as long as each function can handle the output of the previous function as input.
That's why bumpup is so flexible. It doesn't matter what VCS, programming language, versioning semantic or package registry you use. As long as there is a plugin for it or you take the time to write your own plugin your use case is supported.
Currently there are plugins for the most common use case:
tracking an npm package versioned with semver in git, but contributions for other use cases are welcome.
My takeaways from writing my own cli app
While I maybe could have made any of the three mentioned libraries work for me digging a bit deeper into these tools or filing a feature/bug request I decided that I want to take the challenge because I hoped to learn something new and step out of my comfort zone. The most important takeaways are:
Documentation helps you write better software
Before writing any code I started with the README.md. While writing the README I started thinking from the users perspective and already wrote down the user facing API how it is best suited to the end user and not how it is easiest to program. It also helped me as guideline when thinking of the next step. While there is no need to write 100 page long design documents it definitly is useful to take a few minutes and think about what you want before writing any code. Tom Preston-Werner has a nice article on Readme-Driven-Development if you want to read more on that topic.
Testing is important
Well, I knew before that testing is important. But after trying to push my code coverage to 100% I realized that testing is not only important to know that your code does what it should do but that it helps you write better code. 'How can tests help me write better code?' you may ask. If you don't test your code you might tend to write large functions that do many different things and become hard to maintain over time. But when you try to test them you notice that it's really hard to test them because you need to set up a complex state for each test case. It becomes even harder when external dependencies like filesystems or databases come into play. To easily test these functions I started seperating the algorithmic code from the code dealing with external dependencies which lead to smaller functions that are easier to compose together.
Functional programming is hard but useful
I had heard of functional programming before but never really used the patterns in any projects besides simple Hello World. This time I tried to really incorporate these pattern into bumpup and I noticed that functional programming gets hard really fast. The first steps were easy to understand: functions should be pure and functions can take functions as parameters or return functions. But quickly I had to deal with things like currying, uncurrying, lifting, kleisli composition, monads, functors, etc. The reason why it gets so hard is that functional programming is basically math.
When I wanted to understand what a monad is I looked at the Wikipedia page about monads and was overwhelmed by a bunch of mathematical symbols that I had never seen before. What I had to realize was: I must not try to understand it from the math perspective but from a programming perspective. So whenever I encountered a new pattern I searched for <pattern> in <programming language>
and I could immediately make use of the pattern without understanding the math behind it. However after using the pattern it was easier to understand a bit of the math.
James Sinclair has a good article about why learning functional programming is hard. He explains in detail what I probably failed to explain in the last few sentences.
Writing is hard
I already noticed this while writing the README.md, but at most while writing this article. The first hard thing, especially for a non native english speaker like me, is that after a while all your sentences sound the same. I don't know if it's just because I'm reading my own sentences over and over again or because I'm lacking the english vocabulary and all my sentences are composed of the same few words and have the same structure.
The second hard thing is writing something that is easy to read and follow along: While writing this article I often restructured my sentences, deleted sentences and added others. But I'm still unsure if it's easy to follow along, because I know all the details of bumpup because I wrote it, however everything you know about it is from this article (I assume at least, maybe you stumbled across the github repo before) and I don't know if I always have the right level of detail to make it understandable without having written or seen any of the code. Although writing is hard I will try to write more articles on dev.to because I'm convinced that practicing writing also helps at work and in my private life to communicate facts clearly, precisely and without bloat to colleagues or friends.
The third hard thing about writing in english is the capital 'I', I can't remember having it written right the first time 😆.
That was my first article on dev.to and the internet in general. Let me know in the comments what you think of bumpup and give it a star if you like it, but also what you think about this article.
Posted on July 15, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.