Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

To multi-repo or not #375

Closed
Reinmar opened this issue Dec 12, 2016 · 6 comments
Closed

To multi-repo or not #375

Reinmar opened this issue Dec 12, 2016 · 6 comments
Labels
resolution:invalid This issue is invalid (e.g. reports a non-existent bug or a by-design behavior). status:discussion

Comments

@Reinmar
Copy link
Member

Reinmar commented Dec 12, 2016

It's time we make some really final decisions. We've used multi-repo approach for the last two years and as we're closing to the release some of our decisions become irreversible. This will be one of them – whether we go with a multi-repo multi-package, single repo multi-package, or single repo single package.

Cool stuff

I've written an article in the past explaining our choice: https://medium.com/content-uneditable/why-we-chose-a-multirepo-architecture-for-ckeditor-5-54c7e50af1f5#.2a7c74lup

Summing up and adding few more thoughts:

  • CKEditor 5 is made of libraries which create a framework, based on which you can implement plugins, which then create whole solutions and presets. This is not a single app. Splitting your code lets you think about these things in their own perspective.
  • Since you divided code into multiple packages it's natural to keep them separately. Each has its own releases, its own commits, tests, etc. Publishing multiple packages while keeping the whole source code in one repo is a bit like a split personality – why releasing multiple packages then? What does it give you?
  • We expect that the community will build their plugins and themes around CKEditor 5 (just like it did for CKEditor 4). But I experienced myself how hard it was to actually develop a plugin for CKEditor 4. In such a situation you can either create a repo for your plugin (which makes for a multi-repo at this point) or to for ckeditor5. The latter is ugly, the former makes it hard to test your plugin (because the testing env is in the main repo and works exclusively with what's there). And after you solved the testing issues you want to use 3 more plugins (e.g. you rely on them or simply want them in your build) and so you start thinking about all this anyway.
  • The community doesn't care where you keep your source code up to a point when someone needs to use a development version of one of your packages (e.g. a nightly version). Prior to that one simply deals with (multiple) npm packages. As a kind of an experiment, I checked how I'd develop my Babel plugin. Babel uses a single repo, multi-package architecture. They use (and AFAIU maintain) the tool called Lerna which helps with managing all this. As long as you're developing your package for a stable Babel, there's no issue. However, if you go deeper, things get worse:
    • How to publish your package quickly? It turns out that in the source version Babel packages have <name>/src/ structure, while in a release they build the code to <name>/lib/. This was pretty understandable (src is ES6, lib is ES5) and if I wanted to do the same I'd need to complicate the release process just a bit (still, it's more than usual npm publish).
    • I wanted to understand how to depend on a development version of Babel plugins in my package. I don't know how Lerna works and I looked for help in https://github.com/babel/babel/blob/master/CONTRIBUTING.md#developing, but it didn't help me. I have honestly no idea. The whole problem comes from "You can access the built files for individual packages from packages/<package-name>/lib". So a dev version of Babel plugins is not available in node_modules/? ;| After a while I think that what I needed might be lerna bootstrap.
    • What's the expected 3rd party plugin structure which allows you to quickly switch between the dev and stable versions of Babel?
    • If an official Babel plugin has dependencies, they must be installed by Lerna somewhere. If they are installed in the main node_modules/, then they start to conflict with other package deps. If separately, then what if one official plugin requires another official plugin? Is the latter also symlinked somehow or its stable version is always used? Dealing with npm and symlinks is the most hardcore part of the whole puzzle for me and you don't avoid that in a single repo.
  • Separate repos gives us separate CI builds for each package. Separate code coverage reports and code statistics (e.g. CodeClimate).
  • We can be breaking UI library without breaking builds for the engine. And once we'll start versioning packages, it will be simple to really work on just a subset of the project.
  • We're forced to think about the packages as separate pieces of code. Doing changes to multiple repos at once it's more time consuming so every time such situation occurs you need to ask yourself whether what you're doing is right. At this stage often it is (we often change the API), but if this will be true in the future, we'll clearly see that some APIs are unstable and this means that perhaps we should give them a better thought (and e.g. encapsulate the thing which changes).

Bad stuff

Still, all these arguments should not convince you right away (unless you're an ideologist). What I've seen so far is that none of the options is perfect, but perhaps one of them is just unacceptable.

Let's try to go through all the problems that we had so far and see.

  • GitHub issues management. This is a bit separate topic and we'll be discussing it in another ticket as it can be solved without touching the code base.
  • Bootstraping the dev env. We need a tool to clone all repos which should be cloned and link everything together. Lerna has a similar functionality despite focusing on a single repo, so it's not a problem exclusive to a multi-repo.
  • Maintaining the multi-repo. Not a big deal – we've got tools to update all repos and perform tasks on all repos.
  • Switching to a certain stage in the project history. Needed e.g. for bisecting. If we had a single repo it would be easier, but with a tool registering the whole history of commits to all repos (not a big deal with GH hooks) and our tool to checking out to these versions of certain packages, that won't be an issue.
  • Releasing. Requires automation of the repetitive work if you have multiple packages anyway. We just need to automate a bit more – e.g. automate tagging and creating GH releases. However, we can be sure that we know exactly what change has been made in each package. Generating a changelog for each package if they are kept in one repo would require understanding which package is affected by which commit.
  • Managing boilerplate in each repository. The same is true for Babel – I've also seen there changes touching all packages and making them needs to be automated (e.g. Lerna has run task). The only difference is that we need to commit them to each repos, but that we've got automated as well.
  • The whole business with npm (symlink everything!). It's a painful in both scenarios. Not much difference.

And perhaps more...

Conclusions

For me the conclusions are pretty clear for now. It's more complicated to manage a multi-repo, but not impossible and when you have multiple packages you need to automate a lot of things anyway. OTOH, multi-repo gives you things which a single repo doesn't – that the official environment can be built in a generic way useful for everyone and that multiple packages are really multiple packages, not just multiple npm entries.

Feel free to disagree with me.

There are still two topics which I'd like to review more closely – issue management and general developer experience with CKEditor 5. Both of those topics are a bit related to general multi-repo considerations, but are non-blockers.

Other thoughts

While searching for some articles I found https://github.com/mixu/gr. It looks like a big part of our ckeditor5-dev-env. I always wanted to build a general tool for multi-repo management. This would be more time consuming than developing something totally custom, but may turn to be helpful in long term if the project is used by others. I'm only unsure whether I like the gr's code base, so I'd be more willing to just take it as an inspiration.

The second thing is that after I wrote the article on Medium, I found that D3 and ProseMirror turned to multi-repos as well (perhaps I inspired someone :D) and Aurelia was released something around that moment too. I also remember that there was one more big project which chose this path, but I can't recall now. So we're not alone here ;>.

And last but not least, I also realised that e.g. Babel is a different project than CKEditor 5. CKEditor 5 will never be so open to contributors because we have different approach to OSS. We've our strict rules about what can get into the official code base there's a much higher need to develop 3rd party solutions (plus there are closed source addons too). So what may work for others may not work for us.

@wwalc
Copy link
Member

wwalc commented Dec 12, 2016

Switching to a certain stage in the project history. Needed e.g. for bisecting. If we had a single repo it would be easier, but with a tool registering the whole history of commits to all repos (not a big deal with GH hooks) and our tool to checking out to these versions of certain packages, that won't be an issue.

I'm a bit concerned about the fact that while we figured out some workaround for this problem for us, the same issue most likely will still exist for anyone who forks a couple of ckeditor5 repositories and needs the same thing.

@Reinmar
Copy link
Member Author

Reinmar commented Dec 12, 2016

I'm a bit concerned about the fact that while we figured out some workaround for this problem for us, the same issue most likely will still exist for anyone who forks a couple of ckeditor5 repositories and needs the same thing.

That's a really good point. It's the place where multi-repo works against the same DX as we have.

There are a couple of things to consider, though:

  • CKEditor 5's modular architecture makes it less likely that you'll ever need to fork anything in order to change some default behaviour. In most cases you'll just be able to compose features differently (of course you have to write your own alternative feature). You may still want to hot-fix some bug which hasn't been closed in the major branch, but, in general, you won't need to maintain big forks for a long time.
  • Each package has its own releases so even if you'll need to fork one, you will be concerned just by changes in this one (if you want to merge from upstream). This also means that you'll less often be interested in checking what failed and where. And if it does, it will often be during bumping up other deps or during the merge from upstream (in which case you'll at least be able to focus on a really small subset of commits).
  • In general, you'll be using versioned CKEditor 5 packages, so the whole time travelling will be more
    oriented on downgrading deps to different versions than working with git. I think that this is what many developers did anyway, because between releases the code were often unstable so it was hard to reason about specific commits (unless you're a core dev).

So, I agree that in this specific scenario having multi-repo becomes an obstacle, but such scenarios should be less frequent in CKEditor 5.

I've been also thinking whether we could implement time travel without a single history point, but unfortunately that would be really tricky. The history can be combined based on commit dates, but then you need to create a set of git commits describing these changes anyway in order to be able to use git bisect. So I'd rather focus on having our official history repo.

@Reinmar
Copy link
Member Author

Reinmar commented Dec 12, 2016

Part 2/3: #376 :D

@Reinmar
Copy link
Member Author

Reinmar commented Dec 30, 2016

We started using Lerna and monorepo for https://github.com/ckeditor/ckeditor5-dev/ packages and it works fine there. That case is perfect for a monorepo, because we're the only ones who'll ever work more with these packages.

However, we faced a minor, but an irritating issue with it – we can't now install dev version of these packages in ckeditor5 using package.json (it's possible with separate repositories using "package-name": "repo/path" deps). We normally use a script which makes symlinks to proper places, but doing this e.g. on Travis servers would be less fun.

@Reinmar
Copy link
Member Author

Reinmar commented Jan 17, 2017

BTW, I don't remember whether this was mentioned, but we're struggling with this right now, so I'll repeat it – mono-repo isn't that nice to work with if you want to use a dev version of some package on, e.g., your CI server.

For example, we can't easily install ckeditor5-dev-tests from a branch on Travis, because npm cannot install (AFAIK) a package from a specified branch + directory. So we need all these steps:

clone ckeditor5-dev
cd ckeditor5-dev/packages/ckeditor5-dev-tests
npm link
npm i
cd ../../..
npm i
npm link @ckeditor/ckeditor5-dev-tests

Instead of a single npm i ckeditor/ckeditor5-dev-tests#some-branch as before.

@Reinmar
Copy link
Member Author

Reinmar commented Feb 28, 2017

OK, it seems that nothing's gonna change here. We still use multi-repo and don't plan to change this :)

I updated dev env guide some time ago if you want to know some more details on how to work with this architecture: https://github.com/ckeditor/ckeditor5/wiki/Development-environment.

@Reinmar Reinmar closed this as completed Feb 28, 2017
@Reinmar Reinmar added the resolution:invalid This issue is invalid (e.g. reports a non-existent bug or a by-design behavior). label Feb 28, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
resolution:invalid This issue is invalid (e.g. reports a non-existent bug or a by-design behavior). status:discussion
Projects
None yet
Development

No branches or pull requests

2 participants