2024.12.19
システムの穴を運用でカバーしようとしてミス多発… バグが大量発生、決算が合わない状態から業務効率化を実現するまで
怠惰なエンジニアのためのコード生成(全1記事)
リンクをコピー
記事をブックマーク
高橋一平氏(以下、高橋):では、怠惰なエンジニアのためのコード生成ということで、リクルートマーケティングパートナーズの高橋が発表させていただきます。
さっそくですが、みなさんコードを書くのは好きでしょうか? 僕はあんまり好きではありません。コードを書いていて、ロジックを書くのはおもしろいですが、ボイラープレートや定型文的なコードなどをどうしても書かなければならないことは多いと思います。
こうしたボイラープレートを何度も書くのはしんどい。共通化、設計とかで解決しようとしても限界があるということで、コード生成をして解決しようというのが今回のお話です。
本発表でお話することですが、直接的なコード生成の話。ソースコードをファイルで書き出すという話と、マクロなどを使ったメタプログラミングによって先ほどの問題を解決しようという2つのお話をさせていただきます。
まず、コード生成をするにあたっての前提条件ですが、きちんと設計がなされている、クラスや関数の役割が明確になっていることが前提となってきます。
ルールがないような煩雑なめちゃくちゃなものに関してはコード生成することはできません。それに加えて、副作用がなかったりだとかDIをしっかり使っていたりだとか、テストのしやすいプログラミングになっていることが、まずコード生成よりも先にやらなければならないことだと考えています。
さっそく、コード生成の具体的な方法について話していきます。まずはパーサを書きましょうということなんですが、パーサというのは、構造化されている対象に対してだったら必ず書けます。
例えばXMLであったり、JSONであったり、Protocol Buffersだったり、SQLであったり、はたまた誰かが作ったExcelのフォーマットだったりとか、いろいろあると思いますが、構造化されていれば必ずパースはできる。
開発言語にも依存しない方法なので汎用的にも使えます。例えばSwiftで開発してます、だけどパーサはHaskellで書いてみてSwiftのコードを生成しますとか。そういう使い方でもいいと。けっこう汎用性の効く方法なので、困ったらパーサを書こうということですね。
パーサを使った具体的なコード生成の方法について説明していきます。スキーマ情報を利用したコードの生成の方法ですね。
まずスキーマをまるごとダンプして、それをプログラムで扱いやすいかたちへパースする。
このときにSQLの構文すべてを網羅したようなパースを書かなければならないわけではなくて、ダンプしたものがどういったかたちになるのかに基づいて、ある程度限定したかたちでパースを書いて作るくらいで十分だと思います。
そのパースしたものからDAO関連のソースコードを生成できます。例えばデータモデルやDAO、あとDAOのテストコード、またデータモデルのファクトリなど。そういったものに関してスキーマの情報をもとに自動生成することができて、実際に我々のサービスでもこういったことをやっています。
続いてメタプログラミングについてです。メタプログラミングというのはプログラムを扱うようなプログラミング手法のことで、プログラムを入力したり出力したりするようなプログラミング手法のことです。
メタプログラミングをすることによってなにが嬉しいかと言うと、構文木だとか型の情報にアクセスすることができます。ということはメタプログラミングを利用することで、利用しないようなプログラミングと比べてプログラムの表現力が高まるということが挙げられます。
具体的にメタプログラミングを使ってどういったことができるのかをみていきたいと思います。我々のプロダクトでもこういうことをやっているんですけれども、テストコードのテンプレートの生成です。
ユースケースだとかそういったもののテストのコードの生成だと考えてもらえればいいんですが、例えばテストしたい対象のコードを文字列として読み込む。そこからマクロなどを使って構文木の情報を持ってくる。
そうすると何ができるかと言うと、パースされた構文木の情報がわかるので、モックすべき関数がどんなものかがわかる。モックすべき関数がわかれば何ができるかと言ったら、そこからテストコードのテンプレートを生成することができると。
前提条件としてきちんとクラスの役割が明確であるだとか、テストができる状態になっているというのはここに関連することで、ユースケースとか、書き方が統一されていれば比較的メタプログラミングによってテストコードを作るみたいなことはやりやすいんですけれども。これが人によってバラバラとか、そういった感じで作られていると、なかなかこういう生成は難しくなってきます。
続きまして、ダミーデータを簡単に作る方法ですね。テストを書いていて1番しんどいことは何かと言うと、たぶんダミーデータを作るのがとてもしんどいことだと思います。
巨大なクラスですと1つのユースケースにいくつものモデルが関連していたりして、何種類ものオブジェクトのダミーデータを作らなければならない。テスト用のダミーのデータを作らなければならないということで、これを手でやろうとするととてもしんどいと。
これまで話していたように、ユースケースのパースをしたりとか、メタプログラミングを使ってクラスごとにダミーデータのファクトリを作るっていう解決方法もあります。僕たちももともとそういうクラスごとのダミーデータを生成するという方法でこれを解決していたんですけれども。
クラスごとにファクトリを作らないといけないということで、手でやるとしんどい。生成するにしても、生成したファイルはけっこう膨大なのでメンテナンスのコストが大きくなってしまう。加えて、モデルの書き方がちょっと変わったりすると全体的に影響が出たりすることもあります。
では、これを楽に解決する方法はないのかということで、辛くない解決方法。どんな型のインスタンスだろうが作れる汎用的なファクトリというものを定義できればこれを解決できると考えました。
具体的にどうやってそれを実現するかと言うと、あらゆる型ということなので、あらゆる型が実際どんなかたちをしているのかを一般化することができれば、どんな型のインスタンスでも作れるファクトリというものが作れます。そこで登場するのが直和型と直積型の考え方です。
直和型というのはA+Bで、AかBのどちらかを取るような型というふうに定義します。直積型というのはA×Bということで、AとBの2つの要素を持つような型。
直和型というのはEitherとか聞いたことあると思いますが、そういったものです。直積型というのはTupleとかですね。そういったデータ構造だと思っていただければと思います。
こういったかたちにいろんな型は書き換えることができます。
例えばAnimalという抽象クラスがあって、それを継承するDog、Catっていうクラスがあったとします。AnimalはDogかCatのどちらかなので、DogとCatの直和であると。
Catというのは、nameというStringの要素とageというIntの要素を持っている直積であると定義して、直和と直積だけで記述することができるます。
これを使って、構造に関しては全部直和と直積のかたちで言葉は定義できるので汎用的なファクトリを作ることができる。実際にどのように実装するかと言うと、ジェネリックプログラミングライブラリを使うという方法がたぶん1番一般的だと思っています。
Scalaだったらshapelessっていうライブラリなんですけど、そのあたりを使って作るのが一般的です。もう1つはメタプログラミングを使った方法ですね。自分で自力で実装するという方法があります。
我々のプロダクトではScalaを使っていて、実際にこのようなどんな型のダミーデータでも作れるファクトリというものを実装したんですけれども、それがこの例ですね。
1番上の行で、Dogというクラスを定義しました。その真下でTestObject [Dog] というふうに書いて、型引数を渡しています。これによってDogのダミーデータが生み出されました。
ここで注目してほしいのは、TestObjectというものを書いている段階ではDog型のダミーデータをどのように作るかを、実はTestObjectに与えていません。
これが実際に中では直積と直和のかたちに置き換えて、そこからまた戻してというようなことをやって作っているんですけれども。このようにしてイントロの作り方、Stringの作り方を個別に与えるだけでどんなインスタンスでも作れるようなダミーデータのファクトリを作ることができました。
今回はお話ししませんが、Stateモナドを使うことによって、これは固定のダミーデータなんですけど、ものよって違うダミーデータを作ることも可能になってきます。
ということで、まとめです。コード生成を使って怠惰にプログラミングをしましょう。メタプログラミングとかジェネリックプログラミングみたいなものを使っていくといいんですけれども。
こうしたことは言語に依存するところがあると思うので、メタプログラミングやあまり強くない言語を使っている場合にはパーサを使ってコード生成をすることでなんとかすることができます。
以上になります。ご静聴ありがとうございました。
(会場拍手)
司会者:高橋さん、ありがとうございました。ご質問がある方は挙手お願いいたします。ないですか? じゃあ僕から。僕、実はAndroidエンジニアなんですが、テストとかも書かなきゃいけなくてすごくしんどい気持ちにはなっています。そこで、実際にこれを導入してどれくらい楽になったのかなというのは気になります。
高橋:とくに効果が大きかったのは、汎用ファクトリです。本当に最初の状態ってファクトリすら定義されていなくて、テストを1個書くごとにダミーデータを上からガーっと書いて。1つのテスト書くのに1,000行書く、みたいなことがあったりして大変でした。
ダミーデータを作って、さらにテンプレートの生成みたいなものを組み合わせることによって正常系のテストを1個書くくらいだったら、自分で手で書かなきゃいけないのは数十行とかそのくらいで済むようになりました。生産性の点に関してはだいぶよくなったのかなと思います。
司会者:ありがとうございます。それでは、これで終了とさせていただきます。高橋さん、改めてありがとうございました。
(会場拍手)
2024.12.20
日本の約10倍がん患者が殺到し、病院はキャパオーバー ジャパンハートが描く医療の未来と、カンボジアに新病院を作る理由
2024.12.19
12万通りの「資格の組み合わせ」の中で厳選された60の項目 532の資格を持つ林雄次氏の新刊『資格のかけ算』の見所
2024.12.16
32歳で成績最下位から1年でトップ営業になれた理由 売るテクニックよりも大事な「あり方」
2023.03.21
民間宇宙開発で高まる「飛行機とロケットの衝突」の危機...どうやって回避する?
PR | 2024.12.20
モンスター化したExcelが、ある日突然崩壊 昭和のガス工事会社を生まれ変わらせた、起死回生のノーコード活用術
2024.12.12
会議で発言しやすくなる「心理的安全性」を高めるには ファシリテーションがうまい人の3つの条件
2024.12.18
「社長以外みんな儲かる給与設計」にした理由 経営者たちが語る、優秀な人材集め・会社を発展させるためのヒント
2024.12.17
面接で「後輩を指導できなさそう」と思われる人の伝え方 歳を重ねるほど重視される経験の「ノウハウ化」
2024.12.13
ファシリテーターは「しゃべらないほうがいい」理由 入山章栄氏が語る、心理的安全性の高い場を作るポイント
2024.12.10
メールのラリー回数でわかる「評価されない人」の特徴 職場での評価を下げる行動5選
Climbers Startup JAPAN EXPO 2024 - 秋 -
2024.11.20 - 2024.11.21
『主体的なキャリア形成』を考える~資格のかけ算について〜
2024.12.07 - 2024.12.07
Startup CTO of the year 2024
2024.11.19 - 2024.11.19
社員の力を引き出す経営戦略〜ひとり一人が自ら成長する組織づくり〜
2024.11.20 - 2024.11.20
「確率思考」で未来を見通す 事業を成功に導く意思決定 ~エビデンス・ベースド・マーケティング思考の調査分析で事業に有効な予測手法とは~
2024.11.05 - 2024.11.05