- 複雑性は開発において最も危険な要素である
- 真の効率性は「80/20ソリューション」など、複雑性を避ける実用主義的なアプローチから生まれる
- テストとリファクタリングについて、バランスが取れた柔軟な姿勢を保つことが重要である
- ツール活用および読みやすく保守しやすいコードを書く習慣を採り入れることを強調する
- 過度な抽象化やトレンドを警戒し、単純さを追求する姿勢を勧める
はじめに
- この記事は、長年ソフトウェアを開発してきた中で経験から学んだことを整理した、グラグ・ブレイン開発者の考えのまとめである
- グラグ・ブレイン開発者は自分をそれほど賢いとは思っていないが、長いあいだプログラミングを続ける中で多くを学んだ
- 他の人たちが失敗から学べることを願い、わかりやすくて面白い形で気づきを共有している
- 複雑性こそが開発人生における最大の敵である
- 複雑性はコードベースにひそかに入り込み、最初は理解しやすかったコードさえ次第に修正不能な状態へと追い込む
複雑性の悪霊への対処
- 複雑性は見えない霊のように音もなく染み込み、プロジェクトマネージャーやグラグではない開発者には認識されにくいことが多い
- 複雑性を防ぐ最善の方法は「ノー」と言うことである
- 「この機能は作らない」
- 「この抽象化は導入しない」
- もちろん、キャリアの面では「イエス」と叫ぶほうが得なこともあるが、グラグ・ブレイン開発者は自分に正直な選択を重視する
- 条件によっては妥協(「ok」)も必要であり、その場合は80/20ソリューション(パレートの法則の適用)で問題を単純に解決するやり方を好む
- プロジェクトマネージャーにすべてを話さず、実際には80/20方式でやり切るのも賢い戦略である
コード構造と抽象化
- コードの適切な単位(カットポイント)は時間が経つにつれて自然に見えてくるため、初期段階の抽象化は避けたほうがよい
- 良いカットポイントは、システムのほかの部分とのインターフェースが狭いのが理想である
- 早すぎる抽象化の試みは失敗しやすく、経験豊富な開発者はコードの形がある程度固まってからゆっくり構造化を試みる
- 経験の浅い、あるいは「ビッグブレイン」な開発者は、プロジェクト初期に過剰な抽象化を試み、保守の負担を残しがちである
テスト戦略
- テストに対するこだわりとバランスが重要である
- プロトタイピングの後、コードがある程度固まってからテストを書くことを好む
- ユニットテストは初期に活用するが、実際には中間段階(統合テスト)が最も大きな効果を見せる
- E2Eテストも必要だが、多すぎると保守不能になるため、本当に必要な経路だけを少数に絞る
- バグ報告があったときは、必ず再現テストを追加してからバグを修正する
プロセス、アジャイル、リファクタリング
- アジャイルはグラグ開発者にとって悪くはなく、最悪でもないが、「アジャイル・シャーマン」へ過度に期待するのは危険である
- プロトタイピング、ツール、良い仲間のほうが実際にはより重要な成功要因である
- リファクタリングも良い習慣だが、大規模で無理のあるリファクタリングは危険である
- 複雑な抽象化を無理に導入することが、かえってプロジェクト失敗を招く
保守、完璧主義と謙虚さ
- 既存システムを理由もなく作り直すのは危険であり、「なぜ存在するのかわからない構造」をむやみに消すのは良くない習慣である
- 完璧なコードを夢見る理想主義は、現実にはたいてい問題を引き起こす
- 経験を積むほど、「動いているコードは尊重すべきだ」と身をもって感じるようになる
ツールと生産性
- 優れた開発ツール(IDEのコード補完、デバッガーなど)は生産性を大きく高めるため、深く理解することが重要である
- 型システムの本当の価値は「自動補完」とミス防止にあると強調し、過度な抽象化やジェネリクスはむしろ危険だとする
コードスタイルと重複
- より読みやすくデバッグしやすいコードのために、条件式を複数行に分けるようなスタイルを勧める
- DRY(Don’t Repeat Yourself) 原則は尊重するが、重複コードを無理に排除するよりもバランスが重要だと強調する
- 単純な繰り返しのほうが、複雑なDRY実装より優れている場面も多い
ソフトウェア設計原則
- SoC(関心の分離)原則よりも行動の局所性を好み、「その振る舞いをするコードは、そのオブジェクトの中にあるほうが保守しやすい」と主張する
- コールバック/クロージャ、型システム、ジェネリクス、抽象化などは、少量を適切に使うべきだと警告する
- クロージャの乱用は、JavaScriptで「コールバック地獄」を生みかねない
ロギング、運用
- ロギングは非常に重要で、主要な分岐ごとに残し、クラウド環境ではリクエストIDなどで追跡できるように構成する
- 動的ログレベルやユーザー別ログを活用できれば、運用中の問題追跡に大きく役立つ
並行性、最適化
- 並行性については、できるだけ単純なモデル(状態を持たないWebリクエスト、分離されたワーカーキューなど)だけを信頼する
- 最適化は、実際の性能プロファイルデータを確保してから行うことを勧める
- ネットワークI/Oなどの隠れたコストに注意すべきであり、単にCPUの複雑度だけを見るのは危険である
API設計
- 良いAPIは使いやすいものであるべきで、複雑すぎる設計や抽象化は開発者体験を損なう
- 「ユースケースに合った単純なAPI」と、「複雑なケースにも対応できる階層的なAPI」という構造を勧める
パーサー開発
- 再帰下降パーサーは学術界では過小評価されがちだが、実際のプロダクションコードには最も適していて理解しやすい方法である
- これまでの多くのパーサー開発経験では、ツール生成のパーサーは成果物が複雑すぎて、問題解決の面ではむしろマイナスになることが多い
- おすすめ書籍として『Crafting Interpreters』を最高の一冊に挙げており、実務的な助言が多く含まれている
フロントエンドと流行
- モダンフロントエンド(React、SPA、GraphQLなど)は、かえって複雑性の悪霊をさらに呼び込み、不必要な場合が多い
- Grug本人は、htmxやhyperscriptのような単純なツールで複雑性を減らすやり方を好む
- フロントエンドでは絶えず新しい試みが行われているが、既存アイデアの繰り返しも多い点に注意が必要である
心理的要素、インポスター症候群
- ほとんどの開発者は「自分が何をしているのかわからない」と感じることが多く、FOLD(Fear Of Looking Dumb) 現象から自由になる必要がある
- シニア開発者が「これは自分にも難しい、複雑すぎる」と公に言えば、ジュニア開発者も肩の力を抜ける
- インポスター症候群はよくある感情であり、十分に学びながら成長していけると励ましている
結論
- プログラミングでは複雑性を常に警戒すべきであり、単純さを保つことが成功する開発の核心である
- 経験、ツールの効果的な活用、謙虚さ、実際に動作するコードへの敬意が、長期的に効率的で価値ある開発へとつながる
- 「複雑性はとても、とても悪い」—この一文を常に覚えておくべきである
1件のコメント
Hacker Newsの意見
print文でデバッグしている現実を見てきた。自分のワークフローを同僚に伝えようとしても反応がない。システムを理解するための最良の出発点はまさにデバッガだという点には同意する。テスト中に興味深いコード行で止めてスタックを見るのは、頭の中でコードを追うよりずっと簡単だ。デバッガの使い方を身につけておけば、本当にささやかな超能力を得るようなものだ。できるならぜひ一度試してみてほしいprintデバッグが唯一可能な選択肢になる。ログシステムにまで問題があったり、プログラムがログを出力する前にそのままクラッシュしたりすると、printすら使えない状況になるprintによる出力や self-checking code を入れるほうが生産的だ。printを入れるほうが、デバッガでステップ実行するよりずっと速い。さらに、printのコードはプログラムに残るが、デバッグセッションは消えてしまう。" 私もこの意見に同意する。開発プロセスの大半では、print→仮説→実行のループのほうがずっと速く問題解決につながる。頭の中でコードを「実行してみる」のではなく、コードフローについてすでに動作モデルがあるので、printが間違った出力を見せればたいてい素早く実態を直感できる。関連リンク: The unreasonable effectiveness of print debuggingprintデバッグのコードが誤ってバージョンに入り込んで問題を起こした経験を何度もした後だった。その後 CLI デバッガでもいろいろ冒険したし、Junit+デバッガ(Eclipse など IDE ベース)で実験的なコードをその場で書きながらテストとして残すプロセスは、Python REPL と同じくらい便利だと感じた。ただし、デバッガを環境に合わせてセットアップする初期投資は必要だ