この記事は Go Advent Calendar 2022 10日目の記事です。

はじめに

私が Go に初めて触れたのは2017年ごろだったんですが、しばらく触れない時期があり本格的に触り始めたのはここ数年でした。
以前別の記事でも触れたのですが、依存モジュールの管理もいつの間にか楽になったなと思ったものです。
今年はそんな Go を使ったプロダクトを一から作ってリリースすることができたので、今自分の中で一番熱い言語と言っても良いかもしれません。

今回は上記プロダクトのうち、Go に関連して導入したツールを色々紹介していきます。

検討したツール

  • 静的解析ツール・テストの導入
  • API ドキュメント
  • マイグレーション
  • テーブル定義自動生成
  • ホットリロード
  • スケジュールタスク OSS の修正

静的解析ツール・テストの導入

そもそも Go はコードの静的解析やテストが標準機能で実現できます。
それらを使うのは前提として、あとは細かいところをチーム開発用に整備していきました。
ちなみに、いずれも GitHub Actions を使って自動でチェックするようにしています。

静的解析ツール

go vet でも色々チェックしてはくれるものの、チーム開発するうえで自動でチェックできることは可能な限りやってしまいたいという思いがありました。
そこでルールが色々揃っており、カスタマイズもできる staticcheck を導入し、go vet と併用しています。
採用するルールについてはこちらの記事を参考にさせていただきました。

また、セキュリティ的に問題のあるコードに関しては gosec を使ってチェックしています。

テスト

Go の標準機能にはアサーションがありません。
そのため、値の細かい比較やエラーメッセージは自前で実装する必要がありました。
最初はそれでも十分だったのですが、コード量が増えてくるにつれ「本当に必要なテストが出来ているか」を担保できているかのチェックがコードレビューでしづらくなってきました。
そこで testify を導入し、アサーションに関しては testify に任せるようにしました。
これだけで冗長なテストコードをかなり減らすことができました。

例えば以下のようなコードが、

func TestSomeFunction(t *testing.T) {
    s, err := SomeFunction() // s は何かの配列、err はエラー
    if err != nil {
        t.Fatal(err)
    }
    if len(s) == 0 {
        t.Fatal("Response length is zero")
    }
}

以下のように書けます。

func TestSomeFunction(t *testing.T) {
    s, err := SomeFunction()
    assert.Nil(t, err)
    assert.NotEmpty(t, s)
}

API ドキュメント

コードと別で管理すると乖離していく未来しか見えなかった点や、とはいえドキュメントは欲しいという点を踏まえて Swagger を導入しました。
Web フレームワークには Gin を使っていたのですが、gin-swagger というツールを使って Swagger とも連携できました。
コード内にコメント形式で記述することでドキュメントを自動生成してくれるため、ドキュメントの管理コストをかなり小さくできました。
interface を指定することで、interface に変更があった場合自動で追従してくれるのも嬉しいポイントです。
そこからは OpenAPI Generator を使い、フロントから api を利用するコードについても自動生成しています。

マイグレーション

ORM には GORM を使う予定だったのですが、一方で GORM のマイグレーションについては色々課題を感じてもいました。
データパッチなども考慮すると、マイグレーションについては SQL を直接書いて実行できるツールを使いたいと思い、結果 sql-migrate を使うことにしました。
導入が他ツール同様 go install のみで済むという点もかなり嬉しいポイントです。

テーブル定義自動生成

tbls を利用しています。
元々は PlantUML などを使って ER 図を書いていたのですが、コードとの同期が難しかったり各開発者にセットアップを強いる必要があったりチーム開発をスムーズに進めるにあたりいくつか問題点がありました。
Markdown で管理できると良いなと思っていたところで出会ったのがこのツールです。
対象の DB に接続し、そこにある情報を正として ER 図や DDL を生成してくれます。
Docker コンテナも用意されていたので、それを実行することで自動生成するようにしています。
CI で実施できるともっと良いんですが、今はバックエンドを触る人数もそこまで多くないので一旦ローカルで生成するようにしています。

# compose.yml の例
services:
  # 中略
  gen-erd:
    image: ghcr.io/k1low/tbls:v1.56.1
    volumes:
      - ./docs/erd:/var/docs/erd
    depends_on:
      - database
    command: doc mysql://user:password@database:3306/sample_db?hide_auto_increment var/docs/erd -f

ホットリロード

Go はコンパイルする性質上、コードを修正するたびにバイナリを再起動する必要があります。
これを毎回やってると面倒なので、ホットリロードツールは必須でした。
色々調べてみたんですが、スター数や導入の簡単さから Air を導入しました。

スケジュールタスク OSS の修正

こちらは Go で書かれた OSS である ecschedule を使っています。
バッチ処理ももちろん Go で書いており、それを AWS の ECS スケジュールタスク上で動かしています。
Terraform での管理も考えましたが、

  • インフラの変更とタスクの更新はライフサイクルが異なること
  • アプリケーション開発者にインフラ知識も強いることになること

といった理由から別々で管理することにしました。
また、以前別の記事でも書いたのですが、ecschedule については自分も少しコントリビュートしているので、何か問題が起きても対応しやすいというメリットがありました。

ちなみに、前述の記事でも書いていますが ECS へのデプロイには ecspresso というツールを使っており、こちらも Go 製です。

まとめ

というわけで、以前リリースした Go プロダクトについてやったことや使っているツールを簡単に振り返ってみました。
Go を使ったプロダクト開発の参考になれば幸いです。

今回紹介したツールのうちいくつかは、以前の記事でも具体的なコードとともに紹介しています。
興味がある方はぜひそちらもご覧ください。