the nice, the painful — OXIDT


After a yr of Dioxus fullstack in manufacturing, the Rust code is probably the most boring a part of my corporate affairs. CI is probably the most thrilling half, and never in a great way: a launch construct takes 90 minutes on a GitHub-hosted runner.

That yr began at my earlier job and continued by myself merchandise. Back in April I wrote about why I picked Rust for a production micro SaaS. The brief model: slower initially, cheaper to stay with later. This publish is the invoice.

What I’m operating

SeggWat is an in-app suggestions software for SaaS merchandise. Its dashboard is a Dioxus 0.7 fullstack app inside a Cargo workspace: a backend crate with server features, an auth crate, and about ten UI crates protecting insights, suggestions, concepts, boards, widgets and settings. Twelve crates rely on dioxus. Everything sits on PostgreSQL.

infra.page is constructed on the identical stack.

“Fullstack” means one Rust codebase doing three jobs. It renders on the server, runs server features because the backend, and compiles the shopper to WebAssembly so the web page turns into interactive.

The good

One language, all the way in which down

Database question, corporate affairs rule, server operate, button: all Rust. One toolchain, one formatter, one set of habits, one place to look when one thing breaks.

That sounds minor till you’re a solo founder juggling a number of merchandise. I don’t have a “frontend mind” and a “backend mind” that every want warming up. I open the workspace and hold going.

The similar varieties on each side

This is the one I’d miss most.

A struct will get outlined as soon as. The database layer returns it, the server operate sends it, and the element renders it. When I rename a subject, cargo test offers me a listing of each place that broke, together with the UI. There’s no OpenAPI spec to regenerate and no TypeScript interface drifting out of sync with the backend.

The win isn’t writing code sooner. It’s refactoring with out worry, late at evening, with no one to assessment it.

The door to different platforms stays open

Dioxus can goal desktop and cellular, together with iOS, from the identical element mannequin. None of my merchandise wants a local app as we speak. But figuring out I may ship one and not using a rewrite mattered once I determined the place to place a yr of labor.

Production is boring

That’s the entire level. Once it compiles, it principally simply runs. An entire class of “undefined isn’t a operate” bugs by no means reaches customers, so the bugs that do get by are logic bugs. That’s the place I’d slightly spend my consideration anyway.

The “boring in manufacturing” argument from April survived contact with actual customers.

The painful

Compile instances

This is the half Rust evangelists are inclined to skip.

The dashboard compiles twice: as soon as for the server and as soon as to wasm32 for the browser. A dozen Dioxus crates plus a macro-heavy UI add up. Locally it’s tolerable on a good machine. CI is a unique story, and the feedback in my workflow information learn like a diary of it:

  • The WASM construct alone takes about 22 minutes on 6 cores. That’s a single leaf crate: the dashboard compiled for the browser.
  • A launch picture takes about 90 minutes on a GitHub-hosted 2-core runner. On my very own VPS runner it’s round 40, and that’s the nice model.
  • Clippy bought OOM-killed. cargo clippy --workspace --all-targets on the 7 GB hosted runner died with exit code 143 and no diagnostics in any respect. The repair was a ten GB swapfile plus CARGO_BUILD_JOBS=1 to chop peak reminiscence. My linter wants swap.

At some level I finished constructing photographs on each push to fundamental. They now construct on launch tags or once I set off them by hand, as a result of 90 billed minutes per push is a foolish technique to spend cash.

What I attempted

  • Caching with Swatinem/rust-cache. It helps, however there’s a entice. A workflow run can solely restore caches from its personal ref or the default department, and GitHub evicts caches unused for 7 days. Once I finished constructing on fundamental, launch builds saved beginning chilly. My workaround is nearly humorous: after a dependency bump, I manually set off a construct on fundamental simply to maintain the cache heat.
  • Building and testing in a single go, so the exams reuse compiled artifacts as a substitute of compiling the international stage a second time.
  • The mould linker. Nice, however linking was by no means the bottleneck. Compiling was.
  • A self-hosted runner for the heavy builds. This was by far the most important win, 90 minutes all the way down to 40. It additionally means I now keep a construct server.

Every certainly one of these helped. None of them modified the underlying math: two compile targets, an enormous dependency tree and many macros means quite a lot of compiling.

“All Rust” comes with an asterisk

The corporate affairs logic is all Rust. The edges aren’t.

SeggWat’s insights web page exhibits charts, and I couldn’t discover a Rust charting library I’d belief in manufacturing. So the charts are Chart.js, loaded from a CDN and wrapped in about 340 strains of JavaScript that Dioxus calls by dioxus-use-js. Styling is Tailwind, so CI additionally installs Bun and runs the Tailwind CLI earlier than Rust even begins.

I’m nice with that. It’s pragmatic. But in the event you’re selecting Dioxus particularly to flee JavaScript, know what you’re signing up for: Rust, plus some JS glue, plus a JS toolchain in your pipeline.

Would I do it once more?

People ask, “What helps me ship quickest?” The higher query is, “What helps me hold delivery with out hating my life?”

For the backend, the reply continues to be Rust, and it isn’t shut.

For the frontend, I’ve modified my thoughts a bit. If I have been constructing one thing actually app-like, with heavy shopper state, offline help or an actual cellular app, I’d choose Dioxus once more. But most of what I construct is dashboards: varieties, tables, a couple of charts and a few toggles. For that, I’m paying a 22-minute WASM tax on each launch for interactivity I barely use.

What’s subsequent: Topcoat

So my subsequent venture goes to make use of Topcoat, a brand new fullstack framework from the tokio-rs org, announced in July.

It makes the other guess from Dioxus. Everything renders on the server, and elements are async features that may question the database straight. For interactivity, you write $(...) expressions in unusual, type-checked Rust. Topcoat evaluates them on the server for the primary render and in addition interprets them to JavaScript, so that they re-run immediately within the browser. There’s no WASM bundle and no second compile goal.

#[component]
async fn faq(cx: &Cx) -> Result<impl View> 

Example from the Topcoat README.

On paper, Topcoat retains what I really like about Dioxus (one language, shared varieties, no API layer) and removes the half that hurts most, the 22-minute WASM construct.

To be clear, it is a guess, not a consequence. Topcoat’s README calls it early-stage and experimental, with breaking modifications anticipated. I’m giving up the multi-platform choice. And I haven’t measured a single construct but.

I’ll write the follow-up with actual numbers: compile instances, CI minutes, and no matter I find yourself lacking from Dioxus. If my launch construct drops from 40 minutes to 10, you’ll hear about it. If it doesn’t, you’ll hear about that too.


I’m Hauke. I construct SeggWat, Stepshots and infra.page in Rust and write about it on OXIDT.



Source link