Deep Engineering #51: Francesco Ciulla on Rust, Go, and Service-Level Engineering Decisions
On Rust versus Go, latency-sensitive services, memory overhead, deployment workflows, and the backend constraints that shape language choices
Build Production-Ready AI Applications with Rust, Claude and Codex
Learn how to use Claude and Codex as development partners for building reliable Rust applications faster. This hands-on workshop with Francesco Ciulla shows how to scaffold, refactor, debug, test, and productionize AI-assisted Rust code with confidence.
Use code DEEPENG50 for 50% off.
✍️ From the editor’s desk,
Welcome to the 51st issue of Deep Engineering!
The Rust team released version 1.96.0 on May 28, shipping new Copy-compatible range types, stabilized assert macros, and Cargo security fixes. It is the kind of release that tells you something important about where the language is. Now on a steady six-week release cadence, Rust’s toolchain keeps getting more integrated, and each release moves the language further from its reputation as a difficult, specialist tool and closer to something engineering teams can simply rely on.
That maturity is part of what is changing the production calculus for engineering teams this year. Francesco Ciulla, author of The Rust Programming Handbook and head of developer relations at Zerops, has used both Rust and Go in production. His view on the Rust versus Go debate is neither dismissive of Go nor evangelistic about Rust.
Ciulla discussed how Rust and Go solve different backend problems, where Go still wins, where Rust’s flat latency and binary size arguments become genuinely decisive, and why his thinking on committing to Rust for a production backend has changed.
Today’s expert insights are based on the broader conversation about Rust adoption we had with Ciulla. You can read our previous issue or watch the full Q&A here.
Let’s get started.
Featured Newsletter: Java Tips and Tricks
Subscribe to Java Tips and Tricks on Substack for practical Java insights, modern Java features, software design discussions, and updates from the Java ecosystem.
→ Subscribe to Java Tips and Tricks
Expert Insights
Rust and Go Are Better Compared Service by Service
by Saqib Jan with Francesco Ciulla
Most engineering teams debating Rust versus Go for their next backend service are usually trying to answer the question too early, assuming the decision starts with the language rather than the service.
Francesco Ciulla, author of The Rust Programming Handbook and head of developer relations at Zerops, thinks that framing misses the point. The useful question is not whether Rust is better than Go, or whether Go is more practical than Rust. It is whether the specific service being built has constraints that make one language’s trade-offs more valuable than the other’s.
The distinction Ciulla draws is practical. Go still earns its place in cloud infrastructure, CLI tooling, hiring, and teams that need a service working quickly without introducing a new adoption burden. Rust, in his view, becomes much harder to ignore when a service is constrained by memory use, latency predictability, high-concurrency performance, or the need to remove whole classes of runtime failure from the system. That is why the Rust versus Go debate becomes less useful the longer it stays at the language level. The decision only starts to make sense when it moves down to the service level.
Most teams are asking the wrong question
Most engineers who have followed the Rust versus Go conversation online will have encountered it as a tribal argument, with advocates on both sides treating the choice as a matter of identity rather than engineering judgment. Ciulla rejects that framing, and part of what makes his view useful is that he is not arguing from a Rust-only position. He currently works with Go at Zerops, where the company’s CLI is written in Go, and he has run Rust services in production on his own projects.
“I would never say that Go is a bad programming language,” Ciulla says. “You can feel how powerful Rust is because we are talking about completely different scenarios and still Rust has something to say. But that does not make Go bad.”
The more useful framing, he argues, is to stop treating Rust versus Go as a general-purpose language comparison and start treating it as a service-level engineering decision. The question is not whether an organization should adopt Rust instead of Go. The question is whether a specific service in the system has properties where Rust’s trade-offs are worth paying for, or whether Go’s simplicity, ecosystem, and hiring advantages matter more.
That reframe changes the conversation because it moves the decision away from preference and toward evidence. Once the team is looking at the service rather than the language, the relevant questions become much more concrete. Is memory use a real constraint? Is tail latency a business problem? Does the service sit on a critical path? Does the team have anyone who can review Rust code well enough to put it into production safely? Without those questions, the debate quickly becomes ideology dressed up as architecture.
Go wins on hiring, tooling, and the Docker ecosystem
Ciulla is careful not to turn the comparison into an anti-Go argument. Go is a natural fit for CLI tools, cloud infrastructure tooling, and the Docker and Kubernetes ecosystem. Docker is written in Go and Kubernetes is built on Go. For teams building tools that live inside that ecosystem, Go is the default for reasons that have little to do with language preference and everything to do with integration, community, and the availability of patterns the team can learn from.
The hiring argument also runs in Go’s favor, and Ciulla thinks engineering leaders should be honest about it. “In terms of finding Go engineers, probably at the moment it is easier,” he notes, “because there are probably more of them.” For a company that needs to move quickly and cannot afford a long search for specialized Rust talent, that matters. Staffing is not separate from engineering judgment. It is one of the constraints that determines whether a technical choice can survive contact with production.
Go also has lower adoption friction in general-purpose backend contexts where the performance ceiling is not the binding constraint. If a team is building a service that needs to be working by the end of the week, deployed reliably, and understood by the next engineer who touches it, Go’s simplicity and the breadth of its ecosystem are real advantages. Ciulla makes the same point more generally when talking about technology choices under deadline pressure. “When you need something simple, and you’re familiar already with Java or JavaScript, why don’t you use it?” The same principle applies to Go for teams already operating in that ecosystem.
That matters because Rust adoption is not free. It requires a different mental model, a compiler that forces decisions earlier, and at least one person on the team who knows the language well enough to validate what is being shipped. For a service that does not need Rust’s performance profile, those costs may not be worth paying.
Join Francesco Ciulla to learn how to build production-ready AI applications with Rust, Claude and Codex. Register here
Rust wins on performance and it is not a close contest
Where Ciulla becomes more direct is performance. On raw performance, he does not see much ambiguity. “Check the benchmarks yourself and send me the link where Go beats Rust,” he says. “Sometimes they are at the same level. In terms of pure performance, there is no story.”
That bluntness is useful, but the more important argument in his interview is not about benchmark wins. It is about latency predictability. Languages that rely on garbage collection, including Go, Java, and Node.js, can introduce pauses when the collector runs. An HTTP request that arrives during one of those pauses may experience higher latency than one that does not, even though the service logic is identical.
“By not having a garbage collector on the backend side, you basically have flat latency,” Ciulla explains. “You don’t rely on luck, or on the user not being the unlucky one. It’s a problem that is removed.”
For most web applications running at moderate scale, that distinction may not matter enough to justify a language change. Ciulla’s point is not that every API should be rewritten in Rust. His argument is that services with strict latency requirements, high concurrency, or service-level objectives tied to consistent tail latency should be evaluated differently from ordinary backend services. In those cases, the lack of a garbage collector is not an aesthetic language feature. It changes the runtime behavior the team has to reason about.
The resource efficiency argument is equally concrete. Ciulla describes running a Rust web server in production and observing roughly four megabytes of RAM in development and five megabytes in production. On a one-gigabyte droplet, that means many small Rust services can sit idle at once without creating the same memory pressure a heavier runtime might introduce. That is not a benchmark designed to win an online argument. It is an operational fact that changes what an infrastructure team has to provision, monitor, and pay for.
That is where Rust’s case becomes strongest. Not when the team wants a language that is fashionable, and not when someone wants to win the Rust versus Go debate, but when a specific service is expensive, latency-sensitive, memory-constrained, or sitting on a path where runtime predictability matters.
Deployment changes the operational argument
One of the more practical points Ciulla makes is that Rust changes the shape of the deployment artifact. When you run cargo build on a Rust project, you get an architecture-specific executable binary. A Windows machine produces a Windows executable. A Mac produces a Mac executable. A Linux machine produces a Linux executable. You can also cross-compile by changing the target architecture through the compiler, which lets a team produce a Linux binary from another environment when the workflow requires it.
Ciulla’s preferred production workflow is to build the Rust binary directly inside the Docker image build process. “My flow is that I prefer to build the Rust binary directly when I build the Docker image,” he explains, “so I have something which is just deployable everywhere, a Linux executable running in a Docker container.”
The appeal is straightforward. The team gets a lightweight binary compiled for the right architecture, packaged inside a portable container. “The dream for operations is having an executable inside a Docker container,” he says. “You get something which is lightweight and can run everywhere Docker is installed.”
That matters for teams already containerizing services, which is most teams building at meaningful production scale. A Rust binary inside a container keeps the deployment artifact small while preserving the operational consistency teams expect from Docker. The container still matters because real systems rarely deploy one service by hand. They orchestrate many services, restart them, replace them, and scale them across environments. As Ciulla puts it, “We are not in the 90s anymore.”
The argument is not that Rust removes the need for containers. It is that Rust and containers fit together cleanly. Rust gives the team a compact executable artifact. Docker gives the team a repeatable deployment boundary. Together, they reduce some of the runtime and packaging overhead that teams accept as normal in other ecosystems.
Rust is ready for more production backends
The most interesting part of Ciulla’s position is that it has changed. Two years ago, he would not have committed to building a paid SaaS product with a Rust backend. A year ago, he was still hedging. In 2026, he removes the qualification entirely. “If I had a paid product, I would use Rust,” he says. “Let’s remove the probably.”
That shift is not based on general enthusiasm for the language. Ciulla describes himself as skeptical by default, and his advice throughout the interview is to try technologies directly rather than rely on what advocates or critics say online. His confidence comes from the maturity he now sees in the Rust backend ecosystem, especially around Axum, which he says he would now use in production if he were building a SaaS or paid product.
The surrounding toolchain also strengthens the case. Cargo handles dependency management, building, testing, and documentation in a single integrated workflow. There is no equivalent of the npm versus yarn versus pnpm decision that JavaScript teams often navigate before they even get to the application itself. Running tests is cargo test. The integration is not a small ergonomic convenience. For teams that have spent years carrying build-system complexity, dependency churn, and fragmented tooling across projects, a coherent toolchain reduces the amount of process overhead attached to the language.
Ciulla sees that as part of why Rust’s reputation can lag behind its current reality. The language still has a learning curve, especially around ownership, lifetimes, and the borrow checker, but the ecosystem around the language has become more integrated rather than more fragmented. For experienced developers willing to learn Rust on its own terms, that changes the adoption equation.
Start the decision with the service
Ciulla’s adoption advice was consistent across our interview. Do not rewrite everything in Rust. Do not introduce it because the internet says it is the future. Do not make the language the strategy. Start with one service.
The right starting point, in his view, is a critical service with a real performance, latency, or memory problem. It might be a login service. It might be an API under heavy load. It might be a component that slows the rest of the system down. The point is to choose the service where Rust’s advantages are tied to an actual constraint rather than a general preference.
That is also where organizational readiness becomes unavoidable. If a team has no one who understands Rust well enough to review AI-generated code or validate a production deployment, the language can become a liability. “Who decides if the AI-generated Rust service is okay to put in production?” Ciulla asks. “You need the validation of an expert.”
That expert does not make Rust special. It makes Rust normal. Every new technology needs someone inside the organization who can tell the difference between code that compiles and code that should be shipped. Without that person, the team is not adopting a better tool. It is creating a new category of production risk.
For teams that do have the right service and the right internal expertise, Ciulla thinks the timing has changed. Rust is no longer only a systems language that backend teams admire from a distance. It is becoming a practical choice for the services where its properties matter most. The mistake is treating that as a universal recommendation. The opportunity is knowing exactly where it applies.
The Rust versus Go decision, then, is not really a Rust versus Go decision at all. It is a question about constraints. If the service needs simplicity, staffing depth, cloud tooling familiarity, and quick delivery, Go may be the better engineering choice. If the service needs predictable latency, low memory overhead, strong runtime control, and a deployment artifact that maps cleanly to containers, Rust deserves serious consideration. The senior engineering move is not to pick a side. It is to know which job each language is being asked to do.
Go deeper with Francesco Ciulla’s book
The Rust Programming Handbook: An End-to-End Guide to Mastering Rust Fundamentals, a practical guide to Rust’s ownership model, memory safety guarantees, concurrency patterns, trait system, and real-world use in systems and web programming.
In case you missed
🛠️ Tool of the Week
cargo-nextest — a next-generation test runner for Rust that replaces cargo test with faster, more reliable test execution for production Rust projects.
Highlights
Runs each test in its own process, surfacing hidden state sharing and enabling better parallelism than cargo test’s single-process model.
Retries flaky tests automatically, reducing noise in CI pipelines without manual intervention.
Outputs machine-readable JUnit XML, compatible with most CI systems out of the box.
Version 0.9.137 added a JSON schema for user configuration, standardizing nextest settings across a workspace.
📎 Tech Briefs
Docker Desktop 4.77.0 released - Marketplace extensions now install by pinned manifest digest, reducing tag-mutation risk after publication.
Rust 1.96.0 released - New Copy-compatible range types, assert_matches! macro stabilization, and two Cargo security fixes for third-party registry users.
Go 1.26.4 released - Security fixes ship for crypto/x509, mime, and net/textproto alongside compiler and runtime bug fixes.
Docker Desktop security update - Two container-to-host code execution CVEs in the Model Runner inference backend patched, with a Linux kernel backport fixing a container privilege escalation.
rust-analyzer update - New diagnostics, predicate evaluation, and completion fixes improve daily Rust IDE feedback loops.
That’s all for today. Thank you for reading this issue of Deep Engineering.
We’ll be back next week with more expert-led content.
Keep building,
Saqib Jan
Editor-in-Chief, Deep Engineering
If your company wants to reach senior developers, software engineers, and technical decision-makers, speak to us about partnering with Deep Engineering.









the rust vs go debate always resolves the same way: teams pick go to ship this quarter and rust to stop getting paged next quarter