查看原文
其他

25,000,000 行的代码就问你敢不敢动?!

屠敏 整理 程序人生 2018-12-16

点击上方“程序人生”,选择“置顶公众号”

第一时间关注程序猿(媛)身边的故事

你经历过绝望吗?

近日,Hacker News 上发起了一个名为“你见过最糟糕的代码是什么?”(https://news.ycombinator.com/item?id=18442637)的话题,引发了无数网友回忆讨论,甚至还再次让软件巨头 Oracle 登上头条。

25,000,000 行的代码就问你敢不敢动?!

日前,我们还在说如今的 Oracle 惨遭亚马逊、Salesforce 弃用,究其根本原因,不是因为亚马逊等企业为了省钱,而是因为 Oracle 数据库逐渐满足不了他们业务的发展需求。

在 Oracle 内部,相比每隔六个月就更新一次的 Java,Oracle 数据库版本的更新频率可以用 2-3 年甚至更久来表示。就在上文所述的 Hacker News 话题中,来自 Oracle 的程序员为我们解释了其中的缘由,庞大的 Oracle 数据库并不像外人看得那么简单,修复 Bug 可以分分钟让人奔溃。

该程序员以 Oracle 数据库 12.2 版本为例,它拥有了近 2500 万行的 C 代码。

每次更新,你需要在不破坏现有测试 1000 次的情况下更改产品中的单行代码。就 Oracle 数据库产品而言,是好几代程序员在有限的期限内编写的这些代码,但与此同时,这些代码中也充斥着大量的垃圾代码。

非常复杂的逻辑、内存管理、上下文切换等都与数千个 flag 一起保存。整个代码都带有神秘的宏命令,如果没有使用笔记本而是手动扩展相关的宏,那么你就无法清楚地明白这些宏。甚至可能需要一天到两天才能真正理解某个宏的作用。

有时你需要了解 20 个不同 flag 的值和效果来预测代码在不同情况下的行为方式。有时多达数百个 flag!“我并不夸张。”该程序员表示道。

Oracle 这个产品仍然存活并且可以供企业和开发者使用的唯一原因是数百万次测试!

接下来,该程序员分享了 Oracle 数据库开发人员的日常:

- 开始处理一个新的 Bug。

- 花两周的时间试图了解 20 种不同的 flag,这些 flag 以神秘的方式相互作用,造成了这个困境。

- 再添加一个 flag 来处理新的特殊情况。添加几行代码来检查此 flag 并解决有问题的情况,避免该 Bug。

- 将更改提交到包含大约 100 到 200 台服务器的测试服务器集群,这些服务器将编译代码,构建新的 Oracle 数据库,并以分布式方式运行数百万个测试。

- 下班回家。第二天来上来,继续做其他事情。测试可能需要 20 小时到 30 小时才能完成。

- 一天结束,下班回家。再来上班时,检查前天的集成测试结果。如果幸运的话,将会大约有 100 个失败的测试。如果运气不好,将大约会有 1000 个失败的测试。随机选择一些测试并尝试了解你的假设出了什么问题。也许还有 10 多个 flag 要考虑才能真正理解 Bug 的本质。

- 添加一些 flag 来尝试解决问题。再次提交更改以进行测试。再等 20 到 30 个小时。

- 另外,重复以上步骤大概两周左右,直到你能得到将这些 flag 组合起来的“神秘咒语”(没有错误发生)。

- 终有一天,你会成功,带来测试失败为零的结果。

- 针对你新更改的部分添加 100 多个测试,以确保下一个不幸接触这段新代码的开发人员永远不会破坏你的修复程序。

- 完成最后一轮的测试提交工作。然后提交以供审核。审查本身可能还需要 2 周到 2 个月。所以现在继续讨论下一个 Bug。

- 在 2 周到 2 个月之后,当一切都完成后,代码将最终合并到主分支中。

以上是在 Oracle 修复 Bug 的程序员日常的非夸张描述。现在想象一下开发新功能会有多么恐怖。开发一个小功能需要 6 个月到一年(有时是两年!比如添加一种新的身份验证模式,比如支持 AD 身份验证),现在也可以理解为什么 Oracle 数据库的更新速度永远追不上 Java 了。

而对于这款产品可以商用也真的是一个奇迹。到了最后,这名程序员崩溃地说:我不再为 Oracle 工作了。永远不会再为 Oracle 工作了!

对于这一现状,更有不少网友表示了同情:

@nathan_f77:这绝对是疯了。 我甚至无法想象代码库的复杂性。我认为我的 Rails 测试套件已经很慢了,因为它需要 4 分钟。如果我用 C 或 C ++ 编写它可能是 10 秒。

我无法想象一个 C / C ++ 的应用程序,其中测试套件在具有 100-200 台服务器上需要 20-30 小时。如果你仅更改一次之后突破 100-1000 次测试,那么它就不像独立的模块化那样了。

测试运行间隔 30 小时! 我绝对不会接受这份工作, 因为光听起来,就像是地狱。

rm -rf 的怨念

那如果说在 2500 万行的代码上动刀,光是测试就已经如此复杂了,除了之外,是否还有比这更可怕的代码?

必须有!

让很多程序员后悔到想剁手的“rm -rf”绝对要算一个,糟糕的不是命令行本身,而是它带来的后果。此前,不仅有顺丰程序员的删库跑路事件,就连前MegaEase 创始人&CTO 陈浩(微博@左耳朵耗子)也未能逃脱该命令行带来的魔咒。

那年写 Unix Shell 脚本,本想删除一些临时的子目录,如:rm -rf ${mydir}/ ,结果呢,我没检查 ${mydir} 这个变量是否为空,于是呢,在某种情况下,这变量真的为空了,于是,我成了团队的千古罪人。

那些年,我们见过和创造的“渣渣”代码

论起是否遇到过糟糕的代码时,天下的程序员似乎有着极高的相似性,在此,更有知乎网友(https://www.zhihu.com/question/30776912)吐槽:

@小猪:

if (b == true) {...}

我不常写 C,不知道 C 程序员是不是觉得这种写法是理所当然的,但当我在 Java 代码中频繁的看到这种代码的时候,我真的很无力。

@周越:

(a != b) ? b : a

@侯杰:

enum FiveLine
{
  Gold,
  Wood,
  Water,
  Fire,
  Earth,
};

看枚举名字不知道五行(hang)是什么鬼,看了枚举内容恍然大雾,原来是五行(xing)……

@玻璃杯中的鱼:

// 以下所有left代表右

// 以下所有right代表左

写在最后

在程序员的日常生活中,面对参差不齐的代码,Debug 成功了叫创新,改 Bug 失败叫掉坑,但是,如今的大牛哪一个又不是在写 Bug 与 Debug 中博弈过来的呢,也正是有了这些糟糕的代码才能让彼时的菜鸟们真正得以历练,而对于历练过程中需要注意什么,对此,CSDN 也曾发文从代码的基本规范和约束、编程思想、版本迭代与重构、设计模式等角度,为大家一一讲清如何才能成长为优雅的大牛程序员

你曾经又写过哪些让你后来捂脸的糟糕代码?欢迎下方留言,分享那些年的菜鸟岁月。

---【End】---

推荐阅读:

print_r('点个赞吧');
var_dump('点个赞吧');
NSLog(@"点个赞吧!");
System.out.println("点个赞吧!");
console.log("点个赞吧!");
print("点个赞吧!");
printf("点个赞吧!\n");
cout << "点个赞吧!" << endl;
Console.WriteLine("点个赞吧!");
fmt.Println("点个赞吧!");
Response.Write("点个赞吧");
alert(’点个赞吧’)

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存