「動けば別に良くない?」という新人エンジニア
はい。2年前私ですね。
転職用のポートフォリオである食事管理アプリを完成させた私はダニング=クルーガー効果
によりイキリ散らかし、
その4ヶ月後に大変苦労したのを覚えています。
前回、
良い質問をして自己成長に繋げるためのあれこれ
という記事を書きましたが、それに並んで若年エンジニアの悩みの種シリーズ
です。
ヒーヒー言いながら動くコードは何とか描けるようになってきた。
「けど、どこに何書いていいかわからへん」
という悩みは誰もが経験するのではないでしょうか。
コードレビューの際に「その処理はこっちに書きましょうか。」
とか言われるけれどなかなか、しっくりこず。
設計について勉強してみるように勧められても、何から手をつけていいのかわからなかったのを覚えています。
何となくやらないといけないのはわかるけど、「何故やどうして」がはっきりしないので,
モヤモヤしている時間はしんどかった気がします。
この記事ではどういう心理状態だったのかを書いてみたいと思います。
現在そんな心理状況の方
や新人エンジニアの教育にあたっている先輩エンジニアの方
の参考になれば幸いです。
そもそもなぜ設計するか
そもそもなぜするのか分かっていないと話が逸れがちになります。ここが一番大事かもしれない。
ユーザーの要求に応え課題を解決するプロダクトであり続けるため
これは家を建てる時とかも同じではないかと思っていますが、設計というのはあるべき姿を体現するための計画
だと思っていて、完成物を正しい方向に導くためのノウハウや決め事だと思っています。
加えてソフトウェア開発のソースコードを書くという行為においては、家を建てるといったときに比べて、「あり続ける」
という部分の比重が重くなると思っています。それは完成物に絶えず変更が加わり、新しい機能がどんどん追加
されることが想定されます。家を建てるときにリフォームのしやすさはそこまで重視されませんが、システムを作る工程においては、使い回しのしやすさ、拡張性、保守性が高いものがいいとされます
。(もちろんパフォーマンス的な観点もありますが)
安全に変更が行えることを担保するため
システム開発において変更を加えるというのはその機能を変えるために純粋なコストがかかるだけでなく、デグレ(もともと提供できていた機能に不具合が出る)恐れがあるため、変更のしやすさという観点がのちの開発効率に影響する。
変更を加えることが難しいコードは技術的負債
となります。
辛いですね。
なぜピンとこないのか
技術的な話題や目先の問題で一杯一杯になり私の頭の中で先行してソースコードを設計する目的を見失っていた。
私がエンジニアになりたての頃は「ユーザーに価値提供する」という目的
と、設計的な話題(特に具体的な戦術的な話)
がうまく結びついておらず、なぜそれをする必要があるのかよく分かっていませんでした。
「こっちに変えましょうね。」
と技術的に何となく説明を受けると、そっちがいいのはわかる気はするけれど、言語化するのも難しいし、次同じ状況があっても、あるべきところにコードを配置することができる気がしなかった
のを覚えています。
結論としては常にユーザーへの価値提供にどう影響してくるのかまで結びつける
ことで、なぜその設計手法が取られるのか分かってくるというのが徐々にわかるようになってきた気がする。
というのが私なりの解なのですがその辺を書いていこうと思います。
痛みを感じたことがないので、必要性が感じられなかった
これは結構ありがちですが、転職用のポートフォリオを作っているような段階においてなかなか技術的負債を抱えるような規模のプロダクトを作る人はいない。加えてユーザーが自分自身ということが多いので機能がユーザーの求めているものとズレてしまう。
といったことは起こりづらい。
しかしながら現場で扱うような業務のコードは巨大
です。
例えるならミニ四駆作って転職用の成果物として提出し、業務臨んだら自家用車を組み立てることになった
ぐらいのインパクトがあったのを覚えています。
エンジニアになって、良くないコードを目にしたときに反面教師的に設計を覚えていったり、どこに何を書くかを気をつけるようになった方も多いのではないかと思っています。
設計を軽視するとどうなるかという話
「ソースコードをどこに書くべきか」ということを軽視して、コードを描き続けるとどうなるかという例え話で説明してみる
図書館の本をソースコードに見立ててみる
図書館のユーザーに対する価値提供としては「来館者が欲しいと思った本を借りられること」
と定義します。
来館者が返しにきた本をとりあえず忙しいので、全く置き場を考えずに、目の前の棚に置いていったとする。
館内にあれば物理的には貸し出し可能な状態になるので短期的は業務は完了します。
最初のうちは返却物である本の名前と場所は覚えていられるので、来館者に本の場所を聞かれたら答えられるかもしれないが、それを一年後に覚えていられるだろうか。
その管理方法を一年つづけた時に本の場所に秩序がなくなり、どこに何があるか分からないという状態になるのは想像可能であると思います。
これでは「来館者が欲しいと思った本を借りられること」
という価値提供ができる状態を維持できなくなりますよね。
新人エンジニアが何故「動けばいいじゃないか」と思ってしまうかを考察してみる
上記の例を用いたことで、大雑把にソースコードの秩序は保たれるべきというのは理解できたと思います。これに対して何故最初から秩序を保とうという気になれなかったかを考えてみる
。
それはエンジニアになるまでに作ったソースコードの量が本の整理に例えた時にせいぜい家の本棚程度の分量だったからだと思っている。
家の本棚は自分で置くので大体場所は覚えているし、万が一場所が分からなくても10秒ほどで目的の本はみつけられるでしょう。
ソースコードも同じで、作ったのも自分、使うのも自分で機能をよく知っている上に規模も小さいのでどこに書いても、改修したい機能に対するコードは秩序がなくてもすぐに見つけられる。せいぜいアイウエオ順になっていれば見やすいかなという程度です。
家の本棚で問題ないからと言って、それが図書館に並んだ時に全ての本がアイウエオ順で並んでいたらどうでしょうか。
本の題名を知る人にとっては問題ないですが、ミステリー小説を読みたいという動機で図書館に訪れた人にとって不都合が出る
のは容易にわかるでしょう。
規模や複雑さ違えばベストな設計は変わる
エンジニアになる前に学習していた頃に下記のように学んだ人も多いのではないでしょうか。
MVCフレームワークではロジックはモデルに書きましょうね
Rails由来の思想だと思いますが、ファットコントローラーだめ、ファットモデル最高
みたいな風潮は少なからずあると思います。
これが業務に入った時にはどうかというと。
パイセン2号「データの取得処理はリポジトリに書いてって言いましたよね?」
私は思いました。
「いやモデルに書けっていいましたやん!!」
これは結構強烈に覚えています。
初めてプログラミングスクールやメンターへの不信感が出るタイミングではないでしょうか。
(不信が募るのは初めてじゃないかw)
何故、LaravelやRailsに得体の知れないクラス名が出てくるかというと、
本の例えにヒントがあるのですが
規模が大きくなると物事の管理はより細かい概念で区別されます。
本の管理においては図書館になるとアイウエオ順以外に「ジャンルで分ける」
ことが加わりました。
つまり
家の本棚程度の規模であればMVCで十分だけど規模が大きくなるとより細かい粒度の概念でソースコードを管理しないとユーザーの要求に応えられなくなる
ということですね。
これはシステムの存在意義が低下していますよね。
何故いきなり聞いたこともない階層クラス名が出てくるか
原因については、想像できるようになったのではないでしょうか。
で、どうして行けばええの?
コードや本格的な難しい話に入る前に、具体例を用いながら持っておいた方がいい考え方について記します。
判断の回数は少なく解釈の余地がない方がいい
これはソースコードでもイメージしやすいと思っていますが、ソースコード上のifやelse
に関してはできるだけ少ない方がいいです。
書いている最中はまだ何とかなると思うのですが、複雑にif文が連なっていると、
書いた本人でさえ、後から見ると訳が分からなくなります。
人とのコミュニケーションにおいて
これはプログラミングに限ったことではないと思っていて、私はエンジニアになる前に流通業界で営業と配送の手配や管理業務を行っていたのですが、人と人が関わる中で認識の違い
が発生します。
作業者に日々作業指示をして仕事をしてもらうのですが、同僚の中にはトラブルを多く抱えている人がいました。
原因を聞いてみると
- 認識に齟齬があり
- 相手の勘違いで〜
という話をしていました。
この時に私が考えていたことは
指示の与え方次第で認識の齟齬の起きやすさは変わるということです。
つまり、作業者に対して指示を起こす際に、指示を出す側の人間の問題で、
作業者が同じ主体であったとしても認識齟齬を起こしミスを起こす確率が変わるのです。
具体的なことを言うと曖昧な表現や判断の機会が多いとそれだけ意図とは違う動きになってしまうリスクが高まります。
「青森より多くのリンゴを仕入れた」
というフレーズについて考えてみます。
この文は
- 青森から多くのリンゴを仕入れた
- 青森県よりも多く(長野県から)りんごを仕入れた
のように2通りの解釈が可能です。つまり聞き手に前後の文脈から解釈・判断する余地を与えています。
人とのやり取りは助詞の選択・比較対象の省略というのはかなり細かい部分になりますが、
この辺に気を配っているかどうかで相手がミスすることを防ぐことができます。
(以前、人とのコミュニケーション寄った話の記事を書いたので興味ある方はどうぞ→ 良い質問をして自己成長に繋げるためのあれこれ)
ソースコードにおいて
ifなどの条件分岐のネストは読み手に解釈の余地を与えます。
避けるためにはNGの条件を先に判定して該当のものを返してしまうようなテクニック的要素と
クラスや関数が担う範囲が大きすぎるかもしれないという問題が考えられます。
後者は小さい粒度の概念に分離できないか検討する必要があります。
小さい関心事に分ける事で限定的な名前をつけることができるようになり、
名前以上の役割を担っていることに気づきやすくすることができます。
Martin Fowlerはリファクタリング 既存のコードを安全に改善するにおいて次のように記していいます。
Any fool can write code that a computer can understand. Good programmers write code that humans can understand.
コンピューターが理解できるコードは誰でも書ける。優れたプログラマは人にとって分かりやすいコードを書く
この上記のフレーズが個人的に結構好きなのですが、技術者にとってコード書く
という行為はコンピュータに対して命令する行為
ですが、
そのコードに手を加えて改修・改善を行う際の主体は「人
」なのだということを再認識しました。
誰が見ても(半年後の自分を含め)ソースコードから期待する動作が分かりやすく記述されていることがいいコードを書くということで、
その感性を磨き続けなければならないと感じてきます。
疎結合な状態を保つ
疎結合な状態を保つ
ですが依存性を排除して機能として独立していることが望ましいということだと考えています。
これは最初に書籍などで見た際にイメージがつきにくかったことを覚えています。
例としては
ネジが錆び付いてタイヤ部分を取り外すことができない自転車
があるとします。
この時、自転車本体とタイヤの関係は結合状態を切り離すことができず、依存関係にあると言えると思います。
この自転車でタイヤのフレームが歪んでしまった場合、錆で本体との取り外しができないことによって
この自転車の利用者は歪みという負債を抱えたままこの自転車に乗り続けるか、自転車を買い換えるかの二択を迫られます。
部分的な機能として切り出しができず依存している状態というのは、痛みに耐え続けるのか、
影響範囲の大きい修正や全体のリプレイスを迫られることになります。
他のクラス中身の詳細を確認して使うより、機能として提供されているものを使う
病院というクラスがあり、医療知識の集積場と考えます。
病院にもし診療という機能がなく、医学書から自分の症状に合う薬を調べて患者が薬を選択しなければならないとした病院として使い勝手が悪いですよね。
しかも、医学書の改訂版が実は出ていて書き変わっていた、、、みたいなことが起こり得るとしたら何を信用していいのか分からなくなりますよね。
症状からお医者さんが判断して治療は薬の提供を行ってくれるから、患者は調べるコストも医療の知識がなくとも病院を利用することができます。
これをソースコードに直すと料金のクラスがあって、大人料金と子供料金の切り分けられる年齢を返して
使い手側で判断のロジックを書いてしまうと、同じような判定のロジックをいくつも書くことになり、変更の際にも修正のコストが跳ね上がります。
料金のクラスの責務としてfunctionを持ち、引数に年齢をもらって料金を返す機能を持った方が、
使い手はただ使うだけという状態にしておいた方がロジックが点在せず保守性の高いコードになります。
設計について興味が出てきた方に
具体的にちゃんと学びたいという思いが出てきた方は下記の書籍から始めることをお勧めします。
難しい表現を用いずに、オブジェクト指向の設計の考え方が自然と身につくように書いてあり、
私は購入してから10回以上読んでいます。