WebAssembly, or Wasm for short, has proven itself as a revolutionary technology time and time again, thanks to its fast, near-native execution speed, variety of programming language choice, and robust deny-by-default sandboxing model. It has already seen enormous use all over the web and has been a formidable player on the server side too.
Every burgeoning ecosystem needs a vision for how all the pieces fit together, and that’s exactly where the WebAssembly Component Model comes in. The Component Model is a system that facilitates interactions between individual units of WebAssembly code and interactions between WebAssembly code and a host environment. It’s all centered around the concept of WebAssembly components, which are essentially regular Wasm modules with encoded data types. These types allow for the generation of behind-the-scenes glue code that makes components seamlessly talk to each other.
The Component Model ushers in a new age of modern software development—developers can pick and choose components from any programming language ecosystem and compose them into a single app. Have a data processing app in Python but need an efficient parser from Rust? No problem. One team at your organization is proficient in Go but another only writes JavaScript? No issue whatsoever—each team can produce WebAssembly components that compose without fuss.
WebAssembly faces a problem we’ve seen with nearly every new code execution platform—how do we safely, securely, and efficiently share data between separate code units, potentially produced by completely different programming language toolchains? Generally, two separate WebAssembly modules could do what many systems do, and pass data between them as JSON, Protobuf, or any other prolific data exchange format. Serializing and deserializing these formats is fairly expensive, and after all, given that the two modules that want to talk to each other are likely being executed by the same runtime side by side and not over a network, it is possible for the implementation of this to be better.
The struggle lies in the way each programming language represents data. Language implementations may choose to use different encodings for strings, and one language may prefer to store sequential data as an array while another may prefer linked lists. To allow code from different languages to talk to each other, they need to agree on a common form for all types of data. This is known as an Application Binary Interface, or ABI. In practice, none of them agree. Usually, if it’s desired to get some code written in one language to talk to some code written in another language, some poor developer must sit down and carefully (and painfully) write code to translate between the two languages. It requires intimate knowledge of how both languages work and is exceptionally error prone. Some language ecosystems have managed to produce tools that can do this automatically, but even then, it’s only for select languages.
Thankfully, the Component Model defines a common data form for all languages, known as the canonical ABI. To make things easy for humans, there also exists an interface definition language called WebAssembly Interface Types, or WIT, for describing component interfaces.
So, we’ve got all these components, but how can we tell where we can use them? After all, WebAssembly runs in loads of places—in web browsers, on servers, at the edge, on tiny devices, and more—all with different capabilities. WIT brings us the concept of worlds. A world is the interface that a component conforms to—it’s a set of functions a component can import and a set of functions the component exports. It allows us to reason about components and how they compose fairly easily. Here's the definition of a component that lives in the `wasi:cli/command` world, which describes components that run on a terminal command line:
Source: https://github.com/WebAssembly/wasi-cli, modified for brevity
This component cares about things like the filesystem and randomness while also having streams for input and output, but importantly, it provides a function to run when the command is invoked.
Worlds can be defined by anyone, but there are a number of standards-track worlds for components that can handle HTTP requests, access USB devices, use AI models, and many more to come. Host environments can advertise what worlds they implement to make it easy for developers to know which components work. Since components are composable, it’s also possible to implement some worlds in terms of other worlds and compose the two components. For example, if you have a host environment that implements a sockets world, but you have a component that wants to make HTTP requests in an HTTP world, you can write an adapter component that implements the HTTP world using sockets:
With a simple composition, your component can now run in the sockets world with no modification! The possibilities are endless.
The Component Model is monumental, not just for WebAssembly, but for all developers everywhere. This will fundamentally change the way developers will reason about and develop software—there’s already tons of utility, and it’s still the early days. Stay tuned into developments happening in the WebAssembly ecosystem, as we’re sure to see more and more compelling use cases arise as adoption continues to climb.