Introduction
Hello, breathtaking DEV people! 👋
All last week I was refactoring my Go projects. And realized that I was using the same tools to check code quality and safety everywhere. Yes, this should be done from time to time to keep the code base clean and orderly (this applies not only to programming, but in general in life).
🚀 By the way, this article is not about the built-in linters and formatters in the Go plugin for VS Code or similar! These will be different projects from various authors, that you can add to your programming cycle.
So, why not tell my readers about helpful tools? Let's go! 👇
📝 Table of contents
- Why it is important and my vision for Go projects
- Setting up the environment correctly
- The gosec package
- The go-critic package
- The golangci-lint package
- Afterwards
Why it is important and my vision for Go projects
I have to tell you: I love writing code. But more than that, I like reading structured code from other developers.
Why? It helps to better understand the author, adopt useful techniques and methods, as well as, just to understand the work of a particular function and the package as a whole!
For this reason, I am meticulous about tools to make it easier to write code and, as a consequence, to read it afterwards. I encourage you to be careful and use the tips and tools I describe in this article, too… someone is still reading the code you've written.
Setting up the environment correctly
First, the default .editorconfig
for all of my Go projects, looks like this:
# ./.editorconfig
root = true
[*] # <-- rules for all files
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[{go.mod,go.sum,*.go}] # <-- rules only for Go's project files
indent_style = tab
indent_size = 4
# ...other rules...
The second, but just as important as the first, always updated Go plugin for Visual Studio Code (which I mentioned above) with default settings.
The gosec package
The gosec
inspects source code for security problems by scanning the Go AST. This package contains over 30 different rules to test the security of your Go code.
☝️ Note: In computer science, an abstract syntax tree (AST), or just syntax tree, is a tree representation of the abstract syntactic structure of source code written in a programming language.
Here's an example from one of my projects. I call the command gosec ./...
in the root directory of my module:
[gosec] 2021/10/25 18:03:37 Including rules: default
[gosec] 2021/10/25 18:03:37 Excluding rules: default
[gosec] 2021/10/25 18:03:37 Import directory: /Users/koddr/Code/project/api/app/models
[gosec] 2021/10/25 18:03:37 Checking package: models
# ...list of all *.go files...
Results:
[/Users/koddr/Code/project/api/platform/cdn/digitalocean_spaces.go:62] - G304 (CWE-22): Potential file inclusion via variable (Confidence: HIGH, Severity: MEDIUM)
61: // Open the file from system path.
> 62: file, errOpen := os.OpenFile(pathToFile, os.O_RDONLY, 0o600)
63: if errOpen != nil {
Summary:
Gosec : 2.9.1
Files : 29
Lines : 2760
Nosec : 0
Issues : 1
And this give me a lot of information to fix it! You can easily fix this part of the code by wrapping the pathToFile
variable with the filepath.Clean()
function built into Golang.
🤔 Note: Yes, it may seem strange at first glance, but if you've ever run Go projects in production, you'll understand why you can't leave such things out of your code.
The go-critic package
The go-critic
is a collection of checks that detect style, performance issues as well as some common programming errors. It provides as much useful checks as possible. This wonderful tool not only shows you the problematic part of your Go code, but also tells you what to replace it with.
For an example, let's run gocritic check -enableAll ./...
for the popular Swagger documentation generator package swaggo/swag
:
./operation.go:1033:24: ioutilDeprecated: ioutil.ReadFile is deprecated, use os.ReadFile instead
./parser.go:604:1: paramTypeCombine: func(tagName string, dirPath string) ([]byte, error) could be replaced with func(tagName, dirPath string) ([]byte, error)
./packages.go:41:5: emptyStringTest: replace `len(packageDir) == 0` with `packageDir == ""`
# ...and over 20 more...
If there is a rule group you don't need at the moment, just disable it via a disable
flag with tag like this:
gocritic check -disable='#style' ./...
Supported tags (always enabled):
-
#diagnostic
— kind of checks that detect various errors -
#style
— kind of checks that find style issues -
#performance
— kind of checks that detect potential performance issues -
#security
— kind of checks that find security issues
And tags which disabled by default (enable it by the -enable='<TAG>'
flag with specific tag, or use -enableAll
flag):
-
#experimental
— check is under testing and development -
#opinionated
— check can be unwanted for some people
For turn off a specific rule, just run this command:
gocritic check -disable=emptyStringTest ./...
☝️ Note: You can find a complete list of all the rules here.
The golangci-lint package
The golangci-lint
is a fast Go linters runner. Most installations of this package performed for CI, but you can use it, for example, as a standard linter in your favorite IDE.
golangci / golangci-lint
Fast linters runner for Go
The main advantages:
- Runs linters in parallel, reuses Go build cache and caches analysis results
- YAML-based configuration
- Many linters included, no need to install them
- Minimum number of false positives because of tuned default settings
- Nice output with colors, source code lines and marked identifiers
And yes, personally, I often use it with GitHub Actions to check all incoming PRs for projects in the repositories.
📌 Note: If you missed it, here's my series of articles that will give you insight and practice with GitHub Actions.
The config file for Actions looks like this, for example:
# ./.github/workflows/golangci-lint.yml
name: golangci-lint
on:
push:
branches:
- master
pull_request:
jobs:
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
with:
version: latest
only-new-issues: true
# ...
A small demo from the developers of golangci-lint
that shows the problematic areas of the Beego web framework (by golangci-lint run
command):
Afterwards
I hope that this article has helped you. Write in the comments if you have any other good tools for checking and formatting code. And be never afraid to take a little time to make your code great again.
It's well worth it! 😉
Photos and videos by
- Tools authors and Vic Shóstak
P.S.
If you want more articles (like this) on this blog, then post a comment below and subscribe to me. Thanks! 😻
❗️ You can support me on Boosty, both on a permanent and on a one-time basis. All proceeds from this way will go to support my OSS projects and will energize me to create new products and articles for the community.
And of course, you can help me make developers' lives even better! Just connect to one of my projects as a contributor. It's easy!
My main projects that need your help (and stars) 👇
- 🔥 gowebly: A next-generation CLI tool that makes it easy to create amazing web applications with Go on the backend, using htmx, hyperscript or Alpine.js and the most popular CSS frameworks on the frontend.
- ✨ create-go-app: Create a new production-ready project with Go backend, frontend and deploy automation by running one CLI command.
Top comments (5)
Greetings,
/self-promotion:
If you plan to use tools like
gosec
,go-critic
andgolangci-lint
(and you should!), you might also consider automating their usage with something like pre-commit hooks.My golang hooks package has built-in support for all 3 tools.
I use them in my own projects ands find it very useful to both reject commits when checks fail, and to make it easy to invoke the checks manually as I'm developing.
Hi,
Great addition to the article! Thanks for the link and the (brief) explanation. That's what I write articles for, so we can communicate through them and learn new tools.
I have used the golangci-lint function in many of my projects. Glad that you have mentioned it. You can check out my Go Array Tutorials covered in detail.
Thanks for golangci-lint!
Glad this tool helped you! 😉
You can integrate it not only in CI, but just use it via
golangci-lint run ./...
to check locally.