At work I’ve been building a new program on top of an SDK that’s under very active development. After about 6 weeks without updating the version, the SDK had deleted some code I was using and had a ton of breaking changes. If I’d simply updated the library, my entire program would fail to build, and it also wouldn’t be clear how to get it back to a good state. Following in the spirit of Branch by Abstraction, I’d rather introduce the new code side-by-side with the old and incrementally migrate without breaking the program.

I work in Go, where our import statements look like this:

import (
  github.com/grafana/grafana
  github.com/joeblubaugh/lib
)

The import statements use package names, and if the packages are URLs, then go build will try and download modules by doing a git clone based on the package’s URL. Go’s dependency management uses require statements to describe the dependency tree:

require github.com/joeblubaugh/lib v0.1.0

So, let’s say I need to update from v0.1.0 to v0.5.0, but I need to keep the v0.1.0 code around. I can’t have duplicate module names in go.mod, but I can play a sneaky trick with replace directives:

require (
  github.com/joeblubaugh/lib v0.1.0
  joe/upgraded               v0.0.1
)

replace joe/upgraded v0.0.1 => github.com/joeblubaugh/lib v0.5.0

By introducing a fake module name and a replace directive, I can import code from two different versions into the same program and incrementally move over to the new package:

import (
  github.com/joeblubaugh/lib
  joe/upgraded
)