Slide 1

Slide 1 text

開発生産性・開発体験改善の Rules of Thumb i10416@某

Slide 2

Slide 2 text

WHO AM I GitHub: https://github.com/i10416 SpeakersDeck: https://speakerdeck.com/i10416

Slide 3

Slide 3 text

WHO AM I やっていた・いること @WORK アプリ開発 (Android) バックエンド開発 (Go・ Rust) terraform 職人 データパイプライン (Apache Spark) 負債の連帯保証人 やっていること @その他 OSS Contribution(有名なもの だと Scala コンパイラ )

Slide 4

Slide 4 text

おしながき 開発生産性・開発体験改善のゴールと取り組み 開発生産性・開発体験改善の Rules of Thumb 開発生産性・開発体験改善の方法論

Slide 5

Slide 5 text

開発生産性・開発体験の改善のゴール

Slide 6

Slide 6 text

開発生産性・開発体験の改善のゴール リリース リリースサイクルの短縮 リリース後のエラーから修正までの期間の短縮 ↑ のゴールを他チーム・プロダクトに負担を押し付けずに実現する 人材獲得、既存メンバーのリテンション オンボーディング・引き継ぎの負担の軽減

Slide 7

Slide 7 text

開発生産性・開発体験の改善のゴール リリースサイクルを短くし、エラーの原因特定・復旧を早めることで、ユーザーに素早く、 継続的に価値を届けることができます .

Slide 8

Slide 8 text

開発生産性・開発体験の改善のゴール 日々のトイルが少なく、既存技術の表面的な部分以上の理解を深めたり、可用性やパフォー マンスなどの非機能要件に対応したり、新技術や尖った技術に取り組む余地がある組織は、 キャリア形成の面でソフトウェアエンジニア・データサイエンティスト人材獲得のためのア ピールやリテンションにもつながります .

Slide 9

Slide 9 text

開発生産性・開発体験の改善のゴール オンボーディング・引き継ぎの負担の軽減することで人の出入りを促進できます . これは人材 獲得、メンバーのリテンションと少し矛盾するように聞こえますが、組織の新陳代謝や属人 性の抑制の点で効果があります . 一人二人が入れ替わっても安定しているチームはメンバーにとっても休暇を取りやすく長期 的に見て働きやすい環境だと考えています .

Slide 10

Slide 10 text

開発生産性・開発体験の Rules of Thumb

Slide 11

Slide 11 text

開発生産性・開発体験改善の Rules of Thumb ボトルネックから改善する . コードを書く量を減らす . 読む負担を下げる . より (依存関係の )上流で問題を解決する &上流の都合を下流に漏らさない . 例 : インフラ・ミドルウェア > ライブラリ > アプリケーション 例 : (Web標準などより広義の )仕様 > (ベンダー SDK やミドルウェアの実装 ) > (社内 の )仕様 > 社内の実装 例 : コンパイル時 > テスト時 > 開発環境実行時 > 本番環境実行時

Slide 12

Slide 12 text

開発生産性・開発体験改善の Rules of Thumb ボトルネックから改善する ビルドや CI のチューニング、 otel の自動計測 (auto instrumentation)、テストカバレッジの 向上、エラー監視用 SaaS の利用など、リリース頻度、パフォーマンス計測やエラーの原因特 定・復旧時間の数字に直接効く、かつアプリケーションの変更が最小になるものから手をつ けるのが吉 . ボトルネックを特定できるようにログ、トレース、 (マイクロ )ベンチマークなど、計測の習慣 を !

Slide 13

Slide 13 text

ただし、こういった低難易度・高リターンのものは少なく、残る高リターンのものは難易 度・複雑度が高いため過小投資になり、往々にして静的解析の導入やテンプレートの作成な ど、低難易度・低リターンな作業に着地しがちではある .

Slide 14

Slide 14 text

開発生産性・開発体験改善の Rules of Thumb コードを書く量を減らす、読む負担を下げる コードを読む時間と書く時間の割合は 10:1 と言われています 参考 : 開発者は開発に使う労力の 58% をコードの理解に費やしているとも言われています . Software professionals spent 58% of their development efforts on code comprehension. https://ieeexplore.ieee.org/document/7997917

Slide 15

Slide 15 text

開発生産性・開発体験改善の Rules of Thumb コードを書く量を減らす、読む負担を下げるモチベーション 業務では自分が書いたコードを読むよりも他人が書いたコードを読む必要があります . また、日々の業務でもコードを書くだけでなく誰かのコードをレビューしたりバグを修正し たりする必要があります .

Slide 16

Slide 16 text

開発生産性・開発体験改善の Rules of Thumb コードを書く量を減らす、読む負担を下げるモチベーション 担当者が退職したり、担当者が他のプロジェクトに移った (&運が悪いと放置された )ものを引 き継ぐこともあります . 動いている (≒お金を産んでいる )アプリケーションを触る際に既存の資産を有効活用するには 基本的に誰かの書いたコードを読まざるを得ません .

Slide 17

Slide 17 text

開発生産性・開発体験改善の Rules of Thumb より上流で問題を解決する &上流の都合を下流に漏らさない より上流で問題を解決する方がより多くのユーザーがその恩恵を受けられ、重複した作業が 減ります . なお、上流で行うだけでは不十分で、上流の (実装上の )都合を下流に漏らさないこ とが、ユーザーに本質的でない調査や変更を要求しなくていい ため望ましいです . 参考 : The golden Rules of software quality Open–closed principle

Slide 18

Slide 18 text

例えば、社内独自ツールを作るよりもデファクトのツールや、コンパイラ組み込みの機能に 追加する方が (特殊なセットアップなしに )より多くのユーザーが恩恵を受けられます . 最新のリリースではその機能をデフォルトで有効化せずオプトインのフラグを用意すればユ ーザーに機能の有効化・無効化の選択肢と導入の猶予を与えられます .

Slide 19

Slide 19 text

開発生産性・開発体験改善の Rules of Thumb 上流の都合を下流に漏らさないモチベーション あるサービス Aが上流の実装を漏らしていると、そのサービスを利用するクライアントは、サ ービス A が公開する API が、そのサービス Aが定める (依存してもいい安全な )仕様なのか、上 流のサービスの (安全ではない )実装なのか調査する必要があります .

Slide 20

Slide 20 text

開発生産性・開発体験改善の Rules of Thumb 上流の都合を下流に漏らさないモチベーション パブリックなインターフェースを公開する場合、公開する側は互換性維持の何らかのポリシ ー (※ )を定めるべきですが、あるサービス A が上流の実装を漏らしていると、サービス Aはコ ントロールできない上流の実装に関する互換性維持のポリシーという、できない約束をクラ イアントに対してすることになります . ポリシーを定めない (約束をしない )場合はサービス A以下のクライアント群がそれぞれ上流の 実装を吸収する処理を用意しなければなりません . ※ 例 : Go 1 and the Future of Go Programs Long-term compatibility plans for Scala 3

Slide 21

Slide 21 text

上流の実装の漏洩の例 見せられないよ

Slide 22

Slide 22 text

以下のような上流の事情を下流に漏らすべきではない . 見せられないよ 見せられないよ 見せられないよ もう少し一般的にいうと、クライアントが本来知らなくていいことを知らないでいいように するべき .

Slide 23

Slide 23 text

???「なるほど ? 完璧な作戦っスねーーっ」 (以下略 ) 念の為補足しておくと、最初から完璧な仕様を決めればいいと言いたいわけではない . 最初から完璧な仕様を決めることはできないので初期に仕様が緩くなる (実装詳細が漏れる )の は仕方がないが、仕様を伝播させる仕組みを用意して段階的に仕様を洗い出していけるよう にする必要がある . キーワード : ドメインモデルの蒸留

Slide 24

Slide 24 text

開発生産性・開発体験改善の方法論 (と障壁 ) ここからは、アプリケーションレベルでとれる手段と、実際にそれらを既存のプロダクトに 導入する際の障壁について話します .

Slide 25

Slide 25 text

コード生成

Slide 26

Slide 26 text

コード生成 : Motivation コード量の抑制 RPC 境界のデータの詰め替え・エラー処理の削減 API クライアントや SDK の自動生成 冗長なコードの削減

Slide 27

Slide 27 text

参考 : コード生成の例 IDL: protobuf Google Cloud SDK IDL: GraphQL GitHub API IDL: smithy AWS SDK IDL: thrift Open API Slack SDK おまけ : (コンパイル時 )マクロ Rust macro_rules! Template Haskell

Slide 28

Slide 28 text

コード生成 : Motivation コード生成器周辺のツールチェーンの活用 ドキュメンテーション : e.g. protoc-gen-doc バリデーション : e.g. smithy validator 互換性チェック : e.g. buf build プラグイン

Slide 29

Slide 29 text

コード生成 : Motivation 仕様 First アプローチの強制 システム内・システム間のやりとりのあるべき状態が定義される 例えば、 protobuf を管理する中央レポジトリがあれば、それを spec の協議や 意思決定の場として利用できる サービス間の責任の範囲を決められる spec に書いたことは保証しなければならないが、書いていないものに依存し た場合は依存した側の責務と言い切れる

Slide 30

Slide 30 text

余談 : Open API(Swagger) は便利だが不十分 Open API(Swagger) は人間が見るドキュメントを生成するには便利だが機械的にコード生成 するには不十分 問題点 コードジェネレータの仕様の違いによってシリアライズ・デシリアライズの実装が異な るので汎用的なコード生成器として利用できない 例 : oneof や enum フィールドの ser/de の (明確な )仕様はない 独自型・モジュール分割・参照の解決など、コードを組織化する基本的な機能が不十分 URI、 UUID や Duration 型など、中レベルの型へのマッピングが存在しない

Slide 31

Slide 31 text

ZOZO Tech Blog: OpenAPI Generatorに適した OpenAPIの書き方 のように、汎用的なコード生成を優先して詳細な仕様書 (型定義 )としてのスキーマを諦める方 針もある . その場合、下流のサービスは、スキーマのコメントと上流の実装を信じるか、バリデーショ ンを再度行う手間が生じる .

Slide 32

Slide 32 text

余談 : Open API(Swagger) Alternatives Open API よりも新しい IDL(GraphQL スキーマ、 Protobuf など ) と TypeScript・ Rust・ Kotlin・ Scala・ Swift などの比較的モダンな静的型付け言語の組み合わせならば、詳細な仕様 書 (型定義 )とコード生成を両立できる可能性が高い .

Slide 33

Slide 33 text

モジュール分割(あるいはアーキテクチャ)

Slide 34

Slide 34 text

モジュール分割 クリーンアーキテクチャ、ヘキサゴナルアーキテクチャなど、さまざまなアーキテクチャが あるが、肝心なのは I/O(特に DB、クラウドインフラやコントロール下にない API) や RPC フ レームワークとそれ以外がうまく分離されていること 以下は一例 . {project root} └── modules ├── prelude: 特定のアプリケーションに依存しない型、機能や拡張. ├── core: ビジネスロジック. prelude に依存. | ├── feature-a | ├── ... | └── feature-z ├── cli(optional): core に依存. └── http: api と core に依存. web RPC フレームワークへの依存を含む.

Slide 35

Slide 35 text

モジュール分割 : 「うまく」分離されている とは (ソースコードを入念に読まなくても )依存の方向や依存の内容が機械的に保証できる 例 : core モジュールが web フレームワークに依存していない 例 : コア部分を再利用して cli(など別の呼び出し方 ) を提供できる Convention over Configuration(CoC)を強制できる 内部の変更が外部に不必要に伝播しない 例 : prelude の変更は core には伝播するが cli や http には伝播しない 例 : prelude、 core、 http モジュールはそれぞれ別のライフサイクルを持てる コードベースが特定のフレームワークの寿命に影響されにくい

Slide 36

Slide 36 text

モジュール分割 : App と Lib の分割 コアロジックと I/O や RPC フレームワークが分離されていると、モジュールをアプリケーシ ョンとライブラリに区別できる . ライブラリを GitHub Packages、 Google Artifact Registry、 AWS Code Artifact などで配布 すれば、効率よくバージョン管理や変更の伝播ができる . ※ ツールチェーンによっては GitHub Repository をそのまま依存のソースにできる . {project root} └── modules ├── lib:prelude: 特定のアプリケーションに依存しない型、機能や拡張. ├── lib:core: ビジネスロジック | ├── feature-a | ├── ... | └── feature-z ├── app:cli: core に依存. └── app:http: core に依存. web RPC フレームワークへの依存を含む

Slide 37

Slide 37 text

余談 : モジュール分割 : App と Lib の分割 ライブラリベースのアプローチは万能薬ではなく、ライブラリを中心とした設計にする場合 には以下の点を検討する必要がある . Google Artifact Registry、 AWS Code Artifact のようなセキュアで安定したライブラリ 配布基盤の用意 Self-Host Renovate のような Push ベースの依存更新自動化の用意 パッケージのバージョン管理・互換性チェックなどの自動化

Slide 38

Slide 38 text

モジュール分割の障壁 高度なモジュール分割を (比較的容易に )可能にするプログラミング言語とそのツールチェーン は限られている . 基本的に、しっかりとしたビルドツールと、クロスコンパイル、ビルド flavor の切り替えや ビルド時間の改善などの強い要請があるエコシステム 例 : Kotlin + Gradle Swift + SPM Rust + Cargo Scala + sbt Node.js + {Nx|Turborepo|etc.} ※ 比較的容易に : サンプル・情報が豊富 &設定項目が少ない・ハックが不要

Slide 39

Slide 39 text

再掲 : 開発生産性・開発体験改善の Rules of Thumb ボトルネックから改善する . コードを書く量を減らす . 読む負担を下げる . より (依存関係の )上流で問題を解決する &上流の都合を下流に漏らさない .