自動生成というと エクセルなどのドキュメントから 超高速開発のようにコードを自動生成したり、雛型コードを生成して 後から詳細の実装を追記したり 雛型コードを継承して差分実装をしたり という*1感じだと思います。
そして、私自身、自動生成できるんだったら、それが一番良いんじゃないか? 設計から実装について強い関連付けがあれば 仕様と実装の乖離が無くなるのでベストじゃないか?と思っていました。
でも、それは違うのかもしれない、と 以前から ぼんやりと思っていて、一度 自分の中で整理しておいた方が良いのでは?と思って まとめてみました。
なお、今回 まとめよう と思うに至った直接のきっかけは
vermeer.hatenablog.jp
の神崎さんのRDRAによる要件定義手法の お話を伺ったことです。
要件定義から全体を俯瞰する情報を生成するということと、全てを書こうとしないという話から 色々と自分の中で波及していったという感じです。
要件定義や設計からソースコードのような実装を自動生成しようとすると、原則として全てを書かないといけないという前提があります。神崎さんのお話を伺って、必要なものを作るための手段であって、ソースコードのような実装資産を作ることだけが自動生成ではない、という結構 当たり前のことに立ち戻れて、自分なりに きちんと整理しておこう、となった次第です。
した方が良い(しても良さそう)
粒度とか項目毎の考察のボリュームはまちまちです。
思ったままに書いていきます。
一通り、書いてみて思った結論を先に示すと
- 編集不可を保証できるものだけにする
- 仕様と振る舞いが明確であること
- 実装ではなく検証を扱うものの方が安全
と考えます。
ワークフロー定義
ワークフロー的な、BPMとか、ルールエンジンとかユーザーがビジュアルで確認しながら定義・設計するものは自動生成しても良いように思います。
ユーザーが要件として定義したものは、そのまま実現したいこと直結しています。
また、この手のツールはビジュアルでフローを見やすくしているなど工夫がされているのでユーザーにとっても 要件を表現しやすいと思います。
ただし、定義のやり方によっては、フローというよりもプログラムの分岐レベルの粒度の設計をしてしまって、疑似プログラミング言語による実装になりかねませんので、そこは注意が必要だと思います。
加えて、全てのフローを同一粒度で表示するため、規模が大きくなると 1つのフローがやたらと大きくなったり、逆に細かく部品化しすぎることで 全体の見通しが悪くなるなど、実際に使っていく中で 理想と現実の違いに悩まされることにもなりそうな気はしています。
ですが、それは粒度の問題です。自動生成に向いている向いていないという意味だと、生成した定義(XMLなど)を 開発者が編集する余地はなく 割と向いている と思います。
詳細情報から全体を把握するドキュメント
個別から全体を組み立てて全体を見通す資産を自動生成の対象として有効だと思います。整合性検証ツールや影響調査のレポートも類似です。
生成される資産は実行資産に対して直接の影響がないので安心して扱うことができます。
SIerの大規模プロジェクトだと、このためのチームが作られたりするように思います。*2
設計書という詳細情報から
自分のイメージに近いのは RDRAです。
*3
個々の設計書から、それらをまとめて俯瞰できるドキュメントを自動生成する、というのは有効だと思います。
トップダウンで作成するから、そんなものは いらないという考えもあると思います。
ですが、実際のところ 詳細の検討をしている中で 分かってくる関連性や それにあわせた修正を図りたいということは 多々あります。改修案件なども、その一例でしょう。
必要になった時に、積み上げて作成すれば良いかもしれませんが、これもまた大変で機能間で関連があると思っていたのに無いということになっていたり、その逆だったり。
それは記述誤りなのか、作成時のミスなのか、いずれにしても 詳細から積みあがったものと差異があると、そもそも検証・確認・検討ができません。
全体像が 正しく積みあがっているという証明は、想像以上に要件知識を必要します。
というか、規模が大きくなると、それが「確からしい」というレベルでも有識者じゃないと判断が難しいというのが、私の経験則です。
全体俯瞰を作るだけなのに、ドメインマスターの時間を必須とするのは 良い仕組みとは言えません。
長々書きましたが、部分から全体の作成は機械的にやりましょうよという事です。
そして、それが出来うるように ドキュメントには そういう仕掛けを始めから仕込んでおきましょう、ということです。
あとから仕込むのは大変です。
設計書の書式を検討する場合、何を どの工程で設計として記載していくのか?ということを考えると思います。
私としては、それだけでなく、例えばRDRAのように詳細から全体を組み立てる仕組みを 設計書でどうやって記載するようにしておくか ということを案件初期から検討しておくことをお勧めします。
実装という詳細情報から
同じく、ソースコードからクラス図を生成するみたいなものは、一定の有効性はあるように思います。
ただ、コードからクラス図を作るというのについては、「と思います」以上のことは 私の経験としては言えません。*4
例えば、JavaDoc や Swagger のようなものです。
JavaDocはAPI仕様ということで、事前に実装無しで作成して それに合わせてテストをつくっておき、その上で実装するということもあるでしょう。そういう流れだとJavaDocは設計に相当します。
ただ、私の場合だと ちょっとそこまで理想的ではないというか、結構 雑です。頭の中で だいたいこんな感じのクラスを作っていきますか、と考えてから コメントなしで実装をザックリして、レビューしながら補足としてJavaDocを追記している という感じなので 仕様ではありますが 設計とは違うという感じです。
あとは、テスト作りながらを JavaDocを記述していくという感じです。テストしていく中で APIの説明が無いとか 分かりにくいと自分が感じたら、その感覚にあわせて 記述していくという感じです。*5
JavaDocにせよ Swaggerにせよ、私の理解としては、実装から生成した 今動いている機能のAPI仕様であって設計書ではない、と思っています。
そして実装と乖離の無いAPI仕様が いつでも作成できるというのが良いところです。*6
コメントいらない コードを読め、というのは良いですが、外部ライブラリを使ったりしていると そのコード自体を参照できないので API仕様は大事です。
実装から実装(または実行資産)
Pluggable Annotation Processing API
Doma*7 のようなAnnotation Processorにより実装から補助実装を作る仕組みも自動生成の1つだと思います。
Annotation Processorによるコードの自動生成のポイントは
- コアとなる実装に対して 対となる補助的な実装が 常に同期を取って生成される
- 生成されたコードは 編集できない*8
というところです。
ちょっと違いますが Lombok*9 も近しいと考えています。
最近、Lombokは避けられつつあるような気がしなくはないのですが、単純なアクセッサーであれば Lombokが生成するコードで問題ないと考えています。*10
注意した方が良いと思うところとしては、Lombokでアクセッサ以外のコードを扱おうと思った場合は、自分でDelombokして生成されるコードの確認はしておいた方が良いでしょう。Annotation Processorはソースコードを生成するので デバッグなどで追跡する事が出来ますが、Lombokはクラスを編集しているようで ソースコードが無く デバッグが やりにくかったりします。
他にも、破壊的な事も出来そうなので 気をつけて使った方が良いレベルものではあります。例えば、JPAのEntityクラスからスキーマーを自動生成する(というか ALTER TABLEレベルの変更をする)というものは 便利ですが破壊的でもあります。
個人的には、Domaのように 自ライブラリを利用するための補助コードを自動生成するくらいの影響度が丁度良い気がしていますが、分かった上であれば 結構思い切ったものも生産性向上という意味では 面白いかもしれません。
ちなみに、そんなことを思って 自作したのが以下の「messege propertieからEnumクラスを作成する」です。*11
vermeer.hatenablog.jp
DSLから実コード生成するのを自動生成するというのも厳密には その範疇かな?と思ったので、思考の出し切りということで記載しておきます。
SQLのように標準化されたものだけでなく、オレオレDSLも有効なツールだと思います。
やはりポイントは、DSLで作られたソースコードを直接修正をしない(やる人いない気がするけど)というところです。
E2Eテストスクリプト(もしくはデータ)
E2Eテストについて、やりすぎ注意というのは それなりに分かっているつもりです。
大きい問題はテストスクリプトのメンテナンスコストだと思っています。
なので「すべてのテストケースを自動生成せよ」というつもりはありません。
IT(とか ST)は、ユーザーが「こうあってほしい」という要件の確認でもあり、開発側からすれば担保すべきものでもあります。*12
ユースケースの主たる経路シナリオ(基本・代替・例外)について、テスト設計を通じて合意をして、その合意の取れた資産でテストを実行する、としておくと、ユーザー・開発側 ともに安心感をもって先に進めることができます。
少なくとも 全く頓珍漢な方向には行っていないということを相互に確認できる仕組みは あった方が良いとか、これらのテストは「常に満たされた状態にしておく」ということが重要なので いつでも実行できるようにもしておきたいとか、これ以上は テストそのものの話なので割愛しますが、良さそうだという雰囲気だけでも伝わればと思います。
また、ユーザー自身がテストスクリプトを記述できるのであれば 当然ですが自動生成よりも もっと効果はあると思いますが、今回はあくまで自動生成の是非ということで除外しておきます。
以下に、その理由を追記しますが、あくまで この適用は
自動生成したスクリプトで出来る範囲に留めるというのが原則です。
もし、スクリプトそのものに対して 何かしら手を加えないといけないのであれば、補助する仕組みを自作するか、より良いものを選定し直すか、自動生成の対象外にして別管理とするか、再検討した方が良いです。
ユーザーと開発側の仕様認識を一致させることを優先すべきで、その上で 自動生成できる範囲であれば自動生成したスクリプトでテストする、という割り切りは必要です。
手順を示して結果を確認するだけだから
内部でどのようなことをしていようと関係なく、ユーザーにとって外してほしくないことは「こういうシステムであってほしい」というアウトプットです。
これは設計というよりも、要件定義に近しいです。
それって基本設計じゃないの?というのは適切な指摘だと思うところですが、ポイントは実現したい業務シナリオの方なので要件定義に近い物だと思っています。*13
なので、「画面項目のバリデーションチェックの確認」みたいなE2Eテストについては、やっても良いけど、あんまりやるとしんどくなるよ、という感じです。やったとしても、代替ルートの確認の一環として数ケースを扱うだけにした方が良いでしょう。
ポイントは実現したい業務シナリオの充足であって 網羅的な緻密さではありません。
ユーザーと開発間で、ユースケースに対する理解に齟齬が無い環境を作る ということが重要です。
ユーザーがメンテナンスできる
エクセルなどのドキュメントはユーザー自身がメンテナンスできます。
そして、内部実装の詳細を分からなくても、実現したい操作手順を記述することは ユーザーが実際にやりたいことなので イメージが しやすいところです。
また、ドキュメントを通じてシステムで出来ないことを、事前に開発側から伝えることも出来ます。
こんなことがあるか分かりませんが、テストシナリオで「ここでYahoo検索」という記述をしたとして「開発するシステムはプライベートネットワーク上で動くものなので外部ネット情報は検索できません」と言えます。その上で、なぜユーザーがYahoo検索をしたいのか というところから、当初見えていなかった要望を把握する事ができるかもしれません(例えば住所入力で郵便番号を調べたかったとか)。
条件ありで しても良い
設計から実装
後述で、しない方が良い として実装の自動生成については 色々と示したいと思っていますが、その中でも こういうものだったら 対象としても良いかもしれない というのを先に示したいと思います。
いずれにしても、大前提として生成した資産を直接編集しないということを守れる仕組みがある場合に限る
と考えています。
必ずと言って良いほど この手の「触るな危険」運用は守られないというのが 私の経験則です。
<<ちょっと私の昔話>>
とあるベンダーのSAStrutsをベースとしたフレームワークが自動生成をするものでした。
何もないところから作り上げることと比べると生産性を高めるものであったとは思います。少なくとも、ある程度の形が整うくらいまでは。
詳細は割愛しますが、それには私の直感というか経験を踏まえた「多くの割り切り」を適用していったから、というのも少なからず貢献したと思っています。
それでも、帳票出力のところで JasperReportを使っていたのですが、その定義の自動生成は そこそこしんどかったですね。
自動生成で作れる範疇の開発で収まれば良いのですが、細かい要件取り込みが必要となる帳票系は 外部に提出する資料だったりするので ユーザー自身が割り切ることが出来ない(公的資料のレイアウトとか)ところがあります。
まぁ、一番つらかったのは、ベンダー提供のフレームワークなのに プロジェクト内で 一番理解しているのが私だけだったというところ。ベンダーのフレームワークチームへの問い合わせは出来たので 諸々解決はしていけましたし、開発に関わってくださったリーダーの方自体は 実装経験もある方だったので 全く頓珍漢ではなかったのですが、流石に VBクラサバだけの経験で、SAStrutsなにそれ?では ちょっと…というのは あったりなかったり。
特に佳境に入った際、実コードはこうなっていて、コード上でこういうことをしたかったら 設計書をこういう風に書かないとみたいな局面になると、私の自動生成コードの編集待ちが ボトルネックになりかねず。*14
正直、開発規模とメンバー数が 自分で制御可能な規模だったから出来たようなものです。すべての要件定義について、それがシステムの どの機能で どのように実現されるのかというところまで 私自身が語れる、というものでなかったら無理だったでしょう。*15
<<昔話はここまで>>
触るな危険の運用を守る方法は1つです。
責任を取る人以外は触らない運用を徹底するです。
これは生成されるコードそのものも含めます。
上述の昔話では、設計書と生成されるコードについては、チェックアウトして編集不可にしました。
責任者によるコードチェックアウトは、実際は結構な事件・事故の元になりえますが、そのくらいのリスクを孕んでいると思って運用しないと、ほぼ確実に それ以上の破壊*16が待っていると個人的には考えています。
つまり規模が大きくなったら運用困難だと思った方が良い、というのが素直な印象です。
定数
恐らく通常だったらやるであろう、定義ファイルから 生成した定数クラスコードを使って 実行環境に反映させる、というような運用ですが、こちらは 後述しますが しない方が良い に分類したいと考えています。
直感的には、定義情報から定数クラス(Enumクラスや、message.properties)を生成するのは良いと思いますし、機械的な作業という意味では しても良いと思います。
ただし適用には条件があります。
- 対象定義書をクラスパス上に配置して上述のAnnotationProcessorなどの ビルド都度 上書き生成するような 強制力のある仕組みとペアにする
- 更新者を限定的にするというケースに限った運用の徹底(上述)
わざわざ手で転記するのではなく自動生成をした方が良いのでは?というのは分かります。
であれば、実行資産を直接編集して、必要であればユーザーに提示するドキュメントに反映する仕組みを設けた方が良いと思います。これは上述の「詳細情報から全体を把握するドキュメント」でOKとしたパターンに準じます。
しない方が良い
以下で、色々と例示していきますが、した方が良い の裏返しです。
生成した資産に手を加える仕組みの場合は、しない方が良いし
可逆性を求めるとなると、更にしない方が良いです。
こちらも、一通り書いた 振り返りを先に書くと
上述の前提に加えて
適切なサンプル実装があれば十分じゃないですかというのが 私の総括的なところです。
定数クラス
上述の「条件ありで しても良い」とした「定数クラス生成」は、典型的なところなので 総括的に先に記載しておこうと思います。
一番の理由は、生成した資産に対して 誰でもできそうだからです。
そういうものは たいしたことはないと 誰もが思うために 設計書と実体の乖離が容易に発生してしまいがちです。
運用ルールがあり、引継ぎ作業もして、手順書を作っても、誰でもできそうの誘惑は大きく、何よりも管理者側にとっても、誰でも編集できると思っていることは さらに困ったことにつながります。
ちょっとだけ、今だけ、緊急だから、あとから同期を取れば良いから。
一番悪いのは、最後のところ。
それ、本当に責任もってやります?
だったら 始めから ちゃんと定義書から定義ファイルを作って更新しましょう?
してはいけない とまでは言いませんが、するんだったら ちゃんとしましょう という感じです。現実問題として、あなたが誘惑に負けた管理者に「ルールはルールなので徹底してください」と言えるのであれば問題ありません。*17
雛型コード
全く何もないところから、設計書を元に雛型となるコードを生成して、そのコードに対して実装を肉付けして開発をしていくパターンです。
良さそうですよね? 私も良さそうに思いました。
では なぜ しない方が良いのか?
可逆性がない、だけど設計書との整合性を保とうとしてしまうからです。
開発側で あくまで雛型だと分かっていても、設計者やユーザーからすれば 雛型ではなくて 整合性の取れたものとして扱われます。誰だって自分が関わったものを 使い捨て にするのは嫌です。
何よりユーザーに 使い捨てになります と開発側が言えるでしょうか?
では 設計書であっても、リファクタリング対象とすれば 捨てることは無いという理屈があったとしましょう。
でも生成されるのは あくまで雛型です。
設計書を修正する度に 業務要件を追記したソースコードへ雛型コードをマージすることを求められるとしたら?
マージなんて差分チェックして転記するだけだから簡単でしょう と無邪気に言われたら?
難しい・簡単の話だと、もちろん簡単ですが、この煩雑さは求めている生産性と品質を担保している開発プロセスでしょうか?
こうやって当初の計画は簡単に崩れます。
幸運にも、ユーザーが設計書から作成されるコードが雛型であることを理解しているパターンでも油断はできません。
雛型コードだけれど、設計書から作成できると分かったら 出来るだけ無駄をなくしたいと思ってしまいます。
そうすると、設計書から作成される雛型コードを より「完璧にしよう」と躍起になってしまいます。
さっさと実装して動く状態で確認をしたくても、設計書を こねくり回してコード生成の確定を後ろに倒していきます。
そして、設計書からコードを出力して 早く動くものを確認できるようにするための仕組みのハズが、気が付くと逆方向に向かってしまうのです。
結局、作ったものを 捨てるのは 誰にとっても 心苦しいのです。
私の経験としては、実装に関わる設計変更は同期をとりましょう ということになりました。
といっても、実装されているコードに都度 取り込みたくはありません。仕方が無いのでくコードに合わせて設計書を編集しました*18。
もうこうなると自動生成とは?って感じですし、謎のスキルです。
さて、そもそも雛型コードの生成によって、何を求めたのでしょうか?
満たそうとしたのは、まっさらな状態から Mavenで基本となるパッケージの生成にプラスアルファで設計書からコードも作ってしまいましょう、ということだけです。
これって スキルの高い人が丁寧に作成した、実際に動いているプロジェクトさえあれば ほとんど満たされます。
自動生成ツールを作っている人は 良かれと思って 何もない状態からコードを作ってくれていますが、理想と考える枠組みを示すくらいなら、いっそのこと 俺たちの考える最も理想的な実装例 を示してくれたほうが嬉しいです。
ただし、こんな感じで簡単に動きますよ、というレベルのサンプルはダメです。求めているのは段ボールハウスではありません。モデルハウスです。
フレームコード
大きい枠組みとなる部分のソースコードを設計書から自動生成するというパターン。
例えば、個々のロジックは、テンプレートパターンなどによる差分実装で肉付けをしたり、必要なモジュールを呼び出して連携するやり方を刺しています。
これについては、BPMに近しいので そこそこいけそうです。
でも、これも提供するものが編集可能なソースコードである以上、 雛型コードで示した課題を解消するものにはなりえません。
BPMやルールエンジンにおいて、フローを制御する部分は 定義情報を読み込んで制御しており原則として直接編集をしない仕組みだから成り立っています。
もし、定義情報であるXMLについて 直接編集をしたとしても あくまで定義情報なので XMLを編集するツールで読み込むことができる(つまり可逆性がある)ので問題はありません。
基底クラス
設計書からソースコードを生成はするけれど、あくまで基底クラス(Abstract Class)であって、生成したコードには編集を加えないというパターンです。
これだと、生成したソースコードを 編集対象としていないので 良さそうです。
私も これについては良さそうに思っていました。
私の浅い記憶では Entityクラスに対して 本方式を取っていたように記憶しています。
基底となるEntityとして スキーマー定義に一致するJavaBeanを生成して、プラスアルファの操作については 継承して差分実装するという やり方です。
正直なところ、今回の整理を改めてするまで、このパターンについては 良さそうだと思っていました。
たしかに、基底クラス方式は、雛型コードと違って 編集するのは継承後のクラスのみとしておけば、事故の起きる余地はありません。それに継承による実装はオブジェクト指向っぽくて良さそうです*19。
また、スキーマー定義と一致するORMクラスを自動生成してくれる仕組みも ありがたいですし、私自身、Netbeansで自動生成したものを使っていて便利です。
加えて、スキーマーから再生成をした際も、そのまま上書きをするという運用を徹底すれば、継承でも問題は なさそうに思われます。
今現在でも、絶対にやってはいけないとは言いにくいと思っており悩ましいところです。
これまでの整理と違って、このパターンについては どちらかというと設計思想的な意味で しない方が良い という整理を考えているので 正直毛色が違います。
さて、そもそもですが、自動生成した実装を継承するというのは どういう設計思想になるのかな?ということです。
考えられるのは、継承先に対して横断的に行いたいことや 共通して持たせておきたい機能があるということで フレームワーク特有の実装を基底クラスに組み込んで、それを活用するという設計思想です。
操作の抽象と意図したインターフェースを持っていれば、そのあたりも 基底クラスに implilemtしておくというのも あるでしょう。
実装に対して、ある一定の強制を強いることで コーディング規約のような役割(作法)を示すことも 広い意味ではフレームワークの適用範囲だとするのは 間違っているとは言えないでしょう。
継承よりも委譲
継承を嫌忌するというよりも、継承する必然のあるクラスですか?というのを考えてみましょう、という感じです。
例えば、スキーマーから生成したコードは 継承をすべき概念・属性でしょうか?
スキーマーがドメインモデルに近しいものになる事はあると思いますが、でも 継承するもの とは違うように思います。
もしくは、スキーマー定義(構造)を ドメインモデルに対して強依存をさせるということは 良い設計?という言い方でも良いと思います。
ちょっとしたCRUDアプリで スキーマー=画面要素 みたいな密結合アプリだと 余計なものがなくて良い、ということはありそうですが、それはそれで かなり限定された要件というか・・・、それを汎用的なシステム基盤として採用するのは 本当に良いのかな?というように、現在では 考えるに至ったという感じです。
もし、今回例示した、スキーマーの写像である Entityクラスを自動生成するのであれば、finalなクラス であれば良いと思います。そして、それを委譲して使用するという設計思想です。
結局、全プロパティを単純に委譲するだけだから無駄じゃない?というケースもあると思いますが、そのあたりの あるある話 は 継承よりも委譲 のところで論じられているように思いますので、そちらに譲りたいと思います。
横断的な制御
これは上述の Entityクラスの自動生成に限った話ではなく、Actionクラスなど レイヤー毎に適用したい機能実装を横断的に行うというようなことを示しています。
基底クラスを用いて横断的な制御を組み込むパターンは、自動生成云々とは関係なしに見かけるように思います。
賛否があるように思いますが、間違っている という訳でもないでしょう*20。
とすると、自動生成でも問題ないのでは?というところですが やはり しない方が良い というのが結論です。
横断的な制御 を目的と考えた場合、実装の約束事として基底クラスを必須としなくてはいけないケースが 私の想定の範疇では考えつきませんでした。それらの要件は、アノテーションとインターフェースを用いたAOPで解決できるし、その方がスッキリと やりたいことが実現できると思います。また、約束事として基底クラスを継承するよりも、こちらの方が柔軟です。
バッチスクリプトや、ビルドスクリプトを定義情報(エクセルの設計書とか)から自動生成する、というのは かつて やったこともあったりしますが、それをするのであれば、定義や設計を読み込むスクリプトを作って そのパラメータ情報として ドキュメントをインプット情報として使う というのが正しいやり方だと思います。
スクリプトそのものを作った場合、何も知らない運用者が 一度でも スクリプトを直接修正してしまったら、大体のケースで今 動いているスクリプトが正しいということで 自動生成から作られたスクリプトは使われなくなります。
私の観測範囲では、突貫でスクリプトを直接編集して対応した場合にも、ドキュメントを修正して 差分比較云々をして、最終的に生成したスクリプトで改めてリリースしていれば まだ良心的で*21、最悪なのは、設計書から生成したスクリプトと 実行されているスクリプトが 振る舞いも含めて乖離してしまっているケースで、もはや過去作業実施者は何を元にユーザーと合意を取ったのか?という疑心暗鬼になるということが発生しかねないということです。
いずれにしても、振る舞いに直結するような自動生成については、ソースコードと同じく やめた方が良いです。
ビジネスルールから定義実装
RDRAでいうところのビジネスルールに相当するものです。場合によってはディシジョンテーブルと言われるものも含みます。
かつて、私は、ルール設計書から、XML定義ファイルを出力するというツール作成をしたことがあります。そのまま定義資産として読み込めますし、リソースとして実行中でも置き換えれば反映されるので、良さそうだと思っていました。
定義情報自体に対して、開発者が直接手を加えることは運用上まず起きない(そんな面倒なことをする開発者もいない)ということもあり、安全性も担保されていたと思います。
ですが、今は、しない方が良い、と思うようになっています。
これは、その時にも 少し違和感を感じていた感覚があったのですが、追加した要素が意図通りに制御判定に使われているか、意図以外のところへの影響はないのか、ということについては担保するものでは無いと思うようになったからです。
メリットと思われることが、さほどメリットとも言えない、というところと 開発思想として なんだかシックリこないということがあって そういう結論に至りました。
設計と実装の乖離が無い というメリットへの反論
自動生成のメリットの最たるものですし、実現もしているので良さそうです。
でも、開発者が自動生成した資産を使用している(呼び出している)クラス側で、そのルールが意図通りの挙動であるかテスト実装をして確認をしている様子を見たときに「あれ?」と思ったのです。
自動生成ツールとして、設計通りの意図の定義を出力はしますが、それが 想定通りのケースを満たすものなのか というのは別の話なんですよね。
ルール定義は、それなりに整合性チェックをツール側でもしていましたが、チェックをかいくぐるタイプミスがあった場合や参照クラスのバージョン違いなどによる関連障害などを勘案すると確認をしないというわけにはいきません。
設計と実装の乖離は無い、ただし その実装が正しく動く保証はないということです。
正しい、というのは、期待した挙動、ということです。
いくら「このif文だと、それ以外の挙動はしない」といっても、動かしていなければ保証されたとは言えません。
実際に動かしてみたらNullPointerで実行時例外、なんていうのは良くある話です。
動的に入れ替えが出来る というメリットへの反論
リソース資産として、実行中にでもルールを入れ替えできる、というのもルール定義XMLのメリットでした。
ただ、それを実施することは一度も見たことがありませんでした。
ルールはビジネスロジックの中核です。ただのif文でも無ければ、ただの定数の集合でもありません。
そんな類の資産を実行中の稼働環境にいきなり適用するプロジェクトがあったら そちらの方が恐ろしいです。
動的入れ替えができるということは ビルドをしなくても定義の入れ替えができる というメリットもあります。大きなプロダクトだとモジュールをビルドでまとめるだけでも まぁまぁの時間が必要です。
でも、それをルール定義XML部分だけに適用しても効果は あまり見込めませんし、繰り返しますが ルールのようなコアな資産を確認もせずに適用することは有り得ません。
でも、その時は そこには気が付けませんでした。今回の整理をする中でメリットと思っていたことが 改めて考えて そうではない と気が付いたという感じです。
ロジックによる改善余地が無い
やっていることは、ifやcaseで記述に準ずることと、スコープ上のインスタンスをDIして参照する、ということを おそらく内部的に行っていたと思います。
そのうち、条件分岐がXMLの主な役割で、あとはDIする対象資産の表明もしていたと思います。
表で記載された設計を元に単純な条件分岐を実装する、という単純な実装なので 自動生成でも良さそうですが、例えば状態遷移図のような 大きめの条件表記や、設計書の書き方によっては 冗長な判定となるようなXML定義書を出力ということが分かっていました。
例えば 早期リターンできそうなところも、XML定義だと しっかり最後まで全ての条件を総当たりする、みたいなことです。
例えば、性能面で課題があった場合に 改善の余地は XML読み込み部品に全て委ねられるか、XML定義の作成論理を全要件に最適なものとなるようにチューニングするしかありません。仮にそれが出来たとしましょう。では それまでに作成したXML定義は どうなるのでしょう?、ツールによって再生成したものが 正しい挙動を担保してくれる、ということをどうやって保証するのでしょう?
保証できるとしたらテストだけです。
一度 生成ツールの改修を行ったことがありますが、まぁまぁに大変でした。設計書の書き方の見直しがメインで生成するXML自体の変更は無かったので、前後のツールで資産を出力して xpathで比較するツールで検証する、みたいなことをして変更前後の保証をしました。これが内部の判定構造を変えるようなものだったら比較が出来ないものだったら・・・、考えるだけで ぞっとします。
もし、するのであれば・・・
対象ルールクラスのテスト用のスクリプトおよびテストデータと、ルールクラスのインターフェースを 自動生成するというのが 良かったように思います。
その上で、条件判定用のXML定義を生成するもしくは、生成した実装をDIでInjectionする、みたいな仕組みであれば 良いかもしれない という感じです。
ここまで面倒なことをする?というところはありますが、こうしておけば 正しく安全に変更できる仕組みになると思います。
設計からテストも作るし、実装も作る、ということについては 無駄では?それで正しい挙動の保証になるの?というという考え方もあると思います。どちらかというと、振る舞いに関する実装をするのであれば テストを優先したうえで 実装を生成すべきではないでしょうか?というのが 私の考えです。
びっくりしたのですが、ちょうど本記事を書いている時に神崎さんのツイートで似たようなことをつぶやかれていました。
余談
超高速開発ツール
自動生成といえば 設計書からコードを生成する、超高速開発ツール系についてですが、私は決して反対をするつもりはありません。
使用者において
ということが明確であれば、使っても問題ないと思います。
極論ですが、エクセルの表で定義情報を まとめておいて Scratch*22でロジックを実装する、くらい明確な意図であれば 問題は無いと思います。
ただし、設計書からノンプログラミングでシステムが出来ますという事を鵜呑みにして導入するのは 止めた方が良いと思います。
まず、システムに対して その程度の認識であることが危険です。
使用者は むしろシステムというものを良く分かっている人です。
個人的には、セールストークとして、それを標榜するのは売り手の論理なので仕方なし、と思っています*23。
なので、私が言いたいのは 買い手が賢くなりましょう ということです。
システムは ある面では 簡単に見えるところもありますが*24、簡単そうに見えて そうでもないところもある、そういう塩梅が分かっている人であれば、超高速開発ツールは 多分というか かなりのところ有益なツールだと思います。
EUC*25やRPAも同じ範疇だと思います。
疑似言語からコード生成(ネタ枠)
元々はDSLのところでネタ的に書いていたのですが、雑音になるので 余談に移動させました。
疑似言語というのは、自分の経験で関わったことがあるBAGLES2というCOBOL用の疑似言語です。ほとんどの人が知らないんじゃないかと思うのですが、まぁネタとして挙げてみた以上の意味はありません。
プログラミング工程がカットできるので工数削減できます、ということのようですが、BALGES記述は ほぼイコール COBOLの実装を そのまま日本語で記述したもので、しかもCOBOLで出来ることを表記表現可能な範囲で限定したものです。
プログラミングレスというよりも、実行可能なBAGLES設計書を ユーザーが要件定義として書いてくれるのなら、それは ユーザーがプログラミングをしているのと同じ、です。
ようは、設計書の形をした日本語で書けるプログラム言語です。
これは、汎用機開発において、プログラミング設計という工程で疑似プログラミング記述して、それをCOBOLに書き換える(コーディングする)という開発プロセスならではの話だと思います。
この COBOLに書き換える っていうのは無駄だよね、っていう意味にはなりますけど、これをもって業務仕様から自動生成って言って良いのかなぁ、という思いはあったりなかったり。
前向きに言えば この日本語で表現した疑似言語を通じて、情報システム部の人たちは実装を把握しようとしてくれていましたし、その土壌にはなっていたと思います。もし、これが C とか Javaで 書かれたものだったら 丸投げになるか、Javaなのに COBOLみたいな実装を強要された可能性はあります。*26
各種定義情報を分類して記述していくので、神クラス(モジュール)ができにくい仕組みです。知らない方も多いと思いますが、設計ツール、分類ツールとして学べるところはあると思います*27。
さいごに
もともとの記事は「自動生成はソースコードではなく、テストを対象にするべきでは?」でした。
ただ、それだけだと 自分が考えていることとしては ちょっと足りていない気がして、コードの自動生成は 止めた方が良いというのも書いておこうと思いました。
思いついたときに追記・追記をしていったので思った以上に時間がかかってしまいましたが、実際に自動生成をするか しないか 別にして、いざ何かしらやろうと思ったときに「あっ、これは 止めておいた方が良いと思って整理したものだ」という回帰ができる資料にはなった気がします。