[转贴]人类基因是一套屎山代码吗?

我第一次了解基因组的时候,有种强烈的既视感——这不就是一个跑了 38 亿年、从来没重构过、还没有文档的遗留软件系统吗?

但深入看下去会发现,这个比喻既对又不对。对的地方在于,基因组里确实充满了让任何有代码洁癖的人抓狂的设计;不对的地方在于,它能这么”丑”地运行 38 亿年不崩溃,本身就是一种我们工程师造不出来的奇迹。

下面展开聊聊。

一、基因组”屎”在哪里

视网膜装反了。 脊椎动物的感光细胞朝向脑后而不是朝向光源,光线要先穿过血管和神经层才能到达感光细胞。更糟的是,所有神经纤维得从视网膜前方汇聚穿出眼球——这个出口就是我们的盲点。章鱼的眼睛就是正着装的,没有盲点。这是典型的早期架构决策锁死,后期改不动了。

DNA 复制有”最后一公里”问题。 线性染色体每次复制末端都会丢失一小段,因为 DNA 聚合酶的工作方式就是没法复制到最末端。解决方案是端粒——在末端加一堆无意义的重复序列当”牺牲品”。但端粒会随分裂越来越短,短到临界点细胞就衰老死亡。这基本上是个内存泄漏,用”预分配一大块缓冲区慢慢消耗”来规避,而不是真正修复。

假基因满地都是。 人类基因组里有约 2 万个假基因——曾经能工作的基因因为突变坏掉了,但 DNA 序列还留着,占用空间还要被复制。嗅觉受体基因里大约一半都是坏的。相当于代码库里一半函数标记着 // TODO: deprecated 但没人敢删。

内含子是强制插入的废话。 一个基因转录出 mRNA 后,要把内含子切掉才能翻译成蛋白质。问题是内含子常常比外显子长得多,有的基因 99% 都是要被切掉的部分。每次基因表达都要做一遍剪接,纯属额外开销。更糟的是剪接出错会导致疾病,某些地中海贫血就是这么来的。

产道和大脑打架。 直立行走要求骨盆窄,大脑演化要求头大。两个需求冲突的结果是人类分娩极度痛苦且危险——现代医学出现前,产妇死亡率长期在 1-2% 每胎。

阑尾、智齿、尾椎,全是遗留垃圾。 阑尾是盲肠退化的残留,平时没用发炎了还要命;智齿是颌骨缩小后塞不下的多余牙齿;尾椎是尾巴退化剩下的几块骨头。全都是”功能废弃但没清理干净”的反模式。

免疫系统偶尔攻击自己。 免疫系统靠随机生成抗体识别外敌,理论上要排除掉攻击自身的那些,但这个过滤机制不完美。红斑狼疮、类风湿、多发性硬化,都是免疫系统在打自己人。相当于防火墙规则写错把自己 IP 也 ban 了。

还有更多:基因组里 8% 的序列是古代病毒感染留下的残骸,被永久 commit 进去;一种叫 Alu 的重复片段占了基因组 10%,有 100 多万份拷贝;一个基因往往影响几十个性状,改一处崩一片……

二、为什么会这样

看完上面这些你可能会问:怎么会搞成这样?一个简单的答案:演化是一个没有目标、没有设计师、只能在运行中打补丁的贪心算法。

展开来看,有几层原因:

第一,演化只看当下,不做规划。 自然选择的逻辑是:谁现在能活下来多生孩子,谁的基因就留下来。它没法说”我先忍几代把视网膜翻过来,子孙受益”——任何中间过渡形态如果竞争不过同代对手,这条路就断了。一旦走上某条路径,哪怕后来发现是坑,也只能在坑里继续优化。

第二,没有”删除”操作。 软件工程可以 git revert、删文件、重构,演化不行。一段 DNA 如果不再有用,通常不会被主动清除,只会慢慢积累突变直到失效,但序列还在那儿占地方。删除需要理由,保留不需要——只要复制成本不至于让个体活不下去,就会一直留着。

第三,突变是随机的,而且绝大多数有害。 演化的”创新原材料”就是 DNA 复制错误这类随机事件。随机变动打在复杂系统上,绝大多数结果是让它变差。真正有益的突变极其罕见,所以演化大部分时间在做微调,很少能发生大规模架构重构。

第四,局部最优陷阱。 想象一个山地地形,演化是一个只能往高处走的盲人。它能爬到离出发点最近的山头,但看不到远处可能更高的山峰——因为去那个更高的山,必须先下山,而”下山”等于”适应度降低”,会被淘汰。喉返神经绕道就是这样,要改成直接路线,中间过渡形态的神经发育可能出错,那代动物活不下来,所以永远改不了。

第五,模块之间紧耦合。 基因不是独立的乐高块,一个基因常常参与多个功能,一个功能常常依赖多个基因。想优化 A 功能可能会意外破坏 B。骨盆和大脑的冲突就是如此——骨盆涉及行走、支撑内脏、分娩三个功能,任何一个方向的优化都会拖累另外两个。

第六,没有”测试环境”。 所有演化实验都在生产环境上直接跑,失败的代价是死亡。这意味着任何需要”先倒退再前进”的重构都无法进行。软件工程可以在分支上重写核心模块,测试通过后合并。演化只有 main 分支,而且每次 commit 都必须保证进程不 crash。

三、那如果让工程师重新设计,能搞出最优的基因组吗?

这是很多人会问的下一个问题。答案大概率是不能——而且这个问题本身藏着一个陷阱。

首先,“最优”对谁最优? 个体寿命最长?繁殖效率最高?对环境变化最鲁棒?能耗最低?演化潜力最大?这些目标互相冲突。追求长寿就要降低突变率,但这会牺牲演化速度,环境一变就灭绝。追求繁殖快就要简化结构,但简化的代价是鲁棒性下降。没有统一的”最优”定义,任何设计都是多目标妥协。

其次,设计最优需要预知未来。 那些看起来没用的”垃圾 DNA”里,藏着演化的原材料。假基因有时会被重新激活,重复序列给了重组变异的空间,内源性病毒偶尔被驯化成有用基因(哺乳动物胎盘里的 syncytin 蛋白就来自病毒)。

如果按”当下最优”原则把这些都删掉,基因组在当前环境下效率更高,但一旦环境剧变——气候变化、新病原体、辐射水平改变——这个过度精简的物种就没有应对余地了。冗余是演化的保险,精简掉就等于取消了进化能力。就像软件工程里的过度优化:把所有东西都内联、去掉所有抽象层,短期跑得快,但后续完全改不动。

第三,复杂系统本身就不可预测。 2010 年 Craig Venter 团队做合成生物学,试图设计”最小基因组”——剥掉所有非必需基因,造一个只保留核心功能的细菌。结果发现有近三分之一的”必需基因”他们根本不知道是干什么的,但一删除细菌就死。我们连一个细菌都没法完全理解,何况人类。

第四,几种看起来很美的”优化”其实都有隐藏代价:

•去掉二倍体的冗余?基因组缩小一半,但任何一个基因出错就没有备份。

•去除内含子?转录效率提高,但失去可变剪接能力,一个基因只能产出一种蛋白。

•统一密码子?信息密度更高,但抗突变能力归零——任何一个碱基错误都会改变氨基酸。

•去除重复序列?基因组缩小 50%,但失去染色体结构稳定性(着丝粒、端粒都依赖重复序列)。

每一个优化都有隐藏成本。

第五,工程师有个共同盲点:人类设计的系统容易在”设计时没想到的场景”下崩溃。飞机、核电站、操作系统都出过这类事故。演化出来的系统相反——常见场景下可能效率不高,但极度鲁棒,因为所有能活到现在的生物都被无数种极端情况筛选过。一个从头设计的基因组没有经过足够长的真实环境测试,大概率会在某些人类没想到的边缘情况下崩溃。

四、所以回到开头的问题

人类基因组是屎山代码吗?

从代码洁癖的角度看,是。从能跑通 38 亿年不崩溃的角度看,又不是。

更准确的说法可能是:人类基因组是一套没有文档、没有版本控制、靠 38 亿年线上热更新演化出来的遗留系统——屎,但是能跑,而且跑得还挺稳。

它的”丑”不是偶然,而是所有约束条件的必然产物:不能停机、不能回滚、不能预先设计、只能靠随机变异和残酷淘汰一步步爬出来。任何看起来冗余、绕路、不合理的地方,背后都可能藏着我们现在还看不到的约束。

这让我想起一个经典的软件工程悖论:看起来丑的老代码里,每一行丑陋背后都藏着一个血的教训。重写一遍,通常是把那些教训重新踩一遍。

基因组也是这样。你看它丑,是因为你只看到了代码,没看到它经历过的那几十亿年生产事故。​​​​​​​​​​​​​​​​