実用 Go言語
実用 Go言語 ―システム開発の現場で知っておきたいアドバイス | 渋川 よしき, 辻 大志郎, 真野 隼記 |本 | 通販 | Amazon
どんな本
Goを業務で使っていこうとしている初心者が中級者になるためにGoらしく書いたり運用したりするためのTips集
感想
Goは慣れない文法はありつつもシンプルである。その分コードを書き進めていく際にGolangの定石や設計パターンをどれだけ知っているかが上達するために重要。この本は実際の業務でありがちが要件や悩みどころに対して、その辺りのGolangの定石や設計パターンはどうなっているのかというのを解説してくれている点がとても良い。困ったときに対象となる章を開いて知見を得るというような使い方もできそうなので初心者が手元に置いておくと便利そうな一冊。
ただJavaとの比較が多いのでJavaを知らないと知らんがなとなる
個人的にはPHP/Ruby/JS(仕事) -> Golang(個人開発) -> TS/Dart(仕事) -> Rust(個人開発)と変遷してきたのだが、今またGolangに戻ってきて感じるのは手に馴染む必要十分なツールだなということ。
DartでFlutter開発していた時に困ったらソース読めばいいか〜となるくらい標準ライブラリのコードが綺麗でドキュメントが多くすぐジャンプしてなんでも追えた経験が結構好きだった。あれに近いものをGolangにも感じる。
PHP/Ruby/JSを触っていた初期にGolangへ入門した当時は静的型にそもそも慣れてないし書きにくいなと思っていたが、その後いくつもの言語を使い、型にもシステムプログラミングの肌感もある程度知った今、Golangは楽にかけて良い...という気持ちになる。文法的にも機能的にも少ないから記憶力が低下した老化した脳への負担が少ないのも良い。
Rustも所有権とか関数型的な考え方とかそもそもシステムプログラミング全般の実装を学ぶときに得るものが大きかったが、自分は所詮Webやアプリ開発を行う仕事が主なのでGolangで十分なことが多い。
当時使っていた頃はv1.8~1.12くらいでまだモジュール周りのセオリーが整ってないような時期だったけど今は採用している会社も増えているしエコシステムもより成熟した印象を受ける。
実際の業務開発で積み重ねられた経験に裏打ちされた実用本が発売されるほどには各所で使われているということだ思うのでこれを機にまたGolang方面を主戦場としていくのも良さそうと感じた。
メモ
開発環境はGolangの開発環境用のDev Containersを作るで作ったDev Containersを使う
Goは1.12とかGoModuleが導入されるかされないかあたりまでは個人開発で使っていたが、その後使うことがなくなった。この本では1.18に対応しているので知らない文法や用例、その他決まり事などが増えていそう。
まずは付録Aと付録Bを読んで文法の駆け足おさらいと最新情報を得るための情報源をキャッチアップする
付録A: 駆け足で学ぶGoの基礎
20ページくらいでおさらいできて便利。が、reflection/Contextあたりは省かれてるので別途復習しないとだめっぽい。Genericsは3,4章で、goroutineは16章で触れられている。
プログラミング言語Go完全入門が良さそう
Tutorial: Getting started with generics - The Go Programming Language
Contextはスレッドローカルストレージ的な使い方ができるやつ
付録B: Goの最新情報を知るための情報源
GoDocの読み方
気になる章をつまみ食いしていくか
1.6 関数のオプション引数
構造体パターン > ビルダー形式 | Functional Option パターン
関数定義のセオリーを知ることは野良のpackageのインターフェースを把握しやすくなることにつながる
他のライブラリのコードやGoDocを読む時にパターンを知っておくと認知負荷が減るので良い
1.7 プログラムを制御する引数
基本はflagパッケージで十分。ちゃんとCLIとして整えるなら別だけど。
spf13/cobra: A Commander for modern Go CLI interactionsは使ったことある。kingpin.v2 - gopkg.in/alecthomas/kingpin.v2は知らなかった
環境変数を構造体にマッピングするkelseyhightower/envconfig: Golang library for managing configuration data from environment variables便利そう
1.8 メモリ起因のパフォーマンス低下を解消する
スライス/マップのメモリ確保が主因
メモリ確保の流れ
すでに確保済みのメモリが不足した場合は、OSに要求する
確保済みのものから新しい配列用にメモリを割り当てる
新しい配列に古い配列の内容をコピー
古い配列にアクセスするスライスや変数がなくなったらメモリを解放
ユーザー空間ではなくOSの処理に移行 -> 遅い
最大量がわかっている場合はcapacity指定をする
計測重要
どうやって計測するのがメジャーなのだろうか?
deferはreturnでスコープを抜けるときに実行されるのでforなどの中ではループを抜けるまでdeferが実行されない -> リソース確保が解けない
1.9 文字列の結合
+での結合が遅い理由 -> Goの文字列は不変 == 上記の問題と一緒でメモリが都度新しく確保されてしまうので
strings.Buiderを使う。Growを使うと最大文字数を指定できる。
ひとまとめにしちゃうのもあり -> str := "a" + "b" + "c"みたいな
2章 定義型
良いコード/悪いコードで学ぶ設計入門でもよく語られていた業務知識を型で豊かにしていくみたいなことは定義型と構造体の拡張で行うと良さそう
なるべくレシーバーとして実装することが、Go らしいプ ログラミングの方法
fmtの拡張で秘匿情報のログ漏れ対策良い
Stringer/GoStringerを定義するだけ
この二つの方の違いは?
前者は%sや%v、後者は%#v(Goの構文で出力される。例えば構造体であれば構造体のフィールド名まで表示される)のフォーマットに使われる
3章 構造体
ファクトリ関数はどのpackageでも普通に使われてるからstructの初期化の標準機能だと思ってたけど慣習だった
確かにゼロ値での初期化は注意が必要で暗黙知が生まれやすそう。ファクトリを使う慣習の方が事故が減りそうではある。
あーGoは構造的部分型なんだった
ジェネリクスの解説ここにあった。Go1.18に対応してるが付録Aからは省かれてるから無いのかと思った
構造体設計のポイント
値ではなくポインタとして扱う
値として扱うとフィールドにmapとかスライスを使えないのでそもそも不便
が、値として扱えばスタックにメモリ確保されるだけなのでヒープを使わずGCの手間を減らせる
ValueObjectt系なものは値として扱い、Entityは構造体でミュータブルに扱うのがおすすめっぽい。全部イミュータブルにしたくはなるが...
ゼロ値の初期化をデフォルトのままにするのか別途初期化処理を施すのかは場合による
「ポインターで扱う前提」「ミュータブルな API セット を提供」「特定のファクトリー関数でのみ動作(ゼロ値動作を保証しない)」が、いちばんお手軽
空の構造体
メモリを占有しないのでゼロバイトで使いまわせるのか、なるほど。
チャネルのキャッチボールとかで使われたりする
sync.Pool - Go Packages
デザインパターンとしてはテンプレートパターンよりストラテジーパターンを使うのが主流
人間が読んで理解できるデザインパターン解説#3: 振舞い系(翻訳)|TechRacho by BPS株式会社
インターフェース(抽象型)と具体的な実装を分ける。インスタンス化するときに具体的な実装だけ差し替えられるようにする。
4章 インターフェース
構造的部分型
シンプルなインタフェースを使う API をコアとして作り、それをラップして使いやすい API を別に提供するのが、標準ライブラリを含め Go で広く行われている設計手法
型アサーション(type assertion): A Tour of Go
型スイッチ(type switch): A Tour of Go
localstack
クラウドサービスをローカルでも再現するやつ。ローカルと本番環境を同じにしてテストするのに使える。
へぇ〜
テストを考えてすべ てモック化できるようにインタフェースにしておく、という考えをしている Go ユーザーは少数派です。
ストラテジーパターンとしてDI的に差し替える実装の方が手軽そう
実装の都合であればインタフェースにはしない、利用者の利便性のため のインタフェースならあり
5章 エラーハンドリング
エラーに含まれる文字列を特定の文字列と比較する方法はアンチパターン
どの言語でも一緒
exponential backoff
Exponential Backoff And Jitter | AWS Architecture Blog
log.Fatalを使うとプログラムが即座に終了してdeferでリソースが解放できない。レビュー時に気を付ける。
Errors are values - The Go Programming Language
kisielk/errcheck: errcheck checks that you checked errors.
errのチェック忘れ防止
6章 パッケージ・モジュール
最新のセオリーを知っておきたいので有益。ネットの情報だとばらつきが結構あるので。
パッケージ
フォルダ = パッケージ
モジュール ⊇ パッケージ
モジュールとパッケージ
モジュール: https://github.com/myname/myproject
パッケージ: myproject
ライブラリ管理
Go Module使え
GOPATHは過去の遺物になった
Goで新規プロジェクトを開始する
パッケージ構成について
golang-standards/project-layout: Standard Go Project Layoutは有名だが複雑すぎる。公式の推奨でもない。
パッケージを分けたくなるほど複雑な場合はリポジトリを分ける方が良い
共通循環参照だけは避けたい。
パッケージは出来るだけ分けず、ルートに共通部分のロジックを集め、そこをutils置き場とする
proxy.golang.orgなどのミラーがあるのでgithubの大元のリポジトリが削除されてもDL自体はできる。vender不要になってるらしい。
プラグイン機構
知らん概念だった。
標準のpluginパッケージはあんまり使われてないらしい。import時にinitが呼ばれる(ブランクインポート)を応用して、initをもつpluginを作り、実行側でそのpackageをimport _ "someplugin/plugins/a"のように読み込み、必要なpackageのみpluginとして使うみたいなやり方が紹介されている。
その他
Go 製 CLI にプラグイン機構を作る方法n選
Consider pluggable CLI tool implementation #gocon - Speaker Deck
7章 Goプログラミングの環境を整備する
VSCodeの便利機能一覧のところ全部覚えたい
Introduction | golangci-lint
よく使われるビルドフラグ
CGO_ENABLED=0 go build -trimpath -ldflags '-s -w -X main.version=1.0.0' main.go
cgoが必要な時は1にする
特定のCPU向けにビルドする場合はこんな感じの環境変数も足す
GOOS=linuxとGOARCH=amd64
Go向けのGitHub Actions例
Go向けのEditorConfig例
github/gitignore: A collection of useful .gitignore templates
以降8~16章は各種ユースケースごとで使える話題が多め。サラッと目を通しつつ近々で使いそうとか興味のあるところをかいつまんで読んでいく(とはいえここからが本番感がある...)
8章 さまざまなデータフォーマット
JSON-to-Go: Convert JSON to Go instantly
JSONをGoの構造体に変換してくれるやつ
json.RawMessageを使って動的に変わるjsonのフィールドを遅延評価できるようにしておく
JSONを構造体にマッピングしつつ生データを保存するUnmarshalJSONの実装方法 - My External Storage
固定長フォーマットを扱うという経験が無いんだけどどこで使われてるんだ...
9章 Goとリレーショナルデータベース
ORM使うのか?とマイグレーションどう管理してるのか?だけ気になる
sql package - database/sql - Go Packagesが全て。ORMもこれのラッパーにすぎない。
RDBの作成時刻や更新時刻用カラムに関するプラクティス | おそらくはそれさえも平凡な日々
テストは出来るだけ本物のデータベースを使う
Prisma で本物のDBMSを使って自動テストを書く - mizdra's blogのブコメでt-wadaさんもDBにアクセスするところのテストでモックはお勧めできないと言っている
ORM
GORM - The fantastic ORM library for Golang, aims to be developer friendly.
ent
マイグレーションの管理の話は出てこなかった...。上記ORMには機能的には存在しているようだ。
entを使った省エネバックエンド開発 | メルカリエンジニアリングを読むとマイグレーションは周りに関してはentは良さそう
【golang】sqlcコマンドで「SQLクエリから型安全なGoコードを生成」し、生産性を上げたいを読むとsqlcというのもあるっぽい
10章 HTTPサーバー
ミドルウェア例
chi/middleware at master · go-chi/chi
Middleware | Echo - High performance, minimalist Go web framework
APIドキュメント生成
deepmap/oapi-codegen: Generate Go client and server boilerplate from OpenAPI 3 specifications
Go の Open API 3.0 のジェネレータ oapi-codegen を試してみた | フューチャー技術ブログ
11章 HTTPクライアント
特に気になったところなし
12章 ログとオブザーバビリティ
Official PCI Security Standards Council Site - Verify PCI Compliance, Download Data Security and Credit Card Security Standards
https://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/c301.html
rs/zerolog: Zero Allocation JSON Logger
OpenTelemetry
13章 テスト
Table Driven Test
Goのテーブル駆動テストをわかりやすく書きたい
標準のtestingパッケージで十分。あとはノウハウ。
$ go test -run Model
特定のワードが含まれるテストだけ実行
$ go test -short
時間のかかるテストをスキップしてくれる
stretchr/testify: A toolkit with common assertions and mocks that plays nicely with the standard library
google/go-cmp: Package for comparing Go values in tests
reflect.DeepEqualの強化版
Exampleへの追加
Goのtestingを理解する in 2018 - Examples編 #go - My External Storage
14章 クラウドとGo
15章 クラウドのストレージ
Go CDK
オブジェクトストレージ版、database/sqlのようなベンダによらず統一的に操作できるインターフェースで扱えるようにするライブラリ
16章エンタープライズなGoアプリケーションと並行処理
プログラミング言語Go完全入門のゴルーチンとチャネルの章を読めば良さそう
主に無限ループ起因のゴルーチンリークは気をつける
uber-go/goleak: Goroutine leak detectorみたいなライブラリもあった
リソース
Release History - The Go Programming Language
リリースノート
Effective Go - The Go Programming Language
Effective Go — プログラミング言語 Go ドキュメント v0.1 documentation
Goらしい書き方を学ぶためのドキュメント
Standard library - Go Packages
標準ライブラリ読むのが良い
プログラミング言語Go完全入門
メルカリの人のGolangの基本文法解説
Tutorial: Getting started with generics - The Go Programming Language
ジェネリクス
読書メモ golang