株式会社Stackの2024年開発体制の振り返り

sonatard
·
公開:2025/12/31

減らせたもの

Infra層の削減

Infra層でSpannerに関してはQueryのみを配置して、lib/spannerc packageに配置した1つの関数にQueryを渡すことで実行可能にした。これによりInfra層にQueryごとの関数を用意することがなくなった。Go 1.18からType Parametersが導入されたことで、コード生成ツールを利用せずにQueryに依存しない汎用的な実装をできるようになった。

OpenCensus+SentryからOpenTelemetry+Error Reportingに移行

GoのSDKのOpen Telemetry対応が進んだため移行を進めた。また活用していない割に高い料金を払っていたSentryをError Reportingに移行した。Error ReportingはGoのpanicと同等のフォーマットの出力しかスタックトレースとして認識しないため、エラーラップpackageを独自で実装して出力の形式をpanicに合わせることで対応した。

構造化ログ出力をslogに移行

独自実装をslogに移行した。Goではslog+OpenTelemetryが一般的になった。

ログ分析をLog Analyticsへ移行

今まではAPIのログをCloud Loggingへの出力、sinkの設定、BigQueryへのインポートが必要だったが、Log Analytics向けのログバケットを作るだけでBig Query以上のログ分析が可能となった。

Cloud Run Jobsへ移行

巨大なバッチ処理はDataflowやCloud Tasksで実施していたがCloud Run Jobsへ移行した。Usecaseレイヤーにバッチ処理を書いてmain関数から実行するだけバッチ処理を実装できるようになった。

ページネーション実装

Go 1.23でIteratorが導入されたことでページネーションの実装を減らすことが可能になった。バッチ処理でIteratorを活用することでUsecaseレイヤーでcursorの更新が不要になった。

HTTP周りの改善

他のレイヤーで行っていた処理をHTTP Transportに寄せた。HTTPログ出力、リトライ、otelなど。Graceful Shutdownに真剣に向き合った。

Goの外部依存package

直接依存しているpackageはSDK以外だと13になった。このうち3つはgqlgen、gqlgenc、gqlparserである。

Goのsort packageからslices packageのSortへ移行

Type Parmatersに対応したslices packageへ移行。複数フィールドを対象にしたソートをcmp.Orで実装可能になった。slices package+cmp.Compare+cmp.Orが今後のスタンダードになりそう。

Goのmath/rand packageからmath/rand/v2 packageへ移行

Spannerのスケールアウト制御

SpannerがAuto Scaleに対応した。

Spannerのバックアップ

Spannerのバックアップをするためには、様々なツールを組み合わせて実現する必要があったがスケジュールドバックアップが提供されたので採用した。

SpannerからBigQueryへのエクスポート

BigQueryへのエクスポートは、Cloud Storageに配置してからBigQueryのインポートが必要だったが、External datasetsによって不要になった。

Dockerfileの削除

コンテナイメージは以前からkoを利用しているためDockerfileは利用していなかったがCIイメージでDockerfileを使っていた。現在はGitHub Actionsのアクションを利用する方針に切り替えた。

directiveの指定忘れのツールを削減

今まではdirectiveの用途ごとにLintツールを作成していたが、新規のdirectiveが増えた際に対応コストが高かった。そこでyamlに設定を書くことでdirectiveの指定忘れを検出できる汎用的ツールを実装して移行した。https://github.com/gqlgo/directive

Androidの外部依存ライブラリ

外部ライブラリは、Barcodeリーダー系で3つ、accompanistのpermission、accompanist由来のplaceholder、Ktor、Coil、Apollo、compose-richtext、Timber、libphonenumber、com.ionspin.kotlin:bignum、Google提供のライブラリに抑えられている。

KMPに対応したKtor 3がリリースされた。Ktor 3に対応したApollo 4.10、Coil 3がリリースされた。okHTTPはKtorのエンジンとしてだけ利用して、okHTTPへの依存は減らしてKtor 3だけに依存した。Ktor 2への依存もない。

KtorのDate型とGraphQLのDate型のマッピングをApolloへPull Requestが取り込まれたことで、社内コードから削除。

Navigationをタイプセーフにするためのライブラリはkiwicom/navigation-compose-typedを使っていたが、公式ライブラリへ移行。新たに公式ライブラリがkiwicom/navigation-compose-typedをベースに作られたため、移行コストを抑えられた。公式と同じ選択ができていた。

KotlinにUUIDが入ったので移行。

Apolloのページネーションの状態管理をJetpack ComposeのStateからApolloのページネーションヘルパーに移行。Apollo Kotlinでは将来的に1行でページネーションを実装可能になる。

Javaへの依存を減らしてKotlinに依存を増やした。

変更したもの

Cloud Tasksの非同期処理の関数ジャンプ

Cloud Tasksを利用しているとAPIを叩くことになるため、コードジャンプが難しい。Cloud Tasksのタスクを追加する際にUsecase関数を指定することで関数ジャンプ可能とした。

冪等なユニークなIDを生成するためのUUIDv5

IDにはUUIDv4を利用することが一般的だが、IDを冪等に生成してUpsertすることで冪等な処理を実装しやすくすることができる。これを実現するために一部のテーブルではUUIDv5を採用した。UUIDv5では指定した文字列に対して一意のIDを生成することができる。複合キーを使うこともできるが、今はそうしていない。なんとなく制約が強すぎる気がしている。