WebAssembly (Wasm) began its journey in the web browser. However, it has since expanded, becoming a sought-after technology for server-side environments, Internet of Things (IoT) systems, and synchronous plugins. With this kind of horizontal expansion, even reaching into multi-tenanted cloud environments, it is clear that Wasm has some desirable attributes. One additional innovation, on the Wasm side, elevates the technology beyond just being desirable. Wasm has the potential to change the game for software developers, revolutionizing the landscape of software development.
What is this new thing that makes Wasm so exciting and special? It’s a technology that hides behind a deceptively boring name: The WebAssembly Component Model.
Before diving into the Component Model, though, let’s trace Wasm’s journey from the web browser to the cloud.
Why Wasm Moved Beyond the Web Browser
Wasm was developed for a very specific purpose: A consortium of developers from Mozilla, Apple, Microsoft, and Google wanted a vendor-neutral standardized way to run languages other than JavaScript inside of the web browser. As an industry, we have accrued software written in languages such as C, Java, and Python over many decades. In more recent times, newer languages like Rust continue to add to the vast array of software tools and libraries. However, web browsers, being restricted to a JavaScript runtime, are not able to execute code written in these high-level languages.
Wouldn’t it be great, reasoned the Wasm creators, if we could create a standard binary format to which any of these languages could compile? From this point onwards, we saw toolchain development that enabled high-level languages like C, Rust, and others to be compiled into WebAssembly binaries. These binaries could be loaded into web browsers and interacted with using JavaScript. This brought about a rich interplay with existing web technologies; suddenly, web developers could write JavaScript code that interfaced with these WebAssembly binaries, harnessing functionality and achieving near-native performance right there in the browser.
The web browser environment (for which Wasm was initially designed) does carry forward some constraints that any deliberately interoperable system (Wasm) must abide by:
Security: The web browser routinely runs code from unknown and untrusted sources. As we click around on the internet, we rely on the web browser to protect us from bad actors and buggy code. In the same vein, Wasm must be highly secure.
Portability: Web browsers run natively on all major operating systems and various system architectures. Wasm must not require that an application be compiled to a specific OS or architecture but must support running on many (ideally all) platforms without compromising performance. Wasm must ensure users have a smooth and efficient experience regardless of where the application runs.
Performance: As we browse the web, we grow impatient, waiting for things to load. Just a few extra moments can give us the feeling that our expected digital dopamine isn’t arriving on schedule. At this point, we often close that tab or move on, clicking, swiping, and liking something else. Wasm must always load and execute immediately to ensure that a user’s interest is retained.
In addition to the three constraints above, a fourth—highly audacious constraint— remains. A way in which all of the disparate language communities (each with its timelines, processes, and prioritizations) can adopt Wasm into their build and runtime toolchains. Optimally, as soon as it is practicably possible.
By rights, Wasm should have failed simply because that fourth item mentioned above could easily be considered unrealistic (in the real world). Yet, against the odds, language communities began supporting Wasm. First, C and C++ gained support (from the Wasm creators themselves), as did the burgeoning Rust programming language. That very well may have been the stopping point. But it was not. Language after language has begun adding support. Python, Ruby, Swift, the .NET languages, Go, Zig… the list started growing and continues to grow. Even wholly new projects (like the functional programming language Grain, which compiles exclusively to Wasm) are building their community and undergoing ongoing development in the Wasm space.
With this level of language support, Wasm consistently increases its foothold as a promising tool. Wasm’s security, portability, and performance virtues are making savvy developers outside of the web browser world begin to notice. This foothold gets stronger whilst stories of companies like BBC and Disney (using Wasm in their embedded streamed video apps) appear, other parts of the web like Samsung documentation pages go on to explain “WebAssembly and its application in Samsung Smart TVs”. Cloud innovators such as Fastly, Fermyon, and Microsoft continue to enhance Wasm tooling and frameworks, integrating Wasm seamlessly into cloud and edge computing. Companies like Shopify, Suborbital (now part of F5), and Dylibso making leveraging Wasm as a plugin framework a reality. All roads lead to refining the Wasm application developer experience and simplifying Wasm’s implementation in mainstream products and services. If we boil it down, in every case, the magic formula is the same: Wasm offers a secure, portable environment that performs well across devices. In addition, there is broad support from various language ecosystems.
Detractors might point out that this is mainly overwrought praise for boring features. Sure, it’s fine. One could argue that other solutions just might also be “fine”, right? More to the point, if the ambitions behind Wasm stopped here, then I would have to agree: Wasm is simply a “good enough” technology. But something has been brewing in the standards groups working on Wasm. And this “something” boosts Wasm from fine to redefining.
The Component Model is the Future
Here’s an intriguing question: If languages such as Rust can compile to Wasm and Python can operate within Wasm, could there be an architecture to build interoperable Wasm libraries, applications, and environments? If the answer is affirmative, we might be on the verge of realizing a goal that has largely eluded the programming world:
creating libraries (programs with reusable code) that are universally usable, regardless of their source language.
The audacious goal of the Wasm project was that, in theory, any language should be able to run in a Wasm runtime. And the surprising fact is that many languages (over two dozen) already can.
To put this in perspective, let’s consider some standard tasks typically encapsulated in libraries: parsing an XML or JSON file, formatting a date, or implementing a complex encryption scheme. For each language, its community writes this code in their preferred language. JavaScript has an XML parser; so does Rust, and every major language features an XML parser crafted for it. Each of these libraries needs updates, patches for security issues, and more. Consider the countless hours dedicated to maintaining myriad libraries, all of which ultimately perform the same essential functions. What really drives this point home for me is that RFC 7159 officially describes JSON as a language-independent data interchange format – you read correctly; “language-independent”.
The Component Model addresses the challenge of enabling code compiled to Wasm to intercommunicate. It facilitates communication between one Wasm component and another. This means a Python program can now import a library written in JavaScript, which can import a library written in Rust. Remarkably, the original programming language has no bearing on the usability of the code. Let’s put this into meaningful context. How does this change how we talk to computers? Standards like Unicode already allow us to represent our written human languages in 8-bit sequences. For instance, a UTF-8 encoded JSON string can be deserialized into bytes, raw data that computers can store, transmit, process, and stream. These bytes can subsequently be serialized back into a UTF-8 encoded JSON string. The good news is that many highlevel programming languages already support such standards (and have done so for quite some time). At this point, you might wonder how on earth we will be able to deal with different implementations of high-level language variables. Take strings, again, as an example. A string in C might be represented entirely differently from a string in Rust or a string in JavaScript [1]. In fact, there are two types of strings in Rust. Rust’s “String” variable, denoted by an upper case “S”, is stored as a vector of bytes and Rust’s other string type, denoted by lower-case letters “str”, which is prefixed by an ampersand, is stored as a slice [2]. Does this exacerbate the situation? No. Because The Wasm Component Model has an agreed-upon way of defining those richer types and an agreed-upon way of expressing them at module boundaries. These type definitions are written in a language called Wasm Interface Type (WIT), and the way they translate into bits and bytes is called the Canonical Application Binary Interface (ABI). It now becomes clear that components are portable across architectures, operating systems and languages. What do we stand to gain from this? Well, for one, we can stop reimplementing the same libraries in every language under the sun.
In addition, a library can be written in the language best suited for it and then shared to all other languages. For example, a high-performance cryptography library might best be written in Rust, where one could argue that built-in handling of null pointer dereferencing, dangling pointers, and buffer overflows might make Rust the safest tool for that particular task. Another example is that a library related to data processing might better be written in Python, perhaps due to its network effect in this programming genre and Python’s already extensive data processing library ecosystem. Don’t hang your hat on just these conversations. This is but the tip of the iceberg.
The component model enables developers to update specific sections of an application rather than overhauling the entire system. If a security vulnerability emerges in the cryptography component, for instance, only that individual component needs to be upgraded, leaving the rest of the application untouched. Moreover, such updates might be executed in real-time, eliminating the need for a full system shutdown, rebuild, and deployment. This approach could foster more agile application iterations. Furthermore, data storage backends can be interchanged seamlessly without modifying the broader application’s code. Implementing specialized tests, collecting metrics, and debugging could be as straightforward as integrating the appropriate intermediary components into the application without altering the existing code.
This approach will seem revolutionary for developers accustomed to regular code rebuilds and redeployments. What’s thrilling is that this technology is already here. Tools like Fermyon Spin and the Bytecode Alliance’s Wasmtime fully support this component model. And with the Component Model being standardized by the respected W3C (the standards body responsible for HTML and CSS), it’s open for anyone to implement. It’s anticipated that the Component Model will be widely adopted in the Wasm ecosystem within a year.
Join us at KubeCon + CloudNativeCon North America this November 6 – 9 in Chicago for more on Kubernetes and the cloud native ecosystem.