Stream: ideas

Topic: Monorepo?


view this post on Zulip Anton (Aug 28 2024 at 12:32):

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 :)

view this post on Zulip Sam Mohr (Aug 28 2024 at 16:19):

In short, this is definitely what we should be doing. I'll expound when I get to the office later

view this post on Zulip Ayaz Hafiz (Aug 28 2024 at 16:37):

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.

view this post on Zulip Richard Feldman (Aug 28 2024 at 16:44):

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!

view this post on Zulip Ayaz Hafiz (Aug 28 2024 at 16:44):

yeah, that's what i mean

view this post on Zulip Richard Feldman (Aug 28 2024 at 16:44):

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

view this post on Zulip Richard Feldman (Aug 28 2024 at 16:45):

and I think having them be standalone just like every other platform makes them more valuable in that way

view this post on Zulip Richard Feldman (Aug 28 2024 at 16:46):

also having the issues be separate is nice in this case too I think

view this post on Zulip Anton (Aug 28 2024 at 17:32):

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.

view this post on Zulip Anton (Aug 28 2024 at 17:33):

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

view this post on Zulip Anton (Aug 28 2024 at 17:40):

we could even just git clone in CI directly, wouldn't even need submodules for that!

Yeah, this is simpler to set up than a monorepo, we could start with this

view this post on Zulip Brendan Hansknecht (Aug 28 2024 at 19:13):

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

view this post on Zulip Brendan Hansknecht (Aug 28 2024 at 19:13):

So we pulled many examples out of the roc repo and attempted to merge them to use less platforms

view this post on Zulip Brendan Hansknecht (Aug 28 2024 at 19:13):

I think a monorepos would not be the right solution for roc

view this post on Zulip Brendan Hansknecht (Aug 28 2024 at 19:14):

I think roc versioning is probably what we need. That should decouple and remove a lot of pain

view this post on Zulip Sam Mohr (Aug 28 2024 at 19:15):

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

view this post on Zulip Brendan Hansknecht (Aug 28 2024 at 19:17):

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.

view this post on Zulip Sam Mohr (Aug 28 2024 at 19:18):

You're right. As much as it would avoid out-of-sync issues, the builtin-task PR would be like 200 files

view this post on Zulip Luke Boswell (Aug 29 2024 at 03:21):

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:

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 03:53):

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 #ignore that 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.

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 03:54):

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

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 03:54):

I think this should be dealt with by adding real versioning to roc.

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 03:55):

We will have a nightly, but every time we make a breaking version, we will also attempt to update a version flag.

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 03:56):

Otherwise, every idk, week? we can just add new non-breaking changes to the a new minor version of roc.

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 03:57):

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)

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 04:00):

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.

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 04:01):

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.

view this post on Zulip Luke Boswell (Aug 29 2024 at 04:08):

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.

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 04:12):

Not sure the exact syntx we would use but roughly 0.0.0-alpha-{breaking}.{minor} I think is the proposed versioning

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 04:13):

Or maybe just alpha-{breaking}.{minor}

view this post on Zulip Luke Boswell (Aug 29 2024 at 04:36):

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?

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 05:03):

I feel like given we want version numbers from the other ideas thread, this makes a lot of sense to do now.

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 05:10):

As for expectations. I expect that:

  1. We have 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).
  2. We maintain exactly 1 recommended version that we expect all platforms (outside of those in roc-lang/roc), packages, and users to use.
  3. Every x amount of time, we automatically push a new minor version. It is expected that all platforms/packages/users should be able to automatically upgrade. Would be best if this just automatically pushed a new version after a daily job to test the core platforms and packages. If they all work it just pushes. Otherwise, this could be triggered weekly but I think CI systems should attempt to just upgrade to a new minor version.
  4. If we have a breaking change, we update the breaking change number. And just keep the core roc repo moving. Platforms and packages have time to manually update. Once our core platforms/packages are updated, we set the breaking change version to the recommended version.

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 05:14):

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.

view this post on Zulip Luke Boswell (Aug 29 2024 at 05:33):

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.

view this post on Zulip Luke Boswell (Aug 29 2024 at 05:37):

Actually... how do releases work for platforms like basic-cli and basic-webserver?

view this post on Zulip Luke Boswell (Aug 29 2024 at 05:37):

and also PR's...

view this post on Zulip Luke Boswell (Aug 29 2024 at 05:37):

Does CI in the external platforms target the recommended version or the latest roc main?

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 05:38):

I expect them to use the recommended version.

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 05:38):

Then they would update the the prerelease. At which point we will label the prerelease as recommended

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 05:39):

Oh, I guess for this plan to work, we would need to remove all basic-cli examples from the roc repo.

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 05:40):

Cause otherwise we are stuck in a versioning dependency loop.

view this post on Zulip Luke Boswell (Aug 29 2024 at 05:48):

For Roc version format, how does 0.0.0-<alpha>.<breaking>+<commit> look?

e.g. 0.0.0-alpha.12+479feca

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 05:48):

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

  1. roc-lang/roc updates and creates a prerelease
  2. platforms at a slower pace update to a prerelease
  3. the prerelease becomes the recommended release

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 05:49):

how does 0.0.0-alpha.<breaking>+<commit> look?

I like that

view this post on Zulip Luke Boswell (Aug 29 2024 at 05:52):

Brendan Hansknecht said:

.. remove all basic-cli examples 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.

view this post on Zulip Luke Boswell (Aug 29 2024 at 05:53):

They're all in -p roc_cli at the moment, it wouldn't be hard to pull out into a separate module.

view this post on Zulip Luke Boswell (Aug 29 2024 at 05:55):

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).

view this post on Zulip Luke Boswell (Aug 29 2024 at 06:01):

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.

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 06:04):

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.

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 06:06):

Luke Boswell said:

Brendan Hansknecht said:

.. remove all basic-cli examples 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.

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 06:07):

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.

view this post on Zulip Luke Boswell (Sep 03 2024 at 22:47):

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.

view this post on Zulip Richard Feldman (Sep 03 2024 at 22:51):

hm, can you elaborate on that? I'm not sure what the symptom is there :big_smile:

view this post on Zulip Luke Boswell (Sep 04 2024 at 00:34):

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.

view this post on Zulip Richard Feldman (Sep 04 2024 at 00:46):

ah gotcha! :thumbs_up:

view this post on Zulip Luke Boswell (Sep 04 2024 at 00:52):

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.

view this post on Zulip Anton (Sep 04 2024 at 09:59):

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