0%

Why is everyone in such a rush?

Walk into any bookstore, and you’ll see how to Teach Yourself Java in 24 Hours alongside endless variations offering to teach C, SQL, Ruby, Algorithms, and so on in a few days or hours. The Amazon advanced search for [title: teach, yourself, hours, since: 2000 and found 512 such books. Of the top ten, nine are programming books (the other is about bookkeeping). Similar results come from replacing “teach yourself” with “learn” or “hours” with “days.”

The conclusion is that either people are in a big rush to learn about programming, or that programming is somehow fabulously easier to learn than anything else. Felleisen et al. give a nod to this trend in their book How to Design Programs, when they say “Bad programming is easy. Idiots can learn it in 21 days, even if they are dummies.” The Abtruse Goose comic also had their take.

Let’s analyze what a title like Teach Yourself C++ in 24 Hours could mean:

  • Teach Yourself:

In 24 hours you won’t have time to write several significant programs, and learn from your successes and failures with them. You won’t have time to work with an experienced programmer and understand what it is like to live in a C++ environment. In short, you won’t have time to learn much. So the book can only be talking about a superficial familiarity, not a deep understanding. As Alexander Pope said, a little learning is a dangerous thing.

  • C++:

In 24 hours you might be able to learn some of the syntax of C++ (if you already know another language), but you couldn’t learn much about how to use the language. In short, if you were, say, a Basic programmer, you could learn to write programs in the style of Basic using C++ syntax, but you couldn’t learn what C++ is actually good (and bad) for. So what’s the point?

Alan Perlis

once said: “A language that doesn’t affect the way you think about programming, is not worth knowing”. One possible point is that you have to learn a tiny bit of C++ (or more likely, something like JavaScript or Processing) because you need to interface with an existing tool to accomplish a specific task. But then you’re not learning how to program; you’re learning to accomplish that task.

  • in 24 Hours: Unfortunately, this is not enough, as the next section shows.

Teach Yourself Programming in Ten Years

Researchers (Bloom (1985), Bryan & Harter (1899), Hayes (1989), Simmon & Chase (1973)) have shown it takes about ten years to develop expertise in any of a wide variety of areas, including chess playing, music composition, telegraph operation, painting, piano playing, swimming, tennis, and research in neuropsychology and topology. The key is deliberative practice: not just doing it again and again, but challenging yourself with a task that is just beyond your current ability, trying it, analyzing your performance while and after doing it, and correcting any mistakes. Then repeat. And repeat again. There appear to be no real shortcuts: even Mozart, who was a musical prodigy at age 4, took 13 more years before he began to produce world-class music. In another genre, the Beatles seemed to burst onto the scene with a string of #1 hits and an appearance on the Ed Sullivan show in 1964. But they had been playing small clubs in Liverpool and Hamburg since 1957, and while they had mass appeal early on, their first great critical success, Sgt. Peppers, was released in 1967.

Malcolm Gladwell has popularized the idea, although he concentrates on 10,000 hours, not 10 years. Henri Cartier-Bresson (1908-2004) had another metric: “Your first 10,000 photographs are your worst.” (He didn’t anticipate that with digital cameras, some people can reach that mark in a week.) True expertise may take a lifetime: Samuel Johnson (1709-1784) said “Excellence in any department can be attained only by the labor of a lifetime; it is not to be purchased at a lesser price.” And Chaucer (1340-1400) complained “the lyf so short, the craft so long to lerne.” Hippocrates (c. 400BC) is known for the excerpt “ars longa, vita brevis”, which is part of the longer quotation “Ars longa, vita brevis, occasio praeceps, experimentum periculosum, iudicium difficile”, which in English renders as “Life is short, [the] craft long, opportunity fleeting, experiment treacherous, judgment difficult.” Of course, no single number can be the final answer: it doesn’t seem reasonable to assume that all skills (e.g., programming, chess playing, checkers playing, and music playing) could all require exactly the same amount of time to master, nor that all people will take exactly the same amount of time. As Prof. K. Anders Ericsson puts it, “In most domains it’s remarkable how much time even the most talented individuals need in order to reach the highest levels of performance. The 10,000 hour number just gives you a sense that we’re talking years of 10 to 20 hours a week which those who some people would argue are the most innately talented individuals still need to get to the highest level.”

So You Want to be a Programmer

Here’s my recipe for programming success:

  • Get interested in programming, and do some because it is fun. Make sure that it keeps being enough fun so that you will be willing to put in your ten years/10,000 hours.
  • Program.

    The best kind of learning is learning by doing. To put it more technically, “the maximal level of performance for individuals in a given domain is not attained automatically as a function of extended experience, but the level of performance can be increased even by highly experienced individuals as a result of deliberate efforts to improve.”

    and “the most effective learning requires a well-defined task with an appropriate difficulty level for the particular individual, informative feedback, and opportunities for repetition and corrections of errors.” The book Cognition in Practice: Mind, Mathematics, and Culture in Everyday Life is an interesting reference for this viewpoint.

  • Talk with other programmers; read other programs. This is more important than any book or training course.

  • If you want, put in four years at acollege (or more at a graduate school). This will give you access to some jobs that require credentials, and it will give you a deeper understanding of the field, but if you don’t enjoy school, you can (with some dedication) get similar experience on your own or on the job. In any case, book learning alone won’t be enough. “Computer science education cannot make anybody an expert programmer any more than studying brushes and pigment can make somebody an expert painter” says Eric Raymond, author ofThe New Hacker’s Dictionary. One of the best programmers I ever hired had only a High School degree; he’s produced a lot ofgreatsoftware, has his own news group, and made enough in stock options to buy his own nightclub.

  • Work on projects with other programmers. Be the best programmer on some projects; be the worst on some others. When you’re the best, you get to test your abilities to lead a project, and to inspire others with your vision. When you’re the worst, you learn what the masters do, and you learn what they don’t like to do (because they make you do it for them).
  • Work on projects after other programmers. Understand a program written by someone else. See what it takes to understand and fix it when the original programmers are not around. Think about how to design your programs to make it easier for those who will maintain them after you.
  • Learn at least a half dozen programming languages. Include one language that emphasizes class abstractions (like Java or C++), one that emphasizes functional abstraction (like Lisp or ML or Haskell), one that supports syntactic abstraction (like Lisp), one that supports declarative specifications (like Prolog or C++ templates), and one that emphasizes parallelism (like Clojure or Go).
  • Remember that there is a “computer” in “computer science”. Know how long it takes your computer to execute an instruction, fetch a word from memory (with and without a cache miss), read consecutive words from disk, and seek to a new location on disk. (Answers here.)
  • Get involved in a language standardization effort. It could be the ANSI C++ committee, or it could be deciding if your local coding style will have 2 or 4 space indentation levels. Either way, you learn about what other people like in a language, how deeply they feel so, and perhaps even a little about why they feel so.
  • Have the good sense to get off the language standardization effort as quickly as possible.

With all that in mind, its questionable how far you can get just by book learning. Before my first child was born, I read all the How To books, and still felt like a clueless novice. 30 Months later, when my second child was due, did I go back to the books for a refresher? No. Instead, I relied on my personal experience, which turned out to be far more useful and reassuring to me than the thousands of pages written by experts.

Fred Brooks, in his essay No Silver Bullet identified a three-part plan for finding great software designers:

  1. Systematically identify top designers as early as possible.
  1. Assign a career mentor to be responsible for the development of the prospect and carefully keep a career file.
  1. Provide opportunities for growing designers to interact and stimulate each other.

This assumes that some people already have the qualities necessary for being a great designer; the job is to properly coax them along. Alan Perlis put it more succinctly: “Everyone can be taught to sculpt: Michelangelo would have had to be taught how not to. So it is with the great programmers”. Perlis is saying that the greats have some internal quality that transcends their training. But where does the quality come from? Is it innate? Or do they develop it through diligence? As Auguste Gusteau (the fictional chef in Ratatouille) puts it, “anyone can cook, but only the fearless can be great.” I think of it more as willingness to devote a large portion of one’s life to deliberative practice. But maybe fearless is a way to summarize that. Or, as Gusteau’s critic, Anton Ego, says: “Not everyone can become a great artist, but a great artist can come from anywhere.”

So go ahead and buy that Java/Ruby/Javascript/PHP book; you’ll probably get some use out of it. But you won’t change your life, or your real overall expertise as a programmer in 24 hours or 21 days. How about working hard to continually improve over 24 months? Well, now you’re starting to get somewhere…


References

Bloom, Benjamin (ed.) Developing Talent in Young People, Ballantine, 1985.

Brooks, Fred, No Silver Bullets, IEEE Computer, vol. 20, no. 4, 1987, p. 10-19.

Bryan, W.L. & Harter, N. “Studies on the telegraphic language: The acquisition of a hierarchy of habits. Psychology Review, 1899, 8, 345-375

Hayes, John R., Complete Problem Solver Lawrence Erlbaum, 1989.

Chase, William G. & Simon, Herbert A. “Perception in Chess” Cognitive Psychology, 1973, 4, 55-81.

Lave, Jean, Cognition in Practice: Mind, Mathematics, and Culture in Everyday Life, Cambridge University Press, 1988.


AnswersApproximate timing for various operations on a typical PC:

execute typical instruction 1/1,000,000,000 sec = 1 nanosec
fetch from L1 cache memory 0.5 nanosec
branch misprediction 5 nanosec
fetch from L2 cache memory 7 nanosec
Mutex lock/unlock 25 nanosec
fetch from main memory 100 nanosec
send 2K bytes over 1Gbps network 20,000 nanosec
read 1MB sequentially from memory 250,000 nanosec
fetch from new disk location (seek) 8,000,000 nanosec
read 1MB sequentially from disk 20,000,000 nanosec
send packet US to Europe and back 150 milliseconds = 150,000,000 nanosec

Appendix: Language Choice

Several people have asked what programming language they should learn first. There is no one answer, but consider these points:

  • Use your friends. When asked “what operating system should I use, Windows, Unix, or Mac?”, my answer is usually: “use whatever your friends use.” The advantage you get from learning from your friends will offset any intrinsic difference between OS, or between programming languages. Also consider your future friends: the community of programmers that you will be a part of if you continue. Does your chosen language have a large growing community or a small dying one? Are there books, web sites, and online forums to get answers from? Do you like the people in those forums?
  • Keep it simple. Programming languages such as C++ and Java are designed for professional development by large teams of experienced programmers who are concerned about the run-time efficiency of their code. As a result, these languages have complicated parts designed for these circumstances. You’re concerned with learning to program. You don’t need that complication. You want a language that was designed to be easy to learn and remember by a single new programmer.
  • Play. Which way would you rather learn to play the piano: the normal, interactive way, in which you hear each note as soon as you hit a key, or “batch” mode, in which you only hear the notes after you finish a whole song? Clearly, interactive mode makes learning easier for the piano, and also for programming. Insist on a language with an interactive mode and use it.

Given these criteria, my recommendations for a first programming language would be Python or Scheme. Another choice is Javascript, not because it is perfectly well-designed for beginners, but because there are so many online tutorials for it, such as Khan Academy’s tutorial. But your circumstances may vary, and there are other good choices. If your age is a single-digit, you might prefer Alice or Squeak or Blockly (older learners might also enjoy these). The important thing is that you choose and get started.


Appendix: Books and Other Resources

Several people have asked what books and web pages they should learn from. I repeat that “book learning alone won’t be enough” but I can recommend the following:


Notes

T. Capey points out that the Complete Problem Solver page on Amazon now has the “Teach Yourself Bengali in 21 days” and “Teach Yourself Grammar and Style” books under the “Customers who shopped for this item also shopped for these items” section. I guess that a large portion of the people who look at that book are coming from this page. Thanks to Ross Cohen for help with Hippocrates.

Linux

简言之,我想对那些觉得 Linux 永远也学不会的“菜鸟”们说:

  1. Linux 和 Unix 里面包含了一些非常糟糕的设计。不要被 Unix 的教条主义者吓倒。学不会有些东西很多时候不是你的错,而是 Linux 的错,是“Unix 思想” 的错。不要浪费时间去学习太多工具的用法,钻研稀奇古怪的命令行。那些貌似难的,复杂的东西,特别要小心分析。
  2. Windows 避免了 Unix,Linux 和 Mac OS X 的很多问题。微软是值得尊敬的公司,是真正在乎程序开发工具的公司。我收回曾经对微软的鄙视态度。请菜鸟们吸收 Windows 设计里面好的东西。另外 Visual Studio 是非常好的工具,会带来编程效率的大幅度提升。请不要歧视 IDE。要正视 Emacs,VIM 等文本编辑器的局限性。当然,这些正面评价不等于说你应该为微软工作。就像我喜欢 iPhone,但是却不一定想给 Apple 工作一样。
  3. 学习操作系统最好的办法是学会(真正的)程序设计思想,而不是去“学习”各种古怪的工具。所有操作系统,数据库,Internet,以至于 WEB 的设计思想(和缺陷),几乎都能用程序语言的思想简单的解释。

先说说我现在对 Linux 和相关工具(比如 TeX)的看法吧。我每天上班都用 Linux,可是回家才不想用它呢。上班的时候,我基本上只是尽我所能的改善它,让它不要给我惹麻烦。Unix 有许许多多的设计错误,却被当成了教条,传给了一代又一代的程序员,恶性循环。Unix 的 shell,命令,配置方式,图形界面,都是相当糟糕的。每一个新版本的 Ubuntu 都会在图形界面的设计上出现新的错误,让你感觉历史怎么会倒退。其实这只是表面现象。Linux 所用的图形界面(X Window)在本质上几乎是没救的。我不想在这里细说 Unix 的缺点,在它出现的早期,已经有人写了一本书,名叫 Unix Hater’s Handbook,里面专门有一章叫做 The X-Windows Disaster。它分析后指出,X Window 貌似高明的 client-server 设计,其实并不像说的那么好。

这本书汇集了 Unix 出现的年代,很多人对它的咒骂。有趣的是,这本书有一个“反序言”,是 Unix 的创造者之一 Dennis Ritchie 写的。我曾经以为这些骂 Unix 的人都是一些菜鸟。他们肯定是智商太低,或者被 Windows 洗脑了,不能理解 Unix 的高明设计才在那里骂街。现在理解了程序语言的设计原理之后,才发现他们说的那些话里面居然大部分是实话!其实他们里面有些人在当年就是世界顶尖的编程高手,自己写过操作系统和编译器,功底不亚于 Unix 的创造者。在当年他们就已经使用过设计更加合理的系统,比如 Multics,Lisp Machine 等。

可惜的是,在现在的操作系统书籍里面,Multics 往往只是被用来衬托 Unix 的“简单”和伟大。Unix 的书籍喜欢在第一章讲述这样的历史:“Multics 由于设计过于复杂,试图包罗万象,而且价格昂贵,最后失败了。” 可是 Multics 失败了吗?Multics,Oberon,IBM System/38, Lisp Machine,…… 在几十年前就拥有了 Linux 现在都还没有的好东西。Unix 里面的东西,什么虚拟内存,文件系统,…… 基本上都是从 Multics 学来的。Multics 的机器,一直到 2000 年都还在运行。Unix 不但“窜改”了历史教科书,而且似乎永远不吸取教训,到现在还没有实现那些早期系统早就有的好东西。Unix 的设计几乎完全没有一致性和原则。各种工具程序功能重复,冗余,没法有效地交换数据。可是最后 Unix 靠着自己的“廉价”,“宗教”和“哲学”,战胜了别的系统在设计上的先进,统治了程序员的世界。

如果你想知道这些“失败的”操作系统里面有哪些我们现在都还没有的先进技术,可以参考这篇文章:Oberon - The Overlooked Jewel。它介绍的是 Niklaus Wirth(也就是 Pascal 语言的设计者)的 Oberon 操作系统。

胜者为王,可是 Unix 其实是一个暴君,它不允许你批评它的错误。它利用其它程序员的舆论压力,让每一个系统设计上的错误,都被说成是用户自己的失误。你不敢说一个工具设计有毛病,因为如果别人听到了,就会以为你自己不够聪明,说你“人笨怪刀钝”。这就像是“皇帝的新装”里的人们,明明知道皇帝没穿衣服,还要说“这衣服这漂亮”!总而言之,“对用户友好”这个概念,在 Unix 的世界里是被歧视,被曲解的。Unix 的狂热分子很多都带有一种变态的“精英主义”。他们以用难用的工具为豪,鄙视那些使用“对用户友好”的工具的人。

我曾经强烈的推崇 FVWM,TeX 等工具,可是现在擦亮眼睛看来,它们给用户的界面,其实也是非常糟糕的设计,跟 Unix 一脉相承。他们把程序设计的许多没必要的细节和自己的设计失误,无情的暴露给用户。让用户感觉有那么多东西要记,仿佛永远也没法掌握它。实话说吧,当年我把 TeXbook 看了两遍,做完了所有的习题(包括最难的“double bend”习题)。几个月之后,几乎全部忘记干净。为什么呢?因为 TeX 的语言是非常糟糕的设计,它没有遵循程序语言设计的基本原则。

这里有一个鲜为人知的小故事。TeX 之所以有一个“扩展语言”,是 Scheme 的发明者 Guy Steele 的建议。那年夏天,Steele 在 Stanford 实习。他听说 Knuth 在设计一个排版系统,就强烈建议他使用一种扩展语言。后来 Knuth 采纳了他的建议。不幸的是 Steele 几个月后就离开了,没能帮助 Knuth 完成语言的设计。Knuth 老爹显然有我所说的那种“精英主义”,他咋总是设计一些难用的东西,写一些难懂的书?

一个好的工具,应该只有少数几条需要记忆的规则,就像象棋一样。而这些源于 Unix 的工具却像是“魔鬼棋”或者“三国杀”,有太多的,无聊的,人造的规则。有些人鄙视图形界面,鄙视 IDE,鄙视含有垃圾回收的语言(比如 Java),鄙视一切“容易”的东西。他们却不知道,把自己沉浸在别人设计的繁复的规则中,是始终无法成为大师的。就像一个人,他有能力学会各种“魔鬼棋”的规则,却始终无法达到象棋大师的高度。所以,容易的东西不一定是坏的,而困难的东西也不一定是好的。学习计算机(或者任何其它工具),应该“只选对的,不选难的”。记忆一堆的命令,乌七八糟的工具用法,最后脑子里什么也不会留下。学习“原理性”的东西,才是永远不会过时的。

Windows

Windows 技术设计上的很多细节,也许在早期是同样糟糕的。但是它却向着更加结构化,更加简单的方向发展。Windows 的技术从 OLE,COM,发展到 .NET,再加上 Visual Studio 这样高效的编程工具,这些带来了程序员和用户效率的大幅度提高,避免了 Unix 和 C 语言的很多不必存在的问题。Windows 程序从很早的时候就能比较方便的交换数据。比如,OLE 让你可以把 Excel 表格嵌入到 Word 文档里面。不得不指出,这些是非常好的想法,是超越“Unix 哲学”的。相反,由于受到“Unix 哲学”的误导,Unix 的程序间交换数据一直以来都是用字符串,而且格式得不到统一,以至于很多程序连拷贝粘贴都没法正确进行。Windows 的“配置”,全都记录在一个中央数据库(注册表)里面,这样程序的配置得到大大的简化。虽然在 Win95 的年代,注册表貌似老是惹麻烦,但现在基本上没有什么问题了。相反,Unix 的配置,全都记录在各种稀奇古怪的配置文件里面,分布在系统的各个地方。你搞不清楚哪个配置文件记录了你想要的信息。每个配置文件连语法都不一样!这就是为什么用 Unix 的公司总是需要一个“系统管理员”,因为软件工程师们才懒得记这些麻烦的东西。

Mac

再来比较一下 Windows 和 Mac 吧。我认识一个 Adobe 的高级设计师。他告诉我说,当年他们把 Photoshop 移植到 Intel 构架的 Mac,花了两年时间。只不过换了个处理器,移植个应用程序就花了两年时间,为什么呢?因为 Xcode 比起 Visual Studio 真是差太多了。而 Mac OS X 的一些设计原因,让他们的移植很痛苦。不过他很自豪的说,当年很多人等了两年也没有买 Intel 构架的 Mac,就是因为他们在等待 Photoshop。最后他直言不讳的说,微软其实才是真正在乎程序员工具的公司。相比之下,Apple 虽然对用户显得友好,但是对程序员的界面却差很多。Apple 尚且如此,Linux 对程序员就更差了。可是有啥办法呢,有些人就是受虐狂。自己痛过之后,还想让别人也痛苦。就像当年的我。

我当然不是人云亦云。微软在程序语言上的造诣和投入,我看得很清楚。我只是通过别人的经历,来验证我已经早已存在的看法。所以一再宣扬别的系统都是向自己学习的 Apple 受到这样的评价,我也一点不惊讶。Mac OS X 毕竟是从 Unix 改造而来的,还没有到脱胎换骨的地步。我有一个 Macbook Air,一个 iPhone 5,和一个退役的,装着 Windows 7 的 T60。我不得不承认,虽然我很喜欢 Macbook 和 iPhone 的硬件,但我发现 Windows 在软件上的很多设计其实更加合理。

我为什么当年会鄙视微软?这很简单。我就是跟着一群人瞎起哄而已!他们说 Linux 能拯救我们,给我们自由。他们说微软是邪恶的公司…… 到现在我身边还有人无缘无故的鄙视微软,却不知道理由。可是 Unix 是谁制造的呢?是 AT&T。微软和 AT&T 哪个更邪恶呢?我不知道。但是你应该了解一下 Unix 的历史。AT&T 当年发现 Unix 有利可图,找多少人打了多少年官司?说微软搞垄断,其实 AT&T 早就搞过垄断了,还被拆散成了好几个公司。想想世界上还有哪一家公司,独立自主的设计出这从底至上全套家什:程序语言,编译器,IDE,操作系统,数据库,办公软件,游戏机,手机…… 我不得不承认,微软是值得尊敬的公司。

公司还不都一样,都是以利益为本的。我们程序员就不要被他们利用,作为利益斗争的炮灰啦。见到什么好就用什么,就学什么。自己学到的东西,又不属于那些垄断企业。我们都有自由的头脑。

当然我不是在这里打击 Linux 和 Mac 而鼓吹 Windows。这些系统的纷争基本上已经不关我什么事。我只是想告诉新人们,去除头脑里的宗教,偏激,仇恨和鄙视。每次仇恨一个东西,你就失去了向它学习的机会。

本文把程序员所需掌握的关键知识总结为三大类19个关键概念,然后给出了掌握每个关键概念所需的入门书籍,必读书籍,以及延伸阅读。旨在成为最好最全面的程序员必读书单。

前言

Reading makes a full man;

conference a ready man;

and writing an exact man.

优秀的程序员应该具备两方面能力:

  • 良好的程序设计能力:
    • 掌握常用的数据结构和算法(例如链表,栈,堆,队列,排序和散列);
    • 理解计算机科学的核心概念(例如计算机系统结构、操作系统、编译原理和计算机网络);
    • 熟悉至少两门以上编程语言(例如 C++,Java,C#,和 Python);
  • 专业的软件开发素养:
    • 具备良好的编程实践,能够编写可测试(Testable),可扩展(Extensible),可维护(Maintainable)的代码;
    • 把握客户需求,按时交付客户所需要的软件产品;
    • 理解现代软件开发过程中的核心概念(例如面向对象程序设计,测试驱动开发,持续集成,和持续交付等等)。

和其它能力一样, 程序设计 能力和 软件开发 素养源自项目经验和书本知识。项目经验因人而异(来自不同领域的程序员,项目差异会很大);但书本知识是相通的——尤其是经典图书,它们都能够拓宽程序员的视野,提高程序员的成长速度。

在过去几年的学习和工作中,我阅读了大量的程序设计/软件开发书籍。随着阅读量的增长,我意识到:

  • 经典书籍需要不断被重读——每一次重读都会有新的体会;
  • 书籍并非读的越多越好——大多数书籍只是经典书籍中的概念延伸(有时甚至是照搬);

意识到这两点之后,我开始思考一个很 功利 的问题:如何从尽可能少的书中,获取尽可能多的关键知识?换句话说:

  • 优秀的程序员应该掌握哪些关键概念?
  • 哪些书籍来可以帮助程序员掌握这些关键概念?

这即是这篇文章的出发点——我试图通过这篇文章来回答上面两个问题。

标准

进入必读书单之前,我先介绍下书单里的书籍选择标准和领域选择标准。

书籍选择标准

  1. 必读:什么是必读书籍呢?如果学习某项技术有一本书无论如何都不能错过,那么这本书就是必读书籍——例如Effective Java于Java,CLR via C#于C#;
    • 注意我没有使用“经典”这个词,因为经典计算机书籍往往和计算机科学联系在一起,而且经典往往需要10年甚至更长的时间进行考验;
  2. 注重实践,而非理论:所以这个书单不会包含过于原理性的书籍;
  3. 入门—必读—延伸:必读书籍的问题在于:
    1. 大多不适合入门;
    2. 不够全面。考虑到没有入门阅读和延伸阅读的阅读列表是不完整的——所以书单中每个关键概念都会由一本入门书籍,一本必读书籍(有时入门书籍和必读书籍是同一本),和若干延伸阅读书籍所构成。

概念选择标准

  1. 全面:全面覆盖软件开发中重要的概念;
  2. 通用:适用于每一个程序员,和领域特定方向无关;
  3. 注重基础,但不过于深入:优秀的程序员需要良好的计算机科学基础,但程序员并没必要掌握过于深入的计算机科学知识。以算法为例,每个程序员都应该掌握排序、链表、栈以及队列这些基本数据结构和算法,但计算几何、线性规划和网络流这些算法可能就不是每个程序员都需要掌握的了;

通过这几个标准,我把程序员应掌握的关键概念分为程序设计,软件开发,以及个人成长三大类,每一大类均由若干关键概念组成。

程序员必读书单

入门书籍

程序设计:

  1. 基础理论 : 编码:隐匿在计算机软硬件背后的语言
  2. 编程语言:
    • C : C 和指针
    • C++ : C++ 程序设计原理与实践
    • Java : Java 核心技术(第9版)
    • C# : 精通 C#(第6版)
    • JavaScript : JavaScript DOM编程艺术(第2版)
    • Python : Python 基础教程(第二版)
  3. 编程语言理论 : 编程语言实现模式
  4. 程序设计 : 程序设计方法
  5. 算法与数据结构 : 算法(第4版)
  6. 程序调试 : 调试九法——软硬件错误的排查之道

软件开发:

  1. 编程实践 : 程序设计实践
  2. 面向对象程序设计 : Head First设计模式
  3. 重构 : 重构
  4. 软件测试 : How to Break Software
  5. 项目管理 : 极客与团队
  6. 专业开发 : 程序员修炼之道:从小工到专家
  7. 大师之言 : 奇思妙想:15 位计算机天才及其重大发现
  8. 界面设计 : 写给大家看的设计书
  9. 交互设计 : 通用设计法则

个人成长:

  1. 职业规划 : 软件开发者路线图
  2. 思维方式 : 程序员的思维修炼:开发认知潜能的九堂课
  3. 求职面试 : 金领简历:敲开苹果微软谷歌的大门
  4. 英语写作 : The Only Grammar Book You’ll Ever Need

这个阅读列表覆盖了软件开发各个关键领域的入门书籍和必读书籍,我相信它可以满足绝大多数程序员的需求,无论你是初学者,还是进阶者,都可以从中获益:

  • 基础理论 包括了程序员应该掌握的计算机基础知识;
  • 编程语言 对软件开发至关重要,我选择了 C , C++ , Java , C# , Python ,和 JavaScript 这六门 主流编程语言 进行介绍,如果想进一步理解编程语言,可以阅读 编程语言理论 里的书目;
  • 在理解编程语言的基础上,优秀的程序员还应该了解各种 程序设计 技巧,熟悉基本的 算法数据结构 ,并且能够高效的进行 程序调试 。
  • 良好的程序设计能力是成为优秀程序员的前提,但软件开发知识也是必不可少的:优秀的程序员应具备良好的编程实践 ,知道如何利用面向对象 , 重构 ,和软件测试编写可复用,可扩展,可维护的代码,并具备软件项目管理知识和专业开发素养;
  • 就像我们可以从名人传记里学习名人的成功经验,程序员也可以通过追随优秀程序员的足迹使自己少走弯路。 大师之言 包含一系列对大师程序员/计算机科学家的访谈,任何程序员都可以从中获益良多;
  • 为了打造用户满意的软件产品,程序员应当掌握一定的 界面设计 知识和 交互设计 知识(是的,这些工作应该交给UI和UX,但如果你想独自打造一个产品呢?);
  • 专业程序员应当对自己进行 职业规划 ,并熟悉程序员 求职面试 的流程,以便在职业道路上越走越远;
  • 软件开发是一项需要不断学习的技能,学习 思维方式 可以有效的提升学习能力和学习效率;
  • 软件开发是一项国际化的工作,为了让更多的人了解你的代码(工作),良好的 英语写作 能力必不可少。

程序设计

1. 基础理论

编码:隐匿在计算机软硬件背后的语言

编码:隐匿在计算机软硬件背后的语言 这本书其实不应该叫编码——它更应该叫“Petzold教你造计算机”——作者 Charles Petzold 创造性的以编码为主题,从电报机和手电筒讲到数字电路,然后利用 数字电路 中的逻辑门构造出 加法器触发器 ,最后构造出一个完整的 存储程序计算机 。不要被这些电路概念吓到—— 编码 使用大量形象贴切的类比简化了这些概念,使其成为最精彩最通俗易懂的计算机入门读物。

深入理解计算机系统(第2版)

深入理解计算机系统(第2版) 这本书的全名是:Computer Systems:A Programmer’s Perspective(所以它又被称为 CSAPP),我个人习惯把它翻译为程序员所需了解的计算机系统知识,尽管土了些,但更名副其实。

深入理解计算机系统 是我读过的最优秀的计算机系统导论型作品,它创造性的把操作系统,计算机组成结构,数字电路,以及编译原理这些计算机基础学科中的核心概念汇集在一起,从而覆盖了指令集体系架构,汇编语言,代码优化,计算机存储体系架构,链接,装载,进程,以及虚拟内存这些程序员所需了解的关键计算机系统知识。如果想打下扎实的计算机基础又不想把操作系统计算机结构编译原理这些书统统读一遍,阅读 深入理解计算机系统 是最有效率的方式。

延伸阅读:

2. 编程语言

编程语言是程序员必不可少的日常工具。工欲善其事,必先利其器。我在这里给出了 C,C++,Java,C#,JavaScript,和Python 这六种 常用编程语言 的书单(我个人不熟悉 Objective-C 和 PHP,因此它们不在其中)。

需要注意的是:我在这里给出的是编程语言(Programming Language)书籍,而非编程平台(Programming Platform)书籍。以 Java 为例, Effective Java 属于编程语言书籍,而 Android编程权威指南 就属于编程平台书籍。

C

C和指针

忘记谭浩强那本糟糕不堪的 C 程序设计, C和指针 才是 C 语言的最佳入门书籍。它详细但又不失简练的介绍了 C 语言以及 C 标准库的方方面面。

对于C语言初学者,最难的概念不仅仅是指针和数组,还有指向数组的指针和指向指针的指针。 C和指针 花了大量的篇幅和图示来把这些难懂但重要的概念讲的清清楚楚,这也是我推荐它作为C语言入门读物的原因。

C程序设计语言(第2版)

尽管 C程序设计语言 是二十多年前的书籍,但它仍然是C语言——以及计算机科学中最重要的书籍之一,它的重要性不仅仅在于它用清晰的语言和简练的代码描述了 C 语言全貌,而且在于它为之后的计算机书籍——尤其是编程语言书籍树立了新的标杆。以至于在很多计算机书籍的扉页,都会有“感谢 Kernighan 教会我写作”这样的字样。

延伸阅读:

  • C 专家编程 :不要被标题中的“专家”吓到,这实际是一本很轻松的书籍,它既包含了大量 C 语言技术细节和编程技巧,也包含了很多有趣的编程轶事;
  • C 陷阱与缺陷 :书如其名,这本书介绍了 C 语言中常见的坑和一些稀奇古怪的编程“技巧”,不少刁钻的C语言面试题都源自这本小册子;
  • C 语言参考手册 :全面且权威的 C 语言参考手册,而且覆盖 C99,如果你打算成为 C 语言专家,那么这本书不可错过;
  • C 标准库 :给出了15个C标准库的设计思路,实现代码,以及测试代码,配合 C 程序设计语言 阅读效果更佳;
  • C 语言接口与实现 :这本书展示了如何使用C语言实现可复用的数据结构,其中包含大量 C 语言高级技巧,以至于 Amazon 上排行第一的评论是 “Probably the best advanced C book in existance”,而排行第二的评论则是 “By far the most advanced C book I read”。

C++

C++ 程序设计原理与实践

作为C++的发明者,没有人能比 Bjarne Stroustrup 更理解C++。Bjarne在Texas A&M大学任教时使用C++为大学新生讲授编程,从而就有了 C++ 程序设计原理与实践 这本书——它面向编程初学者,既包含 C++ 教程,也包含大量程序设计原则。它不但是我读过最好的C++入门书,也是我读过最好的编程入门书。

比较有趣的是, C++ 程序设计原理与实践 直到全书过半都没有出现指针,我想这可能是Bjarne为了证明不学C也可以学好C++吧。

C++ 程序设计语言(第4版)

同样是 Bjarne Stroustrup 的作品, C++ 程序设计语言 是 C++ 最权威且最全面 的书籍。第4版相对于之前的版本进行了全面的更新,覆盖了第二新的C++ 11标准,并砍掉了部分过时的内容。

延伸阅读:

  • A Tour of C++ :如果你觉得 C++程序设计语言 过于庞大,但你又想快速的浏览一遍新版 C++ 的语言特色,那么可以试试这本小红书;
  • C++ 语言的设计与演化 :C++ 的“历史书”,讲述了 C++ 是如何一步一步从 C with Classes 走到如今这一步,以及 C++ 语言特性背后的故事;
  • C++ 标准库(第2版) :相对于其它语言的标准库,C++ 标准库虽然强大,但学习曲线十分陡峭,这本书是学习 C++ 标准库有力的补充;
  • 深度探索 C++ 对象模型 :这本书系统的讲解了 C++ 是如何以最小的性能代价实现对象模型,很多C++面试题(包括被问烂的虚函数指针)都可以在这本书里找到答案;
  • Effective C++More Effective C++ :由于 C++ 的特性实在繁杂,因此很容易就掉到坑里。Effective 系列既讲述了 C++ 的良好编程实践,也包含C++的使用误区,从而帮你绕过这些坑。

Java

Java 核心技术(第9版)

平心而论 Java 核心技术 (即Core Java)并不算是一本特别出色的书籍:示例代码不够严谨,充斥着很多与C/C++的比较,语言也不够简洁——问题在于Java并没有一本很出色的入门书籍,与同类型的 Java 编程思想 相比, Java 核心技术 至少做到了废话不多,与时俱进( Java 编程思想 还停留在 Java 6之前),矮子里面选将军, Java 核心技术 算不错了。

Effective Java(第 2 版)

尽管 Java 没有什么出色的入门书籍,但这不代表 Java 没有出色的必读书籍。 Effective Java 是我读过的最好的编程书籍之一,它包含大量的优秀Java编程实践,并对泛型和并发这两个充满陷阱的 Java 特性给出了充满洞察力的建议,以至于 Java 之父 James Gosling 为这本书作序:“我很希望 10 年前就拥有这本书。可能有人认为我不需要任何 Java 方面的书籍,但是我需要这本书。”

延伸阅读:

  • 深入理解 Java 虚拟机(第2版) :非常优秀且难得的国产佳作,系统的介绍了 Java 虚拟机和相关工具,并给出了一些调优建议;
  • Java 程序员修炼之道 :在这本书之前,并没有一本 Java 书籍系统详细的介绍 Java 7 的新特性(例如新的垃圾收集器,try using 结构和 invokedynamic 指令),这本书填补了这个空白;
  • Java 并发编程实践 :系统全面的介绍了 Java 的并发,如何设计支持并发的数据结构,以及如何编写正确的并发程序;
  • Java Puzzlers :包含了大量的 Java 陷阱——以至于读这本书时我说的最多的一个词就是 WTF,这本书的意义在于它是一个 反模式 大全, Effective Java 告诉你如何写好的 Java 程序,而 Java Puzzlers 则告诉你糟糕的 Java 程序是什么样子。更有意思的是,这两本书的作者都是 Joshua Bloch

C#

精通 C#(第6版)

可能你会疑问我为什么会推荐这本接近 1200 页的“巨著”用作 C# 入门,这是我的答案:

  1. C# 的语言特性非常丰富,很难用简短的篇幅概括这些特性;
  2. 精通 C# 之所以有近 1200 页的篇幅,是因为它不但全面介绍了 C# 语言,而且还覆盖了 ADO.NET,WCF,WF,WPF,以及 ASP.NET 这些 .Net 框架。你可以把这本书视为两本书——一本 500 多页的 C# 语言教程和一本 600 多页的 .Net 平台框架快速上手手册。
  3. 尽管标题带有“精通”两字, 精通 C# 实际上是一本面向初学者的C#书籍,你甚至不需要太多编程知识,就可以读懂它。

CLR via C#(第 4 版)

CLR via C# 是C#/.Net最重要的书籍,没有之一。它全面介绍了 .Net 的基石—— CLR 的运行原理,以及构建于 CLR 之上的 C# 类型系统,运行时关系,泛型,以及线程/并行等高级内容。任何一个以 C# 为工作内容的程序员都应该阅读此书。

延伸阅读:

  • 深入理解 C#(第 3 版) :C# 进阶必读,这本书偏重于C#的语言特性,它系统的介绍了C#从1.0到C# 4.0的语言特性演化,并展示了如何利用C#的语言特性编写优雅的程序;
  • .NET设计规范(第 2 版) :C# 专业 程序员必读,从变量命名规范讲到类型系统设计原则,这本书提供了一套完整的.Net编程规范,使得程序员可以编写出一致,严谨的代码,
  • C# 5.0 权威指南 :来自 O’Reilly 的 C# 参考手册,严谨的介绍了 C# 语法,使用,以及核心类库,C#程序员案头必备;
  • LINQ to Objects Using C# 4.0Async in C# 5.0 :LINQ 和 async 分别是 .Net 3.5 和 .Net 4.5 中所引入的最重要的语言特性,所以我认为有必要在它们上面花点功夫——这两本书是介绍 LINQ 和 async 编程的最佳读物。

JavaScript

JavaScript DOM 编程艺术(第 2 版)

尽管JavaScript现在可以做到客户端服务器端通吃,尽管 JQuery 之类的前端框架使得一些人可以不懂JavaScript也可以编程,但我还是认为学习JavaScript从HTML DOM开始最为适合,因为这是JavaScript设计的初衷。 JavaScript DOM编程艺术 系统的介绍了如何使用JavaScript,HTML,以及 CSS 创建可用的 Web 页面,是一本前端入门佳作。

JavaScript 语言精粹

JavaScript语言包含大量的陷阱和误区,但它却又有一些相当不错的特性,这也是为什么 Douglas Crockford 称JavaScript为 世界上最被误解的语言 ,并编写了 JavaScript 语言精粹 一书来帮助前端开发者绕开JavaScript中的陷阱。和同类书籍不同, JavaScript 语言精粹 用精炼的语言讲解了JavaScript语言中好的那部分(例如闭包,函数是头等对象,以及对象字面量),并建议读者 不要 使用其它不好的部分(例如混乱的类型转换,默认全局命名空间,以及 奇葩的相等判断符 ),毕竟,用糟糕的特性编写出来的程序往往也是糟糕的。

延伸阅读:

Python

Python 基础教程(第二版)

Python 的入门书籍很多,而且据说质量大多不错,我推荐 Python 基础教程 的原因是因为它是我的Python入门读物——简洁,全面,代码质量很不错,而且有几个很有趣的课后作业,使得我可以快速上手。

这里顺便多说一句,不要用 Python 学习手册 作为Python入门——它的废话实在太多,你能想象它用了15页的篇幅去讲解if语句吗?尽管 O’Reilly 出了很多经典编程书,但这本 Python 学习手册 绝对不在其中。

Python 参考手册(第 4 版)

权威且实用 Python 书籍,覆盖 Python 2和 Python 3。尽管它名为参考手册,但 Python 参考手册 在 Python 语法和标准库基础之上对其实现机制也给出了深入的讲解,不容错过。

延伸阅读:

3. 编程语言理论

编程语言实现模式

大多数程序员并不需要从头编写一个编译器或解释器,因此 龙书(编译原理) 就显得过于重量级;然而多数程序员还是需要解析文本,处理配置文件,或者写一个小语言, 编程语言实现模式 很好的满足了这个需求。它把常用的文本解析/代码生成方法组织成一个个模式,并为每个模式给出了实例和应用场景。这本书既会提高你的动手能力,也会加深你对编程语言的理解。Python 发明者 Guido van Rossum 甚至为这本书给出了 “Throw away your compiler theory book!” 这样的超高评价。

程序设计语言——实践之路(第 3 版)

程序员每天都要和编程语言打交道,但是思考编程语言为什么会被设计成这个样子的程序员并不多, 程序设计语言——实践之路 完美的回答了这个问题。这本书从编程语言的解析和运行开始讲起,系统了介绍了命名空间,作用域,控制流,数据类型以及方法(控制抽象)这些程序设计语言的核心概念,然后展示了这些概念是如何被应用到过程式语言,面向对象语言,函数式语言,脚本式,逻辑编程语言以及并发编程语言这些具有不同编程范式的编程语言之上。这本书或极大的拓宽你的视野——无论你使用什么编程语言,都会从这本书中获益良多。理解这一本书,胜过学习十门新的编程语言。

延伸阅读:

  • 七周七语言:理解多种编程范型 :尽管我们在日常工作中可能只使用两三门编程语言,但是了解其它编程语言范式是很重要的。 七周七语言 一书用精简的篇幅介绍了 Ruby,Io,Prolog,Scala,Erlang,Clojure,和 Haskell 这七种具有不同编程范式的语言——是的,你没法通过这本书变成这七种语言的专家,但你的视野会得到极大的拓宽;
  • 自制编程语言 :另一本优秀的编译原理作品, 自制编程语言 通过从零开始制作一门无类型语言 Crowbar 和一门静态类型语言 Diksam,把类型系统,垃圾回收,和代码生成等编程语言的关键概念讲的清清楚楚;
  • 计算的本质:深入剖析程序和计算机 :披着 Ruby 外衣的 计算理论 入门书籍,使你对编程语言的理解更上一层楼。

4. 程序设计

程序设计方法

现代编程语言的语法大多很繁杂,初学者使用这些语言学习编程会导致花大量的时间在编程语言语法(诸如指针,引用和类型定义)而不是程序设计方法(诸如数据抽象和过程抽象)之上。 程序设计方法 解决了这个问题——它专注于程序设计方法,使得读者无需把大量时间花在编程语言上。这本书还有一个与之配套的教学开发环境 DrScheme ,这个环境会根据读者的程度变换编程语言的深度,使得读者可以始终把注意力集中在程序设计方法上。

我个人很奇怪 程序设计方法 这样的佳作为什么会绝版,而谭浩强C语言这样的垃圾却大行其道——好在是程序设计方法 第二版 已经被免费发布在网上。

计算机程序的构造与解释(第 2 版)

计算机程序的构造与解释 是另一本被国内大学忽视(至少在我本科时很少有人知道这本书)的教材,这本书和 程序设计方法 有很多共同点——都使用 Scheme作为教学语言;都专注于程序设计方法而非编程语言本身;都拥有相当出色的课后题。相对于 程序设计方法计算机程序的构造与解释 要更加深入程序设计的本质(过程抽象,数据抽象,以及元语言抽象),以至于 Google 技术总监 Peter Norvig 给了这本书 超高的评价

延伸阅读:

  • 编程原本STL 作者的关于程序设计方法佳作——他把关系代数和群论引入编程之中,试图为程序设计提供一个坚实的理论基础,从而构建出更加稳固的软件。这本书是 程序设计方法计算机程序的构造与解释 的绝好补充——前者使用函数式语言(Scheme)讲授程序设计,而 编程原本 则使用命令式语言(C++);
  • 元素模式设计模式 总结了 面向对象程序设计 中的模式,而 元素模式 这本书分析了 程序设计 中的常见模式的本质,阅读这本书会让你对程序设计有更深的理解;
  • The Science of Programming :会编程的人很多,但能够编写正确程序的人就少多了。 The Science of Programming 通过 前条件——不变式——后条件 以及逻辑谓词演算,为编写正确程序提供了强有力的理论基础,然后这本书通过实例阐述了如何应用这些理论到具体程序上。任何一个想大幅提高开发效率的程序员都应阅读此书。

5. 算法与数据结构

算法(第 4 版)

我在 算法学习之路 一文中提到我的算法入门教材是 数据结构与算法分析:C语言描述 ,我曾经认为它是最好的算法入门教材,但自从我读到 Sedgewick算法 之后我就改变了观点——这本 算法 才是最好的算法入门教材:

  • 使用更为容易的Java语言作为教学语言;
  • 覆盖所有常用的数据结构和算法,并均给出其完整实现;
  • 包含大量的图示用于可视化算法——事实上这是我读过的图示最为丰富形象的书籍,这也是我称其为最好的算法入门书籍的原因。

编程珠玑(第 2 版)

编程珠玑(第 2 版) 是一本少见的实践型算法书籍——它并非一一介绍数据结构/算法的教材,而是实践性极强的算法应用手册。作者( Jon Bentley )从他多年的实际经验精选出一些有趣而又实用的问题,然后展示了他解决这些问题的过程(分析问题,选择合适的算法,解决问题,以及验证答案)。任何程序员都可以从中获益。

延伸阅读:

  • 编程珠玑(续) :严格来说这本书并非 编程珠玑 的续作,而是一本类似于番外篇的编程技巧/实践手册;它不像 编程珠玑 那般重视算法的应用,而是全面覆盖了程序员所需的能力;
  • 算法导论(第 3 版) :尽管我在这边文章开头提到会尽量避免理论性的书籍,但没有 算法导论 的算法阅读列表是不完整的,我想这本书就不需要我多介绍了; :-)
  • 算法设计与分析基础(第 3 版) :侧重于算法设计,这本书创新的把常见算法分为分治,减治,变治三大类,并覆盖了动态规划,回溯,以及分支定界等高级算法设计方法,属于算法设计的入门佳作。

6. 程序调试

调试九法——软硬件错误的排查之道

一个让非编程从业人员惊讶的事实是程序员的绝大多时间都花在调试上,而不是写程序上,以至于 Bob 大叔调试时间占工作时间的比例 作为衡量程序员开发能力的标准。 调试九法——软硬件错误的排查之道 既是调试领域的入门作品,也是必读经典之作。 调试九法 的作者是一个具有丰富实战经验的硬件工程师,他把他多年的调试经验总结成九条调试法则,并对每一条法则都给对应的实际案例。任何程序员都应通过阅读这本书改善调试效率,即便是非程序员,也可以从这本书中学到系统解决问题的方法。

延伸阅读:

  • Writing Solid Code最好的调试是不调试—— Writing Solid Code 介绍了断言,设计清晰的 API,以及单步代码等技巧,用于编写健壮的代码,减少调试的时间;
  • 软件调试的艺术 :调试工具书——这本书详细的介绍了常见的调试器工具,并通过具体案例展示了它们的使用技巧;

软件开发

1. 编程实践

程序设计实践

Brian Kernighan 是这个星球上最好的计算机书籍作者:从上古时期的 Software Tools ,到早期的 Unix编程环境C 程序设计语言 ,再到这本 程序设计实践 ,每本书都是必读之作。

尽管程序设计实践只有短短 200 余页,但它使用精炼的代码和简要的原则覆盖了程序设计的所有关键概念(包括编程风格,算法与数据结构,API 设计,调试,测试,优化,移植,以及领域特定语言等概念)。如果你想快速掌握良好的编程实践,或者你觉着900多页的 代码大全 过于沉重,那么程序设计实践是你的不二之选。我第一次读这本书就被它简洁的语言和优雅的代码所吸引,以至于读研时我买了三本程序设计实践——一本放在学校实验室,一本放在宿舍,一本随身携带阅读。我想我至少把它读了十遍以上——每一次都有新的收获。

代码大全(第2版)

无论在哪个版本的程序员必读书单, 代码大全 都会高居首位。和其它程序设计书籍不同, 代码大全 用通俗清晰的语言覆盖了软件构建(Software Construction)中各个层次上 所有 的重要概念——从变量命名到类型设计,从控制循环到代码结构,从测试和调试到构建和集成, 代码大全 可谓无所不包,你可以把这本书看作为程序员的一站式(Once and for all)阅读手册。更珍贵的是, 代码大全 在每一章末尾都给出了价值很高的参考书目(参考我之前的 如何阅读书籍 一文),如果你是一个初出茅庐的程序员, 代码大全 是绝好的阅读起点。

延伸阅读:

  • 编写可读代码的艺术 :专注于代码可读性(Code Readability),这本书来自 Google 的两位工程师对 Google Code Readability 的总结。它给出了大量命名,注释,代码结构,以及 API 设计等日常编码的最佳实践,并包含了很多看似细微但却可以显著提升代码可读性的编程技巧。这本书的翻译还不错,但如果你想体会书中的英语幽默(例如Tyrannosaurus——Stegosaurus——Thesaurus),建议阅读它的 英文影印版
  • 卓有成效的程序员 :专注于生产效率(Productivity),它既包含源自作者多年经验的高生产率原则,也包含大量的提高生产率的小工具,每个追求高生产率的程序员都应该阅读这本书;
  • UNIX编程艺术 :专注于程序设计哲学,这本书首先总结出包括模块化,清晰化,可组合,可分离等17个Unix程序设计哲学,接下来通过 Unix 历史以及各种 Unix 编程工具展示了这些原则的应用。尽管个人觉的这本书有些过度拔高 Unix 且过度贬低 Windows 和 M$,但书中的 Unix 设计哲学非常值得借鉴。

2. 面向对象程序设计

Head First 设计模式

无论是在 Amazon 还是在 Google 上搜索设计模式相关书籍, Head First 设计模式 都会排在首位——它使用风趣的语言和诙谐的图示讲述了观察者,装饰者,抽象工厂,和单例等关键设计模式,使得初学者可以迅速的理解并掌握设计模式。 Head First 设计模式 在Amazon上 好评如潮 ,就连设计模式原书作者 Erich Gamma 都对它给出了很高的评价。

需要注意, Head First设计模式 是非常好的设计模式入门书,但 千万不要 把这本书作为学习设计模式的唯一的书——是的,Head First 设计模式拥有风趣的语言和诙谐的例子,但它既缺乏 实际 的工程范例,也没有给出设计模式的应用/适用场景。我个人建议是在读过这本书之后立即阅读 “四人帮”设计模式Bob 大叔敏捷软件开发 ,以便理解设计模式在实际中的应用。

设计模式

设计模式 作为设计模式领域的开山之作,Erich Gamma,Richard Helm,Ralph Johnson等四位作者将各个领域面向对象程序开发的经验总结成三大类23种模式,并给出了每个模式的使用场景,变体,不足,以及如何克服这些不足。这本书行文严谨紧凑(四位作者都是PhD),并且代码源自实际项目,属于设计模式领域的必读之作。

需要注意: 设计模式 不适合 初学者阅读——它更像是一篇博士论文而非技术书籍,加上它的范例都具有很强的领域背景(诸如 GUI 窗口系统和富文本编辑器),缺乏实际经验的程序员很难理解这本书。

延伸阅读:

3. 重构

重构

任何产品代码都不是一蹴而就,而是在反复不断的修改中进化而来。 重构 正是这样一本介绍如何改进代码的书籍——如何在保持代码行为的基础上,提升代码的质量(这也是重构的定义)。

我见过很多程序员,他们经常声称自己在重构代码,但他们实际只做了第二步(提升代码的质量),却没有保证第一步(保持代码行为),因此他们所谓的重构往往会适得其反——破坏现有代码或是引入新 bug。这也是我推荐 重构 这本书的原因——它既介绍糟糕代码的特征(Bad smell)和改进代码的方法,也给出了重构的完整流程——1. 编写单元测试保持(Preserve)程序行为;2. 重构代码;3. 保证单元测试通过。 重构 还引入了一套重构术语(诸如封装字段,内联方法,和字段上移),以便程序员之间交流。只有理解了这三个方面,才能算是理解重构。

修改代码的艺术

这里再重复一遍重构的定义——在保持代码行为的基础上,提升代码的质量。 重构 专注于第二步,即如何提升代码的质量,而 修改代码的艺术 专注于第一步,即如何保持代码的行为。

提升代码质量并不困难,但保持代码行为就难多了,尤其是对没有测试的遗留代码(Legacy Code)而言——你需要首先引入测试,但遗留代码往往可测试性(Testability)很差,这时你就需要把代码变的可测试。 修改代码的艺术 包含大量的实用建议,用来把代码变的可测试(Testable),从而使重构变为可能,使提高代码质量变为可能。

延伸阅读:

  • 重构与模式 :这本书的中文书名存在误导,它的原书书名是 Refactoring to Patterns——通过重构,把模式引入代码。这本书阐述了重构和设计模式之间的关系,使得程序员可以在更高的层次上思考重构,进行重构。

4. 软件测试

How to Break Software

关于软件测试的书籍很多,但很少有一本测试书籍能像 How to Break Software 这般既有趣又实用。不同于传统的软件测试书籍(往往空话连篇,无法直接应用), How to Break Software 非常实际——它从程序员的心理出发,分析软件错误/Bug最可能产生的路径,然后针对这些路径进行 残酷 的测试,以保证软件质量。

我在第一次阅读这本书时大呼作者太过“残忍”——连这些刁钻诡异的测试招数都能想出来。但这种毫不留情(Relentless)的测试风格正是每个专业程序员所应具备的心态。

注意:如果你是一个测试工程师,那么在阅读这本书前请三思——因为阅读它之后你会让你身边的程序员苦不堪言,甚至连掐死你的心都有 :-D。

xUnit Test Patterns

How to Break Software 注重黑盒测试,而这本 xUnit Test Patterns 则注重白盒测试。正如书名所示, xUnit Test Patterns 覆盖了单元测试的每个方面:从如何编写良好的单元测试,到如何设计可测试(Testable)的软件,再到如何重构测试——可以把它看作为单元测试的百科全书。

延伸阅读:

  • Practical Unit Testing with JUnit and Mockito :尽管 xUnit Test Patterns 覆盖了单元测试的方方面面,但它的问题在于不够与时俱进(07 年出版)。 Practical Unit Testing 弥补了这个缺陷——它详细介绍了如何通过测试框架 JUnit 和 Mock 框架 Mockito 编写良好的单元测试,并给出了大量优秀单元测试的原则;
  • 单元测试的艺术(第 2 版) :可以把这本书看作为前一本书的.Net版,适合.Net程序员;
  • Google 软件测试之道 :这本书详细介绍了 Google 如何测试软件——包括Google的软件测试流程以及Google软件测试工程师的日常工作/职业发展。需要注意的是:这本书中的测试流程在国内很可能行不通(国内企业缺乏像Google那般强大的基础设施(Infrastructure)),但它至少可以让国内企业有一个可以效仿的目标;
  • 探索式软件测试James Whittaker 的另一本测试著作,不同于传统的黑盒/白盒测试,这本书创造性的把测试比喻为“探索”(Exploration),然后把不同的探索方式对应到不同的测试方式上,以便尽早发现更多的软件错误/Bug。

5. 项目管理

极客与团队

很多程序员都向往成为横扫千军(One-man Army)式的“编程英雄”,但卓越的软件并非一人之力,而是由团队合力而成。 极客与团队 就是这样一本写给程序员的如何在团队中工作的绝好书籍,它围绕着 HRT 三大原则(Humility 谦逊,Respect 尊重,和 Trust 信任),系统的介绍了如何融入团队,如何打造优秀的团队,如何领导团队,以及如何应对团队中的害群之马(Poisonous People)。这本书实用性极强,以至于 Python 之父 Guido van Rossum 都盛赞这本书 “说出了我一直在做但总结不出来的东西”

人月神话

尽管 人月神话 成书于 40 年前,但它仍是软件项目管理重要的书籍。 人月神话 源自作者 Fred Brooks 领导并完成 System/360OS/360 这两个即是放到现在也是巨型软件项目的里程碑项目的经验总结。它覆盖了软件项目各个方面的关键概念:从工期管理( Brooks定律 )到团队建设( 外科团队 ),从程序设计(编程的本质是使用正确的数据结构)到架构设计( 概念完整性 ),从原型设计(Plan to Throw one away)到团队交流(形式化文档+会议)。令人惊讶的是,即便40年之后, 人月神话 中的关键概念(包括焦油坑, Brooks定律概念完整性外科团队第二版效应 等等)依然适用,而软件开发的 核心复杂度 仍然没有得到解决( 没有银弹 )。

延伸阅读:

  • 人件(原书第3版) :从人的角度分析软件项目。 人件 从雇佣正确的人,创建健康的工作环境,以及打造高效的开发团队等角度阐述了如何改善人,从而改善软件项目;
  • 门后的秘密:卓越管理的故事 :这本书生动的再现了软件项目管理工作的场景,并给出了各种实用管理技巧,如果你有意转向管理岗位,这本书不容错过;
  • 大教堂与集市 :这本书从黑客的历史说起,系统而又风趣的讲述了开源运动的理论和实践,以及开源软件项目是如何运作并发展的。了解开源,从这本书开始。

6. 专业开发

程序员修炼之道:从小工到专家

不要被庸俗的译名迷惑, 程序员修炼之道 是一本价值极高的程序员成长手册。这本书并不局限于特定的编程语言或框架,而是提出了一套切实可行的实效(Pragmatic)开发哲学,并通过程序设计,测试,编程工具,以及项目管理等方面的实例展示了如何应用这套开发哲学,从而使得程序员更加高效专业。有人把这本书称之为迷你版 代码大全 —— 代码大全 给出了大量的优秀程序设计实践,偏向术;而 程序员修炼之道 给出了程序设计实践背后的思想,注重道。

程序员职业素养

程序员修炼之道 指出了如何成为专业程序员,这本 程序员职业素养 则指出了专业程序员应该是什么样子——承担责任;知道自己在做什么;知道何时说不/何时说是;在正确的时间编写正确的代码;懂得自我时间管理和工期预估;知道如何应对压力。如果你想成为专业程序员(Professional Developer)(而不是码农(Code Monkey)),这本书会为你指明前进的方向。

延伸阅读:

7. 大师之言

奇思妙想:15 位计算机天才及其重大发现

奇思妙想:15 位计算机天才及其重大发现 是一本极具眼光的技术访谈书籍——在这本书访谈的 15 位计算机科学家中,竟出现了 12 位 图灵奖 获得者——要知道图灵奖从 1966 年设奖到现在也只有六十几位获奖者而已。

奇思妙想 把计算机科学分为四大领域:编程语言;算法;架构;人工智能。并选取了每个领域下最具代表性的计算机科学家进行访谈。因为这些计算机科学家都是其所在领域的开拓者,因此他们能给出常人无法给出的深刻见解。通过这本书,你可以了解前三十年的计算机科学的发展历程——计算机科学家做了什么,而计算机又能做到/做不到什么。从而避免把时间浪费在前人已经解决的问题(或者根本无法解决的问题)上面。

编程人生:15位软件先驱访谈录

同样是访谈录,同样访谈 15 个人, 编程人生 把重点放在程序员(Coders at work)上。它从各个领域选取了15位顶尖的程序员,这些程序员既包括 Ken ThompsonJamie Zawinski 这些老牌Unix黑客,也包括 Brad Fitzpatrick 这样的80后新生代,还包括 Frances AllenDonald Knuth 这样的计算机科学家。这种多样性(Diversity)使得 编程人生 兼具严谨性和趣味性,无论你是什么类型的程序员,都能从中受益良多。

延伸阅读:

  • 图灵和 ACM 图灵奖(1966-2011) :通过图灵奖介绍整个计算机科学发展史,非常难得的国产精品图书;
  • 编程大师访谈录 :可以把这本书看作为二十年前的 编程人生 ,被访谈者都是当时叱咤风云的人物(例如微软的创造者 Bill Gates ,Macintosh 的发明者 Jeff Raskin ,以及 Adobe 的创始人 John Warnock 等等)。有趣的是这本书中大量的经验和建议到如今依然适用;
  • 编程大师智慧 :类似于 编程人生 ,不同的是被访谈者都是编程语言的设计者——这本书覆盖了除C语言以外的几乎所有主流编程语言。通过这本书,你可以从中学到编程语言背后的设计思想——编程语言为什么要被设计成这样,是什么促使设计者要在语言中加入这个特性(或拒绝那个特性)。从而提升对编程语言的理解。

8. 界面设计

写给大家看的设计书

书如其名, 写给大家看的设计书 是一本面向初学者的快速设计入门。它覆盖了版式,色彩,和字体这三个设计中的关键元素,并创造性的为版式设计总结出CRAP四大原则(Contrast 对比,Repetition 重复,Alignment 对齐,Proximity 亲密)。全书使用丰富生动的范例告诉读者什么是好的设计,什么是不好的设计,使得即便是对设计一无所知的人,也可以从这本书快速入门。

认知与设计:理解UI设计准则(第 2 版)

写给大家看的设计书 强调实践,即如何做出好的设计; 认知与设计:理解 UI 设计准则 强调理论,即为什么我们会接受这样的设计而反感那样的设计。如果你想要搞清楚设计背后的心理学知识,但又不想阅读大部头的心理学著作,那么 认知与设计 是你的首选。

延伸阅读:

  • GUI 设计禁忌 2.0 :这本书指出了 GUI 设计的原则和常见误区,然后通过具体范例指出了如何避免这些误区。如果你的工作涉及到用户界面,那么这本书会为你减少很多麻烦;
  • 界面设计模式(第 2 版) :这本书将用户界面中的常见元素/行为组织成彼此关联的模式,以便读者理解并举一反三,从而将其运用到自己的应用中;
  • 移动应用 UI 设计模式 :类似于 界面设计模式 ,但面向移动平台。它给出了 iOS,Android,以及Windows Phones 上常用的 90 余种界面设计模式,从而使得你不必把这些平台的应用挨个玩一遍也可以掌握各个平台的设计精髓。如果你主攻 Android 平台,那么 Android 应用 UI 设计模式 会是更好的选择;
  • 配色设计原理版式设计原理 :如果你读过 写给大家看的设计书 之后想继续深入学习设计,这两本书是不错的起点。

9. 交互设计

通用设计法则

书如其名, 通用设计法则 给出了重要的 125 个设计原则,并用简练的语言和范例展示了这些原则的实际应用。每个原则都有对应的参考文献,以便读者进一步学习。我之所以推荐这本书,是因为:1. 程序员需要对设计有全面的认识;2. 程序员并不需要知道这些设计原则是怎么来的,知道怎么用即可。这本书很好的满足了这两个要求。

交互设计精髓(第3版)

交互设计精髓 是交互设计领域的圣经级著作。交互设计专家(以及 VB 之父) Alan Cooper 在这本书中详细介绍了交互设计的原则,流程,以及方法,然后通过各种范例(主要来自桌面系统)展示了如何应用这些原则。

需要注意的是这本书的 第 4 版 已经出版,它在第三版的基础上增加了移动设计以及 Web 设计等内容。

延伸阅读:

  • The Design of Everyday Things :交互设计领域的另一本经典之作,它通过解读人类行动背后的心理活动,展示了设计问题的根源,并给出了一系列方法用以解决设计问题(需要注意,尽管这本书有中译版,但中译版对应的是 02 年的旧版,而非13年的新版);
  • The Inmates Are Running the AsylumAlan Cooper 的另一本经典,这本书非常辛辣的指出让不具备人机交互知识的程序员直接编写面向用户的软件就像让精神病人管理疯人院(The Inmates Are Running the Asylum),然后给出了一套交互设计流程以挽救这个局面;
  • 简约至上:交互式设计四策略 :专注于把产品变的更加简单易用。作者通过删除,组织,隐藏,和转移这四个策略,展示了如何创造出简约优质的用户体验。

个人成长

1. 职业规划

软件开发者路线图

软件开发者路线图 是一本优秀且实用的程序员职业规划手册。这本书由若干个模式组成,每个模式都对应于程序员职业生涯中的特定阶段。通过这本书,读者可以很方便的找到自己目前所处的模式(阶段),应该做什么,目标是什么,以及下一个模式(阶段)会是什么。如果你时常感到迷茫,那么请阅读这本 路线图 ,找到自己的位置,确定接下来的方向。

延伸阅读:

  • 卡耐基全集 :非常著名的为人处世书籍。很多人把这本书归类到成功学,但我并不这么认为——在我看来,这本书教的更多的是如何成为一个让大家喜欢的人。作为天天和机器打交道的程序员,这套书会帮助我们与人打交道;
  • 沃顿商学院最受欢迎的谈判课 :这本书不是教你去谈判,而是教你通过谈判(Negotiation)去得到更多(Getting more,这也是这本书的原书书名)。小到买菜砍价,大到争取项目,这本书中的谈判原则会让你收益良多;
  • 程序员健康指南 :作为长期与计算机打交道的职业,程序员往往会受到各式各样疾病的困扰,这本书正是为了解决这个问题而出现:它从改善工作环境,调整饮食结构,预防头痛眼痛,以及进行室内/室外锻炼等方面出发,给出了一套全面且可行的程序员健康改善计划,以帮助程序员打造健康的身体。

2. 思维方式

程序员的思维修炼:开发认知潜能的九堂课

作为程序员,我们需要不断地学习——既要学习新技术,也要学习如何解决各种领域的问题。为了提升学习效率,我们需要学习 如何学习程序员的思维修炼 正是这样一本讲如何学习的书,它集合了认知科学,神经学,以及行为理论的最新研究成果,并系统的介绍了大脑的工作机制。通过这本书,你将学会如何高效的使用自己的大脑,从而提高思考能力,改善学习效率。

如何把事情做到最好

Mastery is not about perfection. It’s about a process, a journey. The master is the one who stays on the path day after day, year after year. The master is the one who is willing to try, and fail, and try again, for as long as he or she lives.

为什么同样资质的人,大多数人会碌碌无为,而只有极少数能做到登峰造极?如何在领域内做到顶尖?如何克服通往顶尖之路上的重重险阻? 如何把事情做到最好 回答了这些问题,并极具哲理的指出登峰造极并不是结果,而是一段永不停止的旅程。阅读这本书不会让你立刻脱胎换骨,但它会指引你走向正确的道路——通往登峰造极之路。

延伸阅读:

  • 怎样解题:数学思维的新方法 :不要被标题中的“数学思维”吓到,它并不仅仅只是一本数学解题书,它所提出的四步解题法(理解题目->拟定方案->执行计划->总结反思)适用于任何领域;
  • 暗时间刘未鹏 所写的关于学习思维方法的文章集,既包含了他对学习方法的思考,也包含了大量进一步阅读的资源;
  • 批判性思维:带你走出思维的误区 :这本书系统的分析了人类思维的常见误区,并针对各个误区给出了解决方案,从而帮助程序员养成严谨正确的思考方式;
  • Conceptual Blockbusting: A Guide to Better Ideas :与批判性思维相反,这本书专注于创造性思维(Creative Thinking),它分析了阻碍创造性思维的常见思维障碍(Blockbuster)以及这些思维障碍背后的成因,并给出了各种方法以破除这些障碍。

3. 求职面试

金领简历:敲开苹果微软谷歌的大门

知己知彼,百战不殆。 金领简历:敲开苹果微软谷歌的大门 是程序员求职的必读书籍,它覆盖了程序员求职的方方面面:从开始准备到编写简历,从技术面试到薪酬谈判。由于该书作者曾在 Google,微软,和苹果任职并进行过技术招聘,因此这本书的内容非常实用。

顺便吐个槽:这本书翻译的还不错,但我实在无法理解封面上的“进入顶级科技公司的葵花宝典”这段文字——找个工作而已,用不着切JJ这么凶残吧。-_-#

程序员面试金典(第 5 版)

同样是来自 金领简历 作者的作品, 程序员面试金典(第 5 版) 专注于技术面试题,它既包含了 IT 企业(诸如微软,Google,和苹果)的面试流程以及如何准备技术面试,也包含了大量(超过200道)常见技术面试题题目以及解题思路。无论你打算进入国内企业还是外企,你都应该把这本书的题目练一遍,以找到技术面试的感觉(我在求职时就曾经专门搞了一块白板,然后每二十分钟一道题的练习,效果很不错)。

延伸阅读:

  • 编程之美:微软技术面试心得 :恐怕是国内技术面试第一书,这本书里面的多数题目都曾经是国内IT企业面试的必问题目。这本书的缺点是它太旧而且被用滥了(以至于一些企业开始避免使用这本书上的题目)——但你可以把它当成一本算法趣题来读;
  • 剑指 Offer:名企面试官精讲典型编程题 :相对于东拼西凑的XX面试宝典, 剑指Offer 是一本少见的国产精品技术面试书籍,尽管这本书的技术面试题目不多(60 余道),但作者为大多数题目都给出了不同方式的解法,并分析了这些解法之间的优劣,此外作者还以面试官的视角分析了技术面试的各个环节,从而帮助读者把握技术面试;
  • 人人都有好工作:IT 行业求职面试必读 :可以把它看做 金领简历 的补充阅读——这本书的特点在于它给出了非常详细的简历/求职信/电子邮件编写技巧,而这正是不少国内程序员所缺乏的。

4. 英语写作

The Only Grammar Book You'll Ever Need

词汇量决定阅读能力,语法决定写作能力。计算机专业词汇并不多,但精确性非常重要,因此每个程序员都应具备良好的英语语法,但程序员并不需要过于专业的英语语法——掌握常用语法并把它用对就可以。 The Only Grammar Book You’ll Ever Need 正好可以满足这个需求,尽管它篇幅不大(不足 200 页),却覆盖了英语中的关键语法以及常见错误。把这本书读两遍,它会大幅度提高你的英语写作能力。

风格的要素

既是最畅销的英语写作书籍,也是计算机书籍中引用最多的非计算机书籍。 风格的要素 用极其简练的语言讲述了如何进行 严肃精确清楚 的英语写作。从这本书中,你不仅可以学到英语写作,更可以学到一种严谨至简的处事态度,而这正是专业开发所必需的。

延伸阅读:

  • 牛津英语用法指南(第 3 版) :全面且权威的英语用法指南,它覆盖语法,词汇,发音,以及修辞等方面,并兼顾口语和书面语,以帮助读者掌握合理的英语用法(Proper English Usage)。不要被这本书的篇幅(1000 多页)吓到——原书并没有这么厚,因为这本书被翻译成中文但又得保留原有的英文内容,所以它的篇幅几乎翻了一倍。考虑到这本书使用的词汇都很基础,所以我认为具有英语基础的读者直接阅读原版( Practical English Usage )会更合适;
  • 写作法宝:非虚构写作指南(30周年纪念版) :详尽的非虚构(Non-Fiction)写作指南,无论你要写地方,技术,商务,运动,艺术,还是自传,你都可以从这本书中找到珍贵的建议;
  • 中式英语之鉴 :中国人使用英语最大的问题就是会把中式思维掺杂其中,从而形成啰里啰嗦不伦不类的中式英语(Chinglish)。 中式英语之鉴 系统的探讨了中式英语以及其成因,然后根据成因对中式英语进行归类,并对每个类别给出了大量的实际案例以及修改建议。如果你想摆脱中式英语,那么这本书是绝好的起点。

如何使用这个书单

学而不思则罔,思而不学则殆。

不愤不启,不悱不发。举一隅不以三隅反,则不复也。

不闻不若闻之,闻之不若见之,见之不若知之,知之不若行之,学至于行之而止矣。

来自他人的书单

它山之石,可以攻玉。我在本文最后给出其他中外优秀程序员的书单,以便参考&补充。

刘未鹏(暗时间作者)

以下同一条目下用“/”隔开的表示任选,当然也可以都读。

  1. 编码:隐匿在计算机软硬件背后的语言
  2. 深入理解计算机系统 / Windows 核心编程 / 程序员的自我修养
  3. 代码大全 / 程序员修炼之道
  4. 编程珠玑 / 算法概论 / 算法设计 / 编程之美
  5. C 程序设计语言
  6. C++ 程序设计语言 / C++ 程序设计原理与实践 / Accelerated C++
  7. 计算机程序的构造与解释
  8. 代码整洁之道 / 实现模式
  9. 设计模式 / 敏捷软件开发(原则模式与实践)
  10. 重构

云风(中国游戏编程先行者,前网易游戏部门资深程序员,简悦创始人)

  1. C++ 编程思想
  2. Effective C++
  3. 深度探索 C++ 对象模型
  4. C++ 语言的设计与演化
  5. C 专家编程
  6. C 陷阱与缺陷
  7. C 语言接口与实现
  8. Lua 程序设计
  9. 链接器和加载器
  10. COM 本质论
  11. Windows 核心编程
  12. 深入解析 Windows 操作系统
  13. 程序员修炼之道
  14. 代码大全
  15. UNIX 编程艺术
  16. 设计模式
  17. 代码优化:有效使用内存
  18. 深入理解计算机系统
  19. 深入理解 LINUX 内核
  20. TCP/IP 详解

洪强宁(豆瓣技术总监)

  1. 代码大全
  2. 人月神话
  3. 编码:隐匿在计算机软硬件背后的语言
  4. 计算机程序设计艺术
  5. 程序员修炼之道
  6. 设计模式
  7. 计算机程序的构造与解释
  8. 重构
  9. C 程序设计语言
  10. 算法导论

陈皓(CoolShell博主)

  1. 点石成金:访客至上的 Web 和移动可用性设计秘笈
  2. 重来:更为简单有效的商业思维
  3. 黑客与画家
  4. 清醒思考的艺术
  5. TCP/IP 详解
  6. UNIX 环境高级编程
  7. UNIX 网络编程

张峥(微软亚洲研究院副院长)

  1. 算法概论
  2. Data Structure and Algorithms
  3. C 程序设计语言
  4. UNIX 操作系统设计
  5. 编译原理
  6. 计算机体系结构:量化研究方法
  7. 当下的幸福
  8. 异类:不一样的成功启示录

Jeff Atwood(Stackoverflow联合创始人)

  1. 代码大全
  2. 人月神话
  3. 点石成金:访客至上的Web和移动可用性设计秘笈
  4. 快速软件开发
  5. 人件
  6. The Design of Everyday Things
  7. 交互设计精髓
  8. The Inmates Are Running the Asylum
  9. GUI设计禁忌 2.0
  10. 编程珠玑
  11. 程序员修炼之道
  12. 精通正则表达式

Joel Spolsky(Stackoverflow联合创始人)

软件项目管理

  1. 人件
  2. 人月神话
  3. 快速软件开发

编程技艺

  1. 代码大全
  2. 程序员修炼之道

编程哲学

  1. 禅与摩托车维修艺术
  2. 哥德尔、艾舍尔、巴赫:集异璧之大成
  3. 建筑模式语言

界面设计

  1. 点石成金:访客至上的 Web 和移动可用性设计秘笈
  2. 交互设计精髓
  3. The Design of Everyday Things

资本运作

  1. 漫步华尔街

图形设计

  1. 写给大家看的设计书

思维方式

  1. 影响力
  2. Helplessness On Depression, Development and Death

编程入门

  1. 编码:隐匿在计算机软硬件背后的语言
  2. C 程序设计语言

DHH(Ruby on Rails创始人)

  1. Smalltalk Best Practice Patterns
  2. 重构
  3. 企业应用架构模式
  4. 领域驱动设计
  5. 你的灯亮着吗?发现问题的真正所在

参考

  1. 怎样花两年时间去面试一个人
  2. What is the single most influential book every programmer should read?
    1. Code Complete (2nd edition) by Steve McConnell
    2. The Pragmatic Programmer
    3. Structure and Interpretation of Computer Programs
    4. The C Programming Language by Kernighan and Ritchie
    5. Introduction to Algorithms by Cormen, Leiserson, Rivest & Stein
    6. Design Patterns by the Gang of Four
    7. Refactoring: Improving the Design of Existing Code
    8. The Mythical Man Month
    9. The Art of Computer Programming by Donald Knuth
    10. Compilers: Principles, Techniques and Tools by Alfred V. Aho, Ravi Sethi and Jeffrey D. Ullman
    11. Gödel, Escher, Bach by Douglas Hofstadter
    12. Clean Code: A Handbook of Agile Software Craftsmanship by Robert C. Martin
    13. Effective C++
    14. More Effective C++
    15. CODE by Charles Petzold
    16. Programming Pearls by Jon Bentley
    17. Working Effectively with Legacy Code by Michael C. Feathers
    18. Peopleware by Demarco and Lister
    19. Coders at Work by Peter Seibel
    20. Surely You’re Joking, Mr. Feynman!
    21. Effective Java 2nd edition
    22. Patterns of Enterprise Application Architecture by Martin Fowler
    23. The Little Schemer
    24. The Seasoned Schemer
    25. Why’s (Poignant) Guide to Ruby
    26. The Inmates Are Running The Asylum: Why High Tech Products Drive Us Crazy and How to Restore the Sanity
    27. The Art of Unix Programming
    28. Test-Driven Development: By Example by Kent Beck
    29. Practices of an Agile Developer
    30. Don’t Make Me Think
    31. Agile Software Development, Principles, Patterns, and Practices by Robert C. Martin
    32. Domain Driven Designs by Eric Evans
    33. The Design of Everyday Things by Donald Norman
    34. Modern C++ Design by Andrei Alexandrescu
    35. Best Software Writing I by Joel Spolsky
    36. The Practice of Programming by Kernighan and Pike
    37. Pragmatic Thinking and Learning: Refactor Your Wetware by Andy Hunt
    38. Software Estimation: Demystifying the Black Art by Steve McConnel
    39. The Passionate Programmer (My Job Went To India) by Chad Fowler
    40. Hackers: Heroes of the Computer Revolution
    41. Algorithms + Data Structures = Programs
    42. Writing Solid Code
    43. JavaScript - The Good Parts
    44. Getting Real by 37 Signals
    45. Foundations of Programming by Karl Seguin
    46. Computer Graphics: Principles and Practice in C (2nd Edition)
    47. Thinking in Java by Bruce Eckel
    48. The Elements of Computing Systems
    49. Refactoring to Patterns by Joshua Kerievsky
    50. Modern Operating Systems by Andrew S. Tanenbaum
    51. The Annotated Turing
    52. Things That Make Us Smart by Donald Norman
    53. The Timeless Way of Building by Christopher Alexander
    54. The Deadline: A Novel About Project Management by Tom DeMarco
    55. The C++ Programming Language (3rd edition) by Stroustrup
    56. Patterns of Enterprise Application Architecture
    57. Computer Systems - A Programmer’s Perspective
    58. Agile Principles, Patterns, and Practices in C# by Robert C. Martin
    59. Growing Object-Oriented Software, Guided by Tests
    60. Framework Design Guidelines by Brad Abrams
    61. Object Thinking by Dr. David West
    62. Advanced Programming in the UNIX Environment by W. Richard Stevens
    63. Hackers and Painters: Big Ideas from the Computer Age
    64. The Soul of a New Machine by Tracy Kidder
    65. CLR via C# by Jeffrey Richter
    66. The Timeless Way of Building by Christopher Alexander
    67. Design Patterns in C# by Steve Metsker
    68. Alice in Wonderland by Lewis Carol
    69. Zen and the Art of Motorcycle Maintenance by Robert M. Pirsig
    70. About Face - The Essentials of Interaction Design
    71. Here Comes Everybody: The Power of Organizing Without Organizations by Clay Shirky
    72. The Tao of Programming
    73. Computational Beauty of Nature
    74. Writing Solid Code by Steve Maguire
    75. Philip and Alex’s Guide to Web Publishing
    76. Object-Oriented Analysis and Design with Applications by Grady Booch
    77. Effective Java by Joshua Bloch
    78. Computability by N. J. Cutland
    79. Masterminds of Programming
    80. The Tao Te Ching
    81. The Productive Programmer
    82. The Art of Deception by Kevin Mitnick
    83. The Career Programmer: Guerilla Tactics for an Imperfect World by Christopher Duncan
    84. Paradigms of Artificial Intelligence Programming: Case studies in Common Lisp
    85. Masters of Doom
    86. Pragmatic Unit Testing in C# with NUnit by Andy Hunt and Dave Thomas with Matt Hargett
    87. How To Solve It by George Polya
    88. The Alchemist by Paulo Coelho
    89. Smalltalk-80: The Language and its Implementation
    90. Writing Secure Code (2nd Edition) by Michael Howard
    91. Introduction to Functional Programming by Philip Wadler and Richard Bird
    92. No Bugs! by David Thielen
    93. Rework by Jason Freid and DHH
    94. JUnit in Action
  3. Recommended Reading for Developers
  4. Book Reviews – Joel Spolsky
  5. The five programming books that meant most to me

这两部分的目的都是拓宽我们讨论“技术”的术语。技术应以三种不同的形式理解:作为嵌入工具(例如锅,锅,炉子)的过程;明确的指示(如食谱);以及过程知识,或者我们也可以称为隐性知识,专有技术和技术经验。过程知识是一种很难作为指令写下来的知识。您可以给一个人一个设备齐全的厨房和一个非常详尽的食谱,但是除非他已经有烹饪经验,否则我们不应该指望他准备一道好菜。

我认为在谈论技术时,我们有两个很大的偏见。首先,我们在工具和配方方面考虑得太多,而实际上我们应该在过程知识和技术经验方面考虑得更多。其次,我们大多数人过多地关注数字世界,而对工业世界则关注不足。我们对数字世界的痴迷将我们对技术未来的期望朝着计算机朋克反乌托邦的方向发展。相反,我希望我们可以期待在工业进步的推动下,对技术未来充满喜悦。

过程知识由经验丰富的员工代表

过程知识由经验丰富的员工代表。我一直在研究半导体行业,这有助于更广泛地阐明我对技术创新的想法。在半导体生产中很容易识别出所有三种技术形式:工具,指令和工艺知识。TSMC,英特尔和三星是执行摩尔定律最负责的三家公司。这三家公司每年投资超过100亿美元,以推动这一技术前沿。

这些公司拥有的工具和知识产权很容易观察。我认为他们拥有的过程知识甚至更重要。工艺知识也可以称为技术和工业专长;就半导体而言,这包括如何存储晶圆,如何进入洁净室,在晶圆厂工艺的不同阶段应使用多少电流以及不计其数的其他知识。这种知识是通过经验赢得的。任何有详细说明但没有实际制造芯片经验的人都可能会搞砸。

我相信,技术最终会因为人和他们所拥有的过程知识的深入而进步。我将新工具和IP的创建视为我们积累的过程知识的认证。我不想将工具和IP视为技术进步的最终目的,而是希望将它们视为培训更好的科学家,工程师和技术人员的里程碑。

积累的工艺知识和资本使半导体公司能够继续生产越来越复杂的芯片。如果这些公司还不具备深厚的工艺知识储备,则不可能每24个月将晶体管密度提高一倍。这不仅是任何资本充足的公司都可以购买的工具;或蓝图,如果没有经验将它们编成代码,很难遵循这些蓝图。美国在设计和制造半导体方面拥有数十年的经验,并且已经开发了人才生态系统,成功地推动了摩尔定律的发展。这些人才库使美国能够在至关重要的技术上保持领先地位。

美国的工业基础一直在下降

美国的工业基础一直在下降。但是,半导体领域的持续创新是美国制造业的一个例外。该国过去曾培育充满活力的工程实践社区(我喜欢布拉德·德龙(Brad DeLong)这个),这是谈论许多行业领域积累的过程知识的另一种方式。但是,并非所有的工程实践社区都处于良好状态。

美国制造业的实际产出低于2008年经济衰退之前的水平。这意味着整整十年美国制造业并没有真正的增长。(实际上,这一措施可能太过乐观了-ITIF提出了一个论据,即由于计算机速度的过度质量调整而导致了制造业产出措施的偏差。带走了计算机,如今购买的计算机越来越少,而美国的实际产出却越来越少。制造业将进行有意义的降低)。制造业就业人数在1979年达到高峰,近20万工人; 它在2000年下降到1700万,在2008年下降到1400万,今天是1200万。自1979年以来,美国人口增长了40%,而制造业工人的数量几乎减少了一半。

当企业和工厂离开时,积累的过程知识也消失了。行业经验,扩展专业知识以及边做边学带来的所有事物都会衰减。我今年初访问了德国,与行业人士进行了交谈。德国人不断提出的一个观点是,美国已经实现了自身的工业化并分散了生产网络。尽管德国通过提升价值链来应对全球化,但美国制造业基地大多通过放弃生产来应对。

布拉德·塞瑟(Brad Setser)表明,美国的制成品出口水平低,在富裕国家中脱颖而出。叫我头脑简单,但我认为世界上最发达国家应该负责 向世界各地出口商品。相反,美国既有贸易赤字,也有经常账户赤字。美国的贸易逆差之所以高,不仅是因为它进口了很多商品。它之所以很高,是因为它的出口不多。为了使其他国家从美国进口更多商品,首先,美国应该出售更好的商品。

知识应在整个供应链中传播,并在堆栈中上下流动

知识应在整个供应链中传播,并在堆栈中上下流动。成功的行业倾向于聚集到紧密的生产网络中。欣赏集群奇迹的最简单方法是看硅谷,那里的资本,学术界,大量急切的劳动力以及大小不一的公司彼此相邻。对于在硅谷待了一段时间的任何人来说,显而易见的是,这种经济联系的集中是使该系统运转的魔力的一部分。

工业集群还有许多其他例子。台湾的半导体产业成立,至今仍以台北南部的一个小型工业园区为中心。硅谷之所以被命名是因为它是半导体生产的中心(并且附近有足够的有毒超级基金站点可以证明这一遗产)。不仅仅是芯片:汽车,电子,生物技术,航空和机器零件都倾向于在地理位置上聚集。

接近使得更容易生成过程知识。但是,当我们通过分离设计和制造来拆分这些生产网络时会发生什么呢?有时候没什么大不了的,有时候效果很好。但是我相信,在大多数情况下,位错会使维护过程知识变得更加困难。

设计过程和生产过程都会生成有用的信息,而位错使该信息难以传播。我认为我们倾向于打折我们在生产过程中可以获得多少知识,以及如何将其反馈到设计过程中。也许更容易从计算示例中体会到这一点。 Arjun Narayan告诉我,好的软件设计需要对芯片有深刻的了解,反之亦然。最好的开发人员是那些了解流程如何在堆栈上下交互的人员。

当我们停止知识流向上堆积时会发生什么?我认为美国工业机器人行业的弱势具有启发意义。美国在制造高端精密制造设备方面几乎没有地位。在工厂自动化系统,机床,机器人手臂和其他类型的生产机械方面,最先进的供应商是日本,德国和瑞士。我认为,美国地位低下的原因可以直接归因于公司从众多制造业领域撤离。如果工程师不接触工业流程,他们将如何进行自动化系统的设计?

从A报价文章:“关于先进制造业,以总统奥巴马的一份报告,他在2012年的科学顾问委员会,编制得出的结论是‘硬道理’是美国落后于制造业创新等富裕国家。”

我看不出有足够的美国人为美国没有制造先进的工业机器人而烦恼。认为机器人将在将来完成所有制造工作可能很好。但是必须有人来制造这些机器人,并拥有先进机器人制造的知识产权,而且在大多数情况下,有人不是美国。工程实践界最健康的国家也率先为该行业设计工具,这绝非偶然。他们能够将知识嵌入到新工具中,因为它们继续生成过程知识。

让我们尝试保留过程知识。

让我们尝试保留过程知识。工业工作的减少使积累工艺知识变得更加困难。如果一个州失去了大部分电气工程师,土木工程师或核工程师的工作,那么进入这些领域的年轻人就会减少。技术发展放慢,这变成了自我强化的下降周期。

我认为我们应该尝试继续处理知识。

日本的伊势神宫 就是这种类型的非凡例子。每隔20年,看护人都会彻底拆除神殿并重新建造。木制神rine已经被重建了1200年。当地人希望确保他们永远不会忘记建造神社的生产知识。很明显,年长的一代想向年轻的一代传授建筑技术:“下次我将把这些职责留给您。”

定期拆除和重建木庙听起来不像是在浪费时间。但是我不确定本地优先事项是否在这里完全搞砸了。这些人知道,写下甚至建造单个木结构所需的每条指令都太困难了。想象为机械零件或芯片创建指令要困难得多。我们每隔一段时间就会发现不知道如何使用的古老工具。这些神社看守人认为,保存生产知识很重要,我认为这令人钦佩。

建立广阔的工业基础并实践实践学习曾经是美国的方式。布拉德·德隆(Brad DeLong) 再次说道:“当第二次工业革命的技术问世时,美国凭借其广阔的棉花市场和丰富的自然资源以及卓越的工程技术社区,能够突飞猛进,并且实际上大大超过了英国几乎每个地方的生产效率 这样一来,二十世纪就变成了美国世纪,而不是第二个英国世纪,这在很大程度上是因为汉密尔顿的赌注促使美国不仅仅追随比较优势。

未来不只是服务

未来不只是服务。制造业不是总是低附加值的东西吗,而是应该由服务来驱动未来?我不确定。我怀疑我们能否将所有希望寄托在服务领域,因为它往往会遇到两个大问题:很多问题都是赢家通吃,其余大部分是零和。

美国服务业工作的问题之一是,大多数收益是由很少的工人获得的。两个服务部门生产率很高:技术和金融。但是其他行业,例如零售业,酒店业和食品服务业,并没有那么快地促进生产力的增长。因此,尽管总产量可以增加,但消费却不会保持上升趋势。这是因为对冲基金经理和机器学习工程师(而不是酒店员工和零售员工)非对称地产生了服务的整体生产收益,而这些高生产率的工人只能消耗那么多。

大量服务工作中的另一个问题是其中很多都是零和,Adair Turner很好地说明了这一点。太多的服务工作是要抵消其他服务工作的努力,例如在诉讼中,原告的律师为被告的律师创造了工作。通常,零和是不对称的:十几个黑客盗窃,随后各地的公司都需要花费数十亿美元来保护员工或承包商,以保护自己。一个州遭受了几次刑事灾难,政府随后需要雇用数百名官员,以使人们感到安全;少数人实施会计欺诈,随之而来的轩然大波迫使公司和银行将合规部门的规模扩大了数万个。

我之前链接到Brad Setser 作品中有一条有趣的台词。他告诉我们,美国服务贸易盈余如此之高的原因之一是,美国人出国旅行的倾向很低。我认为这不是赚取贸易盈余的好方法。

我最喜欢的彭博列的类型已经成为诺亚史密斯扣篮上英国。服务业约占英国经济的80%,这带来了许多问题。其中包括过去二十年来生产力的低水平增长,对金融危机的极大脆弱性以及最大公司的研发支出水平低下。马特·克莱因(Matt Klein)提出了一个有趣的主张:“带走大伦敦-繁荣的程度在一定程度上取决于是否愿意为来自中东和前苏联的寡头提供服务-英国是最贫穷的国家之一西欧国家。”

关于英国的另一件事:我真的很喜欢《权力竞争》Power to Compete),这本书是两位日本思想家之间对话的组成部分。(感谢诺亚提出建议。)有很多好的建议,其中一个是:“如果你看英国,我认为优先辩论和思考的政策使他们失败了。”(我会也表明这种对论据的博雅艺术强调将大学变成了吉拉迪安恐怖的孵化器。)

竞争能力值得一读,因为只有两个人清楚地认识到了低增长的问题,并且认真地提供解决方案。没有一种态度让我觉得更清新。

没有工业基础,我们如何做科幻小说

没有工业基础,我们该如何做科幻小说?我已经否定了服务的理由;工业有积极的理由吗?是的,我想是这样。

互联网很重要,我们可能仍会低估其影响。但是我不认为我们应该让创新完全局限于数字世界,因为还有很多东西需要建设。这个世界还不够发达,每个人都只能以较低的收入获得住房,食物,水和能源。数以亿计的人仍然生活在极端贫困中,这意味着制造和物流还没有克服为所有人提供廉价物质舒适的障碍。

而且我认为,除非我们建立了许多其他东西,否则我们不能将自己称为“发达”世界。我们在20年代安装的信号设备的引导下,在20世纪70年代建造的地铁上工作。自从协和飞机退役以来,我们在地球上的行驶速度一直比较缓慢,而此时全球旅行者都希望能够更快地到达主要枢纽。我们确定发达国家不会经历过早的去工业化吗?当人们提到数字世界变得非常有趣的事实时,我倾向于认为这种回应带有“让他们吃iPhone”的口号。

我并不是说制造业具有特殊的道德价值,而且我之前已经承认制造业中的许多都是令人不快和危险的。我对工业感兴趣,因为我认为维护工业基础是构建未来科幻技术的前提。

我在寻找什么?对于初学者:能源太便宜而无法计量;火星及以后的殖民地;我们的沙漠重新造林;纳米技术使我们可以印刷基本材料;预防,治疗或治愈大多数疾病的医疗器械和药品;对材料有更深刻的理解;还有很多其他事情。为了实现所有这些,我们需要开发更多的工具和机械。

在太空时代成长的人提醒我们,我们重新调整了技术期望值要低得多。这是弗里曼的儿子乔治·戴森George Dyson),他观看了从拜科努尔发射的太空旅游火箭后说:“我小时候,弗里曼正在建造这艘太空船,该太空船原本打算在1965年与50个人一起离开,前往火星和土星。然后的问题是,美国人还是俄罗斯人将接管太阳系。距人造卫星50年后,我们来到了这里,我正看着一个美国人支付3500万美元,乘坐苏维埃时代的火箭进入低地球轨道。”

让我们继续关注太空旅行的话题。木星的各个卫星上可能都有 温暖的海洋。为什么我们没有优先考虑在我们自己的太阳系中可能存在于我们行星门口的外星生命?我现在正在自愿参加探索这些海洋的任务。如果我必须在那里筹集资金,我将提议写下一个Moby-Dick或《海底两万个同盟》,这当然取决于居住在这些深处的怪物的性质和经济价值。

我承认,所有这些创新都有可能出现。汽车正在变得无人驾驶,甚至可能很快就飞起来。私人空间的工作确实看起来很酷。繁荣可能会带回超音速喷气机。但是我发现它们的潜在总和并不令人兴奋。而且我们可能无法全部获得:美国无法建立全国性的收费系统,因为要求不同的州将其应答器转移到相同的无线电频率太困难了(即使在国会法案要求他们这样做之后也是如此) )。如果我们无法确定各州之间可互操作的收费系统,那么我们多久才能制定出自动驾驶汽车的监管系统?

美国应该效仿德国

美国应该效仿另一个国家。我钦佩有一个国家,这个国家的经验将本文的许多主题融合在一起:我认为美国应该向德国学习。我认为德国是在培养工程实践社区方面做得最好的国家。几十年来,甚至数百年来,德国一直在进行工业深化。

我今年早些时候到那里研究了其在行业中的卓越表现。我不能声称完全了解系统如何工作,但是我至少可以确定成功的几个因素:学术合作,公司鼓励和对技能传承的承诺都可以发挥作用。老年工人的态度令我特别震惊,他们认为他们有责任将知识传授给年轻工人。今天,德国公司仍然是工业技术许多领域的领导者。

这并不是说德国一切都很好。德国和欧洲其他地区似乎错过了通向数字时代的巴士。这是 很难说出许多欧洲公司在过去30年来在他们的行业领导者开始; 我能想到的最好的例子是英飞凌(Infineon),它是西门子公司的一个半导体分拆公司,市值为300亿美元,我想我们可以加入Spotify,尽管我不确定它是否真的是领导者。这里有一个显着的线经济学:“如果爱立信和诺基亚继续萎缩,只有一个欧洲公司,施耐德电气公司,将成为世界上最大的35家科技公司的收入中离开了。”

德国和美国有不同的优势。前者擅长工业,后者擅长信息技术。但是我觉得奇怪的是,每个人都在对方擅长的方面很差。我无法辨别强迫我们选择其中一个的原则,而且我乐观地认为,一个国家应该能够在行业和互联网上都表现出色。

在过去的几十年中,美国和德国的增长都令人失望。我可以确定每个国家的关键数据点。在美国,主要城市的租金增长已经超过了收入的增长。由于我仍无法理解的原因,旧金山等城市的房主有能力否决新住房的创建。谁能相信他们能够摆脱这个困境?在德国,现在继承了全部财富的一半以上,高于 1970年代的20%(使财富几乎达到英国的水平)。似乎人们大多放弃了通过做新事物来创造财富的想法。

这是另一个问题:美国政府正在慢慢放弃其 财政回旋的余地。1962年,非国防性可支配支出占联邦预算的60%以上。到2017年,这一数字已降至15%,并且预计将继续下降。随着份额的下降,我想知道这将如何影响立法者的思维方式,他们预计分配资金的责任将会减少。绝对而言,他们控制的预算仍然很大。但是我怀疑这会剥夺美国政客的主动权:他们可以让政府继续自动驾驶,因为他们的前任已经承诺了大多数可用资金。如果立法者不再有空间确定新的支出计划,除了找到要禁止的新事物然后与另一方争论之外,该怎么办?

美国和德国的创新方式不同,各自都有很大的缺陷。我希望他们能够修复这些缺陷。我相信,我们可以有一个国家,其财富主要是通过新的经济活动而不是通过继承来创造的;建造新的住房,而不是允许现有居民否决建筑;该国政府愿意考虑应该启动的新项目,而不是让预算自动执行。我认为我们不必在行业和互联网之间进行选择;我们的国家既可以拥有蓬勃发展的工业部门,也可以拥有蓬勃发展的互联网部门。

发展是硬道理

发展是唯一的硬道理(或经济增长的社会后果)。如果一个人在一个低增长的社会中生活了太长时间,那么就很容易忽视经济增长的好处。在我所读的大多数评论中,接受低速经济增长是隐藏的前提。我认为这是当今美国和欧洲知识社会中最深的偏见:它几乎遍及所有话题,从博客文章和畅销书到电影和流行歌曲。我发现没有比经济增长太低的普遍沮丧更加激进和令人耳目一新的话题。

我希望更多的人认为经济高速增长会产生重大的积极影响。当人们经历了几年的高增长之后,他们就有条件期望更多的增长。这种期望增加了公司和个人的风险偏好:在过去的40年中,人们已经看到自己的生活以一百种不同的方式变得越来越好,并且他们对更多的事情会有所改善感到乐观。他们会更乐于创业或尝试新的职业,而这些活动甚至都不会感觉像是冒险活动,因为新的机会一直在不断涌现。

所有这些都是基于以下事实:更高的增长提高了我们处理各种问题的能力。如果我们习惯了低增长,那么除非我们能够强迫财富重新分配,否则很难想象我们的生活会得到重大改善。但是,如果几十年来我们经历了高收入增长,那么就更容易想象我们可以解决我们的问题,因为我们不断积累更多的资源来解决这些问题。

对于企业来说,情况也是一样,因为他们希望明年的需求会比今天更大,因此他们有胆量去扩展和尝试新事物。我认为这些原则对于硅谷的公司很清楚,因为硅谷的公司高管根据六个月的预期职位而不是当前职位做出决策。我怀疑增长的历史也会对政府政策产生积极影响。它鼓励政府参与长期规划,因为立法者已经看到情况会好转,并期望有更大的回旋余地。

布赖恩·卡普兰(Bryan Caplan)的想法陷阱为这种观点提供了帮助,在撰写原始作品时我并不陌生。我发现卡普兰的想法引人注目,它影响了我的世界基线模型。低增长的国家将继续停滞不前,因为经济僵化是自我强化。高增长的国家将继续增长,因为活力是自我增强的。后者将拥有乐观的人,而前者将变得自满

反乌托邦的科幻小说是停滞增长的自然产物。难怪过去几十年来出版的许多科幻小说是如此惨淡。当数字技术加速发展但我们无能为力时,我们还能期待什么呢?如果更好的商店和更多的潮人咖啡馆是人们面对物理变化的唯一机会,那么很难想象未来会发生怎样的根本变化。另一方面,如果城市每十年重新划分自己的地盘并重塑自己,那么期待变化就容易得多。

我给美国,日本和欧洲选民的信息是,请消除对低速经济增长的这种冷漠态度。除了认真讨论如何实现GDP的持续增长之外,似乎您可以在任何政治问题上激怒选民。选举能够提出一项计划以实现GDP增长3%并维持几十年的领导人怎么样?不幸的是,甚至连卡普兰都不确定如何摆脱想法陷阱,因为他说打破这种平衡的唯一方法是“运气”。

乐观是增长的推动力

乐观是增长的推动力(或者,更多的行业而更少的Twitter)。我不想挥舞着声称增长归结为运气,而是想以另一种方式挥舞着双手,并说要归结为乐观。我承认论点是圆滑的,因为乐观情绪可能是增长的内生因素。但是我建议,乐观主义也可能是由于接近行业,更多地享受科幻小说以及避免使用Twitter和政治。

我希望我们更多的人学习30年代,这个十年见证了美国人的创造力在机械改进方面的系统应用。那是在化学,橡胶,电机,科学仪器和许多其他事物上取得飞跃发展的时期。在过去的十年中,“技术”指的是航空,无线电,石油开采,电影院等领域的进步。人们认为技术在许多方面(不仅是少数几个方面)正在加速发展是多么令人耳目一新。

1939年是纽约世界博览会的一年。巨大的生产量鼓励人们去想象工业技术的前景,并思考未来将如何比过去更好。阅读Futurama展览的一些报道很有趣:“由通用汽车赞助700万美元,相当于今天的9,100万美元,它是有史以来最大的动画模型:35,738平方英尺。它需要约三千名木匠,电工,制图员和模型制作者的劳动,以及五十万个规模不等的微型建筑物的制造,以及五万辆未来派的银色汽车,其中有上万辆设计用于移动。

我认为,当我们决定取笑塑料制品并随后庆祝华尔街时,美国文化发生了错误的转变。塑料是非同寻常的,但是我们顶尖大学的毕业生对加入投资银行的热情要高得多,而不是提高我们对材料的掌握程度。如果我们将优秀人才视为稀缺资源,那么我认为很遗憾资产管理吸引了这么多聪明的年轻人。我不确定改善资本配置是否是我们技术文明的最关键的推动力。从边缘上讲,更直接的改善可能来自于有聪明的人在行业中谋生。

我希望我们更多的人能够更多地接触工业流程。让我们更多地看一下重型机械,化学制品,火箭以及工业界的所有奇迹。还有其他人想订阅有关工业发展的杂志吗?我希望每月出版一次,以采访不同行业的工程师为特色。我们可以了解到当前的技术水平,它们如何到达这里,当前的约束是什么,即将发生的事情以及接下来的推测:如果我们解决了一些大的约束,我们可以取得什么样的进步期待?

这些都是我希望我们更多的人感兴趣的东西。从边缘看,我们应该转移注意力从什么?轻松:Twitter和政治。

我的意思是消除Twitter的愤怒面。Twitter的各个部分确实非常出色,每天我都为追随者在我的摘要中丢弃多少个重要链接而感到惊奇。我鸣叫/转发三件事:信息密集的片段,有趣的图片和好玩的笑话。通常,我会尝试跟随同样的人。偶尔,愤怒的Twitter浪潮会在我的快乐小岛上消失,但这并不常见。

有无数的社会问题令人生气:我们可以整天在Facebook,Reddit或Twitter上争论,就像我们中的某些人一样。我对Twitter可以激起“接管”的愤怒感到惊讶。Twitter的全国性运动是谴责《纽约时报》的专栏作家,任何人都可以在场外玩。我认为我们不必屈服,让大家都知道我们觉得专栏很垃圾,专栏作家很糟糕,而且论文值得整体谴责。我们应该抵制这种趋势,认为大声的推文具有对另一端造成毁灭性打击的能力:“您只会失去一次纯真。”

不管媒体试图说服我们多少,我们大多数人都不需要关注日常的政治事务。让我们把目光转向更具生产力的用途,例如读书和学习技能。在2016年大选之后,我要做的第一件事就是取消订阅《纽约客》;我为自己迅速采取果断行动而感到自豪。像我一样,另一种可能性是离开美国。我知道这不是每个人的选择,但至少对于某些年轻人来说,也许这是度过国外的好时机。当无聊的辩论是周围的国家情绪时,请出去,转而到一个更理智的环境,在这里您可以真正地学到一些东西。

我知道很少有人对物质世界和工业技术充满好奇。我希望我们可以将注意力转向研究它们,而不必关注政治。

优先事项正确的人呢

优先事项正确的人。上一篇文章中,我广泛引用了尼尔·斯蒂芬森的想法。他的小说强调物质世界的重要性。他相信科幻小说能够激发乐观情绪。我发现Matt Levine是另一位清楚了解数字技术局限性的作家。他的新闻通讯中有许多实例说明现实世界是如何混乱的,并且不能完全将其视为 财务数字抽象。

以下是我要强调的其他人,因为他们有适当的优先事项。他们每个人都在帮助建立数字世界方面发挥了作用。在完成这项工作之后,他们大多将注意力转移到改善物质世界上。

比尔盖茨。微软的联合创始人离开公司后决定将精力集中在什么方面?不是计算机,互联网或移动设备。相反,它是慈善事业,着重于健康,教育和能源。他认为有足够的人致力于数字世界,而他(和他的首都)应该主要尝试改善物质世界。

弗里曼·戴森(Freeman Dyson)拥有丰富的想象力,通常他的疯狂想法与数字世界无关。从来没有我不喜欢阅读戴森(Dyson)访谈。是他早期的项目之一:“我们决定,我们将由一枚由核弹驱动的太空船绕太阳系。我们将飞船发射到太空中-“炸弹,炸弹,炸弹,炸弹”,每秒约四枚炸弹-一直上升到火星,然后又到达木星和土星,我们打算自己走。”

安迪·格罗夫(Andy Grove)。这位前英特尔首席执行官在晚年成为了美国制造业的拥护者。他在2010年《彭博商业周刊》的一篇文章中阐述了他的案情。在这里,我只列出最不完整的摘要:格罗夫(Grove)怀疑初创企业能否提供大量工作;美国应该专注于将初创公司扩大为大公司;而且,当美国出口工作岗位时,它还出口了创新能力和扩展专业知识。换句话说,他对过程知识的丧失感到遗憾。他认识到,当制造业工作岗位离开美国时,“我们打破了经验链,这对技术创新至关重要。”

这是格罗夫的更多内容:“我们对个人业务的追求通常涉及将制造和大量工程转移到国外,这阻碍了我们将创新带入国内规模的能力。如果不扩大规模,我们不仅会失去工作,还会失去对新技术的控制。失去扩展能力最终将损害我们的创新能力。”

格鲁夫在他的论文结尾呼吁重建美国工业共同体。或正如我所说的,重新获得过程知识。大部分内容是由我对半导体行业的学习所推动的,我发现发现业界最重要的人物之一以相似的眼光令人振奋。我很高兴地指出,安迪·格罗夫(Andy Grove),泰勒·科恩(Tyler Cowen)和彼得·泰尔(Peter Thiel)的想法是本文的推动力。

更好的资本配置不会导致技术加速。

更好的资本配置会始终导致技术加速吗?我不这么认为。实际上,我认为,过程知识流失的部分责任可以归因于美国金融业,包括投资者和金融分析师,其重点是资本回报率。(这也是Andy Grove提出的。)

我的基本观点是,技术最终会因为人而进步,尤其是因为他们设法积累了很多过程知识。我认为,美国金融部门没有充分认识到拥有大量技术经验丰富的工人的重要性。确定和衡量工具和IP的存量,而不是人们头脑中存在的过程知识,当然要容易得多。结果,投资者和财务分析师系统地奖励了最渴望减少员工人数的公司,他们认为这是成本。但是,仅仅因为我们不能直接测量过程知识,并不意味着我们应该忽略它的存在。

我相信工具和IP是开发过程知识的自然结果。但是,相反的顺序并不成立:仅拥有大量工具和IP并不能保证我们可以创建更多的工具。因此,我对工艺知识的流失感到不安,并敬佩像德国和日本这样的国家,它们一直在保持其工程实践社区的健康。

这些观点有哪些挑战?我想保留过程知识,因为我认为这对于增长和构建未来的工业技术很重要。我也承认,我的论点面临许多挑战。以下是一些我觉得最有趣的内容。

德国制造业工人的数量也在下降。是的,德国制造业的雇员人数也在下降(请参阅统计信息以及Brad DeLong文章中的图表)。这是否表明我最喜欢的国家也没有保留其工程实践社区?我想说几句话,它保存了它们。首先,尽管美国制造业的实际产出仍低于2008年的水平,但德国的实际制造业产出已大大超过衰退前的水平,并已恢复到长期的增长趋势。如果我们看看遭受失业的行业,我们可以看到它们集中在采矿和纺织品生产等低价值行业;在过去十年中,德国在化学,汽车,机械和电气设备等高价值领域获得了就业。深入研究数据使我对德国以正确的方式做出反应的主张更加自信,因为它通过减少不太可能在过程知识中获得重大收益的工作,并继续在价值链中向上发展。

非物质化。我记得读过Scott Sumner的一篇文章,概述了我们不再对物理事物感兴趣的所有方式。萨姆纳(Sumner)多产,我找不到职位了;我只记得他提供了许多例子,这些例子表明年轻人对经验的兴趣比对拥有东西的兴趣更大。(如果有人可以通过电子邮件将该帖子发送给我,我很乐意在此处链接。更新:该帖子在这里。)如果确实是这样,我们对通过音乐会和异国情调确立地位更加感兴趣休假,那么我们可能无法满足维持不断增长的工业基础所需的总需求。

大多数过程知识已经嵌入到工具中了吗?我最近读过的最好的论文之一是Willy Shih,他主张是肯定的。我建议阅读全文,该文章是本文的提示之一。我不确定是否容易引用任何汇总统计数据来试图反驳它,但是我可以提出一些建议。我认为目前还不是每个人都能有效使用最新工具的情况。我们看到半导体,航空和互联网领域的公司主要集中在少数几个国家。与此相关,国家间的收入继续分化即使有足够资本的公司也可以购买相同的先进机械。这些事实表明,某些国家比其他国家更有效地使用工具,这可能是因为它们拥有更多的过程知识。

我很高兴地认识到,还有许多尚未解决的挑战。我并不是说我已经确定了哪些行业应该得到更好的发展,什么时候应该放开一个非生产性行业。如果我认为合理的业务决策摆脱了流程知识,那么批判它可能是错误的。我的文章的目的是要求更多的人考虑过程知识的增长方式,并建议我们应该在更多的方面推动技术前沿。

作为人力资本的绝对乐观。我想通过回到半导体来解决这一问题。我相信技术进步不是不可避免的,并且我们有力地推动技术进步。每24个月使晶体管密度增加一倍,并不是天生的恩典,这是天生的恩赐。除非我们对此有所考虑,否则这种进展不会发生。摩尔定律不是一个承诺,而是一个挑战,到目前为止,我们已经很好地实现了。

有一天,我们可以举起手来宣布我们已经在半导体领域进行了足够的创新。“相反,未来是服务,而不是这种有毒的制造工作。”我们可以解雇所有书呆子,扔掉他们所有的书,并关闭所有这些工厂。假设我们要花几年的时间才能恢复常识。当我们随后想要振兴该行业时,它可能不像插入机器,清除蓝图上的灰尘,然后高兴地期望生产恢复到先前的水平那样简单。这些工程师所获得的来之不易的过程知识将逐渐消失,而工人将不得不重新学习一堆东西。

我认为过程知识的这种衰退已经发生在许多行业。在德国,情况并非完全如此(尽管它有自己的问题)。当我与德国人谈论工业时,他们宣称他们对去工业化保持警惕。他们说工程学是德国人身份的一部分,他们不太可能轻易放弃。我发现这是一件令人难以置信的事情。让我们再看一看英国,它似乎已经做出了一个有意识的 决定,即它将不再从事工业生产:国家的工业部门。1980年代政府对研发的大量支持被取消,这种情况进一步恶化。如果我们愿意的话,很容易让行业下滑。

我希望我们更多的人 学习生产网络。这意味着更多地考虑系统。健康的生态系统很难维护,但是如果您构建它们并继续为其注入活力,它们将带来持续的突破。服务和技术等法律行业的人们可以很好地掌握这些生产系统 ; 我希望我们也可以从这些角度考虑工业基础。

我不认为互联网不过是令人惊叹的东西。但是我认为,数字世界的奇迹使人们很难看清其他所有事物的移动速度。许多技术领域都取得了谨慎的进展,但我们忽略了这一点,因为我们的手机使我们如此。当我们的物理世界停滞不前时,我们的应用程序会不断完善;我认为,消费者互联网的奇迹使我们迷惑了我们的技术基础有多强大。

彼得·泰尔Peter Thiel):“第一步是了解我们的位置。我们已经在沙漠中流浪了40年,我们认为这是一片迷人的森林。如果我们要找到一条摆脱沙漠和通往未来的道路,那么第一步就是要看到我们已经进入了沙漠。”

可能的后果之一是,过去几十年来出版的许多科幻小说都趋向于网络朋克反乌托邦。(三体问题是一个例外。)我们在城市的自然景观中看不到太多变化,相反,我们得到了传感器,信息和屏幕的大量增加。相比之下,50年代和60年代的科幻小说更为乐观。那是太空时代,那时我们正忙于重塑物理世界,到那时,30年代的工业成就已使他们显而易见。产业的深入发展导致人们对科幻小说持乐观态度,而数字扩散则将其推向反乌托邦。

1939年世界博览会上的Futurama展览非常受欢迎:人们认为该展览的科幻技术确实会在1960年出现。但是随后人们开始降低期望值。这是一关于30年代技术的书的摘录:“ 25年后,通用汽车试图用新的未来世界来复制其早期的成功,展望了25年甚至更久……它无法捕捉大众的想象力,并且它对未来的预测已远远超出了预期。这两个标题相似的展品形成鲜明对比,这与总体数据和行业数据试图告诉我们的是一致的。”

我希望我们恢复乐观。仅仅告诉每个人还不够:“仅仅选择保持乐观。”相反,我建议我们可以通过对工业的更大理解以及实现更高的经济增长来培养乐观情绪。推进技术前沿不应该仅仅是别人的问题。取而代之的是,如何做到这一点应该让我们更多的人关注。

一、Google Hacking介绍

Google是一个伟大的信息收集工具,在如今的互联网时代,想想看一个人可以轻易的从搜索引擎中获取你详细的个人信息是件多么叫人激动又害怕的事情?Google也可以当作我们的第二个“社工库”。

搜索引擎的组成:

  • 爬行器(机器人、蜘蛛)
  • 索引生成器
  • 查询检索器

二、Google Hacking基本搜索

Intext 搜索出现的关键词

Inurl 搜索包含关键词的url

Intitle 搜索包含关键词的标题

Site 搜索包含关键词的站点

filetype 搜索包含关键词的文件类型

Link 对于页面包含的外部链接搜索

Daterange 搜索特定的日期范围

三、Google搜索基本规则

Google 不分大小写

Google 可以使用通配符:*表示一个词/字

Google 会智能地保留一些内容,比如一些过时的词,一些不适合呈现的内容(比如违法信息)

最常用的:”关键字” ,双引号会使Google强制搜索包含关键字的内容

布尔操作符:AND(+) 、NOT(-) 、OR(|),AND 现在已不需要,多个关键字google会都匹配到

四、Google Hacking常用语法

1、搜索标题

标题一般是一个网站内容的高度概括,比如后台登陆?WEBSHELL?例如:

1
intitle:"Java " [ Sec. Info ], [ Files ], [ Console ], [ Sql ], [ Php ], [ Safe mode ], [ String tools ], [ Bruteforce ], [ Network ], [ Self remove ]

2、搜索正文

正文的话就各式各样了,试试login?、admin?、内容编辑?往往会有意想不到的收获。例如:

1
intitle:"index" intext:"Login to the Administrative Interface"

3、关键网站/网址的搜索

网站->site:

网址->inurl:

site:往往我们在对一个大型厂商进行测试时,除了利用工具进行域名爆破,还可以通过Google来帮助,例如site:qq.com;

inurl:是In-系指令中最强大的一个,换句话说,这个高级指令能够直接从网站的URL入手挖掘信息,只要略微了解普通网站的URL格式,就可以极具针对性地找到你所需要的资源,甚至隐藏内容。网站构建者通常将某一类信息集中在一个网站的目录中,所以搜索URL中的词本身就是对某一方面内容的一个限定。如果在加上一定的词进行组配,搜索结果将更贴近需求。

例如:

1
site:xxx.com inurl:phpmyadmin/index.php & (intext:username & password & "Welcome to")

4、文档类型的搜索

filetype 爱查资料的人应该不陌生,当它配合上我们其他的关键词时有意思的东西可能就会出现。

目标文件类型:.pwl口令文件、.tmp临时文件、.cfg配置文件、.ini系统文件、.hlp帮助文件、.dat数据文件、.log日志文件、.par交换文件

示例:

1
filetype:sql site:com and "insert into" admin "2014"

文档示例:

1
2
3
4
5
6
7
8
9
10
11
12
Filetype:xls username password email
Filetype:xls inurl: “password.xls”
Filetype;xls private
Inurl:admin filetype:xls
Filetype:xls inurl:contact
Filetype:xls inurl: “email.xls”
Allinurl:admin mdb
Filetype:mdb inurl:users.mdb
Inurl:email filetype:mdb
Inurl:backup filetype:mdb
Inurl:profiles filetype:mdb
Inurl:*db filetype:mdb

SQL数据库示例:

1
2
3
4
5
6
7
8
nurl:nuke filetype:sql
Filetype:sql password
Filetype:sql “indetified by” –cvs
“#dumping data for table username user users password”
“#mysql dump” filetype:sql
“#phpmyadmin mysql-dump” filetype:txt
“#phpmyadmin mysql-dump”
“insert into” –”the

数据库文件示例:

1
2
3
4
5
6
7
8
Filetype:cfm “cfapplication name ” password
Filetype:mdb inurl:user.mdb
Inurl:email filetype:mdb
Inurl:forum filetype:mdb
Inurl:/db/main.mdb
Inurl:profiles filetype:mdb
Filetype:asp dbq=”* server.mappath(“mdb”)”
Allinurl;admin mdb

5、link搜索

link: 可以得到一个所有包含了某个指定URL的页面列表;

1
2
3
4
5
6
7
8
查找后台地址:site:域名 inurl:login|admin|manage|member|admin_login|login_admin|system|login|user|main|cms
查找文本内容:site:域名 intext:管理|后台|登陆|用户名|密码|验证码|系统|帐号|admin|login|sys|managetem|password|username
查找可注入点:site:域名 inurl:aspx|jsp|php|asp
查找上传漏洞:site:域名 inurl:file|load|editor|Files
查找eweb编辑器:site:域名 inurl:ewebeditor|editor|uploadfile|eweb|edit
查找存在的数据库:site:域名 filetype:mdb|asp|#
查看脚本类型:site:域名 filetype:asp/aspx/php/jsp
迂回策略入侵:inurl:cms/data/templates/images/index/

各种敏感数据收集:

1
2
3
4
filetyle:xls inurl:gov username passwordinurl:phpmyadmin/main.php 
intitle:phpmyadminfiletype:inc inurl:config.inc hostfiletype:sql cdb_members
inurl:forumdatafiletype:txt inurl:"新建文本文档.txt"密码inurl:phpinfo.php
intitle:"phpinfo()""PHP Version"+"Server API"filetype:log inurl:log mdb

6、缓存、快照搜索

没有突破口时,缓存可能是一大杀器。通过爬虫,快照,在管理者不经意的情况下记录下敏感信息。

案例:

曾经碰到过一个学生可能都比较熟悉的名为教育技术服务平台的信息泄露,它们无意中把自己gitlab公开在外网,当然没有任何防范爬虫的措施,虽然设有登陆密码但是可以通过google快照来查看里面的各种敏感代码。

如何访问谷歌的缓存链接?

  1. 在你的计算机上,使用 Google 搜索查找所需网页。
  2. 点击相应网站网址右侧的绿色向下箭头。
  3. 点击网页快照
  4. 打开缓存版页面后,点击“当前页”链接即可返回实际页面。

在如今这个Lean/Agile横扫一切的年代,设计似乎有了被边缘化的倾向,做事的周期如此之快,似乎已容不下人们更多的思考。MVP(Minimal Viable Produce)在很多团队里演化成一个形而上的图腾,于是工程师们找到了一个完美的借口:我先做个MVP,设计的事,以后再说。

如果纯属个人玩票,有个点子,hack out还说得过去;但要严肃做一个项目,还是要下工夫设计一番,否则,没完没了的返工会让你无语泪千行。

设计首先得搞懂要解决的问题

工程师大多都是很聪明的人,聪明人有个最大的问题就是自负。很多人拿到一个需求,还没太搞明白其外延和内涵,代码就已经在脑袋里流转。这样做出来的系统,纵使再精妙,也免不了承受因需求理解不明确而导致的返工之苦。

搞懂需求这事,说起来简单,做起来难。需求有正确的但表达错误的需求,有正确的但没表达出来的需求,还有过度表达的需求。所以,拿到需求后,先不忙寻找解决方案,多问问自己,工作伙伴,客户follow up questions来澄清需求模糊不清之处。

搞懂需求,还需要了解需求对应的产品,公司,以及(潜在)竞争对手的现状,需求的上下文,以及需求的约束条件。人有二知二不知:

  1. I know that I know
  2. I know that I don’t know
  3. I don’t know that I know
  4. I don’t know that I don’t know

澄清需求的过程,就是不断驱逐无知,掌握现状,上下文和约束条件的过程。

这个主题讲起来很大,且非常重要,但毕竟不是本文的重点,所以就此带过。

寻找(多个)解决方案

如果对问题已经有不错的把握,接下来就是解决方案的发现之旅。这是个考察big picture的活计。同样是满足孩子想要个汽车的愿望,你可以:

  1. 去玩具店里买一个现成的
  2. 买乐高积木,然后组装
  3. 用纸糊一个,或者找块木头,刻一个

这对应软件工程问题的几种解决之道:

  1. 购买现成软件(acuquire or licensing),二次开发之(如果需要)
  2. 寻找building blocks,组装之(glue)
  3. 自己开发(build from scratch, or DIY)

大部分时候,如果a或b的Totacl Cost合理,那就不要选择c。做一个产品的目的是为客户提供某种服务,而不是证明自己能一行行码出出来这个产品。

Money是个很重要的点,可惜大部分工程师脑袋里没有钱的概念,或者出于job security的私心,而忽略了。工程师现在越来越贵,能用合理的价格搞定的功能,就不该雇人去打理(自己打脸)。一个产品,最核心的部分不超过整个系统的20%,把人力资源铺在核心的部分,才是软件设计之道。

对工程师而言,DIY出一个功能是个极大的诱惑。一种DIY是源自工程师的不满。任何开源软件,在处理某种特定业务逻辑的时候总会有一些不足,眼里如果把这些不足放在,却忽略了人家的好处,是大大的不妥。前两天我听到有人说 “consul sucks, …, I’ll build our own service discovery framework…”,我就苦笑。我相信他能做出来一个简单的service discovery tool,这不是件特别困难的事情。问题是值不值得去做。如果连处于consul这个层次的基础组件都要自己去做,那要么是心太大,要么是没有定义好自己的软件系统的核心价值(除非系统的核心价值就在于此)。代码一旦写出来,无论是5000行还是50行,都是需要有人去维护的,在系统的生命周期里,每一行自己写的代码都是一笔债务,需要定期不定期地偿还利息。

另外一种DIY是出于工程师的无知。「无知者无畏」在某些场合的效果是正向的,有利于打破陈规。但在软件开发上,还是知识和眼界越丰富越开阔越好。一个无知的工程师在面对某个问题时(比如说service discovery),如果不知道这问题也许有现成的解决方案(consul),自己铆足了劲写一个,大半会有失偏颇(比如说没做上游服务的health check,或者自己本身的high availability),结果bug不断,辛辛苦苦一个个都啃下来,才发现,自己走了很多弯路,费了大半天劲,做了某个开源软件的功能的子集。当然,对工程师而言,这个练手的价值还是很大的,但对公司来说,这是一笔沉重的无意义的支出。

眼界定义了一个人的高度,如果你每天见同类的人,看同质的书籍/视频,(读)写隶属同一domain的代码,那多半眼界不够开阔。互联网的发展一日千里,变化太快,如果把自己禁锢在一方小天地里,很容易成为陶渊明笔下的桃花源中人:乃不知有汉,无论魏晋。

构建灵活且有韧性的系统

如果说之前说的都是废话,那么接下来的和真正的软件设计能扯上些关系。

分解和组合

软件设计是一个把大的问题不断分解,直至原子级的小问题,然后再不断组合的过程。这一点可以类比生物学:原子(keyword/macro)组合成分子(function),分子组合成细胞(module/class),细胞组合成组织(micro service),组织组合成器官(service),进而组合成生物(system)。

一个如此组合而成系统,是满足关注点分离(Separation of Concerns)的。大到一个器官,小到一个细胞,都各司其职,把自己要做的事情做到极致。心脏不必关心肾脏会干什么,它只需要做好自己的事情:把新鲜血液通过动脉排出,再把各个器官用过的血液从静脉回收。

分解和组合在软件设计中的作用如此重要,以至于一个系统如果合理分解,那么日后维护的代价就要小得多。同样讲关注点分离,不同的工程师,分离的方式可能完全不同。但究其根本,还有有一些规律可循。

总线(System Bus)

首先我们要把系统的总线定义出来。人体的总线,大的有几条:血管(动脉,静脉),神经网络,气管,输尿管。它们有的完全负责与外界的交互(气管,输尿管),有的完全是内部的信息中枢(血管),有的内外兼修(神经网络)。

总线把生产者和消费者分离,让彼此互不依赖。心脏往外供血时,把血压入动脉血管就是了。它并不需要知道谁是接收者。

同样的,回到我们熟悉的计算机系统,CPU访问内存也是如此:它发送一条消息给总线,总线通知RAM读取数据,然后RAM把数据返回给总线,CPU再获取之。整个过程中CPU只知道一个内存地址,毋须知道访问的具体是哪个内存槽的哪块内存 —— 总线将二者屏蔽开。

学过计算机系统的同学应该都知道,经典的PC结构有几种总线:数据总线,地址总线,控制总线,扩展总线等;做过网络设备的同学也都知道,一个经典的网络设备,其软件系统的总线分为:control plane和data plane。

路由(routing)

有了总线的概念,接下来必然要有路由。

每一处分叉,就涉及到一次路由。

路由分为外部路由和内部路由。外部路由处理输入,把不同的输入dispatch到系统里不同的组件。做web app的,可能没有意识到,但其实每个web framework,最关键的组件之一就是url dispatch。HTTP的伟大之处就是每个request,都能通过url被dispatch到不同的handler处理。而url是目录式的,可以层层演进 —— 就像分形几何,一个大的系统,通过不断重复的模式,组合起来 —— 非常利于系统的扩展。遗憾的是,我们自己做系统,对于输入既没有总线的考量,又无路由的概念,if-else下去,久而久之,代码便绕成了意大利面条。

再举一例:DOM中的event bubble,在javascript处理起来已然隐含着路由的概念。你只需定义当某个事件(如onclick)发生时的callback函数就好,至于这事件怎么通过eventloop抵达回调函数,无需关心。好的路由系统剥茧抽丝,把繁杂的信息流正确送到处理者手中。

外部路由总还有「底层」为我们完成,内部路由则需工程师考虑。service级别的路由(数据流由哪个service处理)可以用consul等service discovery组件,service内部的路由(数据流到达后怎么处理)则需要自己完成。路由的具体方式有很多种,pattern matching最为常见。

无论用何种方式路由,数据抵达总线前为其定义Identity(ID)非常重要,你可以管这个过程叫data normalization,data encapsulation等,总之,一个消息能被路由,需要有个用于路由的ID。这ID可以是url,可以是一个message header,也可以是一个label(想象MPLS的情况)。当我们为数据赋予一个个合理的ID后,如何路由便清晰可见。

队列(Queue)

对于那些并非需要立即处理的数据,可以使用队列。队列也有把生产者和消费者分离的功效。队列有:

  • single producer single consumer(SPSC)
  • single producer multiple consumers(SPMC)
  • multiple producers single consumer(MPSC)
  • multiple producers multiple consumers(MPMC)

仔细想想,队列其实就是总线+路由(可选)+存储的一个特殊版本。一般而言,system bus之上是系统的各个service,每个service再用service bus(或者queue)把micro service chain起来,然后每个micro service内部的组件间,再用queue连接起来。

有了队列,有利于提高流水线的效率。一般而言,流水线的处理速度取决于最慢的组件。队列的存在,让慢速组件有机会运行多份,来弥补生产者和消费者速度上的差距。

Pub/Sub

存储在队列中的数据,除路由外,还有一种处理方式:pub/sub。和路由相似,pub/sub将生产者和消费者分离;但二者不同之处在于,路由的目的地由路由表中的表项控制,而pub/sub一般由publisher控制 [2]:任何subscribe某个数据的consumer,都会到publisher处注册,publisher由此可以定向发送消息。

协议(protocol)

一旦我们把系统分解成一个个service,service再分解成micro service,彼此之间互不依赖,仅仅通过总线或者队列来通讯,那么,我们就需要协议来定义彼此的行为。协议听起来很高大上,其实不然。我们写下的每个function(或者每个class),其实就是在定义一个不成文的协议:function的arity是什么,接受什么参数,返回什么结果。调用者需严格按照协议调用方能得到正确的结果。

service级别的协议是一份SLA:服务的endpoint是什么,版本是什么,接收什么格式的消息,返回什么格式的消息,消息在何种网络协议上承载,需要什么样的authorization,可以正常服务的最大吞吐量(throughput)是什么,在什么情况下会触发throttling等等。

头脑中有了总线,路由,队列,协议等这些在computer science 101中介绍的基础概念,系统的分解便有迹可寻:面对一个系统的设计,你要做的不再是一道作文题,而是一道填空题:在若干条system bus里填上其名称和流进流出的数据,在system bus之上的一个个方框里填上服务的名称和服务的功能。然后,每个服务再以此类推,直到感觉毋须再细化为止。

组成系统的必要服务

有些管理性质的服务,尽管和业务逻辑直接关系不大,但无论是任何系统,都需要考虑构建,这里罗列一二。

代谢(sweeping)

一个活着的生物时时刻刻都进行着新陈代谢:每时每刻新的细胞取代老的细胞,同时身体中的「垃圾」通过排泄系统排出体外。一个运转有序的城市也有新陈代谢:下水道,垃圾场,污水处理等维持城市的正常功能。没有了代谢功能,生物会凋零,城市会荒芜。

软件系统也是如此。日志会把硬盘写满,软件会失常,硬件会失效,网络会拥塞等等。一个好的软件系统需要一个好的代谢系统:出现异常的服务会被关闭,同样的服务会被重新启动,恢复运行。

代谢系统可以参考erlang的supervisor/child process结构,以及supervision tree。很多软件,都运行在简单的supervision tree模式下,如nginx。

高可用性(HA)

每个人都有两个肾。为了apple watch卖掉一个肾,另一个还能保证人体的正常工作。当然,人的两个肾是Active-Active工作模式,内部的肾元(micro service)是 N(active)+M(backup) clustering 工作的(看看人家这service的做的),少了一个,performance会一点点有折扣,但可以忽略不计。

大部分软件系统里的各种服务也需要高可用性:除非完全无状态的服务,且服务重启时间在ms级。服务的高可用性和路由是息息相关的:高可用性往往意味着同一服务的冗余,同时也意味着负载分担。好的路由系统(如consul)能够对路由至同一服务的数据在多个冗余服务间进行负载分担,同时在检测出某个失效服务后,将数据路只由至正常运作的服务。

高可用性还意味着非关键服务,即便不可恢复,也只会导致系统降级,而不会让整个系统无法访问。就像壁虎的尾巴断了不妨碍壁虎逃命,人摔伤了手臂还能吃饭一样,一个软件系统里统计模块的异常不该让用户无法访问他的个人页面。

安保(security)

安保服务分为主动安全和被动安全。authentication/authorization + TLS + 敏感信息加密 + 最小化输入输出接口可以算是主动安全,防火墙等安防系统则是被动安全。

继续拿你的肾来比拟 —— 肾脏起码有两大安全系统:

  1. 输入安全。肾器的厚厚的器官膜,保护器官的输入输出安全 —— 主要的输入输出只能是肾动脉,肾静脉和输尿管。
  2. 环境安全。肾器里有大量脂肪填充,避免在撞击时对核心功能的损伤。

除此之外,人体还提供了包括免疫系统,皮肤,骨骼,空腔等一系列安全系统,从各个维度最大程度保护一个器官的正常运作。如果我们仔细研究生物,就会发现,安保是个一揽子解决方案:小到细胞,大到整个人体,都有各自的安全措施。一个软件系统也需如此考虑系统中各个层次的安全。

透支保护(overdraft protection)

任何系统,任何服务都是有服务能力的 —— 当这能力被透支时,需要一定的应急计划。如果使用拥有auto scaling的云服务(如AWS),动态扩容是最好的解决之道,但受限于所用的解决方案,它并非万灵药,AWS的auto scaling依赖于load balancer,如Amazon自有的ELB,或者第三方的HAProxy,但ELB对某些业务,如websocket,支持不佳;而第三方的load balancer,则需要考虑部署,与Amazon的auto scaling结合(需要写点代码),避免单点故障,保证自身的capacity等一堆头疼事。

在无法auto scaling的场景最通用的做法是back pressure,把压力反馈到源头。就好像你不断熬夜,最后大脑受不了,逼着你睡觉一样。还有一种做法是服务降级,停掉非核心的service/micro-service,如analytical service,ad service,保证核心功能正常。

把设计的成果讲给别人听

完成了分解和组合,也严肃对待了诸多与业务没有直接关系,但又不得不做的必要功能后,接下来就是要把设计在白板上画下来,讲给任何一个利益相关者听。听他们的反馈。设计不是一个闭门造车的过程,全程都需要和各种利益相关者交流。然而,很多人都忽视了设计定型后,继续和外界交流的必要性。很多人会认为:我的软件架构,设计结果和工程有关,为何要讲给工程师以外的人听?他们懂么?

其实pitch本身就是自我学习和自我修正的一部分。当着一个人或者几个人的面,在白板上画下脑海中的设计的那一刻,你就会有直觉哪个地方似乎有问题,这是很奇特的一种体验:你自己画给自己看并不会产生这种直觉。这大概是面对公众的焦灼产生的肾上腺素的效果。:)

此外,从听者的表情,或者他们提的听起来很傻很天真的问题,你会进一步知道哪些地方你以为你搞通了,其实自己是一知半解。太简单,太基础的问题,我们take it for granted,不屑去问自己,非要有人点出,自己才发现:啊,原来这里我也不懂哈。这就是破解 “you don’t know what you don’t know” 之法。

记得看过一个video,主讲人大谈企业文化,有个哥们傻乎乎发问:so what it culture literally? 主讲人愣了一下,拖拖拉拉讲了一堆自己都不能让自己信服的废话。估计回头他就去查韦氏词典了。

最后,总有人在某些领域的知识更丰富一些,他们会告诉你你一些你知道自己不懂的事情。填补了 “you know that you don’t know” 的空缺。

设计时的tradeoff

Rich hickey(clojure作者)在某个演讲中说:

everyone says design is about tradeoffs, but you need to enumerate at least two or more possible solutions, and the attributes and deficits of each, in order to make tradeoff.

所以,下回再腆着脸说:偶做了些tradeoff,先确保自己做足了功课再说。

设计的改变不可避免

设计不是一锤子买卖,改变不可避免。我之前的一个老板,喜欢把 change is your friend 挂在口头。软件开发的整个生命周期,变更是家常便饭,以至于变更管理都生出一门学问。软件的设计期更是如此。人总会犯错,设计总有缺陷,需求总会变化,老板总会指手画脚,PM总有一天会亮出獠牙,不再是贴心大哥,或者美萌小妹。。。所以,据理力争,然后接受必要的改变即可。连凯恩斯他老人家都说:

When the facts change, I change my mind. What do you do, sir?

What do you do, sir?

  • Some programming concepts that were mostly theoretical 20 years ago have since made it to mainstream including many functional programming paradigms like immutability, tail recursion, lazily evaluated collections, pattern matching, first class functions and looking down upon anyone who don’t use them.
  • A desktop software now means a web page bundled with a browser.
  • Object-Oriented Programming (OOP) has lost a lot of street credibility although it’s still probably the most popular programming model. New trait-based programming models are more pervasive in modern languages like Go, Rust and Swift. Composition is preferred over inheritance.
  • You are not officially considered a programmer anymore until you attend a $2K conference and share a selfie from there.
  • Because of the immense proliferation of multi-processor CPUs, parallel programming is now usually supported at the programming language level rather than primitive OS calls of 20 years ago. It brought in asynchronous programming primitives (async/await), parallel coroutines like goroutines in Go language or channels in D, composability semantics like observables with reactive programming.
  • A pixel is no longer a relevant unit of measurement.
  • Garbage collection has become the common way of safe programming but newer safety models are also emerging like lifetime semantics of Rust and snarky jokes in code reviews.
  • 3 billion devices run Java. That number hasn’t changed in the last 10 years though.
  • A package management ecosystem is essential for programming languages now. People simply don’t want to go through the hassle of finding, downloading and installing libraries anymore. 20 years ago we used to visit web sites, downloaded zip files, copied them to correct locations, added them to the paths in the build configuration and prayed that they worked.
  • Being a software development team now involves all team members performing a mysterious ritual of standing up together for 15 minutes in the morning and drawing occult symbols with post-its.
  • Language tooling is richer today. A programming language was usually a compiler and perhaps a debugger. Today, they usually come with the linter, source code formatter, template creators, self-update ability and a list of arguments that you can use in a debate against the competing language.
  • Even programming languages took a side on the debate on Tabs vs Spaces.
  • Adobe Flash, which was the only way to provide some smooth interaction on the web, no longer exists, thankfully. Now we have to develop on three different platforms with entirely different programming models in order to provide the same level of interaction.
  • IDEs and the programming languages are getting more and more distant from each other. 20 years ago an IDE was specifically developed for a single language, like Eclipse for Java, Visual Basic, Delphi for Pascal etc. Now, we have text editors like VS Code that can support any programming language with IDE like features.
  • Code must run behind at least three levels of virtualization now. Code that runs on bare metal is unnecessarily performant.
  • Cross-platform development is now a standard because of wide variety of architectures like mobile devices, cloud servers, embedded IoT systems. It was almost exclusively PCs 20 years ago.
  • Running your code locally is something you rarely do.
  • Documentation is always online and it’s called Google. No such thing as offline documentation anymore. Even if there is, nobody knows about it.
  • A tutorial isn’t really helpful if it’s not a video recording that takes orders of magnitude longer to understand than its text.
  • There is StackOverflow which simply didn’t exist back then. Asking a programming question involved talking to your colleagues.
  • People develop software on Macs.
  • Internet connectivity is the norm and being offline is an exception which is the opposite of how it was back then.
  • Security is something we have to think about now.
  • Mobile devices can now show regular web pages, so no need to create a separate WAP page on a separate subdomain anymore. We create mobile pages on separate subdomains instead.
  • We open source everything by default except the code that would really embarass us.
  • There are many more talented women, people of color and LGBT in the industry now, thanks to everyone who fought against discrimination. I still can’t say we’re there in terms of equality but we are much better.
  • Getting hacked is a regular occurence. Losing all your user data usually circumvented by writing a blog post that recommends changing passwords and that’s pretty much it. Apology isn’t required.
  • Working as a programmer remotely is easier than ever thanks to the new technologies like video conferencing, ubiquitous internet access and Keurigs.
  • We don’t use IRC for communication anymore. We prefer a bloated version called Slack because we just didn’t want to type in a server address.
  • We run programs on graphics cards now.
  • Your project has no business value today unless it includes blockchain and AI, although a centralized and rule-based version would be much faster and more efficient.
  • For some reason, one gigabyte is now insufficient storage space.
  • Because of side-channel attacks we can’t even trust the physical processor anymore.
  • A significant portion of programming is now done on the foosball table.
  • Since we have much faster CPUs now, numerical calculations are done in Python which is much slower than Fortran. So numerical calculations basically take the same amount of time as they did 20 years ago.
  • Creating a new programming language or even creating a new hardware is a common hobby.
  • Unit testing has emerged as a hype and like every useful thing, its benefits were overestimated and it has inevitably turned into a religion.
  • Storing passwords in plaintext is now frowned upon, but we do it anyway.

DP has always been an obstacle when preparing for interviews. For me it is one of the hardest topic. There were several times in the past that I tried to master it, but all attempts failed. Either because I could not find good resources, or because I did not have enough time to really dive into it, have a lot of practice, and identify different patterns. To tell the truth, I even feared that I would never be able to understand it well.

This winter I had another attempt, and made up my mind to grasp the techique. I solved/read 45 DP problems of different patterns in 4 days (yes, you might think that is quite slow). At the begining, I struggled as much as all my previous attempts, but slowly I found I am getting better and I started to be able to think in the DP-way. Today I solved several problems independently, with memoization and tabulation and even space optimizations, I think I am finally understand this category of problem. The process is hard and frustrating, I know! Thus, I want to share my experience so that you might get some help.

Resources / My way of learning

The resources I recommend for learning DP. I use them in the order as listed.

  1. Dynamic Programming
    If you are not familiar with DP yet, there’s no point in diving into Leetcode problems directly. The explanation of Basic Concepts is very clear. You could also try the first several basic problems to have a taste of DP.
  2. Some typical/famous DP problems (OPTIONAL)
    I would recommend you to try to read (you might not be able to solve it, which is totally fine!) several DP problems to have a tiny peek into DP. You do not really need try to identify the pattern or memorize the solution. Just to get some feeling about DP. You may not need it, but this is helpful for me. I recommend Longest Common Subsequence, 0/1 Knapsack, Climbing Stairs.
  3. From good to great. How to approach most of DP problems.
    A very good illustration of the motivation behind DP. DP is essentially an optimizaton for pure recursion. If we are solving overlapping subproblems, we can save result to subproblems, we avoid repeated computations. This also shows the implementaion: how to convert recursion code to memoization, and to tabulation. For me, it is very helpful.
  4. Dynamic Programming Patterns (MUST READ)
    Best post on DP ever! The summarized pattern, problem statement and approach are very helpful. I would say my previous efforts of learning DP brings me at point 3 (which is not easy as to understand the motivation and the implementation of DP are hard already!). For the last 4 days, I spent most of my time working on problems, grouped by patterns, mentioned in the post. All my previous nightmares on DP gone! You cannot afford to miss this post!
    Other notes I made when reading the post:
    • The statement, approach and code snippet for each pattern in the original post is helpful and comprehensive.
    • Pattern 1 and 2 are kind of similar.
    • One hint of pattern 3 problems is that they usually involve a list/array of numbers, either explicitly or implicity, like in 1130, 96 and 1039.
    • DP on strings is usually, if not always, done by comparing two chars at a time.
  5. Back to Back SWE
    Video explanation on many algorithm problems, including DPs. Detailed, slow and clear. There are several sentences from this guy, which I always remind myself when solving DP problems: “DP is not about building dp table. It is about identifying subproblem, and caching answers to subproblems in order to solve the original problem“.

DP implementation tips

With the following tips in mind, the implementation for memoization and tabulation is trivial!

  • The most important step, and also the first step, in solving DP problem is to identify the recursive equation. Then the implementation just follows recursion -> memoization -> tabulation.
  • For tabulation, every entry, like dp[i][j], that could be used must be filled. However for memoization, the value might not exist in the dp table because you can directly provide it in the return value of solve(mem, i, j).
  • It is easy to convert recursion to memoization. For tabulation, draw graph to see clearly how the dp table is filled up(Lower rows to higher rows, or reverse? Left to right, or reverse? Upper left to lower right, or reverse?). The direction of filling up the dp table affects the values of the loop variables used in tabulation. A common error occurs when the for loop does not conform to the way in which the dp table is filled.

Two styles of dp table

Quite commonly, dp tables are built such that dp[m][n] is the ultimate solution. However, there are also a number of DP problems where a variable is updated when building the dp table and the variables contains the final answer(e.g., 647).

Last note: keeps practicing! I think I would review those problems for several rounds in the coming weeks, just to keep my self comfortable with DP. When practicing, try to solve with recursive, memoization, tabulation, and even optimize the space when possible.


When I ask people at trendy big tech companies why algorithms quizzes are mandatory, the most common answer I get is something like “we have so much scale, we can’t afford to have someone accidentally write an O(n^2) algorithm and bring the site down”. One thing I find funny about this is, even though a decent fraction of the value I’ve provided for companies has been solving phone-screen level algorithms problems on the job, I can’t pass algorithms interviews! When I say that, people often think I mean that I fail half my interviews or something. It’s more than half.

When I wrote a draft blog post of my interview experiences, draft readers panned it as too boring and repetitive because I’d failed too many interviews. I should summarize my failures as a table because no one’s going to want to read a 10k word blog post that’s just a series of failures, they said (which is good advice; I’m working on a version with a table). I’ve done maybe 40-ish “real” software interviews and passed maybe one or two of them (arguably zero).

Let’s look at a few examples to make it clear what I mean by “phone-screen level algorithms problem”, above.

At one big company I worked for, a team wrote a core library that implemented a resizable array for its own purposes. On each resize that overflowed the array’s backing store, the implementation added a constant number of elements and then copied the old array to the newly allocated, slightly larger, array. This is a classic example of how not to implement a resizable array since it results in linear time resizing instead of amortized constant time resizing. It’s such a classic example that it’s often used as the canonical example when demonstrating amortized analysis.

For people who aren’t used to big tech company phone screens, typical phone screens that I’ve received are one of:

  • an “easy” coding/algorithms question, maybe with a “very easy” warm-up question in front.
  • a series of “very easy” coding/algorithms questions,
  • a bunch of trivia (rare for generalist roles, but not uncommon for low-level or performance-related roles)

This array implementation problem is considered to be so easy that it falls into the “very easy” category and is either a warm-up for the “real” phone screen question or is bundled up with a bunch of similarly easy questions. And yet, this resizable array was responsible for roughly 1% of all GC pressure across all JVM code at the company (it was the second largest source of allocations across all code) as well as a significant fraction of CPU. Luckily, the resizable array implementation wasn’t used as a generic resizable array and it was only instantiated by a semi-special-purpose wrapper, which is what allowed this to “only” be responsible for 1% of all GC pressure at the company. If asked as an interview question, it’s overwhelmingly likely that most members of the team would’ve implemented this correctly in an interview. My fixing this made my employer more money annually than I’ve made in my life.

That was the second largest source of allocations, the number one largest source was converting a pair of long values to byte arrays in the same core library. It appears that this was done because someone wrote or copy pasted a hash function that took a byte array as input, then modified it to take two inputs by taking two byte arrays and operating on them in sequence, which left the hash function interface as (byte[], byte[]). In order to call this function on two longs, they used a handy long to byte[] conversion function in a widely used utility library. That function, in addition to allocating an byte[] and stuffing a long into it, also reverses the endianness of the long (the function appears to have been intended to convert long values to network byte order).

Unfortunately, switching to a more appropriate hash function would’ve been a major change, so my fix for this was to change the hash function interface to take a pair of longs instead of a pair of byte arrays and have the hash function do the endianness reversal instead of doing it as a separate step (since the hash function was already shuffling bytes around, this didn’t create additional work). Removing these unnecessary allocations made my employer more money annually than I’ve made in my life.

Finding a constant factor speedup isn’t technically an algorithms question, but it’s also something you see in algorithms interviews. As a follow-up to an algorithms question, I commonly get asked “can you make this faster?” The answer is to these often involves doing a simple optimization that will result in a constant factor improvement.

A concrete example that I’ve been asked twice in interviews is: you’re storing IDs as ints, but you already have some context in the question that lets you know that the IDs are densely packed, so you can store them as a bitfield instead. The difference between the bitfield interview question and the real-world superfluous array is that the real-world existing solution is so far afield from the expected answer that you probably wouldn’t be asked to find a constant factor speedup. More likely, you would’ve failed the interview at that point.

To pick an example from another company, the configuration for BitFunnel, a search index used in Bing, is another example of an interview-level algorithms question.

The full context necessary to describe the solution is a bit much for this blog post, but basically, there’s a set of bloom filters that needs to be configured. One way to do this (which I’m told was being done) is to write a black-box optimization function that uses gradient descent to try to find an optimal solution. I’m told this always resulted in some strange properties and the output configuration always resulted in non-idealities which were worked around by making the backing bloom filters less dense, i.e. throwing more resources (and therefore money) at the problem.

To create a more optimized solution, you can observe that the fundamental operation in BitFunnel is equivalent to multiplying probabilities together, so, for any particular configuration, you can just multiply some probabilities together to determine how a configuration will perform. Since the configuration space isn’t all that large, you can then put this inside a few for loops and iterate over the space of possible configurations and then pick out the best set of configurations. This isn’t quite right because multiplying probabilities assumes a kind of independence that doesn’t hold in reality, but that seems to work ok for the same reason that naive Bayesian spam filtering worked pretty well when it was introduced even though it incorrectly assumes the probability of any two words appearing in an email are independent. And if you want the full solution, you can work out the non-independent details, although that’s probably beyond the scope of an interview.

Those are just three examples that came to mind, I run into this kind of thing all the time and could come up with tens of examples off the top of my head, perhaps more than a hundred if I sat down and tried to list every example I’ve worked on, certainly more than a hundred if I list examples I know of that someone else (or no one) has worked on. Both the examples in this post as well as the ones I haven’t included have these properties:

  • The example could be phrased as an interview question
  • If phrased as an interview question, you’d expect most (and probably) all people on the relevant team to get the right answer in the timeframe of an interview
  • The cost savings from fixing the example is worth more annually than my lifetime earnings to date
  • The example persisted for long enough that it’s reasonable to assume that it wouldn’t have been discovered otherwise

At the start of this post, we noted that people at big tech companies commonly claim that they have to do algorithms interviews since it’s so costly to have inefficiencies at scale. My experience is that these examples are legion at every company I’ve worked for that does algorithms interviews. Trying to get people to solve algorithms problems on the job by asking algorithms questions in interviews doesn’t work.

One reason is that even though big companies try to make sure that the people they hire can solve algorithms puzzles they also incentivize many or most developers to avoid deploying that kind of reasoning to make money.

Of the three solutions for the examples above, two are in production and one isn’t. That’s about my normal hit rate if I go to a random team with a diff and don’t persistently follow up (as opposed to a team that I have reason to believe will be receptive, or a team that’s asked for help, or if I keep pestering a team until the fix gets taken).

If you’re very cynical, you could argue that it’s surprising the success rate is that high. If I go to a random team, it’s overwhelmingly likely that efficiency is in neither the team’s objectives or their org’s objectives. The company is likely to have spent a decent amount of effort incentivizing teams to hit their objectives – what’s the point of having objectives otherwise? Accepting my diff will require them to test, integrate, deploy the change and will create risk (because all deployments have non-zero risk). Basically, I’m asking teams to do some work and take on some risk to do something that’s worthless to them. Despite incentives, people will usually take the diff, but they’re not very likely to spend a lot of their own spare time trying to find efficiency improvements(and their normal work time will be spent on things that are aligned with the team’s objectives)4.

Hypothetically, let’s say a company didn’t try to ensure that its developers could pass algorithms quizzes but did incentivize developers to use relatively efficient algorithms. I don’t think any of the three examples above could have survived, undiscovered, for years nor could they have remained unfixed. Some hypothetical developer working at a company where people profile their code would likely have looked at the hottest items in the profile for the most computationally intensive library at the company. The “trick” for both isn’t any kind of algorithms wizardry, it’s just looking at all, which is something incentives can fix. The third example is less inevitable since there isn’t a standard tool that will tell you to look at the problem. It would also be easy to try to spin the result as some kind of wizardry – that example formed the core part of a paper that won “best paper award” at the top conference in its field (IR), but the reality is that the “trick” was applying high school math, which means the real trick was having enough time to look at places where high school math might be applicable to find one.

I actually worked at a company that used the strategy of “don’t ask algorithms questions in interviews, but do incentivize things that are globally good for the company”. During my time there, I only found one single fix that nearly meets the criteria for the examples above (if the company had more scale, it would’ve met all of the criteria, but due to the company’s size, increases in efficiency were worth much less than at big companies – much more than I was making at the time, but the annual return was still less than my total lifetime earnings to date).

I think the main reason that I only found one near-example is that enough people viewed making the company better as their job, so straightforward high-value fixes tended not exist because systems were usually designed such that they didn’t really have easy to spot improvements in the first place. In the rare instances where that wasn’t the case, there were enough people who were trying to do the right thing for the company (instead of being forced into obeying local incentives that are quite different from what’s globally beneficial to the company) that someone else was probably going to fix the issue before I ever ran into it.

The algorithms/coding part of that company’s interview (initial screen plus onsite combined) was easier than the phone screen at major tech companies and we basically didn’t do a system design interview.

For a while, we tried an algorithmic onsite interview question that was on the hard side but in the normal range of what you might see in a BigCo phone screen (but still easier than you’d expect to see at an onsite interview). We stopped asking the question because every new grad we interviewed failed the question (we didn’t give experienced candidates that kind of question). We simply weren’t prestigious enough to get candidates who can easily answer those questions, so it was impossible to hire using the same trendy hiring filters that everybody else had. In contemporary discussions on interviews, what we did is often called “lowering the bar”, but it’s unclear to me why we should care how high of a bar someone can jump over when little (and in some cases none) of the job they’re being hired to do involves jumping over bars. And, in the cases where you do want them to jump over bars, they’re maybe 2” high and can easily be walked over.

When measured on actual productivity, that was the most productive company I’ve worked for. I believe the reasons for that are cultural and too complex to fully explore in this post, but I think it helped that we didn’t filter out perfectly good candidates with algorithms quizzes and assumed people could pick that stuff up on the job if we had a culture of people generally doing the right thing instead of focusing on local objectives.

If other companies want people to solve interview-level algorithms problems on the job perhaps they could try incentivizing people to solve algorithms problems (when relevant). That could be done in addition to or even instead of filtering for people who can whiteboard algorithms problems.

索罗斯

第一,本期资本战争发生在外汇、期货、股票甚至房地产市场,又按照时间线发生在不同国家——这些战局互有牵连,推进也是由浅入深。

第二,问不到亲历过的人,信息源全靠自己挖。

中西方媒体的倾向性都很明显——双方都不客观,因此本期对于任何数据和资料,我必须找到第三方来佐证,信息源除了新闻、市场数据、采访和交易公告,还包括索罗斯的《改革全球资本主义》,《金融炼金术》,《索罗斯论全球化》,沈联涛的《十年轮回-从亚洲到全球的金融危机》,港府救市基金和量子基金数据,金融四十人论坛论文,《全球资本主义危机》以及《朱镕基讲话实录》,我不是推荐这些书啊,只为四个字:以我为准。

言归正传,这场战争发起者索罗斯,大家都多少对他有些偏见,虽然是犹太人,但他家庭并不富裕,索罗斯早期是个不折不扣的屌丝。

不过他生性高调且富有冒险精神,所以其发家史也是充满了各种投机和豪赌,虽然我们都说是索罗斯血洗东南亚、索罗斯狙击香港,但其实干这些事儿的是国际游资,索罗斯只是代表了其中的一小部分,之所以他来扛大旗,是因为他本人喜欢到媒体上高谈阔论,频频发表讲话或者接受采访,所以久而久之名气大了就成了一面旗帜,至于为什么在金融界他的名字让人闻风丧胆,那得看看他在狙击香港之前都干了什么,也让我们探究一下索罗斯带领的国际游资特有的战术战略。

欧洲

时间回到那个风起云涌的时代,在经历了二战后,世界格局被一位坐在轮椅上的伟人重新定义,曾经的日不落眼睁睁看着霸权易主。英法式微,两德分裂,欧洲大国政治地位严重下滑,以至于在五十年代沦为冷战的马前卒。因此欧洲各国希望联合。摆脱美国制约。同时建立一个跨国家联盟,稀释各国主权缓解欧洲各国千年以来的冲突,同时强调经济合作,专注发展 欧共体也就是现在欧盟就是这个诉求的产物,在欧盟实行单一货币前,也就是还没有欧元这个货币前,欧洲国家制定了欧洲汇率机制(ERM),说白了就是把各国货币的汇率锁在一起,ERM就是英国金融市场的弱点,并且很快被国际游资察觉,当时欧洲的具体做法是,加入ERM的欧洲国家要把自己的货币跟德国马克挂钩,使自己的货币兑马克的汇率仅在小范围内波动,这样的操作造成只要马克贬值,欧洲其他货币一起贬值,马克升值,其他货币一起升值,这样你在欧洲经商的话,反正大家一起升或者一起贬,商人就不用太考虑汇率的影响了,这样才能推进欧洲经济一体化,当然这是政策制定者美好的初衷,但是汇率上的统一进退,就得保证利率上的统一进退,为什么呢,幼儿园化的讲,利率你们可以理解为把钱换成对应国家的货币后存起来的利息,如果英国利率是10%,德国利率5%,那你们肯定把钱换成英镑,都存到英国赚10%的利息啊,这样大家都把德国马克换成英镑,那英镑就会快速升值,而德国马克就会快速贬值,那这个欧洲汇率机制就维持不下去了,所以要想汇率统一进退,那利率也得统一进退。

这就是当时的背景 然而1989年柏林墙倒塌后,东德人大量涌入西德。德国的政府福利及社保开销大幅上升,因此德国政府财政赤字飙升,为了应对亏空,德国开始印钱,加剧通货膨胀,通胀压力迫使中央银行提高利率,这儿还得幼儿园化一下,通胀就是市面上的钱太多了,钱本身不值钱了,那怎么回收一下呢,就用提高利率的办法,利息高了,大家就会把钱用于投资或者直接存起来赚取利息,这样市面上流通的钱就减少了,当时德国被迫提高利率。而此时的英国经济正在衰退,通货紧缩,英国反而需要提高通胀,所以英国需要降低利率来刺激自己经济的复苏。但英国政府却受到欧洲汇率体系的限制,就因为德国的高利率政策,自己也得维系着高汇率和高利率,但是根据蒙代尔三角也称三元悖论:一国的经济目标有三种:

①各国货币政策的独立性;

②汇率的稳定性;

③资本的完全流动性。

这三者,一国只能三选其二,而不可能三者兼得。但英国想三个都占,在维持汇率稳定和自由的资本流动前提下,英国还要维持高利率。这就是饮鸩止渴。索罗斯也正是在赌英国无法同时维持这三者,而必须放弃汇率稳定,退出欧洲汇率机制。到时候被高估的英镑就会崩盘式下跌。

所以闻到了血腥的味的索罗斯开始为狙击英镑作准备。

虽然英国首相和财政大臣在各种公开场合一再重申坚持现有政策不变,英国有能力将英镑留在欧洲汇率体系内,翻译成人话就是,我们英国政府依旧会维持英镑和马克的汇率,那些投机者抛售的英镑我们政府全给他买回来,力挺英镑汇价,但索罗斯的判断是——英国政府是在虚张声势,所以开始大举做空英镑,做空是什么意思不做赘述,没做笔记的参考保时捷那期,索罗斯开始用各种方法借入英镑,一边在市场上抛售砸盘一边在媒体上制造恐慌情绪 在这段时间里,市场对于英镑的不信任开始发酵,英镑对马克的比价在不断地下跌,从2.95跌至2.8,英国政府为了对抗索罗斯代表的国际空军,从市场上大举买入33亿英镑来干预市场。于是当时就是一边索罗斯在抛,一边英国央行在接,虽然英镑汇率有所小涨,但很快又回到了下行通道,这使索罗斯为代表的国际游资更加坚信自己的判断,国际游资从1992年7月开始就从各大金融机构手中借走大量英镑和里拉,然后9月在外汇市场卖掉换成德国马克,大规模的抛售英镑和意大利里拉使得市场陷入恐慌,这些国家的央行尤其是英国央行——英格兰银行不得不拆巨资来购买各自的货币来维持原汇率,索罗斯在做空各国货币的同时,他还预测各国为了对抗自己,未来一定会短期内提高利率,因此在欧洲各国的利率期货的多头市场上做了埋伏,多头就是涨了赚钱空头就是跌了赚钱啊,随着国际游资的持续抛盘,市场上出现了恐慌情绪,加之索罗斯利用欧洲媒体散布不利于英国的消息,英镑持续下跌,空头渐渐压过了多头,英国政府为了扳回局势,从国际银行组织借入资金继续买入英镑,企图阻止英镑继续贬值,但此时市场看空英镑的趋势已经形成,以索罗斯代表的国际游资势头正猛,连很多英国本土机构加入了做空英镑的行列,所以虽然政府在市场上不断承接这些空军抛售的英镑,但国际游资也不断加大抛盘力度,仅索罗斯一人就抛售了120亿美元的英镑,大量基金、小银行甚至英国百姓都受到当时气氛的影响,恐慌的情绪造成大家争先恐后的抛售英镑,空军形成碾压之势,1992年9月15日,英镑对马克的比价已跌至欧洲汇率体系规定的下限。英镑已处于退出欧洲汇率体系的边缘。英国只好殊死一搏,之后的一天内,英格兰银行为了吸引资金回流,拉升英镑汇率,歇斯底里的将利率提升了两次,由10%分两次提高到了15%,现在咱们听说利率提高了0.5%就如同听到了大新闻一样转发,所以能想象当时英国一天就提升了5%是多么绝望的一个操作了吧。但没想到收效甚微,欧洲资本市场根本不买英国央行的账,再加上空头继续倾泻式的抛盘,英镑的汇率最终跌穿2.778的最低限。至此,英国政府已经动用了价值269亿美元的外汇储备,但最终还是惨败,当天晚上被迫宣布退出欧洲汇率体系。这样英镑汇率就不会维持在一个固定的区间内了,英镑犹如脱缰野马开始贬值,之后的几天里疯狂跳水,英镑兑马克贬值16%。英镑兑美元贬值26%,英国人史称黑色星期三。

索罗斯也被经济学家杂志称为打垮了英格兰银行的人。索罗斯曾在书中透露,当时准备了超过150亿美元的英镑筹码,然而在投入120亿的时候英国就投降了,索罗斯从英镑空头交易中获利近10亿美元,而索罗斯也早就预测到了各国在应对危机时的利率政策,在英国、法国和德国的利率期货上埋伏的多头也帮他赚了不少,再加上做空英镑的大获全胜,当年他的总利润高达20亿美元,在这一年里,索罗斯的基金增长了67.5% 这次战役的大捷让索罗斯尝到了甜头,以他为代表的国际游资也总结出了一套手术刀式的做空战术,既然我用这种方法能打败英国,那其他国家呢?

东南亚

之后他的目光瞄准了东南亚,90年代初期,当西方国家正处于经济衰退中,东南亚国家的经济却出现奇迹般的增长,当时由于广场协议,日本的制造业成本高得离谱,所以日本企业都把工厂迁往东南亚,尤其是泰国,随着时间的推移,泰国经济过热的迹象更加突出,泰国央行不断提高利率来降低通货膨胀。这个画面是不是似曾相识,高利率吸引了大量国际游资进入泰国,加之当时泰国在东南亚各国金融市场的自由化程度最高。这样泰国就被架到了一个很危险的高位,索罗斯闻到了猎物的味道,在 1995 年 1 月中旬泰国的房地产价格开始下跌时,索罗斯就对泰铢进行了试探性进攻——在即期外汇市场大量抛售泰铢。但泰国央行直接入市干预,此次进攻并未酿成危机。这里科普一下泰国的汇率制度,由于泰国实行盯住“一篮子货币”的固定汇率制度,而篮子中的主要货币——美元的权重占80%至82%;所以说白了泰国就是盯住美元的固定汇率,泰铢对美元的汇率长期维持在25∶1,这段时间由于美元汇率持续走低,泰铢跟随美元一起贬值,这就导致泰国出口持续增长,这也得幼儿园化一下,为什么货币贬值就能刺激出口,比如我是生产玩具的,一件玩具卖25泰铢我就能有不错的利润,如果泰铢对美元的汇率为25泰铢换1美元,那我玩具在美国就卖1美元,但现在泰铢贬值了,变成25泰铢换0.8美元了,那我的玩具在美国只用卖0.8美元我就能赚,相较于其他玩具,我的玩具就明显具备价格优势——又便宜又没有牺牲质量,大家都来买我的性价比高的玩具,这个玩具市场就慢慢被我占领了,因此当时泰国生产卖到了全世界,泰铢跟随美元贬值强力的刺激了泰国的出口,导致泰国经济快速发展。1986~1994年,年平均出口增长率达到22%,经济增长率达9.5%,成为“亚洲四小龙”之一。这个场景对于看过我经济崛起那期的同学,是不是觉得和我们加入世贸之后有点像? 但是自1993年起,美国计算机互联网产业崭露头角,美国经济迎来新的一轮增长,所以国际汇率有所变化,美元对主要货币由贬值转为升值,所以泰铢又跟着美元一起升值,刚才说货币贬值可以让我的东西卖的更实惠抢占市场,但这一升值,东西就变贵了,慢慢的卖不出去了,这就对泰国出口和经济增长当头一棒。泰国慢慢发现以前我把东西卖到全世界躺着赚外汇,但现在卖不出去了,国库慢慢儿入不敷出了,在固定汇率制度下,就是刚才说的盯住美元汇率制度下,外汇市场失去了自我调节的功能,面对不断恶化的国际收支形势,泰国政府不得不快速放开资本市场,通过各种优惠政策和高利率,吸引国外资本流入,弥补国际收支赤字,当时泰国政府走了两步棋,简直让国际游资开心到爆炸,一是开放离岸金融业务,凡获批准的商业银行均可从国外吸收存款和借款,并在泰国境内发放外币贷款。二是企业可以自由对外借款,非居民在国内和国外可自由开立泰铢账户,进行存款、借款和自由汇兑。听不懂没关系,你只要知道这两组政策直接给外国投机者打开了镣铐,来吧,来我们这儿随便玩儿。刚才说到泰国出口遭受打击,因此泰国制造业利润率下降,但资本都去回报率高的地方啊,你出口和实业不赚钱了,大量资本就流入房地产和股市,形成严重泡沫。1993到1996年仅3年多,泰国的房地产均价上涨了近4倍。 好了,现在万事俱备只欠索罗斯,到了1996年,泰国的资本账户已经完全开放,外资可以随意进入,1997年2月,索罗斯代表的国际游资开始行动了,他们与泰国央行签订远期合约,利用抵押当地资产的方式,借入大量泰铢然后在外汇市场抛售泰铢换成美元。关于期货、期权和收益互换我不赘述了,在保时捷和爱马仕LV那期都解释过,2 月 14 日,由于索罗斯大量抛售泰铢,泰国央行发现不妙开始反击,在外汇市场上大量购入泰铢,同时提高短期利率,就是索罗斯他们做空泰铢,总得先借到泰铢才能卖掉砸盘啊,但是借泰铢也得给别人利息,泰铢短期利率的提高就是为了打击这些国际投机分子的,我把借泰铢的利息定的巨高,看你们还借不借,这两板斧用出来之后,泰铢即期汇率很快得到稳定,第一轮做空失败,但泰国的外汇储备被大量消耗。索罗斯为首的国际游资并没有放弃,老子是干过英格兰银行的人,泰国我也势在必得,当年的一些新闻和采访非常有意思,索罗斯为首的国际对冲基金在各种国际媒体上,包括泰国当地媒体疯狂叫嚣,在舆论上形成了泰国这边是大刀加步枪,而我索罗斯是核弹加特林的局面,国际资本又一次站到了索罗斯的空军这一边,我在查阅资料的时候发现甚至一些泰国自己的银行和投资机构也在做空自己,发国难财,空头对多头的碾压之势形成,这一切都那么似曾相识,三个月之后,索罗斯为首的空头卷土重来,开始有计划有组织的在市场上抛售泰铢,手术刀式的砸盘开始了,至1997年5月底,泰铢从25:1下跌到26.6:1。而这时泰国央行才开始反击:直接动用100亿美元接盘空军抛出来的泰铢;同时禁止银行借泰铢给国际游资。而且泰国政府开始打击放出不利言论的媒体,出动警察进入交易所和监管机构进行调查,追踪抓捕空头账户的控制人,泰国政府的绝地反击并没能阻止大势,反而自己的公信力进一步下降,市场上消极的情绪弥漫,连泰国百姓都认为泰铢的价格就要崩溃了,于是老百姓就跟风用泰铢换美元。形成了全民泰国危机大逃亡,这导致本来要用外汇储备和国际游资搏杀的政府不得不把美元外汇储备换给这些恐慌的百姓,进一步加速了外汇储备的消耗,到1997年6月底,泰国外汇储备下降300亿美元,外汇池基本干涸,没有了外汇储备就等于没有了弹药,眼睁睁的看着国际游资继续砸盘。而此时泰铢对美元的汇率已经跌破28∶1。1997年7月2日,弹尽粮绝的泰国政府被迫宣布放弃固定汇率制度,当天泰铢对美元的汇率直接崩盘,跳水30%。之后的25天内,泰铢汇率继续疯狂跳水60%。泰铢如同废纸一般被大家唾弃,国际游资血赚40亿美元,泰国人的财富也被彻底洗劫一空,泰铢大幅度贬值迅速波及包括菲律宾、马来西亚、新加坡、韩国和印度尼西亚在内的整个东南亚地区,最终形成了亚洲金融危机,当然亚洲金融危机的形成也不只是因为泰铢崩盘,泰铢崩盘只是导火索之一,金融危机背后的原因非常复杂也很有意思以后会单做一期,在击破泰铢之后,索罗斯并不满足,他决定席卷整个东南亚,索罗斯的空头飓风很快就扫荡了印度尼西亚、菲律宾、缅甸、马来西亚等国家。国际游资用着同样的招数和套路,似曾相识的场景不断上演,印尼盾、菲律宾比索、缅元、马来西亚林吉特都不是国际游资的对手,一开战就直接拉闸,汇率集体跳水,导致这些国家的工厂倒闭,银行破产,物价上涨,百姓失业,东南亚一片鬼狐狼嚎,惨不忍睹。这场扫荡东南亚的空头飓风一举刮去了百亿美元的财富,使这些国家人民辛辛苦苦积累的几十年经济增长化为灰烬。人民的财富被国际游资洗劫一空。

香港

索罗斯再次笑到了最后,这时这位空军领袖的资金实力也因几场大捷变的空前强大,野心进一步膨胀 好了,现在进入本期最高能的环节,这个披荆斩棘的空军魔王目光盯上了我们的金融中心——香港,这回,他想把相同的手术刀套路用在香港。

香港和泰国也有一些相似之处,

第一是香港享有国际自由金融中心的美誉,国际资本在港流动相对自由,

第二,香港的联系汇率制度和泰国最早的固定汇率制度神似,关于汇率制度做一下科普,固定汇率就是泰铢那种盯住一篮子货币的制度,联系汇率是指将港币与某特定外币的汇率固定下来,并严格按照兑换比例使货币发行量随外汇存储量联动的货币制度。

解释一下,就是香港有三家发钞银行:汇丰银行、渣打银行和中国银行。它们每发行7.8港币必须向香港的金融管理局交纳1美元,以此来保证港币背后有美元作为支撑,维持汇率稳定。这就是联系汇率,而浮动汇率就是字面意思,泰国的固定汇率被国际游资玩烂了就只好实行有管理的浮动汇率了,汇率制度是理解国际资本战争、国际贸易甚至国际经济局势的基础,再幼儿园化一点可以把港币当成美元的代金券,合7.8港币代表1美元。代金券理论确实好记但我还是要扩展一下,2005年开始,香港联系汇率制度增加了强方兑换保证(1美元兑7.75港币)和弱方兑换保证(1美元兑7.85港币),并承诺只要触及区间的任何一边,香港金管局会入市干预,让汇率在1美元兑7.75-7.85港币之间浮动,回到1997年,国际游资认为香港和英国泰国差不多,没能力维持汇率稳定。自己的老套路可以用起来,所以开始着手狙击香港,但空军没有直接攻击外汇市场,而是进行手术似的慢条斯理的布局,开始提前低调借入港币,囤积筹码,等机会砸盘,这个跟做空泰铢和英镑的方法类似,但当局及时发现了有人在大量借入港币。香港金管局的总裁任志刚应对策略也很简单,听说你们要借港币?那我就提高香港银行同行业拆借利率,使借港币的利息成本大幅上升,看你们还借不借,而因为每次索罗斯大举借港币,任志刚就用这一招,同一招被用了三次,因此任志刚被圈内人笑称任一招。但这招却上了索罗斯的当,具体是怎么操作的呢, 1997 年 10月索罗斯为首的空军开始了进攻。空军在货币市场大量抛售港币,港币汇率下跌。到了10月底,许多银行在金管局结算账户上的港币已经没有足够结余,而金管局为了进一步提高国际游资借港币的成本,不仅没有注入港币流动性,反而发出通知,要对反复通过流动性机制向金管局借港币的银行收取惩罚性的高息。一时间港币难求,同业隔夜拆借利率一度飙升至 300%。这里肯定有人听晕了,降维说,就是当时港府把所有在市场上借港币的人都怀疑是索罗斯那边儿的空军,在市场上本来流通的港币就不多的情况下,还在大量回收流动性,市场上流通的港币就更少了,到处都借不到港币,大家没有了流动性(港币),市场上又借不到,怎么办,卖资产换钱呗,本来担心联系汇率不保的香港居民和外国投资者就更加大量抛售港币资产,尤其是港股,再加上银根的突然收紧,任一招的提高短期利率,使股市雪上加霜,股票开始暴跌,恒生指数一路向下。原来索罗斯在这等着呢,索罗斯早在几个月前就已经布局好了空头头寸,你让我借不到港币,但你的代价是楼市股市承压,我就做空股市,我的空军从股市下跌就可以大量获利。1997年10月,恒生指数因为这一波趋势持续下跌,这也引起了小规模的恐慌导致大量跟风抛盘,再加上国际游资的空军砸盘和舆论上的煽风点火,恒生指数从1997年10月3日的15000多点,一路倾泻至10月28日的9060点,引用人民网对当时的一段报道:1997年10月20日,香港市民苏顶明永远不会忘记这一天。苏顶明是一名普通的出租车司机,此前他到内地玩了6天。等回到香港时他却发现,原本价值30多万元的股票就只剩下不足4万元 在股票空头成功获利后,索罗斯为首的国际游资士气大振,又玩起了老一套舆论战术,频繁出现在各大媒体,发表各种不利于香港的言论,并且叫嚣着要像当年席卷东南亚一样,完全击溃港币,这一连串的舆论引导,马上又吸引了大量的空军秃鹫加入索罗斯的阵营,香港如临大敌,黑云压城城欲摧,1998 年起,空军开始连续对香港发动攻击。此时香港节节败退,已经开始有中文媒体和金融机构预言香港将要全面崩盘。路边小报八卦杂志都在担心香港的未来,恐慌的情绪一直在蔓延,1998 年 8 月,港币到了最危险的时候。国际游资的攻击变本加厉,大肆卖空港币。从 1998 年年初到 8 月中旬,每当港币利率稳定就借入港币,8 月港币空头规模已经超过了300 亿港币。同时空军大量积累股票和股指期货的空头头寸。根据当时官方新闻和数据,截至 1998年 8 月,国际游资大约有 8 万份空头合约。恒生指数每下跌 1000 点,国际游资便可获利40 亿港币。这些秃鹫在静静地等待时机 8 月 5 日,蓄谋已久的空军开始了新一轮猛烈轰炸,砸盘开始,即期外汇市场出现 300 多亿港币的卖盘,远期外汇市场也出现 116 亿港币的卖盘。8 月 6 日,香港和伦敦市场又出现 155 亿港币的卖盘。8 月 7 日,市场再出现 78亿港币的卖盘。随着汇率承压,股市也一路狂泻,空军同时在股票市场砸盘。空头的双线操作导致当日恒生指数收报 7018 点下跌 3.5%,5 个交易日累计跌去 917 点,2500 亿港币市值化为乌有 我们确实醒悟的太晚了,还没等我们做出反应,空军乘胜追击,1998年8月13日香港恒生指数跌穿6600点,从1997年8月16000多点的高位下跌将近10000点至6600点,这是香港的至暗时刻,中产阶级的财富惨遭洗劫,难道我们也要像英国泰国一样,被掠夺几十年的发展成果?此时离8月28日的恒指交割日时间已近,眼看这些国际游资马上可以血赚一笔。甚至还想试图想引起恐慌一举击溃港币的联系汇率制度,港府在这个存亡之秋,顶着巨大的压力做出了艰难的决策——政府入市干预,有人这时就会问了:做个决策怎么就艰难了,这样的决策,其实就是拿着人民的钱去和空军搏杀,而且要一定程度牺牲自由港的声誉,如果做了这个决策结局还是负面的,那个决策者很容易成为历史罪人甚至留下千古骂名,这段由于涉及国家领导人,为了能在国内发出,我不细说了,总之拍板是个很复杂的过程。而同一时期,港府也获得了来自中央的承诺,这里要强调一下,国内很多文章和视频都说眼看香港支撑不下去了,北京就支援香港大量的外汇储备,对于热血而充满期待的看客,我知道,如果这时候加上这样一个桥段,中央如救世主一般踩着祥云,闪现到被打的奄奄一息的港府身后,万丈光芒,伸出援手,绝地反击,那节目效果就太好了,但你们是了解我的,要对得起以我为准这四个字,还是要谨慎,所以我在这块进行了大量的挖掘,在各种回忆录、新闻、采访和人物传记中都没有找到明确的证据证明直接给了外储,所以这个热血的场景可能只是营销号为了迎合大家情绪的一厢情愿,既然没有证据,我们就不能说直接给外汇了,而且港府当年本身也有约980亿美元的外汇储备,虽然没直接给,但中央政府确实公开承诺过,以下是当时朱镕基答记者问的珍贵画面,朱总理当时这段话的力度非常大,这段我看了好几遍,在我写作的时候还有点激动的 1998年8 月 14 日是这场战役的转折点,此前被痛打的香港金融市场,弥漫的硝烟中突然杀出一只训练有素的军队,政府入市搏杀绝地反击。政府开始有序的购买恒生指数中 33 种成分股,拉动指数攀升,当天恒生指数上升 564 点,报收 7244 点,国际游资受到初步打击。同时港府在远期外汇市场上承接国际游资的卖盘并要求各券商不要向国际游资借出股票,切断空军的“弹药”供应。局势开始逆转,空军接连遭受打击,三市场开始呈现上扬趋势,空军开始慌了,他们没想到中央会公开承诺也没想到港府会入市,那既然这样,我就要把你们一波全带走,歇斯底里的空军准备玩一票大的,他们认为,在结算日8月28日前,凭借手中还有大量筹码,可以一举击溃整个香港 1998年8月27日,也就是结算日8月28日前一天。上午10点股市开盘。一开始,空军的卖盘就如排山倒海一般扑来。在第一个15分钟内,成交额就达19亿港币;在第二个15分钟内,成交额为10亿港币。而在收市前的15分钟,战斗进入白热化状态,全天交易之惨烈,令场上所有交易员都目瞪口呆。这一天,香港政府动用了200亿港币,委托10家经纪行在33家恒指成分股上围追堵截。恒生指数报收7922点,比上一个交易日上扬88点,这是自97年11月4日以来的最高点。 对于即将来临的8月28日结算日,国际游资的空头和港府的多头,当晚无人入眠 时间最终来到了决战之日, 8 月 28 日这一天香港街道熙熙攘攘,这座城市依旧像往常一样运转,风平浪静的表象下是金融市场的风起云涌,有一点要提前科普一下,恒指期货的结算价格为这一天每五分钟恒生指数的平均值,因此,要抬高结算价,就必须保证恒生指数走势平稳且一直保持在高位,要达此目的,我们必须得竭尽全力,寸土必争,换句话说,索罗斯代表的空军砸多少,我们就得接多少,8 月 28 日上午10点整开市后,仅5分钟,股市的成交额就超过了39亿港币。半小时后,成交金额就突破了100亿港币,到上午收盘,成交额已经达到400亿港币,下午开盘后抛售有增无减,成交量一路攀升,但国家队的围追堵截一直让恒指和期指始终维持在7800点以上,随着下午4点的收盘钟声响起,显示屏上不断跳动的恒指、期指、成交金额最终分别锁定在7829点、7851点和790亿港币上。根据还能找到的且有第三方佐证的资料和报道,当时空军约在7500点卖空恒指8月合约,而结算点位收在7851点,空军在做空恒指8月合约上巨亏,在这一天,港府动用了几乎所有可以动用的外汇储备,全盘吃下国际卖盘,平均每分钟就有价值3.5亿的股票易手。全天成交额达到了香港股市有史以来的最高纪录——790亿港币。2005年3月17日出版的《南方周末》曾以这样一段文字记述当年的那一幕:“许多香港人攥紧了矿泉水瓶,在炎热的天气里挤在一起,看着证券交易所的显示牌目瞪口呆。” 1998年9月7日,香港金融管理局颁布了外汇、证券交易和结算的新规定,使国际游资的投机大受限制。当日恒生指数飙升588点,以8076点报收。加上日元升值、东南亚金融市场趋稳等因素,空军的资金、换汇成本大幅上升,不得不败退离场:9月8日,9月合约价格升到8220点。8月底转仓的期指合约要平仓退场,每张合约又要亏损4万港币。至此,国际游资见大势已去,纷纷丢盔弃甲,落荒而逃 回顾整场战役,虽然空军在最后阶段的空单巨亏,但是很多在早期就布局做空港股的投机者还是赚的,有人就要问了,为什么港府只拉到7851点,而不是想尽办法拉到10000点甚至更多呢,这样不就可以让空军亏的更多吗,先不说当时我们有没有能力做到,就算可以做到我们也不会去做的,香港以自由市场的招牌吸引国际资本来维持繁荣,索罗斯要打垮三个市场还要击溃联系汇率机制。这就太过分了,港府才出手干预,打退了点到为止即可,即便这样当时做出决策的人还心惊胆战,如果直接把索罗斯打死,虽然我们扬眉吐气,但以后国际资本就不认香港这块自由市场的招牌了,所以关于这场战役的胜负结论如下。1、政府是胜方:这场战役保住了恒指,保住了三个市场的稳定,也保住了联系汇率制度,达到了港府的战略目标,虽然损失了部分自由市场的声誉,但相对于保住根基是值得的。同时国家队在底部进入市场干预进行救市,在市场恢复后,国家队的基金盈利颇丰。2、索罗斯代表的空军并不是最大输家,香港民众、普通股民和小型跟风投机者才是损失最惨重的,尤其是从16000跌到6600那段时间,在三个市场中,中产阶级财富遭受重创。3、国际游资一部分确实是亏的很惨,但还是有很多早期就布局的赚了不少,索罗斯的量子基金前期赚钱,后期赔钱,两者相抵总体上没赔多少,这期最难的一块就是探索到底索罗斯是亏了还是赚了,如果是亏了,到底亏了多少,双方的战损比也是一场战役胜负的重要评判标准,我对比了量子基金98年的年报,现在居然还能搜到,水分多大先不说,至少当时这些基金在信息披露和投资者关系这一块还是很规范的,年报中指出截至1998年12月31日,基金业绩受到两方面头寸的重大不利影响——集中在俄罗斯和印尼市场,而没提到香港,可以看出当年在香港亏损占总亏损比例不大,而索罗斯本人在自传中的态度是:在香港基本没亏。但我不信,因为我在研究他的资料和背景时发现,这个b说话也不完全靠谱,曾经上个月的答记者问和这个月的采访对不上,自己写的书和采访时候说的又不一样。所以他的话也不能全信,这期文案我在写的时候老以为自己弄错了,把三方数据拿出来一对才知道信息源本身矛盾,再加上他旗下的基金名字又特别相似,海外媒体报道的时候还会把名字写串,各种因素导致写到这块的时候因为挖掘和核对信息头快炸了,但怎么都对不上,所以我放弃计算具体金额了,我选择换个思路——确定亏损的区间,首先我们刚才知道了他亏损的数量级,再根据当时一则八卦新闻——某接近索罗斯的Banker声称,1998年除了在俄罗斯的亏损外,索罗斯集团在香港也亏损了20亿美元。这个八卦新闻是不是真的不要紧,重要的是量子基金的官方发言人出来辟谣了,说亏损20亿美元是错误的,并且拒绝回答亏损是比20亿多还是比20亿少。巫师给大家分析一下啊,当时这件事在金融圈沸沸扬扬,市场上八卦和谣言四起,官方只辟谣了20亿这个数字,图什么呢,我索罗斯不要面子的啊,我天天在媒体上叫嚣要干死香港结果你现在说我赔了20亿,我这老脸往哪放,肯定是根本没赔这么多你非说我赔了这么多,侮辱了我量子基金的名誉,我旗下的基金都是很强的很能赚钱的啊,你们别泼脏水,说白了就是这个行为本身大概率承认了亏损小于20亿,这回破案了,看来八卦确实是接近真相的重要手段,但是这只是我的推论,只是给大家一个概念,我为什么这么纠结他到底亏了多少呢,因为国内的文章和视频经常动不动就鼓吹索罗斯大败亏损百亿什么的,而我想给大家一个客观的概念,免得听信了营销号,好像这次战役我们真把人家血虐了似的,其实人家只是小亏,索罗斯亏得最惨的还是在毛子手里,因为毛子犯浑耍赖,还有他在日元也栽了大跟头,如果反馈好可以单做一个索罗斯失败合集,这一场香港保卫战的终结标志着国际游资的手术刀式战略行不通了,从1992攻击英国就开始使用的这一系列战术战略,也是国际游资自认为万能公式的攻击套路,在香港的决战中寿终正寝

我给大家的结论虽然是索罗斯没亏多少,但是这场战役对我们意义重大,1998年那会儿,欧美看咱们就跟看个弱智一样,又穷又傻,衣衫褴褛,愚昧落后,在他们眼中,98年的我们就是个落后的第三世界国家,在这些金融大鳄眼中,我们的经济金融实力如同东南亚小国一样可以任人鱼肉,他们认为他们可以如探囊取物般从香港掠夺巨量的财富,甚至不惜把香港蹂躏至三大市场崩溃,汇率制度崩盘,人民几十年辛勤劳动积累的财富化为乌有,但是我们成功阻止了这场噩梦的发生,这一场打赢在家门口的硬仗,有着特殊的意义 我们都看到了中国在各个领域的崛起:政治和外交话语权的逐步扩大,军事领域国防力量的推陈出新,航天领域新的技术突破,中国资本走向世界,还有我们的高铁、基建、5G等等等等,我们在这些看的见的地方站起来了,甚至在一些领域引领全球,然而在你们看不到的金融领域,在这些数字和博弈的背后,在各种交易、算法和规则的斡旋背后,我们也用一场场硬仗维护着中华民族的尊严和威慑,这次索罗斯代表的国际游资撞到我们这块钢板,是在向全世界传达一个重要信息——这个市场不好惹。一场硬仗让家门外的豺狼虎豹不敢轻易大举攻击人民币,给未来的人民币汇率稳定和十几年的经济腾飞打下坚实基础,我们的果敢和坚定反击让这些秃鹫一直对中国主权金融实体保有敬畏,在帷幕前的成就也是来自于帷幕后的支撑,1998年后的中国在国际上披荆斩棘,我们的经济列车过关斩将,历史车轮滚滚向前,时代洪流浩浩汤汤,广袤的华夏大地上一幅幅绚丽壮阔的史诗不断盛开,中华民族能以更加昂扬的姿态立于世界之林,纵横20余年,在世界的海洋中,中国号这艘巨轮,承载着我们共同意志和向往,凝聚着台前幕后各行各业奋斗进取的磅礴力量,虽然周围暗涌环伺,但我们依旧昂扬向前,在看的见的地方站的直是因为在看不见的地方站的稳,这也是那一代先驱传承下来的隐形宝藏 资本永不眠。