Learning Software Architecture学习软件架构
In reply to an email asking about learning software design skills as a researcher physicist:回复一封关于物理学研究人员如何学习软件设计技能的邮件:
I was attached to a bioinformatics lab early in my career, so I think I understand what you are talking about, the phenomenon of “scientific code”! My thoughts:我职业生涯早期曾在生物信息学实验室工作,所以我认为我明白你在说什么,即“科研代码”现象!以下是我的想法:
First meta observation is that “software design” is something best learned by doing. While I had some formal “design” courses at the University, and I was even “an architect” for our course project, that stuff was mostly make-believe, kindergarteners playing fire-fighters. What really taught me how to do stuff was an accident of my career, where my second real project (IntelliJ Rust) propelled me to a position of software leadership, and made design my problem. I did make a few mistakes in IJ Rust, but nothing too horrible, and I learned a lot. So that’s good news — software engineering is simple enough that an inquisitive mind can figure it out from first principles (and reading random blog posts).第一个元观察是,“软件设计”最好通过实践来学习。虽然我在大学里上过一些正规的“设计”课程,甚至还在课程项目中担任过“架构师”,但那些东西大多是过家家,就像幼儿园小朋友玩消防员游戏一样。真正教会我如何做事的,是我职业生涯中的一次偶然——我的第二个正式项目(IntelliJ Rust)将我推向了软件领导岗位,并使设计成为了我必须解决的问题。在 IntelliJ Rust 中,我确实犯过一些错误,但没什么大不了的,我也从中受益匪浅。所以这是个好消息——软件工程足够简单,只要有好奇心,就能从第一性原理(以及阅读各种博客文章)中摸索出来。
Second meta observation, the bad news: Conway’s law is important. Softwaregenesis repeats the social architecture of the organization producing software. Or, as put eloquently by neugierig,第二个元观察,坏消息是:康威定律(Conway’s law)很重要。软件的演变过程会重复生产该软件的组织的社会架构。或者,正如 neugierig 所言:
If I were to summarize what I learned in a single sentence, it would be this: we talk about programming like it is about writing code, but the code ends up being less important than the architecture, and the architecture ends up being less important than social issues.如果我要用一句话总结我所学到的东西,那就是:我们谈论编程时,好像它只是关于编写代码,但最终代码的重要性不如架构,而架构的重要性又不如社会因素。
I suspect that the difference you perceive between industrial and scientific software is not so much about software-building knowledge, but rather about the field of incentives that compels people to produce the software. Something like “my PhD needs to publish a paper in three months” is perhaps a significant explainer?我怀疑你所感知到的工业软件与科研软件之间的差异,与其说是关于构建软件的知识,不如说是关于驱使人们产出软件的激励机制领域。比如“我的博士学位需要在三个月内发表一篇论文”这样的情况,或许是一个重要的解释因素?
Two things you can do here. One, at times you get a chance to design or nudge an incentive structure for a project. This happens once in a blue moon, but is very impactful. This is the secret sauce behind TIGER_STYLE, not the set of rules per se, but the social context that makes this set of rules a good idea.在这里你可以做两件事。第一,有时你有机会设计或引导一个项目的激励结构。这种情况千载难逢,但影响巨大。这就是 TIGER_STYLE 背后的秘诀,不是规则本身,而是使这套规则成为好主意的社会环境。
Two, you can speedrun the four stages of grief to acceptance. Incentive structure is almost never what you want it to be, but, if you can’t change it, you can adapt to it. This is also true about most industrial software projects — there’s never a time to do a thing properly, you must do the best you can, given constraints.第二,你可以快速经历悲伤的四个阶段并走向接受。激励结构几乎永远不会是你想要的样子,但如果你无法改变它,就去适应它。大多数工业软件项目也是如此——永远没有时间把事情做得尽善尽美,你必须在现有约束下做到最好。
Let me use rust-analyzer as an example. The physical reality of the project is that it’s simultaneously very deep (it’s a compiler! Yay!) and very wide (opposite to an LLM, a classical IDE is a lot of purpose-built special features). The social reality is that “deep compiler” can attract a few brilliant dedicated contributors, and that the “breadth features” can be a good fit for an army of weekend warriors, people who learn Rust, who don’t have sustained capacity to participate in the project, but who can sink an hour or two to scratch their own itch.让我以 rust-analyzer 为例。该项目的物理现实是,它既非常深(它是一个编译器!耶!),又非常广(与大语言模型相反,传统的 IDE 是由许多专用功能组成的)。社会现实是,“深度的编译器”可以吸引少数才华横溢的忠实贡献者,而“广度功能”则非常适合大批周末战士——那些学习 Rust、没有持续精力参与项目,但可以花一两个小时来满足自己需求的人。
My insistence that rust-analyzer doesn’t require building rustc, that it builds on stable, that
it doesn’t have any C dependencies, and that the entire test suite takes seconds, was in the
service of the goal of attracting high-impact contributors. I was wrangling the build system to make
sure people can work on the borrow checker without thinking about anything else.我坚持要求 rust-analyzer 不需要构建 rustc,要求它在稳定版上构建,要求它没有任何 C 语言依赖,并且整个测试套件在几秒钟内完成,这些都是为了吸引高影响力贡献者的目标服务的。我一直在打磨构建系统,以确保人们可以在不考虑其他任何事情的情况下,专注于借用检查器(borrow checker)的工作。
To attract weekend warriors, the internals of rust-analyzer are split into multiple independent
features, where each feature is guarded by catch_unwind at runtime. The thinking was that I
explicitly don’t want to care too much about quality there, that the bar for getting a feature PR
in is “happy path works & tested”. It’s fine if the code crashes, it will only attract
further contributors, provided that:为了吸引周末战士,rust-analyzer 的内部结构被拆分为多个独立的功能,每个功能在运行时都由 catch_unwind 保护。我的想法是,我明确不想在这些地方过度追求质量,功能 PR 的准入门槛就是“Happy path 能跑通且已测试”。代码崩溃也没关系,这只会吸引更多的贡献者,前提是:
- the quality is isolated to a feature, and doesn’t spill over, 质量问题被隔离在特定功能内,不会扩散;
- at runtime, the crash is invisible to the user (it’s crucial that rust-analyzer features work with an immutable snapshot, and can’t poison the data). 在运行时,崩溃对用户是不可见的(关键在于 rust-analyzer 的功能必须基于不可变的快照工作,且不能污染数据)。
In contrast, when working on the core spine which provided support for features, I was very relatively more pedantic about quality.相比之下,在处理提供功能支持的核心骨干代码时,我对质量的要求会相对严苛得多。
A word of caution about adapting to, rather than fixing incentive structure — the future is
uncertain, and tends to happen in the least convenient manner. The original motivation behind
rust-analyzer experiment was to avoid the need to write a parallel compiler (the one in IntelliJ
Rust), and to prototype a better architecture for LSP, so that the learnings could be backported
to rustc. So, even in core (especially in core), the code was very experimental. Oh well. Stuck
with one more compiler now, I guess?关于适应而非修复激励结构,有一点需要提醒——未来是不确定的,而且往往以最不方便的方式发生。rust-analyzer 实验最初的动机是避免编写一个并行编译器(IntelliJ Rust 中的那个),并为 LSP 原型化一个更好的架构,以便将经验回馈给 rustc。所以,即使是在核心代码中(尤其是核心代码),代码也非常具有实验性。唉,好吧。现在看来,我只能继续维护另一个编译器了?
I might hazard a guess that something similar happened to uutils project, which started as the primary destination for people learning Rust, and ended up as Ubuntu coreutils implementation.我大胆猜测,uutils 项目也发生了类似的情况,它最初是人们学习 Rust 的主要目的地,最终却变成了 Ubuntu coreutils 的实现。
Third, now to some concrete recommendations. Sadly, I don’t know of a single book I can recommend which contains the truths. I suspect one can only find such a book in an apocryphal short story by Borges: practice seems to be an essential element here. But here are some things worth paying attention to:第三,现在给出一些具体的建议。遗憾的是,我不知道有哪本书可以推荐给你并包含这些真理。我怀疑只有在博尔赫斯的伪经短篇小说中才能找到这样的书:实践似乎是其中必不可少的要素。但以下是一些值得关注的内容:
Boundaries talk by Gary Bernhardt is all-time favorite. It contains solid object-level advice, and, for me, it triggered the meta inquiry.Gary Bernhardt 的《Boundaries》演讲是我一直以来的最爱。它包含了扎实的对象级建议,对我而言,它引发了我的元认知探索。
How to Test is something I wish I had. I immediately understood the importance of testing, but it took me a long time to grow arrogant enough to admit that most widely-cited testing advice is shamanistic snake-oil, and to conceptualize what actually works.《How to Test》是我希望早点看到的东西。我很快就理解了测试的重要性,但我花了好长时间才变得足够“傲慢”,去承认大多数广为流传的测试建议都是招摇撞骗的巫术,并去概念化什么才是真正有效的。
∅MQ guide and, more generally, writings by Pieter Hintjens introduced me to Conway’s Law thinking. That “feature development” architecture of rust-analyzer? – optimistic merging, applied.《∅MQ guide》以及更广泛意义上 Pieter Hintjens 的著作,将我引入了康威定律的思考方式。rust-analyzer 那种“功能开发”架构?——就是乐观合并(optimistic merging)的应用。
Reflections on a decade of coding by Jamii is excellent, goes very meta. It is intentionally the first of my links.Jamii 的《Reflections on a decade of coding》非常出色,深入到了元层面。我特意将其放在链接的第一位。
Ted Kaminski blog is the closest there is to a coherent theory of software development, appropriately framed as a set of notes to a non-existing book!Ted Kaminski 的博客是目前最接近连贯的软件开发理论的内容,它被恰当地构建为一本不存在之书的笔记!
As for the actual books, Software Engineering at Google and Ousterhout’s The Philosophy of Software Design are often recommended. They are good. SWE, in particular, helped me with a couple of important names. But they weren’t ground breaking for me.至于具体的书籍,《Software Engineering at Google》和 Ousterhout 的《The Philosophy of Software Design》经常被推荐。它们确实不错。特别是 SWE,帮助我理清了几个重要的概念名称。但它们对我来说并不是开创性的。