Deno shows us there's a better way

Recently, I completed a full rewrite of my first personal project, which was a Django project running on Heroku. Now it's a Deno project running on Deno Deploy. The experience has really gotten me thinking about the amount of pain we put up with to deploy simple stuff these days, especially with containers. Deno shows us that it doesn't have to be that way.

I know what you're thinking, "we already had a great setup: rsyncing PHP files". Unfortunately, rsyncing PHP files still lacks many critical features needed for more complicated projects. Collaboration, continuous integration, dependency management, multi-region support, scalability, preview environments, runtime security are just some of the features you'd have to build yourself. That's part of why containerization (and its ecosystem) has taken off, since it provides a well-trodden path for each of those features ("just a few more CNCF projects and we can really get this thing poppin'")

But people have been saying for a long time that containers are painful to work with. Historically I've been a bit dismissive of those people because, like all technology, containers make tradeoffs. You have to tolerate the slow build times, bloated images, and YAML hell in order to get the language agnosticism, dependency bundling, immutability, and access to Kubernetes and other projects that give your app superpowers as long as it's containerized. At least, that's what I used to believe.

Deno seems to take a holistic approach, where the development environment, deployment method, and infrastructure are all self-contained within the deno CLI. Maybe some of this comes from Golang's decision to bundle the package manager and the formatter/linter directly into the language, which Deno does as well.

I think this is a smart move. The more mature platforms I see have more tight integration with the application code itself. Normally this is a set of config files (terraform or k8s manifests), but there have been efforts to replace config files will full languages such as Pulumi. Even before that, there were libraries like Netflix's Hysterix which bakes a service mesh into application code (before service meshes even existed). I think these threads of history tie into Deno's all-in-one approach to produce a nice developer experience with just a git repo and a CLI tool.

The rewrite

The Django project that I rewrote in Deno is your typical 3-tier web app. I'd classify it as a non-trivial application that took me maybe 30 eight-hour working days to build originally (which translates to years of weekend coding). I was able to rewrite it in a week of evenings, about 4 work days. And this was my first experience writing TypeScript/Deno, so I had to look up a bunch of simple questions like how to do loops or create maps!

  • The development environment setup is easy, just a curl | bash to get the Deno CLI. The language server can be installed easily into Zed or VS Code. No messing around with JDKs or system Python versions.
  • Packages are cached globally but tracked/locked in your git repo, so no giant node_modules folder and no Python virtual environments.
  • Everything you need to develop—including data storage via Deno KV—is included in the CLI, so no need for Docker.
  • Package install times are so fast that I wasn't bothered by the fact that Deno's caching/vendoring was broken when I tried to set up Gitlab CI.
  • Compile/run/test times seem nearly instantaneous (it is a small project with only a single dependency), so I was able to run tests as a pre-commit hook without even noticing a delay.
  • Deployment to multiple regions with a highly available database only takes a single command which runs several times quicker than the Heroku deployments I was doing before.
  • Deno has capabilities-based security, where it doesn't get the ability to spawn arbitrary processes, read environment variables, or read/write any file in your home folder.

The beautiful thing is that (no offense) I don't think any of these are revolutionary ideas on their own. I don't even think it'd be too hard to add these features to existing languages. Just seems like nobody else seems keen on bringing all of these elements together.

There were some rough edges, and I know Deno has a lot of beta-quality things, but they've been in beta for a while. The metrics and logs are lacking, I had some issues with the API docs, some error messages were unclear, you can't simply download a backup of your KV data, and I hit the classic React issue where some content refers to old Deno features/techniques that don't exist anymore. One big missing feature was that I can't imagine running a Deno Deploy site without a caching/rate-limiting CDN in front to control costs, which is such an easy feature Deno could include. Without that, kinda defeats the whole purpose of running at the edge.

Conclusion

Computers are so ridiculously powerful these days that it's so weird we still have CI/CD pipelines that take tens of minutes. SQLite runs 300k test cases in "only a few minutes" on every commit. We also have huge servers with huge disks where you could store the top 99% of every library, package, binary, etc., completely eliminating the need for CI/CD caching. We have advanced compilers that can do incremental compilation. We have an enormous pool of seasoned software engineers who could bring all these things together, and an enormous tech industry itching to pay for it. Maybe if everyone wasn't busy building ad tech and chat bots, we'd get somewhere.