Philosophy and strategy concerning stability

Hello alls !

I would like to share my modest opinion concerning the philosophy and the strategy of the Zig project. I’m not a software engineer, but I love zig as a hobby, and I hope I am offering a constructive feedback.

I believe the Zig project is very well managed:

  • Zig has clear guidelines for the development of the language.
  • Zig has a leadership who share and defend their values concerning the software ecosystem.
  • Zig has a foundation which ennacts these values for the zig project
    and its future.

But as the project progresses, I realize there’s a keyword which is becoming shadowy: stability. When we look back at public declarations, we discover that stability was a clear goal at first, but with time, it became more and more hidden by short term goals, until the long term objective seems to be carefully avoided in public presentations.

I guess it’s a word hard to honor when development is ongoing and still discovering opportunities and difficulties. But I think it is the word in which all the values of the Zig project come together – or at least, it is my impression by following the project for a few years.

I would like to encourage you to think twice about this trend in your strategy, and consider this opinion: technical difficulties to reach 1.0 do not mean stability is not your goal, and do not mean you can’t express a clear path to reach that goal. Here follow some ideas to consider.

  1. Zig’s roadmap to stability

I guess, having this «roadmap to stability» written somewhere would be a significant help for people to consider the Zig language and support the Zig project.

Such a roadmap does not have to define deadlines. It does not have to be written in marble either. It can evolve, and should probably be transparent concerning its changes. It could simply explain technical expectations (incremental compilation, async IO…), steps to fulfill (review of the standard library), etc. All it has to do, in my opinion, is to share a vision.

  1. Zig’s definition of stability

I guess a first attempt to define which behavior is expected from a stable Zig would also help people to phase their expectations with what Zig can promise.

I wish Zig’s management could attempt to define what stability means for the language, compiler, and standard library. Does it mean the language definition can still evolve? Is it a promise my code written for 1.0 will compile on 2.0 compiler? Does it mean bug fixes will be backported to previous versions of the compiler? Does it mean tools to auto-update one’s codebase? Or does it mean an ever evolving language with well documented changes?

Maybe that definition would lead to prefer another word than «stability» («sustainable» for example). Maybe will it be needed to offer a variety of words: what does stability could mean before 1.0, what will it mean after 1.0, what does it mean for the language, what does it mean for the standard library, etc.

  1. Zig’s strategy concerning stability

I guess a roadmap and a definition would be first steps toward a strategy concerning stability. Such a strategy could also define measures, tools or best practices, to enforce our definition of stability, or to help us approach it. With such a strategy, even if we do not reach «stability», we can still be proud of the efforts we are devoting to reach that goal. And I guess Zig could be innovative on that field too.

That was my modest opinion. I hope this feedback will appear constructive to your eyes.

Cheers !

4 Likes

stability is the goal, but its not a goal you want to finish quickly, once you declare stability making changes becomes much harder. Stability will come after the zig team has accomplished or discarded all goals which could cause breakage.

Andrew has stated multiple times, that come 1.0 breaking changes will be exceedingly rare, he can’t promise they won’t ever happen.

zig uses the semver versioning scheme, which is BREAKING.COMPATIBLE.PATCH (patch is often left out, as it’s usually not important) so 1.0 language and 2.0 language would be incompatible, that’s what those numbers signify.

But compiler and language versions aren’t the same, a compiler can change its args and apis, without affecting the language, so a 2.0 compiler could support a 1.0 language.

Even in the case that the language has a breaking change, there would likely be flags for backwards compatibility.

4 Likes

Tbh, I would rather have a proper deprecation strategy instead of a ‘stability promise’.

For instance D3D managed to become a better designed API (than OpenGL) exactly because the D3D team wasn’t afraid to essentially build a completely new 3D API in every major D3D version, while OpenGL tried to evolve in a backward compatible way by piling new features on top of an API designed in the early 90s, which already since 2010 was hardly sustainable anymore and resulted in an incredibly messy API.

One notable feature of D3D is that even though each new D3D version was a massively breaking change, MS still guaranteed that properly written games that were coded against an old D3D version still work on modern Windows versions (within reason, but D3D9 should still work). It’s also not required to use the latest D3D version, for instance it still makes a lot of sense to start new code on D3D11 than D3D12.

Also, I wouldn’t overestimate the value of API/feature stability in the day and age of the internet and package managers :wink:

Outside the Zig world I need to fix my code all the time too.

Update to the new Dear ImGui version? There’s a high likelyhood that I need to update my code too.

Update to a new C compiler version? There will almost definitely be new warnings which I need to either fix or at least suppress in the build system.

What’s important is that required changes are also actually breaking (e.g. no behaviour changes that continue to compile - I want such things to break compilation so that I know what code needs to be updated).

An evolving stdlib with ‘API versions’ also doesn’t necessarily mean that there’s a separate copy of the stdlib for each version. Instead the versioned APIs could be thin layer around a private core stdlib implementation.

8 Likes

I have the feeling my main concern is not addressed by your comments : official communication is becoming very shy concerning this topic. But I guess only few people can comment about this.

Thank you for your insight nonetheless ! I’m not in position to discuss technical choices, but it has been informative reading your opinion. :slight_smile:

Cheers !

1 Like

I don’t know how up-to-date this list is and I absolutely do not blame you for not finding it, but the Roadmap section of the 0.11 release notes (from 2023-08-04) has a step-by-step plan for reaching 1.0:

Here are the steps for Zig to reach 1.0:

  1. Stabilize the language. No more Language Changes after this.
  2. Complete the language specification first draft.
  3. Stabilize the Build System (this includes Package Management).
  4. Stabilize the Standard Library. That means to add any missing functionality, audit the existing functionality, curate it, re-organize everything, and fix all the bugs.
  5. Go one full release cycle without any breaking changes.
  6. Finally we can tag 1.0.

Currently we’re still at the first step. Everything about the language, build system and standard library can and will change if the core team believes it’s necessary to improve Zig, and many of those changes will be very invasive and break your code.

The core team will generally try to make updating from one version to the next easy through things like letting zig fmt automatically update outdated language constructs or by keeping around deprecated standard library APIs for one release cycle, but this isn’t always technically possible when there’s no unambiguous upgrade path (and sometimes they simply forget or don’t care).

If you decide use Zig today you need to be conscious of that there will be breaking changes and that you will need to occasionally comb through and rewrite parts of your code as the language evolves. It’s also a good idea to actively pay at least a bit of attention to Zig communities and the week-by-week language development process so that you can be aware of upcoming breaking changes in advance, plan ahead and make upgrading less frustrating. If this is a problem for your project and stability is crucial then you probably shouldn’t use Zig in its current state.

11 Likes

:+1:

I’d like to refer to two other examples, Rust and Unicode, in this matter.

When learning Rust, I discovered some issues in its std lib (e.g. issues with AsRef in Rust) that seem to be difficult or impossible to fix, even though Rust follows the concept of “Editions” that were meant to allow backward-compatible fixes.

The idea there is:

Editions do not split the ecosystem

Despite the concept of editions, I don’t think the aforementioned problem was solved (at least the respective Rust issue, that goes back to 2017, is still open).

I don’t know if Rust and Zig compare well here. I feel like Rust’s complexity both increases the

  • chance of mistakes being made
  • difficulty of fixing mistakes

Nonetheless, I think it’s important to have ways to fix previous mistakes being made.

An example of “We promise stability! … Uh oh… (except in these cases)” is Unicode, where normalization stability guarantees had to be given up because the errors were just too bad, while at the same time stability is still held up for cases where errors were just “a matter of dubious interpretation”.

Ideally, I’d hope Zig could avoid running into similar issues. I believe “let’s wait with stabilization” is one way to get closer to that goal, but not sufficient. The other way is providing proper migration/deprecation paths. Afterall, updating our code and our dependencies on a regular basis is something we have to get used to, at least when our software is exposed to the internet.

Personally, although I know this is unlikely, I would like to see stdlib versioned separately from the language.

My reasoning is basically that the stdlib is itself written in Zig. When Zig reaches 1.0, then the stdlib will be written in stable, standard Zig.

So it would be practical to deprecate library versions by moving them to their own repo. Code which doesn’t want to upgrade to a new and improved version of that library can make some modest changes to build.zig.zon and some import statements and carry on indefinitely.

Tying the stability guarantees of the language to those of the standard library seems to inevitably result in deprecated functionality getting carried forward just to prevent a major version bump. I wouldn’t be unhappy if in 2032 Zig was still in the 1.x era but the standard library was on to 3.x or whatever. Maybe even a yearly edition release, who knows.

There are advantages to a stable standard library as well, but I consider those less compelling than what we get from a standardized and stable language.

Regardless, it’s not time for Zig to be valuing stability over growth and maturity. We’ll get there.

8 Likes

I have a feeling you won’t get the answers you seek yet as it’s still being defined in loop with development

Each 0.x breaking change the core team makes is a lesson that helps them shape this future. It’s essentially practice, as they’re allowed themselves to make mistakes, to learn and improve from. So it may be too early to expect such clear definitions from them (and if they provided, maybe too early for us to trust them?)

It’s a bit like looking back at code you wrote last month and shuddering - you’re continually improving and learning, even after many years of experience …

I liked this post and like to think it has encouraged discussions that will help shape the answers you seek.

I want zig 1.x to be “boring”. I want to write it once. and know I can come back to it decades later without feeling the pain that “modern” development throws on me.

9 Likes

This is an insightful understatement.

1 Like

I don’t know how up-to-date this list is […]

I did not remember it was written there, but I do remember the content of this list. And I wonder indeed if it’s still accurate.