その if で本当にいいの?
今年も ミライトデザインのアドベントカレンダー を公開しました。
2021 年に始めたミライトアドカレですが、毎年完走して今年で 4 カレンダー目になります。
今年も完走そして 100 記事到達を目指して、みんなでたのしくやっていきたいと思います。
初日は例年どおり ほげさん ( Zenn ) です。
今日は自分の設計論のまとめ回
いままでいろいろとコーディングや設計の記事を書いたり発表をしたりしてきました。
その中でも特に今年やらせてもらった超複雑なレガシーコードの分析・設計・リアーキテクチャおよび発表の体験は、極めて深い学びを得られました。
ふとその体験が過去の自分の記事と頭の中で結びつき「根底は全部おなじことだったのだなぁ」と感じたので、今日はそれをうまいこと超簡潔にまとめてしまおうという内省企画です。
過去の記事 ↓
2022 年に自分がコーディング中に考えていることの言語化視覚化を試みたやつ
2023 年に「安易に null
を使わずちゃんと考えよう」みたいなことを書き殴ったやつ
先月発表させていただいた最近の取り組み ( 資料と動画アーカイブ )
べつにスケジュール管理をミスったから過去のコンテンツを流用して手を抜いているわけではありません。
投稿前日にネタすら決まっていないのに明け方まで飲んでいたことも関係ありません。
最初に結論「とにかく仕様」
今日の結論は「すべての根底にあるのは仕様だからそれを土台にしない限りなんも意味はないし、表面上のテクや上っ面の設計論だけ持ってきてもちゃんとはまらんぞ」です。
最近 ↑ のコンテンツを見返すことがあったんですが、先月の動画アーカイブから急に「仕様」という単語を多用しています。
それ以前の記事は、潜在的には仕様意識がありそうですがテクにフォーカスしている感じが見受けられました。
どういうことか適当な例で見てみましょう。
ある日のできごと
動いているコードに仕様変更が入ったりテストでバグが見つかったりして、if
を書き足すことがあるでしょう。
このたった 1 行の if
追加の意味することを、普段どれだけ考えているでしょうか。
なにをもとに考えれば ( 少なくとも自分なりの ) 良し悪し判断ができるでしょうか。
書くのはプログラミング言語だけど、自然言語で考える
冷静に自分に問います。
「変数の中身を変えたいから書いたんだっけ」と。
「結果を 5
ではなく 3
にしたいから書いたんだっけ」と。
「if
が書きたいから書いたんだっけ」と。
もちろん違います。
- 特定の処理をスキップしたい
- 処理を変えたい
- 値を変えたい
とにかく、コードが書きたいわけではなくて、やりたいことがあったはずなんです。
解像度をちょっとあげて考えてみましょう。
やりたかったことは「特定のユーザの発送日は特別な計算で決める」です。
仕様変更やバグ報告が来るときコーディングをしているときは「ここに if
入れりゃいいな」と考え、それをそのまま書いてしまいがちです。
しかし一息ついて落ち着いてから、自然言語で考えて今のコードにどう反映するかを考えなければいけません。
自然言語とは仕様
このとき自然言語は無から湧き出ているわけではありません。
なんらかの機能 ( 価値 ) を提供する背景には、かならず問題意識と仕様が存在します。
動いているコードや要望者の発言や過去の Slack などをよく読むと、絶対に ( 当事者すら気づいていない ) 自然言語を見つけられます。
「特定のユーザの発送日は特別な計算で決める」
「メール本文には発送予定日を記載する」
「請求日は発送予定日とする」
などが見つけられたら、それが仕様です。
値とは、仕様の単語の部分
上のような分析をせず場当たり的に if
を書き足していくと、次のようになるでしょう。
しかし上のような自然言語で考えていると「あれ、だれもそうは言ってないけど、これってつまり発送予定日というものについて話しているのでは」と気づく瞬間があります。
そうすればこの処理は「発送予定日を元に受け付け処理を行う」というブロックとして説明できるようになります。
「発送予定日」という値も見出せました。
説明できるものには名前が付けられて、名前があるものは安定します。
「発送予定日」のように名前があるものは妙なところで分割されず、安心して使用できるようになります。
次のような思考は、プログラミング言語による思考です。
- 「
if
が 3 回になっちゃうからまとめたほうがよくないか」 - 「
private fun
にすると別のところでコードが重複しそうだし、引数にしないか」 - 「型が
Date
だと汎用的すぎるからShippingDate
クラス作らないか」
それを次のような自然言語の思考に切り替え、意味と名前を見出すのがポイントです。
- 「発送予定日ってのがあってそれをもとに動いてるっぽいな」
処理とは、仕様の関連の部分
動画の中でも言いましたが「なんとか番号とは 4 桁の数値である ( 必須 )」は仕様のただの一面でしかありません。
これは静的で、なにとも関連せず、用途も示していません。
単語までしか考えられていないと、今回のケースで言えば「発送予定日があることはわかったが、どう生まれるかはわからない」となってしまいます。
これを解決するために分析と設計を進めると「発送予定日計算」という処理が見出せます。
「発送予定日計算」処理の分析を進めると「優良ユーザ」という単語があったので「ユーザ種別」という単語を見出し、それにもまた判定処理がありました。
このように値と値をつなぐかたちで処理を見出していきます。
分析中にこれがうまくできていないと「名詞はいっぱい出せた、クラス図も書いてみた、けどすべての要素が独立しているしコード書ける気がしねぇ」となってしまいます。
処理は明確な名詞として普段の会話でやりとりされてないことが多く、見つけるには深い洞察が必要になります。
テクは実現手段のひとつ
最初に if
を書き足そうとしたとき、コードのことだけを考えれば対応方法はいくらでもありました。
場当たり的に書き足すのか
「あー、じゃあ if
は 3 回必要っぽい」と先読みして書き足すのか
ちょっとカンのいいひとは 3 回の if
ではなく if - else
で書き分けるのか
もうちょっとカンのいいひとは if - else
をみて「優良ユーザ用処理」というものに気づくのか
プログラミング言語の経験と嗅覚で if
ではなくストラテジパターンなどで対応するのか
受け付け処理そのものを DI コンポーネントにするのか
どれでも動きます。
それぞれのコードに、コードだけ見たときの良し悪しがあります。
だからこそ仕様という拠り所を持たないと、自分の判断に自信が持てなかったり議論が並行線になったりしてしまうのです。
今回考えたこの構造も、この仕様のときの対応案のひとつでしかありません。
仕様を拠り所に毎回考えるしかないので「このかたちのコードはこういう if
文にしましょう」という普遍的な答えはないのです。
テクは当然知らなければ使えませんが、テクを覚えれば使えるというわけではないのです。
テクを選ぶ根拠は、意外とテクじゃない場所にもあるんですね。
おわりに
名前を見出して、単語は値として成立する最小で不可分の単位でクラスにし、関連は処理として設計実装する。
今年はそんなことをすべての場面でずーーーっと考えながらレガシーシステムと戦ってきました。
ほかの例に興味を持ってくださったかたは、ぜひ動画アーカイブをご覧ください。
ただちょっとコードを書けばいい状況で見落としていた仕様にまで意識を戻すというのは、水中で限界まで息を止めたときに「あと一秒」と言われるような感覚に似ています。
「いいじゃん」というサボりたい気持ちもいっぱい湧きましたが、なんとかふたつの大きなレガシーコアドメインを討伐するに至りました。
期待値を与えられてそれどおり動くコードを書くというのは大前提で、求められていることも求めていることもそこではないはずなんですよね。
AI でコーディングがサポートされる時代ならなおさらです。
コードを書くのが多少 AI になっても、コード ( の意味 ) を読むのは人間です。
人間が読むのだから、プログラミング言語ではなく自然言語で考えてコードを書く。
そういうスキルは求められ続けると思っています。
いかがだったでしょうか。
事実をまとめる系と違い、私見をまとめる系は書き終わったあとそわそわしますね。
明日は弊社の takuma です。
トランザクション分離レベルについて調べてまとめているようです。
僕もちょうどそのへんの理解を深めなきゃいけないなと思っていたところなので、一緒に学べるのはとてもたのしみです。
明後日以降もおもしろい記事がたくさん控えているので、今年も ミライトアドカレ を最後までおたのしみくださいね。
Discussion