Slide 1

Slide 1 text

CLIツール開発をProtocol Buffers スキーマで駆動する GMOペパボ 古殿直也 BuriKaigi 2025-02-01

Slide 2

Slide 2 text

自己紹介 古殿直也 (Furudono Naoya) @furudono2 (Twitter) お酒と旅行が好き 富山は2回目。前回は剱岳を登ろうとしたが天気がヤバくて諦めた GMOペパボ ホスティング事業部 Webアプリケーションエンジニア 2023年までは学生で、プログラミング言語意味論の勉強をしていた 2

Slide 3

Slide 3 text

目次 良いプログラミング・楽しいプログラミングとはどんなものだろうか How to Design Programsで提案されているプログラムの「デザインレシピ」 スキーマ駆動開発によってデザインレシピをAPI開発に適用する CLIツール開発にもスキーマ駆動開発を適用するための取り組み 3

Slide 4

Slide 4 text

良いプログラミング・楽しいプログラミングとは どんなものだろうか 4

Slide 5

Slide 5 text

プログラミング楽しいですよね? 最初から楽しかったですか? 僕はプログラミングに出会って3年くらいは楽しくなかったです 上手にできるようになってから楽しいです 上手なプログラミングってなんだよ!! 5

Slide 6

Slide 6 text

How to Design Programs How to Design Programs (HtDP) はプログラミングの教科書 "focuses on habits of good programming" プログラミング言語意味論の大家、Matthias Felleisen が著者 「保存と進行」を証明することで型システムの健全性を示すことを提案した 未邦訳だが、原著がWebで無料で読める https://htdp.org/2024-11-6/Book/index.html https://htdp.org 6

Slide 7

Slide 7 text

「デザインレシピ」にのっとることで良いプログラミングができる システマティックな思考・計画・理解を全てのステップで行うアプローチが「良 いプログラミング」 システマティックなプログラムデザインはデザインレシピと反復的な改善の組み 合わせ 7

Slide 8

Slide 8 text

How to Design Programsで提案されている「デザインレシピ」 8

Slide 9

Slide 9 text

デザインレシピ 関数の設計を扱っている 関数は抽象化の基本単位だから プログラムの設計・実装をステップ分けして、以下の順で成果物を作る 1. From Problem Analysis to Data Definitions 2. Signature, Purpose Statement, Header 3. Functional Examples 4. Function Template 5. Function Definition 6. Testing https://htdp.org/2024-11-6/Book/part_one.html#(part._sec~3adesign-func) 9

Slide 10

Slide 10 text

例とともに、デザインレシピの各ステップを紹介する 全てを紹介するのではなく、今回の発表のために適切な部分をピックアップして紹介 します 詳細はぜひ原著を読んでください https://htdp.org/2024-11-6/Book/part_one.html#(part._sec~3adesign-func) 10

Slide 11

Slide 11 text

1. 問題を分析してデータ定義をする ドメインモデリング・データモデリングをするということ 今回の発表ではモデリングの詳細は省略 データ定義の簡単な例: ; センチメートルを表すためにnumber型を用いる ; 気温はnumber型で表す。 ; 摂氏で解釈する。 ; 値は-274以上でなければならない。 11

Slide 12

Slide 12 text

2. 関数のシグネチャ、目的文、関数のスタブを書く // number string Image -> Image // sをimgの上からyピクセル、 // 左から10ピクセルの位置に付け加える function addImage(y, s, img) { return emptyScene(100, 100); } 12

Slide 13

Slide 13 text

3. シグネチャや目的文を例で解説する // number -> number // 一辺の長さがlenである正方形の面積を計算する // given: 2, expect 4 // given 7, expect: 49 function areaOfSquare(len) { return 0; } 13

Slide 14

Slide 14 text

4. 関数のテンプレートを書く 省略 14

Slide 15

Slide 15 text

5. 関数を実装する // number -> number // computes the area of a square with side len // given: 2, expect: 4 // given: 7, expect: 49 function areaOfSquare(len) { return len * len; } 15

Slide 16

Slide 16 text

6. テストする // number -> number // computes the area of a square with side len // given: 2, expect: 4 // given: 7, expect: 49 function areaOfSquare(len) { return len * len; } assert(areaOfSquare(2) == 4); assert(areaOfSquare(7) == 49); 16

Slide 17

Slide 17 text

デザインレシピざっくりまとめ モデリングをする 関数のシグネチャと目的文を定義する 関数を説明するような使用例を挙げる 関数を実装する テストする 17

Slide 18

Slide 18 text

目次 良いプログラミング・楽しいプログラミングとはどんなものだろうか How to Design Programsで提案されているプログラムの「デザインレシピ」 スキーマ駆動開発によるデザインレシピのAPI開発への適用 CLI開発にもスキーマ駆動開発を適用するための取り組み 18

Slide 19

Slide 19 text

Web API開発でもデザインレシピを実践したい 別に新しいことではない。やってる人はやっている。 そう、スキーマ駆動開発なら 19

Slide 20

Slide 20 text

スキーマ駆動開発ではどのようにプログラミングを進めるか 1. データモデリング・ドメインモデリングをする 2. スキーマを定義する 仕様も書く。エンドポイントが扱うリクエストやレスポンス、実行の結果起 きることを含める 3. エンドポイントのユースケースも書く 4. エンドポイントの実装をする 5. ユースケースを実際に試すテストを書く 大体デザインレシピでやるのと同じ進め方 20

Slide 21

Slide 21 text

スキーマ駆動開発とデザインレシピのアイデアは同じだが、粒度が異なる 設計・開発の粒度が異なる デザインレシピは関数の設計・開発を扱う スキーマ駆動開発はAPIの設計・開発を扱う 一方で、APIのことを大きな関数だと思えば、考え方は同じ スキーマ駆動開発では、関数を実装する以上のことが必要。デザインレシピの実践に 集中するために、仕組みで補完したい 21

Slide 22

Slide 22 text

粒度が異なることに起因する、スキーマ駆動開発の障壁 A. どのようにスキーマ(シグネチャ)定義すれば良いかわからない 関数のシグネチャの書き方はプログラミング言語が決めてくれていた スキーマ駆動開発では、データの定義やパラメータ、目的文をどこにどのよ うに書けば良いだろうか B. スキーマ(シグネチャ)定義から実装に移るまでにギャップがある デザインレシピでは、関数シグネチャを元にその関数を実装していた。何も 難しくない スキーマ駆動開発では、スキーマを元にAPIを実装する。 ルーティングとかデータのデシアライズ処理とかはどうなる? C. どのようにテストを書くと良いかわからない デザインレシピの文脈では、関数をテストするだけだった もちろんスキーマ駆動開発でも必要。しかしAPIサーバのテストは単体テスト ほど書き方が自明ではない 22

Slide 23

Slide 23 text

ツールを使えばいい A. どのようにスキーマ(シグネチャ)定義すれば良いかわからない → Protocol Buffersで書く スキーマ駆動開発では、データの定義やパラメータ、目的文をどこにどのよ うに書けば良いだろうか B. スキーマ(シグネチャ)定義から実装に移るまでにギャップがある → protocやConnectでコード生成したり、ライブラリを使う スキーマ駆動開発では、スキーマを元にエンドポイントを実装する。 ルーティングとか型検査とかはどうなる? C. どのようにテストを書くと良いかわからない → runnを使ってシナリオテストする もちろんスキーマ駆動開発でも必要。しかしAPIサーバのテストは単体テスト ほど書き方が自明ではない 23

Slide 24

Slide 24 text

それぞれ順を追って詳細を見ていく 今回選んだツールが唯一の正解ではない スキーマ定義だけでいろんなツールがある。好きなものを使えばいい OpenAPI Protocol Buffers GraphQL 今回はProtocol Buffersを使うことに決める。 24

Slide 25

Slide 25 text

Protocol Buffersの概要 Google が開発したバイナリ形式のデータ構造のためのインターフェース定義言語 データ構造と関数シグネチャ、その集まりとしての「サービス」を定義できる それぞれのデータのエンコード・デコード処理を自動生成できる https://protobuf.dev/ 25

Slide 26

Slide 26 text

A. スキーマ定義は、Protocol Buffersを用いて書けば良い message Square { int32 len = 1; } service Calculator { // 一片の長さがlenである正方形の面積を計算する // given: len = 2, expect: 4 // given: len = 7, expect: 49 rpc AreaOfSquare(Square) returns (int32); } デザインレシピでやったのと同様 // number -> number // 一辺の長さがlenである正方形の面積を計算する // given: 2, expect 4 // given 7, expect: 49 function areaOfSquare(len) { return 0; } 26

Slide 27

Slide 27 text

B. スキーマ(シグネチャ)定義から実装に移るまでのギャップは コード生成やライブラリで解決する protoc (Protocol Buffersコンパイラ)を用いて、コード生成すれば良い スキーマに対応するGoやJavaなどのインターフェースを生成できる 開発者はそのインターフェースを満たす実装をすれば良い、つまり関数を実装 すれば済む 27

Slide 28

Slide 28 text

Connectを用いたAPIサーバのアーキテクチャ スキーマの対応するインターフェースを満たした実装から Webサーバを立てる機能をConnectがライブラリとして提供する https://connectrpc.com/ 28

Slide 29

Slide 29 text

C. Web APIのユースケースを説明するようなテストはどのようなものだろうか AAAパターンで考えてみる Arrange: APIサーバの状態を整える ログインする クレジットカードをあらかじめ登録する Act: 実装したAPIにリクエストを送る 決済APIを実行する Assert: レスポンスを検証したり、意図した状態変化が起きたことを確かめる 200レスポンスが戻る 決済履歴に新しい決済が追加されていることを確かめる 29

Slide 30

Slide 30 text

シナリオテストがWeb APIのユースケースを説明するようなテストである 「シナリオを用いた効果的なピンポイントテスト」は、ユーザ視点のブラックボ ックステスト技法の1つです。 1. 「夜半(When) 」に「A さん(Who) 」が「自宅(Where) 」から「品物 (What) 」を検索 2.ショッピングカートに入れる 3.レジに進む 4.サインインする 5.★クレジットカードの有効期限が切れている場合の処理 6.配送条件を確定する 7.注文を確定する 高信頼化ソフトウェアのための開発手法高信頼化ソフトウェアのための開発手法ガイ ドブック(情報処理推進機構) 30

Slide 31

Slide 31 text

k1LoW/runn を使うと良い シナリオテストツール YAMLでシナリオを書く。シナリオはステップの列で、各ステップではAPIにリク エストを送ったり、データをちょっと編集したり、レスポンスを検証したりする "ステップ1のレスポンスをステップ2とステップ3で使用する" ことができる https://github.com/k1LoW/runn 31

Slide 32

Slide 32 text

Web API開発は適切なツールを使ってスキーマ駆動開発すれば、 デザインレシピをそのまま実践できる A. どのようにスキーマ(シグネチャ)定義すれば良いかわからない → Protocol Buffersで書く B. スキーマ(シグネチャ)定義から実装に移るまでにギャップがある → protocやConnectでコード生成したり、ライブラリを使う C. どのようにテストを書くと良いかわからない → runnを使ってシナリオテストする 32

Slide 33

Slide 33 text

目次 良いプログラミング・楽しいプログラミングとはどんなものだろうか How to Design Programsで提案されているプログラムの「デザインレシピ」 スキーマ駆動開発によるデザインレシピのAPI開発への適用 CLI開発にもスキーマ駆動開発を適用するための取り組み 33

Slide 34

Slide 34 text

CLI開発もスキーマ駆動開発したい 34

Slide 35

Slide 35 text

え、CLIか... API開発と同様に、関数をデザインするのとは違う難しさがある 35

Slide 36

Slide 36 text

ところでなんのためのCLI開発なのか? Kubernetes CronJobでコマンドを実行する目的 apiVersion: batch/v1beta1 kind: CronJob metadata: name: subscription-payment-cronjob spec: schedule: "*/1 * * * *" # 毎分実行 jobTemplate: spec: template: spec: containers: - name: batch image: batch:latest command: - "batch" - "subscription-payment" - "--dryrun" - "false" - "--start" - "2022-01-01T00:00:00Z" - "--end" - "2022-01-02T00:00:00Z" retryPolicy: OnFailure 36

Slide 37

Slide 37 text

CLI開発も「技術スタックを変えずに」スキーマ駆動開発したい A. どのようにスキーマ(シグネチャ)定義すれば良いかわからない → Protocol Buffersで書く B. スキーマ(シグネチャ)定義から実装に移るまでにギャップがある → protocやConnectでコード生成したり、ライブラリを使う ConnectはWeb APIのためのライブラリはあるがCLIは非対応 C. どのようにテストを書くと良いかわからない → runnを使ってシナリオテストする 何が足りないのか? 37

Slide 38

Slide 38 text

入力のデコードと、ルーティングが足りない これまでのAPIサーバ開発では、Protocol BuffersとConnectサーバを使っていた サブコマンドとか引数のパースとも言い換えられる 38

Slide 39

Slide 39 text

ないなら作ればいい 39

Slide 40

Slide 40 text

naoyafurudono/clio-go https://github.com/naoyafurudono/clio-go 40

Slide 41

Slide 41 text

clio Protocol Buffersのスキーマ定義から、CLI引数のデコードとルーティングを自動生 成するツール バッチのために作ったので、現時点では引数をJSONとして渡すようにして実 装をサボっている ターゲット言語はGoだけ protoc pluginとして提供 Connectを使うためにもプラグインを使う。なのでclioをプロジェクトに導入 する障壁は低い これでCLIツール開発もスキーマ駆動開発(つまりデザインレシピにしたがった開発) をできるようになった 41

Slide 42

Slide 42 text

CLIを開発をいかにスキーマで駆動するか A. どのようにスキーマ(シグネチャ)定義すれば良いかわからない → Protocol Buffersで書く B. スキーマ(シグネチャ)定義から実装に移るまでにギャップがある → protocやConnect、clioでコード生成したり、ライブラリを使う C. どのようにテストを書くと良いかわからない → runnを使ってシナリオテストする 42

Slide 43

Slide 43 text

バッチのテストではサーバとして動かす 43

Slide 44

Slide 44 text

44

Slide 45

Slide 45 text

まとめ 良いプログラミングはデザインレシピで実現できる スキーマ駆動開発はAPI開発でのデザインレシピの実現方法である CLI開発にもその領地を広げるために、clioを作った 45