- バックエンド / リーダー候補
- PdM
- Webエンジニア(シニア)
- 他19件の職種
- 開発
- ビジネス
和田 卓人さん(t_wadaさん)に「予防に勝る防御なし - 堅牢なコードを導く様々な設計のヒント」を社内で講演いただきました!
こんにちは、ウォンテッドリーDev Branch VPoE 室長の髙橋です。
ウォンテッドリーの開発組織であるDev Branchでは、外部から有識者を招いて勉強会を開催したり、技術顧問として知見を取り入れるなど、プロダクト開発により強い組織となるためにさまざまな施策を行っています。
今回、「テスト書いてないとかお前それ @t_wada の前でも同じ事言えんの」
でおなじみのt_wadaさん(和田 卓人さん、以下和田さん)に「予防に勝る防御なし - 堅牢なコードを導く様々な設計のヒント」をウォンテッドリー向けにカスタマイズして講演いただきました。
このストーリーでは、今回の講演の経緯から社内の反応・Q&Aまで、講演に関する詳細をご紹介いたします。
社内講演のきっかけ
事の発端は、弊社のVPoEである要(X : @nory_kaname)より、外部エンジニアを招いて勉強会を開催する旨の問いかけからでした。
このとき、エンジニア向け講演といえば和田さんということで引き合いに出されたのですが、
という流れで、和田さんに講演をご依頼することとなりました。
そこで、まずは社内での講演に先立ち、和田さんと事前にお打ち合わせをさせていただきました。お打ち合わせの中で現在のウォンテッドリーに合った講演をということで「予防に勝る防御なし - 堅牢なコードを導く様々な設計のヒント」を講演いただくことになりました。
「予防に勝る防御なし」は、元々PHPのイベント向けに用意された内容でしたが、Rubyを主に扱っているウォンテッドリー向けにカスタマイズいただき、今回の講演が実現いたしました。
講演当日の様子
当日の講演の内容については、ウォンテッドリーのエンジニア向けにRubyやTypeScriptでの事例を交えながら、参加者のコメントを拾っていただきつつ進めていただけました。
(※ こちらは今回の講演の元となった講演のスライドとなります。こちらをカスタマイズして講演いただきました)
社内Slackの反応
参加者は講演を聞きながらSlackでその場で感じたことを実況し、それを和田さんが拾う、という形で行い、リアルタイムにコミュニケーションを取りました。以下に一部引用いたしますので、チャットのライブ感を感じていただければと思います。
防御的プログラミングへの誤解
- つらいコード
- あーこれはかいちゃう
- 例外握りつぶすとか
- よしなにやってくれるコードでハマるとつらい
- スメルってやつ?
- 消臭剤。なるほど
- 消臭剤、良い例えだ…
- 無理に消臭剤入れて逆に臭くなるやつ
- 対処療法ではだめだよ的な
- 傷が深すぎてとりあえず消毒しとくかーって時ある
- 堅牢という言葉の意味的に絆創膏の例とてもしっくりくる
コンストラクタをもう一度呼ぶと破壊的変更ができてしまう
- な、なにーー
- なんだそれ
- コンストラクタとは・・
- コンストラクタなのに
- PHPヤバ過ぎる
(Slack上では爆速でRubyやPythonについても検証が行われました)
例外と表明の使い分け
- 例外はランタイムなイメージがあるすね
- Javaも似てますね
- (そもそも PHP は昔エラーと例外が別扱いだったような気が…)
- Goのエラーにもこういう階層が欲しい(知らないだけかもだけれど)
- 予期された例外を予期していないエラーとして再解釈する必要がある場合もありますね。
- 「良識ある実践の積み重ね」感がすごい
講演会終了後の質問と回答
講演会の終了後に、和田さんへの質問の時間を設けました。活発に質問が行われ、非常に良い知見が集まりました。こちらも一部引用いたしますので、お読み頂いている皆様の参考となれば幸いです。
Q: 手を出せない外部システムの影響を防ぐには?
OSのAPIや外部APIなど、利用している側が手を出せない外部システムの仕様が相容れない場合、構造を隔離する腐敗防止層的なレイヤーを作るしかないのでしょうか?他にも良い手法があれば知りたいです。
和田さんからの回答
他に良い方法は残念ながら無いと思います。このような場合でも腐敗防止層を用意します。
外部APIなどはIssueで修正を働きかける、ということはありますが、自分たちを守るのであれば腐敗防止層を用意するのが良いでしょう。
Q: 設計手法の実践にはどうすればよい?
今回取り扱った設計手法について、実践して試すには似た問題に当たるまで覚えておく必要があるかと思います。こういったことを実践していくにはどうやっていくのが良いでしょうか?
和田さんからの回答
1人で覚えておくのは無理があるので、スクラムのスプリント周期ごとに、前回の周期での振り返りや思い残しを供養する時間を設ける、というのを現場ではやっていました。実際には、翌スプリントの午前中2時間を使い、モブプログラミングで「こう出来たはず」「こうすればよかった」という振り返る時間を設けるなどしました。
Q: 学習テストの活用について
学習テスト(学習用テスト)をうまく運用・活用している例を知りたいです。
学習テストとは「利用している言語やライブラリの挙動に関して書くテスト」という理解ですが、アプリケーションコードと同じ場所に書くのは違和感があります。
和田さんからの回答
実際に学習テストを書く場合は、テストにタグを付けて運用しています。
実際の使い方は、外部ライブラリが不安定であったり仕様変更が多い場合に、防御のために利用している部分の学習テストを書いて、テストが失敗すると動きが変わったと判断しますので、テストは同じ場所に書いてタグで判断します。
ただし、学習テストをやりすぎると、外部ライブラリのテストを自分たちで書いている、という本意でないことが起きるので、ライブラリの挙動理解や、サブセットに関して書くというのをお勧めします。
Q: Simple と Easy の境界は?
(コードレビュー観点においても)どこまで Simple でどこから Easy なのかの境界やそういった勘所を養うためにはどうしたら良いでしょうか?
和田さんからの回答
Simple は Easyより見分けやすいです。Simple は少なさ、短さ、が概念としてあり、機能が多ければその時点で Simple ではありません。Simple とは 脳の認知資源を消費しないものであり、組み合わせて利用することになるので Easy にはなりません。一方 Easy はコンテキストを選びます。Androidアプリの開発は Easy に作れるように開発環境が整備されていますが、Android開発そのもののコンテキストを知らないとその Easy さがわからない、といった具合です。
また、OSSのフレームワークは Easy 狙いのものが多いです。基本的にソフトウェアは同じ時間で多くのことができるかで勝負しており、設計的に筋が良くてもより多くの時間が掛かるようであれば競争力が落ちてしまいます。多く使われている・評価されている・競争力のあるソフトウェアは Easy 寄りの設計がされていることが多いです。
例えば、PHPのフレームワークであるLaravelは、Symphony という別のフレームワークを利用していますが、Symphony は Simple 寄りで手数が多い・Laravel は Symphony を使ってEasy なレイヤーを実現している、という違いがあります。
Q: 考慮しきれない例外処理について
モバイルアプリ開発をしています。(主にKotlinで検査例外のない世界です)
モバイルアプリは、UI | Presenters (ViewModel) | Use Cases | GraphQLといったレイヤードアーキテクチャになっています。所謂CleanArchitectureの同心円におけるPresentersレイヤーでcatch (e: Exception)のように、Use Casesレイヤー以下の例外をすべてキャッチして、エラーのUIを出すことが多いです。
このとき、例外の場合分けが難しいと感じています。データ登録APIに対する例外処理を考えたとき、通信エラーならリトライ、重複なら画面を閉じるなどPresentersレイヤーで例外の型を調べてUIを出し分けることはできますが、様々な例外があり、幅が広くて考慮しきれない場合が多いです。GraphQLクライアントの挙動が変わると実行時の挙動が変わったりもします。
Use Casesレイヤーなどで例外を拾い、独自の例外に変換することも考えますが、Presentersレイヤーは堅牢になる一方で全体的には冗長になり、どちらにしてもGraphQLクライアントの挙動変更には対処しづらいと感じています。こういった例外処理の落としどころはどうすべきでしょうか。
和田さんからの回答
GraphQLクライアント固有の事情に内部が影響されてはいけません。GraphQLが起こす例外はControllersが内側の例外に変換し、自分たちで定義した例外のツリーに当てはめ、クリーンアーキテクチャーにおけるUse Casesより中から外に出るまでのダメージを起こさない、とするのがお勧めです。
Use Cases レイヤーで例外で拾うのではなく、外から中には行ってくるときに、Controllersで内側の例外に変換しながら入っていくようにします。こうすることでGraphQLで挙動が変更されていても内部にダメージがない仕組みとなります。
Q: 啓蒙への思い
今回の講演で取り上げられた問題は、言語(特に関数型言語)によっては言語仕様でカバーされている部分も多くありますが、問題解決している言語を利用するのではなく Ruby や PHP などのオブジェクト指向での開発を啓蒙するのにはどういう思いがあるのでしょうか?
和田さんからの回答
現場では、今回で取り上げたような言語が既に多く導入されているからです。新規プロダクトの場合はより堅牢な言語で書いていけばよいですが、既に開発されてプロダクトにはレガシーが多く残っており、また日々増築されているような状況です。増築する際にもっと堅牢にする、触ったところはきれいに書き直す、ということをしていかないと、どんどん辛い状況になっていきます。
個人としてプログラマーとして使っている道具と、仕事場でお客様が使っている言語は違います。新しい言語の啓蒙は他の人がやっていますので、そうではない場所で「こうできる」というのを話していこう、と考えています。
Q: バグを示すエラーを定義するとは
コードのバグを示すエラーを定義する、というのがピンときませんでした。バグが起こるということを予期してエラーを配置しておくということでしょうか?どういったときに使用するのか例を教えていただけると嬉しいです。
和田さんからの回答
質問の通りで、バグが起こることを予期して配置し、バグのルート例外を作っておくということです。多くのプログラム言語はバグと例外をはっきり区別して定義していないため、二択で判断できると次の判断ができるので運用上好ましい、ということがあります。バグを示す仕組み assert や panic がある言語もありますが、言語の仕様上これらが明確でない場合、実装で相当するものを定義しておくとそこで大きくニ分岐できて運用品質が上がります。ただ、細かいバグを示す例外をたくさん作った方がいいというわけではなく、バグのルート例外を作ってバグだと判断できるだけでも、二択で判断できると運用品質が上がる、というものです。
おわりに
今回は、和田 卓人さん(t_wada さん)をお招きし「予防に勝る防御なし - 堅牢なコードを導く様々な設計のヒント」というテーマで講演をしていただきました。 講演後メンバーからの質問に対しても、予定した時刻を大幅に超えてご回答いただき、大変有意義な時間となりました。また、今回の講演会をきっかけに、エンジニアから今後の開発に繋がるようなアクションも動き出しています。
ウォンテッドリーでは、今後もコミュニケーションやスキルアップの場として、定期的に社外の方を招いた勉強会を企画していきます。その際はまたこのような形でご紹介させていただきますのでお楽しみに。