返回
programming2026年8月21日1 分钟

HN评论:被低估的宝藏与争议

#Hacker News#技术评论#编程实践#软件工程#行业观察

1. HN评论:被低估的宝藏

HN的评论被低估了。HN的评论很糟糕。在我熟悉的任何话题上,绝大多数评论显然是错误的。大多数时候,没有任何懂行的人发表评论,而置顶评论听起来合理但完全错误。此外,许多评论毫无必要地刻薄。你经常会听到刻薄的评论,并伴有类似“这比另一种可能性更好”的论调,然后每个人都互相拍背,评论如“这太棒了”,仿佛刻薄是某种护身符,可以避免空洞的套话。我见过有人对此反驳;当被追问时,人们常说,不刻薄就无法教人,或者教人效率低下,仿佛告诉别人他们很蠢就能帮助他们学习。就好像人们通过观看西蒙·考威尔学会了如何解释事情,而无法理解不夹杂人身攻击的解释概念。保罗·格雷厄姆曾说过:“哦,你永远不应该读关于你写的任何东西的HN评论。”你听到的关于HN评论的负面评价大多是真的。然而,我还没有找到一个公共互联网论坛,其技术评论比HN更好。在我熟悉的话题上,虽然一个帖子中很少能有一条消息灵通的评论,但当这些评论出现时,它们通常会浮到顶部。在其他论坛上,消息灵通的评论要么不存在,要么出现时被听起来合理但完全错误的评论淹没,而且它们出现的频率比HN更低。按数量计算,评论中可能比链接中有更多有趣的技术“帖子”。嗯,这取决于你觉得什么有趣,但就我的兴趣而言,这是真的。如果我看到nkurz关于底层优化的评论,patio11关于商业的评论,nostrademons关于公司运营的评论,我几乎肯定我会读到一条有趣的评论。我能想到大约20到30个人,他们不怎么写博客,但在HN上写了很多精彩的评论,我怀疑我甚至不知道在HN上写精彩评论的人中有一半。我整理了一份非常简短的评论列表,因为评论似乎会丢失。如果你写一篇博客文章,人们几年后还会引用它,但评论大多消失了。我觉得这很可悲——HN上有很多很棒的材料(是的,甚至更多不那么棒的材料)。

2. MS Word文件格式的真相

基本上,Word文件格式是内存的二进制转储。我没开玩笑。他们只是把内存中的任何内容写到磁盘上。我们可以尝试推理为什么(也许更快,也许让代码更小),但我认为最主要的原因是原始开发者不知道更好的做法。后来,当他们试图添加功能时,不得不努力使其向后兼容。这就是很多复杂性所在。有很多疯狂的工作区,如果你允许自己重新设计文件格式,这些工作区本来很简单。很明显,这是管理层强制要求的,因为没有软件开发者会无缘无故地让自己陷入那种地狱。后来他们添加了快速保存功能(我忘了它实际叫什么)。这会将更改附加到文件中,而不改变原始文件。他们实现这个功能的方式真的很巧妙,但大大复杂化了文件结构。我觉得必须指出的一点是(我记得这篇文章最初发布时,我在Slashdot上发了一个大帖子),对于文字处理器来说,双向文件转换几乎是不可能的。这是因为文件格式不包含足够的信息来格式化文档。最明显的地方是分页。文件格式没有说明在哪里对文本流进行分页(除非用户明确输入)。它依赖格式化程序来做这件事。每个文字处理器格式化文本的方式完全不同。例如,Word以错误地分页脚注而闻名。但他们不能改变这一点,因为这会破坏向后兼容性。这是Word Perfect至今仍存在的唯一原因之一——它是唯一能按照美国司法部要求的方式对法律文件进行分页的文字处理器。仅考虑分页问题,你就能看到问题所在。读取Word文档时,你必须像Word那样分页——但文件格式并没有告诉你那是什么。然后,如果有人修改了文档,你需要重新保存,你需要以某种方式标记它应该像Word那样分页(即使它现在可能有Word中没有的功能)。如果只是分页,你也许能做到,但实际上几乎所有事情都是这样。我建议有兴趣的人阅读(一点)XML Word文件格式。你会看到大量像“像Word 95那样格式化”这样的标志。格式没有说明那是什么——因为很明显,文件格式的作者也不知道。它迷失在遗留代码的绝望混乱中,现在没人能弄清楚它的作用。

3. NULL的乐趣

这是这个优秀特性的另一个例子:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
#define LENGTH 128
 
int main(int argc, char **argv) {
    char *string = NULL;
    int length = 0;
 
    if (argc > 1) {
        string = argv[1];
        length = strlen(string);
        if (length >= LENGTH) exit(1);
    }
 
    char buffer[LENGTH];
    memcpy(buffer, string, length);
    buffer[length] = 0;
 
    if (string == NULL) {
        printf("String is null, so cancel the launch.\n");
    } else {
        printf("String is not null, so launch the missiles!\n");
    }
 
    printf("string: %s\n", string); // undefined for null but works in practice
 
#if SEGFAULT_ON_NULL
    printf("%s\n", string); // segfaults on null when bare %s\n
#endif
 
    return 0;
}
nate@skylake:~/src$ clang-3.8 -Wall -O3 null_check.c -o null_check
nate@skylake:~/src$ ./null_check
String is null, so cancel the launch.
string: (null)
nate@skylake:~/src$ icc-17 -Wall -O3 null_check.c -o null_check
nate@skylake:~/src$ ./null_check
String is null, so cancel the launch.
string: (null)
nate@skylake:~/src$ gcc-5 -Wall -O3 null_check.c -o null_check
nate@skylake:~/src$ ./null_check
String is not null, so launch the missiles!
string: (null)

看起来Intel的ICC和Clang还没有赶上GCC的优化。如果你依赖那个优化来获得所需的性能,那就糟糕了!但在过多指责GCC之前,考虑一下,当string为NULL时,这三个编译器在printf("string: "); printf("%s\n", string)上都会段错误,尽管对printf("string: %s\n", string)作为单个语句没有问题。你能看出为什么使用两个单独的语句会导致段错误吗?如果不能,请看这里的提示:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25609

4. 确保自动驾驶备份保持警惕

好的工程尽可能消除用户做错事的可能性。……你不会设计一个邀请误用的功能,然后用说明来试图防止误用。澳大利亚有一次脱轨事故,叫做Waterfall脱轨[1]。它发生是因为司机心脏病发作,导致7人死亡(老实说,这么低是奇迹)。根本原因是死人开关的失效。在Waterfall的情况下,司机有两个死人开关可以使用——1)油门手柄必须克服弹簧保持在一个小角度,或2)地板上的一个杆可以被压下。你必须做其中一件事,这样做的想法是通过允许司机在两者之间交替来防止手腕或脚部抽筋。不这样做会触发紧急制动。事实证明,这位司机足够胖,当他心脏病发作时,他的腿能够压下踏板足够多,以保持紧急系统关闭。因此,死人系统从未触发,尽管驾驶座上有很多死人。我不太记得Waterfall系统的具体细节,但对抗这一点的一种方法是要求踏板保持在释放和完全压下之间的中间位置。这样做的想法是,一条死腿会完全压下踏板,从而触发制动,而完全释放的踏板也会触发制动。我不知道他们是否有那个系统,但肯定那是铁路中使用的一种方法。无论如何,这个问题在汽车中同样可能发生。如果你失去意识,脚变得无力,一条足够重的腿将能够根据它相对于踏板的位置以及它在地板上的杠杆作用,将踏板压下一段距离。我熟悉的另一个确保司机在驾驶时保持清醒的主要系统叫做“警觉”。它的工作方式是,定期地,仪表板上开始闪烁灯光,司机必须确认。如果他们不确认,蜂鸣器警报开始响起。如果他们仍然不确认,列车制动器启动,司机被认为无法操作。让我告诉你一些我参与其中的故事。当我们刚开始时,我们有一个简单的警觉系统。例如,每30秒左右,司机会按一个按钮。好吧,酷。但后来司机变得如此习惯于每30秒按一次按钮,以至于我们遇到司机睡着/打瞌睡但仍然每30秒按一次按钮的情况,因为这已经深深印在他们身上,这实际上是一种潜意识行为。所以我们引入了随机时间警觉,时间在30-60秒之间变化(例如),你只能在灯开始闪烁后的一小段时间内确认。再次,司机开始睡着/半睡,并且每次都会在警报响起时立即按下。所以我们引入了随机时间、任务关联的警觉,这最终解决了问题。现在,司机必须按一个按钮,或转动一个旋钮,或做许多不同的活动,他们必须在随机选择的时间做随机选择的活动,以确认他们的意识。只有在那个时候,我们才终于解决了司机警觉性问题。另见。

5. 声望与FedEx选址

声望。好奇他为什么需要搬到更有声望的职位?大多数人在30多岁时意识到声望是傻瓜的游戏;这是一种通过用他们并不真正关心的人的赞誉来诱导人们做不太有趣且他们自己不想做的事情的方式。

为什么FedEx总部在孟菲斯?……我们注意到我们还需要:(1)枢纽位置有一个合适的、现有的机场。(2)枢纽位置天气好,例如,相对较少的雪、雾或雨。(3)良好的停机坪空间,即在哪里停放和维修飞机以及分拣包裹。(4)良好的劳动力供应,例如,用于分拣中心。(5)相对较低的生活成本以保持价格低廉。(6)友好的监管环境。(7)候选机场不要太忙,例如,不希望到达的飞机在降落前长时间盘旋。(8)机场侧风相对较少,并且有不止一条跑道以防风。(9)跑道海拔不太高,例如,不足以限制最大总起飞重量,例如,排除丹佛。(10)跑道末端附近没有高大的障碍物,例如,山脉。(11)良好的喷气燃料供应。(12)良好的18轮卡车道路通道,用于卡车和飞机之间的包裹交换,例如,这样一些部分可以卡车运到枢纽并存储在那里,并通过飞机直接运送到下订单的客户,比如,晚上11点前下单,早上10点前送达。所以,大约有三个候选地点,孟菲斯,以及我记得的辛辛那提和堪萨斯城。孟菲斯机场在跑道旁边有一些二战时期的旧机库,FedEx可以用作分拣中心、飞机维修和总部办公空间。交易完成——就是孟菲斯了。

6. Etherpad与Wave的合并及其教训

出售给Google的决定是我和联合创始人在生活中不得不面对的最艰难的决定之一。我们对Wave的愿景感到兴奋,尽管我们看到了产品的缺陷。Wave团队告诉我们他们如何希望我们帮助使Wave更简单、更像Etherpad,我们认为我们可以帮助实现这一点,尽管最终我们未能使Wave更简单。我们害怕Google作为竞争对手:他们有更多的工程师和更多的资金支持这个项目,但他们运行它更像一个独立的初创公司,而不是一个普通的大公司部门。Wave办公室在澳大利亚,几乎完全自主。最后,在AppJet濒临失败1.5年后,能够宣布我们的努力成功并为所有冒险投资我们的投资者提供体面的回报是很诱人的。最终,我们加入Wave的决定并没有像我们希望的那样成功。最大的教训是,一个项目背后有更多的工程师和资金实际上可能弊大于利,所以我们因为害怕Wave作为竞争对手是错误的。事后看来这很明显,但当时并非如此。第二,我完全低估了在Wave代码库上迭代的难度。我习惯于在一个通宵中重写软件的主要部分。由于Wave使用的软件开发过程,实际上不可能在产品上迭代。我应该对他们的具体软件工程过程做更多的尽职调查,但我假设因为他们似乎像初创公司一样运作,他们就能像初创公司一样迭代。很多产品问题整个Wave团队都知道,但我们被一个基于糟糕技术选择构建的大型复杂代码库和繁琐的工程过程所束缚,这阻止了快速迭代。

7. 科技新闻的准确性

当我对一个后来在科技媒体上爆出的故事有内部信息时,我总是震惊于文章读者对它的感知与我经历的有多么不同。在我参与的初创公司重大功能发布中,我见过:高管们直截了当地说他们不在一个产品类别上工作,而实际上有一个整个部门已经致力于该类别一年了;初创公司成立时间比Crunchbase/维基百科上列出的日期早1.5年;记者数他们在一次访问中遇到的人数,并将其报告为“团队规模”,因为公司拒绝发布该信息;从未出现在媒体上的融资轮次;被报道为“未披露金额”的收购,但实际上比创始人如果在该公司拿薪水工作赚得还少;项目开始日期实际上是项目人员配备到当前规模的时间,而忽略了一个小团队花了一年左右时间解决问题(或其他小团队花了3-4年时间解决问题);以及被广泛报道为公司成功核心的算法或其他技术,但实际上公司甚至不使用它们。

8. 戴尔自毁音箱与更多案例

戴尔的自毁音箱。作为VLC的主要开发者,我们很久以前就知道这个故事,这只是戴尔把垃圾组件放在他们的机器上并责怪别人。与他们进行任何讨论都是不可能的。所以让我解释一下……在这种情况下,VLC只是使用Windows API(DirectSound),并向Windows内核发送16位有符号整数(s16)。VLC允许放大解码音频之上的输入。这就像回放增益、损坏的编解码器、录制不佳的文件或后置放大,并可能导致饱和。但如果你把mp3文件通过Audacity增加音量并用WMP播放,或者在你的编解码器输出后放置一个放大音量的DirectShow过滤器,这完全相同。例如,很长一段时间,VLC的ac3和mp3编解码器与参考输出相比太低(-6dB)。最坏的情况,这会降低动态范围并大量饱和,但这不会破坏你的硬件。VLC不(也不能)修改输出音量来破坏音箱。VLC是使用官方平台API的软件。这里的问题是,戴尔声卡的输出功率(可以通过振幅的二次方来近似)是戴尔音箱无法处理的。简单地说,声卡最大输出10W,而音箱只能承受6W,他们的BIOS或驱动程序都没有阻止这一点。由于VLC存在于许多机器上,责怪VLC很简单。相关性并不意味着因果关系,这对廉价的戴尔支持来说似乎太复杂了……

在工作中学习,初创公司与大公司。为别人的初创公司工作,我学会了如何快速拼凑解决方案。我学会了不确定性,以及无论你是否确定它会成功都要选择方向。我了解到大多数初创公司都会失败,当它们失败时,最终表现好的人是那些一直关注自己利益的人。我学到了很多基本的技术技能,如何快速编写代码,快速学习新API,以及将软件部署到多台机器上。我了解到扩展开发团队的问题出现得有多快,以及你应该多早开始投资自动化。在Google工作,我学会了如何一劳永逸地解决问题,并将这种文化融入组织。我了解到,即使在成功的公司,一切都是暂时的,伟大的产品通常是通过许多人的大量努力工作而不是伟大的顿悟洞察力构建的。我学会了如何为规模设计系统架构,以及许多用于健壮、高可用、频繁部署系统的实践。我学到了研究的价值,以及在一个重要问题上花大量时间的价值:许多初创公司采取散弹枪式方法,一个周末黑客马拉松接一个,发现没有人想要任何一个,而通常有一些机会没有人解决,因为没有人愿意投入工作。我学会了如何在团队中工作,并尝试理解别人想要什么。我了解到什么问题对大组织来说真的很痛苦。我学会了如何严格研究市场,并使用数据来做出产品决策,而不是基于一个人认为最好的东西来做决定。

我们辜负了这个人,我们要做哪些不同的事情?参加公司领导层会议,离职只是用一个简单的“遗憾是/否”标志记录,我的经验是,没有一次离职有任何影响。大规模离职有影响,趋势有影响,但一个人从来没有,即使那个人是创始人。合理化总是把问题推回离职员工身上,“他们精疲力尽了”,“他们失去了效率”,“他们已经向前看了”,“他们只是没有随着公司成长”,从来不是“我们辜负了这个人,我们要做哪些不同的事情?”

AWS的起源故事。无论如何,当我在那里时,SOA工作正在全面展开。那很痛苦,而且一团糟,因为每个团队做事方式不同,每个API都不同,基于不同的假设,用不同的语言编写。但我想纠正一个误解,即这导致了AWS。不是的。S3是由它自己的团队从头开始编写的。当我在亚马逊时,在零售网站上工作,亚马逊网站没有任何部分运行在AWS上。我知道,当AWS宣布时,大张旗鼓,他们说“为亚马逊网站提供动力的服务现在可以为你的业务提供动力!”或类似的话。这是一个彻头彻尾的谎言。他们唯一共享的是数据中心和标准硬件配置。即使到我离开时,当AWS全速运行(可能已经在运行Reddit),亚马逊网站也没有任何部分运行在AWS上。


🔗 原文链接:https://danluu.com/hn-comments/