Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I truly do not understand why so many jumped from Coffeescript. I still use it and still believe I can be more concise and expressive in Coffeescript. Typescript and ES2015 picked up a lot of wonderful things, but I find myself writing a lot less boilerplate in Coffeescript. I will continue to "do me". I dev in Coffeescript and publish in JS. The project even made it a focus to transpile to readable/friendly JS. No biggie.

I would not publish things like this, though :)

console.log [ i if i % 3 and i % 5, 'fizz' unless i % 3, 'buzz' unless i % 5 ].join '' for i in [ 1 .. 100 ]

Separately, I bought overpriced raspberry pi's instead of cheaper alternatives because I knew the community was a strong source of support. Modern JS has a much larger community than Coffeescript 2.



The article literally provides you with the answers, from a front-end developer survey:

  Lack of delimiters
  Overly opinionated syntactic sugar
  Lack of community support for the language
  Hard to read because of dense syntax
  Prone to errors because of syntactic ambiguities
For me personally it was the last one. The actual parsing/interpretation behavior is undocumented beyond a handful of basic principles, and chock-full of edge-case ambiguities. Running into undocumented/unpredictable behavior literally daily in a language for me was a deal-breaker.


> chock-full of edge-case ambiguities

That is an issue... though I really, really love python because of it's lack of delimiters. It annoys me to no end switching from python to java, jscript, c#, etc. and forgetting a semi-colon or having a comma at the end of a list trigger a compile error.


I am the opposite. I hate python’s lack of delimiters. You move a block of code around when refactoring and suddenly what is part of an if or while is not longer part of an of or a while because the editor screwed up spacing. I would much rather have to deal with a compiler error telling me I need a brace, than a runtime error because code is unconditionally run when it was supposed to be a part of an if statement.


> forgetting a semi-colon or having a comma at the end of a list trigger a compile error.

I mean, these days though, I rarely have "compile errors". Meaning in-editor compiling is so good that any "red squigglies" get immediately fixed. Writing TypeScript in VSCode I can barely remember last time my actual compile step failed.


Well, that's basically because these days the editor often runs the same compiler, just without codegen instead of having a completely separate syntax checker that may or may not highlight the same issues.


Funny, I think Python's whitespace delimiters is one of the saddest design decisions it made. I know why it was made, and linting wasn't a think back in the day, but being able to properly reformat code with machines / compress code is so much easier with real delimiters.


You can skip semicolons in Javascript and Typescript in all but the rare cases where there is ambiguity. That's what I do and there is zero problem.


Am I correct in stating that standard Typescript no longer requires semi-colons?


Yes. This has been the case for at least two years now.


But don't please. Semicolons are not optional. When you don't put your semicolons, they will inserted by the parser and it can introduce some buggy code especially if you are using some formatter.


The behavior of ASI is fully specified. Semicolons are optional. There is no undefined behavior in the language spec that makes using semicolons somehow safer.

Furthermore, even if you always write semicolons you are still subject to ASI modifying your code by inserting additional semicolons where you don’t want them. So you need to learn the rules of ASI regardless of whether you use semicolons or not. https://feross.org/never-use-semicolons/


There is no ASI in “strict mode”. And modules are always in strict mode.

Edit: I was mistaken about strict mode.


That is just not true. You can write semi-less JS modules, strict mode and ASI are orthogonal.


Indeed, just read the spec: https://tc39.es/ecma262/#sec-automatic-semicolon-insertion

No mention of strict mode in the section of ASI. Could have sworn that the ASI issues I’ve ran into in the past was resolved by using strict mode, not sure what I’ve got that from.


Thanks for the link!


Can you show an example of such a bug? The only thing I can think of is

    return
        value
which will get flagged by TS as unreachable code (you can try it in https://www.typescriptlang.org/play)


I can't think of a good real world example atm but

Say, you have

    let a = 1
    let b = 2
    (a + b) <--- b won't be 
    accessible here. 
This will also get flagged by ts. But not everyone uses ts. /shrugs

Feross is right, I guess.


I think eslint would flag it as well for what it's worth (https://eslint.org/demo, you have to set it to newer ES version in the Rules configuration at the bottom of the page).


I just use ESLint + auto fix on save and I've never had to worry about semicolons for years, and even if i did it's not an error in JS.


I recall when we first adopted Coffeescript, the engineers who did it touted that we had about 20% fewer lines of code as an example of coffeescript being better - a lot of which was probably just the removal of lines that only had curly-braces. In hindsight, this wasn't a good metric to judge the language on.

As a larger engineering organization, what we've definitely learned over time is that we need to prioritize readability (and maintainability) over writability - as our code is going to last a long time and the original engineer who wrote it, will likely move on to another project, team, or even company. And while maybe they train their replacement, after a second or third iteration of that, the knowledge can easily end up lost.

Coffeescript, as a language, definitely prioritizes terseness, often at the expense of readability. And we didn't have internal coding standards that pushed back on this.

But even if we had done a better job using coffeescript, we're still better off in typescript today, due to the stronger community and stronger set of available open source tools to work with the language.


I see readability as a key advantage of TS. Once you are able to comfortably read type signatures, it is trivial to see what a function is supposed to do, or what a library is supposed to provide. When things don't work as expected, then it's just a matter of hopping into the debugger to inspect data inflight to find where the typings aren't matching up with reality.


yup, types are a huge advantage, especially in large codebases where you probably don't know all the people who may write code to interact with your code.

Readability of types can become an issue over time, but with the right naming of interfaces and useful type aliases, that can be mitigated to some extent. And as the typescript language and community evolve together, there are new ways to make some types even more readable - I've seen generics for getting the return type of a function, which seems potentially really useful (and some people swear by; I haven't used them extensively, but it looks like a promising idea).


After ten years of JavaScript and after working on TypeScript apps that have now been around for 5+ years, I can assure you TypeScript is less maintainable long term than most languages.

Did you know that TypeScript’s type system is turing complete? Good luck figuring out literal bespoke type frameworks in TypeScript. Did you know you can have a TypeScript application that takes more than 8GB of memory to compile?

TypeScript is literally a Microsoft play to regain Visual Studio market share they lost by creating a language that much like C# and Java, practically speaking, requires a massive feature rich IDE for most developers to write software efficiently with.

You all fell for it, and at this point it’s too late. Millions of extra development hours will wasted maintaining legacy TypeScript apps for years due to a Microsoft marketing stroke of genius.


If genuine developer satisfaction from everyone I know IRL who's used typescript is due to Microsoft's marketing, I say they deserve to win this one.


> where the typings aren't matching up with reality.

There are also libraries which provide runtime type validation, such as zod or io-ts. Can only recommend; they allow that in combination with typescript's strict mode your types become completely reliable.


Uh... welcome to static typing. Yes, it's nice.


It is not at all trivial to see what a function is supposed to do from its type sig. There are an infinite number of valid type safe implementations for:

    transform(a: number): number


It makes a number out of a number. Type signature isn’t supposed to explain the implementation, just the interface.


Something you run into when adding Typescript annotations to existing JS / libraries in the wild is when the signature turns out to be:

    function transform(a: number | string): number | "" | false
And it's usually all by accident. And it works because in practice it's used with a `if (!transform(x))` check.

Static typing doesn't just tell you what's going on, but it also helps you keep your implementation honest with your intent. It's also why it's a good habit to annotate top-level functions instead of relying on their inference.


You can sometimes use overloads to make it better:

    function transform(a: number): number
    function transform(a: string): false
    function transform(""): ""
Depending on how predictable the output is from the types, of course. But for many things it works better than just using union types everywhere.


This is especially true with `| undefined` helping address the billion dollar mistake.


Exactly. The parent post claimed:

> Once you are able to comfortably read type signatures, it is trivial to see what a function is supposed to do

Typing gives minimal insight into what a function actually "does".

The solution is intention revealing naming, available in all languages.


It doesn't really matter what the function does, if the input and output signatures are Interfaces or Types that have been annotated with JSDoc.

You see "Put an object with these properties and types in, get an object of this shape back out."

The beauty of types is in this black-box, abstract behavior.

You, as a library author, can define an Interface/Contract for the behavior of something completely abstractly, and other people can all use each other's implementations or contribute original ones because they obey the contract.

Think about an "Abstract class File, which implements open, read, and write."

There are a lot of ways you could write these methods but if your type signatures match the inputs and outputs they're all equally valid with predictable behavior.

Design by contract is a beautiful thing.


it matters when the function isn't pure but otherwise contracts are good.


Naming is where it starts, but the type signature also carries plenty of information it it, especially once you get to non-primitive types.


In my view, the primary advantage of CoffeeScript was to provide ways to avoid JavaScript footguns. I think that a combination of ES 2015 and wider propagation of best practices have made many such footguns avoidable in practice without using CoffeeScript.


But it brought along its own footguns. The article has one example

> For example, we had a major production bug in the fall of 2013 due to a misplaced space character.


Definitely my experience. Implicit returns are just a breathtakingly bad idea and I have personally witnessed them cause countless bugs.


I think they can be ok in a language where it’s been well thought out. Like implicit return in clojure has never bugged or surprised me.


Interesting, I've never heard complaints about implicit returns before. I really enjoy them in languages like rust. What kind of bugs?


I recall several resulting from the fact that lodash's _.forEach terminates iteration early if the function you pass it returns a falsey value for any element. Most people (myself included) weren't aware of this functionality and thought of the callback as a void function and weren't even thinking of what the last expression in it would evaluate to.


That just seems like a poor choice of api design for forEach, rather than a fundamental problem with implicit returns.


That matches my experience: the pitch was writing less, safer code but I found that JSHint (back then) had a comparable effect without a stop-the-world migration and a lot of the size reductions weren’t significant compared to following any style standards for naming, comments, etc.


I think it does depend on how familiar the syntax feels to each individual. To me Coffeescript is the right balance. I absolutely love writing Literate Coffeescript. I am _not_ a fan of strictly typed languages but I do hope Coffeescript finds a friendly syntax for gradual typing like TS in the future. I usually prototype in a 'ducky' way and then lock down type expectations when I'm polishing.


this is totally fair, but these days you are far more likely to find engineers to hire who know JavaScript or Typescript, than Coffeescript. This certainly matters more to larger projects and larger companies.


I can understand that people don't prefer coffeescript, but if someone "knows" JS or TS they could surely use coffeescript if they needed to do so... since we're still calling them "engineers" anyway.


Sure, but if there majority of your customers want a different language? You should probably listen. That's what happened here, and we are way happier for it.

You might even guess that the "old guard" could have been against such a migration. As someone who should have been a member of that, I wasn't a fan of changing... But then got a few rude reminders from debugging coffeescript that didn't mean what I meant to write. At which point I started thinking that maybe everyone else had a point.


Your comment above seemed to refer to constraints in the other direction: "we can't use Lisp because the only coders available to hire only know Java!" The point is, coffeescript and javascript are much more similar than that, especially now that javascript has imperfectly copied some of coffeescript's improvements.


It's not we can't use it, but rather using it for everything would make hiring, onboarding, and retention tougher, as well as maintenance.

Apologies if I have the wrong impression in the earlier post.


I don't use coffeescript anymore (primarily because I have grown to appreciate benefits of type-safety in larger projects) but miss it every other day.

It is especially tragic that despite the really awesome class syntax in Coffeescript, JavaScript went ahead with something so much more restrictive and as a result we have this whole multi-year fiasco on the right decorator syntax, incompatible implementations in babel and typescript etc. With CS class syntax it was trivial to wrap functions in higher order functions before attaching to the prototype so decorator like use-cases were trivially supported.

We still don't have a well supported syntax for bound member functions (Yeah, you can use arrow functions as member properties, but now you can no longer call super in them, the decorator syntax doesn't work etc. - something that trips up junior members of our team often).

Once you get used to everything being an expression, it is really hard to get back to the awkwardness of a language that distinguishes between statements and expressions. Over the last few months at work, I have been working more on kotlin (in JVM based projects) and it has been a refreshing experience although JS support is lagging behind and nominal typing is a poor match when transpiling to a dynamic language.

I feel like the only major innovation Modern JS offered that has significantly improved the DX was async/await. We are still languishing behind other syntactical enhancements that could drastically improve DX like pipelines and pattern matching and have simultaneously regressed to the ugliness of embedded XML in code (ok that is not part of official JS syntax, but not like we have good alternatives like kotlin builders etc. either).

Over the last week I have been experimenting more with Bolero [1] and it has been a good first experience. I didn't have a great experience with F# and Fable before, primarily because I felt F# was ill-suited as a language for targeting JS, but now with good webassembly support plus elmish, routing and remoting integration Bolero seems to hit a sweet spot in terms of DX, type safety and performance.

[1] https://fsbolero.io


I ported several large code bases off of CoffeeScript last year, mostly to JavaScript, but some to TypeScript. Simply being able to use ESLint is worth it (CoffeLint wasn't even close). I wrote CoffeeScript for years but I don't miss it one bit–especially now that TypeScript has optional chaining!


Personally I’ve also always found Coffeescript easier to read and more pleasurable to write because of its syntax, especially its reliance on white-space. But it seems this is one of those things that really divide people into two groups.

My only bug bear about coffee was its lack of a concise ternary, but it’s use of is/or/unless worked well for me.

I’m a bit sad that my Coffee/Pug/Sass/Yaml stack is supported out of the box in fewer places than it was, all sharing principles of less punctuation, and for me, more clarity as a result, but community support is pushing me towards migrating back to vanilla JS. Glad that some features are coming across, but things like destructuring aren’t as powerful (no [first, middle..., last]). Will be happy to see nullish coalescing come over.

I think the typing thing is relevant for big teams, something I’ve had the luck of avoiding for the most part.


Unless is hugely valuable to me, "do x unless y" is great when x should happen in almost all cases.

It avoids the noise of if statements, I really miss it in languages that don't have it.


I have a really hard time parsing `unless`. Whenever I encounter it in Ruby or CoffeeScript code, it takes me 10-30 seconds to stop and understand what's happening with the code.

Generally, postfix conditionals are a bad idea because they are garden-path sentences [1] by design.

Glad you find them valuable, but IMO they epitomize what many other comments in this thread have said about CoffeeScript's poor readability.

[1] https://en.wikipedia.org/wiki/Garden-path_sentence


I don't see anything common in structure between postfix `unless` and examples of garden-path sentences, which seem to be based on word ambiguity. Nothing is ambiguous in e.g. `print(x) unless x != 0`. Although admittedly without syntax highlighting, conditionality it's less obvious.


You're right, coffeescript's refusal to use python's more sensible ternary idiom ('normal' if condition else 'abnormal') is strange, especially since it already has the useful trailing-if idiom ('normal' if condition).


Same here, still using it for frontend (Ember and React) and backend work to this day and very happy and productive with it. Almost never have cases where I thought I needed more strictness or better community support. I am both more comfortable writing code as well as reading code in it, especially because it enforces significant whitespace and allows you to have more code on the screen because you omit a lot of filler stuff like braces, semicolons, bar/let/const etc. It's not for everyone but my brain is 100% in tune with it. Coffeescript, lodash and a functional approach to writing code are pure bliss to me.


I still have a soft spot for coffeescript, I think it's a very nice language to write. I don't use it simply because no one else at my company does. But at this point, like others have said, JS is much better than it used to be. You have the arrow functions and classes from coffeescript, and honestly, those were probably the strongest features. Although it can probably forever hold the lack of semicolons the javascript's head.


You know you can write JavaScript without semicolons, right? Or did you know, but also know that it can bite you?


I think it's just strongly frowned upon for not many good reasons.

Examples of how can it bite you I could find were really simple. Classic 'newline after return' doesn't go away if you put in semicolons manually because it happens because even if you put them in manually they are still inserted in other places you didn't expect and you have to be still aware of that.

The other bug I've seen is when you start a line with '(' which will be interpreted as function call of a function from previous line. Putting in semicolons at the end of each line saves you from that almost always easily detectable error. But you'd be just as safe if you just put ';(' at the begining of next line instead of '('. And I think starting a statement with '(' is weird enough and specific to javascript (in C family) that ';(' is not much weirder.

I use semicolons in js but for purely esthetic reasons so it looks like Java, C++, PHP, C# and such.


Having the last line of a function always be returned was one of the stepping stones to functional programming for me as it was now finally concise to write pure functions compared to pre-ES2015. Still post ES2015, if the arrow function body isn't a one-liner, you have to put back your curly braces and return keyword.

After a brief time in LiveScript, I've since mostly written in the ML families (Elm & PureScript) where this is the default behavior.

And I still like/use CSON over YAML in some cases.


You're right that one of the biggest reasons to switch is that everyone else has switched (which is a bit sad to me since I think Coffeescript had some good features to offer), and community support + developer tooling are always important factors to consider. We were on Coffeescript 2 which had niceties like JSX syntax support, but even then our team had issues with the lack of IDE support for it. We were using it with GraphQL/Relay which also meant we needed to maintain our own compilation toolchain.

In the end, the benefits of having types and first-class editor integration convinced us to switch fully which we did over the course of a few months towards the end of 2019. One of our team members even wrote a blog post about how we did it:

https://dev.to/alexandras_dev/decaffeinating-2020-migrating-...

More power to you if you keep on using it, but maybe this can help explain the reasoning behind why we made the decision to switch.


As someone who was shamed out of using Perl, I can confirm that "more concise" isn't actually desirable at a certain point. Devs don't like verbose, but they also don't like obtuse. CoffeeScript is obtuse. To borrow a complaint from 10 years ago, it's like executable line noise.


So many moved because Coffeescript was old and typescript was new


Maybe some did. Many also moved because finally they could use a decent statically typed language on the client.


> I truly do not understand why so many jumped from Coffeescript

You clear up your own confusion right here:

> console.log [ i if i % 3 and i % 5, 'fizz' unless i % 3, 'buzz' unless i % 5 ].join '' for i in [ 1 .. 100 ]

And you said:

> I would not publish things like this, though :)

You just did though ;)

> I still use it and still believe I can be more concise and expressive in Coffeescript. Typescript and ES2015 picked up a lot of wonderful things, but I find myself writing a lot less boilerplate in Coffeescript

That boilerplate that you imply hinders conciseness isn't just boilerplate. It's code that helps you automate getting rid of certain classes of bugs. It also in most cases makes the code more readable IMO. And if the problem you are trying to solve is having to type less, I believe that's a job for your editor and tooling.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: