REVIEW.md — WebAssembly Component Tutorial

Reviewed by Esme, 2026-04-21

Overall

This is strong work. The progression from Part 1 through Part 3 is well-structured, the before/after comparisons with the original series are compelling, and the writing voice is clear and conversational without being sloppy. The "plumbing goes away" thesis is well-argued. Below are the issues I found.


Part 1

[clarity] The sentence "Think of it as a language-agnostic interface definition — like a .proto file, but for WASM." is great. More of this kind of analogy, please.

[suggestion] The section "The Interface" jumps straight into creating directories and writing a WIT file without explaining what a "world" is in WIT terminology. You say "our plugin exports a function called length" but the concept of a world is new to the reader at this point. A one-sentence explanation — "A WIT world defines the contract: what the component exports and imports" — would help.

[clarity] The bindgen! path in the host is path: "../plugin/wit" but the README text says "Copy or symlink it" into a local wit/ directory. These are contradictory — the code uses a relative path to the plugin's wit directory, not a local copy. If the intent is to use the relative path (which the code does), the copy/symlink instructions are unnecessary and confusing. Pick one approach.

[style] "we just... implement a trait" — the ellipsis is a bit informal for the tone. Minor.


Part 2

[error] The expected output for multiply is wrong. The README says:

# count: 15, word: hellohellohello

But the code computes new_count = pair.count.wrapping_mul(repeated.len() as u8) = 3 * 15 = 45. The output should be count: 45, word: hellohellohello. Either the expected output or the multiply logic needs fixing.

[clarity] The multiply function's purpose is unclear. It takes a (count, word) pair, repeats the word count times, then multiplies count by the length of the repeated string. This is confusing as a teaching example — the reader is trying to understand data passing, not puzzle through arithmetic. A simpler multiply (e.g., repeat the word count times and return count + 1) would keep the focus on the WIT record passing, which is the point of Part 2.

[suggestion] The "WIT Types Beyond Records" section is a nice reference but feels like it belongs in an appendix or a sidebar. It breaks the flow of the tutorial — you've just shown structured data working, and then you list a bunch of types you don't use. Consider moving it to the end as a "Reference" or integrating the types into the examples.

[error] The WIT type result<u32, string> maps to Result<u32, String> in Rust only for the guest (plugin) side. On the host side via wasmtime bindgen, results are represented differently — typically as a struct with ok/err fields or as Result<T, E> depending on the bindgen configuration. This should be noted if you're claiming direct Result mapping.


Part 3

[error] The "Host Imports" section shows adding import log: func(msg: string); to the WIT file and using log() in the plugin, but the actual part-3/plugin/wit/world.wit and part-3/plugin/src/lib.rs don't include this. If this is meant to be an incremental addition shown in the tutorial, that's fine — but it should be clearly marked as "here's how you'd extend it" rather than presented as something the reader has already built. The current framing reads like the code the reader already wrote should have log in it.

[error] The host import linker.root().func_wrap("log", ...) is likely incorrect. With wasmtime's component bindgen, host imports need to be registered on the specific interface/world namespace, not on the root. The correct API would be something like linker.root().instance_wrap(...) or using the generated ExamplePluginAddToLinker trait. The func_wrap call as shown probably won't compile with wasmtime 29's component API. This needs verification against the actual wasmtime 29 API.

[clarity] The section "What About the Original's JSON-over-stdin Approach?" is excellent — it addresses the question the reader is definitely thinking. Good placement.

[clarity] The sentence "The adaptation layer — JSON parsing, stdin/stdout, whatever — lives in the host, where it belongs" is a key architectural point that could use a bit more unpacking. Why does it belong in the host? Because the host owns the boundary with the outside world. One more sentence would nail this.

[suggestion] The "Going Further" section lists multiple plugins, versioning, WASI, and other languages. These are good teasers but "WASI" deserves slightly more than a bullet point — it's directly relevant because the reader is targeting wasm32-wasip2, and they might wonder what "p2" means and why it matters.


Cross-Cutting

[error] The wasmtime version "29" should be verified against the current release. Wasmtime's API has changed significantly between versions, and the component model APIs in particular have been evolving. If the reader tries wasmtime 29 and the APIs have changed, every code example breaks. Consider pinning to a specific minor version (e.g., "29.0" or whatever is current) and noting the version explicitly.

[error] The wit-bindgen version "0.41" should similarly be verified. The wit_bindgen::generate! syntax and the Guest trait generation have changed across versions.

[suggestion] All three parts have near-identical host boilerplate (Engine, Store, read bytes, Component::new, Linker, instantiate, get plugin). By Part 3, this is repetitive. Consider extracting a helper function or at least acknowledging the repetition and explaining why you're showing it each time (e.g., "We'll show the full setup each time so each part is self-contained").

[style] The comparison tables are effective. The "That's it." single-line paragraphs after showing simplified code land well — don't overuse them but they work here.

[suggestion] There's no mention of error handling on the plugin side. What happens if the plugin panics? What if process gets a malformed book? A brief note about how panics propagate (or don't) across the component boundary would be valuable for a "real world" tutorial.


What's Working

  • The before/after framing is the strongest part of this tutorial. Every section earns its place by showing concrete code that was replaced.
  • The progression from simple → structured → real-world is clean and natural.
  • The writing voice is confident without being condescending. Honest about simplifications.
  • The WIT file is used as the single source of truth throughout — good architectural discipline.