For breaking changes like with builtin task, updating basic-cli, basic-webserver, examples, basic-ssg is getting complicated. Other breaking changes can also require updating important packages like roc-json and roc-parser. This is only going to get more complicated over time. Putting these important platforms and packages in a monorepo in roc-lang/roc could simplify this process a lot.
I would probably still do issue tracking and releases in separate repos and perhaps mirror the respective source in e.g the basic-cli repo. This would allow people that only care about for example basic-cli to avoid source code and issues that are irrelevant to them.
If we make changes to for example the llvm backend, it will also be easy to immediately test those on a significant part of the Roc ecosystem. This is preferable over someone hitting an error when upgrading a platform to the newest version of Roc 20 commits down the line. This would require a bisect and spending time investigating an issue they may know little about.
I've never used a monorepo before so I'd love to hear from those who have :)
In short, this is definitely what we should be doing. I'll expound when I get to the office later
i would be careful not to tie the compiler too much to the ecosystem. if long term there will need to be infrastructure to test the compiler changes on the ecosystem, it may be easier to invest in such tooling now. there are ancillary benefits to avoiding this kind of coupling like avoiding big CI/CD configurations in one repo with disparate projects and having every merge be blocked on full-faith compatibility with the monorepo projects (this is particularly difficult if you need to also make sure those projects work with older versions of the compiler). I don't love submodules but if the goal is to test changes against the current state of the world in the ecosystem, it might be easier to just pull a subset of ecosystem packages during ci.
Ayaz Hafiz said:
I don't love submodules but if the goal is to test changes against the current state of the world in the ecosystem, it might be easier to just pull a subset of ecosystem packages during ci.
we could even just git clone in CI directly, wouldn't even need submodules for that!
yeah, that's what i mean
I am in general a big monorepo fan, but in this particular case something I do appreciate is that people are going to look to these repos as "how to do it" when it comes to platforms
and I think having them be standalone just like every other platform makes them more valuable in that way
also having the issues be separate is nice in this case too I think
people are going to look to these repos as "how to do it" when it comes to platforms
That is true, we could however make the platform folders so standalone that they only take a single Roc binary. The only change people would need to make would be to download a nightly and pass that instead of target/release/roc.
also having the issues be separate is nice in this case too I think
I agree, in case of a monorepo, I think we could still put the issues in the mirror repo
we could even just
git clonein CI directly, wouldn't even need submodules for that!
Yeah, this is simpler to set up than a monorepo, we could start with this
I think monorepos are great....though maybe not for open source work.
I think we should pull back to the examples where it became a hassle to update roc cause we have to update the platform of every single example
So we pulled many examples out of the roc repo and attempted to merge them to use less platforms
I think a monorepos would not be the right solution for roc
I think roc versioning is probably what we need. That should decouple and remove a lot of pain
Roc versioning would be more effort on our part, but would fix that issue. The reason why I'm more of a proponent of a monorepo for Roc is for the reason you point out: with a small active team, it's a lot of work to keep all of these platforms in sync
I think if we bless a very small number of platforms it is probably ok, but think about how much work went into updating basic CLI and basic webserver. Forcing them to update at the same time as a roc compiler change would lead to brutal gigantic changes. They are the changes that no one wants to do in a mono repo that requires understanding, working on, and updating other people's projects.
You're right. As much as it would avoid out-of-sync issues, the builtin-task PR would be like 200 files
Anton said:
For breaking changes like with builtin task, updating basic-cli, basic-webserver, examples, basic-ssg is getting complicated. Other breaking changes can also require updating important packages like roc-json and roc-parser. This is only going to get more complicated over time.
I've been thinking about this, and my recommendation is that we should consider a change that requires coordination between the platforms basic-cli/ssg/webserver (let's call that a breaking change in this context) and other (non-breaking) changes separetely. We should have a different approach for these as opposed to a normal PR. The reason why this is special is that we have other risks here that we need to consider.
For a breaking change, I think we should lower our standards and accept that we are more likely to introduce regressions or break things. We should prioritise landing the breaking change as soon as possible, and instead of trying to fix everything we should defer things for a follow-up where that can be done as a non-breaking change later. Landing a non-breaking change is easy and only requires a single PR, but a breaking change is more complicated and requires coordination between multiple platforms and releases to keep CI happy.
For example, the current Task as builtin PR currently has an issue in the False interpreter platform. We could #ignore that cli test, land the change in main, and then fix the False interpreter platform in a follow up PR. There is a risk that we are making the issue worse and it will be harder to isolate and track down later... but this also needs to be balanced against the risk of maintaining large breaking changes across the ecosystem. So there is a judgement call to be made here around the significance or likelihood based on our understanding from the symptoms or what we think the issue likely is. In this case, I think this issue is isolated to that specific test platform and not a fundamental issue with the implementation of builtin Task -- but I could be wrong.
Another related issue is when do we promote a pre-release to latest (or managing nightlies). I think we should continue to maintain the latest releases all in sync with each other and tested together like we do today, this ensures people using roc continue to have a reliable and stable experience. This is where I think we should maintain our high standards for testing and ensuring all bugs are ironed out.
I think we should continue to use the idea of a latest release as this reflects where the project is at, and I think it is less work than managing a numbered release. There are going to be a lot of changes in the future, don't expect stability right now etc.
I think we solve our stability problem by making it so that all the "latest" releases across the supported platforms are tested against the same specific commit of roc-lang/roc -- and not the head main. We separate the "testing" releases needed to land a breaking change in the compiler, from the releases needed to make a new "latest" for end users.
When we want to upgrade a platform like basic-cli with a new "latest" release, all we need to do is test it against the "latest" roc release. This is a specific commit in roc, and not the current head in main.
When we want to upgrade the "latest" in roc-lang/roc to the latest head, we test it against the latest release of all the platforms.
I think this is the key part of the process to be efficient with our time. I'm in favour of moving from a "nightly" to a "weekly" and adopting a new process of using a "pre-release" to coordinate upgrades with the platforms. The primary reason for this is to de-couple the work and enable multiple people to contribute, rather than trying to land everything all at once.
We cut a new "pre-release" of roc (using a newer commit) on a Friday, and then use that to make pre-releases for the other platforms etc, and when everything is happy, we promote them all to latest, otherwise we replace them when we try again next week.
This ensures we catch issues early and don't upgrade the "latest" versions people are using until key issues are resolved, while enabling fixes and upgrades to still land in main. We are separating the breaking changes from the non-breaking changes and apply different standards to them so we can move faster while still ensuring stability and reliability.
An example I'd like to use to illustrate this, is the recent segfault due to alignment that held up the basic-cli 0.13.0 release for a while. During the time we were investigating this issue, we landed a lot of fixes and upgrades into the latest main. We (well mostly Anton) investigated the issue and then when the fix was ready, we made a new release, tested everything and promoted it to the 0.14.0 as latest. I think this was the correct approach, we tracked down and resolved an important issue that would have likely been an unpleasant experience for users (who would experience random segfaults with no clear reason), while not blocking the progress of other changes and upgrades.
If we can de-couple the breaking changes from the "latest" releases, then I think we can also do this for upgrades across the ecosystem like builtin Task.
This is my idea, but I'm no expert on these things. I'm interested to know what others think, and hopefully we can improve the process a little. :smiley:
We should prioritise landing the breaking change as soon as possible, and instead of trying to fix everything we should defer things for a follow-up where that can be done as a non-breaking change later.
...
For example, the current Task as builtin PR currently has an issue in the False interpreter platform. We could#ignorethat cli test, land the change in main, and then fix the False interpreter platform in a follow up PR.
...
An example I'd like to use to illustrate this, is the recent segfault due to alignment that held up the basic-cli 0.13.0 release for a while. During the time we were investigating this issue, we landed a lot of fixes and upgrades into the latest main. We (well mostly Anton) investigated the issue and then when the fix was ready, we made a new release, tested everything and promoted it to the 0.14.0 as latest. I think this was the correct approach
I partially agree with an important caveat. If it is in the Roc repo (like false). I think we should do are best to avoid ignoring it. An ignored test is rotting code. It likely will never get fixed. If a test is too much churn to keep working, we should remove from the roc repo. That said, if the test is still valuable in general (like False, it has caught a ton of bugs over the years), we should put it in the examples repo or some other testing repo. This allows us to land the breaking change quicker but still keeps the core roc repo fully tested without lots of tests becoming code rot. I do think it is occasionally fine to ignore tests (especially small ones), but I think this is something we should be very wary of.
I do think we should decouple roc breaking changes from basic-cli, basic-webserver, and the ecosystem. Roc should be able to move faster and the other platforms can get updated later. Like what happened with basic-cli 0.13.0 -> 0.14.0 as you mentioned
I think this should be dealt with by adding real versioning to roc.
We will have a nightly, but every time we make a breaking version, we will also attempt to update a version flag.
Otherwise, every idk, week? we can just add new non-breaking changes to the a new minor version of roc.
The goal would be to keep basic-cli, examples, and friends up to date with the latest roc. For minor version changes, we can make them automatically pull in the new compiler. For breaking versions, we can manually update them and deal with any issues (including fixes that need to be added to roc)
As for which version of the compiler users are pushed to download, I think we should make it the latest version that all of basic-cli, basic-webserver and examples repo work on (we can make this list as large or small as we want). It would automatically update with each minor version change. With a breaking version changes (which is always manually triggered), we would wait to promote it until our set of core packages/platforms is working properly.
I think just tracking a breaking and minor change number in roc will solve most of our gripes here. We just need some minor manual process around labeling breaking changes (should be pretty obvious which they are cause they will touch all of the examples in the roc repo) and then labeling them as official releases once the main packages are updated.
For minor versions, it will be best effort that they don't break anything.
For nightly, they will just be nightly...who knows if they work or not.
Brendan Hansknecht said:
I think this should be dealt with by adding real versioning to roc.
Are you talking about Semver here? specifically the development 0.x.x versions.
Not sure the exact syntx we would use but roughly 0.0.0-alpha-{breaking}.{minor} I think is the proposed versioning
Or maybe just alpha-{breaking}.{minor}
I was specifically trying to think of a process that doesn't involve numbered versions. My concern is that it might encourage spending more time coordinating and upgrading releases (because there is now that expectation and we all want people to have a great experience) than adding features and fixing bugs.
With your proposed solution Brendan, do you see us having multiple releases up at one time? Like for roc maybe a nightly and a 0.0.0-alpha-12.1, and e.g. basic-cli 0.15.0, basic-webserver 0.8.0 etc?
I feel like given we want version numbers from the other ideas thread, this makes a lot of sense to do now.
As for expectations. I expect that:
roc-lang/roc), packages, and users to use.So I guess it is kinda a prerelease and recommended release setup, but roc can always keep moving and never waits for platforms outside the actual roc-lang/roc repo. We just define a list of core platforms/packages we want to ensure work and for breaking changes, we only make something a recommended release when those platforms/packages work.
Ohk, this is similar to what I was thinking above too. Thank you for clarifying.
I'm currently making some notes to explore it a little, thinking about each of the workflows involved, so this helps a lot.
Actually... how do releases work for platforms like basic-cli and basic-webserver?
and also PR's...
Does CI in the external platforms target the recommended version or the latest roc main?
I expect them to use the recommended version.
Then they would update the the prerelease. At which point we will label the prerelease as recommended
Oh, I guess for this plan to work, we would need to remove all basic-cli examples from the roc repo.
Cause otherwise we are stuck in a versioning dependency loop.
For Roc version format, how does 0.0.0-<alpha>.<breaking>+<commit> look?
e.g. 0.0.0-alpha.12+479feca
Fundamentally what I am trying to achieve is:
For non-breaking changes, everything can just updated roughly at once and people are pushed towards the latest version. Run CI that verifies all the core platforms/packages and automatically makes a new minor release.
For breaking changes
roc-lang/roc updates and creates a prereleasehow does 0.0.0-alpha.<breaking>+<commit> look?
I like that
Brendan Hansknecht said:
.. remove all
basic-cliexamples from the roc repo.
I wonder if we can include them, but use this as a check for the breaking bump. Like is it possible that if the breaking version been bumped, these are allowed to fail, but if it's the same they must pass.
They're all in -p roc_cli at the moment, it wouldn't be hard to pull out into a separate module.
The build-host PR touches all of these tests already, and currently needs updating assuming we land Task as builtin, so I could potentially roll that change into that PR too (which is easier than merging the changes and updating twice).
nightly which works with all tests in the roc repo but may be broken otherwise (Really just a convenience for testing on latest main without building from source).
For CI in the platforms could we use nix, and not use any of the native versions?
For a PR with nix it's really easy to update the roc version being used, update the commit rev in the flake and then nix flake update.
This could mean we don't need a nightly release -- which is one less thing we need to worry about.
For making a new platform release, we will still need to test and build using the native version instead of just nix. But we will have the pre-release 0.0.0-alpha.12+479feca or similar we can use for that, and it will be the same version that gets promoted to latest/recommended when everything else is ready.
For nightly I was more thinking it would be for users to mess around with the very latest changes, but yeah, it probably isn't needed in this new system.
Luke Boswell said:
Brendan Hansknecht said:
.. remove all
basic-cliexamples from the roc repo.I wonder if we can include them, but use this as a check for the breaking bump. Like is it possible that if the breaking version been bumped, these are allowed to fail, but if it's the same they must pass.
I would rather have a ci job that downloads a set of platforms annd packages for this:
For packages would just run roc test on them. For platforms would run their test suite.
So remove tests that depend on those from the roc repo and instead use those tests only for figuring out breaking changes, minor version changes, and eventual pre-release promotion.
Another thing we haven't discussed above -- we need to move the roc website out of roc-lang/roc to decouple it from basic-ssg.
hm, can you elaborate on that? I'm not sure what the symptom is there :big_smile:
If we make a big breaking change in roc, the basic-ssg platform also needs updating for the website. Similar to the use of basic-cli examples in the tests, these will be broken until there is a compatible release of the platform available.
This latest upgrade to builtin Task, I think we broke a bunch of links to the examples and we didn't realise.
So if we move the website out, it can continue to use an older commit -- or even just the latest build, until we have an upgraded basic-ssg.
ah gotcha! :thumbs_up:
Yeah, I could have worded my statement above more clearly. Was in between tasks and wanted to get the thought out before I forgot about it. It's not that we _need_ to move it out, but we need to think about how we manage the release it's using -- because it currently uses roc built from source. It may be easier to just use the latest build. I think @Anton will have a preference here.
Yeah, I think the website would work well in it's own repo, I think we can then set up netlify previews with CI which I'd love to have, we had problems with doing that on roc-lang/roc.
Last updated: Jun 16 2026 at 16:19 UTC