Well reasoned post. I tend to use Deno when I want to test code that should run in the browser but want to have Typescript support. I was initially really excited by the differences compared to Node but the gap is closing.
I also don't like the subtle differences that Deno has from tsc.
So for a Typescript project that I want to run in the browser I usually just use `npx typescript -p tsc` and import the build dir as modules in the app.
So while I was initially really excited about Deno, I just haven't really needed it for anything special lately.
When I want to write a quick script I usually just use Python because the std lib is so rich.
I was really excited about deno and gave it a fair shake. I was certain I needed what it promised, and I was determined to make it work for me.
Since trying and running up against the friction and barriers, I’ve realized I don’t care much anymore about sharing code from front to back.
It feels weird to reflect on how badly I wanted it and how little I care about it now. I suppose I feel like it’s trying to optimize for the wrong thing.
Part of my reluctance to share code from front to back is that I have Go for the backend now, and using Typescript on the backend feels like a serious disservice to my productivity and happiness. Rust is also compelling in some cases. It’s rare that I actually want to be writing Typescript on the server.
On the frontend I find myself wanting the thinnest wrappers on my backends possible. Not like obtuse UIs that don’t hide anything for my unfortunate users, but I mean in the underlying logic, I have no desire to do anything clever or abstracted in the client.
I have one side project I built with Deno and it’s the one I’m the most reluctant to return to. The only motivation I get to work on it, frankly, is to migrate it off to something else.
I do wish it worked out. But that, and a lot of what this article mentions, has kind of ruined the whole idea for me.
Maybe we will get there one day, but I think we’re much further than many would think. The current options seem like massive compromises only to gain code you can share easily. Which, as mentioned, is quite easy to do with WASM now too. If you don’t need rapid throughput, WASM is great. If you do, it’s often still acceptable. The interface isn’t ideal, but for my purposes it has been better than adopting Deno or Bun.
Having said that… I think these are awesome projects nonetheless. They’re trying to accomplish something worth accomplishing. It’s an envelope we should keep pushing. Me though… I’m too boring for this stuff.
I'm using deno to write integration tests for a complex google sheets script. sharing code between frontend and backend may not be that much of a win, but testing js code with a CLI is a great boon.
> I have Go for the backend now, and using Typescript on the backend feels like a serious disservice to my productivity and happiness
Interesting, I feel the opposite. To me, Go is cumbersome and missing a lot of the nice APIs that Deno provides. I've switched from Go to Typescript (Node, for now, but considering Deno for better tooling and better APIs) and feel way more productive.
But I'm curious about what your experience with Deno itself was, apart from Typescript.
> I'm curious about what your experience with Deno itself was
I'd describe it as mostly good, but not good enough. I prefer it over Node as a server runtime for several reasons (more secure by default is a huge deal, simpler yet better tool chain which "just works", APIs which respect web standards, plugin system is really cool) , and if I had to build a project based around Typescript in a monorepo I'd almost certainly prefer deno over Node.
One thing I really liked at first was how easily you can write executable scripts... I didn't expect to like it, but Typescript is very expressive and sane enough that writing a script with it is relatively pleasant. However, the real snag is that you need deno on a machine to run them... I love writing scripts with Go too, and it's arguably much nicer off the shelf, so that plus quickly lost its appeal as well. I prefer portability too much not to stick with bash, or otherwise publish executable binaries. Regardless, it's a cool feature if you're on a team which uses deno; using TS across the stack, even in your scripts (without the gnarly barrier of npm or some other script runner) seems like it could be legitimately capable and productive, but also ergonomic without significant compromise in most cases.
> Go is cumbersome and missing a lot of the nice APIs that Deno provides
I would have agreed a few years ago, but I know I was personally just "holding it wrong". Go is very powerful and easy to use in the right hands. However, it could only be that I do relatively simple tasks with it, or those the language designers intended. I don't doubt for a second that you had a bad experience with it for legitimate reasons; I only mean that I had a rough time with it because I didn't understand the language deeply enough.
Admittedly, I wanted to use Go like I used Typescript. They're much different languages underneath, but sometimes it feels as though you should be able to design things in similar ways. It's typically not the case, though. The type system is far different, and the fact that it supports concurrency so primitively means a lot of its constructs are designed quite differently than intuition might inform you if you're coming from the Node world. Otherwise, they lack overlap in terms of how objects work, Go's lack of prototypical inheritance, and so on. When you put it all together they're far different from each other, and their patterns less fungible, than they seemed to me at a glance.
However, I'm not a classically trained computer scientist so I've often been prone to holding languages wrong, so to speak, until error and extensive reading pounded a proper grasp and understanding into my mind.
> the nice APIs that Deno provides
I'm curious, what are these missing APIs from your perspective?
I have never used deno. But I think they did what they should have done in the first place. Like npm support. I think they have learned now that you should not make decisions with a group of hipster developers. this is a good thing.
I’m hopeful that the new package registry will become the best way to publish libraries written in TypeScript in a backwards-compatible way. In a TypeScript app, I don’t want to import anything pre-minified, since it makes debugging harder. I just want the source. But other people may need to download different files that are compatible with their systems, and a library maintainer will want to support them without fuss.
I got thoroughly frustrated with Deno on a side project for similar reasons (leaky node compat abstractions, kludgey interop between code that does IO the Deno way and Node modules, immature Deno stdlib, undermaintained Deno libraries, spooky bugs). I lost a lot of time to figuring out how to make things work the Deno way.
Then I spent a day switching my codebase back to Node. I was struck by how much worse my code got. The Deno code made use of niceties like top level await and import maps. I needed to resort to a bundler to dedup instances of Y.js between my backend and Lexical. The Deno libraries tended have cleaner APIs (e.g. Oak vs. Koa).
After I took a step back, I scrapped the Node rework and accepted my misgivings with Deno, for now. Going back to Node was kind of like playing an older game; the graphics were great at the time but now that I've seen what a 2023 backend Typescript codebase looks like, I don't want to go back.
Yep! It requires ESM, which had other knock on effects in my codebase, IIRC with Lexical. I needed to hack around this a bit for Deno's module system as well (using esm.sh to preprocess Lexical) but it was a much nicer DX.
Heh I had the same experience but with bun. Lots of subtle bugs on stream and crypto packages where I spent a lot of time trying to figure out what was wrong only to find that it's a known bug in bun. I would have understood that there'd be bugs in relatively new runtime that's undertaking this huge task but it's not drop-in replacement for node as it claims to be and I wouldn't say v1 is stable at all.
That highlights a different difference. There aren't that many bugs in Deno; there's a lot of missing functionality (especially if you have node/npm-colored glasses on), but Deno is pretty much the highest quality "full Javascript runtime" when NodeJS compatibility is not a requirement.
Bun seems to be.. essentially a rewrite of NodeJS with the same basic plan.
Then you need to rule out Bun too. At least $7M of VC money looking for that 100x return, and apparently they've stated they're building serverless hosting: https://imgur.com/Zn5uMON
Meh, I've been using Deno for a while its nice. Lume is especially nice.
The comments about the VC backed nature of the run time don't really matter to me theres always money stuff happening if a project is successful nothing wrong with being up front about it. Open Source doesn't have a great way to monetize so at the end of the day its not really about the fact that they're trying to pull some money out of their hard work... its whether or not they go full sleaze.
My biggest gripe is that they allowed NPM packages into the ecosystem. I get why, but now everything is trash again.
Only those that haven't been around to see how forks and attempts to competing implementations have turned out to be in the FOSS world, would have expected any other kind of outcome.
Even clang, which is by far one of the most sucessful on this area, is nowadays in third place in ISO C and C++ support, as most contributors only care about the LLVM infrastructure, and key contributors to clang's frontends (Apple and Google) moved away into their own languages instead.
Having been forced to adopt Deno for new Slack integrations on their new integration platform, I ultimately ended up throwing it in the trash and migrating to the legacy Python SDK.
It has a mixed model open-"legacy" framework which at this point will be a death march.
It's fine, but I don't see it gaining critical mass.
I was very big on deno when it came out but they seem to have lost their way.
I jumped on bun before they hit 1.0 and it’s been life changing. Have actually gone down the rabbit hole since then and also got turned on to zig and am building two large open source projects with bun at the center and it’s been delightful.
Business model criticisms aside which are quite similar between deno and bun, and hopefully bun continues to learn from deno’s mistakes and not maintain two different runtimes and wraps the bun runtime in more open protections. I’ve noticed that projects based on rust (tauri, deno, etc.) tend to be less approachable than projects based on imperative zig or go or c.
Bun’s ethos of building fast things that are also fast to develop mirror’s zig’s ethos. Deno’s ethos of how they handle security and other smaller decisions reflect that of rust.
> I jumped on bun before they hit 1.0 and it’s been life changing.
Oh, is Bun that good? :-)
Anyway, I'm using Node but considering trying out Deno for a new side project. I rejected Bun because I'm concerned about the stability and matureness. I don't care about runtime performance, but DX. I find Deno has such a nicer API than Node. I guess Bun is similar. I find tooling much simpler as well (Deno).
How is your experience with Bun so far regarding stability? I've heard there're issues as they've been rushing / hustling along for years?
Is the matureness of the Deno runtime and std lib really an issue? I get the concerns regarding their direction but don't worry about that. Having started looking at Deno just this weekend I found it pretty nice. From tooling to API. Looks to me like the perfect runtime for Typescript. Is anyone running production apps in Deno and can comment on the matureness and stability?
I started a small hobby project. First with TypeScript and Modern Web Dev Server (https://modern-web.dev/guides/dev-server/getting-started/). I don't want bundling and for a while life was good. But then I had some npm packages that didn't support EcmaScript modules. I found a workaround but suddenly I lost motivation. I then thought, okay, perhaps time to change to Deno.
Half a year later I changed to vanilla Rust, trunk, Webassembly and some JavaScript glue code and suddenly a lot of things got easier. Of course as a Rust programmer it is easy for me. But I am also a JavaScript programmer.
This is just anecdotal but it really seems to me that the JavaScript ecosystem is so deeply flawed to burn out people. Let me list two main points which are broken:
- Types are wonky. TypeScript does a huge work to "fix" JavaScript and I love TypeScript. But TypeScript cannot fix the soundness holes in JavaScript. There will be always things that don't work out of the box.
- The missing dependency story. Node introduced CommonJS, but it didn't work on browsers. Browser just added objects to the browser's Window object. Later there are EcmaScript modules but too late. I wrote a hacky polyglot which does both UMD and EcmaScript modules, but I feel the polyglot to be brittle. https://github.com/nalply/modglot
I am still thinking about it, but I feel JavaScript could go the way of PHP. Still important, but too much a mess to be taken seriously. Perhaps in about ten years. I don't know.
> I then thought, okay, perhaps time to change to Deno.
What was wrong with Deno?
I've done a small Rust backend (Axum) at work, so I'm ok at Rust, not really good, but ok, but I find the mental overhead not worth it. There was once a dream about "A smaller Rust" (I think withoutboats wrote a blog post some years ago). Unfortunately, we only have Go with all it's footguns or .NET Core, which I find still to convoluted (once you've lived without classes you don't want to go back). I think Typescript with deno comes closest in terms of typesafety, tooling, good enough performance and memory safety (GC and single threaded).
Yeah, Rust is very complex. But I am lucky that I had several years time to learn Rust, and let me tell you: as long you don't use async, nightly nor unsafe, Rust becomes manageable, at least for me.
This said, I see another big problem of Rust. I think a lot of the complexity stems from Rust does not having a real language for compile-time. I sometimes dream of a scripting language similar to Rust which has as output Rust HIR, wasm or llvm IR. This scripting language implicitly quotes all code and that quoted code gets compiled. But if you don't quote, that code executes and is able to do all the things the Rust compiler does today (monomorphizing, desugaring, effects, state machine generation, etc.). Perhaps Rust wouldn't be as stuck as today, because more people would be able to innovate in creating more well encapsulated compile-time structures. A standard library module would implement generics by monomorphizing, another one module would implement async desugaring, and so on. I also think, this scripting language have simple core primitives and use managed memory. Because it creates tailored intermediate code, in the end we would have a zero-cost native implementation. Of course, to be honest, one downside is that compile-times could be very bad... Just a dream of mine, probably a pipe dream, but whatever, I had fun thinking out loud here.
29 comments
[ 3.0 ms ] story [ 73.3 ms ] threadI also don't like the subtle differences that Deno has from tsc.
So for a Typescript project that I want to run in the browser I usually just use `npx typescript -p tsc` and import the build dir as modules in the app.
So while I was initially really excited about Deno, I just haven't really needed it for anything special lately.
When I want to write a quick script I usually just use Python because the std lib is so rich.
Since trying and running up against the friction and barriers, I’ve realized I don’t care much anymore about sharing code from front to back.
It feels weird to reflect on how badly I wanted it and how little I care about it now. I suppose I feel like it’s trying to optimize for the wrong thing.
Part of my reluctance to share code from front to back is that I have Go for the backend now, and using Typescript on the backend feels like a serious disservice to my productivity and happiness. Rust is also compelling in some cases. It’s rare that I actually want to be writing Typescript on the server.
On the frontend I find myself wanting the thinnest wrappers on my backends possible. Not like obtuse UIs that don’t hide anything for my unfortunate users, but I mean in the underlying logic, I have no desire to do anything clever or abstracted in the client.
I have one side project I built with Deno and it’s the one I’m the most reluctant to return to. The only motivation I get to work on it, frankly, is to migrate it off to something else.
I do wish it worked out. But that, and a lot of what this article mentions, has kind of ruined the whole idea for me.
Maybe we will get there one day, but I think we’re much further than many would think. The current options seem like massive compromises only to gain code you can share easily. Which, as mentioned, is quite easy to do with WASM now too. If you don’t need rapid throughput, WASM is great. If you do, it’s often still acceptable. The interface isn’t ideal, but for my purposes it has been better than adopting Deno or Bun.
Having said that… I think these are awesome projects nonetheless. They’re trying to accomplish something worth accomplishing. It’s an envelope we should keep pushing. Me though… I’m too boring for this stuff.
Interesting, I feel the opposite. To me, Go is cumbersome and missing a lot of the nice APIs that Deno provides. I've switched from Go to Typescript (Node, for now, but considering Deno for better tooling and better APIs) and feel way more productive.
But I'm curious about what your experience with Deno itself was, apart from Typescript.
I'd describe it as mostly good, but not good enough. I prefer it over Node as a server runtime for several reasons (more secure by default is a huge deal, simpler yet better tool chain which "just works", APIs which respect web standards, plugin system is really cool) , and if I had to build a project based around Typescript in a monorepo I'd almost certainly prefer deno over Node.
One thing I really liked at first was how easily you can write executable scripts... I didn't expect to like it, but Typescript is very expressive and sane enough that writing a script with it is relatively pleasant. However, the real snag is that you need deno on a machine to run them... I love writing scripts with Go too, and it's arguably much nicer off the shelf, so that plus quickly lost its appeal as well. I prefer portability too much not to stick with bash, or otherwise publish executable binaries. Regardless, it's a cool feature if you're on a team which uses deno; using TS across the stack, even in your scripts (without the gnarly barrier of npm or some other script runner) seems like it could be legitimately capable and productive, but also ergonomic without significant compromise in most cases.
> Go is cumbersome and missing a lot of the nice APIs that Deno provides
I would have agreed a few years ago, but I know I was personally just "holding it wrong". Go is very powerful and easy to use in the right hands. However, it could only be that I do relatively simple tasks with it, or those the language designers intended. I don't doubt for a second that you had a bad experience with it for legitimate reasons; I only mean that I had a rough time with it because I didn't understand the language deeply enough.
Admittedly, I wanted to use Go like I used Typescript. They're much different languages underneath, but sometimes it feels as though you should be able to design things in similar ways. It's typically not the case, though. The type system is far different, and the fact that it supports concurrency so primitively means a lot of its constructs are designed quite differently than intuition might inform you if you're coming from the Node world. Otherwise, they lack overlap in terms of how objects work, Go's lack of prototypical inheritance, and so on. When you put it all together they're far different from each other, and their patterns less fungible, than they seemed to me at a glance.
However, I'm not a classically trained computer scientist so I've often been prone to holding languages wrong, so to speak, until error and extensive reading pounded a proper grasp and understanding into my mind.
> the nice APIs that Deno provides
I'm curious, what are these missing APIs from your perspective?
Then I spent a day switching my codebase back to Node. I was struck by how much worse my code got. The Deno code made use of niceties like top level await and import maps. I needed to resort to a bundler to dedup instances of Y.js between my backend and Lexical. The Deno libraries tended have cleaner APIs (e.g. Oak vs. Koa).
After I took a step back, I scrapped the Node rework and accepted my misgivings with Deno, for now. Going back to Node was kind of like playing an older game; the graphics were great at the time but now that I've seen what a 2023 backend Typescript codebase looks like, I don't want to go back.
Name your files .ts and run with tsx ./file.ts
All done, good to go
Bun seems to be.. essentially a rewrite of NodeJS with the same basic plan.
Top 3 issues with Deno:
- VC backed. Bubbles burst and priorities change, profit over "consumers"!
- 2 runtimes: Deno deploy is NOT the same as open source Deno.
- Self-hosting (freedom) is in odds with the company's business model. Also joins point 2
The comments about the VC backed nature of the run time don't really matter to me theres always money stuff happening if a project is successful nothing wrong with being up front about it. Open Source doesn't have a great way to monetize so at the end of the day its not really about the fact that they're trying to pull some money out of their hard work... its whether or not they go full sleaze.
My biggest gripe is that they allowed NPM packages into the ecosystem. I get why, but now everything is trash again.
Only those that haven't been around to see how forks and attempts to competing implementations have turned out to be in the FOSS world, would have expected any other kind of outcome.
Even clang, which is by far one of the most sucessful on this area, is nowadays in third place in ISO C and C++ support, as most contributors only care about the LLVM infrastructure, and key contributors to clang's frontends (Apple and Google) moved away into their own languages instead.
It has a mixed model open-"legacy" framework which at this point will be a death march.
It's fine, but I don't see it gaining critical mass.
I jumped on bun before they hit 1.0 and it’s been life changing. Have actually gone down the rabbit hole since then and also got turned on to zig and am building two large open source projects with bun at the center and it’s been delightful.
Business model criticisms aside which are quite similar between deno and bun, and hopefully bun continues to learn from deno’s mistakes and not maintain two different runtimes and wraps the bun runtime in more open protections. I’ve noticed that projects based on rust (tauri, deno, etc.) tend to be less approachable than projects based on imperative zig or go or c.
Bun’s ethos of building fast things that are also fast to develop mirror’s zig’s ethos. Deno’s ethos of how they handle security and other smaller decisions reflect that of rust.
Oh, is Bun that good? :-)
Anyway, I'm using Node but considering trying out Deno for a new side project. I rejected Bun because I'm concerned about the stability and matureness. I don't care about runtime performance, but DX. I find Deno has such a nicer API than Node. I guess Bun is similar. I find tooling much simpler as well (Deno).
How is your experience with Bun so far regarding stability? I've heard there're issues as they've been rushing / hustling along for years?
Half a year later I changed to vanilla Rust, trunk, Webassembly and some JavaScript glue code and suddenly a lot of things got easier. Of course as a Rust programmer it is easy for me. But I am also a JavaScript programmer.
This is just anecdotal but it really seems to me that the JavaScript ecosystem is so deeply flawed to burn out people. Let me list two main points which are broken:
- Types are wonky. TypeScript does a huge work to "fix" JavaScript and I love TypeScript. But TypeScript cannot fix the soundness holes in JavaScript. There will be always things that don't work out of the box.
- The missing dependency story. Node introduced CommonJS, but it didn't work on browsers. Browser just added objects to the browser's Window object. Later there are EcmaScript modules but too late. I wrote a hacky polyglot which does both UMD and EcmaScript modules, but I feel the polyglot to be brittle. https://github.com/nalply/modglot
I am still thinking about it, but I feel JavaScript could go the way of PHP. Still important, but too much a mess to be taken seriously. Perhaps in about ten years. I don't know.
What was wrong with Deno?
I've done a small Rust backend (Axum) at work, so I'm ok at Rust, not really good, but ok, but I find the mental overhead not worth it. There was once a dream about "A smaller Rust" (I think withoutboats wrote a blog post some years ago). Unfortunately, we only have Go with all it's footguns or .NET Core, which I find still to convoluted (once you've lived without classes you don't want to go back). I think Typescript with deno comes closest in terms of typesafety, tooling, good enough performance and memory safety (GC and single threaded).
And the follow-up:
https://without.boats/blog/revisiting-a-smaller-rust/
This said, I see another big problem of Rust. I think a lot of the complexity stems from Rust does not having a real language for compile-time. I sometimes dream of a scripting language similar to Rust which has as output Rust HIR, wasm or llvm IR. This scripting language implicitly quotes all code and that quoted code gets compiled. But if you don't quote, that code executes and is able to do all the things the Rust compiler does today (monomorphizing, desugaring, effects, state machine generation, etc.). Perhaps Rust wouldn't be as stuck as today, because more people would be able to innovate in creating more well encapsulated compile-time structures. A standard library module would implement generics by monomorphizing, another one module would implement async desugaring, and so on. I also think, this scripting language have simple core primitives and use managed memory. Because it creates tailored intermediate code, in the end we would have a zero-cost native implementation. Of course, to be honest, one downside is that compile-times could be very bad... Just a dream of mine, probably a pipe dream, but whatever, I had fun thinking out loud here.