1. 三个改变信息世界的事件,为什么值得放在同一天讲
11 月 13 日这个日子,在技术圈里其实挺有意思。它不像某些被反复纪念的“大日子”那样自带光环,但如果你把这一天发生的几件事摆在一起看,会发现一条很清晰的线索:人类处理、传播和获取信息的方式,在这条时间线上被三次不同的力量推动着往前走。一次是底层计算语言的奠基,一次是信息共享方式的彻底重构,一次是商业信息入口的平民化。这三件事分别对应了“怎么描述计算”“怎么连接信息”“怎么让普通人用上信息”三个层面,凑巧都落在同一天,这种巧合本身就值得拿出来聊一聊。
我做技术内容这些年,越来越觉得孤立地看一个技术事件意义不大。比如你单独看“第一个网页上线”,它就是一个历史节点;但如果你把它和 Lisp 的诞生、和电商平台的崛起放在一起,就能看出一个更完整的图景——信息技术的演进从来不是单点突破,而是语言、协议、应用三层交替发力。底层语言决定了我们能表达什么,协议决定了我们能连接什么,应用决定了普通人能感知到什么。这三层缺一层,整个链条都转不起来。
这篇文章我打算按这个思路来拆。不是简单复述“某年某月某日发生了什么”,而是把每个事件背后的技术逻辑、它解决了什么当时解决不了的问题、以及它对后来做技术的人有什么实际启发,一层层剥开。适合谁看?如果你是刚入行的开发者,想理解自己每天用的东西是怎么来的;如果你是做产品或者做内容的,想搞清楚信息传播的底层规律;或者你只是对技术史有点好奇,想知道那些看起来遥远的事件跟自己有什么关系——那这篇应该都能给你一些能带走的东西。
我会尽量把每个技术点讲透,该补的背景补上,该算的参数算清楚,该说的坑也说清楚。毕竟历史事件本身是固定的,但怎么理解它、怎么把它跟当下的实践连起来,这才是对做技术的人真正有用的部分。
2. 万维网第一个网页:信息共享方式的分水岭
2.1 那个网页到底长什么样,为什么它重要
1990 年 11 月 13 日,一个后来被无数次引用的网页被上传到了当时还非常小众的网络空间里。这个网页的内容其实非常朴素,没有图片、没有样式、没有交互,就是一段说明文字加几个链接,解释这个“万维网”项目是干什么的、怎么用、去哪里找更多信息。如果你今天用浏览器的开发者工具去看类似结构的页面,会发现它的 HTML 简单到几乎不需要解析——就是最基础的标题、段落和超链接标签。
但它的重要性恰恰不在于页面本身有多复杂,而在于它验证了一个关键假设:文档之间可以通过超链接互相引用,形成一个可无限扩展的信息网络。这个假设今天听起来像废话,但在当时,信息共享的主流方式还是文件传输和邮件列表,你要找一份资料,得知道它在哪台服务器上、用什么路径、什么格式。超链接把“找信息”这件事从“知道地址”变成了“跟着链接走”,这是信息组织方式的一次根本性转变。
我经常拿一个生活化的类比来解释这件事:以前的文件系统像是一个巨大的仓库,每个箱子都有编号,你得先查目录才知道去哪个架子找哪个箱子。而超链接像是在每个箱子里放了一张纸条,写着“相关内容在某某架子上”,你打开一个箱子就能顺着线索找到下一个。仓库还是那个仓库,但找东西的体验完全不一样了。
2.2 从“文件传输”到“超文本”的关键跨越
要理解这个跨越有多大,得先看看在它之前人们是怎么共享信息的。当时主流的几种方式各有各的麻烦:文件传输协议要求你知道确切的服务器地址和文件路径,邮件列表是把内容推给一群人但没法结构化检索,早期的公告板系统虽然能讨论但内容组织是线性的。这些方式的共同问题是,信息之间的关联关系没有被显式地表达出来。
超文本的核心思想其实不复杂:把“引用”这件事变成一等公民。你在写文档的时候,可以随时标记“这里引用了另一份文档”,读者读到这个地方就可以直接跳过去。这个机制在纸面上很难实现,但在计算机上天然适合。问题在于,要让它真正跑起来,需要一套完整的约定:怎么标记引用、怎么定位被引用的资源、怎么在网络上传输这些文档。这套约定后来演变成了 URL、HTTP 和 HTML 三件套,也就是我们今天说的 Web 基础协议栈。
这里有个细节值得注意:第一个网页上线的时候,这套协议栈其实还非常粗糙。URL 的格式还没有完全标准化,HTTP 的方法只有最简单的 GET,HTML 的标签集也很有限。但它已经足够跑通一个完整的闭环:你输入一个地址,客户端向服务器发请求,服务器返回一个文档,文档里包含指向其他文档的链接,你点一下就能继续跳。这个闭环一旦跑通,剩下的就是不断迭代和扩展的问题了。
2.3 对后来开发者的实际影响
这件事对后来做技术的人影响有多深?我举一个很具体的例子。今天任何一个后端开发者,在写接口的时候都会下意识地考虑“资源怎么命名”“状态怎么表达”“版本怎么管理”,这些思维习惯的源头就是 Web 的那套设计哲学。RESTful 架构风格之所以能被广泛接受,就是因为它把“资源”和“链接”的概念从 Web 页面扩展到了 API 设计上。你设计一个用户接口,/users/123这个路径本身就隐含了“这是一个资源”“123 是它的标识”“你可以用标准方法操作它”这些约定,而这些约定的思想根源可以追溯到第一个网页所验证的那套模型。
另一个影响是“渐进增强”的思路。第一个网页没有样式、没有脚本,但它的核心功能——展示信息和提供导航——是完整的。后来加上的 CSS 和 JavaScript 都是在不破坏核心功能的前提下做增强。这个思路在今天做前端开发时依然适用:先保证内容可访问、可导航,再考虑交互和视觉效果。我见过不少项目反过来做,先堆了一堆交互效果,结果基础的内容结构一团糟,维护起来非常痛苦。
注意:理解 Web 的早期设计,重点不是记住具体日期,而是理解“超链接作为一等公民”这个设计决策带来的连锁反应。你在设计任何信息系统的时侯,都可以问自己一个问题:我的内容之间的关联关系,是显式表达出来的,还是需要用户自己去猜的?
3. Lisp 语言先驱:为什么这门“老语言”至今还在影响你写的代码
3.1 出生在 11 月 13 日的这位先驱,到底贡献了什么
同一天出生的这位 Lisp 语言先驱,在计算机科学史上的地位其实被严重低估了。大多数人知道 Lisp 是一门“很老但很厉害”的语言,但具体厉害在哪、跟自己的日常工作有什么关系,往往说不太清楚。我试着用最直白的方式讲:Lisp 是第一个把“代码本身当成数据来处理”的编程语言,这个想法后来渗透到了几乎所有现代语言的方方面面。
什么叫“代码当成数据”?举个例子。在大多数语言里,你写一个加法表达式a + b,编译器或者解释器会把它解析成一棵语法树,然后执行。但在 Lisp 里,这个表达式本身就是一个列表(+ a b),你可以像操作普通数据一样操作它——可以把它传给一个函数,可以让函数返回一个新的列表作为新的代码来执行。这个能力叫做“宏”,它让程序员可以在语言层面定义新的语法结构,而不需要等语言的设计者来加特性。
这位先驱在早期的论文和实现里,把递归、符号计算、自动内存管理这些概念系统地引入到了编程语言设计中。递归今天看起来是基本功,但在当时,大多数语言连函数调用都还很原始,更别说让函数直接调用自己了。符号计算则是另一条线,它让计算机不仅能算数字,还能操作符号表达式,这直接催生了后来的计算机代数系统、定理证明器,以及人工智能早期的很多工作。
3.2 那些看起来“新”的特性,其实几十年前就有了
我经常跟年轻开发者聊天,发现大家对“新特性”有一种天然的追捧。比如某个语言加了 lambda 表达式,大家觉得很酷;某个框架引入了不可变数据结构,大家觉得是创新。但如果你了解 Lisp 的历史,就会发现这些东西在几十年前就已经被系统性地研究和实现了。
垃圾回收机制是 Lisp 最早引入的实用特性之一。当时的内存管理需要程序员手动分配和释放,一不小心就内存泄漏或者野指针。Lisp 的实现者想了一个办法:让运行时自动追踪哪些内存还在被引用,不再被引用的就回收掉。这个思路今天已经是所有主流语言的标配,但它的起点就在 Lisp 这一脉。
再比如,条件表达式、递归函数、动态类型、交互式开发环境,这些今天看起来理所当然的东西,在 Lisp 的早期实现里就已经成型了。甚至“REPL”这个交互模式——你输入一个表达式,它立刻求值并返回结果——也是 Lisp 社区最早普及开来的。今天你在 Python 的交互式解释器里敲一行代码看结果,在浏览器控制台里调试 JavaScript,本质上都是在用 Lisp 开创的这种开发方式。
3.3 从 Lisp 思想到现代工程实践
那这些几十年前的思想,对今天的工程实践有什么具体影响?我举几个我实际工作中遇到的场景。
第一个场景是配置文件的处理。很多系统需要一套灵活的配置语言,能表达条件逻辑、能引用其他配置项、能组合和覆盖。如果你用 JSON 或者 YAML,表达能力强一点的需求就会很别扭。但如果你借鉴 Lisp 的 S 表达式思路,把配置也当成代码来求值,灵活性就完全不一样了。一些构建工具和基础设施管理工具就是这么做的,它们的配置文件本质上就是一小段程序。
第二个场景是 DSL(领域特定语言)的设计。当你的业务逻辑复杂到一定程度,用通用语言写会变得非常冗长,这时候就需要设计一套专门的表达方式。Lisp 的宏系统给了一种思路:不需要自己写解析器,直接在宿主语言里定义新的语法形式,让编译器帮你展开成底层代码。这种“语言内嵌 DSL”的做法,在很多需要高度定制化表达的场景里非常有效。
第三个场景是数据处理管道。Lisp 的列表操作和函数组合风格,影响了后来很多数据处理库的设计。你看到map、filter、reduce这些操作,看到链式调用的风格,看到惰性求值的概念,这些都能追溯到 Lisp 那一脉。今天做数据清洗、日志分析、ETL 流程的时候,这种风格依然是最自然的表达方式之一。
提示:如果你只从 Lisp 带走一个东西,我建议是“代码即数据”这个视角。下次你在设计接口或者配置格式的时候,想一想:用户能不能用跟数据同样的方式来表达逻辑?如果能,系统的灵活性和可扩展性会好很多。
4. 电商平台上线:信息入口平民化的商业样本
4.1 从“找书”这个需求切入的商业逻辑
同一天上线的那个电商平台,最早是从卖书这个品类切入的。这个选择在当时看很聪明,现在回头看更有意思。书是一种非常标准化的商品:有明确的标识(ISBN)、有相对固定的分类、有可比较的价格、不需要试用和体验。这些特性让它天然适合在线上销售——用户不需要摸到实物就能做决策,商家也不需要处理复杂的库存和物流问题。
但更深层的逻辑是,它把“信息入口”这件事从线下搬到了线上。以前你想找一本书,要么去书店碰运气,要么去图书馆查目录,信息是分散的、不完整的。这个平台把大量书籍的元信息集中起来,提供了搜索、分类、比价、评价这些功能,用户第一次可以在一个地方完成“发现-比较-决策”的全过程。这个模式后来被复制到了几乎所有品类,但书这个起点选得非常准。
我之所以把这个事件和前面两个放在一起讲,是因为它们共享一个核心主题:降低信息获取的门槛。第一个网页降低了文档获取的门槛,Lisp 降低了表达计算逻辑的门槛,而这个平台降低了商品信息获取的门槛。三个事件发生在不同层面,但方向是一致的。
4.2 早期电商系统的技术挑战
从技术角度看,早期电商系统面临的挑战跟今天很不一样。当时没有成熟的云服务,没有现成的分布式数据库,没有容器编排,很多东西都得自己搭。我根据常见的工程实践还原一下当时可能的技术选型:前端大概率是服务端渲染的 HTML 页面,用模板引擎拼出来;后端可能是某种应用服务器加关系型数据库;搜索功能可能一开始就是数据库的 LIKE 查询,后来数据量大了才换成专门的搜索引擎。
这里有个关键的技术决策点:商品信息的存储和检索怎么设计。书这种商品,属性相对固定——书名、作者、出版社、ISBN、价格、库存。用关系型数据库的一张表就能存下来,加几个索引就能支持基本的查询。但问题是,用户的搜索行为很复杂:可能按书名搜、可能按作者搜、可能按分类浏览、可能组合多个条件。如果全部用数据库查询来实现,性能很快就会成为瓶颈。
常见的做法是引入一个专门的搜索层,把商品数据同步过去,用倒排索引来加速查询。这个同步过程需要考虑数据一致性、更新延迟、失败重试这些问题。我见过不少项目在这个环节踩坑:同步逻辑写得不够健壮,导致搜索结果和实际库存不一致,用户下单了才发现没货。这类问题的根源往往不是技术选型不对,而是对“数据一致性”这个需求的优先级判断不清楚——在电商场景里,库存的准确性比搜索的实时性更重要,所以同步策略应该优先保证最终一致,而不是追求强一致。
4.3 对后来做产品和做技术的人的启示
这个平台的发展路径给后来做产品的人一个很重要的启示:从一个足够窄但足够标准的品类切入,把信息结构和交易流程跑通,再逐步扩展。很多创业项目一上来就想做全品类,结果每个品类的信息模型都不一样,供应链逻辑也不一样,最后什么都做不深。反而是从一个标准品类开始,把搜索、比价、评价、下单、支付、履约这些环节一个个打磨好,再复制到相邻品类,成功率更高。
对做技术的人来说,这个案例的价值在于它展示了“信息架构”的重要性。同样是卖东西,信息怎么组织、怎么呈现、怎么让用户快速找到想要的,这些问题的答案直接决定了系统的设计。你今天做任何有搜索和筛选功能的系统,都可以参考这个思路:先想清楚用户会从哪些维度来找信息,再设计数据模型和索引结构,最后才是界面和交互。顺序反了,后面改起来会很痛苦。
5. 三个事件串起来看:信息技术的三层演进逻辑
5.1 语言层、协议层、应用层的分工与配合
把这三个事件放在一个框架里看,会发现它们分别对应了信息技术的三个层面。Lisp 代表的是语言层,它解决的是“怎么描述计算逻辑”的问题。万维网代表的是协议层,它解决的是“怎么连接和传输信息”的问题。电商平台代表的是应用层,它解决的是“怎么让普通人用上信息”的问题。
这三个层面的关系不是简单的上下堆叠,而是互相依赖、互相促进的。语言层的进步让协议层的实现成为可能——没有合适的编程语言和抽象机制,很难写出复杂的网络协议栈。协议层的成熟让应用层的创新有了基础设施——没有统一的 Web 协议,电商平台就得自己解决信息发布和访问的问题,成本会高很多。应用层的需求又会反过来推动协议层和语言层的发展——电商的大规模并发需求推动了分布式系统和性能优化技术的进步。
我之所以强调这个框架,是因为很多做技术的人容易只盯着自己那一层看。做前端的可能不太关心协议设计,做后端的可能不太关心语言理论,做产品的可能不太关心技术实现。但如果你能把三层串起来看,很多决策会变得更清晰。比如你在设计一个 API 的时候,既要考虑协议层的通用性(用标准的 HTTP 方法和状态码),也要考虑应用层的易用性(字段命名和错误信息要让调用方看得懂),还要考虑语言层的表达力(能不能用类型系统把约束表达出来)。
5.2 每个层面的关键设计决策及其长期影响
具体到每个层面,有几个关键的设计决策值得拿出来说。
语言层最关键的决定是“代码即数据”。这个决定让 Lisp 获得了无与伦比的表达能力,但也带来了一些代价——比如性能开销、学习曲线陡峭、生态相对小众。后来很多语言在借鉴这个思想的时候做了折中:保留宏系统但限制它的使用范围,或者用更安全的元编程机制来替代。你在选择技术栈的时候,其实也在做类似的权衡:表达力和安全性、灵活性和可维护性,往往需要根据具体场景来平衡。
协议层最关键的决定是“无状态”和“可缓存”。HTTP 被设计成无状态的,每个请求都携带足够的信息让服务器独立处理,不依赖之前的请求。这个决定让 Web 服务器可以水平扩展,也让缓存成为可能。但代价是每次请求都要传输重复的信息,对于复杂交互来说效率不高。后来出现的各种会话机制、Token 方案,本质上都是在无状态的基础上模拟有状态的行为。理解这个底层约束,你在设计接口和排查性能问题的时候会更有方向。
应用层最关键的决定是“以用户为中心的信息组织”。电商平台把商品信息结构化、把搜索和筛选做成核心功能、把用户评价纳入决策链路,这些做法今天看起来理所当然,但在当时是需要勇气的。因为这意味着要投入大量资源在信息架构和数据处理上,而不是简单地做一个展示页面。这个思路后来被几乎所有互联网产品继承,成为产品设计的基本功。
5.3 对当下技术选型和职业发展的参考价值
那这套三层框架对今天做技术的人有什么实际参考价值?我觉得至少有两个方面。
第一个方面是技术选型。当你在评估一个新技术的时候,可以先判断它属于哪一层,然后看它在这一层里解决了什么问题、做了什么权衡。比如一个新的前端框架,它主要在应用层工作,那你就看它在信息组织、状态管理、渲染效率上做了什么改进。一个新的数据库,它可能在协议层和存储层都有涉及,那你就看它在一致性、扩展性、查询表达力上怎么取舍。有了这个框架,你就不容易被营销话术带偏,能更清楚地看到技术的本质。
第二个方面是职业发展。技术人的成长路径,往往是从某一层的具体工作开始,逐渐理解上下层的关系,最后能在多层之间做协调和决策。刚开始你可能只写业务代码,属于应用层;后来你开始设计接口和数据结构,涉及协议层;再后来你参与架构设计和技术选型,需要理解语言层和基础设施层的约束。这个过程中,能不能跳出自己那一层去看问题,是区分普通开发者和资深工程师的一个重要标志。
6. 实操复盘:怎么把技术史变成自己的知识资产
6.1 建立自己的技术史时间线
我自己的习惯是维护一个简单的技术史时间线,不是那种大而全的编年史,而是只记录跟我的工作相关的关键节点。比如 Web 协议的几次重要修订、主流编程语言的诞生和重大版本、数据库和分布式系统的重要论文、前端框架的演进脉络。每个节点我只记三件事:它解决了什么问题、它的核心设计决策是什么、它对后来的影响是什么。
这个习惯的好处是,当我在工作中遇到一个设计问题的时候,可以快速定位到历史上有没有类似的场景、别人是怎么解决的、为什么那样解决。比如我在设计一个配置系统的时候,会想到 Lisp 的宏和 S 表达式;在设计一个 API 的时候,会想到 HTTP 的无状态约束和 REST 的演进;在设计一个搜索功能的时候,会想到早期电商平台的信息架构。这些历史案例不一定能直接套用,但它们提供了思考的起点和权衡的参考。
6.2 用“问题-方案-权衡”的框架来读技术史
读技术史最容易犯的错误是把它当成故事会,记住了一堆日期和人名,但不知道跟自己有什么关系。我的建议是用“问题-方案-权衡”这个框架来读。每个技术事件,先问它面对的是什么问题,再看它提出了什么方案,最后分析这个方案做了什么权衡、放弃了什么、换来了什么。
用这个框架看第一个网页:问题是信息分散、关联关系不显式;方案是超链接加统一协议;权衡是简单性优先,牺牲了富交互和复杂排版的能力,但这些后来通过 CSS 和 JavaScript 补上了。看 Lisp:问题是当时语言表达力不足、内存管理容易出错;方案是代码即数据加自动垃圾回收;权衡是运行效率和生态规模,但换来了极强的表达力和元编程能力。看电商平台:问题是商品信息分散、用户决策成本高;方案是集中化信息加搜索筛选;权衡是初期品类有限,但换来了标准化和可扩展性。
这个框架用熟了之后,你看任何新技术都会自动做这个分析,不会被表面的热闹迷惑。
6.3 把历史洞察转化为日常开发中的判断力
最后说一个我自己的体会。技术史读多了,最大的收获不是知道了多少冷知识,而是形成了一种“历史感”——遇到新问题的时候,能感觉到它在历史脉络中的位置,能判断哪些方案是经过时间检验的、哪些只是一时的潮流。
比如“代码即数据”这个思想,经过几十年的检验,在配置管理、DSL 设计、元编程这些场景里依然是有效的。但它在性能敏感的场景里就不太适用,因为抽象层多了会有开销。再比如“无状态协议”这个设计,在需要高扩展性的场景里是首选,但在需要复杂会话管理的场景里就需要额外的工作来弥补。这些判断不是非黑即白的,而是需要根据具体场景来权衡。
我个人的经验是,每当你做一个技术决策的时候,花五分钟想一想:这个问题在历史上有没有人遇到过?他们是怎么解决的?为什么那样解决?这个思考过程不需要很长,但能帮你避开很多明显的坑,也能让你的方案更有依据。技术史不是拿来炫耀的知识,而是拿来用的工具。你用得越多,它就越锋利。