Skip to content

nikolaydubina/go-recipes

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🦩 Go Recipes

Handy well-known and lesser-known tools for Go projects

Know some cool tool or one-liner? Have a feature request or an idea?
Feel free to edit this page or create an Issue!

Hits go-recipes

Contents

AI Tools

⏫ Advanced autocompletion with Copilot

Start typing and after few seconds you will get autocompletion suggestion. Some useful ways to interact with it listed below.

given a function signature and docstring, it will suggest function body
given a function body, it will suggest docstring

Requirements

VSCode
GitHub account

⏫ Code analysis and recommendations with charmbracelet/mods

This is a nice looking CLI wrapper for major LLM APIs from Charm team. It supports OpenAI and LocalAI. It passes arbitrary human language command string and concatenated with STDIN input. Multiple useful commands are possible.

mods -f "what are your thoughts on improving this code?" < main.go | glow
mods -f "you are an expert Go programmer. find potential bugs in following Go code." < my_class.go | glow

Requirements

# OpenAI token or LocalAI model and server
go install github.com/charmbracelet/glow@latest
go install github.com/charmbracelet/mods@latest

⏫ Pull request recommendations with gpt-pullrequest-updater

This tool generates GitHub pull request description and review using OpenAI ChatGPT. There is also GitHub Action available. β€” @ravilushqa

Requirements

# OpenAI token
# GitHub token
go install github.com/ravilushqa/gpt-pullrequest-updater/cmd/description@latest
go install github.com/ravilushqa/gpt-pullrequest-updater/cmd/review@latest

⏫ Commit message recommendation with charmbracelet/mods

Short summaries of changes usually work well.

git diff | mods "summarize following git diff into short git commit message."
git diff | mods "you are expert Go programmer. you are owner of this codebase. think through step by step. summarize following git diff into short git commit message under 10 words."

Example

Add new entries for Using AI in Go projects, including Advanced autocompletion with Copilot and Code analysis and recommendations with charmbracelet/mod. Update page.yaml accordingly.

Requirements

# OpenAI token or LocalAI model and server
go install github.com/charmbracelet/mods@latest

⏫ Test case recommendation with charmbracelet/mods

Concatenate two files and ask to recommend missing test cases. It is not precise, has high false positive and high false negative rate. Often can not detect that tests cases are present at all. However, it can give a fresh perspective on your code. Best results are produced when asking succinct short replies. Example outputs below.

cat fpdecimal.go fpdecimal_test.go | head -c 3600 | mods -f "you are an expert Go programmer. investigate supplied Go program and associated test suite. think through this step by step. make sure you get the right answer. recommend missing test cases. write very succinctly. under 100 words." | glow
cat fpdecimal.go fpdecimal_test.go | head -c 4000 | mods -f "investigate supplied Go program and associated test suite. recommend missing test cases." | glow

Example

For additional test cases, consider adding tests for negative float values, positive and negative infinity, unsigned
integers, zero divided by a number greater than zero, and division with only zeros.                       
------------------
  Test cases:                                                                                                                                                                                                                             
  β€’ Test for unmarshalling JSON into Decimal                                                                          
  β€’ Test for marshalling Decimal to JSON                                                                              
  β€’ Test for multiplication with zero                                                                                 
  β€’ Test for multiplication identity                                                                                  
  β€’ Test for division with zero                                                                                       
  β€’ Test for all comparison operations for the Decimal struct.                                                        
------------------
Missing test cases for the  fpdecimal  Go program include those for testing the  DivMod  and  FromString  functions.
Additionally, there should be tests checking that zero division is not allowed, and tests that ensure the           
FractionDigits  value does not change during the program's runtime. Important test cases include comparing decimals 
for equality, as well as testing the commutativity, associativity, and identity properties of addition and          
multiplication. Finally, the program should have a test that verifies the  MarshalJSON  and  UnmarshalJSON          
functions.

⏫ Time complexity estimate with charmbracelet/mods

This is one of recommended use cases by OpenAI website. It can produce fairly good estimations. However, in its direct form usefulness is questionable, since complex cases are not handled precisely enough, educational potential is limited, and simple cases do not require this. Perhaps, this will be utilized in future work on using models in compiler and programming. Copy function and pipe it to model with prompt asking for time complexity estimation. Below is an example.

pbpaste | mods -f "calculate time complexity of following Go code function." | glow
func AppendFixedPointDecimal(b []byte, v int64, p int) []byte {
  if v == 0 {
    return append(b, '0')
  }

  if p == 0 {
    return strconv.AppendInt(b, v, 10)
  }

  if v < 0 {
    v = -v
    b = append(b, '-')
  }

  s := len(b)
  b = strconv.AppendInt(b, v, 10)

  if len(b)-s > p {
    i := len(b) - p
    b = append(b, 0)
    copy(b[i+1:], b[i:])
    b[i] = '.'
  } else {
    i := 2 + p - (len(b) - s)
    for j := 0; j < i; j++ {
      b = append(b, 0)
    }
    copy(b[s+i:], b[s:])
    copy(b[s:], []byte(zeroPrefix[:i]))
  }

  return b
}

Example

The time complexity of the AppendFixedPointDecimal function is O(N), where N is the length of the input byte slice  
b. This is because the function performs a constant number of operations for each byte in b, and the largest        
operation within the function is the loop that appends zeros to the byte slice, which has a length proportional to  
the input. Other operations, such as integer conversions and comparisons, are considered constant time since the    
input size is fixed.                 
------------------
The time complexity of the function depends on the input values  v  and  p .    
β€’ The first three if statements before the main computation take constant time, 
so they don't contribute to the overall time complexity.                        
β€’ The  strconv.AppendInt  function used twice has a time complexity of          
O(log(v)), where v is the integer being converted to a string representation in 
base 10.                                                                        
β€’ The condition  len(b)-s > p  has a constant time complexity.                  
β€’ The  append  and  copy  operations inside the if and else blocks take linear  
time proportional to  p .                                                       
β€’ The  for  loop has a constant number of iterations, specifically 2 +  p  - (  
len(b)-s ). The  append  and  copy  operations inside the loop also take linear 
time proportional to  p .                                                       
Therefore, the overall time complexity of the function is O(log(v) + p).

Test

⏫ πŸ”₯ Continuous Tests Monitoring with codecov.io

Track tests duration, errors, flackiness. Run JUnit test output converter and submit result to codecov.io via GitHub Action. β€” https://codecov.io

go test -coverprofile=coverage.out -cover -json ./... | gotestsum --junitfile tests.xml

Requirements

go install gotest.tools/gotestsum@latest

⏫ Make treemap of coverage with go-cover-treemap

Visualize distribution of code coverage in your project. This helps to identify code areas with high and low coverage. Useful when you have large project with lots of files and packages. This 2D "image-hash" of your project should be more representative than a single number. Also available at https://go-cover-treemap.io. β€” @nikolaydubina

go test -coverprofile cover.out ./...
go-cover-treemap -coverprofile cover.out > out.svg

Requirements

go install github.com/nikolaydubina/go-cover-treemap@latest

⏫ Browse coverage

This is very helpful tool from the official Go toolchain. Similar visualization is integrated into VSCode and Goland, but can be used separately.

go test -coverprofile cover.out ./...
go tool cover -html=cover.out

⏫ Browse coverage with gocov-html

Browse code coverage in statically generated HTML page. Multiple styles are supported. You may need to convert coverage report into gocov format. β€” @matm

gocov test strings | gocov-html -t golang > strings.html
gocov test encoding/csv strings | gocov-html -t kit > strings.html
gocov test strings|./gocov-html -cmax 90 > strings.html # show functions with <90% coverage

Requirements

go install github.com/axw/gocov/gocov@latest
go install github.com/matm/gocov-html/cmd/gocov-html@latest

⏫ πŸ”₯ Browse coverage with xgo

The displayed coverage is a combination of coverage and git diff. By default, only modified lines were shown. This helps to quickly locate changes that were not covered, and add tests for them incrementally. β€” @xhd2015

xgo tool coverage serve cover.out

Requirements

go install github.com/xhd2015/xgo/cmd/xgo@latest

⏫ Browse coverage in terminal with gocovsh

Browse code coverage similarly to HTML provided by official Go toolchain, but in terminal. Other notable features are package level statistics, coverage only for changed files. β€” @orlangure

go test -cover -coverprofile coverage.out
gocovsh                        # show all files from coverage report
git diff --name-only | gocovsh # only show changed files
git diff | gocovsh             # show coverage on top of current diff
gocovsh --profile profile.out  # for other coverage profile names

Requirements

go install github.com/orlangure/gocovsh@latest

⏫ Pretty print coverage in terminal with nikandfor/cover

It is similar to go tool cover -html=cover.out but in terminal. You can filter by functions, packages, minimum coverage, and more. β€” @nikandfor

cover

Requirements

go install github.com/nikandfor/cover@latest

⏫ Run coverage collector server with goc

This tool allows to collect coverage as soon as code is executed. β€” @qiniu

goc server
goc build
goc profile

Requirements

go install github.com/qiniu/goc@latest

⏫ Visualize live coverage in VSCode with goc

Official Go VSCode plugin already has coverage highlighting. In addition to that, this tool shows covered lines as soon as they are executed. This can be useful for running manual integration or system tests or debugging. β€” @qiniu

Requirements

go install github.com/qiniu/goc@latest

⏫ πŸ”₯ Detect drops in coverage with go-test-coverage

This tool designed to report issues when test coverage falls below a specified threshold. Likely you would want to use it in the CI. β€” @vladopajic

go-test-coverage --config=./.testcoverage.yml

Requirements

go install github.com/vladopajic/go-test-coverage/v2@latest

⏫ Shuffle tests

This is less known option that is disabled by default. However, for robust test suite it is beneficial. More test flags and full description is available at go help testflag.

go test -shuffle=on

⏫ Run tests sequentially

Use when you need to synchronize tests, for example in integration tests that share environment. Official documentation.

go test -p 1 -parallel 1 ./...

⏫ Run tests in parallel

Add t.Parallel to your tests case function bodies. As per documentation, by default -p=GOMAXPROCS and -parallel=GOMAXPROCS when you run go test. Different packages by default run in parallel, and tests within package can be enforced to run in parallel too. Make sure to copy test case data to new variable, why explained here. Official documentation.

...
for _, tc := range tests {
    tc := tc
    t.Run(tc.name, func(t *testing.T) {
        t.Parallel()
        ...

⏫ Run all Fuzz tests

Standard tool runs only single fuzz test. Use following to run all fuzz tests in a package.

go test -list . | grep Fuzz | xargs -P 8 -I {} go test -fuzz {} -fuzztime 5s .

⏫ Detect goroutine leaks with goleak

Instrument your test cases with verification call. Alternatively, you can add single call in TestMain. This tool was recommended by Pyroscope in blog. β€” Uber

func TestA(t *testing.T) {
  defer goleak.VerifyNone(t)
  ...
}

Requirements

go get -u go.uber.org/goleak

⏫ Detect goroutine leaks with leaktest

Refactored, tested variant of the goroutine leak detector found in both net/http tests and the cockroachdb source tree. You have to call this library in your tests. β€” @fortytw2

func TestPoolContext(t *testing.T) {
  ctx, cancel := context.WithTimeout(context.Background(), time.Second)
  defer cancel()
  defer leaktest.CheckContext(ctx, t)()

  go func() {
    for {
      time.Sleep(time.Second)
    }
  }()
}

⏫ πŸ”₯ Visualize test runs with vgt

This tool visualizes Go test results in a browser. It's helpful with understanding parallelism of tests and identifying slow tests. More information can be found in our blog post about optimizing Go tests parallelism. β€” @roblaszczak

go test -json ./... | vgt

Requirements

go install github.com/roblaszczak/vgt@latest

⏫ Summarize go test with tparse

This lightweight wrapper around STDOUT of JSON of go test will nicely render colorized test status, details of failures, duration, coverage, and package summary. β€” @mfridman

set -o pipefail && go test ./... -json | tparse -all

Requirements

go install github.com/mfridman/tparse@latest

⏫ Decorate go test with richgo

Add colors and enrich go test output. It can be used in CI pipeline and has lots of alternative visualizations and options. β€” @kyoh86

richgo test ./...

Requirements

go install github.com/kyoh86/richgo@latest

⏫ Decorate go test with gotest

Add colors to go test output. Very lightweight wrapper around go test STDOUT. β€” @rakyll

gotest ./...

Requirements

go install github.com/rakyll/gotest@latest

⏫ Decorate go test with gotestsum

This wrapper around go test renders test output in easy to read format. Also supports JUnit, JSON output, skipping slow tests, running custom binary. β€” @dnephin

gotestsum --format dots

Requirements

go install gotest.tools/gotestsum@latest

⏫ Format go test results as documentation with gotestdox

Decorates go test results by converting CamelCaseTestNames into readable sentences. β€” @bitfield

gotestdox ./...

Requirements

go install github.com/bitfield/gotestdox/cmd/gotestdox@latest

⏫ Get slowest tests with gotestsum

This is subcommand of gotestsum that processes JSON output of go test to find slowest tests. β€” @dnephin

go test -json -short ./... | gotestsum tool slowest --threshold 500ms

Example

gotest.tools/example TestSomething 1.34s
gotest.tools/example TestSomethingElse 810ms

Requirements

go install gotest.tools/gotestsum@latest

⏫ Auto-Instrument skipping slowest tests with gotestsum

This is subcommand of gotestsum that processes JSON output of go test to find slowest tests and instruments test cases to skip them with t.Skip() statements. β€” @dnephin

go test -json ./... | gotestsum tool slowest --skip-stmt "testing.Short" --threshold 200ms

Example

gotest.tools/example TestSomething 1.34s
gotest.tools/example TestSomethingElse 810ms

Requirements

go install gotest.tools/gotestsum@latest

⏫ Automatically re-run failed tests with gotestsum

Other useful option of gotestsum is to re-run failed tests. For example, if you have flaky tests that are idempotent, then re-running them may be a quick fix. β€” @dnephin

gotestsum --rerun-fails --packages="./..."

Requirements

go install gotest.tools/gotestsum@latest

⏫ Make JSUnit test report with gotestsum

JUnit is widely used format for test reporting. β€” @dnephin

go test -json ./... | gotestsum --junitfile unit-tests.xml

Requirements

go install gotest.tools/gotestsum@latest

⏫ Make JSUnit test report with go-junit-report

JUnit is widely used format for test reporting. Go benchmark output is also supported. β€” @jstemmer

go test -v 2>&1 ./... | go-junit-report -set-exit-code > report.xml

Requirements

go install github.com/jstemmer/go-junit-report/v2@latest

⏫ Get packages without tests

If code coverage does not report packages without tests. For example for CI or quality control.

go list -json ./... | jq -rc 'select((.TestGoFiles | length)==0) | .ImportPath'

Example

github.com/gin-gonic/gin/ginS
github.com/gin-gonic/gin/internal/json

Requirements

https://stedolan.github.io/jq/download/

⏫ Perform Mutation Testing with ooze

Mutation testing is a technique used to assess the quality and coverage of test suites. It involves introducing controlled changes to the code base, simulating common programming mistakes. These changes are, then, put to test against the test suites. A failing test suite is a good sign. It indicates that the tests are identifying mutations in the codeβ€”it "killed the mutant". If all tests pass, we have a surviving mutant. This highlights an area with weak coverage. It is an opportunity for improvement. β€” @gtramontina

go test -v -tags=mutation

Requirements

go get github.com/gtramontina/ooze

⏫ Perform Mutation Testing with avito-tech/go-mutesting

This is fork of zimmski/go-mutesting. It has more mutators and latest updates. β€” @vasiliyyudin

go-mutesting ./...
for _, d := range opts.Mutator.DisableMutators {
  pattern := strings.HasSuffix(d, "*")

-	if (pattern && strings.HasPrefix(name, d[:len(d)-2])) || (!pattern && name == d) {
+	if (pattern && strings.HasPrefix(name, d[:len(d)-2])) || false {
    continue MUTATOR
  }
}

Requirements

go install github.com/avito-tech/go-mutesting/cmd/go-mutesting@latest

⏫ Perform Mutation Testing with go-mutesting

Find common bugs source code that would pass tests. This is earliest tool for mutation testing in Go. More functions and permutations were added in other mutation Go tools it inspired. β€” @zimmski

go-mutesting ./...
for _, d := range opts.Mutator.DisableMutators {
  pattern := strings.HasSuffix(d, "*")

-	if (pattern && strings.HasPrefix(name, d[:len(d)-2])) || (!pattern && name == d) {
+	if (pattern && strings.HasPrefix(name, d[:len(d)-2])) || false {
    continue MUTATOR
  }
}

Requirements

go install github.com/zimmski/go-mutesting/cmd/go-mutesting@latest

⏫ Trace tests with go-test-trace

Collect test execution as distributed traces. This is useful for tracking test duration, failures, flakiness. You distributed tracing storage, search, UI, exploration, dashboards, alarms β€” all will automatically become test status collection. If you run integration tests in your CI, then it is particularly handy to investigate your integration tests same way as real requests, such as Go processes, databases, etc. However, if you do not have distributed traces, it is still useful for adhoc investigations. This tool processes STDOUT of go test. No automatic instrumentation is done. β€” @rakyll

go-test-trace ./...

Requirements

# open telemetry collector
# traces UI (Datadog, Jaeger, Honeycomb, NewRelic)
go install github.com/rakyll/go-test-trace@latest

⏫ Speedup tests for large codebases

As of 2023-12-11, large codebases may be slow to run tests by default commands. Compiling package test binaries first and executing them later can lead to significant overall speedup.

go test -c ./pkg/mypackage -o my_pkg_test_binary.bin
./my_pkg_test_binary.bin | ... # normal test output post processing

Dependencies

⏫ Get Go version of current module

For example, setup correct Go version automatically from go.mod in CI.

go mod edit -json | jq -r .Go

Requirements

https://stedolan.github.io/jq/download/

⏫ Get Go versions of upstream modules

Use this when upgrading version of Go or finding old modules.

go list -deps -json ./... | jq -rc 'select(.Standard!=true and .Module.GoVersion!=null) | [.Module.GoVersion,.Module.Path] | join(" ")' | sort -V | uniq

Example

1.11 github.com/ugorji/go/codec
1.11 golang.org/x/crypto
1.12 github.com/golang/protobuf

Requirements

https://stedolan.github.io/jq/download/

⏫ Get directly dependent modules that can be upgraded

Keep your modules updated. Similar function is integrated in VSCode official Go plugin and GoLand.

go list -u -m $(go list -m -f '{{.Indirect}} {{.}}' all | grep '^false' | cut -d ' ' -f2) | grep '\['

Example

github.com/goccy/go-json v0.5.1 [v0.7.3]
github.com/golang/protobuf v1.3.3 [v1.5.2]
github.com/json-iterator/go v1.1.9 [v1.1.11]

⏫ Get upstream modules without Go version

Find outdated modules or imports that you need to upgrade.

go list -deps -json ./... | jq -rc 'select(.Standard!=true and .Module.GoVersion==null) | .Module.Path' | sort -u

Example

github.com/facebookgo/clock
golang.org/x/text
gopkg.in/yaml.v2

Requirements

https://stedolan.github.io/jq/download/

⏫ Get available module versions

This works even if you did not download or install module locally. This is useful to check to which version you can upgrade to, what is the latest version, and whether there are v2+ major versions recognized by Go toolchain.

go list -m -versions github.com/google/gofuzz

⏫ πŸ”₯ Get go module libyear, number of releases, version delta with go-libyear

libyear is a simple measure of software dependency freshness. It is a single number telling you how up-to-date your dependencies are. For example Rails 5.0.0 (June 2016) is 1 libyear behind 5.1.2 (June 2017). This tool can also compute number of releases, and version number delta. β€” @nieomylnieja

go-libyear /path/to/go.mod

Example

package                             version  date        latest   latest_date  libyear
github.com/nieomylnieja/go-libyear           2023-11-06                        2.41
github.com/pkg/errors               v0.8.1   2019-01-03  v0.9.1   2020-01-14   1.03
github.com/urfave/cli/v2            v2.20.0  2022-10-14  v2.25.7  2023-06-14   0.67
golang.org/x/mod                    v0.12.0  2023-06-21  v0.14.0  2023-10-25   0.35
golang.org/x/sync                   v0.3.0   2023-06-01  v0.5.0   2023-10-11   0.36

Requirements

go install github.com/nieomylnieja/go-libyear/cmd/go-libyear@latest

⏫ Make graph of upstream modules with modgraphviz

For each module, the node representing the greatest version (i.e., the version chosen by Go's minimal version selection algorithm) is colored green. Other nodes, which aren't in the final build list, are colored grey. β€” official Go team

go mod graph | modgraphviz | dot -Tsvg -o mod-graph.svg

Requirements

https://graphviz.org/download/
go install golang.org/x/exp/cmd/modgraphviz@latest

⏫ Make graph of upstream packages with import-graph

Find unexpected dependencies or visualize project. Works best for small number of packages, for large projects use grep to narrow down subgraph. Without -deps only for current module. β€” @nikolaydubina

go list -deps -json ./... | jq -c 'select(.Standard!=true) | {from: .ImportPath, to: .Imports[]}' | jsonl-graph | dot -Tsvg > package-graph.svg

Requirements

https://stedolan.github.io/jq/download/
https://graphviz.org/download/
go install github.com/nikolaydubina/import-graph@latest
go install github.com/nikolaydubina/jsonl-graph@latest

⏫ Scrape details about upstream modules and make graph with import-graph

Find low quality or unmaintained dependencies. β€” @nikolaydubina

go mod graph | import-graph -i=gomod | jsonl-graph -color-scheme=file://$PWD/basic.json | dot -Tsvg > output.svg

Requirements

https://graphviz.org/download/
go install github.com/nikolaydubina/import-graph@latest
go install github.com/nikolaydubina/jsonl-graph@latest

⏫ Scrape licenses of upstream dependencies with go-licenses

Collect all the licenses for checking if you can use the project, for example in proprietary or commercial environment. β€” Google

go-licenses csv github.com/gohugoio/hugo

Example

github.com/cli/safeexec,https://github.com/cli/safeexec/blob/master/LICENSE,BSD-2-Clause
github.com/bep/tmc,https://github.com/bep/tmc/blob/master/LICENSE,MIT
github.com/aws/aws-sdk-go,https://github.com/aws/aws-sdk-go/blob/master/LICENSE.txt,Apache-2.0
github.com/jmespath/go-jmespath,https://github.com/jmespath/go-jmespath/blob/master/LICENSE,Apache-2.0
github.com/gorilla/websocket,https://github.com/gorilla/websocket/blob/master/LICENSE,BSD-2-Clause
github.com/pelletier/go-toml/v2,https://github.com/pelletier/go-toml/blob/master/v2/LICENSE,MIT
github.com/spf13/cobra,https://github.com/spf13/cobra/blob/master/LICENSE.txt,Apache-2.0
github.com/kyokomi/emoji/v2,https://github.com/kyokomi/emoji/blob/master/v2/LICENSE,MIT
go.opencensus.io,Unknown,Apache-2.0
github.com/Azure/azure-storage-blob-go/azblob,https://github.com/Azure/azure-storage-blob-go/blob/master/azblob/LICENSE,MIT
github.com/yuin/goldmark-highlighting,https://github.com/yuin/goldmark-highlighting/blob/master/LICENSE,MIT

Requirements

go install github.com/google/go-licenses@latest

⏫ Explore dependencies with goda

This tool has extensive syntax for filtering dependencies graphs. It can work with packages and modules. β€” Egon Elbre

goda graph . | dot -Tsvg -o graph.svg
goda graph -cluster -short "github.com/nikolaydubina/go-cover-treemap:all" | dot -Tsvg -o graph.svg

Requirements

https://graphviz.org/download/
go install github.com/loov/goda@latest

⏫ Explore dependencies interactively with spaghetti

Useful in large refactorings, dependency breaking, physical layout changes. β€” Alan Donovan, official Go team

Requirements

go install github.com/adonovan/spaghetti@latest

⏫ πŸ”₯ Explore dependencies graph interactively with modview

Transform your Go project's dependency graph into a dynamic, interactive visualization with modview. This powerful tool takes the complexity out of your module graph, offering a clear and explorable view of your project's dependencies. β€” @bayraktugrul

modview --open

Requirements

go install github.com/bayraktugrul/modview@latest

⏫ πŸ”₯ Enforce Go code architecture with go-arch-lint

Architecture linter. Will check all project import path and compare with arch rules defined in yml file. Useful for hexagonal / onion / ddd / mvc / etc patterns. β€” @fe3dback

go-arch-lint

Requirements

go install github.com/fe3dback/go-arch-lint@latest

⏫ πŸ”₯ Check Clean Architecture with go-cleanarch

Clean architecture validator for go, like a The Dependency Rule and interaction between packages in your Go projects. β€” @roblaszczak

go-cleanarch

Requirements

go install github.com/roblaszczak/go-cleanarch@latest

⏫ Use go mod directives

Tell Go compiler which versions of upstreams to include in your build. Tell all users of your module how to deal with versions of your module.

// Deprecated: use example.com/mod/v2 instead.
module example.com/mod

go 1.16

require example.com/other/thing v1.0.2
require example.com/new/thing/v2 v2.3.4
exclude example.com/old/thing v1.2.3
replace example.com/bad/thing v1.4.5 => example.com/good/thing v1.4.5
retract [v1.9.0, v1.9.5]

⏫ πŸ”₯ Locally patch dependency with replace

This can be useful for development. First appeared on blog.

# clone your dependency to $DEP folder
# make changes
go mod edit -replace github.com/google/go-cmp=$DEP

⏫ πŸ”₯ Locally patch dependency with go.work

This is an alternative version may be more robust to accidental mistakes. First appeared on blog.

# clone your dependency to $DEP folder
# make changes
go work init
go work use . $DEP

Code Visualization

⏫ Make C4 diagram with go-structurizr

This library provides tools to generate C4 diagrams. The process is a bit involved, however you get diagram generated from real Go code automatically. Steps are outlined in blog. β€” @krzysztofreczek

Requirements

manually defining Go main.go script to invoke library
graphviz
manual coloring spec (DB, classes)

⏫ Make graph of function calls with callgraph

Visualize complex or new project quickly or to study project. Requires main.go in module. Supports Graphviz output format. Has many options for filtering and formatting. β€” official Go team

callgraph -format graphviz . | dot -Tsvg -o graph.svg
recommend: grep <package/class/func of interest>
recommend: grep -v Error since many packages report error
recommend: adding `rankdir=LR;` to graphviz file for denser graph
recommend: you would have to manually fix graphviz file first and last line

Requirements

go install golang.org/x/tools/cmd/callgraph@latest

⏫ Make graph of function calls in package with go-callvis

Quickly track which packages current package is calling and why. β€” @ofabry

go-callvis .

Requirements

go install github.com/ofabry/go-callvis

⏫ Make PlantUML diagram with goplantuml

Generates class diagram in widely used format with the information on structs, interfaces and their relationships. Render .puml files in for example planttext.com. β€” @jfeliu007

goplantuml -recursive path/to/gofiles path/to/gofiles2

Requirements

go get github.com/jfeliu007/goplantuml/parser
go install github.com/jfeliu007/goplantuml/cmd/goplantuml@latest

⏫ Make PlantUML diagram with go-plantuml

Automatically generate visualization of classes and interfaces for go packages. Recommend recursive option. Render .puml files in for example planttext.com. β€” @bykof

go-plantuml generate -d . -r -o graph.puml

Requirements

go install github.com/bykof/go-plantuml@latest

⏫ πŸ”₯ Visualize the entropy of a code base with a 3D force-directed graph with dep-tree

This excellent interactive visualisation tool lets you explore code base as 3D graph. The more decoupled and modular a code base is, the more spread and clustered the graph will look like. β€” @gabotechs

⏫ Make 3D chart of Go codebase with gocity

Fresh artistic perspective on Go codebase. GoCity is an implementation of the Code City metaphor for visualizing source code - folders are districts; files are buildings; structs are buildings on the top of their files. This project has research paper "GoCity Code City for Go" at SANER'19. Also available at go-city.github.io. β€” @rodrigo-brito

Requirements

go install github.com/rodrigo-brito/gocity@latest

⏫ Make histogram of Go files per package

Find when package is too big or too small. Adjust histogram length to maximum value.

go list -json ./... | jq -rc '[.ImportPath, (.GoFiles | length | tostring)] | join(" ")' | perl -lane 'print (" " x (20 - $F[1]), "=" x $F[1], " ", $F[1], "\t", $F[0])'

Example

================== 18	github.com/gin-gonic/gin
     ============= 13	github.com/gin-gonic/gin/binding
                 = 1	github.com/gin-gonic/gin/internal/bytesconv
                 = 1	github.com/gin-gonic/gin/internal/json
       =========== 11	github.com/gin-gonic/gin/render

Requirements

https://stedolan.github.io/jq/download/

⏫ Explore Go code in browser powered by go-guru with pythia

Explore Go source code in browser. It provides exported symbols summary for navigation. It answers questions like: definition; callers; implementers. It is browser frontend based on go-guru, which was developed by Go core team from Google. β€” @fzipp

pythia net/http

Requirements

go install github.com/fzipp/pythia@latest
go install golang.org/x/tools/cmd/guru@latest

⏫ Interactively visualize packages with goexplorer

Based on go-callvis, this tool is an interactive package explorer of packages. This tool have not been updated for a long time. β€” @ofabry

⏫ Make D2 graph of architecture and dependencies with go-arch-lint graph

Can include vendors or not, and be of type 'flow' or 'di'. β€” @fe3dback

go-arch-lint graph

Requirements

go install github.com/fe3dback/go-arch-lint@latest

Code Generation

⏫ Run go:generate in parallel

Official Go team encourages to run sequentially. However, in certain situations, such as lots of mocks, parallelization helps a lot, albeit, you should consider including your generated files in git. The solution below spawns multiple processes, each per pkg.

grep -rnw "go:generate" -E -l "${1:-*.go}" . | xargs -L1 dirname | sort -u | xargs -P 8 -I{} go generate {}

⏫ Generate String method for enum types

This is an official tool for generating String for enums. It supports overrides via comments. β€” official Go team

package painkiller

//go:generate stringer -type=Pill -linecomment

type Pill int

const (
  Placebo Pill = iota
  Ibuprofen
  Paracetamol
  PillAspirin   // Aspirin
  Acetaminophen = Paracetamol
)

// "Acetaminophen"
var s string = Acetaminophen.String()

Requirements

go install golang.org/x/tools/cmd/stringer@latest

⏫ πŸ”₯ Generate enums encoding with go-enum-encoding

Generate encoding code for enums. This follows json struct tag notation. β€” @nikolaydubina

go generate ./...
type Color struct{ c uint }

//go:generate go-enum-encoding -type=Color
var (
  Undefined = Color{}  // json:"-"
  Red       = Color{1} // json:"red"
  Green     = Color{2} // json:"green"
  Blue      = Color{3} // json:"blue"
)

Requirements

go install github.com/nikolaydubina/go-enum-encoding@latest

⏫ πŸ”₯ Generate enums with goenums

Generate strict and fast enums. Generated code is much more tightly typed than just iota defined enums. You will get JSON decoder and encoder as well. This tool allows to generate extra fields and default values in enum structs. β€” @zarldev

goenums <file-with-iota.go>
package milkyway

type planet int // Gravity[float64],RadiusKm[float64],MassKg[float64],OrbitKm[float64],OrbitDays[float64],SurfacePressureBars[float64],Moons[int],Rings[bool]

//go:generate goenums planets.go
const (
  unknown planet = iota // invalid
  mercury               // Mercury 0.378,2439.7,3.3e23,57910000,88,0.0000000001,0,false
  venus                 // Venus 0.907,6051.8,4.87e24,108200000,225,92,0,false
  earth                 // Earth 1,6378.1,5.97e24,149600000,365,1,1,false
  mars                  // Mars 0.377,3389.5,6.42e23,227900000,687,0.01,2,false
  jupiter               // Jupiter 2.36,69911,1.90e27,778600000,4333,20,4,true
  saturn                // Saturn 0.916,58232,5.68e26,1433500000,10759,1,7,true
  uranus                // Uranus 0.889,25362,8.68e25,2872500000,30687,1.3,13,true
  neptune               // Neptune 1.12,24622,1.02e26,4495100000,60190,1.5,2,true
)

Requirements

go install github.com/zarldev/goenums@latest

⏫ Generate data types from JSON Schema with go-jsonschema

JSON Schema is widely used standard for definition of structured data types. This tool will generate Go struct, decoder and validation based on JSON Schema spec. β€” @omissis

go-jsonschema -p main myschema.jsonschema
// Code generated by github.com/atombender/go-jsonschema, DO NOT EDIT.

package main

import "encoding/json"
import "fmt"

type Veggie struct {
        // Do I like this vegetable?
        VeggieLike bool `json:"veggieLike" yaml:"veggieLike" mapstructure:"veggieLike"`

        // The name of the vegetable.
        VeggieName string `json:"veggieName" yaml:"veggieName" mapstructure:"veggieName"`
}

// UnmarshalJSON implements json.Unmarshaler.
func (j *Veggie) UnmarshalJSON(b []byte) error {
        var raw map[string]interface{}
        if err := json.Unmarshal(b, &raw); err != nil {
                return err
        }
        if v, ok := raw["veggieLike"]; !ok || v == nil {
                return fmt.Errorf("field veggieLike in Veggie: required")
        }
        if v, ok := raw["veggieName"]; !ok || v == nil {
                return fmt.Errorf("field veggieName in Veggie: required")
        }
        type Plain Veggie
        var plain Plain
        if err := json.Unmarshal(b, &plain); err != nil {
                return err
        }
        *j = Veggie(plain)
        return nil
}

// A representation of a person, company, organization, or place
type A2Schema struct {
        // Fruits corresponds to the JSON schema field "fruits".
        Fruits []string `json:"fruits,omitempty" yaml:"fruits,omitempty" mapstructure:"fruits,omitempty"`

        // Vegetables corresponds to the JSON schema field "vegetables".
        Vegetables []Veggie `json:"vegetables,omitempty" yaml:"vegetables,omitempty" mapstructure:"vegetables,omitempty"`
}

Example

{
      "$id": "https://example.com/arrays.schema.json",
      "$schema": "https://json-schema.org/draft/2020-12/schema",
      "description": "A representation of a person, company, organization, or place",
      "type": "object",
      "properties": {
            "fruits": {
                  "type": "array",
                  "items": {
                        "type": "string"
                  }
            },
            "vegetables": {
                  "type": "array",
                  "items": {
                        "$ref": "#/$defs/veggie"
                  }
            }
      },
      "$defs": {
            "veggie": {
                  "type": "object",
                  "required": [
                        "veggieName",
                        "veggieLike"
                  ],
                  "properties": {
                        "veggieName": {
                              "type": "string",
                              "description": "The name of the vegetable."
                        },
                        "veggieLike": {
                              "type": "boolean",
                              "description": "Do I like this vegetable?"
                        }
                  }
            }
      }
}

Requirements

go get github.com/atombender/go-jsonschema/...
go install github.com/atombender/go-jsonschema@latest

⏫ Generate constructor for a struct with gonstructor

Constructor is a widely used useful pattern. This tool generates basic version of it that passes arguments to struct. It also supports initializer method. β€” @moznion

//go:generate gonstructor --type=Structure --constructorTypes=allArgs
type Structure struct {
  foo string
  bar io.Reader
  Buz chan interface{}
}

Requirements

go install golang.org/x/tools/cmd/goimports@latest
go install github.com/moznion/gonstructor/cmd/gonstructor@latest

⏫ Generate Table Driven Tests with gotests

This tool generates basic test placeholder. It is included into official Go plugin in VSCode and other major code editors. β€” @cweill

⏫ Generate mocks with mockgen

This mocking framework integrates well with Go testing package. β€” Go Core team

mockgen . Conn,Driver
# foo.go
type Foo interface {
  Bar(x int) int
}

func SUT(f Foo) {
// ...
}
# foo_test.go
func TestFoo(t *testing.T) {
  ctrl := gomock.NewController(t)
  defer ctrl.Finish()

  m := NewMockFoo(ctrl)

  // Does not make any assertions. Executes the anonymous functions and returns
  // its result when Bar is invoked with 99.
  m.
    EXPECT().
    Bar(gomock.Eq(99)).
    DoAndReturn(func(_ int) int {
      time.Sleep(1*time.Second)
      return 101
    }).
    AnyTimes()

  // Does not make any assertions. Returns 103 when Bar is invoked with 101.
  m.
    EXPECT().
    Bar(gomock.Eq(101)).
    Return(103).
    AnyTimes()

  SUT(m)
}

Requirements

go install github.com/golang/mock/mockgen@v1.6.0

⏫ Generate interface for a struct with ifacemaker

This is a development helper program that generates a Golang interface by inspecting the structure methods of an existing .go file. The primary use case is to generate interfaces for gomock, so that gomock can generate mocks from those interfaces. This makes unit testing easier. β€” @vburenin

ifacemaker -f human.go -s Human -i HumanIface -p humantest -y "HumanIface makes human interaction easy" -c "DONT EDIT: Auto generated"
# human.go
package main

import "fmt"

type Human struct {
  name string
  age  int
}

// Returns the name of our Human.
func (h *Human) GetName() string {
  return h.name
}

// Our Human just had a birthday! Increase its age.
func (h *Human) Birthday() {
  h.age += 1
  fmt.Printf("I am now %d years old!\n", h.age)
}

// Make the Human say hello.
func (h *Human) SayHello() {
  fmt.Printf("Hello, my name is %s, and I am %d years old.\n", h.name, h.age)
}

func main() {
  human := &Human{name: "Bob", age: 30}
  human.GetName()
  human.SayHello()
  human.Birthday()
}

# human_interface.go
// DONT EDIT: Auto generated
package humantest

// HumanIface makes human interaction easy
type HumanIface interface {
  // Returns the name of our Human.
  GetName() string
  // Our Human just had a birthday! Increase its age.
  Birthday()
  // Make the Human say hello.
  SayHello()
}

Requirements

go install github.com/vburenin/ifacemaker@latest

⏫ Generate interface for a struct with interfacer

This tool generates interface for a struct. Can be invoked in go:generate. β€” @rjeczalik

interfacer -for os.File -as mock.File
// Created by interfacer; DO NOT EDIT

package mock

import (
        "os"
)

// File is an interface generated for "os".File.
type File interface {
        Chdir() error
        Chmod(os.FileMode) error
        Chown(int, int) error
        Close() error
        Fd() uintptr
        Name() string
        Read([]byte) (int, error)
        ReadAt([]byte, int64) (int, error)
        Readdir(int) ([]os.FileInfo, error)
        Readdirnames(int) ([]string, error)
        Seek(int64, int) (int64, error)
        Stat() (os.FileInfo, error)
        Sync() error
        Truncate(int64) error
        Write([]byte) (int, error)
        WriteAt([]byte, int64) (int, error)
        WriteString(string) (int, error)
}

Requirements

go install github.com/rjeczalik/interfaces/cmd/interfacer@latest

⏫ Generate interface for a struct with struct2interface

This is alternative tool for interface generation that is aimed to be faster and leaner. It generates only pointer method receiver methods for a struct. β€” @reflog

struct2interface -f . -i IDecimal -p fpdecimal -s Decimal -o idecimal.go

Requirements

go install github.com/reflog/struct2interface@latest

⏫ Generate interface for CSV file with structer

This tool generates struct that can read and write CSV file of this struct. Order of fields is hardcoded. β€” @rjeczalik

structer -f aws-billing.csv -tag json -as billing.Record
# aws-billing.csv
# "InvoiceID","PayerAccountId","LinkedAccountId","RecordType","RecordID","BillingPeriodStartDate","BillingPeriodEndDate","InvoiceDate"
# "Estimated","123456","","PayerLineItem","5433212345","2016/01/01 00:00:00","2016/01/31 23:59:59","2016/01/21 19:19:06"          

# record.go
// Record is a struct generated from "aws-billing.csv" file.
type Record struct {
        InvoiceID              string    `json:"invoiceID"`
        PayerAccountID         int64     `json:"payerAccountID"`
        LinkedAccountID        string    `json:"linkedAccountID"`
        RecordType             string    `json:"recordType"`
        RecordID               int64     `json:"recordID"`
        BillingPeriodStartDate time.Time `json:"billingPeriodStartDate"`
        BillingPeriodEndDate   time.Time `json:"billingPeriodEndDate"`
        InvoiceDate            time.Time `json:"invoiceDate"`
}

// MarshalCSV encodes r as a single CSV record.
func (r *Record) MarshalCSV() ([]string, error) {
    ...
}

// UnmarshalCSV decodes a single CSV record into r.
func (r *Record) UnmarshalCSV(record []string) error {
    ...
}

Requirements

go install github.com/rjeczalik/interfaces/cmd/structer@latest

⏫ πŸ”₯ Generate decorator for interface with gowrap

GoWrap is a command line tool that generates decorators for Go interface types using simple templates. With GoWrap you can easily add metrics, tracing, fallbacks, pools, and many other features into your existing code in a few seconds. β€” @hexdigest

gowrap gen -p io -i Reader -t prometheus -o reader_with_metrics.go

Requirements

go install github.com/hexdigest/gowrap/cmd/gowrap@latest

⏫ Modify struct field tags with gomodifytags

This tool makes it easy to update, add or delete the tags and options in a struct field. You can add new tags, update existing tags (such as appending a new key, i.e: db, xml, etc..) or remove existing tags. It's intended to be used by an editor, but also has modes to run it from the terminal. β€” @fatih

Requirements

go install github.com/fatih/gomodifytags@latest

⏫ πŸ”₯ Generate code from OpenAPI 3 specification with oapi-codegen

Generate Go client and server boilerplate from OpenAPI 3 specifications. β€” @deepmap

oapi-codegen --config=config.yaml api.yaml

Requirements

go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest

⏫ πŸ”₯ Generate C-Go Bindings with c-for-go

This project allows to reuse existing C/C++ libraries in your Go applications, by automatically creating c-go bindings for a given set of C headers and the manifest file. We believe in component-based software engineering and think that reusing C/C++ code in Go applications could bring a huge boost to developer's productivity and system's performance. Read more about the motivation: top reasons to use bindings. β€” @xlab

Generics

⏫ πŸ”₯ Enum via generics with enum

Type safe enums for Go without code generation or reflection. β€” @orsinium

type Color enum.Member[string]

var (
  Red    = Color{"red"}
  Green  = Color{"green"}
  Blue   = Color{"blue"}
  Colors = enum.New(Red, Green, Blue)
)

Refactor

⏫ Replace symbol with gofmt

I found this in announcement notice of Go 1.18 for changes to interface{} to any. This can be useful for other refactorings too.

gofmt -w -r 'interface{} -> any' .

⏫ Apply refactoring patches with gopatch

With this tool it is very easy to perform refactorings. It is also possible to organize and maintain your refactoring procedures through patches. β€” Uber

@@
@@
-import "errors"

-errors.New(fmt.Sprintf(...))
+fmt.Errorf(...)

Example

return errors.New(fmt.Sprintf("invalid port: %v", err))
// becomes
return fmt.Errorf("invalid port: %v", err)

⏫ Keep consistent ordering of imports with goimports

This is official tool for for grouping and sorting imports. However, it has only basic grouping functionality. β€” Go Core team

goimports -w -local .

Requirements

go install golang.org/x/tools/cmd/goimports@latest

⏫ Keep consistent ordering of imports with gci

This tool splits all import blocks into different sections, now support five section types: standard (e.g. 'fmt'); custom; default; blank; dot. It will keep each section sorted and keep ordering of sections consistent. β€” @daixiang0

gci write -s standard -s default -s "prefix(github.com/daixiang0/gci)" main.go
// before
package main
import (
  "golang.org/x/tools"
  
  "fmt"
  
  "github.com/daixiang0/gci"
)

// after
package main
import (
    "fmt"

    "golang.org/x/tools"

    "github.com/daixiang0/gci"
)

Requirements

go install github.com/daixiang0/gci@latest

⏫ Keep consistent ordering of imports with goimportx

This tool groups and sorts imports within groups. It keeps consistent ordering of groups. Detection of groups may be not always accurate. β€” @anqiansong

goimportx --file /path/to/file.go --group "system,local,third"
package main

import (
  "flag"
  "io"
  "log"
  "os"

  "github.com/nikolaydubina/mdpage/page"
  "github.com/nikolaydubina/mdpage/render"
  yaml "gopkg.in/yaml.v3"
)

Requirements

go install github.com/anqiansong/goimportx@latest

Errors

⏫ Errors with return traces with errtrace

Return trace is the path that error took to return to user. This can be more illustrative than typical stack trace that produced the error. This tool have convenience automatic instrumentation CLI to update your code. β€” @bracesdev

git ls-files -- '*.go' | xargs errtrace -w

Requirements

use package "braces.dev/errtrace"
instrument code by wrapping errors through all functions with this library

⏫ Errors with stack traces and source fragments with tracerr

This library collects stack traces and pretty prints code fragments. Stack traces induce performance penalty. β€” @ztrue

package main

import (
  "io/ioutil"

  "github.com/ztrue/tracerr"
)

func main() {
  if err := read(); err != nil {
    tracerr.PrintSourceColor(err)
  }
}

func read() error {
  return readNonExistent()
}

func readNonExistent() error {
  _, err := ioutil.ReadFile("/tmp/non_existent_file")
  // Add stack trace to existing error, no matter if it's nil.
  return tracerr.Wrap(err)
}

⏫ Pretty print panic messages with panicparse

Read panic messages easier. Need to redirect STDERR to this tool with panic stack traces. The tool has HTML output and does lots of deduplication and enhancements. Refer to examples in original repo. β€” @maruel

go test -v |& pp

Requirements

go install github.com/maruel/panicparse/v2/cmd/pp@latest

Build

⏫ πŸ”₯ Fetch private dependencies in CI

If you are building in CI (e.g. GitHub Actions), you need to download private repositories. Common way to accomplish this is with job like below.

name: go private modules
env:
  USER: ${{ secrets.USER }}
  TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }}
run: git config --global url."https://${USER}:${TOKEN}@github.com".insteadOf "https://github.com"

⏫ Show compiler optimization decisions on heap and inlining

Building with -m flag will show decisions of compiler on inlining and heap escape. This can help you to validate your understanding of your code and optimize it.

go build -gcflags="-m -m" . 2>&1 | grep inline

Example

...
./passengerfp.go:25:6: cannot inline (*PassengerFeatureTransformer).Fit: function too complex: cost 496 exceeds budget 80
...
./passengerfp.go:192:6: can inline (*PassengerFeatureTransformer).NumFeatures with cost 35 as: method(*PassengerFeatureTransformer) func() int { if e == nil { return 0 }; count := 6; count += (*transformers.OneHotEncoder).NumFeatures(e.Sex); count += (*transformers.OneHotEncoder).NumFeatures(e.Embarked); return count }
...
./passengerfp.go:238:43: inlining call to transformers.(*OneHotEncoder).FeatureNames
./passengerfp.go:238:43: inlining call to transformers.(*OneHotEncoder).NumFeatures
...
./passengerfp.go:151:7: parameter e leaks to {heap} with derefs=0:
./passengerfp.go:43:11: make(map[string]uint) escapes to heap

⏫ Disable inlining

Usually you may not need it, but can reduce binary size and even improve performance.

go build -gcflags="-l" .

⏫ Aggressive inlining

Usually you may not need it, but can improve performance. This includes mid-stack inlining.

go build -gcflags="-l -l -l -l" .

⏫ Profile-guided optimization

Starting go 1.20 compiler supports Profile-guided optimization. You need to collect profiles and then supply in computation to compiler. You can get improvement in performance by around 4%. Official guideline.

1. store a `pprof` CPU profile with filename default.pgo in the main package directory of the profiled binary
2. build with `go build -pgo=auto``, which will pick up `default.pgo` files automatically.

⏫ Manually disable or enable cgo

Disable cgo with CGO_ENABLED=0 and enable with CGO_ENABLED=1. If you don't, cgo may end-up being enabled or code dynamically linked if, for example, you use some net or os packages. You may want to disable cgo to improve performance, since compiler and runtime would have easier job optimizing code. This also should reduce your image size, as you can have alpine image with less shared libraries.

⏫ Include metadata in binary during compilation with ldflags

You can pass metadata through compiler to your binary. This is useful for including things like git commit, database schema version, integrity hashes. Variables can only be strings.

go build -v -ldflags="-X 'main.Version=v1.0.0'"
go build -v -ldflags="-X 'my/pkg/here.Variable=some-string'"
package main

var Version string

func main() {
  // Version here has some value
  ...
}

⏫ πŸ”₯ Check if symbol or package is included in binary

This is useful for investigations during performance optimization, security, or compiler work. First spotted in blog.

go tool nm main | grep -Ei '<symbol A>|<symbol B>|...'
go tool nm main | grep -Ei 'golangci-lint|gofumpt'

⏫ πŸ”₯ Build for Raspberry Pi, Virtual Machine, embedded or normal PC with gokrazy

Turn your Go program(s) into an appliance running on the Raspberry Pi 3, Pi 4, Pi 5, Pi Zero 2 W, or amd64 PCs! gokrazy.org. The surface area for security vulnerabilities is drastically reduced. gokrazy uses its own minimal Go userland instead of a traditional Linux distribution base. The root filesystem is entirely read-only (making persistent malware installation hard) and new versions of the system are installed by overwriting the root file system with the new version. No default shell access: There is neither xz nor OpenSSH on a gokrazy system. Interactive access for debugging is possible, but needs to be explicitly started. β€” @stapelberg

follow instructions at: https://gokrazy.org/quickstart/

Requirements

go install github.com/gokrazy/tools/cmd/gok@main

⏫ πŸ”₯ Visualise dependencies size in compiled binaries with go-size-analyzer

A tool for analyzing the dependencies in compiled Golang binaries, providing insight into their impact on the final build. WebAssembly demo: https://gsa.zxilly.dev. β€” @Zxilly

gsa --web target_binary

Requirements

go install github.com/Zxilly/go-size-analyzer/cmd/gsa@latest

⏫ Make treemap breakdown of Go executable binary with go-binsize-treemap

Useful for studying Go compiler, large projects, projects with C/C++ and cgo, 3rd party dependencies, embedding. However, total size may not be something to worry about for your executable. β€” @nikolaydubina

go tool nm -size <binary finename> | go-binsize-treemap > binsize.svg

Requirements

go install github.com/nikolaydubina/go-binsize-treemap@latest

⏫ Custom import path

Go can automatically fetch from custom http/https servers using <meta> tag to discover how to fetch code. There are multiple tools that can help set this up. This can help for security and analytics. This is also known as vanity URLs. documentation.

# some notable examples
golang.org/x/exp
go.uber.org/multierr
honnef.co/go/tools/cmd/staticcheck

⏫ Custom import path with govanityurls

Simple HTTP server that lets you host custom import paths for your Go packages. β€” Google

govanityurls

Requirements

go install github.com/GoogleCloudPlatform/govanityurls@latest

⏫ Custom import path with sally

Simple HTTP server that lets you host custom import paths for your Go packages. β€” Uber

sally

Requirements

go install go.uber.org/sally@latest

⏫ Custom import path with kkn.fi/vanity

Simple HTTP server that lets you host custom import paths for your Go packages. β€” @kare

vanity

Requirements

go get kkn.fi/vanity

⏫ Custom import path enforcement

When import path is using custom domain, it is possible to block code from compilation unless it is used. This can help ensure security and prevent breaking changes. documentation.

package pdf // import "rsc.io/pdf"

⏫ Manage multiple Go versions with Goenv

This tool makes it easier for managing multiple Go versions on same host. This works through intercepting Go commands and directing them to the right Go version bin and directory. Official Go documentation on this topic. β€” @clivern

⏫ πŸ”₯ Transpile C to Go with cxgo

CxGo is a tool for translating C source code to Go (aka transpiler, source-to-source compiler). It uses cc v3 for preprocessing and parsing C (no clang/gcc dependencies!) and a custom type-checker and AST translation layer to make the best output possible. β€” @dennwc

cxgo file main.c

Requirements

go install github.com/gotranspile/cxgo/cmd/cxgo@latest

⏫ πŸ”₯ Transpile Go to Javascript with gopherjs

GopherJS compiles Go code (go.dev) to pure JavaScript code. Its main purpose is to give you the opportunity to write front-end code in Go which will still run in all browsers. β€” @neelance

gopherjs build <package>

Requirements

go install github.com/gopherjs/gopherjs@v1.19.0-beta1

⏫ πŸ”₯ Run compile-time function evaluation with prep

By using prep.Comptime, you can evaluate functions at compile time, replacing them with their computed results. β€” @pijng

go build -a -toolexec="prep" main.go
package main

import (
  "fmt"
  "github.com/pijng/prep"
)

func main() {
  // This will be evaluated at compile-time
  result := prep.Comptime(fibonacci(300))

  fmt.Println("Result:", result)
}

func fibonacci(n int) int {
  fmt.Printf("calculating fibonacci for %d\n", n)

  if n <= 1 {
    return n
  }
  a, b := 0, 1
  for i := 2; i <= n; i++ {
    a, b = b, a+b
  }
  return b
}

Requirements

go install github.com/pijng/prep/cmd/prep@latest

Assembly

⏫ Get assembly of Go code snippets online

Use godbolt.org to compile and see assembly of short Go code. You can check different platforms and compilers including cgo. This tool is commonly used by C++ community. β€” @mattgodbolt

⏫ Get Go SSA intermediary representation with ssaplayground

Check what does Go compiler do. Might be useful if you trying to optimize some code or learn more about compiler. https://golang.design/gossa. β€” @changkun

⏫ View Go assembly interactively with lensm

Understand how Go is compiled better. β€” @egonelbre

Requirements

go install loov.dev/lensm@main

⏫ View Go assembly with color annotation with pat/disfunc

This tool shows assembly of functions and what lines mean by color. β€” @maruel

disfunc -f 'nin\.CanonicalizePath$' -pkg ./cmd/nin | less -R

Requirements

go install github.com/maruel/pat/cmd/...@latest

⏫ Generate Go assembly in Go with avo

Write better quality Go assembly quicker in Go language itself. This tool conveniently generates stub for Go code to call your generated assembly. Used by Go core. β€” @mmcloughlin

//go:build ignore
// +build ignore

package main

import . "github.com/mmcloughlin/avo/build"

func main() {
  TEXT("Add", NOSPLIT, "func(x, y uint64) uint64")
  Doc("Add adds x and y.")
  x := Load(Param("x"), GP64())
  y := Load(Param("y"), GP64())
  ADDQ(x, y)
  Store(y, ReturnIndex(0))
  RET()
  Generate()
}

⏫ Generate AST for code snippets with go/ast

Access Go core AST mechanism to generate AST.

package main

import (
  "go/ast"
  "go/parser"
  "go/token"
)

func main() {
  fs := token.NewFileSet()
  tr, _ := parser.ParseExpr("(3-1) * 5")
  ast.Print(fs, tr)
}

Example

0  *ast.BinaryExpr {
1  .  X: *ast.ParenExpr {
2  .  .  Lparen: -
3  .  .  X: *ast.BinaryExpr {
4  .  .  .  X: *ast.BasicLit {
5  .  .  .  .  ValuePos: -
6  .  .  .  .  Kind: INT
7  .  .  .  .  Value: "3"
8  .  .  .  }
9  .  .  .  OpPos: -
10  .  .  .  Op: -
11  .  .  .  Y: *ast.BasicLit {
12  .  .  .  .  ValuePos: -
13  .  .  .  .  Kind: INT
14  .  .  .  .  Value: "1"
15  .  .  .  }
16  .  .  }
17  .  .  Rparen: -
18  .  }
19  .  OpPos: -
20  .  Op: *
21  .  Y: *ast.BasicLit {
22  .  .  ValuePos: -
23  .  .  Kind: INT
24  .  .  Value: "5"
25  .  }
26  }

⏫ Generate AST for code snippets with go2ast

This is a wrapper around go/ast machinery that makes generating AST easier. β€” @reflog

echo "a := 1" | go2ast

Example

[]ast.Stmt {
  &ast.AssignStmt {
    Lhs: []ast.Expr {
      &ast.Ident {
        Name: "a",
      },
    },
    Tok: :=,
    Rhs: []ast.Expr {
      &ast.BasicLit {
        ValuePos: 32,
        Kind: INT,
        Value: "1",
      },
    },
  },
}

Requirements

go install github.com/reflog/go2ast@latest

⏫ Visualize Go SSA function using Graphviz with go-ssaviz

This tool provides a visual overview of Go SSA function using Graphviz. This is especially useful in SSA-based static analysis. This tool generates an HTML page that is easy to navigate. demo. β€” @SilverRainZ

go-ssaviz ./...

Requirements

# get graphviz
go install github.com/SilverRainZ/go-ssaviz@latest

⏫ Make graph of AST with astgraph

This tool visualizes AST as graph, which may be useful to navigate and understand Go AST. This tool has not been maintained for a while. β€” @xiazemin

Requirements

graphviz

⏫ Convert C assembly to Go assembly with c2goasm

This tool can convert C assembly .s into Go assbmely .s files. This is useful for reusing compiler optimizations such as SIMD or loop unrolling in C, which can lead to 10x speedups. However, project has been archived 4+ years ago. β€” @fwessels

gcc -O3 -march=native -S -o c_code.s c_code.c
c2goasm -a c_code.s go_c_code.s
go build -o go_c_code.o -gcflags="-S" go_c_code.s

Requirements

go install github.com/minio/c2goasm@latest

Execution

⏫ πŸ”₯ Embed Go Playground to your blog with codapi

Codapi is a platform for embedding interactive code snippets directly into your product documentation, online course or blog post. example. β€” @nalgeon

'''go
package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}
'''

<codapi-snippet sandbox="go" editor="basic">
</codapi-snippet>

Requirements

deploy sandbox
embed javascript in your blog
markdown go code blocks will turn into runnable snippets

⏫ Embed Go Playground to your blog with goplay

Embed interactive Go Playground component into your blog. Hugo, Docusaurus, Ghost are supported. There is also another tool soksan, however it is discontinued. Live demo with guideline. Other resources β€” GitLab considering to add it in issue; alternative implementation guideline. β€” @ggicci

## Sample Code

{{% goplay %}}
'''go
package main

func main() {
  println("hello world")
}
'''
{{% /goplay %}}

Requirements

reverse proxy server to https://play.golang.org
bloging platform with support for embedding javascript

⏫ Run alternative Go Playground with goplay.tools

Improved Go Playground featuring dark theme, code autocomplete, vim mode, WebAssembly. Available at https://goplay.tools/. β€” @x1unix

⏫ πŸ”₯ Use TinyGo Playground with tinygo

TinyGo is an alternative Go compiler that focuses on embedded devices, and WASM. There are some Go constructs and packages that not supported. In this online playground you can verify your code.

⏫ Run interactive Go kernels in Jupyter Notebook with gophernotes

Run interactive Go interpreter in Jupyter Notebook browser. As of 2023-06-04, it is using gomacro interpreter and can have issues with loading 3rd party packages. β€” @gopherdata

Requirements

# jupyter notebook
go install github.com/gopherdata/gophernotes@v0.7.5
# more instructions on how to install Jupyter Notebook Go kernel in original repo

⏫ Run interactive Go interpreter with yaegi

This interpreter works with 3rd party packages located in $GOPATH/src. It can also be triggered within Go programmatically via Eval(). Works everywhere Go works. β€” @traefik

yaegi

Example

$ yaegi
> import "github.com/nikolaydubina/fpdecimal"
: 0x140000faaf0
> a, _ := fpdecimal.FromString("10.12") 
: {0}
> b, _ := fpdecimal.FromString("5.38")
: {0}
> c := a.Add(b)   
: {15500}
> c.String()
: 15.500
>

Requirements

go install github.com/traefik/yaegi@latest

⏫ Run interactive Go interpreter with gomacro

This is interactive Go interpreter and debugger with REPL, Eval, generics and Lisp-like macros. You can run functions, import 3rd patry packages. Can be useful for learning and experimentation. Some nice features: autocomplete; constant expressions arithmetics. As of 2023-06-02, issues with importing 3rd paty package are possible. β€” @cosmos72

gomacro

Example

$ gomacro
gomacro> import "fmt"
gomacro> fmt.Println("hello, world!")
hello, world!
14      // int
<nil>   // error
gomacro>

Requirements

go install github.com/cosmos72/gomacro@latest

⏫ Run Go function in shell with gorram

Run Go one-liners. This tool will print to STDOUT the return of a function call. β€” @natefinch

cat README.md | gorram crypto/sha1 Sum
echo 12345 | gorram encoding/base64 StdEncoding.EncodeToString
gorram net/http Get https://google.com

Requirements

go install github.com/natefinch/gorram@latest

⏫ Run Go function in shell with gosh

Run Go functions in shell. You can also pass parameters directly from shell. β€” @nickwells

gosh -pln '"Hello, World!"'
gosh -pln 'math.Pi'
gosh -n -b 'count := 0' -e 'count++' -a-pln 'count'
gosh -http-handler 'http.FileServer(http.Dir("/tmp/xxx"))'
gosh -n -b-p '"Radius: "' -e 'r, err := strconv.ParseFloat(_l.Text(), 64)' -e-s iferr -pf '"Area: %9.2f
", r*r*math.Pi' -p '"Radius:"'

Requirements

go install github.com/nickwells/utilities/gosh@latest

⏫ Run simple fileserver with net/http

It takes one line to run HTTP file server in Go. Akin to famous oneliner in Python python3 -m http.server and python -m SimpleHTTPServer. Run this file as usually go run <filename>.

package main

import "net/http"

func main() { http.ListenAndServe(":9000", http.FileServer(http.Dir("."))) }

⏫ Create 3D visualization of concurrency traces with gotrace

Fresh artistic perspective on coroutines execution. There is no advanced functions and it is hard to analyze production systems. However, it could be interesting for educational purposes. β€” @divan

Requirements

go install github.com/divan/gotrace@latest
patch Go compiler, available via Docker
more instructions in original repo

⏫ Wrap command with os/exec

Originally posted in blog.

cmd := exec.Command("ls", "/usr/local/bin")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()

⏫ Capture output of command to file with os/exec

Originally posted in blog. β€” Aaron Son

log, err := os.Create("output.log")
if err != nil {
  return err
}
defer log.Close()
cmd := exec.Command("ls", "/usr/local/bin")
cmd.Stdout = log
cmd.Stderr = log
return cmd.Run()

⏫ Capture output of command and process it with os/exec

Originally posted in blog. β€” Aaron Son

cmd := exec.Command("ls", "/usr/local/bin")
stdout, err := cmd.StdoutPipe()
if err != nil {
  return err
}
scanner := bufio.NewScanner(stdout)
err = cmd.Start()
if err != nil {
  return err
}
for scanner.Scan() {
  // Do something with the line here.
  ProcessLine(scanner.Text())
}
if scanner.Err() != nil {
  cmd.Process.Kill()
  cmd.Wait()
  return scanner.Err()
}
return cmd.Wait()

⏫ Piping between processes with os/exec

ls /usr/local/bin | grep pip. Originally posted in blog. β€” Aaron Son

r, w, err := os.Pipe()
if err != nil {
  return err
}
defer r.Close()

ls := exec.Command("ls", "/usr/local/bin")
ls.Stdout = w
err = ls.Start()
if err != nil {
  return err
}
defer ls.Wait()
w.Close()

grep := exec.Command("grep", "pip")
grep.Stdin = r
grep.Stdout = os.Stdout
return grep.Run()

⏫ errgroup and CommandContext with os/exec

Originally posted in blog. β€” Aaron Son

eg, ctx := errgroup.WithContext(context.Background())
sleeps := make([]*exec.Cmd, 3)
sleeps[0] = exec.CommandContext(ctx, "sleep", "100")
sleeps[1] = exec.CommandContext(ctx, "sleep", "100")
sleeps[2] = exec.CommandContext(ctx, "sleep", "notanumber")
for _, s := range sleeps {
  s := s
  eg.Do(func() error {
    return s.Run()
  })
}
return eg.Wait()

Monitor

⏫ πŸ”₯ Monitor Go Runtime metrics with opentelemetry

These are standard metrics for Go runtime exposed in OpenTelemetry format. Grafana dashboard. β€” Google, (dashboard by @nikolaydubina)

import "go.opentelemetry.io/contrib/instrumentation/runtime"
...
runtime.Start()

Requirements

opentelemetry collector
metrics backend (e.g. prometheus)
dashboard (e.g. grafana)

⏫ Monitor goroutines with grmon

Command line monitoring for goroutines. β€” @bcicen

grmon

Requirements

# start pprof server or grmon in your Go process
go install github.com/bcicen/grmon@latest

⏫ Monitor Go processes with gops

Monitoring memory of Go processes, forcing GC, getting version of Go of processes. β€” Google

gops

Example

983   980    uplink-soecks  go1.9   /usr/local/bin/uplink-soecks
52697 52695  gops           go1.10  /Users/jbd/bin/gops
4132  4130   foops        * go1.9   /Users/jbd/bin/foops
51130 51128  gocode         go1.9.2 /Users/jbd/bin/gocode

Requirements

go install github.com/google/gops@latest

⏫ πŸ”₯ Monitor Go runtime metrics in browser with live-pprof

This is minimal single binary tool that lets you monitor Go app performance. This can be an attractive alternative for local development to avoid operations overhead of full monitoring setup (e.g. Prometheus, Grafana). β€” @clement2026

live-pprof 6060

Requirements

go install github.com/moderato-app/live-pprof@v1

⏫ Monitor Go runtime metrics in browser with statsviz

This tool exposes HTTP endpoint with charts for Go runtime such as heap, objects, goroutines, GC pauses, scheduler. This is useful drop-in solution for visualization of Go runtime. β€” @arl

Requirements

go get github.com/arl/statsviz@latest

⏫ Auto-Instrument all functions with go-instrument

Automatically instrument all functions with Open Telemetry Spans by code generation. Inserts errors into Spans. β€” @nikolaydubina

find . -name "*.go" | xargs -I{} go-instrument -app my-service -w -filename {}

Requirements

go install github.com/nikolaydubina/go-instrument@latest

⏫ Auto-Instrument all functions with otelinji

Automatically instrument all functions with Open Telemetry Spans by code generation. Inserts errors into Spans. Supports custom templates and can be used for Open Tracing or any custom insertions. β€” @hedhyw

otelinji -w -filename input_file.go
otelinji -filename input_file.go > input_file.go
find . -name "*.go" | grep -v "vendor/\|.git/\|_test.go" | xargs -n 1 -t otelinji -w -filename

Requirements

go install github.com/hedhyw/otelinji/cmd/otelinji@latest

⏫ Auto-Instrument functions for DataDog with orchestrion

This is official Datadog tool for automatic instrumentation of code. It has very convenient compiler directives for instrumentation. β€” @DataDog

orchestrion -w ./
//dd:span my:tag
func GetSomeData(ctx context.Context) ([]byte, error) {
  ...

Requirements

go install github.com/datadog/orchestrion@latest

⏫ Continuous Profiling with Pyroscope

This tool allows to ingest profiling data from your application. You would need to add integration in your main file that will sample in-process data and send it to Pyroscope. Here are useful resources blog-go-memory-leaks. β€” Grafana Labs

Benchmark

⏫ Run benchmarks

Start here. This is the standard tool for benchmarking. It can also do advanced features like mutex profiles. More flags are in Go documentation and go help testflag.

go test -bench=. -benchmem -benchtime=10s ./...

Example

goos: darwin
goarch: arm64
pkg: github.com/nikolaydubina/fpmoney
BenchmarkArithmetic/add_x1-10                     1000000000             0.5 ns/op           0 B/op           0 allocs/op
BenchmarkArithmetic/add_x100-10                     18430124            64.6 ns/op           0 B/op           0 allocs/op
BenchmarkJSONUnmarshal/small-10                      3531835           340.7 ns/op         198 B/op           3 allocs/op
BenchmarkJSONUnmarshal/large-10                      2791712           426.9 ns/op         216 B/op           3 allocs/op
BenchmarkJSONMarshal/small-10                        4379685           274.4 ns/op         144 B/op           4 allocs/op
BenchmarkJSONMarshal/large-10                        3321205           345.8 ns/op         192 B/op           5 allocs/op
PASS
ok      github.com/nikolaydubina/fpmoney    62.744s

⏫ Table-driven benchmarks

Similar to tests, Go supports table-driven benchmarks, which is very helpful for fine gradation of meta-parameters. More details in the Go blog.

func benchIteratorSelector(b *testing.B, n int) {
  // ... setup here
  b.ResetTimer()
  for n := 0; n < b.N; n++ {
    err := myExpensiveFunc()
    if err != nil {
      b.Error(err)
    }
  }
}

func BenchmarkIteratorSelector(b *testing.B) {
  for _, q := range []int{100, 1000, 10000, 100000} {
    b.Run(fmt.Sprintf("n=%d", q), func(b *testing.B) {
      benchIteratorSelector(b, q)
    })
  }
}

Example

BenchmarkIteratorSelector/n=100-10    	  297792	      4265 ns/op	    5400 B/op	      13 allocs/op
BenchmarkIteratorSelector/n=1000-10   	   31400	     38182 ns/op	    9752 B/op	      16 allocs/op
BenchmarkIteratorSelector/n=10000-10  	    3134	    380777 ns/op	   89112 B/op	      24 allocs/op
BenchmarkIteratorSelector/n=100000-10 	     310	   3827292 ns/op	  912410 B/op	      32 allocs/op

⏫ πŸ”₯ Align benchmark output

Go aligns benchmarks names to longest seen so far. Create file with name that is lexicographically first and has noop benchmark like following.

func BenchmarkNoop(b *testing.B) { b.Run("--------------------------------", func(b *testing.B) {}) }

Example

$ go test -bench .
goos: darwin
goarch: arm64
pkg: github.com/nikolaydubina/go-hackers-delight
BenchmarkNoop/---------------------------------16         	1000000000	         0.0000001 ns/op
BenchmarkAbs/basic-16                                     	1000000000	         0.9826 ns/op
BenchmarkAbs/Abs-16                                       	1000000000	         0.9647 ns/op
BenchmarkAbs/Abs2-16                                      	1000000000	         0.9943 ns/op
BenchmarkAbs/Abs3-16                                      	1000000000	         0.9819 ns/op
BenchmarkAbs/Abs4-16                                      	1000000000	         1.003 ns/op
BenchmarkAbs/AbsFastMul-16                                	1000000000	         0.9598 ns/op
BenchmarkAvg/basic-16                                     	973716225	         2.045 ns/op
BenchmarkAvg/AvgFloor-16                                  	602586224	         2.050 ns/op
BenchmarkAvg/AvgCeil-16                                   	582029594	         2.054 ns/op
BenchmarkCycleThree/basic-16                              	767160418	         1.560 ns/op
BenchmarkCycleThree/CycleThreeValues-16                   	438818894	         2.729 ns/op
BenchmarkLeadingZeros/uint32/basic-16                     	1000000000	         0.9419 ns/op

⏫ Generate benchmak CPU and Memory profiles with go test

This is useful for identifying most time or memory consuming parts. Recommended to run for single benchmark at a time and with -count or -benchtime for better accuracy.

go test -bench=<my-benchmark-name> -cpuprofile cpu.out -memprofile mem.out ./...

⏫ Visualize callgraph of profiles with pprof

Once you generate profiles, visualize them with pprof. Both memory and CPU profiles are supported. Many options are available. Refer to the link you get in SVG to how to interpret this graph. More official documentation blog, pkg-doc. β€” official Go team

go tool pprof -svg cpu.out > cpu.svg
go tool pprof -svg mem.out > mem.svg

⏫ Visualize flamegraphs of profiles with pprof

Latest versions of pprof can also render Flamegraphs for profiles. Make sure you set -http to start webserver. Then it is available in "View > Graph" in at http://0.0.0.0:80. β€” Google

pprof -http=0.0.0.0:80 cpu.out

Requirements

go install github.com/google/pprof@latest

⏫ Visualize profiles online

You can also visualize profiles with online tools are aloso available https://www.speedscope.app (cpu).

⏫ Get delta between two benchmarks with benchstat

This is standard way to compare two benchmark outputs. Names of benchmarks should be the same. Generate benchmarks as per usual. You would get multiple tables per dimension. If no output, then pass -split="XYZ". If you do not see delta, then pass -count=2 or more in benchmark generation. It is recommended to have alternative implementations in different packages, to keep benchmark names the same. β€” official Go team

benchstat -split="XYZ" old.txt new.txt

Example

name                    old time/op    new time/op    delta
JSONUnmarshal/small-10     502ns Β± 0%     331ns Β± 0%   -33.99%  (p=0.008 n=5+5)
JSONUnmarshal/large-10     572ns Β± 0%     414ns Β± 0%   -27.64%  (p=0.008 n=5+5)
JSONMarshal/small-10       189ns Β± 0%     273ns Β± 0%   +44.20%  (p=0.008 n=5+5)
JSONMarshal/large-10       176ns Β± 0%     340ns Β± 0%   +93.29%  (p=0.008 n=5+5)

name                    old alloc/op   new alloc/op   delta
JSONUnmarshal/small-10      271B Β± 0%      198B Β± 0%   -26.94%  (p=0.008 n=5+5)
JSONUnmarshal/large-10      312B Β± 0%      216B Β± 0%   -30.77%  (p=0.008 n=5+5)
JSONMarshal/small-10       66.0B Β± 0%    144.0B Β± 0%  +118.18%  (p=0.008 n=5+5)
JSONMarshal/large-10       72.0B Β± 0%    192.0B Β± 0%  +166.67%  (p=0.008 n=5+5)

name                    old allocs/op  new allocs/op  delta
JSONUnmarshal/small-10      6.00 Β± 0%      3.00 Β± 0%   -50.00%  (p=0.008 n=5+5)
JSONUnmarshal/large-10      7.00 Β± 0%      3.00 Β± 0%   -57.14%  (p=0.008 n=5+5)
JSONMarshal/small-10        2.00 Β± 0%      4.00 Β± 0%  +100.00%  (p=0.008 n=5+5)
JSONMarshal/large-10        2.00 Β± 0%      5.00 Β± 0%  +150.00%  (p=0.008 n=5+5)

Requirements

go install golang.org/x/perf/cmd/benchstat@latest

⏫ Get summary of benchmarks with benchstat

Compare multiple benchmarks. Names of benchmarks should be the same. Generate benchmarks as per usual. You would get multiple tables per dimension. If no output, then pass -split="XYZ". It is recommended to have alternative implementations in different packages, to keep benchmark names the same. β€” official Go team

benchstat -split="XYZ" int.txt float32.txt fpmoney.txt

Example

name \ time/op          int.bench   float32.bench  fpmoney.bench
JSONUnmarshal/small-10  481ns Β± 2%     502ns Β± 0%     331ns Β± 0%
JSONUnmarshal/large-10  530ns Β± 1%     572ns Β± 0%     414ns Β± 0%
JSONMarshal/small-10    140ns Β± 1%     189ns Β± 0%     273ns Β± 0%
JSONMarshal/large-10    145ns Β± 0%     176ns Β± 0%     340ns Β± 0%

name \ alloc/op         int.bench   float32.bench  fpmoney.bench
JSONUnmarshal/small-10   269B Β± 0%      271B Β± 0%      198B Β± 0%
JSONUnmarshal/large-10   288B Β± 0%      312B Β± 0%      216B Β± 0%
JSONMarshal/small-10    57.0B Β± 0%     66.0B Β± 0%    144.0B Β± 0%
JSONMarshal/large-10    72.0B Β± 0%     72.0B Β± 0%    192.0B Β± 0%

name \ allocs/op        int.bench   float32.bench  fpmoney.bench
JSONUnmarshal/small-10   6.00 Β± 0%      6.00 Β± 0%      3.00 Β± 0%
JSONUnmarshal/large-10   7.00 Β± 0%      7.00 Β± 0%      3.00 Β± 0%
JSONMarshal/small-10     2.00 Β± 0%      2.00 Β± 0%      4.00 Β± 0%
JSONMarshal/large-10     2.00 Β± 0%      2.00 Β± 0%      5.00 Β± 0%

Requirements

go install golang.org/x/perf/cmd/benchstat@latest

⏫ Benchmark against git commit with pat/ba

This tool runs benchmarks and shows delta between git commits. It can also be useful in GitHub Actions. β€” @maruel

Example

$ ba -against HEAD~1
warming up
go test -bench . -benchtime 100ms -count 1 -run ^$ -cpu 1 ./...
git checkout HEAD~1
go test -bench . -benchtime 100ms -count 1 -run ^$ -cpu 1 ./...
git checkout 02152d698f7d548c
02152d698f7d548c...HEAD~1 (1 commits), 100ms x 2 times/batch, batch repeated 3 times.
go test -bench . -benchtime 100ms -count 2 -run ^$ -cpu 1 ./...
git checkout HEAD~1
go test -bench . -benchtime 100ms -count 2 -run ^$ -cpu 1 ./...
git checkout 02152d698f7d548c
go test -bench . -benchtime 100ms -count 2 -run ^$ -cpu 1 ./...
git checkout HEAD~1
go test -bench . -benchtime 100ms -count 2 -run ^$ -cpu 1 ./...
git checkout 02152d698f7d548c
go test -bench . -benchtime 100ms -count 2 -run ^$ -cpu 1 ./...
git checkout HEAD~1
go test -bench . -benchtime 100ms -count 2 -run ^$ -cpu 1 ./...
git checkout 02152d698f7d548c
name                  old time/op    new time/op    delta
HashCommand             69.0ns Β± 2%    67.7ns Β± 2%  -1.91%  (p=0.041 n=6+6)
CLParser                 281Β΅s Β± 1%     281Β΅s Β± 1%    ~     (p=0.699 n=6+6)
LoadManifest             437ms Β± 7%     430ms Β± 3%    ~     (p=0.937 n=6+6)
CanonicalizePathBits    85.9ns Β± 1%    86.2ns Β± 0%    ~     (p=1.000 n=6+6)
CanonicalizePath        83.9ns Β± 1%    84.6ns Β± 0%    ~     (p=0.058 n=6+6)

name                  old alloc/op   new alloc/op   delta
HashCommand              0.00B          0.00B         ~     (all equal)
CLParser                 164kB Β± 0%     164kB Β± 0%    ~     (all equal)
LoadManifest             298MB Β± 0%     295MB Β± 0%  -0.78%  (p=0.002 n=6+6)
CanonicalizePathBits     80.0B Β± 0%     80.0B Β± 0%    ~     (all equal)
CanonicalizePath         80.0B Β± 0%     80.0B Β± 0%    ~     (all equal)

name                  old allocs/op  new allocs/op  delta
HashCommand               0.00           0.00         ~     (all equal)
CLParser                 1.64k Β± 0%     1.64k Β± 0%    ~     (all equal)
LoadManifest             2.61M Β± 0%     2.57M Β± 0%  -1.71%  (p=0.002 n=6+6)
CanonicalizePathBits      1.00 Β± 0%      1.00 Β± 0%    ~     (all equal)
CanonicalizePath          1.00 Β± 0%      1.00 Β± 0%    ~     (all equal)

Requirements

go install github.com/maruel/pat/cmd/...@latest

⏫ Continuous benchmarking

Track how benchmarks change in codebase over time. This is accomplished by running benchmarks for git commits, storing results, and visualizing difference. Running benchmarks can be in GitHub Actions or locally, storage can be in same repository master or dedicated branch, or standalone servers. It should be straightforward to setup this manually. Example of GitHub Action spec and blog from @vearutop, and an example on how it produces a PR comment.

⏫ Continuous benchmarking with gobenchdata

This tool uses go test -bench data in GitHub. It runs benchmarks, and uploads it as GitHub Pages for visualization. It is available as GitHub Action gobenchdata. This is useful to see benchmark trends. β€” @bobheadxi

Requirements

go install go.bobheadxi.dev/gobenchdata@latest

⏫ Continuous benchmarking with benchdiff

Automates comparing benchmarks with benchstat of two git references. It is available as GitHub Action benchdiff which runs benchstat of HEAD vs base branch. This is useful to see how benchmarks change with PRs in CI. β€” @WillAbides

Requirements

go install github.com/willabides/benchdiff/cmd/benchdiff

⏫ Continuous benchmarking with cob

Automate comparing benchmarks with benchstat between HEAD and HEAD^1. It can be used to block CI pipelines if benchmarks deteriorate. It reports output as text in CLI. This cane be useful in CI or in local development. β€” @knqyf263

Requirements

go install github.com/knqyf263/cob@latest

⏫ Generate live traces with net/http/trace

This will add endpoints to your your server. If you don't have server running already in your process, you can start one. Then you can point pprof tool to this data. For production, hide this endpoint in separate port and path. More details in documentation trace, net/http/pprof.

package main

import (
  "log"
  "net/http"
  "net/http/pprof"
)

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/custom_debug_path/profile", pprof.Profile)
  log.Fatal(http.ListenAndServe(":7777", mux))
}

Example

go tool pprof http://localhost:6060/debug/pprof/heap
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
curl -o trace.out http://localhost:6060/debug/pprof/trace?seconds=5

⏫ Generate traces with go test

Produce a trace of execution of tests in package.

go test -trace trace.out .

⏫ View traces with go tool trace

You can view traces interactively in browser with standard Go tooling. This web tool also shows network blocking profile, synchronization blocking profile, syscall blocking profile, scheduler latency profile.

go tool trace trace.out

⏫ πŸ”₯ View traces with gotraceui

Gotraceui is a tool for visualizing and analyzing Go execution traces. It is meant to be a faster, more accessible, and more powerful alternative to go tool trace. Unlike go tool trace, Gotraceui doesn't use deprecated browser APIs (or a browser at all), and its UI is tuned specifically to the unique characteristics of Go traces. This tool also recommend by official Go team from Google in their blog. β€” @dominikh

go tool trace trace.out

Requirements

refer to guideline for requirements for GUI
go install honnef.co/go/gotraceui/cmd/gotraceui@latest

⏫ View in-process traces with trc

This experimental approach illustrates collection of traces, intsrumentation, and visualization. It does not handle distributed traces. Likely useful for special cases or educational or research purposes. β€” @peterbourgon

Requirements

instrument your code with `trc` package
start UI server at port within same process

⏫ View wallclock traces with fgtrace

This tool can be more illustrative of Go traces than standard Go traces. β€” @felixge

package main

import (
  "net/http"

  "github.com/felixge/fgtrace"
)

func main() {
  http.DefaultServeMux.Handle("/debug/fgtrace", fgtrace.Config{})
  http.ListenAndServe(":1234", nil)
}

⏫ Get on/off CPU profiles with fgprof

This tool can be more illustrative of Go profiles than standard Go profiling. β€” @felixge

package main

import (
  "log"
  "net/http"
  _ "net/http/pprof"

  "github.com/felixge/fgprof"
)

func main() {
  http.DefaultServeMux.Handle("/debug/fgprof", fgprof.Handler())
  go func() {
    log.Println(http.ListenAndServe(":6060", nil))
  }()

  // <code to profile>
}

Document

⏫ Make alternative documentation with golds

It has additional information like implementations of interface; promoted methods. The tool has nice minimalistic aesthetics. β€” Tapir Liu

golds ./...

Requirements

go install go101.org/golds@latest

⏫ Read Go binary documentation in man format with goman

This tool fetches the repo's readme as a man page replacement. β€” @christophberger

goman <mypackage>

Requirements

go install github.com/appliedgocode/goman@latest

⏫ Generate badge with gobadge

This tool will generate instructions for shields.io to generate badge. It can read coverprofile. There is also GitHub Action that utilizes it and stores badge in the same repo, coverage-badge-go. β€” @AlexBeauchemin

gobadge -filename=coverage.out
gobadge -label="Go Coverage" -value=55.6% -color=blue -target=OTHER_README.md
gobadge -yellow=60 -green=80
gobadge -color=ff69b4
gobadge -link=https://github.com/project/repo/actions/workflows/test.yml

Requirements

go install github.com/AlexBeauchemin/gobadge@latest

⏫ πŸ”₯ Generate README.md based on GoDoc comments with goreadme

It can be used as a command line tool, as Github action, or as a pre-commit hook. β€” @posener

goreadme
goreadme -variables -functions -methods -badge-godoc
goreadme -badge-godoc -constants -functions -methods -recursive -types -variables > README.md

Requirements

go install github.com/posener/goreadme/cmd/goreadme@latest

Education

⏫ Run Turtle Graphics online with goplay.space

This absolutely adorable visualization is an excellent online resource to learn programming. β€” @iafan

package main

import (
  "fmt"
)

func main() {
  fmt.Println(`
    draw mode
    
    say Let's start...
    right 18
    color red

    forward 7
    say One...
    right 144

    forward 7
    say Two...
    right 144

    forward 7
    say Three...
    right 144

    forward 7
    say Four...
    right 144

    forward 7
    say We've got a star!
    right 144
  `)
}

Style Guide

Security

⏫ Run official vulnerability check with govulncheck

It uses static analysis of source code or a binary's symbol table to narrow down reports to only those that could affect the application. By default, govulncheck makes requests to the Go vulnerability database at https://vuln.go.dev. Requests to the vulnerability database contain only module paths, not code or other properties of your program. See https://vuln.go.dev/privacy.html for more. β€” Go Core team

govulncheck ./...

Example

vulnerability data from https://vuln.go.dev (last modified 2023-06-01 21:27:40 +0000 UTC).

Scanning your code and 1952 packages across 202 dependent modules for known vulnerabilities...
Your code is affected by 2 vulnerabilities from 1 module.

Vulnerability #1: GO-2023-1571
  A maliciously crafted HTTP/2 stream could cause excessive CPU
  consumption in the HPACK decoder, sufficient to cause a denial
  of service from a small number of small requests.

  More info: https://pkg.go.dev/vuln/GO-2023-1571

  Module: golang.org/x/net
    Found in: golang.org/x/net@v0.1.1-0.20221027164007-c63010009c80
    Fixed in: golang.org/x/net@v0.7.0

    Call stacks in your code:
      cmd/kube-controller-manager/app/controllermanager.go:216:40: k8s.io/kubernetes/cmd/kube-controller-manager/app.Run calls k8s.io/apiserver/pkg/server.SecureServingInfo.Serve, which eventually calls golang.org/x/net/http2.ConfigureServer
        requirements:
          - go install golang.org/x/vuln/cmd/govulncheck@latest

⏫ πŸ”₯ Detect escalated privileges in dependencies with capslock

Capslock is a capability analysis CLI for Go packages that informs users of which privileged operations a given package can access. This works by classifying the capabilities of Go packages by following transitive calls to privileged standard library operations. The recent increase in supply chain attacks targeting open source software has highlighted that third party dependencies should not be inherently trusted. Capabilities indicate what permissions a package has access to, and can be used in conjunction with other security signals to indicate which code requires additional scrutiny before it can be considered trusted. β€” Google

capslock -packages=./...

Example

...
CAPABILITY_ARBITRARY_EXECUTION: 317 references
CAPABILITY_EXEC: 317 references
CAPABILITY_FILES: 318 references
CAPABILITY_MODIFY_SYSTEM_STATE: 20 references
CAPABILITY_NETWORK: 317 references
CAPABILITY_OPERATING_SYSTEM: 124 references
CAPABILITY_READ_SYSTEM_STATE: 317 references
CAPABILITY_REFLECT: 348 references
CAPABILITY_RUNTIME: 317 references
CAPABILITY_SYSTEM_CALLS: 12 references
CAPABILITY_UNANALYZED: 332 references
CAPABILITY_UNSAFE_POINTER: 335 references

Requirements

go install github.com/google/capslock/cmd/capslock@latest

⏫ πŸ”₯ Run static analysis with gosec

This tool inspects source code for security problems by scanning the Go AST and SSA code representation. There are numerous rules it checks. β€” @ccojocar

gosec ./...

Requirements

go install github.com/securego/gosec/v2/cmd/gosec@latest

⏫ Perform Taint Analysis with taint

Taint analysis is a technique for identifying the flow of sensitive data through a program. It can be used to identify potential security vulnerabilities, such as SQL injection or cross-site scripting (XSS) attacks, by understanding how this data is used and transformed as it flows through the code. This package provides tools to performs such analysis. Included tool is performing SQL injection taint analysis. β€” @picatz

sqli main.go
package main

import (
        "database/sql"
        "net/http"
)

func business(db *sql.DB, q string) {
        db.Query(q) // potential sql injection
}

func run() {
        db, _ := sql.Open("sqlite3", ":memory:")

        mux := http.NewServeMux()

        mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
                business(db, r.URL.Query().Get("sql-query"))
        })

        http.ListenAndServe(":8080", mux)
}

func main() {
        run()
}

Example

./sql/injection/testdata/src/example/main.go:9:10: potential sql injection

Requirements

go install github.com/picatz/taint/cmd/sqli@latest

⏫ πŸ”₯ Use Microsoft Go compiler with microsoft/go

This is modified version of Go that can be used to build FIPS 140-2 compliant applications. β€” @microsoft

Static Analysis

⏫ Run default static analysis with go vet

Official tool for static analysis of Go programs, with 27+ static analyzers. β€” official Go team

go vet ./...

⏫ Run custom static analysis tool with go vet

Standard go vet can be used to run custom analyzers binaries. Third party analyzers are supported. Lots of official analyzers not included by default into go vet. Analyzer has to satisfy interface and command described here https://pkg.go.dev/golang.org/x/tools/go/analysis. Refer for https://pkg.go.dev/golang.org/x/tools/go/analysis/passes for full list of official Go analyzers. β€” official Go team

go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow
go vet -vettool=$(which shadow)

⏫ Run official static analyzers not included in go vet

There are many analyzers not included in go vet. These tools are experimental and may not work as expected (e.g. usesgenerics does not work). Refer to for full list https://pkg.go.dev/golang.org/x/tools/go/analysis. β€” official Go team

package main

import (
  "golang.org/x/tools/go/analysis/multichecker"

  "golang.org/x/tools/go/analysis/passes/atomicalign"
  "golang.org/x/tools/go/analysis/passes/deepequalerrors"
  "golang.org/x/tools/go/analysis/passes/fieldalignment"
  "golang.org/x/tools/go/analysis/passes/nilness"
  "golang.org/x/tools/go/analysis/passes/reflectvaluecompare"
  "golang.org/x/tools/go/analysis/passes/shadow"
  "golang.org/x/tools/go/analysis/passes/sortslice"
  "golang.org/x/tools/go/analysis/passes/unusedwrite"
  "golang.org/x/tools/go/analysis/passes/usesgenerics"
)

func main() {
  multichecker.Main(
    atomicalign.Analyzer,         // checks for non-64-bit-aligned arguments to sync/atomic functions
    deepequalerrors.Analyzer,     // checks for the use of reflect.DeepEqual with error values
    fieldalignment.Analyzer,      // detects structs that would use less memory if their fields were sorted
    nilness.Analyzer,             // inspects the control-flow graph of an SSA function and reports errors such as nil pointer dereferences and degenerate nil pointer comparisons
    reflectvaluecompare.Analyzer, // checks for accidentally using == or reflect.DeepEqual to compare reflect.Value values
    shadow.Analyzer,              // checks for shadowed variables
    sortslice.Analyzer,           // checks for calls to sort.Slice that do not use a slice type as first argument
    unusedwrite.Analyzer,         // checks for unused writes to the elements of a struct or array object
    usesgenerics.Analyzer,        // checks for usage of generic features added in Go 1.18
  )
}

⏫ Detect most common issues with staticcheck

Start custom linters with this well-known linter. It contains 150+ high quality low false positive rate linters. It is widely adopted by Open Source and tech companies. staticcheck.io. β€” @dominikh

staticcheck ./...

Requirements

go install honnef.co/go/tools/cmd/staticcheck@latest

⏫ Detect potential Nil panics with nilaway

This tool employs sophisticated static analysis techniques to catch Nil dereferences. More details in blog. β€” Uber

nilaway ./...

Requirements

go install go.uber.org/nilaway/cmd/nilaway@latest

⏫ Detect most common issues with go-critic

This linting aggregator and runner is similar to staticcheck. It has 100+ linting rules. It is based on Go Code Review Comments style guide that is used in core Go project itself. It has styling, security, performance rules. It has minimal dependencies and implements rules itself. It exports all analysers into golang.org/x/tools/go/analysis toolchain. β€” @quasilyte

gocritic check ./...

Requirements

go install -v github.com/go-critic/go-critic/cmd/gocritic@latest

⏫ Reference and run common linters with golangci-lint

This tool has comprehensive list of linters. Owners of this aggregator keep track of active linters, their versions, and optimal configs. It contains many optimizations to make linters run fast by parallelism, distributing binaries and Docker images, utilising golang.org/x/tools/go/analysis toolchain.

golangci-lint run

Example

server/web/filter/opentracing/filter.go:21:2: import-alias-naming: import name (logKit) must match the regular expression: ^[a-z][a-z0-9]{0,}$ (revive)
core/utils/pagination/paginator.go:65:3: G104: Errors unhandled. (gosec)
core/utils/pagination/utils.go:33:2: bare-return: avoid using bare returns, please add return expressions (revive)
server/web/grace/server.go:358:3: exitAfterDefer: log.Fatalf will exit, and `defer regLock.Unlock()` will not run (gocritic)
server/web/filter/prometheus/filter.go:90:40: "2006-01-02 15:04:05" can be replaced by time.DateTime (usestdlibvars)
core/utils/slice.go:47:20: builtinShadow: shadowing of predeclared identifier: min (gocritic)
core/utils/utils.go:54:3: ifElseChain: rewrite if-else to switch statement (gocritic)
client/orm/hints/db_hints_test.go:95:2: expected-actual: need to reverse actual and expected values (testifylint)
core/config/env/env.go:95:15: fmt.Errorf can be replaced with errors.New (perfsprint)

Requirements

curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin latest

⏫ Detect non-exhaustive switch and map with exhaustive

This go vet compatible analyzer checks for exhaustive switch statements and map literals. It works for enums with underlying integer, float, or string types (struct based enums are not supported). β€” @nishanths

exhaustive ./...
package token

type Token int

const (
  Add Token = iota
  Subtract
  Multiply
  Quotient
  Remainder
)

package calc

import "token"

func f(t token.Token) {
  switch t {
  case token.Add:
  case token.Subtract:
  case token.Multiply:
  default:
  }
}

func g(t token.Token) string {
  return map[token.Token]string{
    token.Add:      "add",
    token.Subtract: "subtract",
    token.Multiply: "multiply",
  }[t]
}

Example

calc.go:6:2: missing cases in switch of type token.Token: Quotient, Remainder
calc.go:15:9: missing map keys of type token.Token: Quotient, Remainder

Requirements

go install github.com/nishanths/exhaustive/cmd/exhaustive@latest

⏫ Detect structs with uninitialized fields with go-exhaustruct

This tool finds instantiations of structs with zero values. It supports struct tags to mark fields as optional. This may help to prevent unexpected zero values. β€” @xobotyi

exhaustruct ./...
type Shape struct {
  Length int
  Width  int
  volume    int
  Perimeter int `exhaustruct:"optional"`
}

// valid
var a Shape = Shape{
  Length: 5,
  Width:  3,
  volume: 5,
}

// invalid, `volume` is missing
var b Shape = Shape{
  Length: 5,
  Width:  3,
}

Requirements

go get -u github.com/GaijinEntertainment/go-exhaustruct/v3/cmd/exhaustruct

⏫ πŸ”₯ Detect unreachable functions with deadcode

This static analysis tool detects when functions can not be reached in any execution. There is also -test mode that shows if function is reachable by any of tests. β€” Alan Donovan, official Go team

deadcode .

Example

greet.go:23: unreachable func: goodbye
greet.go:20: unreachable func: Goodbyer.Greet

Requirements

go install golang.org/x/tools/cmd/deadcode@latest

⏫ Detect unsafe code with go-safer

Find incorrect uses of reflect.SliceHeader, reflect.StringHeader, and unsafe casts between structs with architecture-sized fields. Research paper "Uncovering the Hidden Dangers Finding Unsafe Go Code in the Wild" presented at 19th IEEE International Conference on Trust, Security and Privacy in Computing and Communications (TrustCom 2020). β€” @jlauinger

go-safer ./...

Example

# github.com/jlauinger/go-safer/passes/sliceheader/testdata/src/bad/composite_literal
composite_literal/composite_literal.go:10:9: reflect header composite literal found
composite_literal/composite_literal.go:10:9: reflect header composite literal found
# github.com/jlauinger/go-safer/passes/sliceheader/testdata/src/bad/header_in_struct
header_in_struct/header_in_struct.go:16:2: assigning to reflect header object

Requirements

go install github.com/jlauinger/go-safer@latest

⏫ πŸ”₯ Detect panic without explaining comment with panic-linter

Panic should only be used very sparingly, for catching bugs basically, and thus deserve a comment to confirm that that's indeed the case. β€” @ldemailly

# bad
panic("catch this")

# good
panic("catch this") // does not happen unless byte is not 8 bits anymore

⏫ Detect unnecessary type conversions with unconvert

Identify expressions like T(x) where x is already has type T. This tool can identify conversions that force intermediate rounding. It also can overwrite files with fix. This tool is not using golang.org/x/tools/go/analysis toolchain. β€” @mdempsky

unconvert ./...
$ unconvert -v bytes fmt
GOROOT/src/bytes/reader.go:117:14: unnecessary conversion
                abs = int64(r.i) + offset
                          ^
GOROOT/src/fmt/print.go:411:21: unnecessary conversion
        p.fmt.integer(int64(v), 16, unsigned, udigits)
                          ^

Requirements

go install github.com/mdempsky/unconvert@latest

⏫ Detect global variables with gochecknoglobals

Global variables are an input to functions that is not visible in the functions signature, complicate testing, reduces readability and increase the complexity of code. However, sometimes global variables make sense. This tool skips such common scenarios. This tool can be used in CI, albeit it is very strict. This tool is useful for investigations. β€” @leighmcculloch

gochecknoglobals ./...

Example

/Users/nikolaydubina/Workspace/hugo/common/paths/path.go:64:5: fpb is a global variable
/Users/nikolaydubina/Workspace/hugo/common/paths/url.go:50:5: pb is a global variable
/Users/nikolaydubina/Workspace/hugo/common/text/position.go:52:5: positionStringFormatfunc is a global variable
/Users/nikolaydubina/Workspace/hugo/common/text/transform.go:26:5: accentTransformerPool is a global variable
/Users/nikolaydubina/Workspace/hugo/common/herrors/error_locator.go:40:5: SimpleLineMatcher is a global variable

Requirements

go install 4d63.com/gochecknoglobals@latest

⏫ Detect slices that could be preallocated with prealloc

Preallocating slices can sometimes significantly improve performance. This tool detects common scenarios where preallocating can be beneficial. This tool is not using golang.org/x/tools/go/analysis toolchain. β€” @alexkohler

prealloc ./...

Example

tools/gopls/internal/lsp/source/completion/completion.go:1484 Consider preallocating paths
tools/gopls/internal/lsp/source/completion/package.go:54 Consider preallocating items
tools/gopls/internal/lsp/template/symbols.go:205 Consider preallocating ans
tools/gopls/internal/lsp/template/completion.go:199 Consider preallocating working
tools/gopls/internal/lsp/tests/util.go:32 Consider preallocating notePositions
tools/gopls/internal/lsp/tests/util.go:240 Consider preallocating paramParts
tools/gopls/internal/lsp/tests/util.go:282 Consider preallocating result
tools/gopls/internal/lsp/tests/util.go:309 Consider preallocating got

Requirements

go install github.com/alexkohler/prealloc@latest

⏫ Detect unnecessary import aliases with unimport

It is common guideline to avoid renaming imports unless there are collisions. This tool detects where original package name would not collide. This tool is useful for investigations. This tool is not using golang.org/x/tools/go/analysis toolchain. β€” @alexkohler

unimport ./...

Example

pkg/apis/apiserverinternal/v1alpha1/zz_generated.conversion.go:29 unnecessary import alias runtime
pkg/apis/apiserverinternal/v1alpha1/zz_generated.conversion.go:30 unnecessary import alias apiserverinternal
pkg/apis/apps/v1/zz_generated.conversion.go:25 unnecessary import alias unsafe
pkg/apis/apps/v1/zz_generated.conversion.go:30 unnecessary import alias conversion
pkg/apis/apps/v1/zz_generated.conversion.go:31 unnecessary import alias runtime
pkg/apis/apps/v1/zz_generated.conversion.go:32 unnecessary import alias intstr
pkg/apis/apps/v1/zz_generated.conversion.go:33 unnecessary import alias apps
pkg/apis/apps/v1/zz_generated.conversion.go:34 unnecessary import alias core
pkg/apis/apps/v1beta1/zz_generated.conversion.go:25 unnecessary import alias unsafe
pkg/apis/apps/v1beta1/zz_generated.conversion.go:27 unnecessary import alias v1beta1
pkg/apis/apps/v1beta1/zz_generated.conversion.go:30 unnecessary import alias conversion
pkg/apis/apps/v1beta1/zz_generated.conversion.go:31 unnecessary import alias runtime

Requirements

go install github.com/alexkohler/unimport@latest

⏫ Detect unexpected import aliases with importas

Ensure that import aliases take one of the allowed values. β€” @julz

importas -alias knative.dev/serving/pkg/apis/autoscaling/v1alpha1:autoscalingv1alpha1 -alias knative.dev/serving/pkg/apis/serving/v1:servingv1 ./...
package main

import (
  v1alpha1 "knative.dev/serving/pkg/apis/autoscaling/v1alpha1" // want `import "knative.dev/serving/pkg/apis/autoscaling/v1alpha1" imported as "v1alpha1" but must be "autoscalingv1alpha1" according to config`
  v1 "knative.dev/serving/pkg/apis/serving/v1"                 // want `import "knative.dev/serving/pkg/apis/serving/v1" imported as "v1" but must be "servingv1" according to config`
)

func main() {
...

Requirements

go install github.com/julz/importas/cmd/importas@latest

⏫ Detect inconsistent import aliases with consistentimports

It greatly helps to navigate large codebases when imports have the same aliases. β€” @nikolaydubina

consistentimports ./...

Example

-: "k8s.io/utils/net" netutils:4 netutil:1
-: "k8s.io/client-go/listers/core/v1" corelisters:1 listersv1:1 v1listers:1
-: "k8s.io/client-go/informers/core/v1" coreinformers:1 informers:1
-: "k8s.io/api/rbac/v1" rbacv1:4 v1:2
-: "k8s.io/apimachinery/pkg/runtime" runtime:3 kruntime:1
-: "k8s.io/api/imagepolicy/v1alpha1" imagepolicyv1alpha1:1 v1alpha1:1
-: "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis

Requirements

go install github.com/nikolaydubina/consistentimports@latest

⏫ Detect naked returns with nakedret

It is common guideline to avoid naked returns. Naked return is when function has named return, and return statement does not specify value. This tool is useful for investigations. β€” @alexkohler

nakedret ./...

Example

/kubernetes/pkg/controller/podautoscaler/replica_calculator.go:421:2: naked return in func `groupPods` with 44 lines of code
/kubernetes/pkg/kubelet/container/helpers.go:374:2: naked return in func `MakePortMappings` with 36 lines of code
/kubernetes/pkg/kubelet/config/config.go:350:2: naked return in func `filterInvalidPods` with 17 lines of code
/kubernetes/pkg/kubelet/config/config.go:449:3: naked return in func `checkAndUpdatePod` with 38 lines of code
/kubernetes/pkg/kubelet/config/config.go:471:2: naked return in func `checkAndUpdatePod` with 38 lines of code
/kubernetes/cmd/kube-controller-manager/app/controllermanager.go:717:2: naked return in func `createClientBuilders` with 19 lines of code
/kubernetes/pkg/proxy/topology.go:77:3: naked return in func `CategorizeEndpoints` with 98 lines of code
/kubernetes/pkg/proxy/topology.go:111:3: naked return in func `CategorizeEndpoints` with 98 lines of code
/kubernetes/pkg/proxy/topology.go:119:3: naked return in func `CategorizeEndpoints` with 98 lines of code
/kubernetes/pkg/proxy/topology.go:137:2: naked return in func `CategorizeEndpoints` with 98 lines of code

Requirements

go install github.com/alexkohler/nakedret/cmd/nakedret@latest

⏫ Detect mixing pointer and value method receivers with smrcptr

Mixing pointer and value method receivers for the same type is discouraged, as per commong guideline Go wiki and Google Go style guide. β€” @nikolaydubina

smrcptr ./...
type Pancake struct{}

func NewPancake() Pancake { return Pancake{} }

func (s *Pancake) Fry() {}

func (s Pancake) Bake() {}

Example

smrcptr/internal/bakery/pancake.go:7:1: Pancake.Fry uses pointer
smrcptr/internal/bakery/pancake.go:9:1: Pancake.Bake uses value

Requirements

go install github.com/nikolaydubina/smrcptr@latest

⏫ Detect vertical function ordering with vertfn

Vertical function ordering is declaring functions before they are used. Based on 'Clean Code' by Robert.C.Martin. β€” @nikolaydubina

vertfn --verbose ./...

Requirements

go install github.com/nikolaydubina/vertfn@latest

⏫ Detect vertical symbol ordering with refdir

This tool goes beyond just vertical ordering of function declarations, it also tracks many other types of symbols. β€” @devnev

refdir --verbose ./...

Requirements

go install github.com/devnev/refdir@latest

⏫ Detect tests with wrong t.Parallel() usage with paralleltest

This linter checks for incorrect usage of t.Parallel() calls. It will detect if t.Parallel() is missing. β€” @kunwardeep

paralleltest ./...

Example

/kubernetes/pkg/scheduler/framework/plugins/nodeunschedulable/node_unschedulable_test.go:28:1: Function TestNodeUnschedulable missing the call to method parallel
/kubernetes/pkg/scheduler/framework/plugins/nodevolumelimits/csi_test.go:68:1: Function TestCSILimits missing the call to method parallel
/kubernetes/pkg/scheduler/framework/plugins/nodevolumelimits/csi_test.go:480:2: Range statement for test TestCSILimits missing the call to method parallel in test Run
/kubernetes/pkg/scheduler/framework/plugins/nodevolumelimits/non_csi_test.go:81:1: Function TestEphemeralLimits missing the call to method parallel

Requirements

go install github.com/kunwardeep/paralleltest@latest

⏫ Detect tests with wrong t.Parallel() usage with tparallel

This linter checks for incorrect usage of t.Parallel() calls. β€” @moricho

go vet -vettool=`which tparallel` ./...

Example

testdata/src/sample/table_test.go:7:6: Test_Table1 should use t.Cleanup
testdata/src/sample/table_test.go:7:6: Test_Table1 should call t.Parallel on the top level as well as its subtests
testdata/src/sample/table_test.go:30:6: Test_Table2's subtests should call t.Parallel

Requirements

go install github.com/moricho/tparallel/cmd/tparallel@latest

⏫ Detect magic numbers with mnd

This tool has heuristics to detect magic numbers. β€” @tommy-muehle

mnd ./...

Example

/go-mnd/examples/bad/main.go:18:23: Magic number: 200, in <condition> detected
/go-mnd/examples/bad/main.go:11:12: Magic number: 2, in <assign> detected

Requirements

go install github.com/tommy-muehle/go-mnd/v2/cmd/mnd@latest

⏫ Detect magic strings with goconst

This tool detects repeated strings. β€” @jgautheron

goconst -min-occurrences 5 -output json ./... | jq

Example

"not reached": [                                                                                                                                          
  {                                                                                                                                                       
    "Filename": "tpl/internal/go_templates/texttemplate/hugo_template.go",                                                                                
    "Offset": 7916,
    "Line": 267,
    "Column": 8
  },
  {
    "Filename": "tpl/internal/go_templates/texttemplate/exec.go",
    "Offset": 15056,
    "Line": 525,
    "Column": 8
  },
  {
    "Filename": "tpl/internal/go_templates/texttemplate/exec.go",
    "Offset": 21354,
    "Line": 699,
    "Column": 8
  },
  {
    "Filename": "tpl/internal/go_templates/texttemplate/exec.go",
    "Offset": 28145,
    "Line": 903,
    "Column": 8
  },
  ...

Requirements

go install github.com/jgautheron/goconst/cmd/goconst@latest

⏫ Detect bound checks with pat/boundcheck

This tool detects bound checks in source code by analysing compiled code. This is useful for audit. β€” @maruel

boundcheck -pkg ./cmd/nin | less -R

Requirements

go install github.com/maruel/pat/cmd/...@latest

⏫ Calculate Cognitive Complexity with gocognit

Cognitive Complexity as defined in this tool can be more illustrative than Cyclometric Complexity. Research paper "Cognitive Complexity - a new way of measuring understandability", 2021. β€” @uudashr

gocognit .
// Complexity Cyclomatic=4 Cognitive=7
// Cognitive complexity give higher score compare to cyclomatic complexity.
func SumOfPrimes(max int) int {         // +1
    var total int
    for i := 1; i < max; i++ {          // +1 (cognitive +1, nesting)
        for j := 2; j < i; j++ {        // +1 (cognitive +2, nesting)
            if i%j == 0 {               // +1
                continue OUT
            }
        }
        total += i
    }
    return total
}

// Complexity Cyclomatic=4 Cognitive=1
// Cognitive complexity give lower score compare to cyclomatic complexity.
func GetWords(number int) string {      // +1
    switch number {
        case 1:                         // +1 (cognitive 0)
            return "one"
        case 2:                         // +1 (cognitive 0)
            return "a couple"
        case 3:                         // +1 (cognitive 0)
            return "a few"
        default:
            return "lots"
    }
}

Example

21 main (BasicSymtabConverter).SymtabFileToTreemap basic_converter.go:23:1
12 symtab parseGoSymtabLine symtab/go_symtab_parser.go:37:1
11 main main main.go:30:1
8 symtab EqSymbolName symtab/symbol_name_parser.go:12:1
7 symtab ParseSymbolName symtab/symbol_name_parser.go:32:1
7 symtab Test_parseGoSymtabLine symtab/go_symtab_parser_private_test.go:5:1
4 symtab Test_ParseSymbolName symtab/symbol_name_parser_private_test.go:5:1
3 main updateNodeNamesWithByteSize main.go:99:1
3 main unique basic_converter.go:119:1
3 symtab (GoSymtabParser).ParseSymtab symtab/go_symtab_parser.go:14:1
2 fmtbytecount ByteCountIEC fmtbytecount/format_bytecount.go:3:1

Requirements

go install github.com/uudashr/gocognit/cmd/gocognit@latest

⏫ Calculate Cyclomatic Complexity with gocyclo

Cyclomatic complexity is a code quality metric which can be used to identify code that needs refactoring. It measures the number of linearly independent paths through a function's source code. For example, excessive usage of nested if and for leads to increased cyclomatic complexity. This tool can report top-N and over, which makes it suitable for CI as a linter and manual investigation. β€” @fzipp

gocyclo .

Example

$ gocyclo -over=5 .
34 examplemodule (*With32FieldsFeatureTransformer).Fit cmd/generate/tests/with32fieldsfp.go:48:1
24 main parseCode cmd/generate/parser.go:83:1
13 examplemodule (*AllTransformersFeatureTransformer).Fit cmd/generate/tests/alltransformersfp.go:27:1
12 examplemodule (*EmployeeFeatureTransformer).Fit cmd/generate/tests/employeefp.go:26:1
11 transformers (*CountVectorizer).TransformInplace transformers/textprocesors.go:84:1
11 structtransformer (*StructTransformer).Transform structtransformer/structtransformer.go:38:1
11 examplemodule (*LargeMemoryTransformerFeatureTransformer).Fit cmd/generate/tests/largememorytransformerfp.go:25:1
10 examplemodule (*WeirdTagsFeatureTransformer).Fit cmd/generate/tests/weirdtagsfp.go:24:1
8 transformers (*SampleNormalizerL2).TransformInplace transformers/samplenormalizers.go:58:1

Requirements

go install github.com/fzipp/gocyclo/cmd/gocyclo@latest

⏫ Calculate Cyclomatic Complexity with cyclop

This linter calculates cyclomatic complexity of functions or packages. It can select minimum complexity and act as blocking linter in CI pipelines. The key offering from this linter is that it can calculate avg cyclomatic complexity on package. β€” @bkielbasa

cyclop ./...
# to find packages with avg cyclomatic complexity above maximum
cyclop -packageAverage 5 -maxComplexity 10000 ./...

Example

/kubernetes/test/integration/scheduler/scoring/priorities_test.go:17:1: the average complexity for the package scoring is 6.100000, max is 5.000000
/kubernetes/test/integration/serviceaccount/service_account_test.go:17:1: the average complexity for the package serviceaccount is 10.666667, max is 5.000000
/kubernetes/test/integration/volume/persistent_volumes_test.go:17:1: the average complexity for the package volume is 6.157895, max is 5.000000
/kubernetes/test/list/main_test.go:17:1: the average complexity for the package main is 5.461538, max is 5.000000
/kubernetes/test/typecheck/main_test.go:17:1: the average complexity for the package main is 5.916667, max is 5.000000
/kubernetes/third_party/forked/golang/net/dnsclient_test.go:10:1: the average complexity for the package net is 5.333333, max is 5.000000

Requirements

go install github.com/bkielbasa/cyclop@latest

⏫ Calculate age of comments with go-commentage

This go vet compatible tool analyses AST and git and collects details on how far comments drift from code they describe. β€” @nikolaydubina

go-commentage -min-days-behind 360 ./...

Example

kubernetes/pkg/util/ipset/ipset.go:283:1: "CreateSet": doc_last_updated_behind_days(1336.83)
kubernetes/pkg/util/ipset/ipset.go:296:1: "createSet": doc_last_updated_behind_days(1603.17)
kubernetes/pkg/util/ipset/ipset.go:320:1: "AddEntry": doc_last_updated_behind_days(1578.10)
kubernetes/pkg/util/ipset/ipset.go:332:1: "DelEntry": doc_last_updated_behind_days(1578.10)
kubernetes/pkg/util/ipset/ipset.go:340:1: "TestEntry": doc_last_updated_behind_days(450.07)

Requirements

# get latest version of git
go install github.com/nikolaydubina/go-commentage@latest

⏫ Ensure if statements using short assignment with ifshort

Linter for checking that your code uses short syntax for if statements whenever possible. However, as of 2023-05-26, it is not maitaned and is not working. β€” @esimonov

ifshort ./...
// bad
func someFunc(k string, m map[string]interface{}) {
  _, ok := m[k]
  if !ok {
    return
  }

  err := otherFunc1()
  if err != nil {
    otherFunc2(err)
  }
}

// good
func someFunc(k string, m map[string]interface{}) {
  if _, ok := m[k]; !ok {
    return
  }

  if err := otherFunc1(); err != nil {
    otherFunc2(err)
  }
}

Requirements

go install github.com/esimonov/ifshort@latest

⏫ πŸ”₯ Detect sub-optimal struct layout with betteralign

This tool detect structs that would use less memory if their fields were sorted and optionally sort such fields. β€” @dkorunic

betteralign -apply ./...

Requirements

go install github.com/dkorunic/betteralign/cmd/betteralign@latest

⏫ πŸ”₯ Detect sub-optimal struct layout with structlayout-optimize

This tool reorders struct fields to minimize the amount of padding. β€” @dominikh

Requirements

go install https://github.com/dominikh/go-tools/blob/master/cmd/structlayout-optimize@latest

⏫ Visualize struct layout with structlayout

Display the byte offset and size of each field, respecting alignment/padding. β€” @dominikh

structlayout -json bytes Buffer | structlayout-svg -t "bytes.Buffer" > /tmp/struct.svg

Requirements

go install github.com/ajstarks/svgo/structlayout-svg@latest
go install honnef.co/go/tools/cmd/structlayout@latest

⏫ Rely on compiler for stricter Enums

For compile time blocking of: accidental arithmetics; implicit cast of untyped constants; all operators except == and !=; β€” simply wrap into a struct in separate package and do not export field. example.

package color

type Color struct{ c uint }

var (
  Undefined = Color{}
  Red       = Color{1}
  Green     = Color{2}
  Blue      = Color{3}
)

⏫ Analyze function callsites with go-callsite-stats

Scrape callsite information about functions to learn better how functions are beinn used. This can help in refactoring, naming, OOP. This tool calculates frequency of names on assignments in returns and frequency of names in arguments. This can be used to detect ignored returns as well. β€” @nikolaydubina

go-callsite-stats ./...
x16:       (no assignments)                  = execHostnameTest(serviceAddress:7)
                                                              (nodePortAddress:3)
                                                              (nodePortAddress0:3)
                                                              (nodePortAddress1:2)
                                                              (clusterIPAddress:1)
x16:       pod:10, err:12                    = CreatePod(client:11, namespace:10, nil:9, pvclaims:6, false:7, execCommand:2)
          clientPod:1                                  (c:2, ns:2, podCount:2, true:3)
          _:1                                          (pod:1, pod:1, pvclaims:2, false:2)
          err:1                                        (ctx:1, nil:1, createdClaims:1, pvcClaims:1)
                                                        (namespace:1, nameSpace:1, podTemplate:1)
                                                        (, basePod:1)
x16:       (no assignments)                  = GET()
x16:       deployment:11, err:14             = UpdateDeploymentWithRetries(c:14, ns:14, deploymentName:3, applyUpdate:1, poll:1,pollShortTimeout:1)                                                         
          _:2                                                            (client:1, namespace:1, pollTimeout:1)
          deploymentWithUpdatedReplicas:1                                (applyUpdate:1, pollInterval:1, name:1)
x16:       err:16                            = waitForDefinition(schemaFoo:12
                                                                (schemaWaldo:3)
                                                                (expect:1)

Requirements

go install github.com/nikolaydubina/go-callsite-stats@latest