Upgrade to Pro — share decks privately, control downloads, hide ads and more …

事業成長を加速させるGoのコード品質改善の取り組み / Code quality improv...

TajimaTheMemer
September 20, 2023

事業成長を加速させるGoのコード品質改善の取り組み / Code quality improvement for Go language

2023/09/20の「ZOZO Tech Talk #8 - Go」にて発表した登壇資料です。
イベント詳細: https://zozotech-inc.connpass.com/event/293668/

TajimaTheMemer

September 20, 2023
Tweet

More Decks by TajimaTheMemer

Other Decks in Programming

Transcript

  1. 事業成長を加速させるGoの
 コード品質改善の取り組み
 ZOZO Tech Talk #8 - Go 
 


    株式会社ZOZO
 ブランドソリューション本部 バックエンド部 FAANSバックエンドブロック
 バックエンドエンジニア
 田島 太一 Copyright © ZOZO, Inc. 1
  2. © ZOZO, Inc. 株式会社ZOZO
 ブランドソリューション開発本部バックエンド部FAANSバックエン ドブロック
 田島太一
 2020年4月中途入社
 MLOpsチームでZOZOTOWN, WEARのML機能の開発・運用に従

    事後、2022年6月よりFAANSの開発チームでバックエンドシステ ムの開発・運用に従事
 最近はトマトにハマって毎日2玉食べている 🍅🍅
 
 

  3. © ZOZO, Inc. 3 ショップスタッフの販売サポートツール「FAANS」とは • WEAR, ZOZOTOWN, Yahoo!ショッピング, アパレルブランドの自社

    ECへのコーディネート投稿やその成 果の可視化など、ショップスタッフのオンライン接客を支援する業務支援ツール • ZOZOTOWN上でブランド実店舗の在庫取り置きを希望した際に、ショップスタッフが FAANS上での簡単 操作で取り置き対応を完結できるといった実店舗業務のサポートにも対応
  4. © ZOZO, Inc. 4 FAANSのバックエンドシステムは全てGoで書かれている • WebAPIサーバやバッチ処理も含め全て Goで実装している。 • WebAPIサーバはClean

    Architectureのポリシーに則った、ドメイン層をレイヤー間の依存関係における 最上位に置くレイヤードなアーキテクチャとなっている。
  5. © ZOZO, Inc. 5 FAANSはまだローンチまもない新規プロダクト • FAANSは正式ローンチからまだ 1年の新しいプロダクト • 0

    → 1の立ち上げフェーズから 1 → 100の成長フェーズに移行 • 新機能の開発や既存機能の改善に取り組む日々 • 一方で、技術的負債も溜まってきているため、負債を改善していく取り組みも行っている。
  6. © ZOZO, Inc. 7 機能開発の傍、様々な技術的負債の解消に取り組んでいます • ソフトウェアのドメインモデル • データベースのデータモデル •

    Web APIのスキーマ定義 • データベースや外部 APIといった外部コンポーネントとの連携処理 • レイヤー構造やパッケージ構成、それらの依存管理といったサーバサイドのアプリケーショ ンアーキテクチャ • コード品質 ← 今日はこのお話 • …
  7. © ZOZO, Inc. 10 コード品質とは • 可読性 • 保守性 •

    安全性 • 再利用性 • パフォーマンス • …
  8. © ZOZO, Inc. 13 Goのコード品質改善の取り組み 1. 徐々に厳しくするLinter設定 2. スタイルガイド「Google Go

    Style Guide」の導入 3. エラーハンドリングの改善 4. 凝集度を高める改善 5. ボーイスカウトルールによる既存コードの継続的改善 ※ 他にも色々やってますが時間の都合上割愛
  9. © ZOZO, Inc. 14 Goのコード品質改善の取り組み 1. 徐々に厳しくするLinter設定 2. スタイルガイド「Google Go

    Style Guide」の導入 3. エラーハンドリングの改善 4. 凝集度を高める改善 5. ボーイスカウトルールによる既存コードの継続的改善
  10. © ZOZO, Inc. 16 golangci-lint • Goの代表的なLinterツールの一つ • govetのようなgo標準のLinterやStaticcheckのようなサードバーティの Linterをまとめて実行できる

    runner • yamlファイルでどのLinterツールを使うか、使わないかといった細やかな設定が可能 • ホワイトリスト型、ブラックリスト型の設定どちらにも対応
  11. © ZOZO, Inc. 17 当初の弊チームのgolangci-lintの設定 • golangci-lintはプロダクト立ち上げ当初から CIに導入していた。 • yaml上では実行するLinterツールを何も指定していない、つまりデフォルトで有効化される

    Linterのみを実 行する設定となっていた。 • golangci-lintでデフォルトで有効化されている Linter一覧 ◦ errcheck ◦ gosimple ◦ govet ◦ ineffassign ◦ Staticcheck
  12. © ZOZO, Inc. 18 Linterの設定を徐々に厳しくしていく手順 • golangci-lintの設定ファイルにおいて disable-all: trueの上でenable配下にLinterを列挙していくホワイトリス ト型を採用した。

    • まずは、enable配下にデフォルトで有効化されていた Linterを明示的に指定して実質的に設定に差分がな い状態とした。 • その際、デフォルトで有効ではない、つまり未導入の golangci-lintのLinter一覧をコメントアウトする形で列挙 しておく。
  13. © ZOZO, Inc. 19 Linterの設定を徐々に厳しくしていく手順 • 続いて、有効化したい Linterをコメントアウトして1つずつPull Requestを分けて有効化していく。 •

    有効化したLinterにより変更していない既存コードの部分で CIがコケるようになると開発体験が下がるた め、導入時にプロジェクトコードの全範囲においてその Linterでエラーが出る箇所を修正する。腕力! 💪
  14. © ZOZO, Inc. 21 Goのコード品質改善の取り組み 1. 徐々に厳しくするLinter設定 2. スタイルガイド「Google Go

    Style Guide」の導入 3. エラーハンドリングの改善 4. 凝集度を高める改善 5. ボーイスカウトルールによる既存コードの継続的改善
  15. © ZOZO, Inc. 22 Goのコード品質改善の取り組み 1. 徐々に厳しくするLinter設定 2. スタイルガイド「Google Go

    Style Guide」の導入 3. エラーハンドリングの改善 4. 凝集度を高める改善 5. ボーイスカウトルールによる既存コードの継続的改善
  16. © ZOZO, Inc. 23 スタイルガイドの導入 • コーディング規約はドキュメントにまとめ、チームで認識合わせできるようにしておくことはチーム開発の 基本 ◦ コミュニケーションコストの削減

    ◦ 新メンバーのオンボーディングの円滑化 • 0から自分たちでコーディング規約を定めていくのは開発組織の規模に見合わない労力がかかり非現実 的なので、一般に公開されていて信頼できるスタイルガイドにできる限り乗っかる戦略 • 導入したGoのスタイルガイド ◦ Effective Go (Go言語公式) ◦ Go Code Review Comments (Go言語公式) ◦ Google Go Style Guide (Google社製)
  17. © ZOZO, Inc. 24 Google Go Style Guide • 2022年11月に公開されたGoogle社によるGoのスタイルガイド。

    • Effective Goを前提としているのでスタイルガイド間のバッティングもない。 • 読みやすく慣用的な Goのコーディングスタイルを示している。 • 可読性が高いコードを書く上での迷いを最小化し、初心者にありがちなミスを避けられるようにすることが 目的。 • https://google.github.io/styleguide/go/
  18. © ZOZO, Inc. 25 Google Go Style Guideは3章構成 規範的(canonical): 規範的かつ永続的なルール

    標準的(normative): 一貫性を持たせるためのルール 章 主な対象者 規範的 標準的 Style Guide EveryOne Yes Yes Style Decisions Readability Mentors Yes No Best Practices Anyone Interested No No • 3章全てをチームが従うべきスタイルガイドとして一先ず導入した。
  19. © ZOZO, Inc. 26 「Style Guide」の5原則 • 個別ケースだけでなく全体に通底する考え方も説明されていて Good! •

    5原則 (上から重要度順) ◦ Clarity (明白さ) : 目的と根拠が読み手にとって明白か ◦ Simplicity (シンプル) : できる限りシンプルなやり方で目的を達成しているか ◦ Concision (簡潔さ): 高いS/N比を有しているか ◦ Maintainability (保守性): 保守は容易か ◦ Consistency: 広範なGoogleのコードベースと一貫しているか ↓ 5原則という優先度付きの判断基準 をチームの共通認識として持つことで、スタイルガイドに具体的な書き方が 記載されていないようなケースにおいても実装時やレビュー時に判断しやすくなり、チームとして合意形成もし やすくなった。
  20. © ZOZO, Inc. 27 Goのコード品質改善の取り組み 1. 徐々に厳しくするLinter設定 2. スタイルガイド「Google Go

    Style Guide」の導入 3. エラーハンドリングの改善 4. 凝集度を高める改善 5. ボーイスカウトルールによる既存コードの継続的改善
  21. © ZOZO, Inc. 28 Goのコード品質改善の取り組み 1. 徐々に厳しくするLinter設定 2. スタイルガイド「Google Go

    Style Guide」の導入 3. エラーハンドリングの改善 4. 凝集度を高める改善 5. ボーイスカウトルールによる既存コードの継続的改善
  22. © ZOZO, Inc. 29 Goにおけるエラーハンドリング • 業務アプリケーションでは関数 /メソッドの呼び先で発生したエラーを fmt.Errorf() (Go

    1.13〜)を使って呼 び先の関数/メソッドの名前や引数の値といった情報を付与する形でエラーをラップしてから上流に伝播さ せるのが一般的。 ◦ そのまま返してしまうとエラーログからエラーの発生箇所が分かりにくくなりトラブルシューティング の難易度が上がる。 ◦ なお、サードパーティライブラリを使ってスタックトレースを出す方法もある。 ※標準パッケージにス タックトレースの機構は現状存在しない。
  23. © ZOZO, Inc. 31 After: 関数/メソッドの”呼び先側”でエラーをラップするようにした • エラーが発生した呼び先の関数 /メソッドの情報を呼び元側ではなく呼び先側で fmt.Errorf()を使ってエラー

    をラップして上流に伝播させるようにした。 • errwrapperというpackageを用意してエラーのラップ処理が関数 /メソッドの内部実装の文頭 1行で済むよ うに簡易化した。 “err”だとエラーハンドリングが抜けている箇所を Linterで 検知できなくなるため ”err”以外の命名とする
  24. © ZOZO, Inc. 34 errwrapperパッケージのPros/Cons • Pros ◦ 呼び先側の関数/メソッドの先頭で一度だけエラーラッピングのロジックを記述するだけで、その関 数/メソッド内で発生するエラーは自動的にラッピングされるのでラク。

    ◦ 呼び先の関数/メソッドの情報以外の情報を付与したいケースを除けば、呼び元ではエラーをラップ する必要がなくなったのでコードがシンプルになった。 • Cons ◦ 標準的なやり方ではないため外部ライブラリの関数 /メソッドでは呼び元でエラーを適切にラップす る必要があり、その分の認知負荷がかかる。
  25. © ZOZO, Inc. 35 Goのコード品質改善の取り組み 1. 徐々に厳しくするLinter設定 2. スタイルガイド「Google Go

    Style Guide」の導入 3. エラーハンドリングの改善 4. 凝集度を高める改善 5. ボーイスカウトルールによる既存コードの継続的改善
  26. © ZOZO, Inc. 36 Goのコード品質改善の取り組み 1. 徐々に厳しくするLinter設定 2. スタイルガイド「Google Go

    Style Guide」の導入 3. エラーハンドリングの改善 4. 凝集度を高める改善 5. ボーイスカウトルールによる既存コードの継続的改善
  27. © ZOZO, Inc. 37 凝集度と結合度 • 保守性と拡張性が高いソフトウェアには高い凝集度と低い結合度が重要。 ◦ 凝集度 ▪

    モジュール内の機能がどれだけ密接に関連しているかを示す尺度 ◦ 結合度 ▪ 二つのモジュール間の依存関係の強さを示す尺度
  28. © ZOZO, Inc. 39 Before: ファクトリ関数を使わずにDomain層のEntityの構造体を初期化 していた • アプリケーション固有のビジネスロジックを実装する UseCase層でDomain層に実装されたEntityを表す構

    造体をファクトリ関数を使わずに初期化していた。 ◦ 属性値のバリデーションチェックなどのドメイン層の知識が Domain層ではなくUseCase層で実装さ れて凝集度が下がってしまう。 → ドメインモデル貧血症 ◦ 属性値の指定忘れやバリデーションチェックのし忘れによる中途半端に初期化された状態の構造 体の存在を許容してしまう恐れ。 → バグの温床
  29. © ZOZO, Inc. 42 After: Entityを表す構造体のファクトリ関数を定義して凝集度を高める • ファクトリ関数内で属性値のバリデーションや変換処理が行われるため、不完全な状態の構造体が生成さ れることがなくなった。 •

    ドメイン固有の知識が UseCase層に散らからずにDomain層に集約されたことで凝集度が高まった。 • 例えば、複数のEntity間で共通の属性などは Defined Typeで適切に「値オブジェクト」化し、その属性のバ リデーション処理を値オブジェクトの生成処理内で行うことで凝集度を高めることも有効。
  30. © ZOZO, Inc. 43 ② Defined Typeによるファーストクラスコレクション • ファーストクラスコレクション ◦

    配列などのコレクションをラップする専用のクラスを持ち、そのコレクションのビジネスルールや振る 舞いをそのクラス内に実装するオブジェクト指向プログラミングの実装パターン • Defined Type ◦ GoではDefined Typeを使用することで独自の新しい型を定義可能 ◦ Slice型に対してDefined Typeで新たな型を定義しその型でメソッドを定義することでファーストクラス コレクションが容易に実現
  31. © ZOZO, Inc. 48 Goのコード品質改善の取り組み 1. 徐々に厳しくするLinter設定 2. スタイルガイド「Google Go

    Style Guide」の導入 3. エラーハンドリングの改善 4. 凝集度を高める改善 5. ボーイスカウトルールによる既存コードの継続的改善
  32. © ZOZO, Inc. 49 Goのコード品質改善の取り組み 1. 徐々に厳しくするLinter設定 2. スタイルガイド「Google Go

    Style Guide」の導入 3. エラーハンドリングの改善 4. 凝集度を高める改善 5. ボーイスカウトルールによる既存コードの継続的改善
  33. © ZOZO, Inc. 53 ボーイスカウトルールは実効的なアプローチ • 新規事業で機能開発に追われる中で継続的に既存コードを改善していく上でボーイスカウトルールは実 効的なアプローチ。 • ただし、限界があるのでボーイスカウトルールだけに頼らない。

    ◦ セキュリティやシステム障害のリスク、後手になると負債回収コストが増大する類の負債は、独立し たタスクとして切り出し優先度を上げてしっかり工数を割いて取り組むことも大事。工数を確保する 交渉力もエンジニアに求められるスキル! 💪 ◦ 定期的にチーム全員が改善タスクに専念する日を作るのも有効。