浪白的博客の小站
首页项目归档照片墙音乐灵境说说杂谈友链关于
封面

"够用就好"这四个字,我花了很久才懂

2026-07-15 23:10:00
✨ 心情:平静
# 随笔
# 心态
# 工程

刚工作那几年,我特别热衷于"把东西做好"。后来发现,很多时候我在做的是"把东西做复杂"。

一个具体的例子

早期做过一个配置读取的模块。需求很简单:从配置文件读几个值给业务用。

我当时的实现:

  • 支持 JSON、YAML、INI 三种格式(虽然只用 JSON)
  • 支持配置继承(base.yaml + env.yaml 合并)
  • 支持环境变量覆盖
  • 支持热更新(监听文件变化)
  • 支持类型校验和默认值
  • 加了一层缓存
  • 写了 40 个单元测试

总共大概 1200 行。

实际用了多少? 只有 JSON 读取和默认值,大概 80 行的量。

剩下 1120 行是负债:

  • 每次有人要加个配置项,得先看文档搞清楚继承规则
  • 热更新在生产环境出过一次 bug(文件被改到一半时读到了残缺内容)
  • 那 40 个测试里有 30 个测的是用不到的功能,跑一次要 8 秒

后来我把它删了,换成:

final class Config
{
    private array $values;

    public function __construct(string $path)
    {
        $values = json_decode(file_get_contents($path), true, 512, JSON_THROW_ON_ERROR);
        if (!is_array($values)) {
            throw new \RuntimeException("配置文件格式错误: $path");
        }
        $this->values = $values;
    }

    public function getInt(string $key, int $default = 0): int
    {
        return isset($this->values[$key]) ? (int) $this->values[$key] : $default;
    }

    public function getString(string $key, string $default = ''): string
    {
        return isset($this->values[$key]) ? (string) $this->values[$key] : $default;
    }
}

40 行。够用,而且所有人都看得懂。

为什么当时会那样做

我复盘了一下,大概有几个原因:

一、把"以后可能需要"当成了"现在需要"。

那时候看了一些讲"可扩展架构"的文章,觉得写得越灵活越专业。实际上,"灵活"是有成本的——每一层抽象都要有人理解它、维护它。

二、想证明自己会写"高级"代码。

这个不太好承认,但确实存在。写 1200 行带继承和热更新的配置模块,比写 40 行朴素代码更能显示"水平"。

现在的我知道,能把复杂需求用简单方案解决,才是水平。

三、没有成本意识。

当时觉得多写代码不花什么。实际上每一行代码都在消耗未来的时间:读它的时间、改它的时间、调试它的时间。

什么时候"过度"是值得的

也不是所有多余的东西都该删。有些"过度"是有道理的:

一、对外接口要留余地。

如果是给别人用的库或者 API,多留一些扩展点是对的,因为你没法预知使用者的需求。

// 对外 API:参数用对象,加字段不破坏兼容
public function send(Message $message): Result

// 而不是
public function send(string $to, string $subject, string $body): bool

二、核心数据结构要想清楚。

数据库表结构、领域模型、数据格式,这些改起来成本极高。这类地方多想几天是值得的。

三、真的有第二个使用方的时候。

注意是"真的",不是"可能"。

怎么判断是不是过度了

我现在的几个自问:

一、这个东西现在有几个人用? 如果只有一个,抽象层可以省掉。

二、如果需求变了,改起来要多久? 如果十分钟能改,那就不用提前设计。

三、新人理解这个要多久? 如果一个简单功能需要解释半小时,说明抽象层太多了。

四、删掉它会不会有人发现? 如果不会,那它可能本来就不该存在。

五、我在写这段代码的时候,是在解决问题还是在展示技巧? 这个问题最扎人,但很有用。

现在的一些具体做法

一、先写最简单能跑的版本。

不是先设计架构再实现,而是先让功能跑起来,然后看哪里真的需要抽象。

二、同一段逻辑出现第三次才抽函数。

出现两次就复制。因为两次的时候你还不确定它们会不会分化;第三次说明模式稳定了。

这条我以前不信,现在是认真的。复制两遍的成本,比抽错接口然后被迫兼容两种用法的成本低。

三、不加"以后可能用到"的参数。

function foo($a, $b, $options = []) 里的 $options 如果现在没有使用场景,就删掉。需要的时候再加,加参数是向后兼容的。

四、配置文件尽量扁平。

嵌套结构看着整齐,但解析和校验的代码会复杂很多。而且改一个值要数清楚层级。

五、定期删代码。

我每季度会挑一个模块,看能不能删掉点东西。删代码比加代码爽。

一件让我改变想法的事

有个模块我写得很"完善",各种设计模式都用上了。半年后要加个功能,我去改的时候,花了一个下午才理解自己当时的设计意图。

那个下午我在想:如果当时写的是 200 行直白的代码,现在可能二十分钟就改完了。

代码的读者主要是未来的自己和同事。让他们少花时间理解,比让代码"优雅"重要。

最后

"够用就好"不是"不追求质量"。它说的是:把精力花在真正需要的地方。

一个功能,如果数据结构选对了、边界情况处理了、错误有日志、有基本的测试,那就是高质量的。至于它是不是用了设计模式、是不是能支持某种未来的扩展,不重要。

我现在写代码会问自己一个问题:如果明天我离职了,接手的人需要多久能改这个功能?

这个问题的答案,比"代码是否优雅"更能说明质量。

avatar

浪白

邪恶的浪白

2026年7月

一
二
三
四
五
六
日
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

Recent Records

写给刚开始写代码的自己

2026-08-28 21:45:00

工具链的耐心是有限的

2026-08-05 22:00:00

重读自己五年前写的代码

2026-05-30 21:25:00