The Changelog: Software Development, Open Source - MAJOR.SEMVER.PATCH (Interview)
Episode Date: June 26, 2024Predrag Gruevski and Chris Krycho joined the show to talk about SemVer. We explore the challenges and the advantages of semantic versioning (aka SemVer), the need for improving the tooling around SemV...er, where semantic versioning really shines and where it's needed, Types and SemVer, whether or not there's a better way, and why it's not as simple as just opting out.
Transcript
Discussion (0)
What's up friends, welcome back, this is the changelog.
We feature the hackers, the leaders, and the innovators behind Semver.
We're joined by Predragorevsky and Chris Krycho, two Semver nerds. That's what Jared called them, and they didn't get upset.
On today's show, we explore the challenges and the advantages of semantic versioning,
a.k.a. Semver.
We highlight the need for improved tooling around Semver, where semantic versioning really
shines and where it's needed, how types can influence Semver, alternative approaches,
and whether or not there's a better way, and why it's not as simple as just opting out.
If you're a Simver nerd like Predrag and Chris,
well, this show's for you.
A massive thank you to our friends
and our partners at fly.io.
That is the home of changelog.com.
With Fly, you can launch your app near your users.
It's good to be near your users,
and they'll help you do it with no ops.
Learn more at fly.io.
All right, let's send her.
What's up, friends?
I'm here with a good friend of mine, Faraz Aboukdij.
Faraz is the founder and CEO of Socket. Socket helps to protect some of the best
engineering teams out there with their developer-first security platform. They protect
your code from both vulnerable and malicious dependencies. So we've known each other for a
while now, Faras. Well, let's imagine somehow I've landed myself at Vercel. And because I'm a big fan
of you, I understand what Socket is, but I don't know how to explain it to anybody else there. I've brought you into a meeting. We're considering
Socket because we want to secure dependencies. We want to ship faster. We want everything that
you promise from Socket. How do you explain Socket to my team at Vercel? Yeah, Socket is a developer
first security platform that stops vulnerable and malicious open source dependencies from infiltrating your most
critical apps. So we do that by focusing on real threats and keeping out all the types of risks
that are out there in open source dependencies. Everything from malicious dependencies,
typo squad attacks, backdoors, risky dependencies, dependencies with hidden behavior. There's all
kinds of risks out there. A lot of reasons why a dependency might be bad news.
And Socket can help you as a developer.
Just keep all that out of your app.
Keep things nice and clean and pristine amongst your dependencies.
I saw recently Dracula.
I'm a fan of Dracula.
I don't know about you, but I love that theme.
Big fan of Zena Rocha.
And I saw there was like a misspelling there.
And so because Dracula is installed on VS Code and lots of different places,
I saw there was a typo squat sitting there that had different intentions than obviously
Dracula did. Is that an example of what you mean?
Absolutely. Yeah. Dracula, that's a perfect example. It's super common these days to see
that type of an attack where you see a common dependency that you have an attacker just pretending to be that dependency, typoing the
name of it by one letter and then trying to get unsuspecting developers to install it. Unfortunately,
we're seeing more and more of these types of attacks in the community and they're taking
advantage of the trust in open source. As developers, we need to be more aware of the
dependencies we're using and make sure that we're not pulling in anything
that could risk the data of our users
or cause a big breach at our companies.
And so part of that is obviously being more careful
and asking questions and looking more carefully
at the dependencies we use.
But also part of that is tooling.
It's really a hard problem to solve
just on your own as a single developer.
And so bringing in a tool like Socket
can really help automate a lot of that work for you. It just sort of sits there in the background. It's really, really quiet. It
doesn't create a lot of noise. But if you were to pull in something that was backdoored or
compromised in some way, we would jump into action right in the PR or right in your editor,
or even as early as you browse the web. We have a web extension that can actually give you
information if you're looking at a package
that's dangerous
or if you're browsing Stack Overflow
and you see somebody saying,
hey, just install this dependency
to solve your problems.
A lot of times even that can be a way
to get the attacker's code
onto your machine.
So Socket jumps in
at all those different places
and can tell you
if something is dangerous
and stop you from owning yourself.
Yes, don't get yourself owned. Use Socket. Check them out. Socket.dev. Big fan of you,
big fan of what you're doing with Socket. Proactive versus reactive to me is the ultimate
shift left for developers. It is totally developer first. Check it out. Socket.dev. Install the GitHub app to be easy or book a demo.
Once again, socket.dev. That's S-O-C-K-E-T dot dev. well we're here with two awesome people that reached out via twitter to talk about
something goes back to our roots jared the very first episode of this podcast was Simvered. Technically.
Right? Remember that? It was a 0.01.
Yes. And then we got crap because
0.02 was not Simver.
But
we did it anyways. So we're here with
Predrag Gorefsky and Chris
Kracho. Welcome to the Change Log.
Thank you. What do you think about the fact
that we Simvered or tried to our
podcast episode numbers? Pretty sweet, right? We tried. Thank you. time you change formats, it's a breaking change, right? If we apply Rust rules, then, you know, 0.0.x, every release is a breaking change, right? Every one is major. So that sounds good to me.
There you go. Technically, before 1.0, you can do whatever you want and the versions are allowed
to do whatever they say, per the SEMVer spec. Except in Rust. Yes. And NPM, actually. They
both have opinions. Hey, we just walked right into what we're here to talk about.
That's right. So we are here, if you haven't read the title, which I'm sure will have Semver in there somewhere, we are here to talk about semantic versioning with two Semver nerds, I'll just call you all that.
You can take a fact if you want. Not meant to be offended. Who have thought deeply on this topic. Adam and I, of course, have thought shallowly, I would say. No offense, Adam. Very shallow. Yeah. Yeah, shallowly on the topic. We know it. We've lived
with it. We've seen its failures and its successes. But let's get to know you by way of SEMVer. So,
Frederic, you have been working with SEMVer in the Rust community. Is that right? Yes. So,
I'm the author of a tool called Cargo SEMVer Checks, which is a linter for semantic versioning.
So, when you're about to publish a new version of your package, Cargo Semver Checks can check its API against the
previous release and just make sure that everything's looking good, that you haven't
made any breaking changes, or that if you have made breaking changes, the next release is major,
not minor. So you're not going to break anyone in the ecosystem.
Gotcha. And Chris, your work with
Semver is what? Basically twofold. One, my biggest one was I spent a bunch of time, I worked at
LinkedIn for about five years. And one of the things I worked on was TypeScript adoption at
LinkedIn. LinkedIn is one of the few big tech companies that instead of doing a big mono repo for all of its code, does a bunch of small
repos and uses Semver internally. And so as we were looking at how do you adopt something like
TypeScript, we wanted a really good handle on how is that going to intersect with our front-end
web development ecosystem using Ember.js and using a bunch of repos using Semver internally
and then also externally. And so I ended up writing a spec, which lives at semver-ts.org,
for what does it mean to try to apply Semver to TypeScript, among other things, because TypeScript
ships breaking changes in what the NPM ecosystem thinks of as minor releases, and also because the type system of TypeScript,
and I'm sure we'll get into more of this as we go, but it complicates what it means to make
a breaking change in really interesting ways. And that got me running down asking,
what does it mean if you have things that borderline on, you know, fancy types that only show up in Haskell or Idris otherwise affecting how your SEMVer spec actually works? which is how Predrog and I ended up talking with each other, in which I looked at, okay, what is everybody doing?
What's the state of the art for how to handle versioning,
whether you're in Ruby or Node on kind of one end of the spectrum
or whether you're in Rust or Elm
or when you're out there doing crazy things
and crazy in a very good way here,
like what the Unison programming language can do.
And just tried to say, here's like what the Unison programming language can do. And just tried to
say, here's kind of the range of options, and here's where we might be able to go in the future
by pulling in new ideas in computer science research and so on. So I'm at least as much,
like, Fredrick and I are competing for who's the nerdiest on Semper here.
I think I've gone fairly deep.
I'm mostly focused on Rust
and I also have done some prototyping
on a similar Semver linter for Python.
So I think I've gone deep and Chris has gone wide.
There you go.
Well, if we were to stay shallow
for another two or three minutes
and got all of us on board
with a foundation of what semantic versioning is
for the lay folk. Chris,
you want to define it for people? Yeah. The big idea is that versioning is a tool for communicating
with the consumers of your package, your app, whatever it may be. And Semver looked at this
problem over a decade ago and said, there are a bunch of emergent ways that the
software ecosystem has come up with to kind of describe the feel of these changes over
time.
It'd be great to give them names that we can all use to talk about them consistently and
to give those names some semantics.
And so semantic versioning, where the names are major, minor,
and patch for the kind of typical number dot number dot number versioning scheme that, again,
emerged organically over the course of decades prior to that. And so a patch version means this
has a bug fix in it, and it's otherwise backwards compatible. A minor version means it might have
bug fixes and it has some new feature and it's backwards compatible. And a major version means
there are breaking changes in this. There might also be features, there might also be bug fixes.
And then there's additional metadata you're allowed to tag on to the end. Like this is a
pre-release version. So 1.2.3-alpha.0 tells you it's an alpha release
and you can stick build metadata on the end
or whatever else you want.
But the big and important bit is that semantic notion
of what major, minor, and patch mean
and the shared vocabulary for talking about them.
And I think it's also important to think about
what Semver means from a user's perspective, because when I maintain a tool and I have a few hundred dependencies, I don't
necessarily want to look very closely at the various different version numbers and think about
what they all mean. But what I do want to be able to do is say, run cargo update inside my Rust
project and know that because everyone adheres to what is a breaking
change and when it gets published as major and not minor, the updates that cargo is going to make to
my lock file are going to be backwards compatible. They're going to result in me still having a
working building project at the end of that command. And this is not specific to Rust. I could
also do the same thing for the NPM ecosystem and
TypeScript. Let's say I could do the same thing for Python. At the end of the day, as a consumer
of Semver, I don't care about the numbers. I don't care about the meanings. I just want to
not have to sign myself up for fixing things that I did not intend to break.
I ran a command. I wanted some new dependencies that are compatible with my build.
And I want that to be the case every time. And notionally, you want that to have the same basic
meaning, even if you're jumping across ecosystems like bundler and composer and NPM and cargo and
all of these can use the same basic language. And so your experience in principle, we can talk about
it in practice, but in principle should be the same, that you can give a specifier to your package manager and say, look, only give me minor bumps to this.
And I'll take new features if they come in, and I'll take patch versions for sure if they come in, but don't give me any breaking changes.
And NPM should be able to say that, and bundlers should be able to say that, and I as a user should be able to say, okay, cool, this is going to work the same basic way.
Maybe I need to tweak the syntax a little or think about a couple different things, but it should translate across ecosystems more or less.
And I love the choice of words there, should be, ominous.
I think we'll come back to that.
I mean, I think that's the big problem, right?
Go ahead, Adam. to that yeah i mean i think that's the big problem right go ahead adam i was gonna say like this might be like the biggest question i have for this whole show and it might be just transcending all the
topics but it's like is simver poorly adopted generally or poorly misunderstood which makes
it hard to fully adopt without pain because like i feel like that's kind of hard like i don't fully
get simver in all the ways there's a lot of y'all are Simver nerds, as Jared lovingly called you guys, you know? And so there's a level of, a depth level that you
all understand Simver, that to me, if I eject and do my job, then I come back in and Simver
matters, I've forgotten all the things. And so I just wonder if there's like a lot of
people like me, and potentially Jared, you aspire like I do with that same kind of thing
is you, you eject, you do your job and you kind of come back and you're like reminding yourself
exactly all the implications of Sember. So is it, is it poorly adopted? And then generally to adopt
it and to use it, it's hard because not everybody does it the same way. I think it's a tooling
problem to be perfectly honest. If any kind of technology requires that you earn a PhD in it before you can be a proficient user, then it's not going to go very far.
And the rules of Sember in Rust, in TypeScript, in Python, they're not obvious.
I mean, I've been doing this for years, and there's hardly a week that goes by that I don't learn about a new horrifying way to accidentally cause a breaking change in a Rust project, in a Python project, in a TypeScript project, right?
So the fundamental problem here are that the rules are too complex. There's too many of them.
It's too easy to break them without noticing. Chris and I have some fantastic examples of doing
this in Rust and TypeScript, where the most innocuous seeming change ends up being breaking for some reason that no sane human would ever think of.
Right.
So the real answer here is the tooling just needs to be good.
We can't keep all of these rules in our head and we need tools to do that for us.
And so I authored one of these tools.
It's not perfect yet, but it's getting better every day.
And I think the name of the game
is not to hold ourselves
to an unreasonable standard of perfection
and just minimize the number of times
that a developer publishes a change
and then wakes up the next morning
to a hundred frustrated users saying,
why did you break my project?
Right?
Because that's a regrettable change
that happened, right?
We want as few of those as possible. And maybe we get to zero one day i don't think we're there but i think
that trying to get as close as possible to zero is a valiant effort and i think we can make a solid
dent in that i would say too like in a in a multi-person project though you also have multiple
people that can version or have implications into the versioning. And who determines whether it is a bug fix or a break?
You don't always know either.
So you may unintentionally submit something to your repository or make a change,
doing a release or somebody does the release for you.
I mean, there's a million different ways it could happen.
It seems like it's such a brittle process fraught with opportunity to fail.
I think that's right.
One of the things that I dug into really deeply when I was talking about this at a conference a month and a half ago is exactly that dynamic.
That what makes this difficult is those semantics all sound good, but the definition of a breaking change gets fuzzy. So there's a guy named Hiram
Wright who works or worked, I'm not sure if he's still there, at Google, who coined a great law
called Hiram's Law and a co-worker of his put it up on the internet. I think it's hiramslaw.com.
And it says in basically so many words, given a sufficient number of users of any API, all observable
behaviors of the system will be depended on by somebody. And my observation is that the
number of users is like a few dozen. It's not many. And that can be used as an argument to
say Semver doesn't count or is not sensible because anything you do
can break somebody
and bug fixes
will probably break somebody.
This is actually the reason
that TypeScript,
the compiler,
ships what I would think of
as breaking changes
in its minor releases
because their argument
from a philosophical point of view
is any change you make
to a compiler breaks someone. So any change you make to a compiler
breaks someone. So it's not sensible to have a discussion about what a breaking change is or
isn't. Now, I don't actually agree with that, but I do think it gets at something really important,
which is that ultimately we're not dealing in something that's purely a technical problem to
be solved. Because ultimately, again, versioning is
about communication, right? It's about that conversation you're trying to have with your
end user, often a conversation you're trying to keep as low bandwidth as possible, where they just
see version number change, what do I need to think about it before they even go digging into your
release notes, so that they can say, oh, yeah, this one should be safe. Let me try it.
Bump worked. Cool. Great. And I move on with my day. And so you have this tension of saying,
what's a bug? If my bug fix, which is a real bug fix, breaks my entire user base,
do I call it a bug fix anymore? And I think what that gets at is that you still need human judgment in there and what you want
is for the tool to catch all the cases which are knowable. So all the cases where you can say no
for sure this is a breaking change you want the tool to tell you that and then additionally you're
going to have things on top of that which might also be breaking changes, where you're saying, okay, this thing might not be a breaking change
in terms of that ridiculous 10,000-word spec
I wrote about TypeScript
or in terms of what cargo semver checks can catch today,
but I'm looking at it and I'm saying,
this one's going to break 96% of my users.
I'm going to go ahead and call it a breaking change
because I'm the human in charge of this and I care about my users and I'm going to make that judgment call. So it's
sort of a start with the things you can prove or know based on tooling. And that eliminates
a massive amount of the argument or confusion in the space because you can just trust the tool to
do it. And then pull in the human judgment and say, does this bug fix feel like a breaking change?
Okay, then it is.
Because again, communication tool above all.
And this is interesting, because I think it's sort of the opposite of many other linters,
where false negatives are actually okay.
Not only are they okay, they're going to happen
no matter how we feel about them, right?
Until we solve that pesky halting problem, right?
No SEMVer checker can catch everything.
Human judgment is always going to be necessary.
What we really strongly don't want
is false positives, right?
The tool saying,
I found a breaking change
where that is not the case.
So we want our tools to be extremely confident when they report something. And it's okay if they don't catch
everything. And that's the approach that cargo semver checks takes. That's the approach that my
semver linter for Python takes. I just think that's the correct approach. And that's not
always the right approach for every flavor of linter, right? Right. Sometimes the false positive is safer.
But here, I tend very strongly to agree.
And a good example is like what Elm does.
Elm has a very conservative and fairly minimal approach to the way it thinks about it.
It basically just looks at the changes when you do something at a type level.
You know, did this add a parameter to the function?
Did it remove one?
Did it add or remove a field
on a data structure?
And very simple checks like that.
And that's all it does.
But it does enforce that
and it's built into the package
publishing flow.
And I think that's great.
Again, it leaves room
for the human judgment,
but it doesn't,
it also sets a conservative baseline
that's guaranteed to be accurate.
Like it's never going to false positive you because any of those changes can be
statically known.
Nope.
A hundred percent.
You made this change.
That's gonna,
that's gonna host somebody.
Right.
What's up, friends?
I'm here in the breaks with David Hsu, founder and CEO at Retool.
If you didn't know, Retool is the fastest way to build internal software.
So, David, we're here to talk about Retool.
I love Retool.
You know that. I've been a fan of yours for years, but I'm on the outside and you're clearly on the inside, right?
You're on the inside, right? I think so. Yeah, I'd say so. Okay, cool. So given that you're on the inside
and I'm not on the inside, who is using Retool and why are they using Retool? Yeah. So the primary
reason someone uses Retool is typically they are a backend engineer who's looking to build some
sort of internal tool and it involves the frontend.
And backend engineers typically don't care too much for the frontend. They might not know React,
Redux all that well. And they say, hey, I just want a simple button, simple form on top of my
database or API. Why is it so hard? And so that's kind of the core concept behind Retool is frontend
web development has gotten so difficult in the past 5, 10, 20 years. It's so complicated today. Put
together a simple form with a submit button, have to submit to an API. You have to worry, for example,
about, oh, you know, when you press the submit button, you got to bounce it or you got to disable
it when it's, you know, is fetching is true. And then when it comes back, you got to enable the
button again. When there's an error, you got to display the error message. There's so much crap
now with building a simple form like that. And Rachel takes that all away. And so really, I think the core
reason why someone would use Retool is they just don't want to build any more internal tools.
They want to save some time. Yeah, clearly the front end has gotten complex. No doubt about that.
I think even front-enders would agree with that sentiment. And then you have back-end folks that
already have access to everything, API keys, production database,
servers, whatever. But then to just stand up Retool, to me, seems like the next real easy
button because you can just remove the entire front end layer complexity. You're not trying
to take it away. You're just trying to augment it. You're trying to give developers a given
interface, that's Retool, build out your own admin, your own view to a Google Sheet or to the production database, all inside Retool.
Let Retool be the front end to the already existing back end.
Is that about right?
Yeah, that is exactly right.
The way we think about it is we want to abstract away things that a developer should not need to focus on, such that the developer can focus on what is truly specific or unique to their business.
And so the vision of what we want to build is something like an AWS, actually, where I think AWS really fundamentally transformed the infrastructure layer.
Back in the day, developers spent all their time thinking about how do I go rack servers?
How do I go manage cooling, manage power supplies?
How do I upgrade my database without it going down?
How do I change out the hard drive while still being online?
All these problems.
And they're not problems anymore because nowadays when you want to upgrade your database,
you just go to RDS, you press a few buttons.
And so what AWS did to the infrastructure layer is what we want to do to the application layer specifically on the front end today. And for me, that's pretty exciting
because as a developer myself, I'm not really honestly that interested, for example, in managing
infrastructure in a nuts and bolts way. Now, I would much rather be like, hey, you know,
I want an S3 bucket. Boom, there's an S3 bucket. I want a database. Boom, there's a database.
And similarly, on the front end or in the application layer,
there is so much crap people have to do today
when it comes to building a simple CRUD application.
It's like, you know, you probably have to install 10, 15,
maybe even 20 different libraries.
You probably don't know what most libraries do.
It's really complicated to load a simple form.
You know, you're probably downloading almost like a megabyte or two of JavaScript.
It's so much crap to build a simple form.
And so that's kind of the idea behind Retool is could it be a lot simpler?
Could we just make it so much faster?
Could you go from nothing to a form on top of your database or API in two minutes?
Well, we think so.
Yeah, I think so too.
So listeners, Retool is built for scale.
It's built for enterprise.
It's built for everyone.
And Retool is built for developers. That's you. You can It's built for everyone. And Retool is built for developers.
That's you.
You can self-host it.
You can run it in the cloud.
A custom SSO, audit log, SOC 2 Type 2, professional services.
Starting with Retool is simple, fast, and of course, it's free if you want to try it right now.
So go to retool.com slash changelog.
That's R-E-T-O-O-L dotcom slash changelog.
Let me make two somewhat contradictory statements, which I both think are valid.
The first one is that I have been a user of Semver tooling for many years in
roughly three different Semver oriented camps, Ruby, Elixir, JavaScript, as just an application
developer. And roughly speaking, and I know Semver enough to realize like major, minor patch,
what should be safe? What should I check? Blah, blah, blah, blah. I've rarely been bitten by these problems as a user.
Now you guys are at the library level and you're at the tooling level.
So very generally, the system isn't terrible.
We are talking about its downfalls and we are nitpicking
and there are many ways that it breaks.
And the problem is we didn't know it broke until it's too late.
In fact, we didn't think it was going to be a break. And so we communicated wrong. Like these
things are all issues and ones that we all bund up against in some places better than others.
But generally speaking, I think it's been somewhat of a win. That being said, with the tooling
situation, Frederick, that you're talking about, and with our desire to constrain ourselves from miscommunicating are we finding
the local maximum right are we are we trying to polish a turd so to speak are we is there ever
a solution to this problem is it worth saying you know we can try to clean up the world that
we're living in or we can just invent a brand new world is there just is semver just fundamentally
broken and we're not going to fix it?
And we could probably just do versioning in a different way.
Some people just ignore it altogether.
Your thoughts?
I actually have some hard data to share on this.
So about a year ago, four enterprising college students and I embarked on a journey to figure
out how good the state of SEMVer compliance is in the Rust ecosystem.
Now, Rust, statically
compiled language, you know, biased towards systems, you'd expect, you know, if anyone is
very hardcore about adhering to Semver properly, it should be Rust, right?
Yeah, for sure.
So what we did is we took the top 1000 most downloaded Rust libraries, right? So these are
extremely popular things like, you know, Surdy and Tokyo
and things like that. Things that multi-billion dollar companies depend on, you know, easily,
right? And they're developed by some of the smartest, most experienced people in the ecosystem.
And we scanned 14,000 of their releases total. We found that more than one in six of them has
unintentionally shipped a SEMVer violation that we could find. And we found that more than one in six of them has unintentionally shipped a SEMR violation that we could find.
And we found that more than 3% of all the releases contained at least one SEMR violation that we could detect and could have prevented.
3% of every release means that on average, if I run cargo update once every 10 days, my project is broken once, right?
Like every 10 days that I run cargo update,
I've updated one dependency that will introduce a breaking change.
Now it's unclear whether I actually use the part of the API
that ended up being broken by that breaking change or not,
right?
So I might get lucky and it might be okay.
But now let's think about this on a population timescale,
right?
There aren't 10 people depending on these libraries, right?
When these breaking changes happen,
they break the entire ecosystem.
And so thousands of people have to spend their time
figuring out why the heck the build is red all of a sudden.
And then maintainers on the other side have to,
all of a sudden in an unscheduled, highly stressful,
highly charged environment,
publish an emergency patch that either undoes the thing or whatever it was.
Imagine if it was bundled with a security fix.
Now, all of a sudden, the pressure is even worse, right?
So as a user, when I run cargo update, I don't want to be broken.
As a maintainer, when I run cargo publish, I don't want to break people. As a member of this ecosystem,
I want the people that don't have a lot of time on their hands to not spend their time pointlessly fighting tooling in one way or another.
I want all of the maintainers of all of the libraries that I depend on
to not have to worry about were they broken or were they going to break somebody.
So the thing here that we're talking about, you know,
sounds like a small effect, 3%.
Yeah, whatever, rounding error, maybe it's okay.
But the thing is that it adds up.
These little bits of friction here and there, they really add up when you consider the large
population and the long time horizon of these projects and how many times, you know, these
projects get downloaded and used every week, right?
So even though the individual impact on
a single person's day is probably next to zero, multiply that out to the number of people that
depend on that software, and all of a sudden, the impact is pretty darn significant.
Okay, so you're saying it's not good enough as is and tooling can improve. Can it get us into a
from 3% down to a minuscule fraction in which we can live with this?
Chris, your thoughts on that?
I think the answer is yes.
And one of the reasons that I'm more bullish on sticking with SEMVR and putting tooling around it is because I did survey the rest of the world, as it were, when it comes to versioning.
And there are a lot of approaches that just say, ah, these problems with SEMVR are fundamental.
Scrap it. One of them, SoloVer, is just have one version number. It's one, two, three, et cetera,
just go up. And that has a certain appeal to it, but the actual fundamental issue there hasn't
changed. All it does is take the burden off of the maintainer of a library and put it on
all of the users. It says, okay, now you're responsible anytime any one of your dependencies
changes, including transitively, anywhere in your dependency tree, to go read the release notes,
which tend to encode things like breaking changes in the release notes. Because,
again, communication problem, right? We want to know what did this do?
And so also as an aside,
all of those proposals include things like,
well, you can also stick like pre-release numbers on the end.
And I'm like, but what, hold on, hold on.
It seems kind of like we're backing our way back
toward this whole Semper thing now, aren't we?
Shouldn't your pre-release just be another number?
I think there is a sense in
which there is a maybe fundamental local maximum. Like maybe it's local, but the hill's so big that
we're not going to find a different path. Could be wrong about that. But when I go looking around,
the things that seem like they might change the calculus here
don't so much eliminate the value of SemVer as they do build on it.
So a good example here is what the Unison programming language does.
Pretty small language, but it is aimed at industry.
It's not pure research.
And they do something that's really wacky in the best way.
You don't store your code as plain text. Instead, they take advantage of the fact that they're a
pure functional programming language with really well-specified semantics. And they say, okay,
we can take your code, normalize it, hash it, and store the compiled output of it with a pointer to it, which means
a whole bunch of interesting things. But for the purposes of versioning means when I make a breaking
change, the original version is still there because that hashed compiled version of it got
committed to a database instead of to plain text. And that database version is what anybody who
depends on it sees.
So when I add a new parameter to my function,
the consumers are still pointing to the old function,
which means they can pull this update and say,
okay, I can progressively switch over to the new function signature,
but I can do that at will
and the two can live next to each other.
And because it is a pure functional programming language
with no side effects
that aren't managed off in the runtime, et cetera, et cetera, you know, leave all that aside.
Suffice it to say, because of that choice, they can just, quote unquote, ship a breaking change
without it ever breaking anyone. The reason you still want Semver here, though, is because Semver
is a communication tool. And so Semver lets you say, okay, there are these new features in the library.
Here's a bug fix.
You're going to want this one.
And even though that means you need to actually go update
which compiled version of this function you're pointing to,
you're getting data from that.
And when you go to publish your library,
you want to be able to use that information.
Even knowing that it's not going to break your users in the same way,
it does let you then say, oh, I didn't actually mean to make a breaking change here.
I wanted this to be compatible and to just keep working forward. So things like that, I think,
are pointers in the right direction. There's also a couple papers out there from folks at the Nova University of Lisbon who are asking, like, what happens if you bake versions as types into Java?
Java because it's the kind of default language to do this kind of research on.
Their proposal is very interesting from a type theoretic and versioning perspective and would never get adopted in industry in a million years because it's just way too much boilerplate. But it does the same thing we're talking about. It bakes this notion
of backwards compatibility in, in a way that I think would, if you were going to actually ship
something like that in an industrial programming language, you would actually want SemVer as
basically how you do it. And their type system that they slap on top of Java
effectively encodes Semver with keywords.
It's like upgrades and replaces and things like that.
So I think there's work to be done here,
but I don't think it's going to be in the near term for one.
So we're going to need the tooling.
And even if and when we see
something like that type system on top of Java or what Unison is doing becoming more widespread,
I think those kinds of things lower the risks in really interesting and important ways,
but they would still really benefit from the kinds of tooling that we're talking about. They also, though, highlight,
I think, one of the things that's easy to miss in these kinds of discussions, which is a lot of
times people like me, who are type theory nerds, et cetera, like to go looking for that kind of
solution to a problem. It has two limitations. One is that's never going to work for Ruby. I say never, but like you could
imagine a world in which type adoption for Ruby is at 100%, but that world seems very unlikely to
me. Not least because a lot of people who love Ruby love it because it's dynamically typed.
And second, doing all of those things purely at that, like let's bake it into the type system
level, has costs. Because it turns out that that itself then becomes a thing that you need to think about in terms of the versioning of your language.
Because one of the things that shows up is that the more foundational whatever your tool is, like if you're an app and you just have consumers, it's not that big of a deal.
Your versioning is basically purely marketing.
If you're a library, you have a bunch of apps that use you and maybe some other libraries.
If you're a framework that everybody else builds on, how well you do this now affects
everybody else in the entire ecosystem. If you're a programming language, you're kind of doing it
at the maximum level and you still have to communicate those versioning constraints to
other people. And the more complicated your type system is,
the harder it is to actually understand what the implications are for versioning.
So the tendency that people like me have to say,
ah, bake it into the types,
then it'll be rigorous and checked forever,
can actually undermine your net goals here
because now you've made it harder
to think about this fundamental communication problem.
And this is an area where Chris and I had an interesting discussion that
made us feel like this would be a good podcast topic.
I agree with him that building versioning into the type system of a language like Ruby or Python
is not the way to go here.
But that doesn't mean that we can't build Semper tooling for dynamic languages.
And in fact, I've built a Python semantic versioning
linter that's in closed beta with a few friends right now. But if someone listening to this wants
access, find me on social media, and I'm very happy to send it your way. The core idea is that
when we as programmers look at a piece of code, and a change to that code, we have a set of rules
and heuristics that we run in our minds to
determine if something is breaking, right? A public function got deleted. A function signature changed.
Now it takes a different number of arguments. A default got, you know, went away from somewhere.
There's nothing stopping us from encoding those rules in a machine-checkable way
and just have the computer go through that checklist
and make sure that we didn't mess anything up, right?
Now, this tool isn't doing anything differently to what we were doing.
And if it finds nothing and we find nothing, then great.
Just ship it as a miner and everything's fine.
But on the off chance that we haven't had our caffeine that day
or we're just tired or, you know, we have something else going
on in our mind. And we miss one of those things, and the tool catches it, that's just pure win.
Right? So it's those cases that I'm very interested in catching with tools, it's fine,
they're never going to be perfect. I'm not perfect either. I'm just very interested in
finding the spaces where my imperfections and the tool's imperfections happen to not overlap
so that at least one of us catches it.
I mean, I like that because it's pragmatic
and because it seems achievable.
I mean, it's just work and it's probably hard work
and I'm glad you put some of that work in,
but it's really like taking that 3%,
which is probably a high watermark
considering it's the Rust ecosystem.
It's probably a much higher percent in other places.
It's actually much higher in Rust as well
because those are just the issues
that we found by running the tool.
If you use the tool, the 3% goes to zero
because the tool would have caught all of that
and prevented it, right?
So that number could only grow even in Rust
because the tool gets better every day.
How comprehensive is the tool?
Do you have a set number of things you're checking and you're just adding to that constantly? How's it work? Absolutely. So we have about 80
lints right now. So that means there are 80 flavors of breaking changes for which if they happen,
we could construct a program that gets broken by that breaking change. So we could constructively
prove that something bad happened. Either that program doesn't compile anymore or it triggers undefined behavior so it would be caught by Miri Rust's undefined behavior scanner
or something like that. So something very obviously goes wrong based on the thing that we found and we
can offer constructive proof of that. I also have a list in an issue on the project's GitHub of like
150 more things that we should catch that we don't catch yet.
And on a fairly regular basis,
new contributors to the project come in
and they go, ah, this bit me last week.
I'm going to check this thing off the list.
And they come in, they write a lint,
they open up a request, we merge it,
and the next release,
that problem is never going to be a problem
for them or for anyone else ever again,
so long as they remember to run the tool in CI before publishing or on their local machine,
if that's how they publish. So it's more of a never again kind of a tool and not really a
perfectionistic idealistic sort of approach. Yeah, exactly. Yeah. Yeah. I love it. So that's
that knocks off Rust, though.
And you've got something you're working on in Python.
But of course, there are many programming languages,
Extant, and many ecosystems,
some of which nominally do Semver.
I mean, there's also the ones that just don't do it.
I mean, that's the other thing with,
first of all, you have to buy into Semver,
but then also the communication of how you do Semver.
Forget the tooling.
I'm sure there's different ideas of what Sember is,
even in the minds of one person.
You know what I'm saying?
Yeah, we do Sember, except for the majors,
because we want to have a marketing opportunity.
And so we throw it out the window when it comes time to market.
Because that's a communication problem as well,
and one that many people dislike Sember because of.
They can't market their big releases. I actually think that is the biggest unsolved weak point of Semver, like more than the
philosophical, what is a bug fix problem? Because that one's really one that we can just agree,
kind of make a definition, make a contract and run with it. Everybody can get on the same page.
Maybe I don't like the contract you picked, but I can at least understand it and follow it. The fact that versions are also a tool for
marketing makes it very difficult for projects which do SEMVR and also do breaking changes
to figure out how to use their marketing. Now, Rust itself as a language has just said,
oh, we'll never ship a breaking
change, problem solved. We'll tackle marketing in entirely other ways. Ember.js famously is like the
most Semver project ever and has struggled enormously with this because the JavaScript
community looks at big releases and thinks, ah, this is Ember JS5 or Ember JS6
coming up in however many months from now.
Sweet, big breaking, big cool features
because when you look at React 19,
you get a bunch of cool features with it,
a ton of stuff and some breaking behaviors
that they slowly built up to
over the course of the React 18 lifecycle.
But then you get to Ember 5
and it says no new features. And you say, I'm
sorry, what? Because Ember says, no, the only thing we use major versions for is removing deprecated
code. And in some ways that's very powerful because it means that you can think about how
you schedule in updating to minor releases, updating to long-term support minor releases.
And then, okay, if an Ember release comes every 18 months,
I can just bracket in.
I need to have these deprecations cleaned up by time X,
whatever that ends up being.
That's really powerful.
And you also know that you're not coupled to the existence of these new features
for I have to get through these breaking changes first.
That's great, but how do you tell that story?
And I would put it as a former Ember Framework core team member
who thought about this a bunch.
We never figured it out.
Like Ember tries to use additions to do this,
which is similar to Rust's notion of additions.
It's this kind of big,
everything comes together at a point in time.
We've updated all the documentation. All the APIs are coherent. Everything is in a place where
we've made this really significant change to the ecosystem. It's a good idea that in the Ember case,
I don't think has worked. I think it has sort of worked in Rust's case because it comes with other
trade-offs around being able to make changes to the surface syntax of the language that are opt-in but breaking and backwards compatible.
That's a whole different podcast and a different discussion.
Super cool, worth talking about.
But I don't think we have a solution for this particular dynamic of how to, if we're going to SemVer seriously, how do we communicate, here's a big marketing moment. Ember's Editions is, I think, a step in the right direction there. But I've sometimes wondered if we don't need like marketing version as just a totally different thing. And I think that's what Editions points at. But I don't know. I think it might also be a matter of seeing somebody with enough scale try it.
Because Ember's very small.
It has a lot of great things going for it.
But if you're 2%, 4%, whatever of the broader JavaScript ecosystem trying that kind of thing, whereas if React did it and did it that way, I think it might stick more in people's minds of, oh, this is how this works.
Now, I don't expect the React community to try that, but you could imagine that a bigger project
might be able to influence the way people think about versioning more effectively by
taking that swing. But it might just also not work and we'd have to try something else.
I also have a maybe strange idea for how to maybe fix this. What if we made breaking changes not actually
require major versions and still adhere to December? So we could actually reserve major
versions for marketing only. Crazy. I know. Hear me out. And just to be clear, I am not planning
on building this until there is a lot more funding flowing to cargo Sanford checks and the other projects I'm working
on. But it is a genuine idea that I think could work. Imagine that we could detect with very good
confidence, breaking changes in a mechanical fashion. And based on the change that we see,
right? Like this argument is now necessary and functions as opposed to previously we could pick up a default, whatever.
If we could generate code mods,
rules for how to change code
so that it's compliant in the new version
and preserves the behavior of the old version, right?
Where when I run cargo update
or the equivalent in NPM or PIP or poetry or whatever,
in addition to picking up the new version,
it also goes in and tweaks my uses of the code
that it updated dependencies for
so that I'm no longer broken, right?
This still satisfies my objective
of the build is still green before and after, right?
And there was a breaking change in the API,
but that breaking change only affects me
the next time I write a piece of code that uses that API and not any of the existing uses.
So in my book, if you ship code mods that are of sufficient quality for all of the changes that are breaking, that does not necessitate a major change to tier December.
Because no human action was involved in making that change.
That change is completely mechanical, right?
And at that point, we can say major versions are for marketing and everything else can
be minor.
Now, obviously, this requires a bunch of tooling and infrastructure that we don't have and
tooling and infrastructure that I would love to build if someone wants to send a lot of
money to my GitHub sponsors page.
But I think it's a viable future that we could have
that solves both this problem and a lot of ergonomics
and usability problems of SemVer as well.
Yeah. That's interesting.
It's a cool idea.
Very much feels like a boil the ocean kind of a thing.
That's what people said about cargo SemVer checks when I started it.
There was a lot of this will never work.
I think one of the things that strikes me about that is that it works. about cargo sound for checks when I started it. There was a lot of, this will never work.
I think one of the things that strikes me about that is that it works if and only if you can make it,
as you said, genuinely 100%
on the code modability side of it.
And so I think it gets at the human communication
dynamic piece of it again.
One of the things I think about a lot is,
if this operation used to be, you know, to use the standard performance language O of one,
constant time access to get something out of this data structure. And now I made a change to the
performance of that data structure that gives it way better memory characteristics, but now makes
it O of N. So it scales with the size of
the number of items in the data structure. And the surface level API didn't change.
That's a breaking change in my book, because you can radically impact the performance of your
end user systems by doing that. But it's not going to be statically catchable by the kinds of things that could code mod it
because the API hasn't changed.
And so I think there's an interesting chat.
Like I think what you just described is good
and it's actually kind of the improved version
of what Rust and Ember
and some other JS frameworks have tried to do
of when we ship a deprecation,
we also ship a code mod with it
that moves you to the new API. And that
goes a long way, especially if it's one of the ones that's 100% rather than like 80%. And now
you have to do the last 80% yourself. And yes, listeners, I said 80% both times on purpose,
because that is my experience of how that goes. You solve that 80% in the last 20%
takes another 80% of the time. I think the other challenge there
is making the thing trustworthy enough
that people feel like it's safe to do that
as part of their NPM update or cargo update
or whatever it is.
And I think you probably have more leverage to do it
in an ecosystem with types
than one that is without types
for the simple reason that it's easier to be confident
that you got it right with that code mod.
Not necessarily impossible in Ruby or JS or whatever,
but easier.
Hmm.
Hmm.
It seems like for incremental adoption,
I like the idea, Prederick, but it seems like for that,, I like the idea, Predic,
but it seems like for that,
just simply adding a prefixed,
I mean, Chris is kind of joking,
but maybe would work kind of like,
why don't we just add another number
that if your version has four numbers,
the first one's marketing.
If it has three numbers,
then it's just the old style.
And so you could just have that
and not having to fix that problem
of the breaking change with the Codemods thing,
which I still think would be a worthy goal
and one that you should definitely get sponsored
by somebody to work on.
But it seems like it's really far away
from something that we could use.
Would it always remain one in front of that?
So if you had four numbers,
would the number one or number two be like... Because that's the thing too with marketing is not to say negatively about those folks but
they're not generally in the minutiae like we are here and there's a thought process break where
what they think is marketing is simply what is acceptable what's praiseworthy what's
celebratory not so much practical and pragmatic in the application state. Right. So it's really just window dressing.
It's not really useful.
It's just there to be there to represent.
Which is why it's optional.
Right.
There's almost a sense in which the ordering is the opposite of what we want for marketing reasons.
True. In that the minor release, which comes along, whether it's on a cadence like Rust or Chrome or whatever, where it comes out on a predictable timeline or where it comes out whenever it comes out.
The minor release, if you think about the SemVerse semantics, is actually the marketing excitement.
It's the we got new stuff moment.
And yet it's the second slot.
Right. got new stuff moment. And yet it's the second slot. And the something broke is the first slot
because there's a good reason for it, which is it's the one that's going to hurt the most.
Hopefully the other ones won't hurt, but doing the breaking changes is going to be some work.
That's why it's breaking. I don't have a solution for that, but it does feel like they're backwards
that way. And in fact, when I was making slides for that talk,
one of the things I noticed
is that when I went to explain it,
and I think I did this
when I was talking about it earlier,
you want to explain the order upside down.
You want to start with patch
and say a patch is a bug fix.
And then a minor is a feature or a bug fix.
And then the major is breaking changes and maybe also a feature or a
bug fix, but you almost end up kind of backwards from the Semver ordering. And I do kind of wonder
if the move, you know, to your question earlier, Jared, about is Semver even the right shape?
I think if we were to take a different swing at it, we want to keep all of those bits, as it were, of data.
But we might want to find some way to treat them as a different kind of ordering.
And getting really nerdy here for a sense, it's not clear to me that when we think about like partial orders or total orders in a deep type theoretic sense, that that's actually quite the right model for versioning.
And the marketing part becomes a piece of that
because the marketing thing may really be very distinct
from when breaking changes land.
And both of those are really important bits,
but it feels really weird to say
that I have version two dot
where that's the
marketing version. 4, where there have been four breaking changes as part of marketing version 2.0,
that feels weird. And then all of these other feature numbers along the way as part of it.
And maybe just thinking about different ordering schemes or ways to kind of decouple those further could be a part of the future here.
You need all of the bits though.
You need the marketing bit somewhere
and you need the breaking change bit somewhere
and you need the bug fix bit somewhere.
One more thing that's interesting
is that there'll be names.
So like Dragonfish, for example.
You know, not only is it, and I'm talking about trunas here in this case trunas scale uh 24.04 which actually is not
semver it's it's i've learned today it's called calver it didn't really even it makes sense but
yep i didn't consider the fact that it's the 24th year in the fourth month so they have a release
cycle everyone understands that that's also they've adopted the Ubuntu way as well which is you know year and month and they have a release schedule
but they also call it a name and in the case of Ubuntu they have certain names I think they're
all based on Pixar if I recall correctly and then Dragonfish in the case of TrueNAS scale that this
is what they named it so they add one more nomenclature, which really is their marketing term.
So I wonder like, is the fourth number really required?
Can you just leverage a naming schema?
That's what Ember has tried to do.
And again, that's the question of like,
does that work or not?
I think Ubuntu is interesting as an example.
I'm not in that ecosystem anymore.
I used to run Ubuntu for a bunch of things,
but I never, even when I did,
I don't remember remembering the names very well.
I remembered the cadence,
but Calvr's interesting,
Calvr being calendar-based versioning,
because once again,
you're back to read the release notes as your mechanic
for did this break something that I depended on?
Because Calvr tells you when something happened,
but it doesn't tell you what happened then.
It's in that way, kind of like Solover,
which says even less.
I like Calvert better
because it at least gives you a date that's useful.
But both of them have the problem of
they basically reduced to something happened, right?
But what?
What happened is what I want to know.
And I think the big name
for it the branding name for it as you're saying adam it helps because then you can actually use
something and i was incorrect i think it's debbie and that leverages pixar characters right jared
toy story was where they started i'm not sure if they moved on from there and i don't know i think
abuntu actually has a double name and i'm not sure i'm trying to figure out quickly because i messed
up yeah they're like alliterative animals, aren't they?
Yeah, alliterative animals is exactly it, I think.
All right, WordyWordHog was 4.10.
WordyWordHog, I remember.
But I do agree.
I never go back and remember which one was.
I know Sid, because that's the experimental branch.
That's a great name for that.
But then the rest of the Debian releases, it was like, was this Woody?
Was this Buzz?
Was this I don't know?
Because they're important as they come up.
But historically, I just forget which one's which.
Because you have a bunch of them over time.
So they kind of lose their meaning.
I think Calvary really has a place with products less so than with libraries.
Because I think just like knowing, it is nice to know when was this product released?
Because it's right there in the version number.
But I agree, it really doesn't tell you much about that release which is fine for products
because you have all kinds of other things around them but with libraries we want to know more
details right like these are this is like business not fun kind of stuff right like we don't want to
break our businesses and for libraries and frameworks I think of Semver as Calver with
an extra optimization slot, in a sense. If every Calver release, your release twice a year is
allowed to make breaking changes, right? That's just like, as if you updated the major number in
every release. But every so often, there's a release that does not make any breaking changes.
Maybe it's just some patches, maybe it's some new features that don't affect existing functionality.
If you tell me that you've made such a release, I don't have to pay as much attention when
upgrading.
I will expect that I can just let my package manager from my ecosystem handle it for me
with no action from my part, right?
Now, you could be conservative.
You could say, I'm not sure. Maybe I've made a breaking change and release a new major every part, right? Now, you could be conservative. You could say, I'm not sure,
maybe I've made a breaking change
and release a new major every time, right?
And that still adheres to SEMVR.
That also happens to be Solover in practice.
But if you tell me that you've made breaking changes
more often than strictly necessary,
then you're shifting the effort on my part,
like Chris was saying earlier, right?
The more you can tell me that based on your gut feeling,
you have not made breaking changes.
And ideally based on the best available tooling
for your ecosystem,
you also have not made any breaking changes.
The more often I'll be able to patch
feeling confident about it
and ideally not spend six hours
then chasing type errors or worse,
you know,
misbehaviors in a system that I've deployed to production, right?
So I think Semver is just an optimization over what we could get otherwise, because I optimize away the necessity of me reading the release notes religiously for every one
of the 400 odd dependencies that my project depends on.
I do think that is suggestive, though, of the value of calendar-driven releases.
I think predictability inversion is actually really, really helpful in that regard. So the
fact that TypeScript releases basically quarterly is really helpful for planning around it. The fact
that Rust releases every six weeks is really helpful for planning around it. And it's helpful
for all the teams who work on those projects because it gives them a schedule.
But it also means that you can think,
okay, maybe we don't have the appetite
as an organization to take a TypeScript update
every quarter.
But knowing that they come out quarterly,
I can bundle it up and say,
I'm going to do this every six months
or I'm going to do this every year
or whatever the cadence I choose.
And it's very difficult to do that
in ecosystems where that kind of schedule doesn't exist. So historically, I don't think C-sharp has,
and they may have shifted this in recent years, but it wasn't the case for a long time that there
was a predictable cadence. They came when they came. And that meant you couldn't plan around
that. For a long time, Ember didn't do that and you couldn't plan around that. For a long time, Ember didn't do that, and you couldn't plan around it.
React certainly doesn't.
React 19 comes when React 19 comes.
And the entire ecosystem gets to go, woo, let's figure this out now.
Okay, this is going to be a ride.
I think having a schedule, one of the things that Predrog just said they got me thinking
is it would be really interesting if your major version intentionally adopted the calendar versioning nomenclature, because that would pull you out of the frame
of thinking about it as a marketing version. Because 24-04, like 24-04.01.2 doesn't make
you think, ah, the marketing version 21-04, like that's not a marketing version, no.
And that might be enough of a shift.
And you could still codify that like this is a system
that tools know how to process as SEMVer-ish in shape
because it's still predictable.
It's still month year with a dash in between them.
It's trivial to parse.
But doing that might give you, as an ecosystem,
a chance of much more clearly distinguishing,
okay, this is a timed release
that bundles up the breaking changes
since the last timed release.
And in effect, doing the kind of thing
that Ember is trying to do now
and that others I would like to see
try to do for breaking changes
where they just Angular does this,
Node does this,
lots of things do this.
Lean into it.
I think Angular and Node
could do exactly this.
And instead of saying it's Node 22,
you could imagine,
especially since they have a six month cadence,
that it's Node 24-whatever.
I don't remember when the next one comes out.
I think September or something.
So 24-09.
And then the patch and bug fix or patch and feature releases in it.
That could be a really helpful way of distinguishing the marketing dynamics.
Hey friends, every time you're out there in the world and you're on public Wi-Fi, let's say a hotel, a cafe, an airport, you're traveling, you're at a friend's house, and maybe they're not very
trustworthy. Maybe they are. Who knows? You're not secure. Any hacker on that network can gain
access to steal your stuff, take your personal data, and do what they want with it. And it
doesn't take much technical knowledge to hack someone. Just some cheap hardware is needed. A
Raspberry Pi. I think your data is valuable.
And I think hackers think so too because they can make up to $1,000 per person just selling your personal information on the dark web. Could be your phone number.
Could be your address.
Could be your driver's license number.
Whatever they can get their hands on, they're going to sell it.
$1,000 per person?
That's crazy. But ExpressVPN stops those hackers from stealing your personal
data by creating a secure encrypted tunnel between your device and the internet. And what I appreciate
most about ExpressVPN personally is it is super secure. It would take a hacker with a supercomputer
over 1 billion years to get past ExpressVPN's encryption. And it's also easy to use. Fire up the app and
click one button and you get protected. And that's what I do when I'm in a cafe or an airport or I'm
out there and about. I'm getting my tires changed. I'm gettingvpn.com slash changelog.
That's expressvpn.com slash changelog.
And you can get an extra three months free.
Again, expressvpn.com slash changelog.
Let's draw a corollary here, potentially,
because arguably Ubuntu has become one of the most popular flavors of Linux to install.
A lot of people use it.
They were the first to come up with LTS.
They do use, you know, this nomenclature you just mentioned.
I just wonder, like, is there a reason potentially beyond the quality of the release cycle, the
team behind it, what they've done, et cetera, has their ability to popularize LTSs to be
more trustworthy over time, as well as this nomenclature of 2404 or 2204 or 2004,
whatever, however it frames out has that.
Do you think that's been a corollary to their success?
I think there are two values there.
One is the clarity in communication, and the other one is the clarity in scheduling, both
internally and externally.
So the communication that LTS says, if you use this, you are not on the hook for worrying about security patches.
You will get those security patches for predetermined period of time.
And so you know what you're signing yourself up for.
And being able to credibly make that commitment is valuable.
The other half of it is the schedule certainty, both internally and externally.
Because I remember some time ago,
I was going to build a new computer for myself.
And I looked at the calendar and I was like,
oh, what I should do is I should wait for another month
because there's going to be a new LTS release out.
And so I don't want to set up a brand new Linux install
when I'm going to have to go through the whole process again in a month.
Right?
And knowing that that calendar, you know, is dependable and is going to happen on a
predictable cadence is very, very valuable.
I imagine for a large project like that, it's also very valuable internally, because if
you're trying to ship a new feature, you don't have to worry about like, will I make it into
the next release on time, depending on when that next release is, you know, when the next
release is, you know, when the deadline is, it's the same deadline on time, depending on when that next release is. You know when the next release is,
you know when the deadline is,
it's the same deadline every year, right?
And so you know where you stand.
You don't have to do any horse trading
with the release manager or PM or whatever,
you know, and exchange favors
or whatever else needs to happen.
I think it eliminates and streamlines
a lot of that effort that normally has to happen whenever you're trying to wrangle a big project toward a release.
And I think that's useful both for the project itself as well as for people depending on it downstream.
At the same time, though, even with your scenario where you want to wait a month because you know the date,
at the same time, if you have confidence in the upgradeability from one version to another
like the dot 10 release in particular uh jared you know i've had i've disclosed you behind the
scenes some fear like oh my gosh i've got to upgrade my ubuntu i'm going to just i'm going
to wipe the system i'm going to back up and i'm start from scratch but i've learned and i've come
to you know trust their ability to architect good software to give me an upgrade path.
And it's also like, this is probably specific to Linux that as soon as you log in, it lets you know,
hey, you've got an upgrade availability, you know, do release whatever it is for the command you run. I've learned to trust that. So at the same time, I might be, well, my, you know, 20, whatever,
21.10 of Ubuntu can easily upgrade to 22.04 safely and I've done it once
before and I'll do it again so there you go so I won't wait the month because I have trust
in their ability to release properly but I do think it really comes back to the beginning of
the show when you said SemVer is really about communication it's a machine readable way to
communicate for both machines and humans that communicates in a lot of ways.
And the challenge really is, is like, how well do people adopt it?
And are there different permutations of it that misalign or, you know, unalign, I suppose, that makes it challenging to do CIMVR?
And like you all said before, if you have to have a PhD in it, is it really worth doing? That's true.
But I think, you know,
coming out of this conversation,
I'm not thinking
Semper needs to die
by any means.
I'm thinking he needs to live
and he needs to have people
like you behind it.
But it is super challenging.
And I think to your point there,
the more that you can make
those kinds of transitions seamless,
this gets it exactly the kind of thing Pedrag was describing
around code mods. Even if you are shipping a breaking change, like even if you don't
adopt the slightly more, I'm still mulling on it proposal there of it's not breaking as long
as the code mod is good enough. Even if you don't do that, if every breaking change you make,
every deprecation you make comes with a good enough code mod so that when you do actually make that breaking change,
you could have the kind of confidence that you're talking about, Adam, that you could
know that when I go from React 19 to React 20 or Laravel, whatever the current major
is to the next one and so on, that it's going to come with that kind of automation.
And that becomes a norm across the
ecosystem, or at least for those foundational pieces, I think that would give you a lot more
confidence to say, I can take this upgrade that is going to be breaking, but I'm not going to be
broken by it because the ecosystem has built this value around this socio-technical contract we've set up with Semver, and we're going to apply all of our technical skill to make the social part of it better.
I also think there's a lovely positive feedback loop going on here.
We've noticed this.
I think we're all on the same page that releasing your software more often is better than not, right?
I'm thinking in the context of, say, like a SaaS application, right?
If you release once a year, it's going to be a much more painful release cycle
than if you release every week or every day, right?
Because big changes are hard to test and small changes are better.
I think there's something similar to be said about software
and the rate at which you apply upgrades, right?
If you accumulate 400 version upgrades
and try to do them all at once,
I mean, good luck to you.
I just don't want to be anywhere in your blast radius, right?
If you try to upgrade them, you know,
once a week or even once a day
and you have high confidence in the tooling,
then good things happen downstream of you.
So more good things happen and so on and so on.
The primary method by which Cargo Semver checks, the rust Sanford Linter I built gets adopted
is that somebody has an accident, you know, rolls a natural one. And right. I mean,
it really is an accident, right? Like they didn't mean to cause a breaking change. It just happens
to them. Right. And then they break somebody. And then somebody opens
the nation and says, hey, you broke me. This is bad. You need to fix this. And a lot of the time,
someone says, oh, by the way, cargo-samver checks catches this. And then no fingers are pointed,
no blame is assigned, but you very quickly see that project adopt cargo-samver checks for the
next release. And if they adopt cargo- cargo server checks and they ship fewer breaking changes,
then libraries that re-export their API and, you know,
rely on it to provide some capabilities downstream,
they get broken less often.
They might also adopt cargo server checks because they see it works well.
And all of a sudden, you know, a rising tide lifts all boats, right?
Everyone's software is better off.
We can all be updated more often. So we all benefit from the new features faster,
we get better performance, we get, you know, security patches patched more promptly and not,
you know, festering open for months while we try to figure out how to navigate our way through all
of the changes that we've accumulated, because we haven't updated any piece of software since,
you know, right i mean i
wish i were making this up but like these are things that happen in the real world right like
wasn't there the case that uh a large number of government machines all over the world are
still running windows xp right they don't do that because it's fun why do they do it it's
very difficult to upgrade okay i wanted to say that's all
to be super clear yeah it's hard upgrade so you would you trust the upgrade path it's a little
easier exactly but that comes with maturity too xp was super old right it's important that you
not just trust the path but you walk the path regularly right because then you have smaller
downstream effects because you have less change every time that you make that change i think that's wisdom hasn't microsoft uh windows been notoriously hard to upgrade every
new major version and that been like until maybe more recently has it been less hard
whereas like mac os for example which is a popular developer machine has been generally easier to
adopt new versions and they made the upgrade path semi-easy i mean for the most part pretty interesting i've had the opposite experience
personally really your mileage may vary developer machines might be different i mean if you're
installing specialized tools i we feel that too we do audio production so there's times whenever
i literally i think on my main machine i may just recently gotten, I don't even know what the latest version of Mac OS is. So I'm not a great example of it. I usually wait one year
to a half a year just to make sure all their kinks are out because Adobe is always behind
and Adobe is our suite of production tooling. And so I know there's some level of latency between
the two and I just can't risk it because I want to make sure you all sound amazing. Our shows are
awesome. They go out on time and I can't be the one that's the just can't risk it because I want to make sure you all sound amazing. Our shows are awesome and they go out on time.
And I can't be the one
that's the bottleneck
on those things
because of my desire
to be on the latest Mac OS.
So that aside,
I do think the upgrade path
generally is easier
in my experience with Mac OS.
I think that actually gets
at something really important
that we haven't touched on so far,
which is that a concern
for backward compatibility,
which can make upgrading easier,
in this context,
the fact that you can still run 16-bit DOS applications
on Windows 11,
which is amazing in a lot of ways,
also makes it much harder
to move the platform forward in other ways.
So Apple, by contrast,
you want to run an application
from the DOS, 16-bit DOS era?
Maybe if you're running it in an emulator
written in JavaScript, you can do that.
Cool.
But on the operating system, absolutely not.
On the other hand,
that's part of what makes it really easier.
I'm not going to say easy
because it's an enormously hard task,
but easier for Apple to do something like ship an architecture transition from x86 to ARM and pull off the whole thing in a matter of like two and a half years, three years, because a framework that is willing to make breaking changes
on the regular can move forward faster,
but that's more cost for the people upgrading.
By the same token,
somebody who's really committed to backward compatibility
can bring along all of the people
on their ecosystem very well,
but it can often be much harder for them to
get the benefits of trying new things. And I think here, actually, the example of React and Ember in
the JS ecosystem is a really good example of this, especially it's less true now. React has gotten
way more thoughtful about breaking changes, but in that kind of mid-2010s to, you know, like 2018, 2019 time,
React would ship breaking changes much more than Ember would.
And React got to try a lot more things than Ember did.
On the other hand, I've talked to a lot of senior engineers at very large companies
who got stuck on React 16 and never moved on.
And that's less true for Ember,
but Ember got to try a lot fewer things along the way.
There's just a fundamental tension there
that then gets manifested in your versioning.
And the versioning is often a symptom
rather than the cause in that case.
It's telling you a true story,
but the story it's telling you
might be telling you something else about the dynamics. The fact that Windows can still run DOS 16-bit applications has other
consequences for the kind of incoherence of the Windows ecosystem. But that's a really big benefit
if you're an enterprise IT administrator who wants to not spend a lot of costs on upgrading
applications that just work
for you. Trade-offs all the way down. I also think that good tooling on the part of language
developers can massively, you know, tweak whether this problem is insurmountable or not that bad.
You know, it's always somewhere on that spectrum, right? Rust has an interesting feature that I
really did not appreciate fully when I was starting out, which is that you can have multiple
major versions of the same library in your project exist at the same time. And like Chris, you were
just saying, this is not a decision that was taken lightly and is not something that is available for
free.
For one obvious consequence, it makes the version resolver much, much more complicated,
right?
Because it now needs to solve a problem that is much bigger in scope.
But what that means is that if, you know, Chris, you wrote a library that depends on
foo version one, and I wrote a library that depends on foo version two, and now Adam wants
to use both of
our libraries. So long as our libraries are not something extremely fundamental, like a framework
like react that expects to be, you know, in full control of everything going on in the in the
environment. Adam can probably just use both of our libraries and be completely fine with the fact
that the two of us can't agree on which version is the correct version of this dependency that Adam doesn't know or care about, right? Now, this is also not for free to Adam,
right? It means that when Adam compiles his project, he has to compile two versions of this
dependency and not one. So maybe his binary size is a little bit bigger, and maybe there's a little
bit of bloat in the development environment and stuff like that, right? But the alternative is Adam
having to pick between your library and mine. And I can tell you from the Python 2v3 transition,
that is not a pleasant experience to be in. I mean, I lived it. I made good software money,
just like moving stuff from Python 2 to Python 3. And if I could have made half as much while
dealing with none of that, that might
have been a worthwhile trade. I think that also gets at different languages have to make different
trade-offs there. NPM allows that, as do Yarn and PNPM and so on. But you generally are fine with
that on the server and extremely not fine with that on the client because your bundle size ends up mattering enormously.
And so you have to be able to pick different trade-offs.
And the NPM, et cetera, one is hard because it's trying to serve both a server ecosystem
and a client ecosystem, a browser ecosystem.
And those just have fundamentally different needs.
One thing that Rust doesn't do, and NPM theoretically does, and Yarn and PNPM actually do, is support
the idea of peer dependencies, which is this secondary dimension to all of this, where
you want to be able to say, I want to support a range of versions of this dependency of
mine, and I want to maybe even be able to test against them. And in principle, that's possible
in the NPM ecosystem and more or less nowhere else. I think it's an under-invested area.
If you're building a new programming language, you should bake peer dependencies in as a first
class concept because frameworks, React being the classic example, but in Rust, Tokyo is a very good
example similarly.
Things that sit at that foundation level that everything else builds on.
You really want to be able to say, my library is currently built against the latest version and Predrogs is built against a version four minor versions back.
But both of us just say, we have a peer dependency on the same major.
And your resolver can now say,
ah, I'm going to get the version
that satisfies both of these.
And neither of you ships a direct dependency on it.
You both specify a peer dependency
and that can quote unquote, just work.
And it's a very powerful thing that mostly doesn't exist.
Again, Yarn and PNPM finally have gotten this right
in about the last two to three
years. But it allows you to pick a different set of trade-offs and to bring this all the way back
around in very full circle to does Sempra work? It only works because you have that ability to
communicate those sets of constraints, that these share the same major version, and they might differ on which features they're using, but they share the same major.
And so you can actually build tooling around that.
And you can't do that without something shaped like SemVer, even if we iterate on the details along the way.
Well, you answered my unspoken question, which was, is there a better way?
And is it too late to unadopt Sunver?
Because that was my unspoken question. It's kind of been camping out in that area. It's like,
is there a better way? Because it's fraught with a lot of issues and a lot of challenges,
but is there a better way? And I think you just kind of answered that.
I think the best place to take it from here is to invest in tooling that we have and make it
work in even more of the
cases starting from scratch we're going to relearn a lot of the same very painful lessons in a much
longer time scale i think right well let's leave folks with some waypoints frederick to some of
the tooling you've been building perhaps chris how they can connect with you to plug into this
conversation and have uh say because there are so many details and so many little avenues and ways that we
can go with this. We could talk about it all day,
but we may walk in circles.
Semver checks. What'd you call it? Create Semver checks.
Cargo Semver checks.
Cargo Semver checks.
Seems like maybe state-of-the-art for people who want to build similar tooling
for their ecosystem.
We have to permeate all ecosystems with this tooling so that everybody who's doing semver can actually
do semver as well as we possibly can so maybe that's a great place for people to either
help out in rust or look at what you're up to and emulate in their language of choice absolutely
and the infrastructure that cargo semver checks is built on the core piece is called
trustful. It is a weird take on a database query engine that is a topic for a whole other podcast
episode sometime. Okay. But the key thing there is that it's language agnostic. So it has bindings
for Python, it works in JavaScript, you can, you know, you can run it in WASM in the web browser,
if you wanted to, you could call it from anything.
And so you could use it as the foundation of a Semver Linter for another language.
That's, in fact, how my Python Semver Linter works as well.
And I'm trying to talk Chris into building one for TypeScript.
I already have too many projects.
But if somebody else wants to partner with us on it...
There you go.
Then what? Then reach out. us on it. There you go. Then what?
Then reach out.
Then reach out.
Okay.
Yeah.
So on my end.
Predrog, I'll let you finish and then I'll.
Yeah.
And Cargo Sanford Checks and TrustFall are both completely open source.
So if someone wants to come in, learn how to write some lints, check out a weird database, maybe write some database optimizations or, you know, whatever you're interested in.
Very happy to do that.
Like I mentioned earlier, I worked with four college students who did some tremendous work
and honestly made cargo server checks into what it is today.
So if four people who hadn't even finished college yet could have such a spectacular
impact on the ecosystem, then anyone listening to this podcast could absolutely have a huge impact.
So come talk to me.
I'm very happy to help.
Yeah.
On my end, I will say if you're in the TypeScript community, do look at semvert.org, semver-ts. dash TS. The other thing is I'm happy to help someone get up to speed on how you would translate
that spec, which it's very long and very detailed because it has to be, how you would translate that
into a query rule set for something like TrustFall, because I do think we could do it.
And I think we could do it using a lot of existing infrastructure like TypeScript ESLint. I don't think it would be a boil the ocean from scratch project. But the other thing I'll say
is if you want to think about, hey, how would we do this for Elixir, especially as Elixir starts
pulling in types and makes a lot of these things even more tractable than it would already be
today, I'd be very happy to talk to you about what a semver-elixir.org kind of spec might look like.
And I would actually love to see these kinds of things emerge more generally of what is it you
need to think about. Cargo has a 10,000 word document, which you can read through and gets
added to on the regular. TypeScript now has one, though I'm basically the only maintainer. I would
love other people to contribute there. And if we started
building up that kind of ecosystem of projects for a variety of languages, what does it look like in
C Sharp? What does it look like in Java? And started making that more of a norm. What does
it look like in Ruby? That can also get a flywheel going because someone doing the time to think
through that, and then a bunch of other people taking the time to codify that into something like the TrustFall rules engine, build a schema, and ship those kinds of things.
We could make this a norm that is, maybe we're having a conversation five years from now where tons of languages do this and it's just normal.
That would be amazing.
And it's just time and effort.
And that's a big just on that statement.
But it is a just.
These things are actually tractable, solvable problems.
So come talk to me.
You can find me at chriscrycho.com and email and whatnot is all there.
I'm on.
I'm really easy.
I'm at chriscrycho everywhere because it's a globally unique identifier.
There's only one of me on the planet.
Is that right?
Good for you.
Easy to find. My blog is Predrog because
.ag is a TLD
and it's an uncommon enough name.
He's a TLD hacker.
It's just Predrog.
You guys make sure we get
links to all of those things.
I got Hiram's Law. We have some other stuff that we'll
throw into the show notes so everybody can just click through
versus having to type and memorize and whatnot.
Very cool.
Adam, anything else?
My thought after this conversation
is that there should be some sort of consortium
or working group or just collective for Simver.
People who care.
Right.
Because you've got skin in the game,
you do care,
and you've got expertise that applies.
And so just as you said, if you want to help somebody else in a different ecosystem, you're willing to help them out.
I feel like there needs to be like a little group of you, you all folks, who just care enough to cross-pollinate so that the whole entire ecosystem of software gets better because it's not about camps. That's why this show is called The Change Law, and not The Rush Show or The Ruby Show or the whatever show, because we cared more importantly about software at large,
the intersection of software and business,
and all the fun things you know about this show.
That's why we went that route, because we cared about software at large,
open source at large, and just really the direction we can all go,
the culture of software development.
And so I would encourage you guys to consider,
if it doesn't exist, make it exist.
And if you do...
Semver Nerds. Yeah, Semver Nerds. There you go. I like make it exist and if you do simver nerds
yeah simver nerds there you go i like that jared come join the simver nerds community is there a
dot nerds tld because then we could have simver dot nerds that would be there needs to be there
should be a predator go figure something out awesome thanks guys yeah thank you so much it's
been awesome my pleasure yeah thanks for having us. Thanks for having us. Thank you for having us.
Okay.
Semver nerds unite.
It is time to communicate better software through versioning.
And I'm,
I've changed my mind during this podcast. I came into the show and maybe you can tell because I was kind of against
Semver.
It's kind of hard to understand.
It's generally been problematic for me.
Others may have different experience, but after hearing Predrog and Chris's arguments and their stances and their experiences and their passion, really, and more so their expertise in Simver, I'm thankful they're on the front lines for one. And number two, I'm kind of for it. It does communicate. It has a place and we just
need to keep working to improve it, working to adopt it and working to better help folks understand
exactly what Semver is, where its place is and how to use it. What do you think? Come hang with
us in Slack, changelog.com slash community. Free to join. By the way, this show has a bonus for our Plus Plus subscribers.
Head to changelog.com slash plus plus. It's better. Yeah, it's better. ChangeLog++ is better.
Drop the ads, get a sticker pack, bonus content, closer to the metal, all the fun things you get
when you directly support us through ChangeLog plus plus once again changelog.com
slash plus plus okay a massive thank you to my new friends over at retool well i say new friends
because i made some new friends i went a couple layers deeper i talked to david shu the founder
and ceo i talked to tamar ben sharkar on behalf of neon recently and CEO. I talked to Tamar Ben-Sharkar on behalf of Neon recently.
And I've been talking to Daniel Kim behind the scenes, making all that happen.
It's so fun to work with partners and sponsors like Retool.
And they're awesome.
Check them out, retool.com.
And, of course, to our good friend Firas Aboukdijeh, representing Socket.
Socket.dev.
Ship faster, secure your dependencies. Check them out, socket.dev ship faster security dependencies check them out socket.dev and also
to our partners over at fly that's the home of changelog.com we love them you should love them
too check them out fly.io and to the beat freak in residence break master cylinder those beats
so bang it so bang it okay plus plus subscribers stick around
everyone else we'll see you on friday
frederick you wrote one of my favorite blog posts of the year you wrote it on april fool's day it
was not a trick it actually happened this was the the Wi-Fi only works when it's raining.
And I didn't know you then. I know you
now. I'm happy to know you. I shared it on
Change Dog News because I love this little story.
Share it with Adam. I'm not sure
if Adam saw this. Chris, you're nodding along.
So you know this story pretty well. This is
just a hilarious fact
of life in the common,
in the modern world. Can you tell that story
just briefly for our plus plus people?
Absolutely.
So it's a completely true story.
It was part of a project called April Cools that a friend of mine started.
We decided that it was a little bit tacky to lie to people on April 1st and instead
decided to pleasantly surprise people with true stories that are on topics unrelated
to our usual beat.
Okay.