I am working on a project with dependency graph like this:
App
Utils
Codec
VM
Codec
Utils
Logics
Utils
Codec
VM
Right now all the packages are in a mono repo and use relative path to locate the dependencies and that works fine.
The APIs are not stable so having everything in a single repo makes it easy to do cross package edit / breaking changes to API.
However, I have a requirement to move VM to its own repo.
What options do I have?
One way is to move every package to its own repo. However I would like to avoid that because of overheads. Some downsides:
Overheads to manage many repos
A lot of unnecessary version bumps are needed compare to mono repo approach
Very poor DX when doing major refactoring. This project is still very early stage so refactoring are expected.
Extra attention are needed to ensure deps are aligned. e.g. all packages should use a same version of swift-log.
Another way is to use git submodules. However I am not sure how do get it to work. I can move VM to its own repo and add it as submodule to the App repo. But VM depends on Utils and I can't really add Utils as submodule to VM because other packages also depends on Utils. So I have to move Utils to its own repo as well and reference it by git url. Then we are back to the previous solution.
Any other ways? Any good big open source project that I can check for inspiration on how they setup the repos?
I have a mono repo with many packages, which works really well for me. It's very easy to move stuff around between the packages and the packages and the main app, also because I'm coming from a situation of quite extensive tech debt and I cannot fix everything in one go. I would not go for an external package unless there's sufficient separation and stability on the API that connects the modules.
A lot of unnecessary version bumps are needed compare to mono repo approach
It drove me nuts and I would never do it again. This was just one external package. If you stick to commits, then you can just push, copy commit has, update your project and you're done. It's considered even a good practice without considering ease of use: Pinning Swift Package Versions: Predictable SPM Package Versions Across All Machines
My mental model is now that versions are for external consumers, not for internal development. So you should not only have a limited amount of releases, but they also should have release notes and clearly indicated goals.
Extra attention are needed to ensure deps are aligned. e.g. all packages should use a same version of swift-log.
I had the same issue and I went for the approach that shared external dependencies should live in their own package. So the consuming packages just refer to that package and don't manage the versioning themselves.
My approach would be:
Stay on the mono repo for a while longer
Identify and move out all of the shared external dependencies to their own package
Move out the smallest package to a separate remote one (I would guess that is Utils?), pinned on commit
Repeat until enough packages are done that you can start sharing the ones you wanted to share
Mark the one you wanted to share, plus all of it's dependencies (should be the packages for the external dependencies plus the ones like Utils) with the same version number and publish it