How immutable scripts in Deno allow Windmill.dev (YC S22) to build production-grade ops
This is a guest blog written by Ruben Fiszel, Founder & CEO at Windmill.dev.
Windmill.dev is an open-source developer platform where companies can build internal workflows and UIs from scripts. Over 300 companies, including enterprise clients such as PhotoRoom, use Windmill as a core part of their production infrastructure for various operations, such as ETL pipelines, stitching together internal and external APIs, and more.
Since companies rely on Windmill for mission critical workflows composed from scripts, our technology must be performant, secure, and flexible:
- Minimal cold start time for scripts
- Securely running untrusted, arbitrary code
- A simple way to share and compose scripts
Windmill can run Python, Typescript, Bash and Go code. For TypeScript, we chose Deno for its performance, security, and ability to create self-contained scripts.
Scripts as a first class primitive
Scripts are the smallest “unit” for building workflows and UIs, due to their flexibility and control. Windmill workflows are composed of various scripts.
Example of a simple single-branch flow to publish on Slack whenever a HackerNews message contains a mention, with sentiment analyzed.
The example above shows that the workflow is not only made of off-the-shelf scripts, but also uses scripts in different languages with all kinds of inputs and resources.
Since scripts are used in all sorts of workflows, their shareability and consistency are important. Also, Windmill is community-driven: scripts are shared on the Hub and all approved scripts are integrated into the product.
With those requirements, the scripts must be reliable, secure and performant. Only Deno offers all of this in one.
Deno makes scripts immutable
Windmill expects each script to expose a main function and declare their dependencies in the same file. The main function’s arguments are parsed to infer the corresponding JSON schema of the payload to trigger such scripts. This model is perfect with Deno.
Deno’s dependency requirements can be stated in the same files where they are used. Furthermore, the import statements can specify the precise version being used, even for npm imports:
import mysql from "npm:mysql2@^2.3.3/promise";
import * as wmill from "https://deno.land/x/[email protected]/mod.ts";
When dependency versions are specified, we can guarantee the reproducibility of the execution of the script regardless of environment. On top of that, sharing Deno scripts is as easy as sharing a single file — there’s no need for a separate dependency manifest, e.g. package.json.
Once a script is created or updated, a new version of it is associated with an immutable and perpetual hash, then stored in Postgres with relevant metadata.
With Deno, scripts on Windmill are immutable and a first class primitive — a shareable, composable building block for the entire community.
Running arbitrary code securely with Deno
Windmill runs user generated code in a multi-tenant environment, so it’s imperative that the execution of each script is isolated (for self-hosted installations, for convenience those securities can be disabled). Deno’s opt-in permission model gives us the granularity to control access for each execution.
For Windmill’s scripts in other languages, the sandboxing goes through NSJail, which has a big performance overhead and is complex to use and configure. Deno, due to its secure by default nature, has an edge in performance for multi-tenant environments.
Minimal cold starts with Deno
We’ve been able to achieve 15ms cold start times with Deno, thanks to its integration with V8 and immutable caching of dependencies
The dependencies that are cached are also propagated across workers of a cluster with a sync system backed by S3, so any Deno script that references a cached dependency won’t need to download it and reap performance benefits.
The cold start of executing a Deno script is around 15ms, which means that most lightweight scripts run 30ms end-to-end.
What’s next?
We’ve been happy using Deno in production at Windmill — it’s simplified development, added a layer of security, and ensured optimal performance for our enterprise clients.
In the future, we aim to match serverless / AWS lambda performance by integrating more deeply with Deno and running it in process.
Don’t miss an update — follow us on Twitter.