美文网首页
2018-03-13 Go best practices

2018-03-13 Go best practices

作者: 四火流年 | 来源:发表于2018-03-13 14:54 被阅读20次

Successful Go Program Design, 6 Years On

Development environment

Some Go developers uses a two-entry GOPATH, e.g.
$HOME/go/external:$HOME/go/internal
go get will fetch into the first path, so it can be useful if you need strict separation of third-party vs. internal code.

Top Tip: put $GOPATH/bin in your $PATH, so installed binaries are easily accessible

Repository structure

The basic idea is to have two top-level directories, pkg and cmd. Underneath pkg, create directories for each of your libraries. Underneath cmd, create directories for each of your binaries. All of your Go code should live exclusively in one of these locations.

github.com/peterbourgon/foo/
    circle.yml
    Dockerfile
    cmd/
        foosrv/
            main.go
        foocli/
            main.go
    pkg/
        fs/
            fs.go
            fs_test.go
            mock.go
            mock_test.go
        merge/
            merge.go
            merge_test.go
        api/
            api.go
            api_test.go

All of your artifacts remain go gettable.

Top Tip: put library code under a pkg/ subdirectory. put binaries under a cmd/ subdirectory.

Top Tip: always use fully-qualified import paths. never use relative imports.

Formatting and style

use gofmt, goimports
The go vet tool produces(almost!) no false positives, so you might consider making it part of your precommit hook.

Configuration

12-factor apps encourage you to use environment vars for configuration.
define and parse your flags in func main. Only func main has the right to decide the flags that will be available to the user.

Program design

Good

      foo, err := newFoo(
          *fooKey,
          bar,
          100 * time.Millisecond,
          nil,
      )
      if err != nil {
          log.Fatal(err)
      }
      defer foo.close()

Bad

      // Don't do this.
      cfg := fooConfig{}
      cfg.Bar = bar
      cfg.Period = 100 * time.Millisecond
      cfg.Output = nil
      foo, err := newFoo(*fooKey, cfg)
      if err != nil {
          log.Fatal(err)
      }
      defer foo.close()

Better

      // This is better.
      cfg := fooConfig{
          Bar:    bar,
          Period: 100 * time.Millisecond,
          Output: nil,
      }
      foo, err := newFoo(*fooKey, cfg)
      if err != nil {
          log.Fatal(err)
      }
      defer foo.close()

Even better

      // This is even better.
      foo, err := newFoo(*fooKey, fooConfig{
          Bar:    bar,
          Period: 100 * time.Millisecond,
          Output: nil,
      })
      if err != nil {
          log.Fatal(err)
      }
      defer foo.close()

Top Tip: use struct literal initialization to avoid invalid intermediate state. inline struct declarations where possible.

Top Tip: make the zero value useful, especially in config objects

Top Tip: make dependencies explicit.

Top Tip: loggers are dependencies, just like references to other components, database handlers, commandline flags, etc.

Logging and instrumentation

  1. log only actionable information, which will be read by a human or a machine.
  2. avoid fine-grained log levels - info and debug are probably enough
  3. use structured logging, like go-kit/log
  4. loggers are dependencies

Let’s use loggers and metrics to pivot and address global state more directly. Here are some facts about Go:

  • log.Print uses a fixed, global log.Logger
  • http.Get uses a fixed, global http.Client
  • http.Server, by default, uses a fixed, global log.Logger
  • database/sql uses a fixed, global driver registry
  • func init exists only to have side effects on package-global state

Testing

Top Tip: use many small interfaces to model dependencies

Top Tip: tests only need to test the thing being tested

Dependency management

  1. FiloSottile/gvt
  2. Masterminds/glide
  3. kardianos/govendor
  4. constabulary/gb

Build and deploy

Top Tip: perfer go install to go build

Conclusion

Top tips:

  1. Put $GOPATH/bin in your $PATH, so installed binaries are easily accessible.
  2. Put library code under a pkg/ subdirectory. Put binaries under a cmd/ subdirectory.
  3. Always use fully-qualified import paths. Never use relative imports.
  4. Defer to Andrew Gerrand’s naming conventions.
  5. Only func main has the right to decide which flags are available to the user.
  6. Use struct literal initialization to avoid invalid intermediate state.
  7. Avoid nil checks via default no-op implementations.
  8. Make the zero value useful, especially in config objects.
  9. Make dependencies explicit!
  10. Loggers are dependencies, just like references to other components, database handles, commandline flags, etc.
  11. Use many small interfaces to model dependencies.
  12. Tests only need to test the thing being tested.
  13. Use a top tool to vendor dependencies for your binary.
  14. Libraries should never vendor their dependencies.
  15. Prefer go install to go build.

相关文章

网友评论

      本文标题:2018-03-13 Go best practices

      本文链接:https://www.haomeiwen.com/subject/vkzafftx.html