程序员“第一性原理”

一、背景

作为程序员我们每天写代码、维护历史代码,有的公司甚至以代码量作为产出的一个指标。但是不同人代码质量却有云泥之别。

特别是维护遗留项目,一般历经多年,多人维护甚至多个团队维护过。修改Bug或者增加新功能时基本不敢动已有的逻辑,而是拷贝一份已有代码并在此基础上继续打补丁。甚至有一些最佳实践比如增加业务开关避免补丁引起线上故障。

这就是在已有的代码上做加法,也是大家笑称中的代码“屎山”。笔者上一家公司中多个产品项目都遇到类似问题。

加法# 二、原因

我们说需求是迭代的,架构和系统是演进的。产品在不同阶段有不同的约束,比如有的阶段是倒排期按时上线,有的阶段是提高质量、代码重构。总的来说,所谓“屎山”代码都不过是历史技术债务。出来混,迟早都是要还的。

根本原因是不了解已有的代码逻辑,不了解需求,对解决问题的思路没有深入的思考。

本文主要是从解决问题的角度来说。只有对需求和问题深入的理解后,对比不同的解决方案我们才能写出精简的代码,也就是对代码做减法。

减法这是需要进行代码和算法的思维训练。LeetCode是一个不错的途径。

以今天的LeetCode“每日一题”为例,3100. 换水问题 II[1]。

1
给你两个整数 numBottles 和 numExchange 。numBottles 代表你最初拥有的满水瓶数量。在一次操作中,你可以执行以下操作之一:喝掉任意数量的满水瓶,使它们变成空水瓶。用 numExchange 个空水瓶交换一个满水瓶。然后,将 numExchange 的值增加 1 。注意,你不能使用相同的 numExchange 值交换多批空水瓶。例如,如果 numBottles == 3 并且 numExchange == 1 ,则不能用 3 个空水瓶交换成 3 个满水瓶。返回你 最多 可以喝到多少瓶水。
1
给你两个整数 numBottles 和 numExchange 。numBottles 代表你最初拥有的满水瓶数量。在一次操作中,你可以执行以下操作之一:喝掉任意数量的满水瓶,使它们变成空水瓶。用 numExchange 个空水瓶交换一个满水瓶。然后,将 numExchange 的值增加 1 。注意,你不能使用相同的 numExchange 值交换多批空水瓶。例如,如果 numBottles == 3 并且 numExchange == 1 ,则不能用 3 个空水瓶交换成 3 个满水瓶。返回你 最多 可以喝到多少瓶水。

例子:

1
输入:numBottles = 13, numExchange = 6输出:15解释:上表显示了满水瓶的数量、空水瓶的数量、numExchange 的值,以及累计喝掉的水瓶数量
1
输入:numBottles = 13, numExchange = 6输出:15解释:上表显示了满水瓶的数量、空水瓶的数量、numExchange 的值,以及累计喝掉的水瓶数量

image

最简单就是按例子模拟写。代码如下:

1
class Solution:    def maxBottlesDrunk(self, numBottles: int, numExchange: int) -> int:        res = 0        # 每次可交换的空瓶数量        exchange = numExchange        # 满瓶数量        full = numBottles        # 空瓶数量        empty = 0        while full > 0 or empty >= exchange:            # 有满瓶时            if full > 0:                empty += full                res += full                full = 0            # 可换瓶时            if empty >= exchange:                empty -= exchange                full += 1                exchange += 1        return res
1
class Solution:    def maxBottlesDrunk(self, numBottles: int, numExchange: int) -> int:        res = 0        # 每次可交换的空瓶数量        exchange = numExchange        # 满瓶数量        full = numBottles        # 空瓶数量        empty = 0        while full > 0 or empty >= exchange:            # 有满瓶时            if full > 0:                empty += full                res += full                full = 0            # 可换瓶时            if empty >= exchange:                empty -= exchange                full += 1                exchange += 1        return res

上面的代码能通过LeetCode的Access,但是存在无效代码,逻辑比较绕,写完不确定是否考虑全边界Corner Case。

如果看推荐题解的话,就会发现差距。推荐题解如下:

1
class Solution:    def maxBottlesDrunk(self, numBottles: int, numExchange: int) -> int:        ans = numBottles        empty = numBottles        while empty >= numExchange:            ans += 1            # numExchange空瓶子换回一瓶满的,再喝掉            empty -= numExchange - 1            numExchange += 1        return ans
1
class Solution:    def maxBottlesDrunk(self, numBottles: int, numExchange: int) -> int:        ans = numBottles        empty = numBottles        while empty >= numExchange:            ans += 1            # numExchange空瓶子换回一瓶满的,再喝掉            empty -= numExchange - 1            numExchange += 1        return ans

官方题解很简洁。程序退出条件我们只需要关注空瓶子的数量。答案我们只需要关注“满瓶”数量,每次交换增加一个瓶子(第一次加上全部满瓶)。

三、总结

写代码无论是新需求还是维护历史代码,需要明确需求背景,深入理解问题,对比不同解决方案找最优,才能写出最简洁的代码。

这也许就是程序员的“第一性原理”吧。

  1. 换水问题 II: https://leetcode.cn/problems/water-bottles-ii/description/?envType=daily-question&envId=2025-10-02

https://leetcode.cn/problems/water-bottles-ii/description/?envType=daily-question&envId=2025-10-02