<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gaspard Boursin</title>
    <description>The latest articles on DEV Community by Gaspard Boursin (@gaspardb).</description>
    <link>https://dev.to/gaspardb</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1431315%2Fec289faf-3a75-43ec-98be-58ed0a46b842.png</url>
      <title>DEV Community: Gaspard Boursin</title>
      <link>https://dev.to/gaspardb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gaspardb"/>
    <language>en</language>
    <item>
      <title>Announcing Meteroid OSS v1 RC and Cloud 🦀</title>
      <dc:creator>Gaspard Boursin</dc:creator>
      <pubDate>Thu, 29 Jan 2026 10:13:11 +0000</pubDate>
      <link>https://dev.to/gaspardb/announcing-meteroid-oss-v1-rc-and-cloud-1gmo</link>
      <guid>https://dev.to/gaspardb/announcing-meteroid-oss-v1-rc-and-cloud-1gmo</guid>
      <description>&lt;p&gt;Hey dev.to! 👋&lt;/p&gt;

&lt;p&gt;Two years ago, we wrote the first line of code for Meteroid. Today, we're launching our hosted cloud platform and releasing our open-source v1 release candidate — and we're doing it on &lt;a href="https://www.producthunt.com/products/meteroid-2?launch=meteroid-2" rel="noopener noreferrer"&gt;Product Hunt&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We'd love your support, but first, let me tell you why we built this thing.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem We Kept Running Into
&lt;/h2&gt;

&lt;p&gt;If you've ever tried to implement anything beyond basic subscriptions, you know the pain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stripe Billing&lt;/strong&gt; takes ~0.8% on top of payment fees, checkout fees, invoice fees, tax fees, revenue analytics fees etc, and gets complicated fast when you need usage-based pricing or some flexibility in your plans&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Specialized billing platforms&lt;/strong&gt; want 5-figure annual contracts before you've even validated your pricing model&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Building it yourself&lt;/strong&gt; means months of engineering time on invoice edge cases instead of your actual product&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We got frustrated enough to do something about it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What We Built
&lt;/h2&gt;

&lt;p&gt;Meteroid is an open-source monetization platform. Not "open-core" with all the good stuff behind a paywall like most our competitors — actually open-source.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One platform for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📦 Subscription management (the basics, done right)&lt;/li&gt;
&lt;li&gt;📊 Usage-based billing &amp;amp; metering (send events, we handle the math)&lt;/li&gt;
&lt;li&gt;🧾 Invoicing (automated, accurate, on-time)&lt;/li&gt;
&lt;li&gt;💼 Quote-to-cash / CPQ (for when sales closes custom deals)&lt;/li&gt;
&lt;li&gt;📈 Revenue insights (actual business intelligence to act on signals, our main goal for 2026)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For the devs wondering about the stack:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backend in &lt;strong&gt;Rust&lt;/strong&gt; 🦀 (speed, safety, and it handles millions of usage events without breaking a sweat)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;React&lt;/strong&gt; frontend&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PostgreSQL&lt;/strong&gt; for transactional data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ClickHouse&lt;/strong&gt; for analytics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kafka&lt;/strong&gt; for event streaming&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We're not slapping a UI on Stripe. This is infrastructure built from the ground up for modern SaaS pricing models.&lt;/p&gt;




&lt;h2&gt;
  
  
  The "Visionary" Plan — Our Launch Offer for founders
&lt;/h2&gt;

&lt;p&gt;We created the &lt;strong&gt;Visionary plan&lt;/strong&gt;: &lt;strong&gt;€49/month&lt;/strong&gt; for the full platform — billing, metering, invoicing, insights, everything — until you hit &lt;strong&gt;€1M in cumulative revenue&lt;/strong&gt; billed through Meteroid.&lt;/p&gt;

&lt;p&gt;No percentage cuts. No "talk to sales" gatekeeping. No bait-and-switch.&lt;/p&gt;

&lt;p&gt;Why? Because we are early-stage founders too. Advanced billing shouldn't be something you "graduate into" at Series B. You should be able to experiment with usage-based pricing, hybrid models, and custom deals from day one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Open Source Matters Here
&lt;/h2&gt;

&lt;p&gt;Billing is core infrastructure. It touches your revenue, your customers, your contracts. We think you should be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Inspect it&lt;/strong&gt; — see exactly how your invoices are calculated&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extend it&lt;/strong&gt; — add custom integrations without waiting on our roadmap&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-host it&lt;/strong&gt; — if that's what you need (Docker Compose and Helm charts included)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trust it&lt;/strong&gt; — no vendor lock-in, no surprises&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We're building in public. Our &lt;a href="https://github.com/meteroid-oss/meteroid" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt; has been open since day one. And we're not going anywhere !&lt;/p&gt;




&lt;h2&gt;
  
  
  What We're NOT Doing
&lt;/h2&gt;

&lt;p&gt;A few conscious choices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ &lt;strong&gt;No giant sales team&lt;/strong&gt; — we're mostly self-serve, so we can focus on building&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;No feature-gating&lt;/strong&gt; — the Visionary plan includes everything, not a crippled "starter" tier&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;No fake "open source"&lt;/strong&gt; — we're AGPL, not open-core with all the features behind enterprise licenses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We do offer &lt;strong&gt;sponsored development&lt;/strong&gt; — if you need a specific integration or feature fast, you can fund it and it goes into the open-source codebase. Everyone benefits.&lt;/p&gt;




&lt;h2&gt;
  
  
  We're Live on Product Hunt 🚀
&lt;/h2&gt;

&lt;p&gt;We're live today !&lt;/p&gt;

&lt;p&gt;If Meteroid sounds like something you'd use — or something you wish existed when you were building your billing system at 2am — we'd really appreciate your feedback and support:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://www.producthunt.com/products/meteroid-2?launch=meteroid-2" rel="noopener noreferrer"&gt;Support us on Product Hunt&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpuoge7zdu9f54zsifpru.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpuoge7zdu9f54zsifpru.png" alt="meteroid home" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Try It Out
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🌐 &lt;strong&gt;Cloud&lt;/strong&gt;: &lt;a href="https://app.meteroid.com/registration" rel="noopener noreferrer"&gt;app.meteroid.com&lt;/a&gt; — sign up, play with the sandbox&lt;/li&gt;
&lt;li&gt;📖 &lt;strong&gt;Docs&lt;/strong&gt;: &lt;a href="https://docs.meteroid.com" rel="noopener noreferrer"&gt;docs.meteroid.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;⭐ &lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/meteroid-oss/meteroid" rel="noopener noreferrer"&gt;github.com/meteroid-oss/meteroid&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💬 &lt;strong&gt;Discord&lt;/strong&gt;: &lt;a href="https://go.meteroid.com/discord" rel="noopener noreferrer"&gt;Join our community&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'd love to hear what you think. Drop a comment, roast our landing page, tell us what features you need — we're listening.&lt;/p&gt;

&lt;p&gt;Thanks for reading, and thanks for building with us. 🙏&lt;/p&gt;

&lt;p&gt;— Gaspard, Donatien &amp;amp; the Meteroid team&lt;/p&gt;

</description>
      <category>rust</category>
      <category>startup</category>
      <category>webdev</category>
      <category>product</category>
    </item>
    <item>
      <title>Anthropic just acquired Bun.js. Here's why.</title>
      <dc:creator>Gaspard Boursin</dc:creator>
      <pubDate>Wed, 03 Dec 2025 13:49:49 +0000</pubDate>
      <link>https://dev.to/meteroid/anthropic-just-bought-bunjs-heres-why-6bh</link>
      <guid>https://dev.to/meteroid/anthropic-just-bought-bunjs-heres-why-6bh</guid>
      <description>&lt;p&gt;Anthropic just acquired Bun. First acquisition ever for the company behind Claude and Claude Code.&lt;/p&gt;

&lt;p&gt;Most takes I've seen focus on speed. "Claude Code needs fast tooling." "Milliseconds matter at scale." That's not wrong, but it's not the interesting part either. &lt;/p&gt;

&lt;p&gt;The interesting part is what this signals about where AI development is headed.&lt;/p&gt;

&lt;p&gt;Let's dive in!&lt;/p&gt;




&lt;h2&gt;
  
  
  First, some context
&lt;/h2&gt;

&lt;p&gt;Bun is a JavaScript runtime that's faster than Node.js. It's also a package manager, bundler, and test runner, all in one. It's open source, MIT-licensed, and has about 7 million monthly downloads.&lt;/p&gt;

&lt;p&gt;Anthropic makes Claude. Their coding tool, Claude Code, has been growing fast: it hit $1 billion in annual run-rate revenue six months after public launch. That's not a typo. Six months.&lt;/p&gt;

&lt;p&gt;Here's the connection: &lt;strong&gt;Claude Code ships as a Bun executable.&lt;/strong&gt; When you install Claude Code, you're running Bun. This isn't a loose partnership but a dependency.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia0.giphy.com%2Fmedia%2Fv1.Y2lkPTc5MGI3NjExenJ0MnN2aHdma3Rsd3RxZmRwcHNmd3M0ZnQyNDZkOHNmNXc1ejRpdiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw%2Fe3dDrlMiQhLXV8SQlf%2Fgiphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia0.giphy.com%2Fmedia%2Fv1.Y2lkPTc5MGI3NjExenJ0MnN2aHdma3Rsd3RxZmRwcHNmd3M0ZnQyNDZkOHNmNXc1ejRpdiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw%2Fe3dDrlMiQhLXV8SQlf%2Fgiphy.gif" width="480" height="480"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Anthropic needed to own this
&lt;/h2&gt;

&lt;p&gt;Here's how developer tooling has evolved in the AI era:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Phase 1:&lt;/strong&gt;&lt;br&gt;
LLMs generate code, humans run it. ChatGPT writes, you copy-paste, you fix.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Phase 2:&lt;/strong&gt;&lt;br&gt;
LLMs call tools via function calling and MCPs. Still orchestrated, constrained, pre-defined.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Next - Phase 3:&lt;/strong&gt;&lt;br&gt;
Agents that write new tools and interactive interfaces on the fly, compile them, execute them, observe results, iterate. Spawn sub-agents and processes. Orchestrate parallel workloads.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If Phase 3 is coming—and Anthropic clearly believes it is—then the runtime isn't just where code executes. It's (literally for once) the &lt;strong&gt;operating system for AI agents&lt;/strong&gt;. You want to own that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The practical angle matters too.&lt;/strong&gt; Bun compiles projects into single-file executables. No Node install, no dependency hell, just a binary. That's how Claude Code ships cleanly to millions of machines, and how agents could eventually distribute tools to each other.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What about OpenAI?&lt;/strong&gt; Both companies have consumer products and APIs, but their acquisition patterns reveal priorities. OpenAI just acquired Sky (NL on MacOS) and invested in consumer-facing features. &lt;br&gt;
Anthropic's first acquisition is a JavaScript runtime. They're betting that the winning AI company will be the one most deeply embedded in how software gets built, not the one with the best chat UI.&lt;/p&gt;

&lt;p&gt;Jarred Sumner (Bun's founder) seems to agree. After walking through the acquisition with Anthropic's competitors, his conclusion: &lt;em&gt;"I think Anthropic is going to win."&lt;/em&gt; That's not PR. That's someone betting his life's work on a conviction.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;We're building &lt;a href="https://meteroid.com" rel="noopener noreferrer"&gt;Meteroid&lt;/a&gt;, a fully open-source billing platform for SaaS.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you have a minute, please give us &lt;a href="https://github.com/meteroid-oss/meteroid" rel="noopener noreferrer"&gt;a star on Github&lt;/a&gt;!&lt;/em&gt; ⭐️&lt;/p&gt;

&lt;p&gt;This would help us a lot ❤️&lt;/p&gt;




&lt;h2&gt;
  
  
  For JS developers: This is probably good
&lt;/h2&gt;

&lt;p&gt;Bun had $26M in funding, zero revenue, and "eventually we'll build a cloud hosting product" as the monetization plan. That's the standard dev tools playbook, and it usually ends one of three ways: awkward pricing, acquisition for talent, or slow death.&lt;/p&gt;

&lt;p&gt;Now Bun has a different mandate: be the best runtime for Anthropic's needs, which happen to overlap heavily with being the best runtime period.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The good news:&lt;/strong&gt; If sustainability worried you (VC-funded, zero revenue, unclear business model) that's resolved. Anthropic's flagship product depends on Bun. They're not letting it rot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The risk shifts, not disappears.&lt;/strong&gt; Bun's roadmap now has a $1B ARR product as its primary stakeholder. That's probably fine; Claude Code needs speed, stability, and Node compatibility, which is what everyone wants. But it's worth watching:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does Node.js compatibility keep improving?&lt;/li&gt;
&lt;li&gt;Do agent-specific optimizations start crowding out general features?&lt;/li&gt;
&lt;li&gt;Does GitHub issue responsiveness stay high?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;My take:&lt;/strong&gt; This is likely net positive. The incentives align more than they conflict. Check back in 18 months.&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The real reason isn't speed.&lt;/strong&gt; It's distribution (single-file executables), dependency control (they already relied on Bun), and positioning for a future where agents are the primary users of developer tooling.&lt;br&gt;
We're moving from "LLMs that generate code" to "agents that build and run their own tools." The runtime becomes the agent's operating system. Anthropic wants to own that.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;For JS devs:&lt;/strong&gt; Sustainability concern addressed. New concern: priority alignment. Watch Node.js compatibility and community responsiveness.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The meta-story:&lt;/strong&gt; Anthropic is betting on developer infrastructure, not consumer AI. This won't be their last acquisition like this.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The most active contributor to Bun is already an AI agent. That's not the future. That's now.&lt;/p&gt;




&lt;p&gt;I hope you enjoyed this read ! (and the handmade cover picture 🙈) &lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you found it insightful, you can support us by leaving &lt;a href="https://github.com/meteroid-oss/meteroid" rel="noopener noreferrer"&gt;Meteroid&lt;/a&gt; a star on Github ⭐️!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Cheers ! &lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>javascript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>What's coming to PostgreSQL ? 🐘</title>
      <dc:creator>Gaspard Boursin</dc:creator>
      <pubDate>Tue, 30 Apr 2024 15:03:16 +0000</pubDate>
      <link>https://dev.to/meteroid/the-elephant-in-the-room-what-future-for-postgresql--gcf</link>
      <guid>https://dev.to/meteroid/the-elephant-in-the-room-what-future-for-postgresql--gcf</guid>
      <description>&lt;p&gt;PostgreSQL, the powerful, reliable and highly extensible open-source relational DBMS, has been &lt;strong&gt;a mainstay in the database world for over three decades&lt;/strong&gt;, gaining a loyal following among developers and organizations alike, for &lt;strong&gt;an ever-growing number of use case&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExeDBweGhnd3RtancydHozaGxwbmoxOHExdTh0dXJvYmR5dTlpYXdiZCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/wwKoFueqHUQ2WWGHDE/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExeDBweGhnd3RtancydHozaGxwbmoxOHExdTh0dXJvYmR5dTlpYXdiZCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/wwKoFueqHUQ2WWGHDE/giphy.gif" width="480" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the past couple years, amidst the increasing importance of "global-scale" P/IaaS like Vercel and Cloudflare, many MySQL or SQLite-based solutions like &lt;strong&gt;Planetscale, Turso, and Cloudflare D1&lt;/strong&gt; have been making waves with their &lt;strong&gt;serverless architecture, global redundancy promises, unlimited storage and easy scalability&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At the same time, PostgreSQL is undergoing a series of transformations that could redefine its role in the database landscape.&lt;br&gt;
In this article, we'll explore some of the exciting developments shaping the future of PostgreSQL, with a focus on the contributions of companies like Supabase, Neon, ParadeDB or AWS.&lt;/p&gt;


&lt;h2&gt;
  
  
  Separation of Compute and Storage
&lt;/h2&gt;

&lt;p&gt;One of the most significant advancements coming to the PostgreSQL ecosystem lies in the separation of compute and storage. Companies like Neon, AWS, and recently Supabase are pioneering this approach, which brings major benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Horizontal Scalability:&lt;/strong&gt; Just add more compute nodes to allow the system to handle increased load without hitting performance walls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost-Effective Scaling:&lt;/strong&gt; The separation allows for independent scaling of compute and storage resources based on specific workload requirements, optimizing costs and possibly offloading storage to S3.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seamless Replication and High Availability:&lt;/strong&gt; The decoupled architecture simplifies replication and enables high availability setups.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Branching and Improved Developer Experience:&lt;/strong&gt; The storage layer can be easily forked and attached to new compute instances, facilitating branching and enhancing the development workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a deeper dive into the benefits and implications of this architecture, I recommend exploring this insightful &lt;a href="https://davidgomes.com/separation-of-storage-and-compute-and-compute-compute-separation-in-databases/" rel="noopener noreferrer"&gt;blog post from David Gomes&lt;/a&gt; and the linked resources.&lt;/p&gt;

&lt;p&gt;Neon is already offering a production-ready postgres serverless offering using this technology, all open source, jump to their &lt;a href="https://neon.tech/blog/hello-world" rel="noopener noreferrer"&gt;hello-world&lt;/a&gt; post for an overview of their approach.&lt;/p&gt;


&lt;h2&gt;
  
  
  Pluggable Storage and Improved Table Engines
&lt;/h2&gt;

&lt;p&gt;At the heart of every database lies its table engine.&lt;br&gt;
In Postgres 12, a basic support for a &lt;strong&gt;"Pluggable Storage"&lt;/strong&gt; was introduced, as a way to &lt;strong&gt;define different table engines for different tables in the same database&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This unlocks the potential to use a single PostgreSQL instance for various use cases, from OLAP and AI vector search to log search, without compromising performance or storage efficiency.&lt;/p&gt;

&lt;p&gt;However the initial implementation had limitations, stalled and didn't fully achieve its goals.&lt;/p&gt;

&lt;p&gt;Some solutions like CitusData's columnar storage leveraged this initial API but came with significant &lt;a href="https://docs.citusdata.com/en/v11.1/admin_guide/table_management.html#limitations" rel="noopener noreferrer"&gt;restrictions&lt;/a&gt; over core PostgreSQL features.&lt;/p&gt;

&lt;p&gt;Enter &lt;a href="https://www.orioledb.com/" rel="noopener noreferrer"&gt;Oriole&lt;/a&gt;.&lt;br&gt;
&lt;a href="https://supabase.com/blog/supabase-aquires-oriole" rel="noopener noreferrer"&gt;Acquired by Supabase&lt;/a&gt; officially in April 2024, Oriole is designed as a drop-in replacement for the existing storage engine, bringing notable performance improvements and advanced features. It reduces I/O (enabling decoupled storage and compute), supports page-level data compression, and introduces index-organized tables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="n"&gt;extension&lt;/span&gt; &lt;span class="n"&gt;orioledb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;-- enable the extension&lt;/span&gt;

&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;int8&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;primary&lt;/span&gt; &lt;span class="k"&gt;key&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;orioledb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;-- Specify the storage format&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Oriole team is actively driving contributions around Pluggable Storage, building upon the efforts initiated in PostgreSQL 12 to create &lt;strong&gt;a more integrated extension ecosystem&lt;/strong&gt;, supporting many more advanced extensions and use cases on standard PostgreSQL instances. &lt;/p&gt;




&lt;p&gt;&lt;strong&gt;A few words about us&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Our project, &lt;a href="https://meteroid.com/" rel="noopener noreferrer"&gt;Meteroid&lt;/a&gt;, is a modern and &lt;strong&gt;open-source billing platform&lt;/strong&gt; that focuses on business intelligence and actionable insights.&lt;/p&gt;

&lt;p&gt;To allow our users to self-host without our complete saas-focused infrastructure, including a Clickhouse cluster, we've been researching options to leverage Postgres more intensively for all our different use cases. We figured we would share some of our findings about the ecosystem through this blog post.&lt;/p&gt;

&lt;p&gt;If you have a minute, please consider &lt;a href="https://github.com/meteroid-oss/meteroid" rel="noopener noreferrer"&gt;starring us on Github ⭐️&lt;/a&gt; ! This would help us a lot ❤️&lt;/p&gt;




&lt;h2&gt;
  
  
  Extending PostgreSQL: OLAP, AI, Search, and Beyond
&lt;/h2&gt;

&lt;p&gt;The PostgreSQL extension ecosystem has seen remarkable achievements lately. Two notable examples are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/pgvector/pgvector" rel="noopener noreferrer"&gt;&lt;code&gt;pgvector&lt;/code&gt;&lt;/a&gt;: This community-driven effort provides a vector data type and &lt;strong&gt;search functionality for high-dimensional vectors&lt;/strong&gt;. Despite its relatively small codebase, &lt;code&gt;pgvector&lt;/code&gt; rivals the performance of many specialized vector databases.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.paradedb.com/" rel="noopener noreferrer"&gt;ParadeDB&lt;/a&gt;: Implemented in Rust, this open-source PostgreSQL extension(s) delivers outstanding results in &lt;strong&gt;both OLAP&lt;/strong&gt; (closing the gap with ClickHouse) &lt;strong&gt;and Search&lt;/strong&gt; (outperforming Elasticsearch in some benchmarks) use cases.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the new Pluggable Storage model, these extensions and many others could be easily added to any PostgreSQL instance, potentially even interacting with each other, unlocking powerful combinations and enabling PostgreSQL to handle a &lt;strong&gt;wide range of workloads&lt;/strong&gt; efficiently.&lt;/p&gt;

&lt;p&gt;Many other projects exist around the postgres community, to cover emerging use cases. A couple examples :  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://electric-sql.com/" rel="noopener noreferrer"&gt;electric-sql&lt;/a&gt; (the people behind CRDTs) is building a sync layer between postgres and local/embeded SQLite database to build &lt;strong&gt;local-first apps&lt;/strong&gt;, a paradigm shift toward dynamic and instant local apps&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AWS RDS team created &lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/PostgreSQL_trusted_language_extension.html" rel="noopener noreferrer"&gt;&lt;strong&gt;Trusted Language Extensions&lt;/strong&gt;&lt;/a&gt; (pg_tle), an initiative to make postgres extensions more accessible to users relying on a cloud managed database. This is a pretty huge step for the open-source/self-hosted community, as until then &lt;strong&gt;building on top of an extension&lt;/strong&gt; could make the deployment a lot more complex.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  A couple of resources to delve deeper
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.youtube.com/@ScalingPostgres" rel="noopener noreferrer"&gt;Scaling Postgres&lt;/a&gt;, a youtube podcast from Creston Jamison with a lot of discussions and news curation across the postgres ecosystem &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://medium.com/@fengruohang/postgres-is-eating-the-database-world-157c204dcfc4" rel="noopener noreferrer"&gt;pg is eating the db world&lt;/a&gt; from @fengruohang , an extensive blog post to understand the unique position of Postgres in the ecosystem&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Future may be both Unified and Distributed
&lt;/h2&gt;

&lt;p&gt;As applications become increasingly global and complex, the need for a &lt;strong&gt;distributed, multi-purpose database&lt;/strong&gt; is more pressing than ever. &lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;incredibly extensible core&lt;/strong&gt; of Postgres, alongside the &lt;strong&gt;massive community and shared efforts&lt;/strong&gt; from many players across the industry, has led to impressive advancements. From the separation of compute and storage to a new Pluggable Storage, and the growing number of highly powerful extensions across languages and domains, Postgres is poised as a &lt;strong&gt;strong contender&lt;/strong&gt; for a global, holistic database solution, to meet the demands of modern software development.&lt;/p&gt;

&lt;p&gt;The future of PostgreSQL may not just be about being a reliable and feature-rich database, but about becoming &lt;strong&gt;a catalyst for building applications that are fast, scalable, and adaptable&lt;/strong&gt; to the ever-changing needs of businesses and users.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExN3FhZmQ2OHRlYmplOWhwYzE0OGptdnA1cmdlemFjdHVyYzAzOGlhaCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/cgd0FGjcugRArA2O8y/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExN3FhZmQ2OHRlYmplOWhwYzE0OGptdnA1cmdlemFjdHVyYzAzOGlhaCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/cgd0FGjcugRArA2O8y/giphy.gif" width="480" height="480"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;I hope you enjoyed this read ! If you found it helpful, don't forget to give &lt;a href="https://github.com/meteroid-oss/meteroid" rel="noopener noreferrer"&gt;our repo&lt;/a&gt; a star! Your support means the world to us.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/meteroid-oss/meteroid" class="ltag_cta ltag_cta--branded" rel="noopener noreferrer"&gt;⭐️ Star Meteroid ⭐️&lt;/a&gt;
 &lt;/p&gt;

&lt;p&gt;Cheers ! &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>opensource</category>
      <category>database</category>
    </item>
    <item>
      <title>What we learned building our SaaS with Rust 🦀</title>
      <dc:creator>Gaspard Boursin</dc:creator>
      <pubDate>Mon, 29 Apr 2024 10:45:02 +0000</pubDate>
      <link>https://dev.to/meteroid/5-lessons-learned-building-our-saas-with-rust-1doj</link>
      <guid>https://dev.to/meteroid/5-lessons-learned-building-our-saas-with-rust-1doj</guid>
      <description>&lt;p&gt;In this post we will &lt;strong&gt;not&lt;/strong&gt; answer the question everybody asks when starting a new project: &lt;strong&gt;Should I do it in Rust ?&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExNHQwOTl6Ym5odmVmNDZpdzVmZG9mMW9yd2tmN2lyZ2NzOWNxc2MxMCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/l83rkRUu4IqyUbt5k6/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExNHQwOTl6Ym5odmVmNDZpdzVmZG9mMW9yd2tmN2lyZ2NzOWNxc2MxMCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/l83rkRUu4IqyUbt5k6/giphy.gif" width="480" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Instead, we'll explore the pitfalls and insights we encountered after confidently answering "&lt;strong&gt;absolutely!&lt;/strong&gt;" and embarking on our journey to build a business using mostly Rust.&lt;/p&gt;

&lt;p&gt;This post aims to provide a high-level overview of our experiences, we will delve deeper into the details in an upcoming series.&lt;/p&gt;

&lt;p&gt;(vote in the comments for our next post 🗳️)&lt;/p&gt;


&lt;h2&gt;
  
  
  Why Rust
&lt;/h2&gt;

&lt;p&gt;Choosing the right language for a project is never a one-size-fits-all decision. &lt;/p&gt;

&lt;p&gt;A couple words about our team and use case : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;we're a team of 6, with almost no prior Rust experience but an extensive Scala/Java background building data-intensive applications&lt;/li&gt;
&lt;li&gt;our SaaS is a Billing platform with a strong focus on analytics, realtime data and actionable insights (think Stripe Billing meets Profitwell, with a dash of Posthog).&lt;/li&gt;
&lt;li&gt;our backend is fully in Rust (divided in 2 modules and a couple of workers), and talks to our React frontend using gRPC-web &lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;We're open source ! &lt;br&gt;
You can find our repo here : &lt;a href="https://github.com/meteroid-oss/meteroid" rel="noopener noreferrer"&gt;https://github.com/meteroid-oss/meteroid&lt;/a&gt;&lt;br&gt;
We would love your support ⭐ and contribution&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We therefore have some non-negotiable requirements that happen to fit Rust pretty well: &lt;strong&gt;performance, safety, and concurrency&lt;/strong&gt;. &lt;br&gt;
Rust virtually eliminate entire classes of bugs and CVEs related to memory management, while its concurrency primitives are pretty appealing (and didn't disappoint).&lt;/p&gt;

&lt;p&gt;In a SaaS, all these features are particularly valuable when dealing with sensitive or critical tasks, like in our case metering, invoice computation and delivery.&lt;/p&gt;

&lt;p&gt;Its significant memory usage reduction is also a major bonus to build a scalable and &lt;strong&gt;sustainable&lt;/strong&gt; platform, as many large players &lt;a href="https://mspoweruser.com/microsoft-forms-new-team-to-help-rewrite-core-windows-components-into-rust-from-c-c/" rel="noopener noreferrer"&gt;including Microsoft&lt;/a&gt; have recently acknowledged.&lt;/p&gt;

&lt;p&gt;Coming from the drama-heavy and sometimes toxic Scala community, the &lt;strong&gt;welcoming and inclusive&lt;/strong&gt; Rust ecosystem was also a significant draw, providing motivation to explore this new territory.&lt;/p&gt;

&lt;p&gt;With these high hopes, let's start our journey ! &lt;/p&gt;


&lt;h2&gt;
  
  
  Lesson 1: The Learning Curve is real
&lt;/h2&gt;

&lt;p&gt;Learning Rust isn't like picking up just another language. Concepts like ownership, borrowing, and lifetimes can be daunting initially, making otherwise trivial code extremely time consuming.&lt;/p&gt;

&lt;p&gt;As pleasant as the ecosystem is (more on that later), &lt;strong&gt;you WILL inevitably need to write lower-level code&lt;/strong&gt; at times.&lt;/p&gt;

&lt;p&gt;For instance, consider a rather basic middleware for our API (Tonic/Tower) that simply reports the compute duration :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ReqBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ResBody&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ReqBody&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;MetricService&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt;
    &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ReqBody&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ResBody&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BoxError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Clone&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;'static&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;'static&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ReqBody&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BoxError&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ResponseFuture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;poll_ready&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Poll&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.inner&lt;/span&gt;&lt;span class="nf"&gt;.poll_ready&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ReqBody&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Future&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;clone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.inner&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;inner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.inner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;started_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Instant&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;sm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;GrpcServiceMethod&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;extract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="nf"&gt;.uri&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;future&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inner&lt;/span&gt;&lt;span class="nf"&gt;.call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;ResponseFuture&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;future&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;started_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;sm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[pin_project]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;ResponseFuture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[pin]&lt;/span&gt;
    &lt;span class="n"&gt;future&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;started_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Instant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;GrpcServiceMethod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ResBody&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ResponseFuture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt;
    &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ResBody&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BoxError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ResBody&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BoxError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;poll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Pin&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Poll&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.project&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;ready!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="py"&gt;.future&lt;/span&gt;&lt;span class="nf"&gt;.poll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cx&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;finished_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Instant&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;delta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;finished_at&lt;/span&gt;&lt;span class="nf"&gt;.duration_since&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="py"&gt;.started_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.as_millis&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="c1"&gt;// this is the actual logic&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;grpc_status_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 

        &lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;record_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nn"&gt;GrpcKind&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SERVER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;this&lt;/span&gt;&lt;span class="py"&gt;.sm&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;grpc_status_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;delta&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nn"&gt;Poll&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Ready&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, in addition to generic types, generic lifetimes, and trait constraints, you end up writing a custom Future implementation for a simple service middleware. &lt;br&gt;
Keep in mind that this is a somewhat extreme example, to showcase the rough edges existing in the ecosystem. &lt;em&gt;In many cases, Rust can end up being as compact as any other modern language.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The learning curve can vary depending on your background.&lt;/strong&gt; If you're used to the JVM handling the heavy lifting and working with a more mature, extensive ecosystem like we were, it might take a bit more effort to understand Rust's unique concepts and paradigms.&lt;/p&gt;

&lt;p&gt;However, once you grasp these concepts and primitives, they become incredibly powerful tools in your arsenal, boosting your productivity even if you occasionally need to write some boilerplate or macros. &lt;br&gt;
It's worth mentioning that &lt;a href="https://www.theregister.com/2024/03/31/rust_google_c" rel="noopener noreferrer"&gt;Google has successfully transitioned teams from Go and C++ to Rust&lt;/a&gt; in a rather short timeframe and with positive outcomes.&lt;/p&gt;

&lt;p&gt;To smooth out the learning curve, consider the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Read the official &lt;a href="https://doc.rust-lang.org/stable/book/" rel="noopener noreferrer"&gt;Rust Book&lt;/a&gt; cover to cover&lt;/strong&gt;. Don't skip chapters. Understanding these complex concepts will become much easier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Practice, practice, practice!&lt;/strong&gt; Work through &lt;a href="https://rustlings.cool/" rel="noopener noreferrer"&gt;Rustlings&lt;/a&gt; exercises to build muscle memory and adopt the Rust mindset. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Engage with the &lt;a href="https://www.reddit.com/r/rust/" rel="noopener noreferrer"&gt;Rust community&lt;/a&gt;.&lt;/strong&gt; They're an incredible bunch, always willing to lend a helping hand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leverage GitHub's search&lt;/strong&gt; capabilities to find and learn from other projects. The ecosystem is still evolving, and collaborating with others is essential (just be mindful of licenses and always contribute back).
We'll explore some of the projects we've been inspired by in the next post.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Lesson 2: The ecosystem is still maturing
&lt;/h2&gt;

&lt;p&gt;The low-level ecosystem in Rust is truly incredible, with exceptionally well-designed and maintained libraries that are widely adopted by the community. These libraries form a solid foundation for building high-performance and reliable systems.&lt;/p&gt;

&lt;p&gt;However, as you move higher up the stack, things can get slightly more complex.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExeWNoejRsb2RhaGsybzQwdXJydjJzbHVpNjR6eW9udzdudjlvdWVjdiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/l2SpOlC7JLROBEkO4/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExeWNoejRsb2RhaGsybzQwdXJydjJzbHVpNjR6eW9udzdudjlvdWVjdiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/l2SpOlC7JLROBEkO4/giphy.gif" width="384" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, in the database ecosystem, while excellent libraries like &lt;a href="https://github.com/launchbadge/sqlx" rel="noopener noreferrer"&gt;&lt;code&gt;sqlx&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/diesel-rs/diesel" rel="noopener noreferrer"&gt;&lt;code&gt;diesel&lt;/code&gt;&lt;/a&gt; exist for relational databases, the story is more complicated with many asynchronous or non-relational database clients. High-quality libraries in these areas, even if used by large companies, often have &lt;strong&gt;single maintainers&lt;/strong&gt;, leading to slower development and potential maintenance risks.&lt;br&gt;
The challenge is more pronounced for distributed systems primitives, where you may need to implement your own solutions.&lt;/p&gt;

&lt;p&gt;This is not unique to Rust, but we found ourselves in this situation quite often  compared to older/more mature languages.&lt;/p&gt;

&lt;p&gt;On the bright side, &lt;strong&gt;Rust's ecosystem is impressively responsive to security issues&lt;/strong&gt;, with swift patches promptly propagated, ensuring the stability and security of your applications.&lt;/p&gt;

&lt;p&gt;The tooling around Rust development has been pretty amazing so far as well.&lt;/p&gt;

&lt;p&gt;We'll take a deep dive into the libraries we chose and the decisions we made in a future post.&lt;/p&gt;

&lt;p&gt;The ecosystem is constantly evolving, with the community actively working to fill gaps and provide robust solutions. Be prepared to navigate uncharted waters, allocate resources accordingly to help with maintenance, and contribute back to the community.&lt;/p&gt;


&lt;h3&gt;
  
  
  ...did I mention we are open source ?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://meteroid.com/" rel="noopener noreferrer"&gt;Meteroid&lt;/a&gt; is a modern, open-source billing platform that focuses on business intelligence and actionable insights.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;We need your help ! If you have a minute,&lt;/strong&gt; &lt;br&gt;
&lt;a href="https://git.new/meteroid" rel="noopener noreferrer"&gt;&lt;br&gt;
&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExZDFvd2M3bnZ4OTF1dzBkcHh1NnlwemY1cTU5NWVjOThoZjU4a2U5biZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/XATW2O9w0hrmuIpvtu/giphy.gif" width="432" height="450"&gt;&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your support means a lot to us ❤️&lt;br&gt;
&lt;a href="https://github.com/meteroid-oss/meteroid" class="ltag_cta ltag_cta--branded" rel="noopener noreferrer"&gt;⭐️ Star us on Github ⭐️&lt;/a&gt;
 &lt;/p&gt;


&lt;h2&gt;
  
  
  Lesson 3: Documentation Lies in the Code
&lt;/h2&gt;

&lt;p&gt;When diving into Rust's ecosystem, you'll quickly realize that documentation sites can be a bit... well, sparse, at times. &lt;br&gt;
But fear not! The real treasure often lies within the source code.&lt;/p&gt;

&lt;p&gt;Many libraries have &lt;strong&gt;exceptionally well-documented methods&lt;/strong&gt; with comprehensive examples nestled &lt;strong&gt;within the code comments&lt;/strong&gt;. When in doubt, dive into the source code and explore. You'll often discover the answers you seek and gain a deeper understanding of the library's inner workings.&lt;/p&gt;

&lt;p&gt;While external documentation with usage guides is still important and can save developers time and frustration, in the Rust ecosystem, it's crucial to be prepared to dig into the code when necessary.&lt;/p&gt;

&lt;p&gt;Sites like &lt;a href="https://docs.rs" rel="noopener noreferrer"&gt;docs.rs&lt;/a&gt; provide easy access to code-based documentation for public Rust crates. Alternatively, you can generate documentation for all your dependencies locally using cargo doc. This approach might be confusing at first, but spending some time learning how to navigate this system can be quite powerful in the long run.&lt;/p&gt;

&lt;p&gt;Needless to say, another helpful technique is to look for examples (&lt;strong&gt;most libraries have an &lt;code&gt;/examples&lt;/code&gt; folder in their repository&lt;/strong&gt;) and other projects that use the library you're interested in, and engage with these communities. These always provide valuable guidance into how the library is meant to be used and can serve as a starting point for your own implementation.&lt;/p&gt;


&lt;h2&gt;
  
  
  Lesson 4: Don't aim for perfection
&lt;/h2&gt;

&lt;p&gt;When starting with Rust, it's tempting to strive for the most idiomatic and performant code possible.&lt;br&gt;
However, most of the time, it's okay to make trade-offs in the name of simplicity and productivity.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffylenuk9pzgynzsvbwpf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffylenuk9pzgynzsvbwpf.png" alt="Done is better than perfect" width="800" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For instance, using &lt;code&gt;clone()&lt;/code&gt; or &lt;code&gt;Arc&lt;/code&gt; to share data between threads might not be the most memory-efficient approach, but it can greatly simplify your code and improve readability. As long as you're conscious of the performance implications and make informed decisions, &lt;strong&gt;prioritizing simplicity is perfectly acceptable.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Remember, premature optimization is the root of all evil. Focus on writing clean, maintainable code first, and optimize later when necessary. &lt;strong&gt;Don't try to micro-optimize&lt;/strong&gt; &lt;strong&gt;¹&lt;/strong&gt; (until you really need to). Rust's strong type system and ownership model already provide a solid foundation for writing efficient and safe code.&lt;/p&gt;

&lt;p&gt;When optimizing performance becomes necessary, focus on the critical path and use profiling tools like &lt;code&gt;perf&lt;/code&gt; and &lt;code&gt;flamegraph&lt;/code&gt; to identify the real performance hotspots in your code. For a comprehensive overview of the tools and techniques, I can recommend &lt;a href="https://nnethercote.github.io/perf-book/introduction.html" rel="noopener noreferrer"&gt;The Rust Performance Book&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feyudtxuaeswhtc9porfc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feyudtxuaeswhtc9porfc.png" alt=" " width="800" height="312"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;¹&lt;/strong&gt; this applies throughout your startup journey, including fundraising&lt;/p&gt;


&lt;h2&gt;
  
  
  Lesson 5: Errors can be nice after all
&lt;/h2&gt;

&lt;p&gt;Rust's error handling is quite elegant, with the &lt;code&gt;Result&lt;/code&gt; type and the &lt;code&gt;?&lt;/code&gt; operator encouraging explicit error handling and propagation. However, it's not just about handling errors; it's also about providing clean and informative error messages with traceable stack traces.&lt;br&gt;
Without tons of boilerplate to convert between error types.&lt;/p&gt;

&lt;p&gt;Libraries like &lt;code&gt;thiserror&lt;/code&gt;, &lt;code&gt;anyhow&lt;/code&gt; or &lt;code&gt;snafu&lt;/code&gt; are invaluable for this purpose. We decided to go with &lt;code&gt;thiserror&lt;/code&gt;, which simplifies the creation of custom error types with informative error messages.&lt;/p&gt;

&lt;p&gt;In most Rust use cases, you don't care that much about the underlying error type stack trace, and prefer to map it directly to an informative typed error within your domain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[derive(Debug,&lt;/span&gt; &lt;span class="nd"&gt;Error)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;WebhookError&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[error(&lt;/span&gt;&lt;span class="s"&gt;"error comparing signatures"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;SignatureComparisonFailed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;#[error(&lt;/span&gt;&lt;span class="s"&gt;"error parsing timestamp"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="nf"&gt;BadHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;#[from]&lt;/span&gt; &lt;span class="n"&gt;ParseIntError&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nd"&gt;#[error(&lt;/span&gt;&lt;span class="s"&gt;"error comparing timestamps - over tolerance."&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="nf"&gt;BadTimestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;i64&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nd"&gt;#[error(&lt;/span&gt;&lt;span class="s"&gt;"error parsing event object"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="nf"&gt;ParseFailed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;#[from]&lt;/span&gt; &lt;span class="nn"&gt;serde_json&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nd"&gt;#[error(&lt;/span&gt;&lt;span class="s"&gt;"error communicating with client : {0}"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="nf"&gt;ClientError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Investing time in crafting clean and informative error messages greatly enhances the developer experience and simplifies debugging. It's a small effort that yields significant long-term benefits.&lt;/p&gt;

&lt;p&gt;However sometimes, even more in SaaS use cases where logs stays outside of the user scope, it makes a lot of sense to keep the full error chain, with possibly additional context along the way.&lt;/p&gt;

&lt;p&gt;We're currently experimenting with &lt;a href="https://github.com/hashintel/hash/tree/main/libs/error-stack" rel="noopener noreferrer"&gt;&lt;code&gt;error-stack&lt;/code&gt;&lt;/a&gt;, a library maintained by hash.dev that allows exactly that, attaching additional context and keep it throughout your error tree. It works great as a layer on top of &lt;code&gt;thiserror&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;It provides an idiomatic API, actualling wrapping the error type in a Report datastructure that keeps a stack of all the errors, causes and any additional context you may have added, providing a lot of informations in case of failure.&lt;/p&gt;

&lt;p&gt;We've encountered a couple of hiccups, but this post is far too long already, more on that in a subsequent post ! &lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Building our SaaS with Rust has been (and still is) a journey. A long, challenging journey at start, but also a pretty fun and rewarding one. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Would we have built our product faster with Scala ?&lt;/strong&gt; &lt;br&gt;
Certainly. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Would it be as effective ?&lt;/strong&gt; &lt;br&gt;
Maybe.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Would we still be as passionate and excited as we are today?&lt;/strong&gt; &lt;br&gt;
Probably not.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rust has pushed us to think differently about our code, to embrace new paradigms, and to constantly strive for improvement.&lt;br&gt;
&lt;strong&gt;Sure, Rust has its rough edges&lt;/strong&gt;. The learning curve can be steep, and the ecosystem is still evolving. But that's part of the excitement.&lt;br&gt;
Beyond the technical aspects, the &lt;strong&gt;Rust community has been an absolute delight&lt;/strong&gt;. The welcoming atmosphere, the willingness to help, and the shared enthusiasm for the language have made this journey all the more enjoyable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExazJlZGppYjY5M3RwOG5sdHdudW94dzk4eXczZm5iMmN0YWUzdG10NyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/sn39fEb1LcHPGQ4b6h/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExazJlZGppYjY5M3RwOG5sdHdudW94dzk4eXczZm5iMmN0YWUzdG10NyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/sn39fEb1LcHPGQ4b6h/giphy.gif" width="251" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, if you have the time and the inclination to explore a new and thriving ecosystem, if you're willing to embrace the challenges and learn from them, and if you have a need for performance, safety, and concurrency, then &lt;strong&gt;Rust might just be the language for you&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As for us, we're excited to continue building our SaaS with Rust, to keep learning and growing, and to see where this journey takes us. Stay tuned for more in-depth posts, or vote for which one we should do next in the first comment. &lt;/p&gt;

&lt;p&gt;And if you enjoyed this post and found it helpful, don't forget to give &lt;a href="https://github.com/meteroid-oss/meteroid" rel="noopener noreferrer"&gt;our repo&lt;/a&gt; a star! Your support means the world to us.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/meteroid-oss/meteroid" class="ltag_cta ltag_cta--branded" rel="noopener noreferrer"&gt;⭐️ Star Meteroid ⭐️&lt;/a&gt;
 &lt;/p&gt;

&lt;p&gt;Until next time, happy coding !&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>rust</category>
      <category>opensource</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
