0: https://nextmv.io/
1: https://pkg.go.dev/plugin
1. Versioning. We have a public SDK repo for users where our API definitions live. The private implementations are linked with plugins. The SDK version a customer might be using has to be an exact match to the SDK version that the plugins were built on. In addition, the Go version they were built on also has to be an exact match to the one being used. Maintaining support for the combinations of these two versions implies building and keeping a lot of .so plugins.
2. Cross compilation. Doing `GOOS=foo GOARCH=bar go build` works great, but not when doing `GOOS=foo GOARCH=bar go build -buildmode plugin`. Cross compilation doesn't work with .so plugins. We have to compile natively on the target arch-os combination, which is challenging during CI/CD. One strategy we have though is to use zig to cross-compile from linux/amd64 to linux/arm64. For the other platforms we support (linux/amd64, darwin/amd64, darwin/arm64), we have to compile on those platforms to get the .so plugins working.
3. Debugging. When an application uses our SDK + .so plugins to run, setting a breakpoint and debugging does not work. Delve doesn't work out-of-the-box and we had to do some workarounds to build with debug flags. That means we have "normal" plugins and "debug mode" plugins. We ship both of those to the users.
4. Path. When compiling plugins on a computer, the file system paths from the resulting executable are stored, thus using those .so plugins on a new computer does not work. For this we build the plugins with the `-trimpath` flag, meaning our command looks something like this: `go build -trimpath -o $slug-$sdkVersion-$goVersion`. This also means that we have to accompany any Go command with the `-trimpath` flag: `go run -trimpath main.go`, `go test -trimpath ./...`, etc...
5. Generics. Type parameters do not cross the .so boundary. We had to come up with workarounds where a generic symbol is never passed.