Building Developer Experience: Speed

By Dotan Nahum October 20, 2021

At Spectral, we’ve created the Developer Experience Manifesto which describes a gist of years of our experience building developer tools and infrastructure. It also serves as the set of core values, for us to work with and help move more effectively when we have a dilemma, a decision to take, or a trade off to perform.

This is a first post in a series that breaks down the Developer Experience Manifesto principles into case studies, and hopefully, will help you understand how to build amazing developer experience and developer tools.

How to Leverage Latency Testing & Long Term Trend Collection

Principle #1: Keep it responsive

Apply UX response time principles to DX

  • Apply UX principle to DX: 100ms for instant feel, 1s for keeping flow of thought, 10s for avoiding context switch
  • Response Times: The 3 Important Limits

Don’t hold up developer services

Remember that you might participate in a feedback cycle

  • Tools that you build may be used as part of other tools, try to respect the constraints of such other tools (composition is a good thing!)
  • TDD Development Cycle

If you can’t make it responsive, make it feel responsive

  • Software may have heavy update cycles, however downloading over night or creating a patch update tricks us into feeling that it’s fast

Case study: Webpack vs esbuild

Being responsive, to a large part is about being mindful of the importance of speed. We’ll discuss a very recent, and widely familiar example of why speed matters.

Javascript Bundlers

Javascript bundlers were largely used to build CommonJS files in the browser, by figuring out dependencies between node modules, packaging these with varying levels of sophistication to be served on a vanilla browser.

The sophisticated parts included minifying, shimming (injecting APIs that old browsers don’t support), and much later, tree shaking (figuring out which parts of a tree are actually used). A generic task of any bundler, is to also figure out dependencies as a tree (or graph), identifying conflicts, compatibility and redundancy.

Webpack

Branding Guidelines | webpack

Until Webpack came along, there were various milestones around bundling Javascript code for browsers. Browserify was one.

Webpack was an evolution over Browserify, in which it presented a system of concepts, that made it as the go-to bundler of the time, and it still is largely dominating.

The concepts were a flexible system of loaders and plugins: any content that you require might belong in a certain class, and its the job of the bundler to figure it out and use the correct loader for it in a modular way. Webpack also supported hot module replacement and made it widely popular.

The current evolution

Today, we fast forward to esbuild, which is a from-scratch rewrite of a Javascript compiler (more precisely, a bundler, but let’s go with compiler for now).

It feels like “just” a performance improvement over Webpack, on the expense of a few features that don’t exist to make the job of building it less complex.

The improvement, all in all, is a 100x improvement. Is it just a time saver?

Actually, it’s more than just a time saver. A 100x improvement was enough to power a new and different approach around bundling, with tools like vite and snowpack. Even Webpack itself can be boosted up with an esbuild loader.

The thinking around building Javascript now evolved into:

  • I need something to just do bundling – because it’s so much faster
  • I need a different something to typecheck, and that’s OK

And so, we’re seeing projects that are now being built (bundled) with esbuild and in parallel, type checked with tsc (for Typescript, just as an example).

Speed made esbuild loveable, so that people went through the bother of separating their workflow into two, and to “pay” for the extra complexity of that.

What worked

Go (programming language) - Wikipedia

What did it take?

For esbuild it meant going to build a part of the Javascript ecosystem, out of that ecosystem: no ready to use parsers, no plugins or compilation tools to reuse, and so on.

In addition, because Go is still garbage collected, there was a careful attention given to allocations and optimization of compilation passes.

And other than that, just a massive, massive undertaking of building such a compiler (a bundler really), for a modern programming language (actually a set of languages).

Take aways

  • While Go is not the perfect language for building compilers – it clearly can work for this specific case
  • For user experience and startup speeds: Nodejs has a clear disadvantage, yet manageable. some other languages have that too – for example, Java, because of JVM boot time (excluding Graal).
  • Some languages have an inherent code-to-runtime overhead. The more lines of code you add, the slower the execution. Nodejs and javascript has that, big time (look here for “framework overhead”). Most compiled languages don’t have that overhead.

Is it always correct to go with a statically compiled language when you need speed? Well, no. If you can box your requirements into the right fit for Nodejs, for example, then you’re good to go.

Here are some pointers around getting speed in interpreted languages:

  • Don’t use many external libraries (or can be made relatively independent)
  • Have relatively small and predictable use cases for the tool
  • You have the tooling to identify your start up hot path and feel that it will stay constant most of the time
  • You have the tooling to benchmark and profile the language overhead effectively (what you can’t measure you cant improve)
  • Some communities make it a case to help others optimize, so look for those (e.g. for Ruby you have fast-ruby)

Related articles

What is OS Hardening and How Can Developers Implement it

What is OS Hardening and How Can Developers Implement it

As cyber threats become increasingly advanced and complex, organizations are forced to adopt a military attitude of ‘war footing’ to secure their systems and servers. Although

What is a DevOps Toolchain and 7 Reasons to Implement it Now

What is a DevOps Toolchain and 7 Reasons to Implement it Now

DevOps teams are one of the most essential links in the software development chain. It seems like they have a hand in everything that takes place

A developer's guide to implementing Policy as Code

A developer’s guide to implementing Policy as Code

In 2022, the adoption of infrastructure as code (IaC) soared, with IaC domain specific languages like HCL, Shell and GoLang gaining popularity and momentum across the

Stop leaks at the source!