Deno is a big deal
I don’t think anyone expected Deno’s announcement at JS Conf EU 2018. I certainly wasn’t. I was pretty content with Node as it were, as I think most people were, and probably still are. It had been out in the world for over 9 years (11 now), the community was huge, the ecosystem was mature, there was some great tooling developed around it and it really managed to cut itself a place as a solid choice for wide variety of use cases. So how could I have possibly foreseen Deno?
And that is precisely why I think Deno is so important.
This is a quote from Rich Harris from his Rethinking Reactivity talk (of which there is a couple of versions available on YouTube). His comments were targetted specifically towards the world of frontend framework, but I think its also painfully true of the backend world.
I think as a community we have simply come to accept a lot Node’s quirks as normal, something
you just have to accept and move on with you’re life. I personally came to Node long after it
was released, and I remember seeing
require for the first time and sort of scratching my head
because I had never seen that in browserland. And I was told “this is how you import stuff in Node,
just roll with it”. And I did. And I never really thought to put that into question after that.
But let’s face it:
require is weird when you think about it. Before Deno came along I had never
really thought about the fact that
require is really opaque in what it actually imports.
require('./my-lib') import a file called
my-lib.js or is it importing
this may seem like an unimportant issue, and maybe it’s not worth spending time to fix it, but the fact
is, Deno makes it easy to be very explicit about what to import in a way that reminds a lot of other
language’s module system. I like that. As a library consumer I must admit it doesn’t have that much
of an impact; I usually just look at the “getting started” section of a library and copy/paste their
require statement. but as a library author it makes life so much easier by giving clear guidelines
on how to structure code so that it can be imported in the way that is expected by both author and consumer.
More generally, if you’re just using the language to build a bigger system or api, it takes out the
question of what convention are you going to use for the project. I’ve personally worked in codebase
that were not so small, had been around for a long time and had switched hands many times. The reality
of those codebase is that you’ll find a mix of conventions between
Deno takes the guessing game out of the equation.
Another element that has caused quite a bit of debate is the security aspect of Deno. Most of the concerns
I hear are either about how easy it is to “bypass” it by using the
-A flag to give permissions, or
about how the security problem can be solved better at the virtualization layer, for instance with Docker or
Kubernetes or whatever workload orchestrator you’re using. To address the first concern, I think it’s a fair
point. It sure isn’t perfect, but at least it acknowledges the issue. And while it can be used in a very
open way using the
-A flag, it’s also flexible enough to be very granular, for example only allowing access
to certain filesystem paths or network domains. In other words, it gives more options to software developers
to secure their application, but has no opinions on what the best choice is, and I like that.
I think the second is really missing the mark though. Why rely on extra levels of virtualizations to restrict
access to system resources when v8 is already essentially a virtual machine, and already acts as a
blackbox with no access to the system it runs in? We don’t have to build extra infrastrucre and extra layers
to secure the runtime. So why do we?
In fact, some companies have even started putting this exact concept in practice. For example, CloudFlare is using V8 isolates to run untrusted code in their workers instead of relying on traditional container technology.
That being said, the real debate that Deno has stirred is around dependency management. Kitson Kelly already made a great post on the subject that I suggest you go and read, so I won’t expand too long on the subject. When it comes down to it, there’s really two things that I really like about the way Deno deals with dependencies. The first one is that it doesn’t have an opinion on where code comes from. Don’t get me wrong, I think the npm CLI is actually quite nice and easy to use. My issue with it is that it defaults to the central npm registry, and using private packages from different registries requires me to do some extra setup to get it to work. It’s extra documentation I need to write so that all of my teammates use the same setup and the same registries and it’s extra toil that I frankly would just rather not have to deal with. Deno doesn’t care. As long as the domain where the dependency is hosted is accessible, it will fetch the code. There’s no setup required, no documentation to write. It just works. The second thing I like is that it strongly couples my code with the code that it depends on. Again, I never really stopped to think about it before Deno, but being able to declare dependencies completely separately from the code that will use it makes little sense. It allows for dependencies to be declared, but never used. Sure we can use solutions like tree shaking to solve the problem, or build linters that will alert on unused dependencies, but the way Deno (and Go) deal with it is just much simpler: Just declare your dependencies as you them, when you need them. that’s it. No muss, no fuss. It solves the issue at the source, rather than rely on extra machinery to fix it after the fact.
Now, that’s not say that this way of doing things is perfect. For instance, a very good criticism of Deno’s
dependency management strategy is that it makes it hard to update dependency versions across a large code base.
Indeed even Go realized this and created Go modules and the
go.mod file. Still, the basic pattern is still the
same. I declare my dependencies as I go, and use
go mod tidy to retroactively update my
go.mod file. But we
had to wait for version 1.14 to get there. It was the result of a long and meticulous investigation on the issue.
It’s a process that Deno will have to go through as well, and I would much rather wait for a solid, lean solution
than rush some half-baked solution. That’s how we got
package.json in the
Maybe Deno is on to something and we should all be converting our code to run on Deno. Maybe it’s missing the mark completely and Node has actually been doing it perfectly right all along. And maybe some of Deno’s innovations are really interesting and should be ported over to Node. I don’t know. But at least we’re talking about it. We’re re-evaluating things we thought we knew, and are experimenting with different solutions to problems in the ecosystem.