<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Oleg Kovalov</title>
    <description>The latest articles on Forem by Oleg Kovalov (@olegkovalov).</description>
    <link>https://forem.com/olegkovalov</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F85843%2F11db78e2-43e4-48d4-b160-deb3735ea3c1.jpg</url>
      <title>Forem: Oleg Kovalov</title>
      <link>https://forem.com/olegkovalov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/olegkovalov"/>
    <language>en</language>
    <item>
      <title>Go linters configuration, the right version.</title>
      <dc:creator>Oleg Kovalov</dc:creator>
      <pubDate>Tue, 28 Mar 2023 07:06:33 +0000</pubDate>
      <link>https://forem.com/olegkovalov/go-linters-configuration-the-right-version-3jeo</link>
      <guid>https://forem.com/olegkovalov/go-linters-configuration-the-right-version-3jeo</guid>
      <description>&lt;p&gt;Originally posted here: &lt;a href="https://olegk.dev/go-linters-configuration-the-right-version"&gt;https://olegk.dev/go-linters-configuration-the-right-version&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;TLDR: See the &lt;code&gt;golangci-lint&lt;/code&gt; config that I find useful &lt;a href="https://gist.github.com/cristaloleg/f1610a9ca73ac420cda170fadd21b944"&gt;Github Gist&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Ah, these mighty linters. Tools that intend to make our life better but might hurt our software engineering egos.&lt;/p&gt;

&lt;p&gt;Somewhere in 2018, I was super eager to make my code and others' code better. Thankfully, I met &lt;a href="https://github.com/quasilyte"&gt;@quasilyte&lt;/a&gt; on Gophers Slack and we made "The most opinionated Go source code linter" &lt;a href="https://github.com/go-critic/go-critic"&gt;go-critic&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But we were not alone, &lt;a href="https://github.com/golangci/golangci-lint"&gt;golangci-lint&lt;/a&gt; started as a &lt;a href="https://github.com/alecthomas/gometalinter"&gt;gometalinter&lt;/a&gt; replacement and became a multi-linter in the Go ecosystem. If you have a linter and it makes code better for everyone and not only for your specific use case, consider adding it to &lt;code&gt;golangci-lint&lt;/code&gt; so everyone can benefit from that.&lt;/p&gt;

&lt;p&gt;(a funny thing that I found during writing this post: both repositories start their git commit history &lt;code&gt;Apr 29, 2018&lt;/code&gt;, LOL)&lt;/p&gt;

&lt;p&gt;As for today &lt;code&gt;golangci-lint&lt;/code&gt; contains 120+ linters and what I often notice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;not everyone is aware of how to configure it&lt;/li&gt;
&lt;li&gt;what are the pros and cons of some fields&lt;/li&gt;
&lt;li&gt;what is The Best™ &lt;code&gt;golangci-lint&lt;/code&gt; config&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this post, I'm going to share my config and some thoughts on why I prefer this or that. The post might look long due to the big YAML sections (hah, YAML), but most of the stuff is quite simple.&lt;/p&gt;

&lt;p&gt;Note: this post is based on &lt;code&gt;golangci-lint&lt;/code&gt; version &lt;code&gt;v1.52.1&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Linters environment
&lt;/h2&gt;

&lt;p&gt;When you're planning to start using or running a linter you need to understand the environment. I see 2 cases: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;personal projects

&lt;ul&gt;
&lt;li&gt;where you decide how to write the code.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;team projects

&lt;ul&gt;
&lt;li&gt;where there are other members with their opinion.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For personal projects everything is cool - just do the stuff, run a linter with any config you want, no problems.&lt;/p&gt;

&lt;p&gt;But with a team situation is different. If you are going to introduce a linter, change its config or disable a check - consider discussing it first, to have a team-wide agreement. This will cause less conflicts later (both code and personal).&lt;/p&gt;

&lt;p&gt;Everything that is about style is subjective. 1 newline in a pull request can start a 10+ comments thread because everyone has something to say. I would like to say this was a joke but it's not, true story.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running a linter
&lt;/h2&gt;

&lt;p&gt;Software engineering or coding in general is like riding a bike. When you're young and not that experienced you gonna use these small additional wheels to move stably. But when you're experienced enough these wheels will distract you more and more. As you can guess wheels are linters.&lt;/p&gt;

&lt;p&gt;After 5 years of writing Go daily I found that I don't see linter's suggestions because there is nothing to suggest. So, I switched from automatic runs to the manual, when I know/feel/think that something might be missed. There is a big chance that someone might find this idea over-optimistic but I know I'm not alone, consider this as a come out.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A short disclaimer: formatting or styling suggestions are fine, only bugs are unacceptable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As for junior and middle engineers I strongly suggest to go 1st option and don't rush to disable the linter. Instead, I heavily recommend enabling more linters, to see more suggestions and keep learning from them. Even if you will not find yourself in a situation when the linter distracts you - it's fine, this doesn't impact your professional skills.&lt;/p&gt;

&lt;h2&gt;
  
  
  My handy command
&lt;/h2&gt;

&lt;p&gt;I have a shell alias in my &lt;code&gt;~/.zshrc&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;lnt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"golangci-lint run --config=~/.golangci.yaml ./..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just type &lt;code&gt;lnt&lt;/code&gt; in a specific directory and see the results. It's very handy when I do &lt;code&gt;git clone&lt;/code&gt; of a repository and try to understand how good it is for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  The config
&lt;/h2&gt;

&lt;p&gt;It consists of 5 parts (as for &lt;code&gt;golangci-lint v1.52.1&lt;/code&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;run&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;general config of the linter&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;output&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;how the result should be presented&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;issues&lt;/code&gt; &amp;amp; &lt;code&gt;severity&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;how to treat the results&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;linters&lt;/code&gt; &amp;amp; &lt;code&gt;linters-settings&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;what linters to run and how to configure them&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's discuss each of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  run
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;run&lt;/code&gt; object is responsible for the &lt;code&gt;golangci-lint&lt;/code&gt; run. Here is what I found useful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Depends on your hardware, my laptop can survive 8 threads.&lt;/span&gt;
  &lt;span class="na"&gt;concurrency&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;

  &lt;span class="c1"&gt;# I really care about the result, so I'm fine to wait for it.&lt;/span&gt;
  &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30m&lt;/span&gt;

  &lt;span class="c1"&gt;# Fail if the error was met.&lt;/span&gt;
  &lt;span class="na"&gt;issues-exit-code&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;

  &lt;span class="c1"&gt;# This is very important, bugs in tests are not acceptable either.&lt;/span&gt;
  &lt;span class="na"&gt;tests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

  &lt;span class="c1"&gt;# In most cases this can be empty but there is a popular pattern&lt;/span&gt;
  &lt;span class="c1"&gt;# to keep integration tests under this tag. Such tests often require&lt;/span&gt;
  &lt;span class="c1"&gt;# additional setups like Postgres, Redis etc and are run separately.&lt;/span&gt;
  &lt;span class="c1"&gt;# (to be honest I don't find this useful but I have such tags)&lt;/span&gt;
  &lt;span class="na"&gt;build-tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;integration&lt;/span&gt; 

  &lt;span class="c1"&gt;# Up to you, good for a big enough repo with no-Go code.&lt;/span&gt;
  &lt;span class="na"&gt;skip-dirs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# - src/external_libs&lt;/span&gt;

  &lt;span class="c1"&gt;# When enabled linter will skip directories: vendor$, third_party$, testdata$, examples$, Godeps$, builtin$&lt;/span&gt;
  &lt;span class="c1"&gt;# Skipping `examples` sounds scary to me but skipping `testdata` sounds ok.&lt;/span&gt;
  &lt;span class="na"&gt;skip-dirs-use-default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;

  &lt;span class="c1"&gt;# Autogenerated files can be skipped (I'm looking at you gRPC).&lt;/span&gt;
  &lt;span class="c1"&gt;# AFAIK autogen files are skipped but skipping the whole directory should be somewhat faster.&lt;/span&gt;
  &lt;span class="c1"&gt;#skip-files:&lt;/span&gt;
  &lt;span class="c1"&gt;#  - "protobuf/.*.go"&lt;/span&gt;

  &lt;span class="c1"&gt;# With the read-only mode linter will fail if go.mod file is outdated.&lt;/span&gt;
  &lt;span class="na"&gt;modules-download-mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;readonly&lt;/span&gt;

  &lt;span class="c1"&gt;# Till today I didn't know this param exists, never ran 2 golangci-lint at once.&lt;/span&gt;
  &lt;span class="na"&gt;allow-parallel-runners&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;

  &lt;span class="c1"&gt;# Keep this empty to use the Go version from the go.mod file.&lt;/span&gt;
  &lt;span class="na"&gt;go&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  output
&lt;/h3&gt;

&lt;p&gt;Let's briefly talk about &lt;code&gt;output&lt;/code&gt;. As you can guess it's all about how results will be presented.&lt;/p&gt;

&lt;p&gt;As I have shared my command (in My Handy command section) I'm using a bash alias. The results are saved in a &lt;code&gt;lint.txt&lt;/code&gt; file that I can process in the editor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# I prefer the simplest one: `line-number` and saving to `lint.txt`&lt;/span&gt;
  &lt;span class="c1"&gt;#&lt;/span&gt;
  &lt;span class="c1"&gt;# The `tab` also looks good and with the next release I will switch to it&lt;/span&gt;
  &lt;span class="c1"&gt;# (ref: https://github.com/golangci/golangci-lint/issues/3728)&lt;/span&gt;
  &lt;span class="c1"&gt;#&lt;/span&gt;
  &lt;span class="c1"&gt;# There are more formats which can be used on CI or by your IDE.&lt;/span&gt;
  &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;line-number:lint.txt&lt;/span&gt;

  &lt;span class="c1"&gt;# I do not find this useful, parameter above already enables filepath&lt;/span&gt;
  &lt;span class="c1"&gt;# with a line and column. For me, it's easier to follow the path and&lt;/span&gt;
  &lt;span class="c1"&gt;# see the line in an IDE where I see more code and understand it better.&lt;/span&gt;
  &lt;span class="na"&gt;print-issued-lines&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;

  &lt;span class="c1"&gt;# Must have. Easier to understand the output.&lt;/span&gt;
  &lt;span class="na"&gt;print-linter-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

  &lt;span class="c1"&gt;# No, no skips, everything should be reported.&lt;/span&gt;
  &lt;span class="na"&gt;uniq-by-line&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;

  &lt;span class="c1"&gt;# To be honest no idea when this can be needed, maybe a multi-module setup?&lt;/span&gt;
  &lt;span class="na"&gt;path-prefix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;

  &lt;span class="c1"&gt;# Slightly easier to follow the results + getting deterministic output.&lt;/span&gt;
  &lt;span class="na"&gt;sort-results&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  issues &amp;amp; severity
&lt;/h3&gt;

&lt;p&gt;Configuring the issues:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;issues&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# I found it strange to skip the errors, setting 0 to have all the results.&lt;/span&gt;
  &lt;span class="na"&gt;max-issues-per-linter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;

  &lt;span class="c1"&gt;# Same here, nothing should be skipped to not miss errors.&lt;/span&gt;
  &lt;span class="na"&gt;max-same-issues&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;

  &lt;span class="c1"&gt;# When set to `true` linter will analyze only new code which are&lt;/span&gt;
  &lt;span class="c1"&gt;# not committed or after some specific revision. This is a cool &lt;/span&gt;
  &lt;span class="c1"&gt;# feature when you're going to introduce linter into a big project.&lt;/span&gt;
  &lt;span class="c1"&gt;# But I prefer going gradually package by package. &lt;/span&gt;
  &lt;span class="c1"&gt;# So, it's set to `false` to scan all code.&lt;/span&gt;
  &lt;span class="na"&gt;new&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;

  &lt;span class="c1"&gt;# 2 other params regarding git integration&lt;/span&gt;

  &lt;span class="c1"&gt;# Even with a recent GPT-4 release I still believe that&lt;/span&gt;
  &lt;span class="c1"&gt;# I know better how to do my job and fix the suggestions.&lt;/span&gt;
  &lt;span class="na"&gt;fix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To be honest, I have never configured the &lt;code&gt;severity&lt;/code&gt; part, it was always empty in my configuration, have nothing to recommend for you.&lt;/p&gt;

&lt;p&gt;If you are using non-default parameters, please share your setup in the comments or DM me, happy to hear.&lt;/p&gt;

&lt;h2&gt;
  
  
  Linters to run
&lt;/h2&gt;

&lt;p&gt;The core idea of the article: linters to enable or disable. Let me make a few disclaimers:&lt;/p&gt;

&lt;p&gt;1) The following config is based on my experience and my preferences.&lt;/p&gt;

&lt;p&gt;2) The linters that I keep disabled should not be treated as "this linter is bad" or "linter's author did a bad thing". Every linter makes sense, it just depends on the context and codebase.&lt;/p&gt;

&lt;p&gt;How to find your best config: I recommend you enable all available linters, run on your codebase (or a few of them), analyze the results and decide what is important for you and what is not. Again: if this config will be used in a team - discuss it before adopting it, and be friendly.&lt;/p&gt;

&lt;p&gt;(to get all available linters run: &lt;code&gt;golangci-lint help linters&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;In other words: YMMV (your mileage might vary). Keep that in mind. Let's go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;linters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Set to true runs only fast linters.&lt;/span&gt;
  &lt;span class="c1"&gt;# Good option for 'lint on save', pre-commit hook or CI.&lt;/span&gt;
  &lt;span class="na"&gt;fast&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;

  &lt;span class="na"&gt;enable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Check for pass []any as any in variadic func(...any).&lt;/span&gt;
    &lt;span class="c1"&gt;# Rare case but saved me from debugging a few times.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;asasalint&lt;/span&gt;

    &lt;span class="c1"&gt;# I prefer plane ASCII identifiers.&lt;/span&gt;
    &lt;span class="c1"&gt;# Symbol `∆` instead of `delta` looks cool but no thanks.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;asciicheck&lt;/span&gt;

    &lt;span class="c1"&gt;# Checks for dangerous unicode character sequences.&lt;/span&gt;
    &lt;span class="c1"&gt;# Super rare but why not to be a bit paranoid?&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bidichk&lt;/span&gt;

    &lt;span class="c1"&gt;# Checks whether HTTP response body is closed successfully.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bodyclose&lt;/span&gt;

    &lt;span class="c1"&gt;# Check whether the function uses a non-inherited context.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;contextcheck&lt;/span&gt;

    &lt;span class="c1"&gt;# Check for two durations multiplied together.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;durationcheck&lt;/span&gt;

    &lt;span class="c1"&gt;# Forces to not skip error check.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;errcheck&lt;/span&gt; 

    &lt;span class="c1"&gt;# Checks `Err-` prefix for var and `-Error` suffix for error type.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;errname&lt;/span&gt;

    &lt;span class="c1"&gt;# Suggests to use `%w` for error-wrapping.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;errorlint&lt;/span&gt;

    &lt;span class="c1"&gt;# Checks for pointers to enclosing loop variables.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;exportloopref&lt;/span&gt;

    &lt;span class="c1"&gt;# As you already know I'm a co-author. It would be strange to not use&lt;/span&gt;
    &lt;span class="c1"&gt;# one of my warmly loved projects.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gocritic&lt;/span&gt;

    &lt;span class="c1"&gt;# Forces to put `.` at the end of the comment. Code is poetry.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;godot&lt;/span&gt;

    &lt;span class="c1"&gt;# Might not be that important but I prefer to keep all of them.&lt;/span&gt;
    &lt;span class="c1"&gt;# `gofumpt` is amazing, kudos to Daniel Marti https://github.com/mvdan/gofumpt&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gofmt&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gofumpt&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;goimports&lt;/span&gt;

    &lt;span class="c1"&gt;# Allow or ban replace directives in go.mod&lt;/span&gt;
    &lt;span class="c1"&gt;# or force explanation for retract directives.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gomoddirectives&lt;/span&gt;

    &lt;span class="c1"&gt;# Powerful security-oriented linter. But requires some time to&lt;/span&gt;
    &lt;span class="c1"&gt;# configure it properly, see https://github.com/securego/gosec#available-rules&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gosec&lt;/span&gt;

    &lt;span class="c1"&gt;# Linter that specializes in simplifying code.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gosimple&lt;/span&gt;

    &lt;span class="c1"&gt;# Official Go tool. Must have. &lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;govet&lt;/span&gt;

    &lt;span class="c1"&gt;# Detects when assignments to existing variables are not used&lt;/span&gt;
    &lt;span class="c1"&gt;# Last week I caught a bug with it.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ineffassign&lt;/span&gt;

    &lt;span class="c1"&gt;# Even with deprecation notice I find it useful.&lt;/span&gt;
    &lt;span class="c1"&gt;# There are situations when instead of io.ReaderCloser &lt;/span&gt;
    &lt;span class="c1"&gt;# I can use io.Reader. A small but good improvement.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;interfacer&lt;/span&gt;

    &lt;span class="c1"&gt;# Fix all the misspells, amazing thing.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;misspell&lt;/span&gt;

    &lt;span class="c1"&gt;# Finds naked/bare returns and requires change them.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;nakedret&lt;/span&gt;

    &lt;span class="c1"&gt;# Both require a bit more explicit returns.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;nilerr&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;nilnil&lt;/span&gt;

    &lt;span class="c1"&gt;# Finds sending HTTP request without context.Context.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;noctx&lt;/span&gt;

    &lt;span class="c1"&gt;# Forces comment why another check is disabled.&lt;/span&gt;
    &lt;span class="c1"&gt;# Better not to have //nolint: at all ;)&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;nolintlint&lt;/span&gt;

    &lt;span class="c1"&gt;# Finds slices that could potentially be pre-allocated.&lt;/span&gt;
    &lt;span class="c1"&gt;# Small performance win + cleaner code.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;prealloc&lt;/span&gt;

    &lt;span class="c1"&gt;# Finds shadowing of Go's predeclared identifiers.&lt;/span&gt;
    &lt;span class="c1"&gt;# I hear a lot of complaints from junior developers.&lt;/span&gt;
    &lt;span class="c1"&gt;# But after some time they find it very useful.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;predeclared&lt;/span&gt;

    &lt;span class="c1"&gt;# Lint your Prometheus metrics name.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;promlinter&lt;/span&gt;

    &lt;span class="c1"&gt;# Checks that package variables are not reassigned.&lt;/span&gt;
    &lt;span class="c1"&gt;# Super rare case but can catch bad things (like `io.EOF = nil`)&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;reassign&lt;/span&gt;

    &lt;span class="c1"&gt;# Drop-in replacement of `golint`.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;revive&lt;/span&gt;

    &lt;span class="c1"&gt;# Somewhat similar to `bodyclose` but for `database/sql` package.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;rowserrcheck&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sqlclosecheck&lt;/span&gt;

    &lt;span class="c1"&gt;# I have found that it's not the same as staticcheck binary :\&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;staticcheck&lt;/span&gt;

    &lt;span class="c1"&gt;# Is a replacement for `golint`, similar to `revive`.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;stylecheck&lt;/span&gt;

    &lt;span class="c1"&gt;# Check struct tags.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tagliatelle&lt;/span&gt;

    &lt;span class="c1"&gt;# Test-related checks. All of them are good.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tenv&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;testableexamples&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;thelper&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tparallel&lt;/span&gt;

    &lt;span class="c1"&gt;# Remove unnecessary type conversions, make code cleaner&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;unconvert&lt;/span&gt;

    &lt;span class="c1"&gt;# Might be noisy but better to know what is unused&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;unparam&lt;/span&gt;

    &lt;span class="c1"&gt;# Must have. Finds unused declarations.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;unused&lt;/span&gt;

    &lt;span class="c1"&gt;# Detect the possibility to use variables/constants from stdlib.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;usestdlibvars&lt;/span&gt;

    &lt;span class="c1"&gt;# Finds wasted assignment statements.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;wastedassign&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And know some linters that I prefer to keep disabled. Again, my use case, yours might be different:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;disable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Detects struct contained context.Context field. Not a problem.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;containedctx&lt;/span&gt;

    &lt;span class="c1"&gt;# Checks function and package cyclomatic complexity.&lt;/span&gt;
    &lt;span class="c1"&gt;# I can have a long but trivial switch-case.&lt;/span&gt;
    &lt;span class="c1"&gt;#&lt;/span&gt;
    &lt;span class="c1"&gt;# Cyclomatic complexity is a measurement, not a goal.&lt;/span&gt;
    &lt;span class="c1"&gt;# (c) Bryan C. Mills / https://github.com/bcmills&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cyclop&lt;/span&gt;

    &lt;span class="c1"&gt;# Abandoned, replaced by `unused`.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;deadcode&lt;/span&gt;

    &lt;span class="c1"&gt;# Check declaration order of types, consts, vars and funcs.&lt;/span&gt;
    &lt;span class="c1"&gt;# I like it but I don't use it.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;decorder&lt;/span&gt;

    &lt;span class="c1"&gt;# Checks if package imports are in a list of acceptable packages.&lt;/span&gt;
    &lt;span class="c1"&gt;# I'm very picky about what I import, so no automation.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;depguard&lt;/span&gt;

    &lt;span class="c1"&gt;# Checks assignments with too many blank identifiers. Very rare.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;dogsled&lt;/span&gt;

    &lt;span class="c1"&gt;# Tool for code clone detection.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;dupl&lt;/span&gt;

    &lt;span class="c1"&gt;# Find duplicate words, rare.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;dupword&lt;/span&gt;

    &lt;span class="c1"&gt;# I'm fine to check the error from json.Marshal ¯\_(ツ)_/¯&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;errchkjson&lt;/span&gt;

    &lt;span class="c1"&gt;# All SQL queries MUST BE covered with tests.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;execinquery&lt;/span&gt;

    &lt;span class="c1"&gt;# Forces to handle more cases. Cool but noisy.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;exhaustive&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;exhaustivestruct&lt;/span&gt; &lt;span class="c1"&gt;# Deprecated, replaced by check below.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;exhaustruct&lt;/span&gt;

    &lt;span class="c1"&gt;# Forbids some identifiers. I don't have a case for it.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;forbidigo&lt;/span&gt;

    &lt;span class="c1"&gt;# Finds forced type assertions, very good for juniors.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;forcetypeassert&lt;/span&gt;

    &lt;span class="c1"&gt;# I might have long but a simple function.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;funlen&lt;/span&gt;

    &lt;span class="c1"&gt;# Imports order. I do this manually ¯\_(ツ)_/¯&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gci&lt;/span&gt;

    &lt;span class="c1"&gt;# I'm not a fan of ginkgo and gomega packages.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ginkgolinter&lt;/span&gt;

    &lt;span class="c1"&gt;# Checks that compiler directive comments (//go:) are valid. Rare.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gocheckcompilerdirectives&lt;/span&gt;

    &lt;span class="c1"&gt;# Globals and init() are ok.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gochecknoglobals&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gochecknoinits&lt;/span&gt;

    &lt;span class="c1"&gt;# Same as `cyclop` linter (see above)&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gocognit&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;goconst&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gocyclo&lt;/span&gt;

    &lt;span class="c1"&gt;# TODO and friends are ok.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;godox&lt;/span&gt;

    &lt;span class="c1"&gt;# Check the error handling expressions. Too noisy.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;goerr113&lt;/span&gt;

    &lt;span class="c1"&gt;# I don't use file headers.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;goheader&lt;/span&gt;

    &lt;span class="c1"&gt;# 1st Go linter, deprecated :( use `revive`.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;golint&lt;/span&gt;

    &lt;span class="c1"&gt;# Reports magic consts. Might be noisy but still good.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gomnd&lt;/span&gt;

    &lt;span class="c1"&gt;# Allowed/blocked packages to import. I prefer to do it manually.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;gomodguard&lt;/span&gt;

    &lt;span class="c1"&gt;# Printf-like functions must have -f.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;goprintffuncname&lt;/span&gt;

    &lt;span class="c1"&gt;# Groupt declarations, I prefer manually.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;grouper&lt;/span&gt;

    &lt;span class="c1"&gt;# Deprecated.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ifshort&lt;/span&gt;

    &lt;span class="c1"&gt;# Checks imports aliases, rare.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;importas&lt;/span&gt;

    &lt;span class="c1"&gt;# Forces tiny interfaces, very subjective.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;interfacebloat&lt;/span&gt;

    &lt;span class="c1"&gt;# Accept interfaces, return types. Not always.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ireturn&lt;/span&gt;

    &lt;span class="c1"&gt;# I don't set line length. 120 is fine by the way ;)&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;lll&lt;/span&gt;

    &lt;span class="c1"&gt;# Some log checkers, might be useful.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;loggercheck&lt;/span&gt;

    &lt;span class="c1"&gt;# Maintainability index of each function, subjective.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;maintidx&lt;/span&gt;

    &lt;span class="c1"&gt;# Slice declarations with non-zero initial length. Not my case.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;makezero&lt;/span&gt;

    &lt;span class="c1"&gt;# Deprecated. Use govet `fieldalignment`.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;maligned&lt;/span&gt;

    &lt;span class="c1"&gt;# Enforce tags in un/marshaled structs. Cool but not my case.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;musttag&lt;/span&gt;

    &lt;span class="c1"&gt;# Deeply nested if statements, subjective.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;nestif&lt;/span&gt;

    &lt;span class="c1"&gt;# Forces newlines in some places.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;nlreturn&lt;/span&gt;

    &lt;span class="c1"&gt;# Reports all named returns, not that bad.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;nonamedreturns&lt;/span&gt;

    &lt;span class="c1"&gt;# Deprecated. Replaced by `revive`.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;nosnakecase&lt;/span&gt;

    &lt;span class="c1"&gt;# Finds misuse of Sprintf with host:port in a URL. Cool but rare.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;nosprintfhostport&lt;/span&gt;

    &lt;span class="c1"&gt;# I don't use t.Parallel() that much.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;paralleltest&lt;/span&gt;

    &lt;span class="c1"&gt;# Often non-`_test` package is ok.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;testpackage&lt;/span&gt;

    &lt;span class="c1"&gt;# Compiler can do it too :)&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;typecheck&lt;/span&gt;

    &lt;span class="c1"&gt;# I'm fine with long variable names with a small scope.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;varnamelen&lt;/span&gt;

    &lt;span class="c1"&gt;# gofmt,gofumpt covers that (from what I know).&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;whitespace&lt;/span&gt;

    &lt;span class="c1"&gt;# Don't find it useful to wrap all errors from external packages.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;wrapcheck&lt;/span&gt;

    &lt;span class="c1"&gt;# Forces you to use empty lines. Great if configured correctly.&lt;/span&gt;
    &lt;span class="c1"&gt;# I mean there is an agreement in a team.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;wsl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Linters config
&lt;/h2&gt;

&lt;p&gt;Many of these linters can be configured more precisely via &lt;code&gt;linters-settings&lt;/code&gt; config. Initially I planned to write about all of them but I think it will be just a waste of time. See &lt;a href="https://golangci-lint.run/usage/linters/"&gt;Linters&lt;/a&gt; page and adapt to your own needs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;linters-settings&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# I'm biased and I'm enabling more than 100 checks&lt;/span&gt;
  &lt;span class="c1"&gt;# Might be too much for you. See https://go-critic.com/overview.html&lt;/span&gt;
  &lt;span class="na"&gt;gocritic&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;enabled-tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;diagnostic&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;experimental&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;opinionated&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;performance&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;style&lt;/span&gt;
    &lt;span class="na"&gt;disabled-checks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# These 3 will detect many cases, but they do sense&lt;/span&gt;
      &lt;span class="c1"&gt;# if it's performance oriented code&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;hugeParam&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;rangeExprCopy&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;rangeValCopy&lt;/span&gt;

  &lt;span class="na"&gt;errcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Report `a := b.(MyStruct)` when `a, ok := ...` should be.&lt;/span&gt;
    &lt;span class="na"&gt;check-type-assertions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;# Default: false&lt;/span&gt;

    &lt;span class="c1"&gt;# Report skipped checks:`num, _ := strconv.Atoi(numStr)`.&lt;/span&gt;
    &lt;span class="na"&gt;check-blank&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;# Default: false&lt;/span&gt;

    &lt;span class="c1"&gt;# Function to skip.&lt;/span&gt;
    &lt;span class="na"&gt;exclude-functions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;io/ioutil.ReadFile&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;io.Copy(*bytes.Buffer)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;io.Copy(os.Stdout)&lt;/span&gt;

  &lt;span class="na"&gt;govet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;disable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;fieldalignment&lt;/span&gt; &lt;span class="c1"&gt;# I'm ok to waste some bytes&lt;/span&gt;

  &lt;span class="na"&gt;nakedret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# No naked returns, ever.&lt;/span&gt;
    &lt;span class="na"&gt;max-func-lines&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;# Default: 30&lt;/span&gt;

  &lt;span class="na"&gt;tagliatelle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;case&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;snake&lt;/span&gt; &lt;span class="c1"&gt;# why it's not a `snake` by default?!&lt;/span&gt;
        &lt;span class="na"&gt;yaml&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;snake&lt;/span&gt; &lt;span class="c1"&gt;# why it's not a `snake` by default?!&lt;/span&gt;
        &lt;span class="na"&gt;xml&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;camel&lt;/span&gt;
        &lt;span class="na"&gt;bson&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;camel&lt;/span&gt;
        &lt;span class="na"&gt;avro&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;snake&lt;/span&gt;
        &lt;span class="na"&gt;mapstructure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kebab&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;I hope I haven't missed some linters. If so - don't forget to mention this in the comments. Here is the config as a &lt;a href="https://gist.github.com/cristaloleg/f1610a9ca73ac420cda170fadd21b944"&gt;Github gist&lt;/a&gt;. Feel free to copy and adapt it. There is no &lt;del&gt;silver bullet&lt;/del&gt; perfect config, find yours.&lt;/p&gt;

&lt;p&gt;Aaaand a huge kudos to &lt;a href="https://github.com/ldez"&gt;Ludovic Fernandez&lt;/a&gt; who is currently keeping &lt;code&gt;golangci-lint&lt;/code&gt; alive. Great job!&lt;/p&gt;

&lt;p&gt;Thanks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/olegkovalov/go-linters-configuration-the-right-version-3jeo"&gt;DEV&lt;/a&gt; / &lt;a href="https://lobste.rs/s/a1gljv/go_linters_configuration_right_version"&gt;Lobsters&lt;/a&gt; / &lt;a href="https://www.reddit.com/r/golang/comments/124gb2h/go_linters_configuration_the_right_version/"&gt;Reddit&lt;/a&gt; / &lt;a href="https://twitter.com/oleg_kovalov/status/1640610322040999937"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
    </item>
    <item>
      <title>Github Actions and Go</title>
      <dc:creator>Oleg Kovalov</dc:creator>
      <pubDate>Tue, 21 Mar 2023 08:26:13 +0000</pubDate>
      <link>https://forem.com/olegkovalov/github-actions-and-go-4mag</link>
      <guid>https://forem.com/olegkovalov/github-actions-and-go-4mag</guid>
      <description>&lt;p&gt;Originally posted here: &lt;a href="https://olegk.dev/github-actions-and-go"&gt;https://olegk.dev/github-actions-and-go&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;TLDR: See &lt;a href="https://github.com/cristalhq/.github/blob/53335fadafd4a2409b342643789aea60465dbf40/.github/workflows/build.yml"&gt;cristalhq/.github&lt;/a&gt; build workflow and how it can be used &lt;a href="https://github.com/cristalhq/jsn/blob/ab26cf0cee3c27ea8bb30707ee4f923e4f735031/.github/workflows/build.yml"&gt;cristalhq/jsn&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;I love open source, and also I love Go. So, a few months ago I decided to build the best CI for Go that I could easily reuse across my projects.&lt;/p&gt;

&lt;p&gt;This post shares the results. &lt;/p&gt;

&lt;p&gt;Note: Post is based on version &lt;code&gt;v0.5.0&lt;/code&gt; of &lt;a href="https://github.com/cristalhq/.github"&gt;cristalhq/.github&lt;/a&gt; repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Github Actions
&lt;/h2&gt;

&lt;p&gt;You probably know what GitHub Actions are, and because they are well-integrated with GitHub, I am too lazy to look at alternatives.&lt;/p&gt;

&lt;p&gt;The only thing that I recommend is to look at &lt;a href="https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions"&gt;Workflow syntax&lt;/a&gt; which explains what syntax is available for us.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependabot
&lt;/h2&gt;

&lt;p&gt;I've noticed that not all developers are happy with Dependabot notifications (pull requests), and they find them noisy. For me, they do more good than bad. Consider just making the interval parameter bigger (a week or longer).&lt;/p&gt;

&lt;p&gt;In almost all my projects I use the following Dependabot configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="na"&gt;updates&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;package-ecosystem&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gomod&lt;/span&gt;
    &lt;span class="na"&gt;directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/"&lt;/span&gt;
    &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;daily&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;package-ecosystem&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GitHub-actions&lt;/span&gt;
    &lt;span class="na"&gt;directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/"&lt;/span&gt;
    &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;daily&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the YAML above in &lt;code&gt;.github/dependabot.yml&lt;/code&gt; and it's done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic workflow
&lt;/h2&gt;

&lt;p&gt;The default Github Actions for Go looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Go&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Go&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-go@v4&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;go-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.19&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go build -v ./...&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Test&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go test -v ./...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In many cases it's ok but we can do better. Let's add more good things to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Regular runs
&lt;/h2&gt;

&lt;p&gt;A few times I got stuck with a failing CI because I missed some 3rd party changes and found them too late, running CI regularly prevents that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;10&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;1'&lt;/span&gt; &lt;span class="c1"&gt;# run "At 10:00 on Monday"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A cron expression is an example, play on &lt;a href="https://crontab.guru/"&gt;crontab guru&lt;/a&gt; to find yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Runs on and Go version
&lt;/h2&gt;

&lt;p&gt;Most of my projects do not interact with the operating system, so running on Ubuntu is enough.&lt;/p&gt;

&lt;p&gt;It's a good practice to keep your code working on the current and previous Go versions. It's not a strict rule, but it might simplify life for you or your users when a new Go version is released.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/actions/setup-go"&gt;actions/setup-go@v4&lt;/a&gt; action names them as &lt;code&gt;stable&lt;/code&gt; and &lt;code&gt;oldstable&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;timeout-minutes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt; &lt;span class="c1"&gt;# just in case ¯\_(ツ)_/¯&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;go&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;stable'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;oldstable'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  First steps:
&lt;/h2&gt;

&lt;p&gt;Just check out the code and install Go. No magic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check out code&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Go&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-go@v4&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;go-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.go }}&lt;/span&gt;
        &lt;span class="na"&gt;check-latest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;check-latest&lt;/code&gt; set to &lt;code&gt;true&lt;/code&gt; will pick the latest Go release for the given Go version. For example, for Go 1.20, it will download Go 1.20.2 (which is the latest at the moment of writing this post).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;actions/setup-go@v4&lt;/code&gt; also supports caching out of the box. Please see &lt;a href="https://github.com/actions/setup-go#caching-dependency-files-and-build-outputs"&gt;README section&lt;/a&gt;. I don't have this configured because most of my projects are dependency-free or I don't care that much.&lt;/p&gt;

&lt;h2&gt;
  
  
  Go steps
&lt;/h2&gt;

&lt;p&gt;Enough to setup the CI job, time to do real work!&lt;/p&gt;

&lt;h3&gt;
  
  
  Formatting and go vet
&lt;/h3&gt;

&lt;p&gt;If code isn't formatted there is no sense to build it (or even to review). Same regarding &lt;code&gt;go vet&lt;/code&gt;. It's the smallest linter but reports good things:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Go Format&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gofmt -s -w . &amp;amp;&amp;amp; git diff --exit-code&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Go Vet&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go vet ./...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thanks to &lt;a href="https://github.com/mvdan"&gt;Daniel Martí&lt;/a&gt; &lt;code&gt;gofmt&lt;/code&gt; is super fast now &lt;a href="https://github.com/golang/go/issues/43566"&gt;Go #43566&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check dependencies
&lt;/h3&gt;

&lt;p&gt;I love to write dependency-free packages but sometimes I need something to import. So, the dependencies must be verified.&lt;/p&gt;

&lt;p&gt;First, we need to check that &lt;code&gt;go.mod&lt;/code&gt; is correct, download dependencies and verify &lt;code&gt;go.sum&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Go Tidy&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go mod tidy &amp;amp;&amp;amp; git diff --exit-code&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Go Mod&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go mod download&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Go Mod Verify&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go mod verify&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might think that &lt;code&gt;go mod verify&lt;/code&gt; is an extra step, but this will help you to catch force pushes (or even supply chain attacks) in your dependencies.&lt;/p&gt;

&lt;p&gt;I heavily recommend adding this to all your workflows.&lt;/p&gt;

&lt;h3&gt;
  
  
  Codegen &amp;amp; build
&lt;/h3&gt;

&lt;p&gt;Maybe it's not the most popular feature in Go and there are different ways to manage this but I prefer and recommend you to commit generated files and always treat them as your code (well, it's yours anyway).&lt;/p&gt;

&lt;p&gt;And let's build all packages and ignore the executables (if any, anyway we are not going to use them on CI):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Go Generate&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go generate ./... &amp;amp;&amp;amp; git diff --exit-code&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Go Build&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go build -o /dev/null ./...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test
&lt;/h3&gt;

&lt;p&gt;Okay, this part might sound overengineered but give me a moment to explain. Most of my projects are quite simple and don't require a database or any separate service to run, it's just a Go package that I use in other projects.&lt;/p&gt;

&lt;p&gt;Doing just &lt;code&gt;go test&lt;/code&gt; is exactly what I'm doing (ignore the flags and &lt;code&gt;if: ...&lt;/code&gt;, I will explain them in a second):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Go Test&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ !inputs.skipTests }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go test -v -count=1 -race -shuffle=on -coverprofile=coverage.txt ./...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But as for Redis client that I'm writing (&lt;a href="https://github.com/cristalhq/redis"&gt;cristalhq/redis&lt;/a&gt;), I need to setup Redis to test everything properly which requires additional changes to the workflow file. And I don't want that, I want to reuse as much as possible!&lt;/p&gt;

&lt;p&gt;What I did: I added a boolean parameter to the GitHub Actions workflow named &lt;code&gt;skipTests&lt;/code&gt; which is &lt;code&gt;false&lt;/code&gt; by default (because most of the projects are simple).&lt;/p&gt;

&lt;p&gt;But for the Redis client repository, I change it to &lt;code&gt;true&lt;/code&gt; and disable tests. I'm running them separately in another workflow file called &lt;a href="https://github.com/cristalhq/redis/blob/c20a6f2df4e854a5a1ffa0c167e66f0bee675df8/.github/workflows/e2e.yml"&gt;e2e.yml&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And when tests are disabled I'm only compiling them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Go Compile Tests&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ inputs.skipTests }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go test -exec /bin/true ./...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's a hacky way to compile all packages and delete executables, see &lt;a href="https://github.com/golang/go/issues/15513"&gt;Go #15513&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To add a parameter to the workflow just do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;workflow_call&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;skipTests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Skip&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tests,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;useful&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;when&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;there&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;dedicated&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;CI&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;job&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tests'&lt;/span&gt;
        &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;boolean&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test flags
&lt;/h3&gt;

&lt;p&gt;Let's take a closer look at the &lt;code&gt;go test&lt;/code&gt; flags:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-v&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;I prefer verbose output where I can see everything&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-count=1&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;prevents caching &lt;code&gt;go test&lt;/code&gt; results, which might be irrelevant for CI but I prefer to keep it&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-race&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;enable race detector, this increases build and run time but it helps to find concurrency bugs earlier, priceless.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-shuffle=on&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;run tests in a different order to prevent implicit test order dependencies&lt;/li&gt;
&lt;li&gt;shameless plug: it was my &lt;a href="https://github.com/golang/go/issues/28592"&gt;proposal&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-coverprofile=coverage.txt&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;collect the coverage to upload it later&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;./...&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;just test all the packages&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Benchmark
&lt;/h3&gt;

&lt;p&gt;Benchmarking on the CI in many cases is a bad idea due to noisy neighbours (read other processes on the machine) that will skew your results.&lt;/p&gt;

&lt;p&gt;But a rare and bad thing: your benchmark might be broken and you might miss that. Well, this happened to me a few times.&lt;/p&gt;

&lt;p&gt;To fix that we can run benchmarks once just to verify that they're still passing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Go Benchmark&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go test -v -shuffle=on -run=- -bench=. -benchtime=1x ./...&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-v&lt;/code&gt; &amp;amp; &lt;code&gt;-shuffle=on&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;you know that already&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-run=-&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;means do not run tests&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-bench=.&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;means run every benchmark&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-benchtime=1x&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;tells to run every benchmark once&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;./...&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;do this in every package&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I took this suggestion from a good Go issue "Go CI best practices" &lt;a href="https://github.com/golang/go/issues/42119"&gt;Go #42119&lt;/a&gt;. Again, thanks to Daniel Marti.&lt;/p&gt;

&lt;h3&gt;
  
  
  Coverage
&lt;/h3&gt;

&lt;p&gt;In the previous step, we collected code coverage, better to keep it somewhere. I prefer &lt;a href="https://about.codecov.io/"&gt;Codecov&lt;/a&gt; but you might use any other:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload Coverage&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ !inputs.skipTests }}&lt;/span&gt;  &lt;span class="c1"&gt;# upload when we really run our tests&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;codecov/codecov-action@v3&lt;/span&gt;
        &lt;span class="na"&gt;continue-on-error&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;  &lt;span class="c1"&gt;# we don't care if it fails&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{secrets.CODECOV_TOKEN}}&lt;/span&gt;  &lt;span class="c1"&gt;# set in repository settings&lt;/span&gt;
          &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./coverage.txt&lt;/span&gt;  &lt;span class="c1"&gt;# file from the previous step&lt;/span&gt;
          &lt;span class="na"&gt;fail_ci_if_error&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's all! This is the build and/or test workflow that I use daily in 100+ Go repositories.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vulncheck
&lt;/h2&gt;

&lt;p&gt;Go team released &lt;code&gt;govulncheck&lt;/code&gt; tool with Go 1.19. It's a simple and very fast tool to check "Does your code contain vulnerable lines?". Check the announcement for more info &lt;a href="https://go.dev/blog/vuln"&gt;https://go.dev/blog/vuln&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And see &lt;code&gt;vuln.yml&lt;/code&gt; workflow in &lt;a href="https://github.com/cristalhq/.github/blob/53335fadafd4a2409b342643789aea60465dbf40/.github/workflows/vuln.yml"&gt;cristalhq/.github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="https://brandur.org/fragments/govulncheck-ci"&gt;https://brandur.org/fragments/govulncheck-ci&lt;/a&gt; where I found this originally.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it can be reused?
&lt;/h2&gt;

&lt;p&gt;Quite easily. GitHub Actions allow us to reuse workflows. To use the described above workflow in any of my projects I need a few lines in &lt;code&gt;.github/workflows/build.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cristalhq/.github/.github/workflows/build.yml@v0.4.0&lt;/span&gt;

  &lt;span class="na"&gt;vuln&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cristalhq/.github/.github/workflows/vuln.yml@v0.4.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(where &lt;a href="https://github.com/cristalhq/.github"&gt;cristalhq/.github&lt;/a&gt; is a repository for common stuff across the whole &lt;a href="https://github.com/cristalhq"&gt;cristalhq&lt;/a&gt; organisation, like security policies, code of conduct, etc)&lt;/p&gt;

&lt;p&gt;One of the projects that use this workflow &lt;a href="https://github.com/cristalhq/jsn/blob/ab26cf0cee3c27ea8bb30707ee4f923e4f735031/.github/workflows/build.yml"&gt;cristalhq/jsn&lt;/a&gt;. See &lt;a href="https://docs.github.com/en/actions/using-workflows/reusing-workflows"&gt;Reusing workflows&lt;br&gt;
&lt;/a&gt; documentation for more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;Here is the full workflow: &lt;a href="https://github.com/cristalhq/.github/blob/53335fadafd4a2409b342643789aea60465dbf40/.github/workflows/build.yml"&gt;build.yml&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That's the basic CI for most of my Go projects. Many things can be added like fuzzing, automatic releases, linting, etc.&lt;/p&gt;

&lt;p&gt;But in this post, I wanted to share the core component of my Go CI workflow.&lt;/p&gt;

&lt;p&gt;Thanks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;p&gt;&lt;a href=""&gt;DEV&lt;/a&gt; / &lt;a href="https://lobste.rs/s/9fbnlv/github_actions_go"&gt;Lobsters&lt;/a&gt; / &lt;a href="https://medium.com/@olegkovalov/github-actions-and-go-639a9841b715"&gt;Medium&lt;/a&gt; / &lt;a href="https://www.reddit.com/r/golang/comments/11xaw79/github_actions_and_go/"&gt;Reddit&lt;/a&gt; / &lt;a href="https://twitter.com/oleg_kovalov/status/1638094023872204800"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
