Julia stability vs Rust for Scientific Computing

Hi everyone! I’ve been seriously considering investing time into Julia for scientific computing and operations research, and I genuinely want it to succeed: the promise of high-level syntax with near-C performance is incredibly appealing.

That said, I recently came across discussions like “What’s is bad about Julia” (updated as December 2025) or “Why I no longer recommend Julia” which mention issues such as bugs in Base and general instability in parts of the ecosystem (The fact that sampling from a probability density could produce an incorrect result is scary to me). I’m trying to understand how much of that still applies today.

For those of you actively using Julia in scientific or production contexts:

  • How stable is the language and its core libraries right now?
  • Are bugs in Base still a practical concern, or have things matured significantly in recent versions?
  • How confident are you relying on Julia for long-term, correctness-critical work (e.g., optimization, simulations, OR models)?

To give more context, my use cases within operations research include building my own solvers (especially metaheuristics) and calling them from Python, as well as running discrete-event simulations in a language that’s significantly faster than Python.

One thing that gives me pause is the comparison with Rust. Even though Rust is newer than Julia, it seems like nobody really questions the stability of the language itself, especially in terms of correctness guarantees. That contrast makes me hesitate.

I’m also weighing alternatives, and part of me wonders whether it’s unreasonable to consider Rust for operations research workflows, given the higher development overhead.

Would really appreciate candid perspectives—especially from people who’ve used Julia in non-trivial, real-world projects.

Hi @gunner, welcome to the forum :smile:

To future readers: These posts have a tendency to go off the rails somewhat, so please consider whether you have a useful experience (positive or negative) to share before posting. Let’s not get into a side conversation about various closed issues from many years ago.

Since you mention OR, I’ll reply as one of the maintainers of JuMP.jl and https://jump.dev.

How stable is the language and its core libraries right now?

Very. Julia’s v1.0 release was in 2018. Things have slowly matured, but there have been no breaking changes. Code that you wrote in 2018 would still work with the latest Julia version. Third-party packages have their own release cadence.

Are bugs in Base still a practical concern, or have things matured significantly in recent versions?

No, and to be honest, they never really were. All software has bugs. Even very expensive commercial software. Just take a look at the release notes of Gurobi: Fixed Issues in Gurobi Optimizer 13.0 - Gurobi Optimizer Reference Manual. There are multiple correctness bugs in every release. See also the 1.5k issues in SciPy Issues · scipy/scipy · GitHub. You shouldn’t trust any software to be correct without careful validation.

Most of the issues happened when you combined two unrelated packages together. Julia doesn’t have formal interfaces, so this can work magically, but it can also lead to issues if the packages don’t share a common set of assumptions. When combining packages it is important to trust but verify.

How confident are you relying on Julia for long-term, correctness-critical work (e.g., optimization, simulations, OR models)?

Very. How long is long-term for you? JuMP has been in development for 13 years. JuMP’s v1.0 release was in March 2022, and since then we have made no breaking changes. You might compare the stability of JuMP to alternative modelling languages in Python…

especially from people who’ve used Julia in non-trivial, real-world projects.

Take a look at some of the videos from old JuMP-dev workshops:

Lots of JuMP users are in the energy modelling space. Checkout my blogpost where we cover all the various thing that people have been doing:

Open Energy Modeling at JuMP-dev | JuMP.

part of me wonders whether it’s unreasonable to consider Rust for operations research workflows, given the higher development overhead

Take a look at the Clarabel solver: Home · Clarabel jl/rs

They have a Julia implementation, where they prototype new features etc GitHub - oxfordcontrol/Clarabel.jl: Clarabel.jl: Interior-point solver for convex conic optimisation problems in Julia. · GitHub

They also have a Rust implementation GitHub - oxfordcontrol/Clarabel.rs: Clarabel.rs: Interior-point solver for convex conic optimisation problems in Rust. · GitHub, which is a more polished version of the solver.

There’s no right answer to the Julia/Rust choice. It’s a trade-off and people will pick different points on the trade-off curve depending on what is right for them.

Hi @gunner,

I can’t comment about Rust, but perhaps I can offer some perspective about why I’m using Julia to modernise some (barely) F90 electromagnetic modelling code.

I’m not so concerned about speed. However, it was relatively easy to match an equivalent F90 implementation, and even easier to exceed it. The Julia version is about x2 faster and it’s considerably simpler from a cyclomatic complexity point of view. This suggests that it’s going to be easier to maintain in the longer term.

The OCD in me would like some sort of ISO standardisation like Fortran, c, c++, … but practically, it makes little difference. What is needed is a user community, a large and growing code base, and extreme caution by developers to preserve backward compatibility.

I haven’t had cause to question language stability. Bugs are always present in any code of moderate or greater complexity. However, I haven’t hit any, and the high-frequency release cycle adds confidence that any show-stoppers would be dealt with appropriately.

My only qualms have been difficulties producing standalone distributables. The developers are aware of this and it seems to be a goal. If this isn’t an issue for you, I’d have no hesitation recommending Julia.

$0.02.

D.

I do think Julia has serious correctness and maintainability issues, but it’s worth being precise about what exactly is the problem, and what Julia, by contrast, does well.

For example, Julia is pretty good about not introducing blatantly breaking changes. Not perfect, mind you [1, 2], but straight up breaking changes are more rare than in e.g. Python. It’s also unlikely that basic numeric computation is going to break: Julia is quite explicit and careful about these kinds of numerics. So, in that sense, if you write some Julia code doing some computation, you probably don’t have worry.

In that vein, my experience has been that the code I wrote in 2020 still works flawlessly, and the code that Kenta Sato wrote in 2017-ish worked perfectly when I took over maintenance. I generally experience way less spontanous breakage maintaining my Julia code than maintaining my Python code. A very low bar, to be sure, but do note that noone goes around talking about Python like it’s a mess of breakage.

I also think the idea that Julia is full of straight up bugs is wildly overstated. Julia’s libraries are typically much better tested than Python’s and, again, I tend to find less bugs in Julia’s packages than in Python’s; at least for similar levels of popularity. There are bugs, sure, but the idea that bugs are so much more plentiful in Julia that it makes your programs less reliable doesn’t match my experience at all.

That being said, my experience of practical stability is shaped by me being quite careful about writing maintainable code, and Julia doesn’t make that easy:

Until recently, there was no public/private distinction in Julia. That border is still very fuzzy. For example, public functions are regularly discovered never to have been made public, and it’s deeply inconsistent and uncertain what properties of public functions are actually stable. For example, is the precise signature of functions considered stable? What about the return type? What about the exceptions they throw? What about the fields of structs?

More broadly, once you get deep into Julia, at least I have gotten the feeling that everything is built on sand, because very little can actually be relied on, when you look in the details. A core developer may not be able to delete a function, but since typically, very little about a function is documented, there’s hardly any function you can really use with the certainty that it’s covered by API.

Part of this is the behaviour of Julia structs and functions that is severely underspecified and underdocumented. For example, after using Julia for nine years, I have yet to understand what type map returns, and to what extent that is documented. Or understand what the init argument to the reduce functions actually do. Or the principles by which Julia functions handle aliasing arguments. That’s not me being dense - that’s just because there isn’t any consistent behaviour to understand. As a consequence, whenever your use common functions like map in your code you just have to pray that someone doesn’t decide it actually means something different than you assumed.

Then there’s the way the whole runtime works which is also full of sharp edges. There are way, way too many compile time traps, so performance in Julia is always a risky bet. Inference is an opaque compiler detail; it’s unstable and tends to shift under your feet - what was inferred three years ago often isn’t inferred when you run the program again, making the performance profile of your code change over time. You will often feel like runtime performance depends on the compiler, the multithreading system implementation, and the garbage collector, all of which are shifting internals that are difficult to satisfy.

I minor nitpick; Rust slightly older than Julia; both first appeared around 2012, but Julia hit 1.0 in 2018, and Rust in 2015.

I’ll note that the Author of “What’s bad about julia” (@jakobnissen) also wrote What’s great about julia and in that post talks a lot about the upsides of julia for scientific work, especially in comparison to Rust.

Regarding “Why I no longer recommend Julia”, I think there’s pretty much a consensus amongst the community that while this article does point at real issues, these issues exist everywhere, the messaging in the article was pretty hyperbolic, and the way e.g. Hacker News crowds constantly trot out the article in any context to argue against julia to claim the language is bad or unstable is highly misleading.

I work with Neuroblox on making solvers for simulating large-scale brain activity, and before that I used it for research purposes in my Physics doctorate work.

  • I find julia very stable, and I am more than satisfied with the language’s execution of it’s SemVer compatibility promises.
  • Bugs happen, but I’m not sure I’ve ever run into language-level bugs that caused my numerical scientific code to silently produce incorrect numerical results, but such things are possible. I have encountered missing features before, had problems where two different features didn’t play well together, or interacted in ways that had subpar performance. The language developers have typically been very responsive to these issues though, and are especially responsive when something is silently just wrong.
    • I think the further one strays away from the language’s core competencies (scientific numerical code), the more likely one is to run into true bugs simply because there’s less eyes on this sort of stuff.
  • I’m pretty confident. There’s two things that I would note though:
    • The stability of peformance rather than the the stability of semantics or correctness is not as guaranteed. Performance stability is typically quite good in julia, but regressions do sometimes happen and are technically allowed to happen (though in practice, performance regressions are almost always treated like bugs by the julia devs). I don’t worry much about this.
    • There’s a small collection of ecosystem projects which are fundamentally tied to language internals (most notably Automatic Differentiation packages which often are very entangled with the compiler), and these packages sometimes will get into a rough spot, and will be routinely broken by julia releases. This is typically fine when the package is new, but once it matures, they start falling behind on keeping up with changes to the language internals and then start breaking. This is really unfortunate, and I hope we can start to do better. So I would start looking into whether or not your project needs to depend on the language’s internals, or depend on packages which depend on internals. This might influence how strongly I can recommend julia.

Rust is a cool language that has a lot of great ideas and powerful features, but I’d definitely not want to do the sort of development you’re talking about in Rust if I could avoid it.

Just to add a bit to the excellent answers you got already: for straightforward computations, as far as correctness is concerned, Julia is very stable and reliable. Code that runs correctly will very likely run correctly in future versions. While no software is every 100% bug-free, most computational bugs in Base and the standard libraries have been weeded out, thanks to a lot of usage and thorough testing.

Others have mentioned performance traps, which I agree with, though the tooling got much, much better over the years. I would say that it is good practice to add something like JET.jl to your unit tests from the very beginning, that way it will not catch you unaware if you get a compiler inference regression.

What is a bit shaky is packages that utilize internals, which may change from one minor version to another. In particular, while automatic differentiation can be stellar in Julia (look at Enzyme.jl and Mooncake.jl), but since it relies on internal representation these packages may take a while to catch up to the latest release.

Note that writing performant, idiomatic Julia code takes a while to learn. Fortunately, Julia is great for quick prototyping and refactoring too, so you have room to grow into the language.

See Metaheuristics.jl for a high-quality example.

Calling Julia from Python, and vice versa, is very easy, with essentially no custom glue code needed:

Though there may be more overhead and threading limitations than the more established workflow of calling compiled shared libraries (C++ or Rust) from Python.

I primarily work with FDA-regulated SaMD and had the same question about a year ago when we were looking to optimize hot paths in our Python codebase. As you can imagine, consistency and correctness of behavior is a pretty big deal when your outputs inform clinical decision making.

I haven’t found any real correctness issues in Julia or its runtime, but there have been some inconsistencies around things like allocations, type stability, interfaces, and task scheduling. That said, all of the software I write goes through an extended validation process that can take many months. Writing in Rust doesn’t mean your software is free from bugs or runtime inconsistencies either (Futurelock: A subtle risk in async Rust | Hacker News). When the outputs matter, nothing substitutes for rigorous testing at many levels. With Julia, the conventions and patterns you use matter quite a bit too: stick to well-understood concurrency and multithreading patterns, and enforce typing that leans closer to static than dynamic where it matters.

As greatpet mentioned, Python and Julia interop works well, with a few caveats around threading: last I checked, only the thread that calls jl_init in Python can safely call into Julia, which means only one Python-to-Julia call at a time. We ran into some other issues here when deploying to production (Julia’s runtime fighting with uWSGI). What worked well for us was using InterProcessCommunication.jl for shared memory along with a lightweight RPC system between Python and Julia. Even our shortest RPC calls take 100ms+, so the RPC overhead is relatively small compared to the computation being done. We use a similar setup for processing training data in our ML pipelines: Python launches a Julia sidecar process and offloads compute-intensive work to it over shared memory.

I mostly use JuMP and related packages profesionally for power system applications, and that has been an awesome experience. I can safely recommend it to my colleagues, and I do. If you are mostly a consumer of packages, and not a producer, then you are somewhat shielded from the internal inconsistencies mentioned above. For example if you are an OR person who just builds models with JuMP then your experience will be mostly painless, while getting all the benefits of a very mature but SOTA optimization package :slight_smile:

I’m a physicist, and I have been using Julia for research in quantum information (specifically Bell nonlocality and quantum cryptography) for a couple of years. Before I used mostly MATLAB and Python, but the performance of those languages was not good enough to solve some of the problems I needed, so I found myself occasionally writing C/C++. Which I abhor, so it was a huge relief to get really good performance in Julia (sometimes better than what I managed with C++) in a language that is a pleasure to write.

As for the talk about instability, the true part is a consequence of Julia’s lack of formal interfaces combined with generic code. For example, some package can define matrices with units, and you can try to use another package to take a Schur decomposition of such a matrix. Whether that’s going to work is impossible to know. You might get the correct result, an error, or a silently incorrect result.

I don’t think that’s a real problem, because it only happens when you mix funny packages like this. In other programming languages this is something you cannot even try. And mixing funny packages does work often enough that it’s a real source of power of Julia, the wealth of packages that you can use is unparalleled.

That said, Julia does have real problems, like the closure capture bug, which I hope will be fixed one day.

With Rust you get formal interfaces, you can be much more certain about whether some combination of crates will work. For me it’s not worth it, because I find writing Rust to be orders of magnitude slower than writing Julia - both because of the language itself and because of the lack of packages I need.

I asked a coding agent to find correctness bugs in the latest stable Julia release, then let it run for a bit. It found many examples in the standard library where a function, called with a realistic input value, returns the wrong result.

I possess neither Mythos access nor a strong math background, so this was done with Opus 4.7 and some of these reports may be noise. But it’s an experiment I would encourage anyone curious to conduct for themselves.

Reproducer Got Expected Why it matters
quantile!(reshape([1.,2,3,4,5],5,1), [.25,.5,.75]; alpha=0, beta=1) [1.5, 3.0, 4.5] [1.25, 2.5, 3.75] The beta keyword is silently ignored for matrix inputs; you get a different quantile definition.
foldr(*, Iterators.flatten([["a","b"],["c","d"]])) "badc" "abcd" Right-associative reduce over a flattened sequence doesn’t give the same result as over a pre-flattened array.
invmod(2, UInt(3)) 0x00 0x2 Switching one operand from Int to UInt flips the answer to 0, never a valid modular inverse.
rval = [0]; findmin!(rval, [0], [1]; init=false); rval [1] [0] The documented kwarg that says “keep my running result” has no effect; breaks streaming argmin. False positive?
findmin(sparsevec([1], [NaN], 2)) (0.0, 2) (NaN, 1) The universal “missing/invalid” sentinel disappears from sparse findmin/findmax. SparseArrays#714
R = givens(1.,1.,1,2)[1] * givens(1.,1.,2,3)[1]; R*(R'*[1.,0,0]) [0.85, -0.15, -0.5] [1.0, 0.0, 0.0] The most basic identity in linear algebra — a rotation times its inverse — fails.
A = UnitUpperTriangular([1 0; 0 1]); rmul!(A, Diagonal([2,3])); det(A) 1 6 An in-place scale silently does nothing through a UnitTriangular wrapper; det doesn’t change. LinearAlgebra#1600
dot([1.,0.], Symmetric(sparse([1],[2],[1.],2,2)), [0.,1.]) 0.0 1.0 Whenever x or y has a zero at the column of an off-diagonal entry, the contribution from that entry is silently dropped even though it’s mathematically nonzero. SparseArrays#713 SparseArrays#715
pinv(Diagonal([1.0, 1e-100])).diag [1.0, 1e100] [1.0, 0.0] (Not a bug, missing feature; see stevengj below) Pseudoinverse of near-singular data isn’t actually regularized: tiny entries become huge inverses (compare to the same on a dense matrix).
d = [1.,2.]; Diagonal(d) .= Diagonal(view(d, 2:-1:1)); d [2., 2.] [2., 1.] (Not a bug; see stevengj below) A .= B silently corrupts when B is a view of A through a Diagonal/etc. wrapper.

The pinv issue is known since 2022.

Edit: Here’s a link to a zip containing a larger set of LLM-generated bug reports as individual Markdown files.

What it reports if you ask the same for, say, numpy?

The pinv bug is known since 2022.

Not a bug, just a missing feature — pinv for Diagonal matrices doesn’t accept atol or rtol arguments to regularize the pseudo-inverse.

(Regularization — dropping small singular values — is not part of the definition of pseudo-inverse. Moreover, for a Diagonal matrix we can compute all of the singular values exactly even if it is badly conditioned, so a default nonzero drop tolerance doesn’t really make sense IMO.)

A .= B silently corrupts when B is a view of A through a Diagonal/etc. wrapper.

It’s documented that mutating functions (like broadcast!) may incur undefined behavior if you pass aliased arguments.

I’m going to step in here and ask that future replies very carefully consider whether they are adding to the conversation. I don’t want this to conversation to derail into specifics about individual features/bugs.

The focus of the thread is on the pros and cons of Julia and Rust.

I think it’s important to remark that yurivish is the author of the blog post “Why I no longer recommend Julia”, where he claims to have stopped using Julia since before 2016.

I’m going to moderate this thread quite strongly. Please avoid making personal remarks.

Let’s please assume that everyone here is acting in good faith. I am aware of the identities of everyone involved. (Future readers, @yurivish is the author of the post referenced by OP: " Why I no longer recommend Julia".)

I’ve pushed a fix here: consistent ordering for composition of Givens rotations · Pull Request #1601 · JuliaLang/LinearAlgebra.jl

(In practice, the ability to compose Givens objects seems to be hardly ever used — I spent a few minutes doing a survey of the Julia package ecosystem and only found this feature to be used in GenericLinearAlgebra.jl, which uses it in a way that doesn’t encounter the bug. Not surprising, since anyone using the buggy method would have noticed the incorrect result immediately — left-multiplying by the adjoint was composing the rotations in the wrong order.)