ソフトウェアアーキテクチャを学ぶ
(matklad.github.io)- ソフトウェア設計は、講義よりも実際のプロジェクトで責任を持ち、問題が自分ごとになったときのほうが深く学べる
- Conway’s Lawは、ソフトウェアが組織の社会的構造を反復するという見方であり、科学コードと産業コードの違いもインセンティブから生じうる
- rust-analyzerは、高速ビルド、stable対応、C依存の排除、数秒で終わるテストによって、高効率なコントリビューターが集中しやすいようにしている
- rust-analyzerは、独立した機能を
catch_unwindで保護し、PR基準を下げた一方で、中核の spine にははるかに厳格な品質基準を適用した - 実験的な構造は長期的な現実になりうり、rust-analyzerもLSPアーキテクチャのプロトタイプから、もう1つのコンパイラを保守する形へとつながった
ソフトウェア設計は実戦で最もよく学べる
- ソフトウェア設計は、公式な講義よりも、実際のプロジェクトで責任を持ち、問題を自分で解決するときのほうがよく学べる
- 大学の設計授業やコースプロジェクトで「アーキテクト」役を務めていたときよりも、2つ目の実戦プロジェクトだった IntelliJ Rust で、設計上の問題が自分の仕事になってから学習が本格化した
- IntelliJ Rustではいくつかの失敗もあったが致命的ではなく、その過程で多くを学べた
- ソフトウェアエンジニアリングは、好奇心のある人が原理から考え、さまざまな文章を読みながら身につけられる程度には単純な面もある
インセンティブ構造と Conway’s Law
- Conway’s Law は、ソフトウェアがそれを作る組織の社会的構造を反復するという見方である
- 産業用ソフトウェアと科学コードの違いは、ソフトウェア構築の知識そのものよりも、人々にソフトウェアを作らせる インセンティブ構造 に由来する可能性がある
- 「3か月以内に論文を出さなければならないPhD」のような状況は、科学コードの形を左右する重要な要因になりうる
- インセンティブ構造には大きく2つの方法で対応できる
-
プロジェクトのインセンティブを設計または動かす
- プロジェクトの インセンティブ構造 を設計または調整する機会はまれだが、その機会があれば影響は大きい
- TIGER_STYLE の核心は規則一覧そのものではなく、それらの規則が良い選択になるようにする 社会的文脈 にある
-
変えられないなら制約に適応する
- インセンティブ構造は望みどおりに与えられることはほとんどなく、変えられないならその構造に合わせて適応しなければならない
- 産業ソフトウェアプロジェクトでも「ちゃんとやる時間」はほとんどなく、与えられた制約の中でできる最善を尽くす必要がある
rust-analyzerで構造と参加者を合わせた方法
- rust-analyzer は、深さと広さの両方を持つプロジェクトである
- 深い側面 では、コンパイラという性格のおかげで、優秀で献身的なコントリビューターを引きつけられる
- 広い側面 では、古典的なIDEが目的別の特殊機能を多く持っているため、Rustを学ぶ人や継続的な参加が難しい週末コントリビューターが、自分の不便を解消するために1〜2時間使うのに向いている
- rust-analyzerが
rustcビルドを要求せず、stableでビルドでき、C依存がなく、テストスイート全体が数秒で終わることにこだわったのは、高効率なコントリビューター を引きつけるためだった - ビルドシステムを磨き込み、人々が他のことを気にせず borrow checker の作業に集中できるようにしようとした
- 週末コントリビューターを引きつけるために、rust-analyzer内部は複数の 独立機能 に分かれており、各機能はランタイムで
catch_unwindによって保護されている - 機能PRの基準は「ハッピーパスが動作し、テストがある」まで下げられており、そのコードがクラッシュしても受け入れ可能だとみなされた
- ただし2つの条件が必要だった
- 品質問題が 個別機能 の中に隔離され、他の部分へ広がらないこと
- ランタイムクラッシュがユーザーに見えないこと。そのため rust-analyzer の機能は 不変スナップショット で動作し、データを汚染できないようになっている
- 逆に、機能を支える中核の spine には、はるかに厳格な品質基準が適用される
実験的な構造が長期的現実になるリスク
- インセンティブ構造を直すより適応する場合は、未来が不確実であり、たいてい最も不都合な形で現実化しうることに注意する必要がある
- rust-analyzerの当初の動機は、IntelliJ Rust の中に並列コンパイラをもう1つ書くのを避け、LSPのためのより良いアーキテクチャを プロトタイプ として検証し、その学びを
rustcに戻すことだった - そのため、中核部分まで含めてコードは非常に 実験的 だった
- 結果として、もう1つのコンパイラを保守することになった
- 同様に uutils プロジェクトも、Rustを学ぶ人々の主要な行き先として始まり、Ubuntuの coreutils 実装になった
参考になる資料と本
- 正解が載っている単一の本 はなく、実戦が必須要素のように見える
- Boundaries by Gary Bernhardt
- 具体的な助言がしっかりしており、さらに高いレベルの探究を促した資料である
- How to Test
- テストの重要性そのものはすぐ理解できたが、広く引用される多くのテスト助言が実用的ではないことを認め、実際に機能するやり方を概念化するまでには長い時間がかかった
- ∅MQ guide と Pieter Hintjensの文章
- Conway’s Law 的な考え方に触れるきっかけになった資料である
- rust-analyzer の機能開発アーキテクチャは optimistic merging を適用した形である
- Reflections on a decade of coding by Jamii
- 非常にメタな内容を扱っており、リンク集 の最初の項目に置くほど優れている
- Ted Kaminski ブログ
- 存在しない本のためのノートという形式で、ソフトウェア開発についての一貫した理論に最も近い
- Software Engineering at Google と Ousterhout の The Philosophy of Software Design
- よく勧められる良書であり、特に Software Engineering at Google は unit test と integration test に関する重要な名称 を整理するのに役立った
- ただし個人的には画期的な本ではなかった
1件のコメント
Hacker Newsのコメント
チートシート的に要約すると、良い設計とは一つのアイデアが全体に行き渡っていて、驚きを最小化する方向であるべきだということ
システムが許せば人は結局そう使うようになり、「みんながただ X してくれれば」で始まる解決策は解決策ではない
データを変換する部分と使う部分を分離し、データモデルはコードより長生きし、結合度は多くの問題の根源である
バージョン管理は避けられず、状態は明示的に表に出すべきであり、情報ごとに単一の信頼できる情報源があるべきだ
命名にもっと時間を使い、テストが難しいなら設計が間違っており、文書化していない決定はいずれ後悔することになる
コミュニケーションにはコストがあるので支払う前に正当化すべきであり、エンジニアの仕事とは不完全な情報の中で経験則を使って問題を解くことだ
記事自体もソフトウェアアーキテクチャの観点ではそれほど一貫していなかった
4+1アーキテクチャビューは UML を抜きにしても大局を考える良い概念的枠組みであり、Pattern-Oriented Software Architecture シリーズも人々が到達するさまざまなアーキテクチャをうまく整理している
Grady Booch がソフトウェアアーキテクチャハンドブックを作っていたこともあるが、今ではほぼ止まっているようで、当時はメーリングリストで企業や大規模オープンソースプロジェクトの大きなシステムアーキテクチャを記録していた
こうした資料を掘り下げると、規模、安全性、性能、相互運用性、フェイルセーフといった異なる焦点に合わせてアーキテクチャが作られており、それぞれの目標には現実的なトレードオフがあることが分かる
この基準でより良いなら、悪い設計に見えても実際には良い設計である
インターフェースは正しく使うのが簡単で、間違って使うのが難しいように作るべきで、プロジェクトを知らない人がどう使うかを考えるべきだ
正しいコードは書きやすくあるべきで、怪しいコードは目立つべきであり、バグはできるだけ左側に移すべきだ
一つのバグを直すよりバグの類型をなくすほうが良く、インターフェースは実装より変えにくいので、正しいインターフェースなら醜い実装でも構わない
コメントやドキュメントはコードがなぜその形なのかを説明すべきで、見た目にはもっと単純な方法があっても、何らかの制約でそれができないならそれを残しておくべきだ
データの観点では重複を避けるべきで、同じ事実を複数箇所に保存すれば、いずれ食い違ってバグが生じる
よく整備された道から外れるにはコストがかかり、本当に価値があるならそうしてもよいが、そのコストを過小評価してはいけない
一見劣って見える退屈な技術のほうが、より良い技術であることは多く、「やる価値があるか?」ではなく「他のことをするのと比べてやる価値があるか?」で期待値を考えるべきだ
自分が他人より賢いと思っていても、知能だけでは十分でない問題があり、実際に爆発するまで見つけられない問題もあるので、他人の失敗から学ぶべきだ
摩擦は静かな殺し屋だ
計画は良いが、時には実際に試してみる必要があり、すべてには金がかかる
コストを考えずに設計すると、あとで難しい選択を強いられることになる
一人で作るプロジェクトであっても、エンジンの制約が「テスト中に飛び出した変な新機能はこういう形で追加せよ」というガイドになってくれる
「特定の状態遷移で何度も実行される新しい効果音を作るにはこうする」といった膨大な文書を毎回持ち歩く必要がなくなる
システム設計者は業界を理解すべきであり、その用語やモデリング習慣を完全に受け入れる必要はないが、データセットを見る理由や視点は理解しておく必要がある
医療市場の複雑さを意図的に単純化して不要な過剰定義をなくし、より統合されたモデルを提供した部分もあるが、そうした変更を自信を持って行えたのは問題領域を十分理解していたからだ
特に名前はほとんど死なない
たまに死ぬことはあっても、名前を変えるには極端な努力が必要なので、ドメイン専門家に命名案を長く検討してもらい、漏れがないか確認することに大きな時間を使う価値が本当にある
いくつかの概念は押し通せても、営業・マーケティングのようなビジネス側は引き続き業界用語を求め、モデルを現在の業界観に合わせるよう圧力をかけてくる
その流れを断ち切ると決めたなら、その断絶には意図と目的が明確であるべきだ
ソフトウェアで最も強調すべき属性は保守性である
作るのにいくらかかるかも問題だが、運用にかかるコストはインフラだけでなく、積み上がる機能要求、コードのリファクタリング、サードパーティソフトウェアのバージョン維持まで含むので、はるかに大きな影響を与える
おすすめ一覧は Ousterhout の A Philosophy of Software Design のように良いものが多いが、総じてソフトウェアアーキテクチャそのものというより、ソフトウェア開発一般論に近い
アーキテクチャを見るなら Shaw/Garlan の Software Architecture: Perspectives on an Emerging Discipline のような古典と Mary Shaw の文章を勧める
ソフトウェアアーキテクチャ分野がなぜ予想どおりに進まなかったのかを探る Myths and Mythconceptions: What Does It Mean to Be a Programming Language, Anyhow? や Revisiting Abstractions for Software Architecture and Tools to Support Them のような近年の論文も良い
実用面では Unix の pipe and filter、REST がなぜ成功し、どこでなぜ崩れるのかを見るとよく、ヘキサゴナルアーキテクチャも重要だ
個人的には、ソフトウェアアーキテクチャとメタオブジェクトプロトコルを結び付けて、プログラミング言語とプログラミングの新しい基盤として捉えようとする Beyond Procedure Calls as Component Glue: Connectors Deserve Metaclass Status もある
これは Mary Shaw の Procedure Calls Are the Assembly Language of Software Interconnection: Connectors Deserve First-Class Status への応答で、手続き呼び出しがアセンブリ言語だとしたら高水準言語はどのような姿になるのかを問うものだ
もしかするとソフトウェアアーキテクチャには、もっと明るく実用的な未来があるのかもしれない
いくつかのデータ構造や型、小さな基本関数の集合さえあれば、それらを組み合わせればよい
Lisp で好きな点の一つは、より複雑な型、特に FFI から来た型が常に不透明であることだ
C に似た言語で構造体を定義したら標準関数群が得られるようなCLOS 実装を見てみたい
アーキテクチャを学ぶ最良の方法は、十分に大きなプロジェクトを作ることではなく、保守することだ
しかも少なくとも二つか三つのプロジェクトでそうすべきだ
プロジェクトが小さすぎるとどんなアーキテクチャでもうまく動き、「大きい」プロジェクトかどうかはコード行数より、それまでに関わった人数、できればチーム数で判断するほうがよい
比較するには少なくとも二つの異なるプロジェクトが必要であり、一つのプロジェクトに何十年も閉じ込められて現代的な問題解決の方法を知らない人も見てきた
だが現実には、プロジェクトを作った人が昇進してアーキテクトになることが多く、保守してきた人がそうなることはまれだ
Google では特に、新しくリリースしないと昇進できず、保守では昇進できないうえ、可能ならリリース直後に抜けるほうが有利なので、より顕著である
逆説的だが、アーキテクトになるのに最も良い立場にいるのは、社内で誰も引き受けたがらない既存プロジェクトの保守に回される外部契約者かもしれない
彼らはアーキテクチャを維持しなければならず、複数のプロジェクトを渡り歩くので比較できる
ただし時間単位で請求するなら、より多くの時間を請求するためにアーキテクチャを過度に複雑化してしまう危険はある
この文脈では Architecture of Open Source Applications を本当に勧める
各章をそのプロジェクトの保守者が書いているシリーズなので、例を通じてアーキテクチャを学べる
アーキテクチャとは何かだけでなく、それを形作った制約、たいていは歴史や変化するプロジェクトのビジョンまで分かる
複数著者の本という限界のため、すべての章が同じだけ良い、または面白いわけではなく、どれも古いが、それでも読む価値はある
http://aosabook.org/
自分が取り組むプロジェクトのメンタルモデルをよりよく得ることに時間を使いたいが、プログラミング言語や特定のアーキテクチャ選択、時間を使う価値がないように見えるほど複雑になった部分が嫌になり始めると、やる気が大きく落ちる
プロジェクトによるが、「フルスタック開発者」として働くのはプログラミングの楽しさを奪う感じがする
すでに週40時間、想像できる中で最も退屈なプロジェクトを見て過ごしている
完璧なプロジェクトは一つもない
そしてプログラミング言語がそこまで大きな問題なら、乗り換えたほうがいい
誰もが複数の言語を扱えるべきだとは思うが、最終的な選択は本人次第だ
最も多く時間を使っている決定が、本当に最も重要な決定なのか確認すべきである
よく設計されたデータ構造は、フレームワーク、言語、プラットフォームよりも、性能や保守性にずっと大きな影響を与える
個人的には毎日 ADHD と付き合いながら働いているので、前に進むために自分を押し続ける必要があり、それは重要でない決定を選んで片付け、慎重な思考が必要な残りの問題領域に集中するという意味だ
「クリーンコード」や「美しいコード」といった言葉は、ジュニアがソフトウェアアーキテクチャのベストプラクティスを学ぶうえであまり役に立たない
ジュニアがなぜ ORM を使うのかと聞いたとき、シニアが「そのほうがきれいだから」と答えても、残るのは疑問符だけだ
むしろ明確な目標の一覧を定義したほうがよい
保守可能性、性能とスケーラビリティ、効率性、レジリエンス、可観測性、テスト容易性とテスト済みであること、セキュリティ、新しい開発者にとって読みやすいことといった基準が、互いにバランスを取る
基準を増やすほど、迷ったときによりよい選択をしやすくなり、開発チームの外の人にも意味があるため、顧客と何にお金を払うのか合意しやすくなる
プロジェクトは生涯の大半を保守モードで過ごし、それはプロジェクトが成功したという良い意味でもあるので、保守可能性も定義できる
新機能をアーキテクチャを壊さず、さらには単一のメソッドシグネチャさえ壊さずに入れられる能力は良い出発点だ
抽象化には非常に慎重であるべきだ
「抽象化は欲しいものがどれだけ単純かを隠してしまうことが多い」という言い方は正しく、ORM はまさにその典型だ
ほとんどの場合、データは第一級市民として扱うべきであり、DBA との協業もしやすくなる
早すぎる最適化に陥らないようにしつつ、ハッピーパス以外も考えるのは難しいが、長所短所を見ないまま良いアイデアの実装に突っ走るのを防いでくれる
自分のコードを扱う人が最悪の日を過ごしていると想像すると、読みやすく快適なものにする助けになる
あちこちのコメント、省略できても置いておくローカル変数、変数名などがここに含まれる
フレームワークは慎重に選ぶべきで、良い召使いだが悪い主人でもある
「フレームワーカーではなくエンジニアになれ」という記事の表現は正しい
強い意見そのものが嫌いなわけではなく、ライブラリやツールではむしろ素晴らしい特性になりうる
そうしたライブラリやツールは、自分の領域について多くのドメイン専門性を持っている可能性が高い
しかしフレームワークでは、その強い意見が「ハンマーを持てばすべてが釘に見える」に変質しやすい
常にそうなるわけでも、常に問題になるわけでもないが、一つのドメインの正統教義を広く適用すると、その正当化は本来ドメイン特化だったのに、より広い文脈では破綻する危険がある
だからフレームワークには、別の ORM や永続化統合レイヤーを差し込めたり、ルーター、バリデータ、その問題に合わない他の構成要素を置き換えられたりするモジュール性を好む
フレームワークのエコシステム内に複数のパラダイムの代替案があるなら、なおさら価値が大きい
ほぼ合っている既製ツールを見つけて、欠点が明確で、必要なときに補える選択肢を選べるからだ
保守性のためには、NIH 症候群と戦うことが非常に重要だ
これは驚くほどの速度で資源を吸い込む継続的なリスクである
同時に、いくつかの構成要素は自分で調整したり完全に所有したりすることで大きな利益を得られる場合もあり、難しいのは何がどちら側なのかを見極めることだ
保守性を一覧の先頭に置いた点には深く共感する
システム工学の観点では、ソフトウェアアーキテクチャは配管設計に似ている
とても重要だが、人は配管の中に住むのではなく、配管のある家に住んでいる
配管が家の残りの部分を考慮していなければ、直すコストがとてつもなく高くなりうる
良い記事だ
「ソフトウェアアーキテクチャを学ぶ」というのは、唯一の正解がないことを理解することだ
これは芸術であり科学でもある
読み物としては Simplify IT - The art and science towards simpler IT solution を勧める
https://nocomplexity.com/documents/reports/SimplifyIT.pdf
Gary Bernhardt の講演は本当に特別だ
他の興味深い場所へつながる概念がたくさん詰まっている