Writing a custom go vet for better code standard

Tanapol Nearunchorn
Life@LINE MAN Wongnai
4 min readFeb 11, 2021

--

Credit: https://fukuokago.dev/en

At LINE MAN Wongnai company, our engineering team is growing faster than ever, new people join the team as Go developers and some current employees seek for a new challenge by taking on a new role to write Go as their main tech stack. While our Go codebase is growing according to the number of engineers, we’re looking for a better way to raise our code quality bar higher in a more efficient way.

As you may know, we do code review for every change before it gets merged. We keep sharing knowledge, constantly refactoring our code base, or sometimes we rewrite the whole system. All of these help maintain code quality and improve it over time. By the way, these methods rely heavily on great, experienced, and visionary code owners to maintain and inspire others engineers to write better code. They also tend to be busy with their responsibilities and it’s even harder to catch up the speed of the code produced by the whole development team.

Today, I’d like to share an effective way I have just found that can help maintain the high quality of code and yet it is scalable as the team grows.

Introducing Go vet

Go has a tool, go vet, which helps examine Go source codes and look for suspicious code that may cause a problem. It also has the ability to add custom rules easily by just implementing a snippet defined by Go Tools APIs.

You can get started by running this command in your project to let go vet check your source codes.

# cd into your project directory
go vet ./...

Go vet has a default list of Analyzers implemented such as checking for unmeaningful assignments, invalid fmt.Printf formatting usages for a specific data type. A complete list can be found here: https://golang.org/cmd/vet/

Go vet can be extended by writing a custom Analyzer defined by this package: https://pkg.go.dev/golang.org/x/tools/go/analysis. The standard go vet command won’t allow you to register any custom analyzers. Go provides a way to write your own vet command easily and allows you to register custom Analyzers using the multichecker package. See the snippet below.

Once you create and register your custom analyzer, the program can be run on your console or CI (Pull request/Merge request pipeline) by the following command

# assume that your custom vet program (main.go)
# is inside the cvet directory
go run ./cvet ./...

Writing your own Analyzer

Now, you know the way to register a custom Analyzer to vet command and run it with your development workflow. Let’s begin writing a custom Analyzer.

The prerequisite of writing Analyzer is that you should understand the Abstract Syntax Tree (AST) of Go source. Go provides tools to help you parse and walk through the AST easily. Please take a look at ast package.

I will give you a sample of our custom analyzer which helps us identify potential problems when using a global sync.Mutex.

I found that go test spawns different processes for different test packages. I was using sync.Mutex in a global variable to help prevent a race condition when writing integration tests that run against the real database. When go spawns a new process, the global mutex cannot perform locking across the processes so I have to find another way of locking or get rid of locking at all.

By the way, I want to protect other engineers from very-hard-to-investigate-and-time-consuming issues, so I decided to write a custom lint to fail any builds when someone creates a global mutex.

What the code does is that it traverses on Go’s Abstract Syntax Tree and identifies global variables and checks the type of each variable whether it is sync.Mutex or not. If the analyzer finds a global mutex variable, it will report the location of the variable declared and ask developers to fix that.

Once you write this Analyzer then you just add a list of Analyzers to the main.go file earlier. 🎉🎉🎉 TADA 🎉🎉🎉 You got your own vet running and it benefits all engineers in the team.

Improve Engineering Workflow

When a problem is found in the code, we should keep asking whether this problem can be protected by writing a custom analyzer or not. Overtime, we will grow a list of analyzers which help developers write programs with better coding standards, spend less time in code review, get faster feedback loop, and have fun to learn internal Go type system, syntax, and tooling.

I hope this article will inspire you and your team to heavily invest in tooling to help engineers create high quality work and yet be much more effective delivering valuable software.

One last thing, don’t forget to share your own Analyzer with the Go community!

--

--