0 Comments

程序员必知的代码整洁之道:别让烂代码拖垮你的项目

写代码这件事,入门不难,难的是把代码写明白。刚入行的时候,大家关注的是”能不能跑通”;干了两三年之后,就开始琢磨”别人能不能看懂”。这道坎跨过去,才算真的长大了。

我见过太多项目,功能上没什么毛病,但是维护起来要人命——改一个字段要翻十几个文件,加一个判断被迫复制粘贴一大段,review 的时候同事看了半天回一句”这句是干嘛的”。这些都不算 bug,却真实地拖慢着整个团队的节奏。

今天不聊玄乎的概念,就把”整洁代码”里最实用、最好落地的几条拎出来讲讲。

命名是性价比最高的重构

代码里出现最多的东西就是名字:变量名、函数名、类名、参数名。名字起不好,剩下的一切都是白搭。有个说法我很认同:阅读代码的大部分时间是在理解命名,而不是理解逻辑。

看下面两段,功能完全一样:

# 糟糕的写法
def f(d):
    t = 0
    for x in d:
        if x[1] == 1:
            t += x[0]
    return t
# 清晰的写法
def calc_active_user_total(users):
    total = 0
    for user in users:
        if user.status == 1:
            total += user.amount
    return total

第二段一眼就知道在算活跃用户的总金额。代价仅仅是名字长了一点,收益却是别人不用再去看数据长什么样。

几个起名的经验:

  • 用能读出来的名字,别用 usrCntlstIdx 这种缩写拼装,写全 userCountlastIndex
  • 布尔值用问句isValidhasPermissionisActive,读完自然知道结果是真还是假。
  • 函数名用动词开头fetchUsersaveOrderdeleteCache,一眼能看出它干了什么。
  • 别加没用的后缀UserInfoUserDataUserObject 里的 Info/Data/Object 基本都是噪音,直接 User 就好。

函数应该只做一件事

判断一个函数是否过长,有个很朴素的信号:你在描述它的功能时,用到了”和”字。比如”这个函数负责校验参数和保存订单”,那它就该拆成两个函数。

单职责不是洁癖,是有实际回报的:

// 拆之前:一段 80 行的函数,混着校验、组装、落库、发消息
async function placeOrder(input) {
  // ... 一堆校验
  // ... 组装订单对象
  // ... 写入数据库
  // ... 发通知
}

// 拆之后:每个函数都能单独理解和测试
async function placeOrder(input) {
  const valid = validateOrderInput(input);
  if (!valid.ok) return { error: valid.reason };

  const order = buildOrder(input);
  const saved = await saveOrder(order);

  await notifyUser(saved);
  return saved;
}

拆分之后带来三个实打实的好处:

  1. 逻辑变薄,读代码的人很快能建立全局视图。
  2. 每个小函数可以独立写测试,出问题也好定位。
  3. 复用变容易——validateOrderInput 换个场景还能接着用。

我留一条判断标准给自己:如果一个函数需要加注释来解释它做了什么,先想想是不是名字和拆分出了问题,而不是急着补注释。

注释是谎言的高发区

很多人以为注释越多越负责,其实恰恰相反。注释最大的风险是它会过时——代码改了,注释没人记得改,慢慢就变成谎言。很多人宁愿相信注释,结果被注释带到沟里。

我的原则是:

  • 好的命名胜过注释。代码自己能说清楚的事,别用注释再重复一遍。
  • 注释用来解释”为什么”,而不是”是什么”。”是什么”交给代码本身,”为什么”才值得写下来。
# 坏注释:重复代码
# 计算总价
total = price * quantity

# 好注释:解释一个不明显的决策
# 运费在满 99 时免单,这里用 100 是因为闭区间判断,
# 恰好 99.5 的订单也要免掉
if amount >= 100:
    shipping = 0

还有一种典型的坏味道:被注释掉的代码。很多人不敢删,觉得”以后可能用得上”。实际上 Git 已经帮你存了历史,删掉它们,代码才会清爽。留着死代码,只会让后来的人困惑”这段怎么不用了,该不该用”。

别让条件判断越来越深

嵌套是整洁代码的头号杀手。一个小改动加一个 if,时间一长就变成”阶梯地狱”:

def process(user):
    if user is not None:
        if user.is_active:
            if user.balance > 0:
                if not user.is_banned:
                    # 真正的业务逻辑
                    charge(user)

这种写法读起来脑子里要维持好几个条件栈。换一种方式来写,立刻清爽:

def process(user):
    if user is None:
        return
    if not user.is_active:
        return
    if user.balance <= 0:
        return
    if user.is_banned:
        return

    # 真正的业务逻辑
    charge(user)

这个技巧叫卫语句(Guard Clause):把不符合条件的情况在最前面”拦”出去,让主逻辑留在最后,处于最浅的层级。读起来顺,改起来也安全。

重复是一切维护成本的根源

重复代码(DRY 原则的反面)是最容易被忽视,又最伤人的问题。今天复制了一段,明天要改逻辑,你就得记得”这段我在另一个文件里也抄过一份”。忘了改,就埋下了 bug。

发现重复,抽个函数或者模块出来,这是最划算的投资:

// 重复出现两次的格式化逻辑
function formatMoney(amount) {
  return amount.toLocaleString('zh-CN', {
    style: 'currency',
    currency: 'CNY',
  });
}

// 订单和账单两处直接复用
const orderLine = formatMoney(order.amount);
const billLine = formatMoney(bill.amount);

当然,也别矫枉过正。如果你为了”消除重复”抽出来的函数,最终比原来的两段还难读,那说明它们只是长得像,本质并不是一回事,这时候保留原样反而更好。DRY 的核心是消除真正的重复,不是消灭一切相似。

小结

整洁代码不是某本书里的教条,而是一套能实实在在降低维护成本的习惯。落到日常,就这几条:

  • 把命名当回事,它是代码里最频繁的沟通。
  • 一个函数只做一件事,拆到能一句话说清为止。
  • 注释解释”为什么”,代码说清”是什么”。
  • 用卫语句把深层嵌套打散。
  • 及时消除真正的重复。

这些事都不难,难的是一直坚持。我的体会是,与其追求一次大重构,不如每次改代码时顺手把眼前这段弄干净一点。积累半年,整个项目的气质会完全不一样。

代码最终是写给人看的,只是顺便让机器去执行。想明白这一点,很多纠结自然就解开了。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注