Golang開発のすすめ

# はじめに
ジョインしているPJ内で `golang+gPRC` なAPIを構築することになりGoを3ヶ月程齧ってみたので
Goの開発時に気をつければ良さそうなポイントをまとめておきます。
この記事はこれからGo開発する人、もしくは私と同じくGoで開発をし始めしばらく経った人を対象としています。

Goをしばらく使ってみて感じたところとして
Goは良い意味でも悪い意味でも自由度が少なく主張の激しい言語という印象です。
ジェネリクス型がないせいで型が違うだけの同じようなコピペコードを複数書く必要があったり
スペースの代わりにタブを強要される所も場合によっては人をイラつかせる要素ではありますが
[郷(Go)に入っては郷(Go)に従え (鵜飼文敏氏)](https://gihyo.jp/news/report/01/GoCon2014Autumn/0002?page=1) とも言われていますしそういうもんなのかなと思ってます。



# 開発環境について
主に開発環境やツールに関するポイント

## Go Modules を使用する
Go では `GOPATH` という特殊な概念がありgoのすべてのコードはサードパーティ製の
ライブラリ等も含めて `$GOPATH/src` 直下に置かなくてはいけません。

コードはモジュール名毎ではなくPJ毎や顧客毎にディレクトリを
分けて置きたいと思うのは職業エンジニアの常だと思うのですが
それを実現するのが Go 1.11 より追加された `Go Modules` モード です。
`Go Modules` モードを有効にすることでコードを置く場所を縛られなくなります。
有効にするには環境変数に `GO111MODULE=on` の設定を加えます。

```
$ export GO111MODULE=on
```
ちなみに `Go Module` モードはGo 1.13ではデフォルト有効になっているそうです[^1]。
上記の環境変数は不要かもしれません。

Go Modules を使用するためには init コマンドで `go.mod` ファイルを生成します。

```
$ go mod init <module名>
```
module名には 実際にそのモジュールを配置しているdomainを入力しましょう。
例えばgithub上で開発するなら `github.com/tomtwinkle/go-sample` のようなパスになります。


## Go Modules でローカルのリポジトリを参照する
Go Modules モードでGoland等のIDEで開発していると `go.mod` の中身を見て
masterブランチにある最新のモジュールを勝手に `go get` してきます。
それでは依存するlibraryもセットで開発していたりすると困ることになるので
ローカルで開発しているディレクトリを参照させたいことがあると思います。

その場合は `go.mod` に `replace <module> => <local directory path>` の設定を追記することで
ローカルのディレクトリを参照させることが出来ます。

```
module github.com/tomtwinkle/go-sample

go 1.13

replace github.com/tomtwinkle/go-lib => ../go-lib

require (
	github.com/tomtwinkle/go-lib v1.0.0
)
```

## `go fmt` `go vet` をpush前に行う
これは開発環境についてというより、遵守しておくと自分と他のメンバーが幸せになるルールみたいなものです。
goには標準ツールの中に `go fmt` `go vet` というコードフォーマット、Lintツールがあります。
リポジトリにpushする際に必ず上記2コマンドでコードをフォーマットしつつ構文誤り等を修正しておきましょう。

`go fmt` はローカルで行うのが吉。
`go vet` に関してはCIに組み込んでしまっても良いかもしれません。

毎度叩くのを忘れる人は ↓ の記事のようにgitのpre-commitにhookスクリプトを作っておくのが良さそうです。
[Go言語のプログラムをgitで管理する時に便利なこと](https://qiita.com/ttyokoyama/items/38047f4d794687d4f291)

## 内部でしか使わないパッケージは `internal` の内部に置く

`internal` パッケージがどのようなものかは以下サイトが参考になります。
https://mattn.kaoriya.net/software/lang/go/20150820102400.htm

* internal/hoge
* internal/fuga

のように配置すると
`internal` 内の `hoge` `fuga` パッケージは private になり外部から参照を行うとエラーになります。

## Goland を使う
身も蓋もない話ですが、Goで開発するならJetBrainsのGoLandを使いましょう。
Golandそのものも使いやすいですがGo開発のためのpluginが揃っているのが魅力です。
`File Watchers` pluginを使い `go fmt` `go vet` をファイル保存時に
自動実行してもらうだけで生産性が1.2倍くらいは向上します（個人談）。
※この記事は個人的なものです｡ 私の雇用者とは全く関係はありません｡




# 開発時のコード & レビュー観点について
## パッケージ構成
現在のプロジェクトは以下を参考にした上でクリーンアーキテクチャの概念を元にパッケージ構成を構築しています。
https://github.com/golang-standards/project-layout

クリーンアーキテクチャについてはここで書くには長すぎるのでまたいずれ。
上記の中で `vendor` フォルダは `Go Modules` を使用する場合不要です。


## エラーハンドリング
Goには例外がないため他言語で書かれる `try/catch` のように
その関数内で起きたErrorを全て拾うようなことは出来……なくはないが(panic/recover機構)
panicは本当にシステムを止めなければ行けないような深刻なError以外では使うべきではないと考えています。
少なくとも現段階で `recover` しなきゃいけないようなコード書く機会ありませんでした。

Goのエラーハンドリングについては[散々他のサイトで言われている](https://medium.com/@sebdah/go-best-practices-error-handling-2d15e1f0c5ee)通りですが
以下のポイントを抑えておくと良いでしょう。

1. error発生後に処理を行う余地を与えず即座に `return` させる。
2. errorを作る際にはその関数内の情報をなるべくWrapする。
3. errorをreturnさせる際には必ずそのerrorをStackさせる。
4. 上記を楽に処理する [pkg/errors](https://github.com/pkg/errors) を使用する。
5. エラーログは[中央集権的(centralized error handling)](https://echo.labstack.com/guide/error-handling) になるべくmainに近い場所で出力する。

### NGパターン

```go
func main() {
	ctx := context.Background()
	sample(ctx)
}

func sample(ctx context.Context) {
	logger := ctxzap.Extract(ctx)

	user, err := getUser()
	if err == nil && user != nil {
		logger.Info(fmt.Sprintf("I'm %s !", user.firstName))
	}
	if err != nil {
		logger.Error(err.Error())
	}
}
```
`getUser()` でErrorを拾った後に即座に返さずに何かしらの処理を入れている。
実際にはerror時にその処理には入らないかもしれないが
機能追加などによりそれは担保されなくなるかもしれない。


### OKパターン

```go
func main() {
	ctx := context.Background()
	logger := ctxzap.Extract(ctx)
	err := sample(ctx)
	if err != nil {
		logger.Error(fmt.Sprintf("%+v", err))
	}
}

func sample(ctx context.Context) error {
	hoge := "sample"
	err := sampleChild(ctx, hoge)
	if err != nil {
		return errors.WithStack(err)
	}
	return nil
}

func sampleChild(ctx context.Context, hoge string) error {
	logger := ctxzap.Extract(ctx)

	user, err := getUser(hoge)
	if err != nil {
		return errors.Wrapf(err, "user not found. arg=%s", hoge)
	}
	logger.Info(fmt.Sprintf("I'm %s !", user.firstName))
	return nil
}
```

error を返す関数を呼んだ直後に `if err != nil {}` で error の有無をチェックし
errorだった場合には即座に return しています。
この `if err != nil {}` 構文はGoを書くようになるとよく見かけるようになります。

## エラーログ出力

エラーログ出力に関しては絶賛模索中なので、こうした方が良いみたいな意見募集中です。
ちなみに上記パターンではmainでエラーログを拾っていますが
gRPC Serverの場合は [go-grpc-middleware](https://github.com/grpc-ecosystem/go-grpc-middleware) を使用し `WithUnaryServerChain` の中で
(もしくはserver-side streamを使用する場合は `WithStreamServerChain` の中で)
custom interceptor を chain させその中で拾うのが良さそうです。

<details><summary>gRPC Serverのエラーログ出力</summary><div>

```go
package main

import (
	"context"
	"fmt"
	grpcMiddleware "github.com/grpc-ecosystem/go-grpc-middleware"
	grpcZap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
	"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap"
	grpcCtxtags "github.com/grpc-ecosystem/go-grpc-middleware/tags"
	"github.com/pkg/errors"
	"go.uber.org/zap"
	"google.golang.org/grpc"
	"log"
	"net"
	customInterceptor "sample"
)

func main() {
	c := zap.NewProductionConfig()
	c.Level = zap.NewAtomicLevelAt(zap.DebugLevel)
	logger, err := c.Build()
	if err != nil {
		log.Fatal(err)
	}

	lis, err := net.Listen("tcp", fmt.Sprintf(":%s", "50051"))

	s := grpc.NewServer(
		grpcMiddleware.WithUnaryServerChain(
			customInterceptor.UnaryServerInterceptor(),
			grpcCtxtags.UnaryServerInterceptor(grpcCtxtags.WithFieldExtractor(grpcCtxtags.CodeGenRequestFieldExtractor)),
			grpcZap.UnaryServerInterceptor(logger),
		),
		grpcMiddleware.WithStreamServerChain(
			customInterceptor.StreamServerInterceptor(),
			grpcCtxtags.StreamServerInterceptor(grpcCtxtags.WithFieldExtractor(grpcCtxtags.CodeGenRequestFieldExtractor)),
			grpcZap.StreamServerInterceptor(logger),
		),
	)
	{ // user handler
		// gRPC api handler
		server := handler.NewUser()
		// protobuf generate gRPC server
		protobufInterface.RegisterUserServer(s, server)
	}
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}
```


```go
package custom_interceptor

import (
	"context"
	"fmt"
	"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap"
	"github.com/pkg/errors"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"google.golang.org/grpc"
)

type wrapServerStream struct {
	grpc.ServerStream
	c context.Context
}

type wrapCore struct {
	zapcore.Core
}

func UnaryServerInterceptor() grpc.UnaryServerInterceptor {
	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (_ interface{}, err error) {
		logger := ctxzap.Extract(ctx)
		logger = logger.WithOptions(zap.WrapCore(func(c zapcore.Core) zapcore.Core {
			return &wrapCore{Core: c}
		}))
		ctx = ctxzap.ToContext(ctx, logger)

		resp, err := handler(ctx, req)
		if err != nil {
			logger.Error(fmt.Sprintf("%+v\n", err))
			return resp, errors.WithStack(err)
		}
		return resp, nil
	}
}

func StreamServerInterceptor() grpc.StreamServerInterceptor {

	return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
		ctx := stream.Context()
		logger := ctxzap.Extract(stream.Context())
		logger = logger.WithOptions(zap.WrapCore(func(c zapcore.Core) zapcore.Core {
			return &wrapCore{Core: c}
		}))
		ctx = ctxzap.ToContext(ctx, logger)

		err := handler(srv, &wrapServerStream{ServerStream: stream, c: ctx})
		if err != nil {
			logger.Error(fmt.Sprintf("%+v\n", err))
			return errors.WithStack(err)
		}
		return nil
	}
}

```
</div></details>

## 関数の引数や戻り値に大きい構造体を使う場合はポインターを使う

こういうのですね。

```go
// 引数：ポインター、戻り値：ポインターの場合
func GetSample(req *RequestSample) (*Sample, error) {}

// 引数：実体、戻り値：実体の場合
func GetSample(req RequestSample) (Sample, error) {}
```

構造体の大きさによる所もありますが毎回構造体をコピーするのはコストが高いです。
大きい構造体を関数に渡したり戻したりする場合はポインターで返すのが良さそうです。
特に戻り値で返す構造体はmodelになる事が多くほぼポインターで良いかなと思っています。

何でもかんでもポインターにしてしまうとシンプルではなくなるので
`int` , `string` などのプリミティブな値の場合はそのまま
`time.Time` みたいな小さな構造体も実体のまま渡した方が使いやすいかなと思っていたり。


## `[]T` ではなく `[]*T` を使用する
どっちが良いのかなと悩んで以下の記事にたどり着きました。
https://www.reddit.com/r/golang/comments/5lheyg/returning_t_vs_t/

> []T:
> 
> Pro: Contiguous in memory with respect to T (the runtime will allocate sizeof(T) * cap), which increases cache locality. For example, a loop over []int can be very efficient and potentially even be vectorized.
> 
> Con: To access a slice element, it has to be copied, which is more expensive than passing a pointer around. Similarly, modifying an element requires copying the element, modifying it and then copying it back.
> 
> []*T:
> 
> Pro: No copying needed in order to read/write elements.
> 
> Con: Requires an indirection to dereference the stored pointer, which can point anywhere in RAM and will be unlikely to take advantage of cache locality.
> 
> Cache locality also includes RAM prefetching; modern CPU architectures are complicated, but sequential access is generally faster than random access.

`[]T` では slice の要素をコピーするため `T` の構造体が大きければ大きいほど
ループ内で処理する際のコストが高くなりそうです。
`[]int` と `[]*int` だともしかしたら前者の方が良いのかもしれません。


## `Context.Value` にはリクエストスコープで完結するものを入れる

皆さんは `Context.Value`　使ってますか？
私は未だにどこまでの範囲で使えばいいのか迷っていますが
レビューの観点で見る場合
取り敢えずリクエストスコープ内で作られ、リクエストスコープが
終わったタイミングで消えるものなら入れてもいいかなと一応定義しています。

### 入れてもよさそうなもの
* request の metadata (ユーザID や ユーザ名 や browser情報など)
* request 時刻

### 微妙だけど入れてるもの
* データベースのtransactionオブジェクト

### 入れちゃダメそうなもの
* アプリのバージョン情報
* データベースのconnectionオブジェクト
* ステートフルなユーザのsession情報


## `testify/assert.Equal()` ではなくて `cmd.Diff()` を使う

`testify/assert.Equal()` は 内部で `reflect.DeepEqual` を使用しています。
`reflect.DeepEqual` は 構造体 の unexport value (private) まで参照するため
`time.Time` 型が含まれた構造体同士を比較する場合など
テストを書く際に考慮しなくてはいけない事柄が増えてしまいます。

* 取得した構造体の unexport value を参照するようなケースはない
* Getter で取得するならその構造体の Getter のテストを書けばよい
* ドメイン駆動なモデルで unexport value により export value が変化するなら  
  そのモデルのテストを書けばよい

以上からテストのassertの観点では `cmd.Diff()` を使用した方が良さそうです。

### assert.Equal

```go
import (
	"github.com/stretchr/testify/assert"
	"testing"
)

type Sample struct {
	Public  string
	private int64
}

func GetSample() (*Sample, error) {
	return &Sample{
		Public:  "a",
		private: int64(1),
	}, nil
}

func TestSample_GetSample(t *testing.T) {
	t.Run("assert.Equal", func(t *testing.T) {
		expected := &Sample{
			Public: "a",
		}
		actual, err := GetSample()
		if err != nil {
			t.Error(err.Error())
		}
		assert.Equal(t, expected, actual)
	})
}
```

```
--- FAIL: TestSample_GetSample (0.00s)
    --- FAIL: TestSample_GetSample/assert.Equal (0.00s)
        main_test.go:25: 
            	Error Trace:	main_test.go:25
            	Error:      	Not equal: 
            	            	expected: &main.Sample{Public:"a", private:0}
            	            	actual  : &main.Sample{Public:"a", private:1}
            	            	
            	            	Diff:
            	            	--- Expected
            	            	+++ Actual
            	            	@@ -2,3 +2,3 @@
            	            	  Public: (string) (len=1) "a",
            	            	- private: (int64) 0
            	            	+ private: (int64) 1
            	            	 })
            	Test:       	TestSample_GetSample/AssertEqual


Expected :&main.Sample{Public:"a", private:0}
Actual   :&main.Sample{Public:"a", private:1}
```

### cmp.Diff()

unexport value があった場合にどう動くか option によります。
デフォルトでは unexport value があるとpanicして使いにくいため
`DeepEqual` utilを作って使いやすくしています。

```go
import (
	"github.com/google/go-cmp/cmp"
	"github.com/google/go-cmp/cmp/cmpopts"
	"reflect"
	"testing"
)

type Sample struct {
	Public  string
	private int64
}

func GetSample() (*Sample, error) {
	return &Sample{
		Public:  "a",
		private: int64(1),
	}, nil
}

func TestSample_GetSample(t *testing.T) {
	t.Run("cmp.Diff", func(t *testing.T) {
		expected := &Sample{
			Public: "a",
		}
		actual, err := GetSample()
		if err != nil {
			t.Error(err.Error())
		}
		DeepEqual(t, expected, actual)
	})
}

func DeepEqual(t *testing.T, expected, actual interface{}, opts ...cmp.Option) bool {
	t.Helper()

	var opt cmp.Option
	e := reflect.ValueOf(expected)
	// cmpopts.IgnoreUnexported() オプション で Unexported な attribute を無視する 
	if e.Kind() == reflect.Ptr {
		// cmpopts.IgnoreUnexported() の引数には interface の実体を指定する必要がある
		opt = cmpopts.IgnoreUnexported(e.Elem().Interface())
	} else {
		opt = cmpopts.IgnoreUnexported(expected)
	}

	opts = append(opts, opt)
	if diff := cmp.Diff(expected, actual, opts...); diff != "" {
		t.Errorf("Diff: (-expected +actual)\n%s", diff)
		return false
	}
	return true
}
```

```
=== RUN   TestSample_GetSample/cmp.Diff
--- PASS: TestSample_GetSample (0.00s)
    --- PASS: TestSample_GetSample/cmp.Diff (0.00s)
PASS
```


# 参考サイト
[^1]: https://budougumi0617.github.io/2019/02/15/go-modules-on-go112/