(I am not 100% sure whether this is the right place to have this discussion, if not please point me to the right direction).
So this is a feature I always wanted to have in other languages but I don't know any that solve this problem for me. In fact I think something like a rename package
feature would solve two problems: an obvious one and a less obvious, but arguably more important (or at least cooler) one.
Say I want to use Swift-NIO and I want to use the newest version. So I add a dependency to Swift-NIO 2.50.0. But I also want to use some other library, let's call it foo
, which depends on Swift-NIO 2.20. Or even worse on NIO 1.0. What do I do? The solution I would propose is for me to allow renaming either Swift-NIO or foo
(and transitively its dependencies) to something else. Basically this would allow me to pull in multiple versions of the same code.
This kind of namespacing would allow me to resolve hard to resolve dependencies. So this is the obvious use-case. It would be even cooler if I could now also depend on multiple versions of the swift standard library and other fundamentals (like Foundation). One reason I'd like to do this is testability: I could just use the same version of all libraries my dependencies were tested with.
Now the more cool one (and this is a real-world problem we never really managed to solve in FoundationDB since C/C++ don't really provide a solution for this). Let's take FoundationDB as an example (and let's pretend FDB is written in Swift):
FoundationDB uses deterministic simulation to test all kind of real-world scenarios within a single process. One thing though that is really freaking hard to test is upgrades and downgrades. Basically we would like to run different versions of the same code in the same process and verify that they work together.
We believe that testing this in a real cluster is inadequate: failures are very hard to debug, tests are very expensive to run (as they require more resources than what FDB can do in-process), testing becomes an operational nightmare etc. FoundationDB works around this using two strategies:
- All processes within a cluster have to run at the same version. So when upgrading a cluster all processes have to be upgraded at the same time.
- For things that require disk format change we manually do what I described above. If you look at FDB code you can find that the transaction log server is checked in at versions 4.6, 6.0, and 6.2. Since these are versions where me made significant changes that require on-disk changes with upgrade and downgrade capabilities.
Both of the above come with huge problems. (1) is an operational issue and will eventually cause a scalability limitation. (2) is a maintenance nightmare and makes the code ugly and hard to understand.
We did consider using dlopen
to load multiple versions of the same thing into one process, but so far we couldn't get this working.
So I am wondering whether this is something the language could solve (or at least help solving). If I have a project Bar
I could simply import it's target as of a previous release and then build testing around it. For example, I could run old code against a new instance of my database. I could test that my network protocols are upwards and downwards compatible. And I could do all these things in simple unit tests.
Maybe there's also other solutions for the two problems above (and it doesn't need to be one) but I really would love to have some features either in the language or the build system that would allow me to solve these issues.