Refactoring code across packages (gorename, gomvpkg, fix)

Oftentimes, you may come across a variable within a function that has a strange or non-descriptive name that you are quite keen on renaming. Performing such a rename operation is quite simple; just select the function block and run a find-and-replace operation. Simple as pie!

But what if you want to rename a public struct field or a function that is exported from your package? This is definitely not a trivial task as you need to track down all the references to the thing being renamed (the list may also include other packages) and update them to use the new name. This type of rename operation takes us into the realm of code refactoring; fortunately, there is a tool at our disposal for automating this tedious task: gorename [16]. It can be installed by running go get golang.org/x/tools/cmd/gorename.

One interesting feature of gorename, besides the fact that it works across packages, is that it is type-awareSince it relies on parsing the program before it applies any rename operation, it is intelligent enough to tell the difference between a function called Foo and a struct field with the same name. Furthermore, it includes an extra layer of safety in that it will only apply rename operations as long as the end result is a piece of code that can compile without errors.

Sometimes, you may find yourself needing to rename a Go package or even move it to a different location either within the same project or across projects. The gomvpkg tool [15] can assist in that matter while also taking care of tracking down packages that depend on the renamed/moved package and updating their import statements to point to the new package location. It can be installed by running go get golang.org/x/tools/cmd/gomvpkg. Moving a package is as simple as running the following command:

# Rename foo to bar and update all imports for packages depending on foo.
$ gomvpkg -from foo -to bar

The Go standard library has changed a lot over the years since the release of the first stable Go version back in 2011. New APIs were introduced while other APIs were deprecated and eventually removed. In some cases, existing APIs are modified, often in a non-backward-compatible way, whereas in other cases, external or experimental packages eventually got accepted for inclusion in the standard library.

A relevant example is the context package. Prior to Go 1.7, this package was available at golang.org/x/net/context and quite a few Go programs were actively using it. But with the release of Go 1.7, that package became a part of the standard library and moved to a standalone context package. As soon as engineers switched to the new import path for the context package, their code would instantly become incompatible with code still using the old import path. Therefore, someone would have to undertake the task of reviewing the existing code base and rewriting existing imports to point to the new location for the context package!

The Go designers foresaw such issues and created a rule-based tool to detect code that relies on old, deprecated APIs or packages, and automatically rewrite it to use newer APIs. The tool, aptly named fix (https://golang.org/cmd/fix/), ships with each new Go release and can be invoked each time you switch to a newer Go version by running go tool fix $path. It is important to point out that all applied fixes are idempotent; therefore, it is safe to run the tool multiple times without the risk of your code base becoming corrupted.