Skip to content

gonzaloserrano/go-code-review

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 

Repository files navigation

Go Code Review

An informal Go programming language code review guidelines index.

General References

Official

3rd Party

About Code Reviewing, Git & GitHub

  • The Guidelines for Faster PR Reviews from the Kubernetes project are a must. A quick summary:
    • Do small commits and, even better, small PRs.
    • Use separate PRs for fixes not related to your feature.
    • Add a different commit for review changes so it's easy to review them instead of the whole patch.
    • Test and document your code.
    • Don't add features you don't need.
  • Other guidelines:
    • How to Write a Git Commit Message
    • Use a Global .gitignore to avoid coupling personal environment's files. Use a .gitignore Generator when possible.
    • Prefer documentation as code (example test files) over READMEs.
    • Choose a good GitHub merge strategy:
      • Choose merge when:
        • Multiple commits.
        • Deps in different commit.
      • Choose squash if you just have a single commit to avoid the extra merge commit.
    • Have a proper Continuous Integration (CI) to ensure code quality:
      • Tests are in green (don't forget the -race flag).
      • New code or changes in functionality have the corresponding tests (e.g., gocovmerge + codecov.io or coveralls.io).

Linting

Just use golangci-lint. The defaults are fine. The more linters you enable, the more you can avoid nitpicks in PR reviews.

As a formatter, I personally prefer gofumpt: a stricter gofmt.

Design

In companies I worked with that had rich domains, such as video games and logistics, I've seen really good results applying techniques such as Test-Driven Development, Domain-Driven Design, and Command-Query Separation. You can read about it in this presentation.

Other stuff:

https://medium.com/@copyconstruct/logs-and-metrics-6d34d3026e38)).

Package Design

Other references:

Error Design

Concurrency

  • Coverage: at least happy path. Think about important error paths too.
  • Read Golang Concurrency Tricks.
  • Go makes concurrency easy enough to be dangerous source.
  • Shared mutable state is the root of all evil source.
  • How to protect shared mutable data:
    • With Mutex/RWMutex:
    • Or avoid sharing state and use message passing via channels:
    • Or use the atomic pkg if your state is a primitive (float64 for e.g.):
      • That's a nice thing for testing using spies in concurrent tests for e.g.
    • Or use things like sync.Once or sync.Map (>= Go 1.9).
  • Testing:
    • Good concurrent tests are mandatory and run with the -race flag.
    • Use parallel subtests when possible to make tests run faster (official blog post).
      • Must capture range vars! tc := tc // capture range variable (also see next point).
  • Watch out when:
    • Launching goroutines inside loops, see Using Goroutines on Loop Iteration Variables.
    • Passing pointers through channels.
    • You see the go keyword around without much explanation and tests:
      • Why was it done?
      • Is there a benchmark for that?
    • Async fire-and-forget logic: does that function return or not a value and/or error that must be handled?
  • Goroutine Lifetime:
  • If you write libs, leave concurrency to the caller when possible. See Zen Of Go.
    • Your code will be simpler, and the clients will choose the kind of concurrency they want.
  • Don't Expose Channels.
  • The Channel Closing Principle: don't close a channel from the receiver side and don't close a channel if the channel has multiple concurrent senders.
  • Refs:
  • Performance:

HTTP

Naming

Be careful when using short names. Usually, there is no problem with the receivers' names or temporary variables like indexes in loops, etc. But if your funcs violate the SRP or you have many args, then you can end up with many short names and it can do more harm than good. If understanding a var name is not straightforward, use longer names.

Other refs:

Testing

Testing on the Toilet short articles by Google have tons of wisdom.

Go Testing By Example presentation from Russ Cox (Go technical lead) is also great. Its best tips in my opinion are:

  • Make it easy to add new test cases, by using table-driven tests.
  • If you didn't add a test, you didn't fix the bug.
  • Code quality is limited by test quality.

Other guidelines:

  • Use coverage to find gaps or edge cases.
  • Any change in behavior ideally should have a test that covers it; if you do TDD, the test implementation comes first.
  • Scope: if tests are complicated think about
    • Refactoring the code.
    • Refactoring the tests.
    • Adding arrange / act / assert comments, if needed.
  • Types of tests:
  • Subtests: see concurrency.
  • Tags: if you have tagged some tests (e.g., // +build integration) then you need to run your tests with -tags.
  • Use t.Run and t.Parallel.
  • De-flaking unit tests, by the Kubernetes project.

Test doubles naming (from The Little Mocker by Uncle Bob):

  • Dummy: objects are passed around but never actually used.
  • Fake: objects actually have working implementations, but usually take some shortcut that makes them not suitable for production (an InMemoryTestDatabase is a good example).
  • Stubs: provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test.
  • Spies: stubs that also record some information based on how they were called. One form of this might be an email service that records how many messages it was sent.
  • Mocks: pre-programmed with expectations that form a specification of the calls they are expected to receive. They can throw an exception if they receive a call they don't expect and are checked during verification to ensure they got all the calls they were expecting.

Performance

Gotchas

  • 50 Shades of Go.
  • A time.Ticker must be stopped, otherwise, a goroutine is leaked.
  • Slices hold a reference to the underlying array as explained here and here. Slices are passed by reference; maybe you think you are returning small data but its underlying array can be huge.
  • Interfaces and nil gotcha, see the Understanding Nil talk by Francesc Campoy.

Nitpicks

About

Go code review guidelines

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published