<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Production on</title><link>/tags/production/</link><description>Recent content in Production on</description><generator>Hugo</generator><language>en</language><lastBuildDate>Wed, 05 Nov 2025 11:46:00 +0000</lastBuildDate><atom:link href="/tags/production/index.xml" rel="self" type="application/rss+xml"/><item><title>Lesson 10: When Not to Use Rust — Honest trade-offs</title><link>/post/rust/rust-prod-when-not-rust/</link><pubDate>Wed, 05 Nov 2025 11:46:00 +0000</pubDate><guid>/post/rust/rust-prod-when-not-rust/</guid><description>&lt;p&gt;I like Rust. I&amp;rsquo;ve written 9 lessons about using it in production. I think it&amp;rsquo;s one of the most well-designed languages of the last twenty years. And I&amp;rsquo;m about to spend an entire article telling you when you shouldn&amp;rsquo;t use it.&lt;/p&gt;
&lt;p&gt;Because the most dangerous engineers aren&amp;rsquo;t the ones who don&amp;rsquo;t know Rust — they&amp;rsquo;re the ones who think Rust is always the answer. I&amp;rsquo;ve been that engineer. I once argued for rewriting a Flask API endpoint in Rust because &amp;ldquo;the response time was too high.&amp;rdquo; The response time was 200ms, and 180ms of that was a database query. Rust would have saved us maybe 5ms of JSON serialization. My team lead asked me to go take a walk.&lt;/p&gt;</description></item><item><title>Lesson 9: War Stories — Lessons from real Rust deployments</title><link>/post/rust/rust-prod-war-stories/</link><pubDate>Sun, 02 Nov 2025 15:08:00 +0000</pubDate><guid>/post/rust/rust-prod-war-stories/</guid><description>&lt;p&gt;Every language looks great in blog posts. Production is where the truth comes out. I&amp;rsquo;ve been running Rust services in production for a few years now, and while I&amp;rsquo;m convinced it&amp;rsquo;s the right tool for certain problems, I&amp;rsquo;ve also hit situations where Rust did something I didn&amp;rsquo;t expect, or where its strengths became weaknesses in surprising ways.&lt;/p&gt;
&lt;p&gt;These are real stories. Some names and details are changed, but the bugs and the lessons are exactly as they happened.&lt;/p&gt;</description></item><item><title>Lesson 8: Migrating Services from Go/Python/Java to Rust — When and how</title><link>/post/rust/rust-prod-migration-from-go/</link><pubDate>Thu, 30 Oct 2025 09:33:00 +0000</pubDate><guid>/post/rust/rust-prod-migration-from-go/</guid><description>&lt;p&gt;I&amp;rsquo;ve been involved in three Rust migrations. One from Python, one from Go, one from Java. Two were successes. One was a disaster that got cancelled six months in after burning a quarter of the team&amp;rsquo;s roadmap capacity.&lt;/p&gt;
&lt;p&gt;The failed one wasn&amp;rsquo;t a technical failure — the Rust code was fine. It failed because we rewrote the wrong service, at the wrong time, for the wrong reasons. &amp;ldquo;Rust is faster&amp;rdquo; was the entire justification. Nobody had measured whether speed was actually the bottleneck.&lt;/p&gt;</description></item><item><title>Lesson 7: Feature Flags at the Type Level — Compile-time feature control</title><link>/post/rust/rust-prod-feature-flags/</link><pubDate>Tue, 28 Oct 2025 13:19:00 +0000</pubDate><guid>/post/rust/rust-prod-feature-flags/</guid><description>&lt;p&gt;We had a feature that was ready for staging but absolutely not ready for production. In my previous Go gig, we&amp;rsquo;d have used a runtime feature flag service — LaunchDarkly or similar. Evaluate a boolean at request time, show the new code path to internal testers, hide it from everyone else.&lt;/p&gt;
&lt;p&gt;In Rust, we had another option. We could decide &lt;em&gt;at compile time&lt;/em&gt; whether the feature existed in the binary at all. Not a runtime check. Not a boolean. The code literally wasn&amp;rsquo;t in the production binary. You couldn&amp;rsquo;t accidentally enable it. You couldn&amp;rsquo;t exploit it. It didn&amp;rsquo;t exist.&lt;/p&gt;</description></item><item><title>Lesson 6: API Versioning and Backwards Compatibility — Don't break your users</title><link>/post/rust/rust-prod-backwards-compat/</link><pubDate>Sun, 26 Oct 2025 10:55:00 +0000</pubDate><guid>/post/rust/rust-prod-backwards-compat/</guid><description>&lt;p&gt;I once shipped a &amp;ldquo;minor&amp;rdquo; API change on a Friday. Renamed a JSON field from &lt;code&gt;user_name&lt;/code&gt; to &lt;code&gt;username&lt;/code&gt;. Seemed harmless — we were cleaning up inconsistencies. By Monday morning, we had 14 support tickets from integration partners whose parsers broke. One partner had hardcoded the field name into a system that processed payroll. People didn&amp;rsquo;t get paid because I renamed a JSON field.&lt;/p&gt;
&lt;p&gt;That was the last time I treated backwards compatibility as optional.&lt;/p&gt;</description></item><item><title>Lesson 5: Multi-Crate Workspace Architecture — Scaling your codebase</title><link>/post/rust/rust-prod-multi-crate/</link><pubDate>Thu, 23 Oct 2025 16:42:00 +0000</pubDate><guid>/post/rust/rust-prod-multi-crate/</guid><description>&lt;p&gt;Our compile times hit 8 minutes. Not from scratch — &lt;em&gt;incremental&lt;/em&gt;. Change one line in the domain model and wait 8 minutes to see if it worked. Three engineers were actively avoiding making changes to shared code because the feedback loop was unbearable.&lt;/p&gt;
&lt;p&gt;The problem was obvious: everything lived in one crate. The domain model, the HTTP handlers, the database layer, the gRPC server, the background workers — all sharing one &lt;code&gt;Cargo.toml&lt;/code&gt; with 47 dependencies. Touch anything and the whole thing recompiles.&lt;/p&gt;</description></item><item><title>Lesson 4: CQRS and Event Sourcing — Separating reads from writes</title><link>/post/rust/rust-prod-cqrs/</link><pubDate>Tue, 21 Oct 2025 08:27:00 +0000</pubDate><guid>/post/rust/rust-prod-cqrs/</guid><description>&lt;p&gt;We had this inventory service that was doing fine until it wasn&amp;rsquo;t. Reads were simple — &amp;ldquo;how many units of product X are available?&amp;rdquo; Writes were complex — reservations, adjustments, transfers between warehouses, reconciliation with physical counts. Both read and write operations hit the same database table, the same data model, and the same set of queries that were getting increasingly gnarly.&lt;/p&gt;
&lt;p&gt;Then we hit Black Friday. Read traffic spiked 40x. The complex write queries were locking rows that the read queries needed. We couldn&amp;rsquo;t scale reads without scaling writes. We couldn&amp;rsquo;t optimize the read path without breaking the write path&amp;rsquo;s invariants.&lt;/p&gt;</description></item><item><title>Lesson 3: Hexagonal Architecture in Rust — Ports, adapters, and boundaries</title><link>/post/rust/rust-prod-hexagonal/</link><pubDate>Sun, 19 Oct 2025 11:05:00 +0000</pubDate><guid>/post/rust/rust-prod-hexagonal/</guid><description>&lt;p&gt;About a year ago, I had to swap out our payment provider. In Go, that would&amp;rsquo;ve been a two-week project — chasing down every place we called Stripe&amp;rsquo;s SDK, updating structs, fixing test mocks. In our Rust service, it took a day and a half. The reason wasn&amp;rsquo;t Rust itself. It was how we&amp;rsquo;d structured the code.&lt;/p&gt;
&lt;p&gt;Hexagonal architecture (sometimes called &amp;ldquo;ports and adapters&amp;rdquo;) is one of those patterns that sounds academic until you actually need to replace a database, swap a message broker, or test your business logic without spinning up Docker containers. In Rust, traits make it feel natural rather than ceremonial.&lt;/p&gt;</description></item><item><title>Lesson 2: Domain Modeling with Rust's Type System — Making impossible states impossible</title><link>/post/rust/rust-prod-domain-modeling/</link><pubDate>Fri, 17 Oct 2025 14:38:00 +0000</pubDate><guid>/post/rust/rust-prod-domain-modeling/</guid><description>&lt;p&gt;We shipped a bug to production that cost us about three hours of incident response and a very uncomfortable Slack thread. The root cause? Someone passed a &lt;code&gt;user_id&lt;/code&gt; where an &lt;code&gt;order_id&lt;/code&gt; was expected. Both were &lt;code&gt;String&lt;/code&gt;. Both were UUIDs. The compiler had no way to tell them apart. The function signature said &lt;code&gt;fn cancel_order(order_id: String, user_id: String)&lt;/code&gt;, and someone called it with the arguments flipped.&lt;/p&gt;
&lt;p&gt;This is the kind of bug that makes you rethink everything. Not because it&amp;rsquo;s complex — because it&amp;rsquo;s &lt;em&gt;stupid&lt;/em&gt;. And stupid bugs that slip through a strong type system mean the type system wasn&amp;rsquo;t being used right.&lt;/p&gt;</description></item><item><title>Lesson 1: Structuring a Large Rust Application — Beyond hello world</title><link>/post/rust/rust-prod-architecture/</link><pubDate>Wed, 15 Oct 2025 09:14:00 +0000</pubDate><guid>/post/rust/rust-prod-architecture/</guid><description>&lt;p&gt;The moment I knew our Rust project structure was broken was when a junior engineer asked me where to put a new endpoint. I opened the repo, stared at the &lt;code&gt;src/&lt;/code&gt; directory, and realized I couldn&amp;rsquo;t confidently answer. We had 40,000 lines of Rust spread across files with names like &lt;code&gt;utils.rs&lt;/code&gt;, &lt;code&gt;helpers.rs&lt;/code&gt;, &lt;code&gt;types.rs&lt;/code&gt;, and the ever-popular &lt;code&gt;misc.rs&lt;/code&gt;. Everything compiled. Nothing made sense.&lt;/p&gt;
&lt;p&gt;Most Rust tutorials stop at &amp;ldquo;put your code in &lt;code&gt;main.rs&lt;/code&gt; and maybe &lt;code&gt;lib.rs&lt;/code&gt;.&amp;rdquo; That works for a CLI tool or a weekend project. It completely falls apart when you&amp;rsquo;ve got a team of eight engineers building a platform with multiple services, shared domain logic, and infrastructure that&amp;rsquo;s evolving every sprint.&lt;/p&gt;</description></item></channel></rss>