80 comments

[ 0.23 ms ] story [ 158 ms ] thread
I understand the pain of having changes that should be innocent leading to breaks in production. What is your strategy around testing for upgrades like this? JavaScript is not my area of expertise.
Oftentimes, you'll already have written tests to make sure your logic works. Otherwise, you might just run your app in a test environment and make sure it doesn't immediately crash. (Because that's what CoffeeScript would do in this case, as far as I know.) Pretty standard stuff across the industry.
(comment deleted)
This is your punishment for using Coffeescript.
You very clearly created this count to troll; exists for less than a day, only comments are trolling Coffeescript and D.
I was burned by this as well.

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 were you referencing a variable that doesn't exist? Your code shows a bug in CoffeeScript, which was fixed.
It's interesting to note that the current #1 story on Hacker News is io.js 1.3.0.

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.

I think even when using semver, you should lock down your dependencies (e.g. npm shrinkwrap). My approach is often to periodically delete npm-shrinkwrap.json, Gemfile.lock, composer.lock, etc. and then install updated dependencies and thoroughly test things.

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.

ES6 is pretty good - I'd rather write with that than coffeescript.
Hint: if a project claims to be following semver and the major number is small, the project either is very young, not being actively maintained or they're probably not actually following semver. All three are red flags. Library maintainers shouldn't be afraid to bump the major.
Coffeescript is an amazing tool that has allowed me to be 10x as efficient writing js code. ECMA 6 fixes a bunch of things that coffeescript makes really easy, but until the transpilers work as well as coffeescript, coffeescript still has a place. (From what I understand they are getting close and may have arrived)
And what's the lost efficiency for hitting into incompatibilities between JS<>Coffescript, like the topic?

JS seems small enough that you shouldn't have many problems in this matter, but there's usually some warts

There is a cost, but its without a doubt heavily outbalanced by having half the code.

The JS it generates is a tad ugly, but you save way more time, clarity and readability than with vanilla JS.

This is the first issue like this that I've seen. I actually didn't know about that particular syntax. I've been doing @example inside my code when I need 'this'.

Coffeescript is just coffeescript so there really aren't any incompatibilities.

The only gotcha that I've hit a few times was the automatic returns that are inserted. Sometimes you have a chain of functions that requires a true to be returned from a function and if you don't do it explicitly, coffeescript will return a falsy value.

>Coffeescript is an amazing tool that has allowed me to be 10x as efficient writing js code.

I seriously doubt the 10x number, as it's just some syntactic sugar, and not that advanced at that, and comes with its own issues.

Actually, I (and Fred Brooks, in "No Silver Bullet") don't think there's any technology that added to some programming language will make you 10x more productive.

Maybe having an editor compared to writing to punch cards.

Because it's never right to insult people, especially for something that's an obvious opinion. I like coffeescript, far more than Javascript (though I'll be moving to ES6 soon).

Software development is not a war, please please stop treating it like this. We are all in this together. Insulting people because they choose different tools hurts everyone.

>Software development is not a war, please please stop treating it like this. We are all in this together.

Sort of. And then you get the job to maintain the transpiler-of-the-day code that some previous employee left behind.

Not to say there aren't bad tools out there, but it would be much better and healthier to discuss why you don't like a specific tool, or why the tool is flawed, and to help each other learn and understand vs just calling people morons.
CoffeeScript is Turing-complete; therefore, it is a language.

It appears to be cleaner and sometimes more expressive than JavaScript in some ways. That's not worth Holy-warring against.

Coffeescript is a virus. Anyone who says it makes you work faster is because there is incompetence in writing elegant Javascript.
Lots of rage but light on facts.
JavaScript cannot be elegant by definition.
According to [1], the previously accepted syntax was never documented as being valid. That means that code affected by this change was relying on undocumented behavior.

[1]: https://github.com/jashkenas/coffeescript/issues/3819

Well that adds a much needed bit of context to the conversation. Someone decided to use undocumented behaviour in a production application and got bit. Worded as such it doesn't make Coffeescript to sound so ridiculous.
"decided" implies they knew they were using undocumented behavior and chose to do it anyway. The use of the word 'undocumented' also implies that unless a behavior is explicitly mentioned in the documentation, it should be considered 'private' and 'subject to change' -- also not a safe assumption with many projects, whose documentation is far from completely covering all intended behavior.
Sorry, I'm not a Coffeescript user so I'm familiar with its idioms. I looked up the Coffeescript website and it says:

> 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.

Completely agree that the particular bit of code shouldn't have been relying on that functionality.

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.

Does coffeescript have a formal spec? In languages with only one implementation it can be very hard to tell whether a feature is officially supported.
This is why semver is not a silver bullet. It only covers what the project maintainers think are breaking changes. Their unit tests don't consist of all programs that use their code.

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.

I've written a big project in Coffeescript in the past and it was teremendously more fun than it would have been to write in vanilla JS. However with ES6 around the corner I think it would be a mistake to start any projects in Coffeescript now. We just adopted ES6-to-5 translation via Babel[1] at work and people are really excited about it.

[1] http://babeljs.io/

I'm not sure why this was made into a blog post and put on HN. This kind of thing seems more suited to a bug tracker. Mistakes happen sometimes; report them and let the developer community take a look at them. That's the advantage of open-source.
But, like...

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.

always pin your dependencies
npm has a substantial amount of valuable automated tooling that assumes semver. It is the standard of that community.
Yeah, I completely understand that. I work on big Node apps every day.

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.

There's a lot of value in being able to receive bug fixes without explicitly knowing about them and manually upgrading. Most people do not have enough time to do this, we get paid to ship features not fiddle with developer productivity tools.

This is why npm shrinkwrap exists.

I didn't update the deps. This situation arose when a new server was spun up. Chef dutifully checked out all the various repositories, installed the deps, etc etc.

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.

I've greatly enjoyed writing CoffeeScript, but am concerned that over the long term, it will fall behind javascript in terms of feature support.

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/

Ditto. I don't think the CoffeeScript philosophy as it stands will ever have it keeping up with the pace of JavaScript proper at this point. Within the next year, I think our company will be migrating back to the land of curly braces. The charms of CoffeeScript just aren't worth missing out on all the great stuff coming to JavaScript as of late.
People can do whatever they want with their version numbers. But I like to follow the Apache Version Numbering (http://apr.apache.org/versioning.html) where backward compatibility is inherent in the number. Any change that breaks something requires a major version number change.
Even if it would use semver, you should not auto-install or update packeges in your production environment without having it tested first. Semver is just a promise other people are making, and in production you shouldn't rely on that IMO.
CoffeeScript doesn't follow "Semantic" versioning. Most projects that claim to follow "Semantic" versioning actually don't.

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.

(comment deleted)
I've felt for quite a while that we could have had a 4 octet semver that allows for a first octet romantic version while the others are proper semver.

For example: io.js version 1.2.0.0 instead of 1.3.0.

It would still be machine readable, but allows a human component (romantic version) to precede.

Thoughts?

First, why? Semver is for humans -- it puts a defined meaning to version numbers so that humans can gain useful information from them (this also is useful for automation, because once there is a clear unambiguous meaning, workflows, automated or not, can use that meaning as input.)

The "romantic" version is pure noise that adds no information, for humans or machines.

Second, why octets specifically instead of numbers (or, more precisely, non-negative integers) more generally? Is there any value in restricting the range of components of a version number to 0-255?

I think that is a wonderful idea. It could happily become a de-facto standard with people publishing a 4-octet version in READMEs and simply truncating it in the package.json.
Why, I'm so glad you asked.

In short — true compatibility determined by the test suite, which tells you exactly how compatible two pieces of interdependent software are. We can automate this.

In long:

Compatibility between any two pieces of software — say an app, and its major dependency — is a rich, yet quantifiable thing. It consists of all of the functions you call (or the classes you use) in the dependency, and the way in which you use them. These uses should be covered by tests.

If a library with a large surface area of 100 functions releases a new version ... and that version changes 1 of the lesser-used functions to have different behavior for an edge case, then by SemVer's fiat, it's a Breaking Change, and you bump the version number, and 99.89% of the library users that won't see any trouble in their application won't get the "free" upgrade. That's silly.

In a sanely written library, general backwards-compatibility is always a priority, and it's entirely possible and likely that a well-written app will be able to upgrade without any problems through dozens of "breaking" major revisions. At that point, the "Semantic" version is just a false flag that would be better off ignored. That's silly.

We have robust test suites that tell us exactly what goes wrong, and how, and where. We just need a tool that provisionally upgrades your dependencies (without regard to version) — runs your tests, and tells you what works, and what needs to be updated. If all is well, all is well. The tool can (safely! with assurances!) write the new version in your package.json (or what have you), and you can go merrily on your way. If a test fails, you update your code, or the tool can write into your package.json exactly the version ranges your current code is compatible with.

Why would you ever let a third party — no matter how well intentioned — remotely break your app?

Versions can go back to being for people, not for robots.

> and that version changes 1 of the lesser-used functions to have different behavior for an edge case, then by SemVer's fiat, it's a Breaking Change

IF that function was part of the "public API". (AND the previous behavior was not a bug? You can fix bugs without major version bump in semver)

This is the trickiest part of semver in actual practice, in my experience, having a clear definition and shared understanding between producers and consumers of what the 'public API' is.

I agree tests is the operationalized way to know if 'something has changed in a backwards compat' way; I've considered somehow tagging certain tests as 'public api' and others as not (or vice versa, public api by default unless tagged private). And then perhaps there's a rule, you can not change any test marked public API unless it's going to be a major version bump (or unless the test itself was buggy I guess? there's always exceptions sadly). And incorporating this info into docs and/or CI somehow.

And yeah, actually sticking to semver is not cost-free, for producers or consumers. Everything is a trade off.

You should write a package manager which does this and publish there instead of NPM. Breaking people's code who use NPM and don't follow your manifesto is wrong.
The point of the article is that NPM uses semver. If you disagree with semver you should use a different package manager.

> 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.

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.

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.

There's no irony, io.js is trying to follow semver and is doing so correctly, Jeremy happened to follow it in this one case but will happily break people's app on any arbitrary release.
Just as long as we're not getting personal, hey?

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.

CoffeeScript is published to NPM, which depends on semver. When people do:

   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.
If you aren't shrinkwrapping your NPM dependencies on production code then you're not using NPM correctly.

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.

I don't think development vs production matters here, you're still wasting people's time.

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.

Benefit from the NPM ecosystem?

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.

Jeremy doesn't benefit from having popular JavaScript libraries? This is news to me.
Interesting that your choice of phrase is "having" those libraries. Not "creating", "developing" or "maintaining". It's as if they just dropped into his lap or something.

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".

You're getting far off track here. Regardless of the reason for publishing to NPM, he does so knowing that it will break people's applications. You seem to be suggesting that he owes them nothing; I'm saying he owes them the courtesy not to poke them in their eyes.
If breaking an undocumented feature is such an egregious affront, the proper approach would be to vote with your feet.
Here's the documentation of the public API:

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?

You might have a point here, they should bump to 2.0. Let's see why they didn't do so.
I don't have a preference on semver. I don't think it completely solves versioning, or whether that can be solved. I just wanted to add a point on "dangerous".

> (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.

And now we get into a discussion about what constitutes public vs private API. I completely accept that the naked variable thing wasn't intended behaviour of Coffeescript. Nevertheless, it was a thing that worked and was widely enough used for it to be mentioned in the changelog.

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.)

Maybe semver needs a fourth "Vanity" version, that you can increment when it "feels right". i.e. Backbone 1.43.3.1
The problem we had when trying to push semantic versioning of our app to the rest of the business internally was mostly the fact that breaking changes necessitated a change in the major version, but the business and marketing side wanted control of when a major version could be released.

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 other option is simply _not releasing breaking changes_ until you are ready for a 'major' release with new features. Sometimes this can be a pain, but every choice here can be a pain, the question is how much pain, and for whom (consumers, developers.... marketting people).
Then don't advertise the version number. Give your big releases a flowery name and be done with it.
You posted something to be deliberately antagonistic on a programming topic and then whined about down votes. That's two downvote reasons right there.
What's up with these kind of childish slander of OSS projects being upvoted to the top?

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

Did the first example at least give a warning or did it silently succeed?
Alas, no, the compiler does not warn about that. I don't know if it should though, as accessing a "bar" variable inside that function might be legal if bar was assigned as global variable, which the CoffeeScript compiler has no way to track.
Ah, thats unfortunate... I do think giving a warning should be possible though. Coffescript has some weird variable scoping rules but the scopes are still static so the compiler you should be able to detect if you are accessing one of the compiler-generated internal variables.