Making it easy to do is absolutely key to building software that is a delight to use.
When the core is pure, testing it simply becomes a matter of checking that it responds in the way you expect it to when you send it a specific message. All tests can be unit tests, no complex mocking, stubbing, harness, hooks or, god forbid, frameworks needed. And unit tests are fast, so you can have thousands of them if you need to. With CRUX, you can run a full suite of tests in milli-seconds. This changes the dynamic completely. High confidence from immediate feedback improves productivity by insane amounts.
Why Rust?
Rust is rapidly becoming a default choice for building all kinds of systems and applications.
It’s solid and reliable, safe and secure, fast and efficient.
It also has a very modern and advanced type system that makes it suitable for modeling complex business domains (and, in CRUX, these types flow across the boundary into the native shell’s type system, meaning that breaking changes in the core will stop the shell from compiling — this is a great thing).
You don’t need to write as many tests as you would in, say, TypeScript, because you only need to test logic (not the sloppiness of the language itself). Most people using Rust would agree that the statement “if it compiles, it works” is true, more often than not.
We built CRUX in Rust because it’s not only good at all these things, but it also has a great portability story. In order for CRUX to work, we need to support Foreign Function Interface (FFI) calls to pass messages between CRUX and many different native shells. We also need to be able to compile CRUX to a dynamic library (for Android), to a static library (for iOS) and to WebAssembly (for Web). These cross-platform binaries are trivial to produce using the Rust toolchain, and integration with platform-native tooling is straightforward.