Skip to content

Latest commit

Β 

History

History
4213 lines (2989 loc) Β· 152 KB

README.md

File metadata and controls

4213 lines (2989 loc) Β· 152 KB

🦩 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 bellow.

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

Requirements

VSCode
GitHub account

⏫ Pull requests recommendations with CopilotX

CopilotX has dedicated solutions for: writing PR description; writing tests; writing PR reviews and replies; applying requested PR changes. As of 2023-06-04, it is on waitlist. documentation.

⏫ 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

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

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 bellow.

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

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. Bellow 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).

Testing

⏫ πŸ”₯ 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 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

⏫ πŸ”₯ 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()
        ...

⏫ 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)
    }
  }()
}

⏫ 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

⏫ 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 modules with gmchart

Render in browser Go module graphs. Built with D3.js, Javascript, HTTP server in Go. β€” @PaulXu-cn

go mod graph | gmchart

Requirements

go install github.com/PaulXu-cn/go-mod-graph-chart/gmchart@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

⏫ 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]

⏫ 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

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, calsses)

⏫ 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

⏫ 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 bellow 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

Genereate 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 intializer 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/[email protected]

⏫ 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)
}

⏫ 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 {
    ...
}

⏫ 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/deepmap/oapi-codegen/v2/cmd/oapi-codegen@latest

Refactoring

⏫ 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 maintan 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 consitent 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

⏫ πŸ”₯ Replace unkeyed struct literals into keyed ones with keyify

You may want to do this as preparation for other refactoring steps. This tool has good emacs integration, otherwise may it be hard to use. β€” @dominikh

# before
myFunc(T{1, 2, 3})

# after
myFunc(T{A: 1, B: 2, C: 3})

Requirements

go install honnef.co/go/tools/cmd/[email protected]

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 procuded the error. This tool have conenienve automatic instrumentation CLI to update your code. β€” @bracesdev

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

Requirements

use packae "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

Building

⏫ 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-gudied optimization. You need to collect profiles and then supply in compulation to compiler. You can get improvement in performance by around 4%. Officual 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 complier 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
  ...
}

⏫ 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

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 assmebly 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 undertand Go AST. This tool has not been maintaned 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 archieved 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 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 playform 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

⏫ 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 pacakges. β€” @gopherdata

Requirements

# jupyter notebook
go install github.com/gopherdata/[email protected]
# 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 pacakges 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

Orignally 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

Orignally 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

Orignally 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. Orignally 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

Orignally 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()

Monitoring

⏫ 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

⏫ Visualise 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

⏫ Continious Profiling with Pyroscope

This tool allows to injest 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

Benchmarking

⏫ 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

⏫ 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 pacakge.

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

⏫ Get 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>
}

⏫ Collect and visualize 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

Documentation

⏫ 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/[email protected]
    Fixed in: golang.org/x/[email protected]

    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

⏫ 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

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

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 paralleism, distributing binaries and Docker images, utilising golang.org/x/tools/go/analysis toolchain.

⏫ Detect non-exhaustive switch and map with exhaustive

This go vet compatible analyzer checks for exhaustive switch statemnts and map literals. It works for enums with underyling 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 instatiations 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 unsafe code with go-safer

Find incorrect uses of reflect.SliceHeader, reflect.StringHeader, and unsafe casts between structs with architecture-sized fields. Reseach 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 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 varaibles 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 scenarions 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 pacakge 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 incorroect 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 incorroect 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 anaylisng 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

Congitive 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 copmlexity of functions or packages. It can select minimum compexlity and act as blocking linter in CI pipelines. The key offering from this linter is that it can calculate avg cyclomatic compelxity on package. β€” @bkielbasa

cyclop ./...
# to find packages with avg cyclomatic copmlexity 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

⏫ 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 lern better how functions are beign used. This can help in refactoring, naming, OOP. This tool calcuates 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