程序员“第一性原理”
程序员“第一性原理”
一、背景
作为程序员我们每天写代码、维护历史代码,有的公司甚至以代码量作为产出的一个指标。但是不同人代码质量却有云泥之别。
特别是维护遗留项目,一般历经多年,多人维护甚至多个团队维护过。修改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 的值,以及累计喝掉的水瓶数量 |

最简单就是按例子模拟写。代码如下:
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 |
官方题解很简洁。程序退出条件我们只需要关注空瓶子的数量。答案我们只需要关注“满瓶”数量,每次交换增加一个瓶子(第一次加上全部满瓶)。
三、总结
写代码无论是新需求还是维护历史代码,需要明确需求背景,深入理解问题,对比不同解决方案找最优,才能写出最简洁的代码。
这也许就是程序员的“第一性原理”吧。
- 换水问题 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