Showing 5 posts
Initial versions of the EndBASIC Service, and therefore initial versions of EndTRACKER, used dynamic dispatch to support abstract definitions of system services such as the database they talk to and the clock they use. This looked like a bunch of Arc objects passed around and was done to support extremely fast unit testing. When I generalized the core logic of these services into the III-IV framework, I decided to experiment with a switch to static dispatch. The rationale was that using static dispatch better aligns with the design of well-regarded crates in the Rust ecosystem, and also because I wanted to avoid unnecessary runtime costs in the foundational pieces of my web services. Let me tell you that this decision was a huge mistake and that the experiment has utterly failed. Using static dispatch has been a constant source of frustration due to the difficulty in passing types around and reasoning about trait bounds. The situation had gotten so bad that I dreaded adding new functionality to my services whenever a change to a statically-typed struct was needed, because that meant adding yet another type parameter and plumbing it through tens of source files. In lieu of the difficulties, which eventually turned into blockers to implementing new features, I made the choice of going back to dynamic dispatch. The goal was to gain ergonomics at the expense of a supposedly-negligible runtime cost. Let me tell you about the problems I faced, the refactoring journey, and some measurements I gathered after the rewrite.
One of the things I'm most proud of the Rust web services I have written is how I can run their tests with zero setup and within milliseconds, all while making me confident that "main" can always be shipped to production. I've previously touched upon how this all works in other articles, but it's time for a deep dive. To make things specific, I'll be describing the testing infrastructure of EndTRACKER, the EndBASIC Service, and the sample key/value store app of III-IV. These services are all structured in three separate layers, and I'll be covering the testing strategy for each of them.
A couple of posts ago, I described why I built custom email subscriptions for this blog. I briefly mentioned that there is new automation that scrapes the RSS feed and sends new post notifications to you all. Today, it’s time to look into how this all works and how this is based on a new persistent task queuing service in Rust. The queue handles tasks to periodically scrape the RSS feed and schedule emails, all with various quota enforcers and retry policies in place. Read on for the design requirements and constraints of the task queue, how the client and worker Rust APIs look like, and how this all can be made to work inside the Azure Functions serverless runtime for minimal deployment hassle and cost.
In MVC isn’t MVC, which hit the Hacker News front page overnight, Collin Donnell describes how the MVC design pattern that we use today isn’t really what was originally envisioned in 1979 by Tyrgve Reenskaug. This prompted me to think about how this architecture, if tweaked even further, maps pretty well to today’s designs of other kinds of programs, and I want to explore two cases in this post: web services and CLI apps.
Over the last couple of years, I have developed two small web services in Rust: one for EndBASIC and one for this blog. Those two web services contained significant copy/pasted helper code, which always bothered me because small bug fixes in one rarely propagated to the other. But because this only impacted two inconsequential side projects, the hinderance wasn’t a big deal. Until now. I now face the need to write two more web services (details TBA), and duplicating those foundations twice more felt just wrong. So I spent the last couple of weeks pulling the common code out of the existing services into a… you guessed it… framework, which I have called III-IV ("three four" if you read it out loud) and am ready to announce.