どんな記事か?
こちらのイベント参加の記事になります。
筆者自身の経験をもとに、コードのシンプルさを保つために難しい技術的な部分ではなく、誰でも意識しだいで取り組めて、プログラミンをする上で重要だと感じたことを記事にさせて頂きます。
KISSの原則
いきなりですが、KISSの原則をご存知でしょうか。
有名な原則のうちの1つで、「Keep it simple stupid」の頭文字をとったものです。
直訳すると「簡潔にしておけ、この間抜け」となりますが、「シンプルさを追求すべきだよね」という原則です。
コードが複雑になると、複雑さ自体がリスクとなります。
ここでいうリスクとは以下を指します。
- 拡張性が低く使いづらい
- コードの可読性が低く読みづらい
- 開発・保守のコストが高い
コードが複雑になりやすいケースとは
では、コードが複雑になりやすいケースとはどんなケースでしょうか?
もちろんプロジェクトや環境によって異なりますが、大きく分類すると以下の4つかなと思います。
- 仕様そのものが複雑なケース
- 言語やフレームワークへの知見が少なく、複雑になってしまうケース
- リーダブルコードや設計(ここではレイヤーを指します)への知見が少なく、複雑になってしまうケース
- 前提が間違っており、間違ったまま進めた結果、コードが複雑になってしまうケース(今回取り上げるケース)
それぞれのケースを解説
それぞれのケースを解説いたします。
1. 仕様そのものが複雑なケース
まず、「仕様そのものが複雑なケース」です。
大規模なアプリなどリッチなものを作ろうとすると、どうしても仕様が複雑になってしまいます。
仕様が複雑である場合、あたり前ですがそれを実現させようとするコード自体も複雑になりやすいと言えます。
対処法
交渉できる余地があれば、要求レベルをなるべく落とさずに仕様を簡単な方向へ倒すなど対応が必要です。
交渉できる余地がなければ、できる範囲で複雑にならないように実現するしかありません。
また、ハードルはかなり高くなってしまいますが、頭の良い先人たちがプログラマが遭遇する同じような問題を解決すべく、パターン化された「デザインパターン」を適切に取り組むことも手段の1つだと思います。
2. 言語やフレームワークへの知見が少なく、複雑になってしまうケース
続いては、「言語やフレームワークへの知見が少なく、複雑になってしまうケース」です。
機能を実現させるコードを簡潔に記述する術がわからず、「あぁでもない、こうでもない」とゴニョゴニョ遠回りしてしまうケースです。
(簡潔 = ワンライナーではございません。筆者は過度なワンライナー否定派です)
対処法
これは単純に経験と知見を蓄えていくしかないため、自分で色々勉強しながら、優しい先輩に教えてもらいつつ、蓄えていきましょう。
新しい気づきや教えてもらったことは、忘れないようにしっかりメモして引き出しを増やしていきましょう。
3. リーダブルコードや設計への知見が少なく、複雑になってしまうケース
続いて3点目は、「リーダブルコードや設計への知見が少なく、複雑になってしまうケース」です。
設計とは大きな枠ではなく、レイヤーのことを指しています。
レイヤーについては深堀致しませんが、View層であれば「関心ごとが見た目だけになっていて、ビジネスロジックとは切り離されているか」、などです。
つまり、「適切な関心の分離」がなされているかになります。
また「リーダブルコード」とは、オライリーから出版されている書籍です。
リーダブルコードには、命名についてや単一責任の原則などすぐに取り組めて且つ、効果の大きな手法が数多く記載されています。
個人的には、全プログラマーにおすすめしたい書籍で多くの学びがあります。
対処法
レイヤー云々は全体を見渡す必要があるため、先輩にしっかりと教えてもらいながら、ぼんやりとでも意識できるようになれれば良いかなと思います。
最初の頃はリーダブルコードの手法を実践するだけでも、コードのシンプルさを保てるようになると思います。
筆者がかなり初期の頃に、簡単にまとめた記事がございますので、リンクだけ載せておきます。
4. 前提が間違っており、間違ったまま進めた結果、コードが複雑になってしまうケース
最後に、今回取り上げるケースになります。
こちらに該当するのは、仕様が複雑ではないのにコードが複雑になってしまうようなケースが該当いたします。
難しい技術的な話ではなく そもそもやろうとしている前提が誤っていれば、当然アウトプットされるものも間違っているよね という内容です。
例えばですが、「仕様を正しく理解しておらず、なんとなくコードを書き始めたらコードがごちゃごちゃになってしまった」など。
対処法
では、前提が正しいかどうか? をどうやって判断すれば良いのでしょうか?
ここからは、筆者が有効だと感じた、「クリティカルシンキング」について述べさせていただきます。
クリティカルシンキングとは?
まず、クリティカルシンキングとは?ですが、批判的思考法 と呼ばれています。
批判とは決して「攻撃的に批判する」ということではなく、ソースに対して常に疑問を持つ思考法の1つです。
そのため、目的を常に意識して「そもそも何を作ろうとしているのか?」と一度立ち止まって、本質を深堀していく思考法です。
出典元)https://manetore.net/glossary3815/
クリティカルシンキング特徴
クリティカルシンキングの簡単な特徴です。
- 批判的思考法
- 攻撃的という意味ではない
- ソースに対して常に疑問を持つこと
- 目的を常に強く意識する
- そもそも何のためにやるのか
- そもそも何を解決させたいのか
- 考え方のクセを理解する
- 本質まで考え続ける
- 目的達成のための思考法などと呼ばれる
ロジカルシンキングとの違い
思考法のフレームワークとして「ロジカルシンキング」がございますが、ロジカルシンキングとクリティカルシンキングとの違いについてです。
出典元)https://dyzo.consulting/5222/
上記図を見ていただいてわかるように、ロジカルシンキングは複数のソースや前提から積み上げて結論を導き出したり、逆に1つの問いからツリーを複数生やして思考を広げたりする手法です。
一方のクリティカルシンキングは、「そもそもの前提が正しいのか?」を常に問い続けます。
根本や前提が違えば、導き出されるものは全く違ったものになってしまう
ロジカルシンキングでいかに筋の通った結論を出したとしても、前提が間違っていれば導き出されるものも当然間違ってしまいます。
仕様が複雑ではないのに、コードが複雑になってしまうのは経験上このケースが該当すると感じます。
クリティカルシンキングで前提を見直してみる
クリティカルシンキングを用いて前提を見直す際に、筆者が留意している点がございます。
- そもそも何を実装 / 解決したいのか
- 仕様自体を正しく把握できているか
- 誤った認識で誤った機能を作ろうとしていないか
- 仕様が複雑すぎるのであれば、要求レベルを落とさずに仕様をシンプルな方向へ打診してみる
- 何を実現させたいのか、仕様を正しく理解できていれば仕様レベルでの打診ができるようになる
- 仕様自体を正しく把握できているか
- そもそもコードを書いているレイヤーは正しい場所か
- 例:View層にビジネスロジックが混じっていないか
- 使う側に知識があるとバグの元となり、保守も大変
- 例:View層にビジネスロジックが混じっていないか
- そもそも性質の違うものを無理に共通化しようとしていないか
- アクターは誰なのか?
- 元々違うドメインのものを合体させようとすると、歪になる
- etc...
これらに留意することで、コードが複雑になっている時点で、前提が誤っていないか立ち止まって振り返ることができます。
実際に筆者が陥ったケース
実際に筆者自身が、陥ってしまいコードの複雑さと格闘していたケースです。
- やろうとしたこと
- 共通コンポーネントを作成したい
- 共通側はどんなデータかを知る必要がないようにしたい
- 前提が誤っていた時の状況
- usecase層で記述していたが、デザイン(UI)があったり、componentに依存方向が向いてしまっていた
- 実装を進めていくと、共通コンポーネントなのに、ジェネリクス以外の具体的な型があったり、TypeScriptの型を実装のために捻じ曲げてオプショナルにしてしまったり、本末転倒な状態になっていた
- コードが複雑怪奇で実装にも時間がかかっていた
- 根本を見直した後
- usecase層ではなく適切なレイヤーへ置いた
- 実装のために型を捻じ曲げる必要が無くなった
- 不要なコードがなくなり、見通しがよくなった
- 結果コード自体がシンプルになった(KISSの原則)
このように、コードが複雑になっているなと感じた時点で、前提を見直していくことで、コードをシンプルに保つことができました。
まとめ
今回のまとめです。
- KISSの原則とは
- 「コードはシンプルにしよう」と言う原則
- コードが複雑になりやすいケースとは
- 仕様そのものが複雑なケース
- コードも複雑になりがち
- 言語やフレームワークへの知見が少なく、複雑になってしまうケース
- 思ったようにコーディングできなくて、ゴニョゴニョなってしまうケース
- リーダブルコードやレイヤーへの知見が少なく、複雑になってしまうケース
- 責務の分離や手法への知見が乏しいケース
- 前提が誤っており、間違ったまま進めた結果、コードが複雑になってしまうケース
- 今回取り上げたケース
- 仕様はシンプルにも関わらず、コードが複雑になってしまうケース
- 仕様そのものが複雑なケース
- クリティカルシンキングとは?
- 批判的思考法
- 攻撃的という意味ではない
- ソースに対して常に疑問を持つこと
- 目的を常に強く意識する
- そもそも何のためにやるのか
- そもそも何を解決させたいのか
- 考え方のクセを理解する
- 本質まで考え続ける
- 目的達成のための思考法などと呼ばれる
- 批判的思考法
- コードが複雑になっていれば、一度立ち止まって前提を見つめ直してみる
最後に
コーディングしていると、目の前の機能実現に向けて一杯一杯になり、視野が狭くなってしまう時がどうしてもあると思います。
しかし、客観的にコードを見つめ直してみた時に、「コードが複雑になっているな」と感じることがあれば、一度立ち止まって前提を見直してみることをおすすめいたします。
経験が浅ければコードが複雑になっていることすら気づかないケースもあるでしょう。
その時は書籍を読んでみたり、ちゃんとしたレビューを受けたりして、 まずは「気づけるようになること」 を目標に、焦らずゆっくり経験を積んでいきましょう。
諸先輩の皆さん通ってきた道です。
諦めず正しい方向性で努力すれば、必ず点と点とが結びついて「そういうことだったのか!」となる時が必ず訪れます。
「継続は力なり」です。
以上、新人プログラマ応援 イベント参加の記事でした。
コメント@n_hideyuki 0 @ashworth (編集済み) - ある西暦(整数)を入力すると、それがうるう年か、そうでない(平年)か、を判定するプログラムを作成せよ。
- うるう年と判定する条件は、以下のとおりである。
- 2022/4/28仕様変更
3 @Zuishin Source Browser https://source.dot.net
3 @lensouko 2 @lensouko 2 @ashworth 0 @ashworth 0 @ashworth 0 @ashworth
0 @ashworth 0 @Zuishin
0 @ashworth 0 @ashworth 0 @ashworth
0 @Zuishin 1 @ashworth 0 @ashworth 0 @Zuishin 1 @ashworth 0 @ashworth 0 @Zuishin 1 @ashworth 0 @Zuishin (編集済み) 2 @ashworth 0 @ashworth 0 @ashworth (編集済み) 汎化能力を最大化する特徴抽出 https://www.riken.jp
汎化性能とは? - Qiita https://qiita.com
汎化能力 - 機械学習の「朱鷺の杜Wiki」 https://ibisforest.org
0 @ashworth 0 @ashworth (編集済み)
プログラマの雑談部屋 ★200
https://medaka.5ch.net
0 @twister 0 @Zuishin (編集済み) 2 @ashworth (編集済み) 主語がない人の心理と改善方法とは?10の特徴を知れば即解決! | 特徴.COM https://tokutyou.com
0 @Zuishin 0 @ashworth 0
ベースになる別の言語からのコンバージョンを自動生成されているソースコードとか読む気が失せますよ。
また、チェーンメソッドを多く使ってなんかうまくやろうとしているのはいいけどみたいなコードも....。
1行でかっこよく書くのは初心者には読むのは相当つらいですよ。
たまにはだれかがコードメンテナンスをしてよりよいコードを目指すほうがよいかと。
中途半端にプログラミングを分かった気になっている人ほど、読みにくく分かりにくくバグが入りやすく改修が難しいコードを書きます。
例えば
A: 4の倍数の年は、うるう年
B: ただし、100の倍数の年は、うるう年ではない(平年)
C: ただし、400の倍数の年は、うるう年
という命題に対して、
と言いながら、
こういうコードを書いてしまう。条件がそれ以上変わらない前提なんです。
それ、初心者向けの演習問題だから成立しているだけですよ、っていうところが分かっていません。
業務でコードを書いていれば、
D: ただし30の倍数の年は、うるう年
E: ただし、30と4の倍数の年はうるう年ではない
F: D, Eは2025年より適用する
:
みたいな条件が後から足されてきます。
その時に
こういうコードが書かれてあると、拡張が非常にしずらい。バグも混入しやすくなる。
このやり方で条件を書き足していけば複雑怪奇になって行くのは目に見えています。
この記事の本文に書かれてある通りです。
仕様が提示されたら、その仕様に沿ってコードを書くのは業務でコードを書く場合の鉄則です。
仕様を粉々に砕いてミンチにしてしまったら、元に戻しずらいのです。
ですから、普通に仕事をしていれば
まともな人はこういうコードを書きます。
ですが、自分本位で趣味の延長でコードを書いている人は自己満足から
こういうコードを書いてしまいます。
そのことを指摘されると、発狂し始めます。@error_401とかね。
極めて基本的なコーディング手法である『ガード』すら知りません。
だから世の中のシステムは大体どれもこれも不具合だらけで『どうしてこうなっちゃうの?』というような謎の挙動をする物ばかりです。
多くのプログラマが身勝手で自分本位なために、『仕様』とは経時的に変更されていくものという当たり前の教訓が、何年経っても身についていかないのです。
まともな人の書いたコードの実例を見てみましょう。
@ashworth さん
2022-04-27 23:56 の書き込みに対して
別記事のコメントで誹謗中傷を繰り返した挙句、別の方の記事にまで名指しで他人をバカにするコメントを書くのは人として問題があるとは思わないのでしょうか?
以下は個人的な感想
他人のソースを真っ当な人間は書かないとか初心者でもやらないという人がバカにするコードって何故かMicrosoftのコードにも普通に書かれてるやり方なんですよね
問題があるコードなら沢山の人のチェックを通ってると思われるフレームワークのソースに残ってるとは思えないんですけど
ちなみに僕が初心者でもやらないと文句を言われたコードは早期リターン+ループ内でのリターンです
フラグで持ってメソッドの最後にリターンしろって言われました
フラグで持つのが嫌ならと提示されたのがメソッドの中身がelseifが延々と続ける形でした
先に記事とは関係ないコメントを書いてしまったので、こちらは記事に対しての感想です
これに対しては自分が使いやすいツール、テキストファイルでもExcelでもなんしかその作業ができる環境でインストール可能なソフトやツールの中からその使用を整理するのに分かり易いと感じたものを利用して整理することが大事かと思います
極端な話、他の人が分からなくても自分さえ分かればいいんです
勿論、そのあとで他の人にもわかるように整理し直した資料を作っておくと他の人に展開できるし、後任も理解の助けになるでしょうから人が見てわかる仕様を整理した資料は大事ですけど、まずは自分が理解しないと話にならないので
その上で、次は
とも重なりますが、仕様を整理した内容であってるかどうかの確認もしないといけません
ようは、「~ってXXってことですよね?」って確認ですね
これが漏れてると、これとこれ一緒じゃね?と判断してまとめたのが、実は勘違いからくるものだったら仕様が一つ飛んでしまうことになります
なので、新人の時は「このくらい訊かなくても判るだろって」言われても、あるいは言われそうだと思ってもこまめに聞くことが大事です
これはエンジニアとしてはベテランになっても、新しいプロジェクトに参加したらそのプロジェクトの仕様に対しては新人なので同じ様に確認が大事だという事です
もしも隣に誰か座っているのでしたら、考えをまとめる際にその人に聞いて貰うというのも手です
結構、それで解決した人も多いので
そして、この記事に書いてある「ソースに対して常に疑問を持つこと」はソースに限らず自分が使用を理解しようとしてまとめた考えにも本当にあってる?この場合は考慮の入れたけど、他の場合でまとめたパターンからだと漏れるケースはない?ってことを念頭に置いておくと比較的マシになるかとは思います
記事の見出しそのものについて
コードが複雑になってきたとか膨大になってきたと感じたら、仕様を元に処理を細分化して、ここではこれをするだけ、ってな感じで切り分けて作るかな?
例えばif文の条件が複雑化してきたと考えたなら、そこをメソッドに切り出して、ここはXXの判定処理として、細かい条件は仕様書から引っ張ってきた日本語でコメントに書いておく
ってな所かな?
@lensouko
どうしてそうやって歪めて捉えます? 性格悪いんですか?
「バカにする」って何ですか? それ、あなたの主観ですよね。
わたしは「こういうコードは迷惑なので辞めてください」としか言ってないです。
ん-と、どこから突っ込んだら良いのかわからないんですが、
Microsoftのソフトウェアがおかしな挙動する物ばっかりなので、あなたが何を主張したいのか良く分からないです。
バカにされたようなコードが満載だからMicrosoftのソフトウェアは挙動がおかしいんじゃないですか?って。
へー。ならその人は私にもそう言うんでしょうね。
多分その人、今頃仕事無くなってるから言えないでしょうけど。
@Zuishin
日本語、読めますか?
初心者向けの演習問題を晒して
とか。
とても、頭がまともな人のセリフとは思えませんね…。
というか、いつまで粘着してるんですか? 本当に迷惑行為ですよ。
まともな人はこういうコードを書きます。
に対して以下の条件
を足した場合に、
頭がまともじゃない人が書いたコードはこうなるらしいです。
知能指数の低い人にコードを書かせてはいけない理由が良く分かりますよね。
ちなみに、5chによるとミジンコという人が書いたコードらしいです。
余談ですけど
なんとかフラグって、アンチパターンで真っ先に上がってくる奴ですよ。
必要も無いのになんとかフラグが山のようにあるプロジェクトって、
大体コードがスパゲティになって炎上しています。
良い先輩に巡り合えましたね♪
うるう年の条件が変わるわけないじゃないですか。
@Zuishin
条件が変わらないコードしか書けない方には最適な命題ですね。
Zuishinさんは、変化に弱いから。
@Zuishin
だから汎化の意味も分からない。
これを見ただけでも、フラグが如何に害悪か、早期にリターンしないとどのようにコードが腐るのか、
一目瞭然ですよね。
えっと、一度汎化の意味を調べたほうがいいと思いますよ。
誰でも知ってる言葉を間違ってイキると、後でかなり恥ずかしい思いをしますから。
@Zuishin
ええと、日本語で使用している漢字って表意文字なので、
一文字一文字に意味があるので、
その文字の意味を組み合わせて言葉というのは出来ていて、
その意味を組み合わせることで日々新しい言葉が生まれているんですけど、
汎化
と言われて辞書に載っている事しか意味が分からないなら、
それはもう、日本人じゃ無いんじゃないですかねぇ?
さすがにここまで頭が悪い人だとは思ってもみなかった…。
それ、話の主題と全く関係ないじゃん…。
話と全く関係なくても、ちょっとでも噛みつければOKな人だったのか…、@Zuishinって人は…。
@Zuishin
なんだかよく分からないけど、
あなたは「汎化」が辞書に載っている用法かどうかで争いたいんですか?
頭大丈夫ですか?
本気で意味、分からなかったんですか?
あなたが汎化を知らず二文字の漢字から意味を想像して書いたというのが伝わりました。
@Zuishin
って…。
それ、あなたの事でしょ? 知らなかったから調べて悦に入ってる。
わたし、言いましたよね?
って。
意味分からなかったのかな?
分らないんだろうな、線路に沿って走る事しか出来ない機械人間には…。
言葉ってね、創り出していくものなんですよ?
あなたみたいに杓子定規のロボットには分らないでしょうけどね。
あなたは勝ち誇っているつもりらしいけど、哀れです。
@Zuishin
なんで哀れだと思うか分かっていますか?
あなたが必死で食いついているそれ、
話の本題と、まーったく関係ない事だからです。
あなたただ、あなたのくだらないプライドの為だけに食い下がってますよね?
あなた、哀れなロボットですね。
あなたが新しい言葉を生んで自分だけで使っていることは伝わりました。
@Zuishin
なんだか良く分からないけど、あなたはそれで納得した、と。
で、本題は?
まさか、ほかのスレまで来て本題と全く関係ない「汎化」の定義に拘っていたなんて事はないですよね?
本題については何が言いたいんですか???
いえ、独自の言語を使う方には説明しにくいので大丈夫です。
そもそも私の最初のコメントでほとんどの人は理解できます。
@Zuishin
何を心配しているのやら…。
私はあなたと違って漢字の一文字一文字の意味が分かりますので、
あなたが「日本語である」と考えている言葉なら何を書いても大丈夫ですよ?
それとも、怖くて書けませんか? あなたはロートルだから怖くて仕方ないのも分かりますけどねぇ。
あなたが、「条件が変わらないコードしか書けない方には最適な命題ですね。」の意味が分かっていないからこのように話がこじれているんじゃなかったでしたっけ?w
@Zuishin という人は本当に杓子定規しか出来ない方だな…。
というか、もう寝る寸前だったのでめんどくさかったから適当にあしらっていましたけど、
『言わないか?』と思って検索してみたら普通に使うじゃないですか。
用法も字で書いてある通りだ。
「学習用の簡易な演習問題に対してだけなく、広範な新たな問題についても正しく解決させる事」
使い方としても何の違和感もないでしょう。
普通に使っていたけど前に機械学習を勉強した時に自然に覚えていたんですね。
まぁ、知らなくても日本人なら漢字を見れば普通の人は意味が分かるはずですけど。
逆に言えば、@Zuishin さんは機械学習を知らないんだ。
漢字を見ても、意味が分からないんだ。
…凄いな、@Zuishinさんの日本語能力。いろんな意味で…。
@Zuishin
違いましたね。残念でした。
5chで、全く関係ない話題の中で下記のコードが笑い話として提示されていました。
この記事『あなたのコード複雑怪奇になっていませんか?』を地で行くコードです。
よく似ているでしょう?
あと、2回くらい仕様変更が繰り返されたら全く同じスメルを放ち始めますよ。
仕事としてコードを書いていれば、まともなエンジニアなら普通はこういう嗅覚が育ってくるものだと思いますけどねぇ…。
@lensouko @Zuishin
汎化
オブジェクト指向プログラミングの分野では、様々なクラスやオブジェクトに共通する性質をまとめ、それらの共通の親クラス(スーパークラス)として定義することを汎化ということがある。
パターン認識や機械学習の分野では、既知のパターンから特定の分類に共通する性質や法則性、規則性などを抽出して、未知のパターンの認識や分類に応用できる形にまとめることを汎化ということがある。
-IT用語辞典より
汎化
広い範囲に共通して適用できるようにすること。一般化。普遍化。
-weblio辞書より
閏年に適用されないので、共通部分がないんですよ。
汎化するには、まず閏年だけで説明できないとだめです。
「閏年を計算する関数」は、「仕様変更のない関数」「関数」「暦に関する関数」などのように汎化できますが、「仕様変更のある関数」に汎化することはできません。
それは汎化ではなく、すり替えと言います。
@Zuishin
「何が」ですか? Zuishin さんは主語が欠落した文章をしょっちゅう書いていますが、日本語が不自由なんでしょうか?
あなたが、他人の言った事を全く聞いて居ない事は良く分かりましたが、
何度も申し上げた通り、一般的に、
こういう風に、論理演算子でずらずらと繋げて書かれると後の人が困りますので、迷惑ですから辞めるか、
一人だけでプログラミングしていてください。
あなたが職業プログラマではなくて、ただの趣味グラマなら止めません。
ゴールデンウィークも今日を入れればあと3日くらいありますから、
この記事のタイトル『あなたのコード複雑怪奇になっていませんか?』を10回くらい暗唱して、
『どういうコードが将来複雑怪奇になっていきそうかなぁ…』と、
漠然とでも考える機会を設ければ、すこしはメンテナンスのしやすいコードというのがどういう物になるかのヒントくらいは掴めるのではないでしょうか。
まあ頑張ってください。
いや、おまえがな…。
何主張なんだろう?