Learning Software Architecture
609 points
• 6 days ago
• Article
Link
作者以自己从生物信息学实验室转向担任 IntelliJ Rust 项目负责人的经历为例,认为学习软件设计更适合通过实战而不是依靠正规教育。大学课程能打好基础,但真正的理解来自于在真实项目中动手、解决问题。因为软件工程本质上是讲逻辑的,好奇的人可以通过试验和阅读逐步掌握。
一个重要的洞见是 Conway's Law:软件架构会映射出构建它的组织的社会结构。作者认为,科学软件与工业软件的差别更多源自激励机制而非技术水平。例如,学术界受到发表期限等压力,会影响科学软件的开发方式。他建议两条策略:一是在可能的情况下去影响激励机制(如 TIGER_STYLE 这样的项目所示);二是接受现有约束,在其范围内高效地工作。
以 rust-analyzer 为例,作者指出它既"深"(具有编译器级的复杂性)又"广"(包含大量 IDE 功能),因此需要针对不同类型的贡献者采取不同策略。为了吸引专注的核心开发者,他把优先级放在干净、快速的构建系统和核心架构的高质量代码上;而对于添加孤立功能的偶发贡献者,他则降低了准入门槛,允许质量要求较低的代码,只要这些改动的失败可以被限制住、对用户不可见。这种务实做法在质量与社区增长之间取得了平衡。
他也提醒,迎合既有激励机制存在风险,因为未来的需求难以预测。 rust-analyzer 最初只是一个用于验证 rustc 架构的实验性原型,后来竟发展成了完整的编译器;同样,uutils 最初是 Rust 开发者的练习项目,后来成为 Ubuntu 的 coreutils 实现。这些例子说明项目常会因环境变化而超出最初的设想。
在具体学习资源方面,作者推荐了若干有影响力的作品。 Gary Bernhardt 的 "Boundaries" 演讲提供了实用的设计建议,并引发了他对软件架构的更深入思考。他自己关于测试的随笔反映了在意识到许多传统测试观念无效之后总结出的宝贵经验。 Pieter Hintjens 关于 ZeroMQ 的著作让他接触到以 Conway's Law 为出发点的思维和"乐观合并"等概念,这些都影响了 rust-analyzer 的开发模式。另有值得关注的还有 Jamii 的反思性编码文章和 Ted Kaminski 的博客,后者把软件开发作为一个连贯的理论体系来探讨。
尽管像 Software Engineering at Google 和 Ousterhout 的 A Philosophy of Software Design 这样的书很有价值,作者认为它们仍不及亲身实践带来的改变深刻。他的结论是:在软件设计上达到精通,主要靠做、观察和不断迭代,而不是依赖某一本权威书籍。整个过程需要接受不确定性,适应社会性动态,并通过现实世界的挑战不断打磨自己的理解。
The author, drawing from personal experience transitioning from a bioinformatics lab to leading the IntelliJ Rust project, argues that software design is best learned through practice rather than formal education. He notes that while university courses provide foundational knowledge, real understanding comes from hands-on experience and problem-solving in actual projects. This learning process is accessible because software engineering is fundamentally logical and can be grasped by curious individuals through experimentation and reading.
A key insight emphasized is Conway's Law, which states that software architecture mirrors the social structure of the organization building it. The author suggests that differences between scientific and industrial software stem more from incentive structures than technical knowledge. For example, academic pressures like publishing deadlines shape how scientific software is developed. He advises two approaches: influencing incentive structures when possible, as seen in projects like TIGER_STYLE, or adapting to existing constraints by accepting limitations and working effectively within them.
Using rust-analyzer as a case study, the author explains how its dual nature, being both deep (compiler-level complexity) and broad (many IDE features), required different strategies for different contributor types. To attract dedicated core developers, he prioritized a clean, fast build system and high-quality code in the central architecture. For casual contributors adding isolated features, he lowered the barrier to entry by allowing less rigorous code, as long as failures were contained and invisible to users. This pragmatic approach balanced quality with community growth.
He cautions that adapting to incentive structures carries risks, since future needs are unpredictable. Rust-analyzer began as an experimental prototype to inform rustc's architecture but evolved into a full-fledged compiler itself. Similarly, the uutils project started as a learning exercise for Rust developers but became Ubuntu's coreutils implementation. These examples illustrate how projects can outgrow their original intent due to shifting circumstances.
For concrete learning resources, the author recommends several influential works. Gary Bernhardt's "Boundaries" talk offers practical design advice and sparked his deeper inquiry into software architecture. His own essay on testing reflects hard-won insights after realizing much conventional testing wisdom is unhelpful. Pieter Hintjens' writings on ZeroMQ introduced him to Conway's Law thinking and concepts like optimistic merging, which influenced rust-analyzer's development model. Other notable mentions include Jamii's reflective essay on coding and Ted Kaminski's blog, which approaches software development as a coherent theoretical framework.
While books like Software Engineering at Google and Ousterhout's A Philosophy of Software Design are valuable, the author finds them less transformative than experiential learning. He concludes that mastery in software design comes primarily from doing, observing, and iterating, rather than from any single authoritative source. The journey involves embracing uncertainty, adapting to social dynamics, and continuously refining one's understanding through real-world challenges.
120 comments • Comments Link
软件架构既需要儒家式的实践学习,也需要道家式的化繁为简;真正有价值的架构是在组织现实中经受考验、存活下来的,而不是纸上谈兵的设计。用"code on, code off"去类比《龙威小子》里的"wax on, wax off",把学写代码看作学武艺,是一种有益的思路。越来越多非英语母语者依赖 LLM 来提升写作质量,对这种做法的指责并无太大意义。
像 Shaw 和 Garlan 的 Software Architecture: Perspectives on an Emerging Discipline 这样的经典著作仍然很重要,同时也应研究 Unix 管道、 REST 、六边形架构等成功模式,并探讨如何在编程语言中把连接器做成一等公民。 Residuality 理论提供了与实践经验相符的架构视角,既有专门论述该理论的书籍,也有讨论架构哲学的配套著作。
把系统看作代数数据类型的转换序列等心智模型非常有力且具跨领域迁移性;折叠、展开及其对偶等概念为各种编程环境提供了通用的抽象框架。尽管这些抽象代数模型是跨领域可迁移的有益例子,但它们并非普适;文章对社会约束和组织背景的强调对于理解为何某些架构能成功仍然至关重要。
全栈开发在处理枯燥项目时会消磨编程的乐趣:当每周花 40 小时做无趣的工作,很难保持建设系统性深层心智模型的动力。架构案例研究对帮助非编码背景的人判断 LLM 提出的架构决策非常有价值,类似于医学生通过临床轮转学习;《 Architecture of Open Source Applications 》等资源提供了关于约束如何塑造架构决策的真实案例。
总体而言,讨论揭示了软件架构中理论与实践之间的张力:一方面重视抽象心智模型,另一方面也不得不面对现实组织约束。大家认识到,架构既受社会和商业因素影响,也受技术决策影响,学习需要实践经验与对成功模式的研究相结合。对话还涉及更广泛的议题,包括编程工作的持续演变、 LLM 在沟通中的角色,以及在既定工程实践中寻找创造空间的挑战。 • Software architecture requires both Confucian learning through practice and Taoist simplification by removing unnecessary complexity, with the real architecture being what survives contact with organizational realities rather than what's designed on paper.
• The "code on, code off" analogy to "wax on, wax off" from Karate Kid resonates as a way to think about learning coding as learning martial arts.
• Non-native English speakers are increasingly using LLMs to improve their writing quality, and criticizing this practice adds nothing valuable to discussions.
• Classic software architecture texts like Shaw and Garlan's "Software Architecture: Perspectives on an Emerging Discipline" remain valuable, along with studying successful patterns like Unix pipes, REST, and hexagonal architecture, while exploring how connectors could achieve first-class status in programming languages.
• Residuality theory offers an interesting perspective on architecture that aligns with practical experiences, with both a book on the theory and a companion on the philosophy of architecture available.
• Mental models like viewing systems as sequences of transformations on algebraic data types can be powerful and transferable across domains, with concepts like folds, unfolds, and their duals providing abstract frameworks that apply to various programming contexts.
• While abstract algebraic models are useful examples of architecture that can be transferred across domains, they shouldn't be seen as universally applicable, and the article's emphasis on social constraints and organizational context remains important for understanding why certain architectures succeed.
• Fullstack development can remove the joy of programming when working on dull projects, making it hard to stay motivated to build deep mental models of systems when spending 40 hours weekly on uninteresting work.
• There's significant value in architecture case studies that help non-coders learn to critique LLM architecture decisions, similar to how medical professionals learn through clinical rotations, with resources like "Architecture of Open Source Applications" providing real-world examples of how constraints shape architectural decisions.
• Software architecture is best learned through experience with legacy systems and large codebases rather than abstract books with simple examples, as the field requires understanding how deployment strategies, organizational pressures, and framework constraints shape actual system designs.
The discussion reveals a tension between theoretical and practical approaches to software architecture, with participants valuing both abstract mental models and real-world organizational constraints. There's recognition that architecture is shaped as much by social and business factors as by technical decisions, and that learning requires both hands-on experience and study of successful patterns. The conversation also touches on broader themes about the evolving nature of programming work, the role of LLMs in communication, and the challenge of finding creative space within established engineering practices.