Full-Stack Rust: Yew, Actix and the Concentration Trade-Off

Computer Science Published: July 21, 2018
TIPBACQUALEFAGS

One Repository, One Language, One Config File

A complete login application, frontend and backend, built in a single language and a single repository. That is the experiment behind a July 2018 write-up on building a web app entirely in Rust, and the author's question is a fair one: how much of the web can Rust cover on its own?

The demo does little by design. A user types a username and a password, and the login succeeds only when the two strings match. On success a JSON Web Token (JWT) is stored in the browser and also on the server, which the author admits is unnecessary but useful for something like counting logged-in users.

Everything is configured through one Config.toml. The server listens on 127.0.0.1, port 30080, with TLS off. Logging levels for the actix_web server framework and the app itself sit beside the PostgreSQL host, username and database name.

Why would a finance blog care? Because the choice on display is one every investor faces: concentrate in one well-understood tool, or spread across many. A web team that normally juggles JavaScript on the client, another language on the server and glue scripts in between has built a diversified stack. This author collapsed it into one.

We think the experiment deserves a careful reading, partly for the engineering and partly for the habits of mind it shows. A single language means one compiler checking everything, one set of dependencies to audit and one way for the code to fail. It also means one point of dependence, and anyone who has held a concentrated stock position understands that trade.

The post dates from a period when this was bleeding-edge. The frontend framework, yew, had few stable releases, and the WebAssembly target needed a nightly compiler. Early adopters were paying for novelty with instability, and we will come back to what that costs.

Yew, WebAssembly and the Three-Target Decision

Yew, a Rust framework that borrows ideas from Elm, Angular and ReactJS, runs the frontend. It compiles to WebAssembly (Wasm), a binary format browsers can execute at close to native speed. A tool called cargo-web, a direct dependency of yew, handles the cross compilation.

At the time, the Rust compiler offered three Wasm-related targets. The first, asmjs-unknown-emscripten, produces asm.js through Emscripten. Next came wasm32-unknown-emscripten, which produces WebAssembly through Emscripten. Last was wasm32-unknown-unknown, which uses Rust's own WebAssembly backend.

Of these, the author picked the last, which required a nightly compiler. That is the most aggressive option, and the post says so: it shows what native Rust Wasm can do at its best. It also skips any dependency on the JavaScript and npm world, which at the time was where most Wasm tooling lived.

Build output shows how fast the loop was. A frontend build compiled a release in 11.86 seconds, garbage-collected and processed app.wasm, and started a development server at port 8000 that rebuilds when the code changes. For a young toolchain, that is a respectable developer experience.

Think of it as choosing the newest, least-tested asset in a category because its structure is cleanest. A nightly compiler is the equivalent of an unproven fund with a clever index design: potentially the best exposure, with the risk that the rules change under you. Nightly builds can break between Tuesday and Wednesday, and a production system built on them inherits that.

A sensible reader weighs two questions separately. Is the design better? Probably, for this use case. Is the ecosystem stable enough to bet a payroll system on? In July 2018, we would say not yet.

Components, Services and Agents: Where State Lives

Yew splits the application into components. The RootComponent mounts on the body tag and decides what loads next, attempting to renew a stored JWT through the backend and routing to login if that fails. The LoginComponent holds the form, talks to the backend and saves the JWT in a cookie. The ContentComponent shows the main page, currently a header and a logout button, and notifies the backend on logout. A RouterComponent holds the routes plus a loading state and an error state.

Services are the first reuse mechanism. They carry logic such as logging or cookie handling, they are stateless between components, and each is created when its component initializes. Agents are the second mechanism, and they share data and hold overall application state, as a routing agent must.

The detail that stood out to us is that agents use the Web Workers API to run in separate threads, with a local scheduler for concurrent tasks. A browser application written in Rust can therefore be properly concurrent. Few frontend frameworks of that period offered the same.

Components also implement a rendering trait, so HTML lives inside Rust source through a macro. The author points out that the borrow checker therefore reviews the markup along with the logic. A malformed template becomes a compile error and never reaches a user's screen.

Yew shipped no standalone router, so the author adapted a reference implementation from yew's examples. That is a small cost on a demo and a larger one on a real product. Every gap in a young framework is a piece of work somebody has to write and maintain.

The state design is the interesting part for anyone who thinks about risk. Stateless services and centralized agents mean there is one place to look when something goes wrong. Investors know the equivalent: a portfolio where every position's role is written down is much easier to repair than one assembled by accident.

What a One-Language Stack Teaches About Portfolio Construction

Take the same logic to money. Suppose a $100,000 portfolio with four building blocks: QUAL (a US quality-factor equity fund), EFA (developed markets outside North America), TIP (inflation-protected Treasuries) and a satellite holding in financial stocks such as BAC and GS. The first three act like the standard library, and the last is the nightly compiler.

A plausible illustration, offered as a thought experiment and not as advice: 40% QUAL, 20% EFA, 30% TIP and 10% split between BAC and GS. Each piece has a stated job. QUAL carries domestic equity growth, EFA adds a different set of economies, TIP guards against inflation, and the two banks express a view on credit and capital markets.

Now stress it. If financial stocks fall 25% in a bad quarter, the 10% sleeve costs 2.5% of the portfolio, or $2,500. If instead the whole $100,000 had been in bank shares, the same move costs $25,000. Concentration did not change the odds of the drop, only the size of the hurt.

Rates behave differently. TIPS funds have an effective duration of roughly seven years in this period, so a one-point rise in real yields takes around 7% off TIP's price. On a 30% position that is about 2.1% of the portfolio, or $2,100. We hold those figures loosely, since duration drifts, but the arithmetic shows the scale.

Banks bring a wrinkle. BAC and GS are large buyers of software and employ big engineering teams, so they are exposed to technology choices in a way a typical utility is not. Whether any of them would adopt a language as young as Rust in 2018 is a separate question, and we would not guess. Many investors also already hold financials through broad funds, so adding BAC and GS stacks exposure on top of what QUAL and EFA contain.

The common thread with the Rust experiment is that each component has a documented role and a known failure mode. A single-language codebase gives up a safety net, which is a second language catching what the first misses. A concentrated portfolio gives up the same net.

Trying the Idea on a Small Project

For an engineer curious about this approach, the author's repository is a sensible sandbox. The `rev1` branch holds the code discussed in the article, and the author warns that the architecture is evolving quickly. Pin to that branch and expect later changes elsewhere.

A practical first step is to run the frontend build and read its output before touching any code. Eleven seconds for a release build tells you the toolchain is workable on a laptop. Then change a string in the LoginComponent and watch the automatic rebuild, which shows how short the feedback loop is.

Next, break something on purpose. Rename a field in the HTML macro block or mismatch a type in a message handler, and see how the compiler responds. The author's claim that the borrow checker validates the view is the headline benefit, and it is worth seeing in your own terminal before you believe it.

The backend deserves the same treatment. Edit Config.toml to change the port from 30080, point the PostgreSQL block at a throwaway database and confirm that the server and the stored token follow. Having one file control host, port, TLS, logging and database credentials is a real convenience, and it is also one file that must never be committed with real passwords.

Timing matters in both engineering and investing. Entering a young stack on a side project costs a weekend. Entering it on a revenue-bearing product costs months if the framework changes direction. We would apply the same staging to a portfolio: size the experimental position small, hold it for a defined period and review it against the reason you bought it.

Costs deserve a line in either budget. A nightly compiler means more time spent on toolchain breakage, and a satellite stock position means trading costs and tax events. Neither is large in a demo, and both add up when scaled.

Risks of Betting on a Young Stack

Several risks deserve plain statements. Yew had few stable releases at the time, so an API you depend on today may vanish. The nightly requirement for wasm32-unknown-unknown means a compiler update can break a build that worked yesterday. Hiring is harder too, because few engineers in 2018 had written Rust for the browser.

Against that, the upside is concrete. One language removes the translation layer between client and server, so types defined once can be shared. The compiler catches a class of errors before deployment, and Wasm threads through Web Workers open concurrency that plain JavaScript made awkward.

A portfolio decision has the same shape. If you size an experimental position at 5% of assets and it fails completely, you lose 5%. If it succeeds, you hold a piece of something that mattered early. Sizing is the only part under your control, so spend your attention there.

Scenario work helps. Suppose a team of four spends six months on a Rust web product and the framework changes its component API in month four. If rework takes 20% of the remaining schedule, that is roughly two weeks of lost output per engineer, or eight engineer-weeks. A team that planned for that in advance absorbs it, and a team that did not misses a deadline.

Investors can run an equivalent drill. Write down what you will do if the position drops 30%, before it happens. Decide whether you will add, hold or sell, and what new information would change the answer.

The demo itself has limits worth keeping in view. A login that accepts matching username and password strings is a teaching device, not a security design, and storing a token server-side is flagged by the author as unnecessary for real use. Treat the demo as a map of the terrain and not as a template for production authentication.

Practical Steps for Readers

Engineers should clone the repository, check out `rev1`, and run the frontend build. Spend an hour reading the four components and the routing agent. Then decide whether a single-language stack suits a project where you control the schedule.

Investors can take the same sequence of thinking to their accounts. List each holding and write one sentence on its job: QUAL for domestic quality equities, EFA for international developed markets, TIP for inflation protection, BAC and GS for a focused view on financials. If a holding has no sentence, ask why you own it.

Then size the satellite positions. A common rule of thumb keeps single-stock exposure to a small slice, say 5% to 10% of the portfolio in total, so that a 25% drawdown costs between 1.25% and 2.5% of overall wealth. Whatever number you choose, write it down and rebalance when drift pushes a position past it.

Check overlaps before adding anything. Broad equity funds already contain large banks, so owning BAC and GS on top of QUAL and EFA raises financial-sector weight more than the new position's size suggests. Your fund provider's holdings page shows sector weights, and a ten-minute look there is worth more than any forecast.

Finally, review on a schedule, not on headlines. Pick a date each quarter, compare actual weights to targets and act only on the gaps. The same discipline serves a codebase: review dependencies on a calendar, upgrade on purpose and keep the experimental pieces small enough to replace.

The Rust experiment shows what a tightly integrated stack can do when its builder accepts the instability of early tools. Portfolios reward the same temperament: clear roles, stated limits and a small budget for experiments. This post is educational and not individualized advice, and anyone sizing positions should weigh their own goals, taxes and time horizon.