418 lines
7.8 KiB
Markdown
418 lines
7.8 KiB
Markdown
---
|
||
id: quality-code-metrics
|
||
title: Code Metrics — Complexity / Coverage / Hot spots
|
||
category: Coding
|
||
status: draft
|
||
source_trust_level: B
|
||
verification_status: conceptual
|
||
created_at: 2026-05-09
|
||
updated_at: 2026-05-09
|
||
tags: [quality, metrics, vibe-coding]
|
||
tech_stack: { language: "Various", applicable_to: ["Engineering"] }
|
||
applied_in: []
|
||
aliases: [code metrics, cyclomatic complexity, test coverage, code churn, hot spot, SonarQube]
|
||
---
|
||
|
||
# Code Metrics
|
||
|
||
> "측정 안 한 거 = 개선 X". **Complexity, coverage, churn, hot spots**. Metrics = signal X target. Goodhart's law 주의.
|
||
|
||
## 📖 핵심 개념
|
||
- Complexity: cyclomatic, cognitive.
|
||
- Coverage: line / branch / function.
|
||
- Churn: 자주 변경 file.
|
||
- Hot spot: complexity × churn = risk.
|
||
|
||
## 💻 코드 패턴
|
||
|
||
### Cyclomatic complexity
|
||
```
|
||
함수 안 decision point + 1.
|
||
|
||
if / for / while / case / && / || / ternary 마다 +1.
|
||
|
||
10 미만: OK
|
||
10-20: review 필요
|
||
20+: split
|
||
```
|
||
|
||
```ts
|
||
// CC = 1
|
||
function add(a: number, b: number) { return a + b; }
|
||
|
||
// CC = 4 (3 if + 1 base)
|
||
function classify(n: number) {
|
||
if (n < 0) return 'negative';
|
||
if (n === 0) return 'zero';
|
||
if (n < 10) return 'small';
|
||
return 'large';
|
||
}
|
||
```
|
||
|
||
### Cognitive complexity (modern)
|
||
```
|
||
Nesting 도 카운트.
|
||
Sequential 보다 nested 가 비싸.
|
||
|
||
CC = 같음 but cognitive 다름:
|
||
function flat() {
|
||
if (a) ...;
|
||
if (b) ...;
|
||
if (c) ...;
|
||
// cognitive ≈ 3
|
||
}
|
||
|
||
function nested() {
|
||
if (a) {
|
||
if (b) {
|
||
if (c) ...;
|
||
}
|
||
}
|
||
// cognitive ≈ 6 (nesting penalty)
|
||
}
|
||
```
|
||
|
||
→ SonarQube 측정.
|
||
|
||
### 측정 도구
|
||
```bash
|
||
# JS / TS
|
||
npx complexity-report-html src/
|
||
|
||
# 또는 ESLint
|
||
{
|
||
"rules": {
|
||
"complexity": ["warn", 10],
|
||
"max-depth": ["warn", 4],
|
||
"max-lines-per-function": ["warn", 50]
|
||
}
|
||
}
|
||
```
|
||
|
||
```bash
|
||
# Code Climate (managed)
|
||
# SonarQube / SonarCloud
|
||
# CodeScene
|
||
```
|
||
|
||
### Test coverage
|
||
```bash
|
||
yarn test --coverage
|
||
|
||
# 결과
|
||
# Lines: 87.3%
|
||
# Branches: 76.5%
|
||
# Functions: 92.1%
|
||
# Statements: 87.0%
|
||
```
|
||
|
||
```js
|
||
// jest.config.js
|
||
coverageThreshold: {
|
||
global: { lines: 80, branches: 70, functions: 80 },
|
||
'./src/payment/': { lines: 95, branches: 90 }, // critical
|
||
}
|
||
```
|
||
|
||
→ Critical 만 high. 모든 곳 100% 무의미.
|
||
|
||
### Coverage 함정
|
||
```
|
||
100% line = bug 없음 X.
|
||
- Test 가 assertion 약함
|
||
- Edge case 못 잡음
|
||
- Logic 정확성 검증 X
|
||
|
||
→ Coverage = signal. Mutation testing 가 진짜.
|
||
```
|
||
|
||
### Code churn
|
||
```bash
|
||
# 자주 변경 file (last 6 months)
|
||
git log --since="6 months ago" --pretty=format: --name-only | sort | uniq -c | sort -rn | head -20
|
||
|
||
# 또는 CodeScene / Codacy
|
||
```
|
||
|
||
→ 자주 변경 = bug 가능.
|
||
|
||
### Hot spot (complexity × churn)
|
||
```
|
||
High complexity + High churn = HIGH RISK.
|
||
→ Refactor priority.
|
||
|
||
Low complexity + Low churn = stable.
|
||
High complexity + Low churn = legacy (안 변경하면 OK).
|
||
Low complexity + High churn = OK 자주 변경.
|
||
```
|
||
|
||
### Churn 자주 변경 file 의 의미
|
||
```
|
||
1. Bug magnet — 같은 곳 자주 fix.
|
||
2. 디자인 잘못 — 자주 reorganize.
|
||
3. 활발 — 단순 active feature.
|
||
|
||
→ 추가 분석 필요.
|
||
```
|
||
|
||
### Bus factor
|
||
```bash
|
||
# Each file 의 contributor 수
|
||
git log --pretty=format:"%an" -- file.ts | sort -u | wc -l
|
||
|
||
# 1 = bus factor 1 (위험)
|
||
```
|
||
|
||
→ 1 명만 = pair / share.
|
||
|
||
### File / function size
|
||
```
|
||
Function > 50 lines: split
|
||
File > 500 lines: split / module
|
||
Class > 1000 lines: God class — split
|
||
```
|
||
|
||
→ 정확 cutoff 보다 trend.
|
||
|
||
### Type coverage (TS)
|
||
```bash
|
||
npx type-coverage --strict --at-least 95
|
||
# 95% 미만 = fail
|
||
```
|
||
|
||
→ `any` 사용 % 측정.
|
||
|
||
### Dependency metrics
|
||
```bash
|
||
# Cycles
|
||
madge --circular src/
|
||
|
||
# Depth
|
||
npx dependency-cruiser src/ --output-type metrics
|
||
|
||
# Fan-in / fan-out
|
||
```
|
||
|
||
### Build time
|
||
```
|
||
Slow build = 자주 안 build = bug 자주.
|
||
|
||
목표:
|
||
- Local dev: instant (HMR)
|
||
- CI test: < 10 min
|
||
- Production build: < 5 min
|
||
|
||
측정 + alarm if 늘어남.
|
||
```
|
||
|
||
### CI 안 metric
|
||
```yaml
|
||
- name: Test coverage
|
||
run: yarn test --coverage --coverageReporters=text-summary
|
||
|
||
- name: Complexity
|
||
run: npx eslint --rule 'complexity: [error, 15]' src/
|
||
|
||
- name: Type coverage
|
||
run: npx type-coverage --at-least 95
|
||
|
||
- name: Bundle size
|
||
run: yarn size
|
||
|
||
- name: Lint
|
||
run: yarn lint --max-warnings 0
|
||
```
|
||
|
||
### Dashboard (long-term trend)
|
||
```
|
||
Grafana / Datadog / SonarQube:
|
||
- 매 commit / day metrics
|
||
- Trend graph
|
||
- Per-team / per-module
|
||
|
||
→ "지난 분기 보다 좋아짐?"
|
||
```
|
||
|
||
### Goodhart's law
|
||
```
|
||
"When a measure becomes a target, it ceases to be a good measure."
|
||
|
||
목표 = 100% coverage:
|
||
→ 사람 가 의미 없는 test 추가.
|
||
→ Coverage 100% but quality X.
|
||
|
||
→ Metric = signal. Target 가 아닌 signal.
|
||
```
|
||
|
||
### Healthy ranges
|
||
```
|
||
Cyclomatic: < 10 (most), 15 max.
|
||
Cognitive: < 15.
|
||
Function lines: < 50.
|
||
File lines: < 500.
|
||
Coverage: 70-90% (critical 95%).
|
||
Type coverage: 95%+ (TS strict).
|
||
Cycles: 0.
|
||
```
|
||
|
||
→ 정확 number 보다 trend.
|
||
|
||
### Code review metric
|
||
```
|
||
- PR cycle time (open → merge)
|
||
- PR size (median lines)
|
||
- Time to first review
|
||
- Number of review iterations
|
||
|
||
목표:
|
||
- Cycle time: < 1 day
|
||
- Median size: < 200 lines
|
||
- First review: < 4 hours
|
||
- Iterations: 1-2
|
||
```
|
||
|
||
→ 개선 = 빠른 ship + 적은 bug.
|
||
|
||
### DORA metrics
|
||
```
|
||
1. Deploy frequency
|
||
2. Lead time (commit → prod)
|
||
3. MTTR (mean time to recover)
|
||
4. Change failure rate
|
||
|
||
Elite:
|
||
- Deploy: multiple per day
|
||
- Lead time: < 1 hour
|
||
- MTTR: < 1 hour
|
||
- Failure: < 15%
|
||
```
|
||
|
||
→ Industry benchmark.
|
||
|
||
### Technical excellence checklist (자체)
|
||
```
|
||
- [ ] 80%+ test coverage
|
||
- [ ] Type strict
|
||
- [ ] Lint clean
|
||
- [ ] CI < 10 min
|
||
- [ ] PR cycle < 1 day
|
||
- [ ] Documentation 80%+
|
||
- [ ] On-call < 3 page / week
|
||
- [ ] Deploy frequency 1/day+
|
||
```
|
||
|
||
### 자체 metrics (domain-specific)
|
||
```
|
||
- API endpoint test count
|
||
- Schema validation coverage
|
||
- Error rate per endpoint
|
||
- Latency p99 per endpoint
|
||
- Cache hit rate
|
||
- DB query count
|
||
```
|
||
|
||
### Metric review meeting
|
||
```
|
||
Monthly / quarterly:
|
||
- 어떤 metric 가 worsened?
|
||
- 어떤 module 가 risky?
|
||
- 어떤 action?
|
||
|
||
→ Action items.
|
||
```
|
||
|
||
### Per-team vs whole-codebase
|
||
```
|
||
Per-team metrics: ownership, accountability.
|
||
Whole-codebase: 전체 health.
|
||
|
||
→ 둘 다.
|
||
```
|
||
|
||
### Anti-metric (bad measures)
|
||
```
|
||
- LOC (lines of code) — 더 많이 = 더 좋음 X
|
||
- PR count — 큰 PR vs 많은 작은 PR
|
||
- Hours worked — quality X quantity
|
||
- Code review comments — 많이 = 좋음? maybe noise
|
||
|
||
→ 의미 없는 measure.
|
||
```
|
||
|
||
### Senior 의 metric
|
||
```
|
||
Junior:
|
||
- Output (LOC, PRs)
|
||
|
||
Senior:
|
||
- Quality (review feedback)
|
||
- Mentoring (others' growth)
|
||
- System health
|
||
- Tech debt 줄임
|
||
- Cross-team impact
|
||
```
|
||
|
||
→ Senior 가 LOC 측정 = 잘못된 incentive.
|
||
|
||
### Manager dashboard
|
||
```
|
||
- Team velocity
|
||
- Cycle time
|
||
- Defect rate
|
||
- On-call load
|
||
- Burnout signals (work hours, vacation usage)
|
||
- Engagement survey
|
||
|
||
→ Indicators of team health.
|
||
```
|
||
|
||
### Tools
|
||
```
|
||
SonarQube / SonarCloud — full code quality
|
||
CodeScene — hot spot, knowledge map
|
||
Codacy — automated review
|
||
Code Climate — quality grade
|
||
Codecov — coverage trend
|
||
LinearB / Jellyfish — DORA + dev productivity
|
||
```
|
||
|
||
### "측정만 하지 말고 act"
|
||
```
|
||
Metrics 보기 → action items → 다음 달 비교.
|
||
|
||
매 quarter:
|
||
- Top 3 metrics 가 worsened?
|
||
- 액션 plan
|
||
- Accountability
|
||
```
|
||
|
||
## 🤔 의사결정 기준
|
||
| 측정 | 자주 |
|
||
|---|---|
|
||
| Complexity | PR (CI lint) |
|
||
| Coverage | PR + dashboard |
|
||
| Churn | Monthly |
|
||
| Hot spot | Quarterly |
|
||
| DORA | Monthly (team) |
|
||
| Bus factor | Quarterly |
|
||
|
||
## ❌ 안티패턴
|
||
- **Coverage target = 100%**: useless test.
|
||
- **Metric 가 target**: gaming.
|
||
- **Single metric judge**: 한 면만.
|
||
- **No context**: number 만 — meaning 없음.
|
||
- **개인 별 LOC**: bad incentive.
|
||
- **측정 + no action**: dashboard 의미 없음.
|
||
- **모든 코드 같은 standard**: critical 가 다름.
|
||
|
||
## 🤖 LLM 활용 힌트
|
||
- Complexity + coverage + churn = baseline.
|
||
- Hot spot = complexity × churn.
|
||
- DORA metrics 가 큰 picture.
|
||
- Goodhart — 신호 X target.
|
||
|
||
## 🔗 관련 문서
|
||
- [[Quality_Tech_Debt]]
|
||
- [[Quality_Refactoring]]
|
||
- [[Testing_Mutation_Testing]]
|