Coffeescript 1.9.0 contains a backwards incompatible change
blog.davidbanham.com
blog.davidbanham.com
Take, for example, the other news at the top of the front page, Io.js 1.3.0 — a minor version bump that should include zero breaking changes to the public API.
And yet, 1.3.0 "broke" the way that urls are resolved with regards to trailing slashes: https://github.com/iojs/io.js/pull/278
If you follow SemVer — and I don't think that Io.js should, or have to pretend to — this must be yanked, and re-released as Io.js 2.0.
Ironically, the behavior being complained about in this post relates to how CoffeeScript decides to name internal compiler variables. This is not something that has ever been part of the public API — and is something we can (and might) change at any point. We've changed it in the past.
Breaking changes in your API are a good thing — when the new way is better than the old. But this isn't one of them.
For more on SemVer, and why you should resist giving in to its fascist attempt at world version domination, see: https://gist.github.com/jashkenas/cbd2b088e20279ae2c8e
I get that SemVer is an attempt to address dependency pain — but it's woefully inadequate, reductive and dangerous. We can do better.
> And yet, 1.3.0 "broke" the way that urls are resolved with regards to trailing slashes: https://github.com/iojs/io.js/pull/278
Can you link to documentation of the old way? The way I understand it is that they fixed a bug. If they broke a documented API I would agree with you, should be a major version bump.
Lastly there's a gigantic difference between breaking code because you made a mistake and breaking code because you don't give a shit and romanticize version numbers.
Ironically, this is exactly what has happened with CoffeeScript here - the blog post is conveniently light on details. 1.9.0 seems to have changed some internal variable naming, which no-one should be relying on.
Jeremy did not break anyone's app. He has said, time and time again, that CoffeeScript is not released according to semver. If you choose to implement it without watching your version numbers your app breaking is your responsibility.
I'm starting to understand why people are reluctant to open source their projects. Apparently it means you are tech support for absolutely everyone that uses it on their production systems.
npm install coffee-script --save
Their app is going to be broken in the future. It's irresponsible to put this burden on your users because you romanticize a version number. If it's this important simply don't publish to a package manager that is based on semver.And sure, CoffeeScript could be banished from NPM, but something tells me there would be a lot more complaints about that than about a 1.9.0 breaking change that was actually a bug fix. I prefer a pragmatic approach.
You can't have it both ways, you can't benefit from the npm ecosystem and then get mad when people are upset that you're not following npm's conventions and wasting their time. If you want to be righteous about something then this is what happens.
What benefit, exactly? No-one makes money from the development of CoffeeScript. It being on NPM is a convenience to those who want to use it, not a benefit for the programmer that gets absolutely nothing in return for making it.
Of course there are benefits to being a well known JS developer. But there are also drawbacks to spending a huge amount of personal time developing projects that hundreds of thousands of people depend upon and taking no money in return. Come on.
Don't get me wrong, breaking changes in code are A Bad Thing. But it irks me that instead of everyone saying "oh, what an unfortunate mistake, how can we fix it?" they say "Jeremy broke my app".
http://nodejs.org/api/url.html#url_url_resolve_from_to
They changed the way that it resolves:
url.resolve('/path/to/file', '.')
... to include a trailing slash when it returns the new URL to your code.No big deal, but it breaks apps. It's not backwards compatible. SemVer is not about feature-vs-bugfix. It's about changes to documented behavior. This is a breaking change, so it must be Io.js 2.0.
If you think I'm making a mountain out of a molehill here — I am. This change isn't a big deal. Io.js shouldn't have to bump to 2.0 to deal with it. But pretending to follow "Semantic" Versioning when you're not is just silly.
Death to tyranny, right?
> (From your linked article) If an important security fix happens in a version that also contains a breaking change for your app — you still need to adjust your app to get the fix, right?
Automatically upgrading to the latest minor revision is also a type of security risk.
Take the example of the package manager npm, where most modules (via package.json) don't mention an exact dependency. They tend to allow minor version upgrades, via semver compatibility notation (https://docs.npmjs.com/misc/semver). This is dangerous, since at any point, a module, sub-module (or sub-sub-module..) can release a malicious minor update which has full access to the user's data. An attacker breaking into a module author's computer simply has to release a minor version upgrade to infect dependency chains en masse. This even affects servers, since not everyone uses private npm.
If the version number is pinned, you can avoid the risk. But then again, users don't get the security fixes you mention above.
So, thanks for mentioning it the changelog! It's great that you're aware not only of how your code is intended to be used, but how it is being used in the wild. That's a really good thing and worthy of praise.
The bit that annoyed me was that you knew this, but still chose not to bump the major.
I read your stance on semver the last time we all did this collective dance. Most of it makes perfect sense. It is a valid argument, and I would be very happy for us, as a community, to work towards something better than semver.
That has to happen in a holistic way, though. Our package manager uses semver. Publishing your package on that package manager is an implicit acceptance of that convention. The fact that someone can `npm install coffee-script` leads them to believe that it will at least attempt to follow the rules and conventions of that ecosystem.
(Incidentally, I only found out about the recent underscore-semver-round-2 1.8.0 thing _after_ publishing this post. This isn't a guy trying to stir up larger drama. This is just a guy who had to spend his Friday evening fixing a preventable bug in production and it made him grumpy.)
> As a shortcut for this.property, you can use @property.
It also said that since March 2010 according to the Internet Archive's Wayback Machine. Why did this fellow decide to use `property` instead? I don't know but I wouldn't hold this specific situation against the Coffeescript team. But that's just like my opinion, man.
A totally reasonable response would be "Oh, sorry, I had no idea people were using that in that manner."
Fine, fair enough.
But! It was known that people were relying on that behaviour and it was mentioned in the changelog. That is a big point in favour of Jeremy as a package maintainer. Not only does he know how his code should be used, but he has an understanding of how people are using it in the wild. That's wonderful and praiseworthy.
Where I get grumpy is that this was seen as a significant enough event to mention to the humans who happen to be reading the changelog. Yet, it wasn't deemed appropriate to tell all of the computers that are installing dependencies about it.
When you use undocumented behavior, is it correct to blame the project and take no blame for yourself? That seems to be what happened here.
Anyway, funnily enough, this change was not only rectified on CoffeeScript 1.9.1, but it was also a change that only affected code that relied of the names of compiler-generated variables.
foo = (@bar) ->
# Bad: "bar" is not declared anywhere
console.log bar
# Good: the parameter is "@bar", so that should be used instead
console.log @bar
Relying on a "bar" variable existing inside that function body is no different from relying on an "_i" variable exiting inside a look like: for a in arr
console.log "the index is", _i
... which, BTW, would also break if upgrading to CoffeeScript 1.9, because the compiler-generated iterator variable would now be called "i" :)Update: i misinterpreted something. What was rectified in CoffeeScript 1.9.1 was the addition of an "_at_" prefix for the generated parameters of `(@something) ->` functions, which broke Angular's dependency injection mechanism when using it like `(@$someAngularThingy) ->`. You still can't access the auto-generated "bar" parameter in the first snippet. If you access a "bar" variable inside that function, the auto-generated parameter is then named "bar1", which is the Right Thing to do :D
And the current #1 comment on that story is wondering what the reason is behind the version jumps in quick succession without much in the way of new features (1.0.3->1.0.4->1.2.0->1.3.0).
These two stories nicely sum-up the romantic vs semantic versioning debate.
So bottom line: if you use CoffeeScript, Underscore of Backbone.js pay close attention to upgrades.
The breaking code would look like this:
```
a = (@arg) ->
console.log(arg) # Used to print arg.
# Now needs to be:
console.log(@arg)
```The problem was particularly exacerbated on our end by using `coffeeify`, which includes the latest CoffeeScript - so one could not easily pin to a particular version of CoffeeScript (i.e. 1.8.x).
This brings into question, for me, the decision making process for CoffeeScript. So the migration begins.
Contrast, lodash which just had a bunch of backwards-incompatible changes. They changed its version from 2.x to 3.x, so we all head a heads up. Lots of things broke, but we knew to look for them.
Why did you update to a new version of Coffeescript and not know about this bug before it made it to your production environment?
I agree that Coffeescript should probably just use semver. But they don't, and you have no control over that.
What you do have control over is when you update your dependencies and whether or not you validate releases before deploying to production.
It seems a little disingenuous to act like this is entirely Coffeescript's fault.
But anyone who's had an app in production for very long learns pretty quickly that you you use "1.8.0", not "^1.8.0" if you don't want things to break.
This is why npm shrinkwrap exists.
npm saw that there was a new version of a dep that was advertised as being backwards compatible, so it installed it. Then things broke.
The prevention to this is npm shrinkwrap. Had that been used on this particular service, it would have continued installing coffeescript 1.4.0. It also would have missed out on any security patches, bugfixes or performance improvements that may have happened in the meantime.
You still get the benefit of a reasonably loose specification of what versions of dependencies you expect to be compatible with, but additional security in knowing that you won't be surprised by unexpected breaking changes.
I can see their point, because going from, for example, 1.1.0 to 2.0.0, a user might well expect lots of new features, or at least a significant UI change, and not just "it looks and does the same except for one or two breaking changes".
The compromise was to have an architect's vanity number at the front of the Semantic version, so VANITY.MAJOR.MINOR.PATCH
The [Babel][1] transpiler (formerly 6to5) and tooling like Facebook's typechecker, [flow][2] have large teams and a rapid pace of development.
I don't want to miss out on cool features like generators, let/const, etc just for CoffeeScript's lovely bracket-less code.
[1]: https://babeljs.io/ [2]: http://flowtype.org/