跳到主要内容

Hackers & Painters

作者: 保羅·格雷厄姆 (Paul Graham) 译: 阮一峰


  • 出于兴趣而解决某个难题,不管它有没有用,这就是黑客。

  • 黑客伦理 [hacker ethic]

    • 使用计算机以及所有有助于了解这个世界本质的事物都不应该受到任何限制。任何事情都应该亲手尝试。\ Access to computers-and anything that might teach you something about the way the world works-should be unlimited and total. Always yield to the Hands-On Imperative!
    • 信息应该全部免费。\ All information should be free.
    • 不信任权威,提倡去中心化。\ Mistrust Authority-Promote Decentralization.
    • 判断一名黑客的水平应该看他的技术能力,而不是看他的学历、年龄或地位等其他标准。\ Hackers should be judged by their hacking, not bogus criteria such as degrees, age, race, or position.
    • 你可以用计算机创造美和艺术。\ You can create art and beauty on a computer.
    • 计算机使生活更美好。\ Computers can change your life for the better.

    根据这六条“黑客伦理”,黑客价值观的核心原则可以概括成这样几点:分享、开放、民主、计算机的自由使用、进步。

  • 计算机程序只是文本而已。你选择什么语言,决定了你能说什么话。编程语言就是程序员的思维方式。

  • 阿尔伯蒂有一句名言:“任何一种艺术,不管是否重要,如果你想要在该领域出类拔萃,就必须全身心投入。”

黑客与画家

  • 黑客搞懂“计算理论” [theory of computation] 的必要性,与画家搞懂颜料化学成分的必要性差不多大。一般来说,在理论上,你需要知道如何计算“时间复杂度”和“空间复杂度” [time and space complexity];如果你要写一个解释器,可能还需要知道状态机 [state machine] 的概念;除此以外,并不需要知道特别多的理论。这些可比画家必须记住的颜料成分少很多。

  • 因为如果你不爱一件事,你不可能把它做得真正优秀,要是你很热爱编程,你就不可避免地会开发你自己的项目。

  • 黑客就像画家,工作起来是有心理周期的。有时候,你有了一个令人兴奋的新项目,你会愿意为它一天工作 16 个小时。等过了这一阵,你又会觉得百无聊赖,对所有事情都提不起兴趣。

  • 对于编程,这实际上意味着你可以把 bug 留到以后解决。消灭 bug 对我来说属于轻松的工作,只有在这个时候,编程才变得直接和机械,接近社会大众想象中的编程的样子。消灭 bug 的过程就像解一道数学题,已知许许多多的约束条件,你只要根据条件对方程求解就可以了。你的程序应该能产生 x 结果,但是却产生了 y 结果。哪里出错了?你知道自己最后肯定能够解决这个问题,所以做起来很轻松,就好像刷墙一样,接近于休闲了。

  • “程序写出来是为了让人看懂它的算法,附带告诉计算机如何执行。”一种好的编程语言应该比英语更容易解释软件。只有在那些不太成熟、容易出现问题的地方,你才应该加上注释,提醒读者注意那里,就好像公路上只有在急转弯处才会出现警示标志一样。

不能说的话

  • 大多数成年人故意让孩子对世界有一个错误的认识。最鲜明的例子之一就是圣诞老人。我们觉得,小孩子相信圣诞老人,真是太可爱了。我本人其实也是这样想。但是,扪心自问,我们向孩子灌输圣诞老人的神话,到底是为了孩子,还是为了我们自己?

    我在这里不讨论这样做是否正确。家长想要塑造孩子的心灵,把他们装扮成可爱的小宝宝,这可能是无法避免的。我也可能这样做。但是,就本文而言,这样做会产生一个重要的结果,那就是孩子“被迫”在一个精心设计的环境中长大。他的头脑或多或少是纯洁无暇的,一点也不知道那些“不能说的话”,从来没有被真实的社会生活“污染”过。孩子眼里的世界是不真实的,是一个被灌输进他们头脑的假想世界。将来当孩子长大以后接触社会,就会发现小时候以为真实的事情,在现实世界中是荒唐可笑的。

  • 那些团体神经越紧张,它们所产生的禁止力量就越大。伽利略因为宣布日心说而遭到教廷的审判,这件事讽刺的地方在于,他只是在宣传哥白尼的观点,而后者却安然无恙。事实上,哥白尼不仅不反对教廷,还是一个虔诚的天主教教士,他把自己的著作献给教皇。不幸的是,伽利略正赶上教廷内部反对派上台,宗教改革制度压制,任何非正统的思想遭受到前所未有的严厉控制和禁止。

    为了在全社会制造出一个禁忌,负责实施的团体必定既不是特别强大也不是特别弱小。如果一个团体强大到无比自信,它根本不会在乎别人的抨击。美国人或者英国人对国外媒体的诋毁就毫不在意。但是一个团体太弱小,就会无力推行禁忌。有一种行为怪癖叫做“嗜粪症” [coprophila],它的患者人数以及影响势力眼下似乎就不太强大,无法把自己的观点推广给其他人。

    我猜想,道德禁忌的最大制造者是那些权利斗争中略占上风的一方。你会发现,这一方有实力推行禁忌,同时又软弱到需要用禁忌来保护自己的利益。

    大多数的斗争,不管它们实际上争的是什么,都会以思想斗争的形式表现出来。

为什么这样做

  • 有人可能会问,为什么要去找出“不能说的话”?为什么要故意打探那些龌龊的,见不得人的思想观点?你明知那里有挡住去路的石头,为什么还要把它们翻过来看个究竟呢?

    首先,我这样做与小孩子翻石头是出于同样的原因:纯粹的好奇心。我对任何被禁止的东西都有特别强烈的好奇心。我要亲眼看一下,然后自己做决定。

    其次,我这样做是因为我不喜欢犯错。如果像其他时代一样,那些我们自以为正确的事情将来会被证明是荒唐可笑的,我希望自己能够知道是哪些事情,这样可以使我不会上当。

    再次,我这样做,是因为这是很好的脑力训练。想要做出优秀作品,你需要一个什么问题都能思考的大脑。尤其是那些似乎不应该思考的问题,你的大脑也要养成思考它们的习惯。

    优秀作品往往来自于其他人忽视的想法,而最被忽视的想法就是那些被禁止的思想观点。

  • “守口如瓶”的真正缺点在于,你从此无法享受讨论带来的好处了,讨论一个观点会产生更多的观点,不讨论就什么观点也没有。所以,如果可能的话,你最好找一些信得过的知己,只与他们畅所欲言、无所不谈。这样不仅可以获得新观点,还可以用来选择朋友。能够一起谈论“异端邪说”并且不会因此气急败坏的人,就是你最应该认识的朋友。

  • 你不仅要远距离观察人群,更要远距离观察你自己。顺便提一句,这可不是激进的想法。儿童和成年人的主要差别就在这里。儿童精疲力竭时,可能会大发脾气,因为他不知道为了什么;成年人则会了解是个人的身体状况问题,与外界无关,说一句“没关系,我只是累了”。我想,通过类似的机制,一个人完全可以识别和抵制外界流行的道德观念,把它们与内心世界相分离。

    如果你想要清晰的思考,就必须远离人群。但是走的越远,你的处境就会越困难,受到的阻力也会越大,因为你没有迎合社会习俗,而是一步步地与它背道而驰。小时候,每个人都会鼓励你不断成长,变成一个心智成熟、不再耍小孩脾气的人。但是,很少有人鼓励你继续成长,变成一个怀疑和抵制社会错误潮流的人。

    如果自己就是潮水的一部分,怎么能看见潮流的方向呢?你只能永远保持质疑。问自己,什么话是我不能说的?为什么?

另一条路

  • 如果软件的新版本要等一年后才能发布,我就会把大部分新构思束之高阁,至少过上一段时间再来考虑。但是,构思这种东西有一个特点,那就是它会导致更多的构思。你有没有注意过,坐下来写东西的时候,一半的构思是写作时产生的?软件也是这样。实现某个构思,会带来更多的构思。所以,将一个构思束之高阁,不仅意味着延迟它的实现,还意味着延迟所有在实现过程中激发的构思。事实上,将一个构思束之高阁,甚至会限制新构思的产生。因为你看一眼堆放在一边、还没实现的构思,就会想“我已经为下一个版本准备了很多新东西要实现了”,你就懒得再去思考更多的新功能了。

  • 现在,创业公司有更多的理由选择互联网软件创业,因为开发桌面软件越来越乏味了。如果你现在开发桌面软件,就不得不接受微软公司的授权条款,调用它的 API,为它那个 bug 百出的操作系统伤透脑筋。历尽千辛万苦,你最终写出了一个受大众欢迎的软件,这时你可能会发现,你所做的一切其实只是在为微软公司做市场调查。

  • 你能够做到这一点,意味着竞争者也能做到这一点,所以长时间工作变成了一种必须,不得不如此。因为你能做到,所以你必须做到。这简直就是逆向的帕金森定律

  • 不少公司都很想知道,什么事情可以外包,什么事情不可以外包,一个可能的答案是,公司内部所有不直接感受的竞争压力的部门都应该外包出去,让它们暴露在竞争压力之下。(我这里所说的“外包”,指的是聘请另一家公司来执行,而不是指把业务部门转移到海外。)

  • 由于个人经历的关系,特雷弗·布莱克韦尔对这一点的认识可能比其他任何人都深刻。他写到:“我会进一步说,由于互联网软件的程序员非常辛苦,所以会使得经济优势根本性地从大公司向创业公司转移。互联网软件要求的那种工作强度和付出,只有当公司是其本人所有时,程序员才愿意提供。软件公司可以雇用到能干的人,让他们去干轻松的事情,也可以雇到不能干的人,让他们去干艰苦的事情。但是无法雇到非常能干的人,让他们去干非常艰苦的事情。因为互联网软件的创业不需要太多的资本,所以大公司可以与创业公司竞争的优势就所剩无几了。”

如何创造财富

  • 交换媒介的优点是,它使得交易可以进行下去。缺点是,它往往模糊了交易的实质。人们觉得做生意就是为了挣钱,但是金钱其实只是一种中介,让大家可以更方便地获得自己想要的东西。大多数生意的目的是为了创造财富,做出人们真正需要的东西。

  • 金钱不是财富,而只是我们用来转移财富所有权的的东西。

  • 公司就是许多人聚在一起创造财富的地方,能够制造更多人们需要的东西。

  • 我们这个世界,你向下沉沦或者向上奋进都取决于你自己,不能把原因推给外界。

  • 一个大学毕业生总是想“我需要一份工作”,别人也是这么对他说的,好像变成某个组织的成员是一件多么重要的事情。更直接的表达方式应该是“你需要去做一些人们需要的东西”。即使不加入公司,你也能做到。公司不过是一群人在一起工作,共同做出某种人们需要的东西。真正重要的是做出人们需要的东西,而不是加入某个公司。

    对于大多数人来说,最好的选择可能是为某个现存的公司打工。但是,理解这种行为的真正含义对你没有什么坏处。工作就是在一个组织中,与许多人共同合作,做出某种人们需要的东西。

  • 要致富,你需要两样东西:可测量性可放大性。你的职位产生的业绩,应该是可测量的,否则你做的再多,也不会得到更多的报酬。此外,你还必须有可放大性,也就是说你做出的决定能够产生巨大的效应。

  • 如果你有一个令你感到安全的工作,你是不会致富的,因为没有危险,就几乎等于没有可放大性。

  • 乔布斯曾经说过,创业的成败取决于最早加入公司的那十个人。我基本同意这个观点,虽然我觉得真正决定成败的其实 只是前五人。小团队的优势不在于它本身的小,而在于你可以选择成员。我们不需要小村庄的那种“小 ”,而需要全明星第一阵容的那种“小”。

  • 什么是技术?技术就是某种手段,就是我们做事的方式。如果你发现了一种做事的新方式,它的经济价值就取决于有多人使用这种新方式。技术就是钓鱼的鱼竿,而不是那条鱼。

  • 只要懂得藏富于民,国家就会变得强大。让书呆子保住他们的血汗钱,你就会无敌于天下。

  • “财富”这个词有很多意思,有些并不是指物质财富。我不想做深入讨论,研究到底什么才是真正的财富。我这里指的只是一种特定的技术层面上的“财富” ——人们用金钱向你交换东西。这是一种很有趣、很值得研究的财富,因为它使得你免于饥饿,而且人们是否用金钱交换这种财富取决于他们,而不是取决于你。

关注贫富分化

  • 事实上,财富和金钱是两个概念。金钱只是用来交易财富的一种手段,财富才是有价值的东西,我们购买的商品和服务都属于财富。你到 海外旅游时,不用看当地人的银行账户就会知道你来到的是富国还是穷国。你只要看看他们的财富就行了:建筑、街道、服装、健康状况等。

  • 技术的发展使得通过创造而积累财富的速度第一次有可能超过通过偷窃而积累财富的速度。19 世纪典型的富人不是宫廷朝臣,而是实业家。

设计者的品味

  • 对于建筑师和设计者,它意味着美依赖于一些精心选择的结构性元素,而不是依赖于表面装饰品的堆砌。(装饰品本身并不是坏事,只有它被用来掩盖结构的苍白时,才变成了一件坏事。)

  • 如果解决方法是丑陋的,那就肯定还有更好的解决方法,只是还没有发现而已。

  • 白描其实是最难画的视觉媒介,因为它们要求近乎完美的再现。用数学语言说,线条属于闭合解 [closed-form-solution],水平不够的艺术家没有办法直接解决问题,只能通过不断逼近来求解。

  • 人们有时会说自己有了“状态”,我的理解是,他们这时可以控制自己的脊髓。脊髓是更本能的反应,面对难题时,它能释放你的直觉。

  • 好设计是模仿大自然的设计。我不是说模仿大自然这种行为本身有多么好,而是说大自然在长期的演化中已经解决了很多设计问题。所以,如果你的设计与大自然很接近,那么它基本上不会很差。

一百年后的编程语言

  • 在长期的职业生涯中,我发现冗余的代码会导致更多冗余的代码,不仅软件如此,而且像我这样性格懒散的人,我发现在床底下和房间的角落里这个命题也成立,一件垃圾会产生更多的垃圾。

  • 编程语言进化缓慢的原因在于它们并不是真正的技术。语言只是一种书写法,而程序则是一种严格符合规则的描述,以书面形式记录计算机应该如何解决你的问题。所以,编程语言的进化速度更像数学符号的进化速度,而不像真正的技术(比如交通或者通信技术)的进化速度。数学符号的进化是缓慢的渐变式变化,而不是真正技术的那种跳跃式发展。

  • 我已经预测到了,一旦未来硬件的性能大幅提高将会发生什么事。新增加的运算能力都会被糟蹋掉。

    在我学习编程的年代,计算机还是稀罕玩意。我记得当时使用的微机型号是 TRS-80,它的内存只有 4K,为了把 BASIC 程序装入内存,我不得不把源码中的空格全部删除。我一想到那些极其低效率的软件,不断重复某些愚蠢的运算,把硬件的计算能力全部占用,就感到无法忍受。但是,我的这种反应是错的,我就像某个出身贫寒的穷孩子,一听到要花钱就舍不得,即使把钱用在重要场合(比如去医院看病)都会觉得难以接受。

书呆子的复仇

  • 如果你想在软件业获得成功,就使用你知道的最强大的语言,用它解决你知道的最难的问题,并且等待竞争对手的经理做出自甘平庸的选择。

梦寐以求的编程语言

  • 编程语言不是存在于真空之中,“编程”其实是及物动词,黑客一般都是为某个系统编程。在现实中,编程语言总是与它们依附的系统联系在一起的。

  • 无法以一种语言本身的优缺点评判这种语言。另一个结果则是,只有当一种语言是某个系统的脚本语言时,它才能真正成为编程语言。如果你对此很吃惊,觉得不公平,那么我会跟你说不必大惊小怪。这就好比大家都认为,如果一种编程语言只有语法规则,没有一个好的实现 [implementation],那么它就不能算完整的编程语言。这些都是很正常很合理的事情,编程语言本来就该如此。

  • 你只需要不停地重复同一句话,最终人们将会开始倾听。人们真正注意到你的时候,不是第一眼就看到你站在那里,而是发现过了这么久你居然还在那里。

  • 著名散文家 E.B. 怀特说过,“最好的文字来自不停的修改”。

  • 为了写出优秀软件,你必须同时具备两种相互冲突的信念。一方面,你要像初生牛犊一样,对自己的能力信心万丈;另一方面,你又要像历经沧桑的老人一样,对自己的能力抱着怀疑态度。在你的大脑中,有一个声音说:“千难万险只等闲”,还有一个声音却说“早岁哪知世事艰”。

    这里的难点在于你要意识到,实际上两种信念并不矛盾。你的乐观主义和怀疑倾向分别针对两个不同的对象。你必须对解决难题的可能性保持乐观,同时对当前解法的合理性保持怀疑。

    做出优秀成果的人,在做的过程中常常觉得自己做得不够好。其他人看到他们的成果觉得棒极了,而创造者本人看到的都是自己作品的缺陷。这种视角的差异并非偶然,因为只有对现状不满,才会造就杰出的成果。

  • 因此现实中,尽管软件功能越来越强大,内部接口却往往一成不变,成为整个系统中拖后腿的部分。

    一种可能的解决方法是,将软件内部的接口设计成垂直接口而不是水平接口。这意味着软件内部的模块是一个个垂直堆积起来的抽象层,层与层之间的接口完全由其中的一层控制。如果较高的一层使用了较低的一层定义的语言,那么接口就由较低的一层控制;如果较低的一层从属于较高的一层,那么接口就由较高的一层控制。

  • 帕金森定律 [Parkinson's Law] 的一种原始表达形式是“工作总是到最后一刻才会完成”,后来引申到计算机领域就变成了“数据总是会填满所有的空间”,更一般性的总结则是:“对一种资源的需求总是会消耗光这种资源的所有供应”。

设计与研究

  • 设计与研究的区别看来就在于,前者追求“好” [good],后者追求“新” [new]。优秀的设计不一定很“新”,但必须是“好”的;优秀的研究不一定很“好”,但必须是“新”的。我认为这两条道路最后会发生交叉:只有应用“新”的创新和理论,才会诞生超越前人的最佳设计;只有解决那些值得解决的难题(也就是“好”的难题),才会诞生最佳研究。所以,最终来说,设计和研究都通向同一个地方,只是前进的路线不同罢了。

  • 怎么理解编程语言?你不要把它看成那些已完成的程序的表达方式,而应该把它理解成促进程序从无到有的一种媒介。这里的意思是说,成品的材料和开发时用的材料其实是不一样的。搞艺术的人都知道,这两个阶段往往需要不同的媒介。比如,大理石是一种非常良好、耐用的材料,很适合用于最后的成品,但是它极其缺乏弹性和灵活性,所以不适合在构思阶段用来做模型。

    最后写出来的程序就像已经完成的数学证明一样,是一棵经过精心修剪的树木,上面杂乱滋生的树杈 都已经被剪去了。所以,评价一种语言的优劣不能简单地看最后的程序是否表达得很漂亮,而要看程序从无到有的那条完成路径是否很漂亮。

  • 画家之间甚至流传着一句谚语:“画作永远没有完工的一天,你只是不再画下去而已。”

  • “弱即是强”指的是一种软件传播的模式,由 Common Lisp 专家里查德·加布里埃尔 [Richard P. Gabriel] 于 1991 年在 Lisp: Good News, Bad News, How to Win Big 一文中首先提出。它的含义非常广泛,涉及软件设计思想的各个方面,其中的一个重要结论就是软件功能的增加并不必然带来质量的提高。有时候,更少的功能(“弱”)反而是更好的选择(“强”),因为这会使得软件的可用性提高。相比那些体积庞大、功能全面、较难上手的软件,一种功能有限但易于使用的软件可能对用户有更大的吸引力。加布里埃尔本人经常举 Unix 和 C 语言的例子,Unix 和 C 在设计上考虑了实际环境,放弃了一些功能,但是保证了简单性,这使得它们最终在竞争中胜出,成为主流操作系统和编程语言。

术语解释

  • 抽象 [abstract]: 隐藏细节。编程语言越抽象,你写出程序所需的运算步骤就越少,每一步的功能就越强。
  • 算法 [algorithm]: 完成任务的方法。
  • Blub 困境 [Blub Paradox]: 程序员的思想往往会受到自己正在使用的语言的束缚,不相信还存在更强大的语言。
  • 复杂性 [complexity]: 算法的“时间复杂性” [time complexity] 指的是,当输入的数据量不断增加时,计算机完成过这种算法所消耗的时间
  • 散列表 [hash table]: 一种类似数据库的数据结构,存储在里面的每一段数据都有一个对应的键,使用时只要按照键名就可以取出对应的数据。
  • 函数库 [library]: 已经写好的代码片段,可以用来执行特定任务。
  • 宏 [macro]: 一个能够生成其他程序的程序。
  • 元循环 [metacircular]: 当一种语言的解释器用这种语言本身开发时,就会出现这种情况。与其说这是为了做出这种语言的一种实现,还不如说这是描述语言的一种技巧。
  • 方法 [method]: 面向对象编程中充当某个类的属性的一个子程序。
  • 模块 [module]: 一组子程序和变量,它们可以被视为是一个整体。通常情况下,模块外部的代码只能访问模块内部一部分专门对外公开的子程序和变量
  • 目标码 [object code]: 编译器产生的机器语言。
  • 面向对象 [OO: object-oriented]: 一种组织程序的方式。假定不同的类代表不同类型的数据,那么针对这些数据执行某种特定任务的代码,可以根据数据的不同被分别写进不同的类,成为这些类的方法。
  • 正交的 [orthogonal]: 彼此独立、能够以多种方式组合在一起的一组东西。(乐高积木)
  • 解析器 [parser]: 读取输入的数据然后生成解析树的程序。
  • 解析树 [parser tree]: 解析器读取源码后生成的数据结构。它是将源码翻译成机器语言的第一步。
  • 管道 [pipe]: 将操作系统的各种命令连接起来的一种方式,使得一个命令的输出变成另一个命令的输入。
  • 指针 [pointer]: 一块数据,它的值是另一块数据的内存地址。
  • 进程 [process]: 在同时运行多个程序的操作系统中,同时被运行的程序之一。
  • 质量保证 [QA: Quality Assurance]: 软件行业中负责找出和登记 bug 的人。
  • 递归 [recursive]: 一种调用自身的算法。