Lessons Learned from Building a Server In Rust

Lessons Learned from Building a Server In Rust

Stefan Hodges-Kluck

by Stefan Hodges-Kluck on January 27, 2026

Now that my email newsletter is live, I want to share a little bit about how the project got me deeper into Rust, and what I’ve learned after building a production app in this language from the ground up. Last December, I came upon Luca Palmieri's Zero to Production in Rust, a book that teaches readers Rust by guiding them through building an email newsletter. Motivated by a desire to build my skills, along with a somewhat Luddite desire to learn from a good old fashioned book instead of a YouTube video or online course, I requested the book as a Christmas gift.

For those who aren’t familiar, Rust is a programming language that seeks to break barriers to writing low-level code–that is, code that is closer to the machine code that computers read when a software application is run. According to the Rust open-source book, the language is built to guide programmers through traditional crashes, memory leaks, and security holes of this type of code by providing a strict compiler that thoroughly checks code before runtime for issues. This strictness is by design, as compiler errors are much more preferable to runtime errors because they warn people of issues before applications start running software.

Palmieri’s book gave me a great opportunity to delve into building a Rust server from the ground up. I often get weary of simple “Hello World” and todo list demo apps when I’m following tutorials, so I really appreciate the opportunity this book offers to work on a real app. Palmieri does a good job covering the main requirements of an email newsletter service, and the book covers a number of important backend concepts, such as domain management, database access, CI/CD, user management, logging, error handling, and idempotency. Getting to tackle these issues in Rust has helped boost my skills both in this language and in fullstack development more broadly. 

Instead of following Luca Palmieri’s chapters to the letter, I changed some things in order to fit my project needs. I am deploying the app on Railway instead of Digital Ocean, because I wanted my service to connect to my blog that is already on Railway. I use Amazon SES instead of Postmark for email (for now) because it’s less expensive if I ever end up sending hundreds of emails per month (don’t laugh – a guy can dream! 😜). And because my service is private to my Railway project, I skipped over a chapter on restricting the newsletter to logged-in users. I made additional architectural decisions as well, including creating a database client to abstract away SQL operations, and restructuring some of the code to increase testability. These “side quests” took extra time, but they provided some excellent opportunities for me to wrestle with Rust syntax and think more carefully about what my code was doing each step of the way. 

To be sure, building in Rust was not the easiest way for me to get an email newsletter. While the language is meant to be accessible–and from what I hear, it certainly is more accessible than traditional low-level languages such as C++ and Assembly–it’s definitely a steep learning curve for programmers without any prior low-level programming experience. The Rust compiler’s requirements are significantly stricter than those of other languages, forcing programmers to consider how the code they write creates values on the heap, and how those values consume, transfer, and dispose of memory throughout an application’s runtime. This can result in a sometimes frustrating experience when writing Rust code, when I try to do something I can quickly knock out in a higher-level language but run into a tangled web of compiler errors, and start flailing around throwing & or * or as_ref() or clone() or #[derive(Copy)] or Arc or Box everywhere until hopefully the compiler stops yelling at me. Unfortunately, computers can smell fear, and the more you flail, the more errors you make. 😅 

Still, though, I'm grateful for the struggle, and for the lessons it has taught me. Moments wrestling with the Rust compiler have served as good reminders for me to stop and think more carefully about what my code is trying to do and why, so that I can approach the best solution to my problem. More broadly, this project has reminded me of why I program. I feel, especially now as tech giants and vibe coders hype LLMs that are bigger and faster and faster and bigger, that I can't say this too loudly, but the truth is, I like projects that require me to stop and take my time. When everyone is bragging about how great it is to have a “production ready” app in seconds, I kind of enjoyed building slow on this project, where a few hours’ work ended with something as modest as a passing test or a successful HTTP request to an endpoint. I've been able to build my skills gradually and get my hands dirty this way. 

If I just wanted an email newsletter, I could have probably made something a lot faster if I had just modified my existing Dart project, or if I had purchased an existing solution (of which I am sure there are several). But that would've defeated the purpose. For me, the love of programming has always been as much about the journey as the destination. Just as I feel more satisfied on a beautiful mountain overlook if I hike or bike up to the top than I do if I drive there, here too the time and effort spent to work my way up to the summit of this project has made me appreciate the end product all the more. I love that I was able to struggle and grow through building this thing out on my own. To me, the work of programming is both art and science, a challenge that requires both understanding of complex systems and a creativity to adapt those systems to solve specific problems. If you really want to do it right–and, more importantly, to know why you’re doing it right–this work takes time. 

In order to build my skills, I have tried to build this project without AI assistance as much as possible. But I have to confess that there were times when I found myself stuck in compiler hell and opened my agent window to ask for a little help. What I found was about the same as what I’ve found working with LLM assistance in other languages: it’s…okay, at times. If there’s something really stupid you’re missing in your code and you just can’t remember what you’re missing or where in the docs to figure it out (and God knows I have plenty of those moments), it can be nice to plop an error message into the agent window and get a quick and easy answer. But there were plenty more times when I would watch the agent spin its wheels in the mud, circling around 3-4 failing solutions until I told it to stop. When I have a specific issue I want an agent to solve, and can give detailed instructions on what to do, the LLM works pretty well. When the problem is open-ended and I just ask an agent to “fix it”, it will run around in circles. 

Now, it’s entirely possible that I just haven’t configured my super-high-tech-amazing-AI-coding-environment correctly, and that if I had the right LLM model, the right MCPs, the right documentation, and the right prompts, the results would be better. And I’m definitely learning more about how you can set up LLMs with the context they need to reliably produce good code. But if I did that on this project, I’m not sure I would have learned as much. Computers are pretty remarkable machines. It often feels to me like magical elves are making all of the digital conveniences we take for granted, and one of the greatest feelings of growth I’ve experienced is peeking under the hood and replacing the “magic elf” thinking with knowledge of how things actually work. There’s still loads about computers that I still attribute to the elves, but I like feeling like I can peek under the hood myself. It discourages me that so much of the hype around AI tools seems to take all this fun and give it to an LLM. 

I’m not saying there’s no place for AI-assisted coding. But I still want to take my time to get my hands dirty, to build, to tinker, and to learn. I’ve always absorbed skills and knowledge more when I write things down, and I think this applies to code as well. Software development seems to be turning away from a field in which you learn how to program a computer to do your bidding, and towards a field in which you learn how to prompt an LLM to program a computer to do your bidding. But in order to know how to validate the LLM’s output, you need to have a certain level of familiarity with your code. Writing things out yourself is a great way to build this familiarity. 

I appreciate the opportunity to have been able to build my skills in Rust and backend development by making this newsletter. Feel free to take a look at the source code, and go ahead and sign up for the newsletter by clicking “Subscribe” on the top of my blog page if you haven’t already. I still have loads to learn, both about Rust in particular and about programming in general, and I am excited to take the lessons learned on this project into the next things I make.

Subscribe to blog posts

Enter your name and email to receive new blog posts directly in your inbox.