I find it hard to develop in Julia

Julia is awesome to use, absolutely. But as an application developer, Julia can be frustrating. I wanted to write a post to outline my view on this. I write this post in a subjective tone because I want to share how I feel, because I think that might be useful to the community. My hope is to make my voice heard to Julia’s decision-makers, and to hear from other developers.

In short, I find Julia an unstable and difficult platform to develop for. Precompilation is slow, developer tooling is lacking, and with every Julia release, I fear that my packages will stop working (they often do). The maintenance burden from compatibility work is taking a toll on me – to the point where I am demotivated from starting new Julia projects.

New releases are scary

Julia makes too many breaking changes. Some minor releases have cost me hundreds of painful hours of compatibility work. With every Julia release, I fear that my project might be over, and I wish that the release gets delayed as long as possible. That’s not how I want to feel about my favourite language.

Julia regularly makes big internal changes in minor releases that break (important) packages. There is testing infrastructure to prevent this (PkgEval), but my experience is that PkgEval is not always called when needed. This means that package breakages are missed. And it seems that once your package (or a dependency) breaks, all future PkgEval runs will skip your package.

It’s up to me, the package developer, to test my package with the Julia prereleases (alpha, beta). This feels unfair: I put my trust in an ecosystem – spending my time developing a (popular) package – but now I have to spend time debugging, reporting and working around Julia compatibility issues, with no migration guide. And the end result? If you’re unlucky (like me), a slightly worse experience for my users with higher startup times and a higher memory footprint.

Other languages

I regularly work in both Julia and (trigger warning) JavaScript. Working in JavaScript is more peaceful because of its amazing developer tooling, but stability stands out the most. I can write a large, complex codebase, and I know that it will keep working 10 years from now with little or no maintenance. Packages might change, but the language runtimes (browsers) are extremely stable – my code will keep doing the same thing. The web ecosystem makes exciting developments, but not at the cost of my current work. For this reason, we now usually write new PlutoJL features in JavaScript instead of Julia, to keep our project sustainable. When Julia code breaks, we replace it with JavaScript if possible.

I need more hope

When I struggle with the developer experience of Julia, I am sometimes left feeling like this was somehow my fault. I used the API wrong (but there was no clear documentation). I used internal API (because there was no public API). Precompilation has been improved (but my package starts slower). The change is “not a bug” (but it still broke my package). In these interactions, I feel left behind.

My packages break in the name of progress, but progress towards what? Julia shouldn’t be a platform for compiler experiments. I would be happy to accomodate internal changes if they improve the developer experience. We still need reliable and easy-to-use [Ctrl+C interrupt, static type checking, breakpoint debugging, profiling, friendly error messages, WebAssembly]. Because I think that Julia is great as it is. Let’s just make it easier and get people to use it! In my eyes, Julia’s main issue is lack of adoption – not lack of a better memory layout, lack of compiled use, etc. Stability and developer experience is key here.

That’s it! Does this resonate with you, or is your experience different? I’m curious to hear!

19 Likes

I see where you’re coming from, but to be fair I think you’re in a very unique position to have Julia releases be so breaking.
As far as I can tell, this is due to a deep integration with semi-private APIs in Julia, concerning Pkg, expressions and eval.
Makie, with around 150 dependencies and a huge amount of Julia code usually has a one line change which breaks on new releases, which is usually fixed by outside contributors before the new Julia version even gets released - the 150 dependencies also seem to be magically fine on every Julia release. This isn’t because I’m a genius or anything, this is mainly because Makie and its dependencies don’t need to call into less stable Julia APIs.
The way Pluto creates a completely new way to do package management seems to be in a fight with the standard Pkg API almost by design.
Same goes for the expression parsing for figuring out the variable assignments I suppose? I’ve created similar packages, which were “hacking” Julia by calling into private APIs or relying on behavior which wasn’t guaranteed, which I had to abandon after releases which had too many “breaking” changes - which weren’t considered breaking by the developers, which I think was true by how the stable API of Julia is defined - still super annoying of course.
I really see only one way forward to improve the situation, which is to make what you need into a public stable Julia API - which, admittedly, may be even more work than fixing the breaking changes on release. I guess a thorough review of what can be moved to a more stable API could also help, but I guess that has been tried or is at odds with the features you want to offer.

To put things into perspective: I think I have never spend more than 10 minutes on adapting Makie for a new Julia version in the last 6 years or so as far as I can remember. On other tiny packages, which where heavily doing things “on the edge”, I might have spend days on an upgrade.

7 Likes

Totally agree. Human and financial resources around Julia seem to always target an advanced audience (e.g., PhDs doing SciML stuff); and IMHO this is not the audience that the language should be focusing on at this point in time.

3 Likes

There’s some things here I really agree with, but also some things here that I think land a bit off with me.

When you do use internal APIs it becomes your responsibility to deal with that when those internal APIs inevitably change. I get that this is frustrating though, and I wish the julia devs were better about at least communicating when they’re changing internal APIs, so that people who have no choice but to use them can at least adapt.

IMO the most reasonable thing you can do is identify internal APIs you’re using, and then enter into conversations with the julia devs about why and how the public APIs just aren’t enough for you, and try and figure out together if a public, stable API can be made that fits your use-case. This won’t solve your problems over night, but hopefully it can eventually get Pluto on a more stable foundation of public APIs.


For better or worse, Julia language development happens primarily in response to the acute needs of developers. If someone is working on something and encounters a problem, they work on a change to the language to fix it.

Pluto is a project that had a lot of interest from developers early on, but various design choices and communications from you (which of course you are completely within your rights to make), have made it clear that you dont see pluto as a tool for developers, but as an educational tool, and if it doesn’t fit the use-cases of developers who are interested in it, then that’s just too bad, go use something else.

While this is a totally reasonable decision to make, it does have the side-effect that it makes it so that the people who tend to develop the language are not paying much attention to Pluto and don’t feel they have a personal stake in helping make public-facing APIs for Pluto to use or to add new language features that Pluto is developing, because it doesn’t actually help the cases they’re working on.


Regarding this:

I’m a little bewhildered. Why shouldn’t julia be a platform for compiler experiments? It literally always has been essentially a compiler experiment, and has always been attractive to people interested in using compiler experiments to solve hard problems. While I get that maybe the changes to the language lately haven’t been particularly helpful to you, there’s been a lot of stuff that have been tremendously helpful to other domains or applications.

Likewise, other people might not care at all about the stuff you list as very important things missing from the developer experience (though most would agree they’re certainly nice to have!).

6 Likes

About the interaction with Pkg specifically, we also had a non-fun experience in BinaryBuilder.jl, where we’ve been stuck for years to Julia v1.7 (yes, that version released in 2021), because BinaryBuilder.jl taps into Pkg internals, which are intentionally non-stable. I’m impressed Pluto.jl managed somehow to keep up with newer Julia versions :smiley:

However we eventually had a much better experience lately when, instead of fighting every single version of Pkg, we had a discussion with the Pkg developers to have a set of tests in Pkg representing the BinaryBuilder.jl’s Pkg workload, after getting help from them to clean up the use of the Pkg API inside BinaryBuilder.jl. The result is that now BinaryBuilder.jl should work with Julia v1.12, v1.13, v1.14, in addition to v1.7, four minor versions supported at the same time is a record! And the integration tests inside Pkg should help with better future support, with fewer fights involved along the way.

10 Likes

Lack of AOT compilation directly leads to lack of adoption from people that need it for their use case. I’m glad this was eventually addressed.

3 Likes

Julia updates can feel very positive (e.g., 1.10 was a great release all around), but they can indeed feel frustrating…

Breaking Changes
Actually, most of Julia minor releases are breaking in practice! This is not my subjective feeling, but I checked the situation a few years ago. Basically, load the General registry state as it was at Julia 1.x release and try instantiating a nontrivial environment on Julia 1.(x+1). Sometimes it doesn’t even instantiate, sometimes instantiates but doesn’t work.
Typically, breakage is indeed localized and not too hard to fix – but it’s unambiguously breaking.

Performance Regressions
of two kinds: precompilation time has been growing steadily over the past years with no sign of reversing. For UX reasons, I personally continue using 1.10 – and worry about the time when it becomes unsupported and one will pay 2-3x for TTFX.
Another kind of performance regression is inference not inferring the type sometimes, or type-unstable code becoming slower, etc – this does happen quite often as well.


All of these issues were discussed already, and they are generally recognized – the question is mostly in the priorities.

4 Likes

At work, I definitely struggle with Julia upgrades due to undocumented breaking changes.

The most recent example: in Julia 1.12, apparently the way command macros worked changed, which broke SQLStrings.jl, which LibPQ.jl (the most popular Julia postgres library) relies on. That’s not a huge deal in itself, but I didn’t see this mentioned anywhere in the release notes or highlights. If there’s a place that tracks breaking changes, that would be great to know before upgrading.

1 Like

As Julia matures, maybe the community as well as core devs can eventually settle on the LTS as the main recommended version.

1 Like