Python Is Fast Until It Isn’t: When Rust Becomes the Better Choice

· · Views: 1,449 · 6 min time to read

Python has long been a default choice for building backends quickly. But as workloads become more demanding, the question is no longer simply how quickly a team can build a service. It’s how far that service can scale before Python itself becomes part of the performance problem.

The AI boom is making that question harder to ignore. As companies integrate LLMs, RAG pipelines, and machine learning workloads into their products, Python backends are taking on increasingly demanding workloads. Teams are looking for ways to reduce latency, increase throughput, and make better use of infrastructure without giving up the productivity that made Python attractive in the first place.

That is where Rust enters the conversation as a tool for the parts of a system where Python has reached its practical limits. The real engineering challenge is figuring out where that boundary lies and how to introduce Rust selectively.

To explore where Python starts to struggle, when Rust actually makes sense, and how teams can adopt it incrementally, we spoke with Dmitry Orlyakov, a software engineer and backend performance expert with 12 years of experience building and optimizing production systems. An active contributor to the engineering community through podcasts, expert discussions, and industry events, including PyCon 2025 UK, Dmitry brings a practical perspective on the trade-offs among developer productivity, performance, and modern backend architecture.

1. Dmitry, you have been actively working with Python since 2014. What are the most notable changes you’ve noticed?

Dmitry Orlyakov:

Python has come a long way. When I first started working with it, we mainly used Python as a fast prototyping tool for basic web APIs and scripting. Today, it has become a default choice for AI/ML, data pipelines, and API-heavy backends. Another notable change is who is using it. You can see from job postings and GitHub activity that more experienced developers are moving to Python. Ten years ago, it was used much more often by beginners.

The backend ecosystem has also changed quite a bit. Many developers still use Django and Flask, but async frameworks like FastAPI have significantly changed how teams build services that handle high concurrency. Type annotations and structured request validation have also become much more common parts of Python development.

2. We’re seeing more and more Python tooling and libraries using Rust under the hood. How significant is that shift, and what does it mean for Python backend development?

Dmitry Orlyakov:

I’ve been seeing this a lot. Many developers don’t notice it directly, but Rust is becoming a bigger part of the Python ecosystem. Some of the performance-critical parts of modern Python tooling are implemented in Rust. Pydantic v2, Polars, Ruff, uv, tokenizers, and other tools use Rust for performance-critical parts, often through bridges such as PyO3.

What’s interesting is that you don’t have to choose between Python and Rust. You can keep Python as your main backend language and use Rust where performance really matters. That’s a different model from the older Python-plus-C/C++ approach, and I think we’ll see more of it as Python handles more demanding workloads.

3. So where exactly does AI come into play here?

Dmitry Orlyakov:

AI is changing backend architecture because these systems have very different performance characteristics from traditional API workloads. A Python service may now be coordinating model calls, retrieval pipelines, and long-running agent workflows, so latency and throughput aren’t just about how quickly Python executes code. You also have to think about how the service manages concurrency, external calls, memory, and observability across the whole workflow.

That’s where the Python-and-Rust combination becomes interesting. You can keep the application logic and orchestration in Python while moving specific components to Rust when execution speed or memory efficiency becomes a significant constraint. I don’t think AI is going to push teams away from Python. Instead, it makes it more important to understand exactly where Python is doing useful application work and where it is becoming a bottleneck.

4. When do you reach the point where Python isn’t fast enough anymore, and what makes Rust a good option at that point?

Dmitry Orlyakov:

Python is often fast enough for APIs and business logic, but it struggles with some CPU-heavy operations. Parsing large files, processing data, or running tight loops can become expensive at scale. Rust allows teams to move these sections into compiled code without replacing the entire Python application.

In the past, developers usually used C or C++ extensions for this kind of work. Rust offers stronger memory-safety guarantees and tooling that many developers find easier to work with. PyO3 and Maturin also make it possible to expose Rust code through a regular Python package.

5. Before reaching for Rust, how do you know that Python is actually the bottleneck? What other performance problems are often mistaken for Python issues?

Dmitry Orlyakov:

A slow Python service is very often not a Python problem. In production, I’ve seen much larger performance losses stem from how the code is written: repeatedly scanning large collections, creating objects that don’t need to exist, or choosing a data structure that doesn’t fit the workload. You can move that same code to Rust and make it run faster, but if the algorithm is wrong, you’ve essentially built a faster version of the same mistake.

The same thing happens at the infrastructure level. A service can appear slow in Python when the real bottleneck is the database. N+1 queries, excessive ORM calls, or operations that should have been batched can leave the application spending most of its time waiting. Network calls and external APIs create similar problems.

That’s why I would profile before changing languages. You want to know where the time is actually going. Is the CPU spending time executing Python code, or is the application mostly waiting on a database, a network request, or some other dependency? Once you have that information, the solution is often much simpler than a rewrite. A better query, a different algorithm, or removing unnecessary work can give you a bigger performance improvement than moving code from Python to Rust.

6. From your experience, what tells you that a Python performance problem is worth solving in Python, and when is it time to move that part of the system to Rust?

Dmitry Orlyakov:

The first step is to always measure the application and identify the exact bottleneck. If the service is spending most of its time waiting for a database or an external API, Rust is not going to solve the problem.

If the bottleneck is CPU-bound, I would first look at improving the Python implementation. A better algorithm or a specialized library may be enough. Rust starts to make sense when you have a clearly isolated part of the system that is still too slow after those optimizations, and the expected performance gain justifies the additional maintenance and complexity.

7. Once you’ve identified a bottleneck and decided to optimize or rewrite part of the system, how do you validate that the change actually improves performance?

Dmitry Orlyakov:

The first thing I look at is whether we’re testing something that resembles the real production workload. I’ve seen teams send the same request at a constant rate, test against a nearly empty database, or ignore normal user behavior. The results can look very precise, but they may tell you very little about how the system will behave in production.

It’s also important to define what you’re trying to improve before you start measuring. A team might focus on requests per second without defining acceptable latency or error rates. Without a clear target, it’s difficult to say whether a change actually solved the problem.

I’ve also seen language and framework comparisons become misleading because the conditions aren’t equivalent. One application might have a warm cache while another starts cold, or the services might be running on different hardware. Microbenchmarks are useful for understanding how fast a particular operation is, but they don’t tell you how an entire backend will perform. For architectural decisions, I’d put much more weight on realistic workloads and production-like measurements.

8. Where do you think this Python-and-Rust model is heading?

Dmitry Orlyakov:

I’ve worked with production systems where, after a detailed analysis and reworking of critical parts, performance improved by tens or even hundreds of times, depending on the workload. In some individual operations, the improvement was close to a hundredfold. That’s why I think the Python-and-Rust combination has a lot of potential.

I expect Python to remain the main language for application logic, while Rust is used for components that need more speed or tighter control over memory. We already see this pattern in data libraries and developer tooling, so the model is well established.

For Python developers, I’d start with Rust’s ownership model and error handling, then learn how PyO3 and Maturin connect the two languages. But the harder skill is knowing where to draw the boundary. Rust is useful for expensive, performance-critical processing, while ordinary backend logic is usually easier to keep in Python. Getting that boundary right matters a lot more than knowing every feature of Rust.

I expect this boundary to become more important as backend workloads get heavier. We’ll definitely see more teams keep Python at the centre of the application and introduce Rust selectively where performance becomes a real constraint.

Share
f 𝕏 in
Copied