想象一下,你们团队在建一座叫“产品大厦”的软件楼盘。每天几十位“施工队”往里头提交新墙、新管道——也就是新代码。如果质量全靠人查,就像让资历最老的质检员老周拿着手电筒一间间房照,效率低,还会漏检。SonarQube 就是一支 7×24 小时值守的智能质检团队:它像扫描仪一样扫过每一行代码,发现墙皮开裂(代码异味)、裸露电线(安全漏洞),还计算出这些年“凑合修”欠下的维修账(技术债务)。而 AI 的加入,让这位质检员不再只贴罚单——它像一位见多识广的老师傅,直接告诉你“这块该怎么修”,甚至自动把修补方案递到你手里。本文带你认识这支 AI 质检团队如何工作,以及怎样把它请进你的施工流水线,让每次代码提交都自动过一趟质检门。
掌握 SonarQube AI 质量门禁,相当于给团队请来一位永不疲倦的代码质检老师傅,这是 AI 应用层工程师把大模型能力落进研发效能基础设施的核心技能。
可视化演示
通过代码异味智能分类检测、SQALE技术债务量化度量、以及CI流水线质量门禁判定三个模式,展示SonarQube结合AI的代码质量分析与治理流程。
本节配有交互动效(进入视口后自动加载,约 3KB)。
代码异味智能分类与AI检测 五大类异味涵盖可维护性可靠性安全性重复代码和设计问题 AI辅助分析对复杂问题生成修复建议从QuickFix到LLM混合架构 Smells = Maintainability + Reliability + Security + Duplication + Design AI提升检测精度与修复效率
代码异味识别
老周每次巡检新盘,第一件事是拿手电筒照墙缝、推推门窗、踩踩地板。他发现房子住久了毛病不少:有的墙皮起鼓,地板走起来咯吱响,门轴动不动卡住。这些“小毛病”不致命,房子不倒,但住着浑身别扭。代码异味(Code Smell)就是代码里的这些“小毛病”——程序能跑,功能也没错,可后来维护的人看到就想叹气。
光靠老周肉眼一间接一间看,迟早漏检。SonarQube 相当于给老周配了一支 7×24 小时加班的机器嗅探犬,内置几百条“闻味”规则,自动扫过每一行代码。它专抓典型的坏味道:一个方法长得像裹脚布(过长函数)、同样的逻辑被复制粘贴了七八遍(重复代码)、if 套 for 再套 while 像迷宫(复杂度过高)。
判断“代码有多绕”,SonarQube 用了一个叫“认知复杂度”的指标。它数出代码里所有控制结构(if、for、while、catch 等),每遇到一个就加 1 分,并且嵌套每深一层还要额外加分,因为人脑读嵌套代码时要记住的上下文越来越多。
一句话总结:认知复杂度就是老周判断“这间房动线有多绕、住着多费劲”的评分表。分数越高,越该尽早把大函数拆成小房间、把走廊拉直,让下一个进来的人少走弯路。
- 统计控制结构先找出函数里所有 if、for、while、catch 等控制结构,数量记为 n。
- 计算嵌套层数每个结构所处的嵌套深度为 nesting_k,嵌套越深,读代码时脑子里要“记住的账”越多。
- 逐项累加把每个结构的“1 + 嵌套层数”全部相加,就得到整段代码的认知复杂度。
安全漏洞扫描
墙皮开裂最多让人心烦,裸露的电线却是要命的。老周巡检时最紧张的是配电箱、燃气阀门这类安全项——平时看着没事,一旦出事就是大事。代码里的安全漏洞就是这种“裸露电线”:SQL 注入、跨站脚本(XSS)、硬编码密码,平时静静躺着,被攻击者碰一下就可能数据泄露、服务沦陷。
SonarQube 的安全规则库就像一本《安全隐患排查手册》,汇总了业界公认的漏洞模式(对应 CWE 漏洞条目)。它对照手册逐行排查,发现相似写法立即报警。AI 增强后还多了一招“安全热区”:规则拿不准、但看着可疑的地方,会标出来请人工瞄一眼——像老周在电箱旁贴一张“请专业电工复检”的便签。
不是每个漏洞都同样吓人,SonarQube 会为每个问题算风险评分。评分思路很朴素:一个漏洞的风险 = 被利用的可能性 × 一旦出事造成的损害。越容易打、打得越疼,优先级越高。
有了这张风险评分,老周就不再眉毛胡子一把抓:先处理“好打又致命”的电线,再排“难打但疼”的燃气阀,低风险项目排到最后慢慢看。团队的安全修复顺序,从此有了数据支撑。
- 评估利用可能性 P根据攻击难度打分,越容易被利用分值越高,取值范围 0 到 1。
- 评估影响程度 C根据数据泄露范围、服务中断时长、资金损失等后果严重性打分,同样取 0 到 1。
- 相乘得风险值两项相乘得到风险评分,SonarQube 按分数从高到低指导团队先修谁。
技术债务度量
老周翻这栋楼的维修记录,发现十年里“小毛病”全是凑合修的:墙裂了贴胶带,漏水了先拿盆接着,每次都说“等下次大修再说”。结果胶带越贴越多,欠的修葺活堆成了小山。代码也是如此——历史上遗留的每个问题都是带着利息的欠账,未来每次改功能都要先为它买单。
SonarQube 用 SQALE 模型给这笔“欠账”算了笔明白账:每个问题按修复难度折算成预估工时(比如一个 Blocker 漏洞折 1 人时、一个长函数折 0.5 人时),再把所有工时加总。加总结果就是“技术债务”,再除以代码规模,就得到技术债务比率(TDR)。
TDR 是个相对数,让不同规模的项目之间可以横向比较。比如 A 项目 10 万行代码、欠债 500 人时,比率 0.5%;B 项目 1 万行代码、欠债 500 人时,比率 5%——后者显然病得更重。SonarQube 仪表盘会给出“修复全部问题需要 X 人·周”,并折算成大致成本,方便向老板要资源。
老周把这张“维修欠账单”挂在项目部最显眼的位置,不是想批评谁,而是让团队看清:今天欠的每一笔,都是明天交付时还要付的利息。债务不可怕,可怕的是对债务数额毫无概念。
- 统计问题清单列出代码库中所有待修复的问题,共 n 个。
- 折算修复工时每个问题按 SQALE 模型估算修复时间 t_i,单位为人·小时,然后全部加总。
- 除以代码规模用代码行数 S 作分母,把总工时换算成比率,便于跨项目对比。
规则引擎配置
老周背后站着一本厚厚的《质检标准手册》:墙皮空鼓超过多大面积算问题?电线离地多高才合规?哪条是强条、哪条是建议?没有这本手册,老周的检查就是“凭感觉”。SonarQube 的规则引擎,就是这本手册的数字电子版。
手册由团队自己定——管理员可以开关每条规则、调整严重级别(Blocker 阻断、Critical 严重、Major 主要、Minor 次要、Info 提示),还能按语言选不同的规则集:Java 项目你关心空指针,前端项目你更在意 XSS 和依赖漏洞。规则不是越多越好,关键是“切中自家痛点”。
规则引擎之上,还有一道“总闸”叫质量门禁(Quality Gate)。它把一堆度量条件汇总成一个决定:放行还是拦截。所有条件必须同时满足才放行——像大门的 N 把锁必须全开。
有了这本“活手册”,老周的检查标准就不再是玄学:什么算问题、严重到什么程度停工,白纸黑字列得清清楚楚。更重要的是,标准和团队一起进化——这周新踩的坑,下周就变成手册里的新规则。
- 收集度量 m_i门禁采集若干度量项,如新增问题数、测试覆盖率、重复代码率等。
- 设置阈值 t_i团队为每个度量配置达标线,阈值越低、门禁越严。
- 逐项判定与求与所有条件用逻辑“与”连起来,任一不满足门禁就失败。
AI修复建议
以前老周查出问题,只能贴一张“限期整改单”——什么问题、什么位置,剩下全靠师傅自己琢磨。现在他手里多了块 AI 平板:扫到问题,平板不光报位置,还直接给出修法——“这块墙皮铲掉,按这个配方重新抹灰”。
SonarQube 的 AI 修复能力就是这么工作的:它把规则的描述、出问题的代码片段、周围的相关代码一起打包,交给大模型生成修复建议。建议不是凭空捏造的,而是结合了规则手册和当前工程上下文“对症下药”。开发者可以一键接受补丁,也可以当作参考答案自行修改。
AI 建议不是“圣旨”,它本质是概率性生成,可能改错。团队要持续追踪它靠不靠谱,最常用的指标就是采纳率——开发者真正接受并合入的建议占比。同时,低置信度的建议会转人工复核,形成“AI 出方案、人来拍板”的协作模式。
老周终于不用只发“整改单”了。AI 平板把他多年攒的修缮经验变成随叫随到的方案,而团队用采纳率给这位“电子老师傅”打分——建议质量高,就多让它出手;质量下滑,就赶紧调整提示策略或补充规则。
- 统计建议总数记录 AI 在一定周期内提供的修复建议总条数 N_offered。
- 统计采纳数量记录被开发者接受并真正合入代码的建议条数 N_accepted。
- 相除得到采纳率采纳率越高,说明 AI 建议越贴合团队习惯与代码风格,可放心加大 AI 的修复权限。
指标度量策略
老周验收楼盘有一条铁律:新装修的房间要一块砖都不能歪、一条缝都不能有;而住了十年的老房子,先排查有没有坍塌风险,再定翻新计划。他从不要求老房子一夜之间变得崭新——那不叫质检,叫拆迁。
SonarQube 的"Clean as You Code"策略正是这个道理。团队只对新代码设置严格门禁,确保新引入的问题近乎为零;对存量代码,则用趋势图观察债务是否在逐步下降,而不是要求立刻清零。存量问题靠专项迭代逐步还,新账绝不多欠。
衡量“新账”最核心的指标是新代码泄漏率:每千行新增代码里带进来的问题数。门禁一旦发现新代码里混进问题,直接打回重做;存量代码的指标只作长期参考,不进门禁判定。
老周把“新老有别”的策略讲给开发商听:新房子严把质量关,老房子排队翻新,这才是可持续的质保管法。对代码来说同理——盯住新代码的泄漏率,历史债慢慢还,质量曲线才能一路向上。
- 统计新增问题统计本次提交新引入的问题数量 I_new,不含历史遗留。
- 统计新增规模统计本次提交新增或修改的代码行数 L_new,作为归一化分母。
- 换算每千行泄漏率把比值乘以 1000,便于门禁设阈值时直观比较。
CI集成门禁
以前质检是“完工后大检查”:整栋楼盖好了才请老周团队进场,结果几百处问题堆在一起,返工成本高到离谱。现在老周被请进了施工流水线:每道工序完成,自动过一遍质检门,不合格当场打回,绝不放行到下一道。
在软件工程里,这条流水线就是 CI(持续集成)。SonarQube 提供名叫 SonarScanner 的扫描器,可以嵌入 Maven、Gradle、GitHub Actions、GitLab CI 等常见工具链。开发者每次 push 代码,流水线就自动触发扫描、分析、上报,一气呵成。
门禁结果直接决定构建命运:通过,代码合并进主干;不通过,构建标红并通知提交者。为了不拖慢开发节奏,扫描要快——增量分析只扫改动文件,理想反馈周期是几分钟内。反馈越短,开发者越愿意“边写边改”,问题死在萌芽期。
老周最终成了流水线上最受欢迎的人:不是因为他来“找茬”,而是因为他把质检嵌进了每一道工序,让问题在刚冒头就被发现。团队从此不用再开“返工大会”,质量门禁把坏代码挡在门外,好代码一路绿灯进主线。
- 扫描耗时 T_scan从代码提交到扫描器完成分析的时间,增量扫描可压缩到秒级。
- 分析上报耗时 T_report服务端执行全部规则并回传门禁判定结果的时间。
- 求和得出反馈周期两者相加就是开发者等待门禁结果的总时长,目标控制在几分钟内。
AI 应用场景
场景一:PR 自动评审与提交前自检
阶段:AI 应用集成(模型推理 + 应用)
解决的问题:传统代码评审依赖人工逐行看,漏检率高、耗时长,新成员容易放过模式性问题。接入 SonarQube AI 后,每次 PR 自动全量扫描,AI 给出修复建议,评审人只需复核关键点,效率大幅提升。
具体案例:某团队在 GitHub 上配置了 SonarQube 质量门禁,开发者提交 PR 后 3 分钟内收到扫描报告;AI 对八成问题给出可直接应用的修复补丁,评审时间从平均 2 小时降到 30 分钟,上线后缺陷率下降 40%。
场景二:安全漏洞应急修复
阶段:模型微调与提示词工程
解决的问题:高危漏洞(如 Log4j2 RCE)爆发时,人工排查和修复全站代码耗时以天计。SonarQube 安全热区 + AI 修复建议能结合漏洞规则与代码上下文,自动生成升级方案或替代写法,把应急时间压缩到小时级。
具体案例:某金融科技团队在 CVE 公告后,用 SonarQube 扫描出 14 处受影响调用点,AI 对其中 11 处直接给出参数化改写补丁,剩余 3 处标注“需人工确认”后由安全组介入,当天完成加固。
场景三:存量系统技术债治理
阶段:数据分析与可视化
解决的问题:老项目技术债高企,团队不知道从哪下手。SonarQube 按债务比率与问题密度排出优先级清单,AI 批量生成重构建议,团队按“低成本高收益”顺序逐批还债,并用趋势图验证效果。
具体案例:某电商后端 200 万行存量代码 TDR 达 28%,团队每月用一个迭代专治技术债:先清 Blocker 和 Critical,再按模块拆解 Major;两个季度后 TDR 降到 12%,发布回归缺陷数减半。
Python 演示
# -*- coding: utf-8 -*-"""mini_sonarq: 简化版 SonarQube AI 代码质量分析器"""import re class CodeSmellDetector: def __init__(self): self._smell_rules = [ ("long_function", 15, "函数超过 15 行,可读性差"), ("too_many_params", 4, "参数超过 4 个,建议封装参数对象"), ] self._vuln_patterns = [ (r"(?i)password\s*=\s*['\"][^'\"]*['\"]", "硬编码密码"), (r"(?i)select .* from .* where .*(\+|%|concat)", "疑似 SQL 注入"), ] def detect_smells(self, code): issues = [] lines = [ln.strip() for ln in code.splitlines() if ln.strip()] body = [ln for ln in lines if not ln.startswith("#")] rule, limit, desc = self._smell_rules[0] if len(body) > limit: issues.append({"type": "smell", "rule": rule, "msg": desc, "fix_hours": 0.5}) m = re.search(r"def\s+\w+\((.*?)\)", code, re.S) rule, limit, desc = self._smell_rules[1] if m and len([p for p in m.group(1).split(",") if p.strip()]) > limit: issues.append({"type": "smell", "rule": rule, "msg": desc, "fix_hours": 0.3}) return issues def detect_vulns(self, code): issues = [] for pat, desc in self._vuln_patterns: if re.search(pat, code): issues.append({"type": "vuln", "rule": pat[:10], "msg": desc, "fix_hours": 1.0}) if re.search(r"#\s*TODO", code, re.I): issues.append({"type": "smell", "rule": "todo_left", "msg": "存在 TODO 注释,需人工确认进度", "fix_hours": 0.2}) return issues class AiFixEngine: """模拟 AI 修复建议引擎""" def __init__(self): self._suggestions = { "长": "将函数拆分为多个单一职责的小函数", "参数": "把参数封装成 config 对象或 dataclass", "硬编码": "改用环境变量或密钥管理服务读取凭据", "注入": "使用参数化查询替代字符串拼接 SQL", } def suggest(self, issue): for key, advice in self._suggestions.items(): if key in issue.get("msg", ""): return advice return "请人工评估后修复" def confidence(self, issue): if any(key in issue.get("msg", "") for key in self._suggestions): return 0.92 return 0.60 def quality_gate(issues, leak_limit=1): """质量门禁:高置信度问题由 AI 自动修复,低置信度转人工""" engine = AiFixEngine() reports = [] leaked = 0 for issue in issues: advice = engine.suggest(issue) conf = engine.confidence(issue) if conf >= 0.85: reports.append("[AI修复] %s -> %s (置信度 %.2f)" % (issue["msg"], advice, conf)) else: leaked += 1 reports.append("[待人工] %s -> %s (置信度 %.2f)" % (issue["msg"], advice, conf)) debt = sum(it["fix_hours"] for it in issues) passed = leaked <= leak_limit reports.append("门禁: %s (漏网 %d <= %d)" % ("通过" if passed else "拦截", leaked, leak_limit)) reports.append("技术债务: %.1f 人时" % debt) return passed, reports def main(): sample = '''def handle_order(user, item, qty, price, discount, address): # TODO: 补全地址校验 sql = "select * from orders where user = '" + user + "'" password = "123456" print(sql, password) return True''' detector = CodeSmellDetector() issues = detector.detect_smells(sample) + detector.detect_vulns(sample) passed, reports = quality_gate(issues) for line in reports: print(line) if __name__ == "__main__": main()
self._smell_rules = [...]用配置列表管理规则,新增一条规则只需加一行,这就是规则引擎的“活手册”。re.search(pat, code)用正则扫描漏洞模式,模拟 SonarQube 对照《安全隐患排查手册》逐行排查。issues.append({...})统一问题结构,把类型、规则、描述、修复工时打包,方便后续统一处理。engine.suggest(issue)根据问题描述匹配修复方案,模拟 AI 老师傅“对症下药”给建议。conf >= 0.85置信度阈值决定“AI 直接修”还是“转人工”,对应生产中的 AI 自主修复策略。sum(it["fix_hours"] for it in issues)汇总所有问题的预估修复时间,即是技术债务的雏形。passed = leaked <= leak_limit质量门禁只关心漏网问题数量是否在容忍范围内,容忍度过高就会放行问题代码。
常见误区
误区看到 SonarQube 报了一堆“异味”就慌,以为代码出错了,甚至停下来把存量代码全部重写一遍,生怕带着异味上线会出事。
正解异味不等于 Bug。它反映的是可读性、可维护性问题——就像墙皮开裂不影响承重,但住着难受。正确姿势是分级处理:新代码严格执行门禁,存量代码按债务优先级逐步还,而不是一次性推倒重来。
为何常见:新手容易把“质量报告”理解为“故障清单”,忽视了质量指标之间的优先级差异,把“建议优化”当成“紧急事故”。
误区AI 都给出补丁了,直接点接受让机器人改完就行,省去人工评审和测试,反正大模型写代码很厉害。
正解AI 建议基于“规则模式 + 上下文 + 历史数据”生成,属于高概率猜测而非证明。它可能改错变量、破坏边界条件,甚至引入新问题。正确做法是:先看置信度,再跑测试,关键位置仍需人工复核。采纳率指标就是用来追踪“AI 建议到底可不可靠”的。
为何常见:大模型在代码补全上表现亮眼,让人误以为“修复建议”和“补全”一样可靠,忽略了静态分析场景对精准度的更高要求。
误区只要整体代码的指标都绿了,质量门禁就安全了,新代码带点问题没关系,反正平均分被拉下来了。
正解整体绿可能掩盖“新代码持续掺垃圾”。SonarQube 的 Clean as You Code 策略恰恰相反:门禁重点盯新代码的泄漏率,存量问题看长期趋势。否则今天一点、明天一点,半年后整栋楼又变危房。
为何常见:人天生关注“平均值”而非“增量”,忽略了新增问题的复利效应——每天一点小问题,积累起来就是一座大山。
误区把技术债务当成“大敌”,要求团队不惜一切代价清零,甚至砍掉新功能开发来还债。
正解技术债务是工程权衡的产物:为了按时上线,偶尔“先凑合、后还债”是理性选择。聪明的团队管理债务而非消灭债务——设定可接受的 TDR 阈值,让债务增长速度低于还债速度,就像家庭理财允许合理的房贷。
为何常见:“技术债”这个词自带负面色彩,让人忽略了它背后其实是取舍与效率,追求绝对零债务反而会导致过度设计和交付变慢。
误区每次提交都扫描会不会让构建变慢?干脆把门禁关了,上线前手动扫一次,省得耽误团队速度。
正解只要配置合理(增量扫描、只跑相关语言规则、适度阈值),扫描反馈可以控制在几分钟内,远比事后返工便宜。门禁的价值在于把问题拦截在最便宜的阶段——写代码的那一刻,而不是发布前的深夜。
为何常见:用过低速全量扫描的人容易被旧体验吓到,忽略了现代扫描器的增量分析能力,把“质量保障”和“速度拖累”错误地对立起来。
面试题
Q1. SonarQube 的核心价值是什么?
- A.提升程序运行速度
- B.自动化发现代码问题← 正确答案
- C.替代人工编写代码
- D.负责云端资源扩容
答案:B。SonarQube 的核心价值类似一支 24 小时值守的质检团队,它不负责让房子“跑得更快”,而是自动找出代码异味、漏洞和技术债务。A 是性能优化工具的事,C 是 AI 编码助手的职责,D 是云平台运维的领域。
Q2. 下列哪个属于代码异味?
- A.函数过长且嵌套过深← 正确答案
- B.服务器内存持续暴涨
- C.数据库连接突然断开
- D.用户反馈页面加载慢
答案:A。代码异味就像墙皮开裂,不是致命事故却是维护负担,长函数嵌套就是典型异味。B 和 C 属于运行时故障,D 属于用户侧体验问题,都不是静态分析关注的“坏味道”。
Q3. 认知复杂度衡量的是什么?
- A.代码被人类理解的难度← 正确答案
- B.编译器执行的耗时长度
- C.进程占用的内存大小
- D.网络请求的响应时间
答案:A。认知复杂度就是老周评估“这间房动线有多绕”的标尺,衡量人类读懂代码所需的心智负担,嵌套越深、分支越多得分越高。它与编译耗时、内存占用、网络响应这些运行时指标无关。
Q4. 技术债务比率公式的分母是什么?
- A.代码规模(行数)← 正确答案
- B.团队开发总人数
- C.每日构建的总次数
- D.历史提交的总条数
答案:A。TDR = 总修复工时 ÷ 代码规模(行数)。分母必须是代码规模,这样才能把不同体量的项目放在同一把尺子上比较,而不是用人数、构建次数或提交条数这些与债务量无关的量。
Q5. 质量门禁放行提交的逻辑是什么?
- A.任一度量条件达标
- B.所有度量条件达标← 正确答案
- C.平均得分超过六十
- D.仅新代码进行检查
答案:B。质量门禁像一道装了 N 把锁的大门,必须所有锁同时打开(所有度量都达标)才放行,任一条件不满足就拦截。它不看“平均分”,也不会只看新代码,而是把全部门禁条件用逻辑“与”连接。
Q6. AI 修复建议采纳率的定义是什么?
- A.被采纳建议数比建议总数← 正确答案
- B.修复建议数比代码行数
- C.错误修复数比总建议数
- D.建议条数比团队人数
答案:A。采纳率 = 被开发者接受并合入的 AI 建议数 ÷ AI 提供的建议总数。它是衡量 AI 修复建议质量的“口碑指标”,类似团队采纳电子老师傅建议的比例,和代码行数、团队人数没有关系。
Q7. "Clean as You Code"的核心思想是什么?
- A.新代码严格达标并逐步降债← 正确答案
- B.存量代码必须全部立即清零
- C.禁用全部规则保证零噪音
- D.每天重写所有历史旧代码
答案:A。Clean as You Code 主张“新代码严格要求、存量债务逐步还”,就像装修时新房间一块砖都不能歪、老房子先排危再翻新。B 和 D 等于逼施工队停工拆楼,C 则因噎废食,都不符合可持续质保的理念。
Q8. 以下哪些是 SonarQube 能检测的问题类型?
- A.代码异味与重复代码← 正确答案
- B.安全漏洞与硬编码密钥← 正确答案
- C.服务器硬件温度告警
- D.技术债务修复工时估算← 正确答案
答案:ABD。SonarQube 的检测范围包括代码异味、重复代码、安全漏洞(如硬编码密钥)以及基于 SQALE 模型的技术债务工时估算。C 属于运维监控平台(如 Zabbix、Prometheus)的领域,不在代码静态分析的范畴内。
Q9. 关于 CI 集成质量门禁的说法,正确的是?
- A.门禁失败可阻止代码合并← 正确答案
- B.扫描器可接入 CI 流水线← 正确答案
- C.快速反馈可加快开发节奏← 正确答案
- D.必须停掉所有开发流程
答案:ABC。门禁失败会令构建标红、阻止合并;SonarScanner 可嵌入 GitHub Actions、GitLab CI 等工具链;反馈周期越短,开发者越愿意在上下文中立即修复问题,从而加快节奏。D 显然是荒谬的,质量门禁是为了保障流程而非停掉流程。
Q10. SQALE 技术债务估算涉及哪些要素?
- A.每个问题预估修复工时← 正确答案
- B.按费率折算修复成本← 正确答案
- C.与代码规模对比算比率← 正确答案
- D.统计团队每日咖啡摄入
答案:ABC。SQALE 先把每个问题折算成预估修复工时(A),再按团队费率折算成修复成本(B),并与代码规模对比得到债务比率(C),从而让不同项目横向可比。D 属于段子选项,和债务估算毫无关系。