遗留代码豁免

半路接入 cha。第一次 cha analyze 报了 200 条 finding,一半是早就在那儿的、跟现在团队无关的代码。CI 要绿,但又不能假装代码没问题。

按优先级三件武器:baseline 表示"以后再说",行内指令 表示"这一处是合理的",配置 exclude 表示"这个路径根本不要看"。

1. 先打 baseline

把当前所有 finding 拍个快照,之后只对新 finding 失败。

cha baseline
git add .cha/baseline.json && git commit -m "Cha baseline at adoption"

CI 用:

cha analyze --baseline .cha/baseline.json --fail-on warning

引入新 finding 的 PR 会挂掉,旧的 finding 静默放过。baseline 文件本身是一组指纹(每条 finding 的稳定标识),体积小、commit 干净、技术债(debt)还掉时 diff 也读得清。

完整流程:Baseline 工作流

2. 行内指令处理特定 item

某个 item 真的有理由违规(200 行的状态机、构造函数实在需要 9 个参数):

#![allow(unused)]
fn main() {
// cha:ignore long_method
fn dispatch_state_machine(&mut self, event: Event) -> State {
    match self.current {
        // ... 200 行有正当理由的代码
    }
}
}
# cha:ignore long_parameter_list
def __init__(self, host, port, user, password, db, ssl_cert, retry, timeout):
    ...

对下一个 item 屏蔽一条、多条或全部:

#![allow(unused)]
fn main() {
// cha:ignore                        — 屏蔽所有
// cha:ignore long_method            — 屏蔽一条
// cha:ignore long_method,complexity — 屏蔽多条
// cha:set long_method=200           — 单独把这个 item 的阈值放宽
}

行内指令不会被写进 baseline 文件——它就是写在源码里的明确决定,做不做 baseline 都在那儿。当你希望豁免在 code review 里看得见时,用它。

完整语法见 行内指令

3. 配置 exclude 整个路径

某些文件 cha 根本不该看——生成代码、第三方 vendoring、测试 fixture:

# .cha.toml
exclude = [
    "vendor/**",
    "src/generated/**",
    "tests/fixtures/**",
    "node_modules/**",   # 文件遍历器本来就尊重 .gitignore,这条通常多余
]

模式是 glob。** 匹配任意层。被排除的路径根本不会被解析——比"跑了再屏蔽"便宜。

决策表

处境用什么
满地都是历史 finding,今天就要绿 CIbaseline
一个文件里一条特别顽固的 finding行内 cha:ignore
一个文件需要单独的阈值行内 cha:set
整个目录就不该被分析配置 exclude

可以叠加。baseline、行内、exclude 是三层独立机制,cha 的处理顺序是 exclude → 分析 → cha:ignore / cha:set → baseline 过滤。一条 finding 要四层全放过才会冒出来。

还债

baseline 不是"永久无视"。隔段时间:

cha baseline                       # 重新生成,捕获当前状态
git diff .cha/baseline.json        # 看少了哪些

如果 git diff 显示有条目消失,说明 debt 还掉了。如果反而出现新条目,那 CI 里的 --baseline 没生效,去查配置。

See also