02. 메트릭 카탈로그
각 메트릭은 정의 → 연구 근거 → 계산 방법(빌드 없이) → 임계값 → finding 기록 방식 순으로 기술한다. 메트릭 ID는 <카테고리>-<번호> 형식이며 detail.csv의 metric_id 컬럼에 그대로 사용된다.
카테고리: SIZE(크기), CPLX(복잡도), ARCH(아키텍처/결합도), DUP(중복), SEC(보안), RES(자원/메모리), HYG(위생/잠재 위험)
현재 구현 상태는 jam metrics의 IMPL 컬럼이 원본이다. metrics v14.5.0 기준 카탈로그의 jam-lite 메트릭은 모두 구현되어 있다.
공통 점수 범위 근거 (metrics v14.5.0)
개별 룰은 아래 **근거** 블록의 정의·임계 근거를 따르고, 집계 모집단은 03-scoring-model.md §2.2를 따른다. 즉 production 파일의 CLOC만 사용하고, 카테고리의 구현된 룰이 지원하는 언어만 해당 카테고리의 eligible 분모에 포함한다. test/documentation/configuration/generated 및 미지원 언어 finding은 감사 출력에 보존하지만 점수에는 산입하지 않는다.
근거: ISO/IEC/IEEE 15939:2017(측정 정보 요구·범위·타당성), SonarSource *Setting the initial scope*(main/test 분리와 test의 source metrics/LOC 제외), SonarSource의 언어별 Quality Profile, El Emam et al.(2001, IEEE TSE)의 size confounding, Letouzey(2012, MTD@ICSE)의 SQALE remediation-cost density. 이 규칙은 룰이 보지 못하는 코드나 비제품 코드가 분모를 키워 점수를 인위적으로 올리지 못하게 한다.
SIZE — 크기 메트릭
SIZE-01: 파일 크기 (File CLOC)
- 정의: 파일별 코드 라인 수(주석/공백 제외 CLOC, 참고용으로 전체 LOC도 기록).
- 근거: 파일/모듈 크기와 결함 밀도의 상관은 고전적 결과다(Basili & Perricone 1984, *Software Errors and Complexity*). 대형 모듈은 이해 비용이 비선형적으로 증가한다. Google 스타일 가이드, SonarQube 기본 룰 등 산업 표준도 파일 크기 상한을 둔다.
- 계산: 라인 스캐너로 주석/공백 분류. 언어별 주석 문법은 discovery 단계의 휴리스틱과 경량 lexer 규칙을 사용한다.
- 임계값: 경고 500 CLOC, 위반 1000 CLOC, 심각 2000 CLOC (언어별 보정: 자동 생성 파일 제외 —
*.pb.go,*_gen.go, lock 파일 등). - finding:
file:1위치, 측정값 = CLOC.
SIZE-02: 함수 길이
- 정의: 함수/메서드별 라인 수.
- 근거: Lipow(1982), 그리고 Martin *Clean Code*(2008)의 "함수는 작아야 한다" 원칙. SonarSource 기본 임계값과 정렬.
- 계산: Go는 AST에서 함수 노드의 시작/끝 라인 차이를 사용한다. 그 외 지원 언어는
internal/lang경량 프론트엔드의 함수 span을 사용한다. - 임계값: 경고 60, 위반 120, 심각 300.
- finding: 함수 시작 라인.
SIZE-03: 매개변수 수
- 정의: 함수의 파라미터 개수.
- 근거: Chidamber & Kemerer(1994) 류의 인터페이스 복잡도 지표. 많은 파라미터는 응집 부족 신호(데이터 클럼프).
- 임계값: 경고 5, 위반 8.
SIZE-04: God File / God Type (책임 집중)
- 정의: 한 파일(또는 클래스/구조체)에 선언된 함수+타입 수가 과다하고, 프로젝트 내 다른 파일들로부터의 참조가 집중되는 경우.
- 근거: Riel(1996) *Object-Oriented Design Heuristics*의 God Class; Lanza & Marinescu(2006) *Object-Oriented Metrics in Practice*의 God Class 검출식(WMC 높음 + ATFD 높음 + TCC 낮음)을 정적 스캔 가능한 형태로 단순화.
- 계산: 파일별
선언 수 × 수신 참조(fan-in) 백분위가 모두 상위 5%면 플래그. 타입(god type)은 메서드 수 ≥ 30. Go=go/ast, TypeScript·Java·Python·C#·C++·Rust=tree-sitter(tsclass.EachClass, 테스트파일 제외·병렬). import 그래프 ARCH 메트릭과 LCOM4(중앙 필드로 연결된 대형 클래스를 응집적으로 봄)가 못 잡는 대형 TS 클래스를 크기로 잡는다(예: 메서드 320개 API 클라이언트). - 점수 반영(v10.0.0): god type은 모두 finding으로 보고하되, 메서드 수는 30~100 구간에서 약한 신호(정당한 fluent/builder API)이므로 ≥100메서드 극단 god type만 SIZE 카테고리 점수에 상한을 건다(1개→89, ≥2개→79; docs/03 §2.9). 30~100 구간은 밀도 점수만 받는다.
- finding: 파일 첫 선언 라인 / 타입·클래스 선언 라인.
SIZE-05: 사변적 일반화 (Speculative Generality) — opt-in
- 정의: 프로젝트 내부 어디에서도 참조되지 않는 exported 심볼. 애플리케이션에서는 사용처 없는 공개 API 표면 = 유지 비용만 있는 사변적 일반화다.
- 근거: Fowler (1999, *Refactoring*)의 Speculative Generality 냄새; AI의 과잉 API 생성 경향(docs/18 축 A). 라이브러리의 공개 API는 외부 소비가 정당하므로
jam.yaml의profile: application명시 시에만 측정한다(오탐 방지). - 계산(Go): production 파일의 exported 최상위 func/type/var/const 중 다른 파일(테스트 포함)에서 단어 경계 매칭으로 참조되지 않는 심볼.
Deprecated:문서 주석 심볼 제외. 상위 20건. - 임계값: info, confidence low(감점 없음, 캘리브레이션).
- finding: 선언 라인, Symbol = 심볼명.
CPLX — 복잡도 메트릭
CPLX-01: 순환 복잡도 (Cyclomatic Complexity, CCN)
- 정의: 함수별 독립 실행 경로 수.
- 근거: McCabe(1976), *A Complexity Measure*, IEEE TSE. CCN > 10을 위험 권고로 제시한 원전. NIST 235도 10–15 상한 권고.
- 계산: Go는 AST에서 분기 노드(if/for/range/case/&&/||)를 카운트한다. 그 외 지원 언어는 경량 lexer 프론트엔드가 if/for/while/case/catch/&&/||/삼항 등을 근사 카운트한다.
- 임계값: 경고 10, 위반 15, 심각 30 (McCabe 원전 + SonarQube 기본값 절충).
- 점수 반영(v12.0.0): 30~100 구간은 밀도 점수만 받지만, CCN≥100 함수(god function/component) 개수가 CPLX 카테고리에 상한을 건다(1개→89, ≥2개→79; docs/03 §2.10) — 대형 코드베이스에서 병적 함수가 density로 희석되는 것을 막는다.
- finding: 함수 시작 라인, 측정값 = CCN.
CPLX-02: 인지 복잡도 (Cognitive Complexity)
- 정의: 사람이 읽을 때의 난이도. 중첩에 누진 가중치를 주고, 선형 구조(switch 등)는 약하게 계산.
- 근거: Campbell(2018), *Cognitive Complexity — A new way of measuring understandability*, SonarSource 백서. CCN이 "테스트 용이성"은 재지만 "이해 난이도"를 못 잰다는 한계를 보완. AI 생성 코드의 깊은 중첩-들여쓰기 문제를 잡는 데 CCN보다 적합.
- 계산: SonarSource 명세의 증가 규칙(중첩 레벨당 +1 추가)을 Go AST와 경량 프론트엔드 위에 근사 구현한다.
- 임계값: 경고 15, 위반 25 (SonarQube 기본 15와 정렬).
CPLX-03: 최대 중첩 깊이
- 정의: 함수 내 블록 중첩의 최대 깊이.
- 근거: 들여쓰기 깊이는 가장 값싼 가독성 예측 지표 중 하나(Hindle et al. 2008, *Reading Beside the Lines: Indentation as a Proxy for Complexity Metrics*, ICPC).
- 임계값: 경고 4, 위반 6.
- 비고: CPLX-02와 중복되는 신호이므로 점수 가중치는 낮게, 위치 지목용으로 활용.
CPLX-04: Halstead Volume / 난이도 (보조)
- 정의: 연산자/피연산자 수 기반 어휘량.
- 근거: Halstead(1977), *Elements of Software Science*. 단독으로는 비판이 많으나(Shepperd 1988) Maintainability Index의 입력으로 필요.
- 계산: Go scanner 또는 언어 공통 경량 lexer 토큰으로 연산자/피연산자 집합(n1, n2)과 총량(N1, N2)을 산출하고,
Volume = (N1+N2) × log2(n1+n2)로 계산한다. import/package/include 보일러플레이트는 제외한다. - 활용: 직접 감점 없음. 큰 파일의 보조 정보 finding과 CPLX-05의 입력으로 사용한다.
CPLX-05: 유지보수성 지수 (Maintainability Index, MI)
- 정의:
MI = 171 − 5.2·ln(HV) − 0.23·CCN − 16.2·ln(LOC)(Visual Studio 정규화: 0–100 스케일). - 근거: Oman & Hagemeister(1992); Coleman et al.(1994), *Using Metrics to Evaluate Software System Maintainability*, IEEE Computer. Microsoft Visual Studio가 채택한 산업 표준.
- 계산: 파일 단위 Halstead Volume, 평균 함수 CCN, CLOC를 사용해 0–100으로 정규화한다. 함수가 없는 파일은 평균 CCN 1로 처리한다.
- 임계값: 낮을수록 위험. 경고 20 이하, 위반 10 이하. 점수 기여는 소액 debt(경고 5분, 위반 10분)로 제한해 SIZE/CPLX-01~03과의 이중 감점을 줄인다.
CPLX-06: 변경 핫스팟 (Change Hotspot) — opt-in
- 정의: git 이력의 변경 빈도와 정적 복잡도가 동시에 높은 파일. 결함·수정 비용이 집중되는 리팩터링 1순위 후보다.
- 근거: Nagappan & Ball (2005, *Use of Relative Code Churn Measures to Predict System Defect Density*, ICSE) — 상대 churn은 정적 지표 단독보다 결함 밀도 예측력이 높다; Tornhill (2015, *Your Code as a Crime Scene*)의 hotspot(churn × complexity) 방법론.
- 계산:
jam.yaml에churn.enabled: true가 있고 스캔 루트가 git 저장소일 때만 실행(ARCH-09처럼 미설정 시 무발화). 최근 12개월git log --numstat의 파일별 커밋 수 × 파일 복잡도 상위 파일을 보고. - 임계값: info 등급 고정(감점 없음). 이력은 환경 의존적이라 점수에 산입하지 않는 순수 우선순위 신호다.
- finding: 파일 위치, value = 핫스팟 점수(변경 횟수 × 복잡도).
CHURN-01: 단명 코드율 (Short-Lived Code Ratio) — opt-in
- 정의: 추가된 라인 중 14일 안에 같은 파일이 재수정된 비율(커밋 단위 근사, blame 불사용). "빠른 생성 후 빠른 재작업"은 AI 협업 생산성의 핵심 정직 신호다(18-ax-ai-productivity-metrics.md 축 B).
- 근거: GitClear (2024/2025, *AI Copilot Code Quality*) — AI 도입 후 2주 내 재수정 코드 비율 상승 실증; Tornhill (2015). 카테고리는 CPLX-06과 같은 CPLX(churn 인프라 공유)로 등록한다.
- 계산: CPLX-06과 동일 게이트(
churn.enabled+ git 저장소). 최근 12개월 커밋의 파일별 추가 라인 시계열에서, 다음 수정까지 14일 미만인 추가분을 단명으로 집계. 최근 14일 내 추가분은 아직 판정 불가라 분모에서 제외. 프로젝트 합계(적격 ≥500줄)와 재작업 집중 파일(적격 ≥100줄·비율 ≥30%, 상위 5개)을 보고. - 임계값: info·confidence low 고정(감점 없음) — 이력은 환경 의존적이다.
- finding: 프로젝트 요약(최악 파일 앵커) + 파일별, value = 단명 비율 %.
CHURN-02: AI 귀속 커밋 비율 (AI-Attributed Commit Ratio) — opt-in
- 정의:
Co-Authored-By:(Claude/Copilot/ChatGPT/Gemini/Cursor/Codex/Devin/Aider/Windsurf 등)·"Generated with" trailer가 있는 커밋의 비율. 품질 판단이 아니라 CHURN-01·DUP를 AI/비-AI 코호트로 분해하기 위한 분모 신호다. - 근거: Forsgren et al. (2021, *The SPACE of Developer Productivity*, ACM Queue) — 귀속 없는 생산성 주장은 성립하지 않는다는 다차원 원칙. trailer는 git 표준 관행만 읽는다(벤더 판별·차단은 비목표, docs/18 §6).
- 계산: CHURN-01과 동일 게이트·동일 git 패스. 커밋 본문의 trailer 정규식 매칭.
- 임계값: info, confidence low. 항상 1건의 프로젝트 요약 finding(최다 변경 파일 앵커).
- finding: value = AI 귀속 커밋 수, 메시지에 총 커밋·비율 기록.
CHURN-03: 테스트 동반 변경율 (Test Co-Change Ratio) — opt-in
- 정의: 프로덕션 코드를 바꾼 커밋 중 테스트 파일을 함께 바꾼 비율. AI 대량 생성 커밋에서 급락하는 대표 프로세스 신호다.
- 근거: Zaidman et al. (2011, *Studying the co-evolution of production and test code*, EMSE). 테스트가 아예 없는 저장소는 HYG-06의 몫이므로 측정하지 않는다.
- 계산: CHURN-01과 동일 git 패스. 프로덕션 커밋 ≥10건일 때 1건의 요약 finding.
- 임계값: info, confidence low(감점 없음). value = 동반 변경 %.
CHURN-04: 거대 일괄 커밋 (Oversized Batch Commit) — opt-in
- 정의: 추가 라인 수가 분포 outlier(평균+2σ, 절대 하한 1,000줄 병용)인 커밋 — "AI 덤프" 배치 패턴.
- 근거: Purushothaman & Perry (2005, *Toward Understanding the Rhetoric of Small Source Code Changes*, IEEE TSE)의 소규모 변경 결함률 우위; Nagappan & Ball (2005).
- 계산: 동일 git 패스, 커밋 ≥10건일 때 상위 outlier 최대 3건 보고(최다 추가 파일 앵커).
- 임계값: info, confidence low(감점 없음). value = 추가 라인 수.
ARCH — 아키텍처/결합도 메트릭
ARCH-01: 모듈 간 순환 의존 (Dependency Cycles)
- 정의: 패키지/디렉터리/모듈 수준 import 그래프의 순환(SCC).
- 근거: Lakos(1996) *Large-Scale C++ Software Design* — 순환 의존은 빌드·테스트·이해 비용을 폭발시키는 1급 설계 결함. Parnas(1972)의 모듈 분해 원칙. MacCormack et al.(2006)은 순환 결합이 변경 전파를 증가시킴을 실증.
- 계산: import 구문 추출 → 프로젝트 내부 모듈로 해석(경로/모듈 메타파일 기반, 빌드 불필요) → Tarjan SCC. 크기 ≥ 2인 SCC가 순환. 사이클의 구체적 경로(A→B→C→A)를 최단 사이클로 복원해 보고. Rust는
mod.rsfacade/re-export 관례를 반영해 부모 모듈과 자식 모듈 사이의 계층 엣지는 제외하고, sibling/cross-branch 의존만 순환 후보로 본다. - 임계값: 순환 1건부터 위반. SCC 크기가 클수록 가중 감점.
- finding: 사이클을 구성하는 각 import 문의 파일:라인.
ARCH-02: 파일 수준 순환 (같은 모듈 내)
- 정의: 단일 패키지로는 합법이지만 파일 간 상호 참조가 얽힌 경우(심볼 수준 휴리스틱).
- 계산: 동일 디렉터리·언어 버킷 안에서 파일별 정의 심볼과 참조 심볼을 매칭해 파일 그래프를 만들고 SCC를 찾는다. Go는 표준 AST의 top-level 선언을 사용하고, 그 외 언어는 경량 함수 추출과 라인 기반 정의 휴리스틱을 사용한다.
- finding: 정보 등급(info), confidence medium. 설계 응집도 검토 신호이며 직접 debt는 없다.
ARCH-03: 불안정성/구심·원심 결합 (Fan-in / Fan-out, Instability)
- 정의: 모듈별 Ca(구심: 나를 import하는 수), Ce(원심: 내가 import하는 수), 불안정성 I = Ce/(Ca+Ce).
- 근거: Martin(1994/2002), *Agile Software Development* — Stable-Dependencies Principle. 안정적이어야 할 핵심 모듈이 불안정(많이 의존)하면 변경 파급이 크다.
- 계산: ARCH-01의 import 그래프 재사용. Ce > 20인 모듈을 과결합 플래그. "안정 모듈 → 불안정 모듈 의존"(SDP 위반) 에지를 보고. Rust는 ARCH-01과 동일하게 부모/자식 모듈 계층 엣지를 제외한다.
- 임계값: Ce 경고 15, 위반 25.
ARCH-04: 디렉터리 구조 응집 (Orphan / Flat 구조)
- 정의: (a) 수백 개 파일이 한 디렉터리에 평탄하게 존재, (b) 어디서도 import되지 않는 내부 모듈(데드 모듈).
- 근거: 모듈화 부재는 AI 산출물의 전형적 증상. 데드 코드는 Hygiene과도 연결.
- 임계값: 한 디렉터리에 소스 파일 50개 이상 경고.
ARCH-05: 허브형 의존 / God Component (Hub-Like Dependency)
- 정의: 구심 결합(Ca)과 원심 결합(Ce)이 동시에 비정상적으로 높은 모듈 — 많은 것에 의존하면서 많은 것이 의존하는 중앙 허브. 순환이 없어도(acyclic) 유지보수의 병목이 되는 전형적 결함.
- 근거: Arcelli Fontana, Pigazzini et al.(2017), *Arcan: A Tool for Architectural Smells Detection*, ICSA. 아키텍처 스멜은 코드 스멜과 독립적으로 유지보수성·테스트 용이성을 저해함이 실증됨(JSS 2025).
- 계산: ARCH-01의 import 그래프 재사용. 한 노드의 Ca와 Ce가 둘 다 그래프 평균+2σ(분포 outlier)를 넘고,
min(Ca, Ce)가 절대 하한(경고 4, 위반 8)을 넘으면 허브로 보고. 순수 source(높은 Ce, 낮은 Ca)나 순수 sink는 제외 — 양방향 모두 중심이어야 함. - 오탐 방지: (a) 노드 12개 미만 그래프는 평가하지 않음 — 작은 그래프에서 분포 기반 신호는 노이즈. (b) barrel/재export 파사드(
index.ts/js,mod.rs,lib.rs,__init__.py)와 아무것도 import하지 않는 순수 집약 노드는 제외(높은 fan-in이 설계상 의도이지 god component가 아님). - 임계값:
min(Ca, Ce)절대 하한 — 경고 4, 위반 8 (분포 outlier 조건과 AND 결합).
ARCH-06: 변경 파급 비용 (Propagation Cost)
- 정의: 전이적 의존(가시성 행렬)의 밀도 — 한 모듈을 변경했을 때 평균적으로 도달 가능한 시스템의 비율. 높을수록 작은 변경이 전체로 퍼짐.
- 근거: MacCormack, Rusnak, Baldwin(2006), *Exploring the Structure of Complex Software Designs*, Management Science. 느슨하게 결합된 설계는 변경 파급 비용이 최대 8배 낮음을 실증.
- 계산: import 그래프의 전이적 폐쇄(가시성 행렬, 대각선 포함)를 노드별 도달 가능 집합으로 구해 밀도 = Σ|도달가능|/N²를 산출. 백분율로 환산해 임계값과 비교. finding은 가장 많이 도달하는 노드(최대 VFO)에 앵커.
- 오탐 방지: 노드 12개 미만 그래프는 평가하지 않음. 그래프당 최대 1건.
- 임계값: 경고 30%, 위반 50%.
ARCH-07: 낮은 응집도 (LCOM4)
- 정의: 한 타입의 메서드들이 *상태를 공유하지 않는* 2개 이상의 독립 응집 그룹으로 나뉘는 경우 — 서로 무관한 책임이 한 타입에 묶여 있다는 신호(분리 권장). ARCH-01~06이 모듈 간 import 그래프(물리적 결합)를 보는 것과 달리, 타입 내부 구조를 보는 첫 메트릭.
- 근거: Hitz & Montazeri(1995), *Measuring Coupling and Cohesion in Object-Oriented Systems* — LCOM4: 메서드 간 "공통 필드 접근 또는 상호 호출"로 그래프를 만들고 연결 요소 수가 응집 그룹. >1이면 분리 가능. 데이터-클래스 오탐 비판(Etzkorn 등)을 게이트로 차단.
- 계산: 진짜 AST에서 타입 구조(필드·메서드·메서드별 수신자 필드 접근 + 형제 메서드 호출)를 추출 → union-find로 연결 요소 산출. Go = go/ast(패키지 단위). TypeScript = tree-sitter(grammar를 wasm32-wasi로 컴파일해 순수 Go wazero 런타임으로 구동, CGO_ENABLED=0 유지 — docs/08 M2). 언어 중립 평가기(
evaluateLCOM)가 두 백엔드를 동일 게이트로 채점. TS는this.<field>member 접근으로 필드/형제호출을 해석하고,class사전필터 + 파일 병렬 파싱으로 대형 모노레포에서도 빠르다. - 정확도 우선 게이트(오탐 0 목표): (a) 필드 접근 또는 형제 호출이 있는 메서드만 참여(고립 메서드 제외, 커넥터는 유지해 거짓 분리 방지), (b) 실질 클러스터(≥2 메서드 + 실제 로직 메서드 ≥1 — 필드 2개↑ 접근 또는 형제 호출)가 ≥2개일 때만 발화 → getter/setter 쌍·DTO·데이터 클래스는 구조적으로 제외, (c) 참여 메서드 ≥5·필드 ≥4, (d) 임베디드 필드·승격 메서드 무시(과소보고), 테스트 파일 제외.
- finding: 타입 선언 위치, 경고 등급·확신 medium, 측정값 = 연결 요소 수. 코퍼스(jam/govwa/go-cleanhttp 등 실 Go 프로젝트) 오탐 0건 검증.
ARCH-08: 깊은 상속 (Deep Inheritance, DIT)
- 정의: 한 클래스의 상속 체인 깊이(DIT). 깊을수록 메서드의 실제 동작이 여러 조상에 흩어져 이해·변경이 어렵다.
- 근거: Chidamber & Kemerer(1994) — DIT는 대표적 OO 결합/복잡도 지표. NDepend 등 산업 도구도 깊은 상속을 경고한다.
- 계산: TypeScript·Java·C#·C++를 tree-sitter로 파싱해 클래스→상위클래스 맵을 만들고, 프로젝트 내부에서 모호하지 않게 해석되는 조상만 따라가며 깊이를 센다. 외부(프레임워크 베이스)·동명 모호 조상은 체인을 끝내 과대계상하지 않는다. Go(상속 없음)·Python(다중상속) 제외.
- 임계값: 내부 상속 깊이 ≥5 → 경고. (외부 조상 미포함이라 5는 이미 깊은 커스텀 계층.)
- finding: 클래스 선언 라인, 측정값 = DIT.
ARCH-09: 레이어 규칙 위반 (Layer Rule Violation)
- 정의: 사용자가 jam.yaml에 선언한 아키텍처 레이어 정책(
arch.layers+arch.rules)을 어긴 import — 예:domain레이어가infra레이어에 의존하지 못하도록 금지했는데 실제로 의존하는 경우. ARCH-01~08이 구조에서 결합·순환·응집을 통계적으로 추론하는 것과 달리, ARCH-09는 사용자가 선언한 규칙 자체가 근거이므로 위반은 항상 확정적이다. - 근거: Clements, P. et al.(2010), *Documenting Software Architectures: Views and Beyond* — module viewtype(레이어드 뷰)은 허용된 의존 방향을 명시적으로 문서화하고 이를 어기는 의존을 아키텍처 위반으로 취급한다.
- 측정: jam.yaml
arch.layers(레이어 이름 → glob 패턴 목록, 선언 순서 보존)와arch.rules(- deny: from -> to,reason권장)를 선언하면, 언어별 import 그래프의 모든 edge에 대해 두 끝점의 파일을 레이어에 매핑한다. 한 파일이 여러 레이어 패턴에 매칭되면 가장 긴 패턴이 우선하고, 길이가 같으면 선언 순서가 빠른 레이어가 이긴다(결정성 보장). 매핑된 (from-레이어, to-레이어) 쌍이arch.rules의 deny 규칙과 일치하면 위반으로 finding을 만든다. - 미매핑 처리: 어느 레이어 패턴에도 매칭되지 않는 모듈이 관련된 edge는 보수적으로 건너뛴다(오탐보다 미탐을 우선).
- 미선언 시:
arch.layers/arch.rules를 선언하지 않은 프로젝트는 이 메트릭이 전혀 평가되지 않는다(finding 0, 점수 불변) — 기존 프로젝트 점수에 영향 없음. - 임계값/비용: 위반 1건 = severity violation, confidence high, DebtMinutes 45/edge. 선언된 규칙은 사용자 자신의 아키텍처 정책이므로 확신도를 낮출 근거가 없다.
- suppress 연동: metric(
ARCH-09)+path glob 조합의 suppress 규칙으로 특정 경로의 위반을 감사 가능하게 억제할 수 있다. - 한계: 레이어 매핑이 파일 경로 glob 기반이라, 파일 이동/리네임 시 레이어 소속이 바뀔 수 있다.
arch.rules에 존재하지 않는 레이어를 참조하면 경고 후 해당 규칙은 무시된다(jam.yaml 파싱 단계).
DUP — 중복 코드
DUP-01: 토큰 기반 중복 블록 (Type-1/Type-2 클론)
- 정의: 동일(Type-1) 또는 식별자/리터럴만 다른(Type-2) 코드 블록 쌍.
- 근거: 클론 분류 체계는 Roy & Cordy(2007), *A Survey on Software Clone Detection Research*. 검출 알고리즘은 PMD CPD/jscpd와 동일 계열인 Rabin-Karp 롤링 해시 토큰 매칭(Baker 1995, *On Finding Duplication and Near-Duplication in Large Software Systems*, WCRE). 클론과 결함의 관계는 Juergens et al.(2009), *Do Code Clones Matter?*, ICSE — 특히 "불일치하게 수정된 클론"이 결함을 유발.
- 계산: 비테스트 소스의 Go scanner 또는 언어 공통 경량 lexer 토큰 스트림 → 식별자/리터럴 정규화(Type-2 검출) → 최소 50토큰 및 6라인 이상인 롤링 해시 윈도 → 해시 충돌 시 실제 토큰 비교로 확정.
- 지표: 프로젝트 중복률 = 중복 블록에 포함된 라인 / DUP 대상 CLOC(비테스트, lexable 소스).
- 임계값: 중복률 경고 5%, 위반 10%, 심각 20% (SonarQube 기본 3%보다 완화 — 바이브코딩 현실 반영, 설정 가능).
- finding: 클론 쌍의 양쪽 위치 모두 기록(
file_a:line_a↔file_b:line_b).
DUP-02: 구조적 중복 (Type-3 근사, 선택)
- 정의: 일부 구문이 추가/삭제된 유사 함수. AST 서브트리 해시(Baxter et al. 1998)의 빌드 없는 근사로, 함수 단위 정규화 토큰 3-gram 구조 유사도를 사용한다.
- 계산: 비테스트 함수 span 안의 정규화 토큰을 3-gram set으로 바꾸고 Jaccard 유사도 72% 이상, 최소 60토큰·10라인 이상인 쌍을 보고한다. 완전 동일한 Type-1/2 클론은 DUP-01이 담당하므로 DUP-02에서 제외한다.
- finding: warning/medium. 값은 유사도 %, threshold는 72%.
SEC — 보안 메트릭 (정적 패턴 기반)
정밀 taint 분석은 비목표. AI가 자주 생성하는 명백한 취약 패턴을 AST 패턴 매칭으로 검출한다. 룰은 CWE ID와 매핑한다. 근거(총론): Pearce et al.(2022), *Asleep at the Keyboard? Assessing the Security of GitHub Copilot's Code Contributions*, IEEE S&P — AI 생성 코드의 ~40%가 취약 시나리오에서 CWE 해당. Perry et al.(2023), *Do Users Write More Insecure Code with AI Assistants?*, CCS — AI 보조 시 더 취약한 코드를 쓰면서 더 안전하다고 믿는 경향.
SEC-01: 하드코딩된 시크릿 (CWE-798)
- 계산: (a) 엔트로피 기반(Shannon entropy ≥ 4.5인 고엔트로피 문자열 리터럴 + 키워드 컨텍스트), (b) 알려진 토큰 포맷 정규식(AWS
AKIA…, GitHubghp_…, private key 헤더 등). truffleHog/gitleaks의 공개 룰셋 접근법 차용. - 오탐 제어: 테스트/fixture/문서/example 경로와 명백한 placeholder·redacted·
{env.VAR}예시는 warning/low로 낮춰 등급 hard cap을 만들지 않는다. URL·파일 경로·placeholder 변수명은 시크릿 값으로 보지 않는다. - 임계값: 1건부터 심각.
SEC-02: 인젝션 위험 문자열 결합 (CWE-89/78/79/943)
- 계산: SQL 키워드를 포함한 문자열과 변수의 결합/포매팅이 쿼리 실행 함수 인자로 흐르는 패턴(함수 단위 로컬 추적),
exec/system/셸 호출에 비리터럴 인자, HTML 템플릿에 unescaped 삽입 패턴. NoSQL/JS 인젝션(CWE-943, v11.0.0): MongoDB$where/$function/$accumulator에 동적 문자열(템플릿${}·concat)이 전달되는 패턴(JS/TS, critical) — SQL 키워드 룰이 못 잡던{$where: \…${userInput}…\}류. - 비고: 함수 경계를 넘는 추적은 안 함 → 보수적으로 "위반" 대신 "경고" 등급, 단 직접 결합은 위반.
SEC-03: 취약 암호/난수 (CWE-327/338)
- 계산: MD5/SHA1/DES 사용, 보안 문맥에서
math/rand·Math.random사용, TLS 검증 비활성화(InsecureSkipVerify: true,verify=False,rejectUnauthorized: false). - 오탐 제어: 테스트/fixture/문서/example 경로의 TLS 검증 비활성화 예시는 warning/low로 낮춘다. 또한 HMAC 맥락의 SHA-1은 flag하지 않는다(v11.0.0) — 파일이
hmac모듈을 쓰면 SHA-1은 키 기반 HMAC digest로 안전(HMAC-SHA1은 깨지지 않음; CWE-327은 평문 해시 대상). 예: itsdangerousHMACAlgorithm. MD5는 맥락 무관하게 계속 flag.
SEC-04: 위험 역직렬화/동적 실행 (CWE-502/94)
- 계산:
eval,pickle.loads,yaml.load(unsafe loader),child_process.exec,execSync,new Function, Pythonos.system·subprocess.*(shell=True)등 언어별 싱크 목록. 싱크 인자가 비리터럴(변수·호출 — 예:eval(req.body.x))이면 확정 인젝션 경로로 보고critical(등급 상한 C), 문자열/숫자 리터럴만 들어가면(예:new Function('return 1'))violation. 셸을 거치지 않는spawn/execFile(배열 인자)은 안전 관용구라 일부러 제외.
SEC-05: 민감 정보 로깅/노출 (CWE-532)
- 계산: 로깅 호출 인자에 password/token/secret 계열 식별자가 직접 등장.
SEC-06: 클라이언트 인젝션 / DOM XSS (CWE-79)
- 계산: JS/TS에서
dangerouslySetInnerHTML(React 자동 이스케이프를 의도적으로 우회하는 싱크 — 항상 보고), 그리고.innerHTML/.outerHTML대입·document.write(·.insertAdjacentHTML(에 비리터럴 값이 전달되는 경우(violation, 확신 medium). 문자열 리터럴 마크업은 안전하므로 제외. - 근거: CWE-79; OWASP DOM-based XSS Prevention Cheat Sheet. 이 싱크들은 신뢰 불가 값이 살균 없이 DOM에 들어가는 표준 XSS 경로다.
SEC-07: 경로 조작 / Path Traversal (CWE-22)
- 계산(2단): (1) 라인 기반 — JS/TS 파일 싱크(
readFile/readFileSync/createReadStream/createWriteStream/writeFile/sendFile/unlink/appendFile)의 인자가 비리터럴이고 사용자 입력 토큰(req/request/.params/.query/.body/process.argv/searchParams/headers/nextUrl/formData)을 포함할 때. recall 지향이라warning·확신 낮음(half-debt, worst-case 상한 미발동). (2) 함수 내 taint 분석(v8.10.0~) — 입력이 지역 변수에 담겨 다중 라인을 거쳐 싱크에 도달하는 흐름(라인 게이트가 놓치는 패턴)을 TS AST 데이터플로우로 추적. 확정된 흐름은violation·확신 높음으로 격상. 전파는 모든 호출에서 중단되어 새니타이저(path.basename등)가 자동으로 taint를 끊고, 인자에 소스 토큰이 직접 있으면 (1)의 몫이라 이중 보고 없음. - 근거: CWE-22. 입력-유도 경로가 파일 싱크에 흐르면 디렉터리 탈출 위험. 소스→싱크 taint 추적은 표준 SAST 기법이며, 확정 흐름은 토큰 근접 휴리스틱보다 강한 근거다.
- Python·Java 확장(v14.2.0, 캘리브레이션): 경로 싱크(Python
open/os.*/shutil.*, Javanew File/Paths.get/파일 스트림)에 동적 구성(결합·f-string·+) 인자가 전달되면 info·확신 낮음(감점 없음) 으로 보고. 라벨 벤치마크로 정밀도 검증 후 등급 승격을 결정한다.
SEC-08: SSRF / 서버측 요청 위조 (CWE-918)
- 계산(2단): (1) 라인 기반 — JS/TS HTTP 클라이언트(
fetch/axios/got/superagent,http(s).get/.request등)의 URL 인자가 비리터럴이고 SEC-07과 동일한 사용자 입력 토큰을 포함할 때(warning·확신 낮음). (2) 함수 내 taint 분석(v8.10.0~) — SEC-07과 동일하게 입력→지역변수→싱크 다중 라인 흐름을 추적해 확정 흐름을violation·확신 높음으로 격상(새니타이저·호출 경계에서 전파 중단, 이중 보고 없음). - 근거: CWE-918; OWASP SSRF Prevention Cheat Sheet. 동적 URL 다수는 양성이라 (1)은 입력 토큰을 요구하고, (2)는 추적된 소스→싱크 흐름만 확정 보고한다.
- Python·Java 확장(v14.2.0, 캘리브레이션): HTTP 클라이언트(Python
requests/urllib/httpx, Javanew URL/HttpClient/HttpURLConnection)에 동적 구성 URL이 전달되면 info·확신 낮음(감점 없음) 으로 보고.
RES — 자원/메모리 관리
Go 도구이지만 대상 언어별 자원 관리 관용구를 점검한다. 정적 한계상 휴리스틱이며, 명백한 경우만 위반 처리.
RES-01: 자원 해제 누락
- 계산: 언어별 "획득→해제" 쌍 규칙. Go:
os.Open/sql.Open/net.Dial·DialTimeout·Listen*/tls.Dial·Listen등 이후 동일 함수 내defer x.Close()부재. Python:open()이with밖에서 사용. C/C++:malloc/free,new/delete,fopen/fclose짝 불일치(함수 로컬만). - 근거: 자원 누수 패턴의 정적 검출은 FindBugs(Hovemeyer & Pugh 2004) 계열의 고전적 버그 패턴 접근.
RES-02: 무한정 동시성/큐
- 계산: 루프 안에서 한도 없는 고루틴/스레드/Promise 생성(세마포어·워커풀 부재 휴리스틱), 버퍼 무제한 채널+무한 생산 패턴. "정보~경고" 등급.
- 근거: 제한 없는 자원 할당/동시 실행은 MITRE CWE-770(Allocation of Resources Without Limits or Throttling) 및 CWE-400(Uncontrolled Resource Consumption)의 대표 패턴이다. JAM은 정확한 부하 모델을 만들지 않고, 명백한 무제한 생성만 경고로 기록한다.
RES-03: 전역 가변 상태
- 계산: 패키지/모듈 수준 가변 전역 변수 수. 경고 등급.
- 근거: Martin(1994/2002)의 응집·결합 원칙에서 전역 가변 상태는 캡슐화를 약화시키고 변경 파급을 키우는 설계 냄새다. 동시 실행 환경에서는 공유 가변 상태가 CWE-362(Race Condition) 계열 위험으로 이어질 수 있으므로, JAM은 보수적으로 경고만 부여한다.
RES-04: 네트워크 타임아웃 부재
- 정의: 타임아웃/데드라인 없이 원격 자원에 동기 접근하는 코드. 원격 측 지연·행이 그대로 호출자 스레드/고루틴 고갈로 이어진다.
- 계산(Go, 비테스트): (1) 기본 공유 클라이언트를 쓰는
http.Get/Post/PostForm/Head호출, (2)Timeout필드 없는http.Client복합 리터럴. 컨텍스트 데드라인으로 별도 제한됐을 가능성이 있어 confidence low. - 임계값: info 등급 캘리브레이션(감점 없음) — CPLX-04와 같은 방식으로, 코퍼스에서 정밀도가 검증되기 전까지 신호만 보고한다.
- 근거: MITRE CWE-1088(Synchronous Access of Remote Resource without Timeout); CWE-400(Uncontrolled Resource Consumption). 안정성 패턴 문헌(Nygard, *Release It!*, 2007)의 timeout 패턴은 통합 지점 장애 전파를 막는 1차 방어선이다.
- finding: 호출/리터럴 위치, Symbol =
http.<Func>또는http.Client.
HYG — 위생/잠재 위험
HYG-01: 삼킨 예외 (빈 catch / 무시된 에러)
- 계산: 빈
catch/except: pass, Go의_ = err및if err != nil {}빈 블록, 에러 반환값 미사용(AST 수준 확인 가능 범위). - 근거: Yuan et al.(2014), *Simple Testing Can Prevent Most Critical Failures*, OSDI — 치명적 장애의 다수가 부실한 에러 처리에서 비롯.
- 오탐 제어: 문서와 테스트 경로는 제외한다. 예제 코드 블록과 테스트 fixture의 빈 catch는 제품 코드 violation으로 보지 않는다.
- 임계값: 건당 위반.
HYG-02: 죽은 코드
- 계산: 프로젝트 내부에서 한 번도 참조되지 않는 비공개 함수/타입(export 규칙 언어별 적용), 도달 불가 코드(return 뒤 구문).
- 근거: MITRE CWE-561(Dead Code)은 실행되지 않는 코드가 유지보수 비용을 높이고 실제 결함/취약 경로를 숨길 수 있음을 명시한다. 공개 API·리플렉션 오탐을 줄이기 위해 비공개 심볼 중심으로만 기록한다.
- 비고: 공개 API·진입점·리플렉션 고려로 보수적 판정(비공개 심볼만).
HYG-03: 방치된 표식 (TODO/FIXME/HACK/XXX)
- 계산: 주석 토큰 내 표식 카운트. 건당 정보, 밀도(건/KLOC) 기준 경고.
- 근거: Potdar & Shihab(2014), *An Exploratory Study on Self-Admitted Technical Debt*, ICSME — SATD(자인된 기술부채) 개념.
HYG-04: 매직 넘버/매직 문자열 밀도
- 계산: 지원 소스 파일에서 숫자·문자열 리터럴을 카운트하고 CLOC 기준 KLOC당 밀도로 정규화한다. import/include, Go import block, 명명 상수 선언(
const,static final,constexpr등), 테스트 파일, config 파일, 관용 숫자(0/1/2/100 등), 자연어 문자열, 짧은 문자열·URL·경로 문자열은 제외한다. - 근거: SonarSource rule S109("Magic numbers should not be used")와 Google/언어별 스타일 가이드의 명명 상수 권고를 따른다. JAM은 단일 리터럴 사용을 결함으로 보지 않고, 파일 단위 밀도와 최소 건수 조건으로 과도한 암묵 값을 경고한다.
- 임계값: 40건/KLOC 이상 + 최소 8건이면 info, 120건/KLOC 이상 + 최소 15건이면 warning. confidence는 medium.
- finding: 파일 단위
HYG-04, 위치는 첫 매직 리터럴 라인, value는 건/KLOC.
HYG-05: 주석 밀도 이상치
- 계산: 파일별 주석/CLOC 비율의 극단치와 Go 공개 top-level API의 doc comment 누락률을 측정한다. 테스트 파일은 제외한다.
- 근거: Maintainability Index 계열(Oman & Hagemeister 1992; Coleman et al. 1994)은 주석 비율을 유지보수성 신호로 다뤘고, Go의 공식 문서화 관례는 exported API 문서 주석을 요구한다. JAM은 "주석이 많을수록 좋다"가 아니라 과도하게 낮거나 높은 이상치만 기록한다.
- 임계값: 주석/CLOC 비율 > 1.5이고 CLOC 20 이상이면 warning/high. 주석/CLOC 비율 < 0.02이고 CLOC 80 이상이면 info/medium, 주석 0줄 + CLOC 200 이상이면 warning. Go 공개 API는 exported top-level 선언이 3개 이상이고 누락률 50% 이상 또는 누락 5개 이상이면 warning/high.
- finding: 파일 단위
HYG-05또는 Go 공개 API 요약Symbol=(public-api).
HYG-06: 테스트 부재 신호
- 계산: 빌드 없이 가능한 근사 — 테스트 파일 수/소스 파일 수 비율, 테스트 0인 디렉터리 비율. 정보 등급(커버리지 측정은 비목표).
- 근거: ISO/IEC/IEEE 29119는 테스트 프로세스를 별도 품질 활동으로 다루며, Yuan et al.(2014)은 단순한 테스트가 치명 장애를 상당 부분 예방할 수 있음을 보였다. JAM-lite는 테스트 실행/커버리지를 측정하지 않으므로 결함 감점이 아니라 "부재 신호"만 정보 등급으로 남긴다.
HYG-07: 파스 실패 (Parse Failure)
- 정의: 분석기가 AST를 만들 수 없는 소스 파일. 파스 실패가 있으면 AST 기반 메트릭이 누락될 수 있으므로 스캔 불완전성 자체를 finding으로 기록한다.
- 계산: Go 파일은 표준 라이브러리
go/parser로 재파싱해 문법 오류 위치를 수집한다(위반·debt 60분·확신 높음). v14.2.0부터 tree-sitter 언어(TS·Java·Python·C#·C++·Rust) 는 명백히 깨진 파스 트리(루트 ERROR 등 보수 판정)를 info·확신 낮음(감점 없음) 으로 보고한다 — 캘리브레이션 후 등급 승격을 결정한다. - 근거: ISO/IEC 25010의 maintainability에는 analysability가 포함된다. 파싱 불가능한 소스는 AST 기반 지표의 관측 가능성을 낮추므로, JAM은 이를 일반 품질 finding이자 strict 모드 실패 조건으로 기록한다.
- 임계값: 1건부터 위반. debt 60분, confidence high.
- finding: 파싱 오류의
file:line:column위치, 메시지에 첫 파서 오류를 기록. - 점수 영향: HYG finding으로 산입하며, violation 등급(Go) HYG-07이 1건 이상이면 등급은 최대 C로 제한한다(info 등급 다언어 신호는 상한 미발동).
jam scan --strict에서는 리포트 작성 후 exit 3을 반환한다.
바이브코딩 위생 지표(HYG-08~11, v12.1.0): 아래 4개는 AI 에이전트 산출물에서 특히 흔한 잔재를 잡기 위해 v12.1.0에서 추가됐다. 모두 외부 도구 없이 자체 구현한 라인/토큰 스캔이며 빌드 없이 여러 언어에 공통 적용된다. 산식/임계/가중치/캡은 바뀌지 않으므로(새 메트릭 추가) 기존 지표 점수는 불변이고, 16종 코퍼스 점수도 모두 그대로다(검증: best/stress 12종 직접 스캔, 나머지 4종은 SEC 상한으로 C 고정).
HYG-08: 주석 처리된 코드 (Commented-Out Code)
- 정의: 삭제되지 않고 주석으로 남겨진 코드 블록. HYG-02(죽은 코드)가 파서가 보는 미참조 심볼을 잡는다면, HYG-08은 파서조차 보지 못하는 "주석 속 코드"를 잡는다 — 에이전트가 로직을 갈아엎으며 옛 버전을 주석으로 남기는 전형적 잔재.
- 근거: SonarSource rule S125("Sections of code should not be commented out"); Martin *Clean Code*(2008) — 주석 처리된 코드는 버전 관리로 대체돼야 한다. CWE-561(Dead Code)과 인접.
- 계산: 전체 라인 주석(
//·#)의 연속 런에서 코드로 보이는 라인(문장 종결;/{/}, 대입, 호출, 제어 키워드 + 코드 구두점/연산자) 비율을 계산한다. 코드 유사 라인 ≥3줄이고 비율 ≥0.6이면 블록당 1건. 문서 주석(///·//!·shebang)·트레일링 주석·블록 주석은 참여하지 않고, SATD 표식(HYG-03)·마크다운/문서 태그(·@param등)·lint pragma를 포함한 런과 선언 직전 doc 주석(godoc/pydoc 관례, 비율 <0.9)은 제외한다. 테스트 경로 제외. - 임계값: warning, confidence medium (다언어 공통 휴리스틱).
- finding: 블록 시작~끝 라인, value = 코드 유사 라인 수.
HYG-09: 미구현 스텁 (Unimplemented Stub)
- 정의: 제품 코드에 남은 "아직 구현되지 않음" 마커. HYG-03(SATD)이 주석 표식을 본다면, HYG-09는 코드 레벨 미구현 마커를 본다 — 에이전트가 API 표면만 세워두고 채우지 않은 흔적.
- 근거: SonarSource rule S3717("Track uses of NotImplementedException"); Potdar & Shihab(2014) SATD 개념의 코드 레벨 연장.
- 계산(비테스트): 언어별 명시 마커 — Rust
todo!()/unimplemented!()(high), C#throw new NotImplementedException(high, VS 자동 생성 스텁), Gopanic("…not implemented/unimplemented/TODO…")(high) /errors.New·fmt.Errorf동일 문구(medium), JS/TSthrow new Error("…not implemented/TODO…")(high), Javathrow new UnsupportedOperationException("…not implemented/TODO…")(high). 마커 문구 게이트가 없는 언어 관용구는 제외한다: Pythonraise NotImplementedError()무인자형은 비공식 추상 메서드 관용구(하위 클래스가 오버라이드하는 베이스 메서드)이므로 flag하지 않고, 문구를 담은raise NotImplementedError("…not implemented…")만 medium으로 잡는다(@abstractmethod/@overload문맥은 추가로 제외). Java의 무인자UnsupportedOperationException도 optional-operation 관용구라 제외한다. 주석 안 마커는 제외. - 임계값: warning. 명시 스텁 매크로/예외는 high, 문구 의존 관용구는 medium.
- finding: 마커 라인, Symbol = 마커 종류.
HYG-10: 디버그 출력 잔재 (Leftover Debug Output)
- 정의: 제품 코드에 남은 디버그 전용 호출. 함수 동작을 들여다보려고 넣은 스캐폴딩을 제거하지 않은 잔재.
- 근거: MITRE CWE-489(Active Debug Code) — 남겨진 디버그 코드는 정보 노출/로그 노이즈로 이어지는 실제 약점. SonarSource S106/S4507 계열.
- 계산(비테스트): 디버그로만 쓰이는 마커만 1건부터 warning으로 잡는다 — Rust
dbg!(, JS/TSconsole.debug/console.trace/console.dir, Java.printStackTrace(), C#Debugger.Break()/Console.WriteLine("DEBUG…"), Pythonbreakpoint()/pdb.set_trace(). 일반 콘솔 출력(console.log·print·fmt.Print*·println!·System.out.*·Console.Write*)은 의도적으로 제외 — 이들은 진짜 이중 용도라(CLI 도구의 stdout은 곧 산출물; 예: sharkdp/hyperfine은 라이브러리 모듈에서println!로 결과를 출력) flag하면 깨끗한 CLI 프로젝트에 오탐이 난다. Go는 디버그 전용 표준 호출이 없어(fmt.Print*·log.*모두 정당) 대상 아님, C/C++도 stdout이 표준 인터페이스라 제외. 주석 안 마커는 제외. - 임계값: warning, confidence medium.
- finding: 마커 라인, Symbol =
(debug).
HYG-11: 명명 규칙 비일관성 (Naming Convention Inconsistency)
- 정의: 한 언어 안에서 함수/메서드 명명 스타일(snake_case vs camelCase)이 섞이는 현상. 세션마다 스타일이 표류하는 에이전트 코딩의 대표 증상으로, 함수 단위 임계값으로는 잡히지 않는다.
- 근거: Deissenboeck & Pizka(2006), *Concise and consistent naming*, Software Quality Journal — 식별자의 일관성이 가독성·유지보수성의 1차 결정 요인. Butler et al.(2010) CSMR — 식별자 품질과 코드 품질의 상관.
- 계산: 언어 버킷별 비테스트 함수 이름을 snake_case 계열 vs camelCase 계열로 분류한다(단일 단어·전대문자 상수·던더(
__x__)·비식별자는 스타일 신호가 없어 제외). Go=go/ast는 사용하지 않고 기타 언어는 경량 프론트엔드 이름을 쓴다. 분류 표본 ≥20이고 소수 스타일 비중 ≥25%면 언어당 1건. 임계값은 코퍼스 벤치마크로 검증(깨끗한 단일 스타일 프로젝트는 소수 비중 ~0%) — ConcentrationGuard와 같은 benchmark-derived 방식. Go는 제외(exported/unexported 대문자 관례가 언어 규칙이라 혼용이 정상), C/C++도 신뢰할 함수 이름 프론트엔드가 없고 생태계가 스타일을 실제로 섞어 제외. - 임계값: warning, confidence medium. value = 소수 스타일 %.
- finding: 해당 언어 첫 분류 함수 위치, Symbol =
(<언어>).
HYG-12: 정적 검사기 억제 (Blanket Static-Check Suppression)
- 정의: 타입/컴파일 검사기를 통째로 끄는 억제 지시. 에이전트가 타입 오류를 못 고칠 때 가장 빠른 "green 만들기"는 검사기를 침묵시키는 것이며, 이는 실제 결함을 숨기는 바이브코딩의 대표적 tell이다. HYG-03(주석 SATD 표식)이 못 보는 "검사기를 끈 행위" 자체를 코드 레벨에서 잡는다(v12.2.0).
- 근거: SonarSource rule S1309("@SuppressWarnings should not be used"); typescript-eslint
ban-ts-comment(‘@ts-ignore대신 이유를 강제하는@ts-expect-error를 쓰라’); mypywarn_unused_ignores. 억제는 Potdar & Shihab(2014)의 자인된 기술부채(SATD)의 코드 레벨 형태다. - 계산(비테스트): 무범위(uncoded) 억제만 잡는다 — TS/JS
@ts-ignore·@ts-nocheck, 코드 없는 Python# type: ignore·# mypy: ignore-errors, 코드 없는 C##pragma warning disable·#nullable disable, Java@SuppressWarnings("all"). 범위를 좁힌 규율 있는 억제는 제외한다:@ts-expect-error(오류가 사라지면 스스로 실패),# type: ignore[arg-type](검증된 특정 코드),#pragma warning disable CS8600(특정 규칙),@SuppressWarnings("unchecked")(특정 범주), 그리고 Rust#[allow(...)](타입 오류를 끌 수 없는 lint 제어라 정당한 사용이 흔함) — 모두 자체 근거를 갖는 형태다. Go/Rust/C/C++는 타입 오류를 통째로 끄는 관용구가 없어 대상 아님. - 임계값: 억제 1건부터 warning, confidence medium.
- finding: 억제 라인, Symbol = 억제 마커. value = 1.
- 캘리브레이션: 16종 코퍼스에 무범위 억제가 0건(itsdangerous의
# type: ignore6건은 모두[code]형태, sindresorhus/is의 52건은 라인 범위eslint-disable)이라 코퍼스에서 완전 dormant, 모든 코퍼스 점수 불변.
HYG-13: 단언 없는 테스트 (Assertion-Free Test)
- 정의: 검증 API를 한 번도 호출하지 않는 테스트 함수. 잘못된 결과에도 실패할 수 없어 "패닉이 안 나는지"만 증명하는 스모크 테스트 냄새다.
- 근거: van Deursen, Moonen, van den Bergh & Kok (2001, *Refactoring Test Code*, XP2001)의 테스트 냄새 분류; Bavota et al. (2012, ICSM)의 유닛 테스트 냄새 실증 연구.
- 계산(Go,
_test.go):Test*함수 본문에 (1)t.*호출(Error/Fatal/Run/Skip 등), (2)assert./require.라이브러리 호출, (3)t를 인자로 넘기는 위임 호출이 전혀 없으면 보고. 위임은 의도로 간주해 정밀도를 우선한다. - 임계값: info, confidence medium. 테스트 파일은 v13 production 모집단 규칙상 항상 점수 밖이므로 점수에 영향 없는 보고 신호다.
- finding: 테스트 함수 선언 위치, Symbol = 함수명.
HYG-14: 의존성 고정 위생 (Dependency Pinning Hygiene)
- 정의: 의존성 매니페스트가 재현 가능한 해석을 보장하지 않는 상태 — lockfile 부재, 상한 없는 유동 버전 범위.
- 근거: NIST SP 800-218 SSDF v1.1 PO.1(의존성 명세·무결성 관리); 재현 가능 빌드/SLSA 공급망 관행.
^/~범위는 npm semver 표준 관행이라 제외한다. - 계산: (1) lockfile 부재 —
package.json↔npm/yarn/pnpm/bun lockfile, poetrypyproject.toml↔poetry.lock,Pipfile↔Pipfile.lock,go.mod(require 존재 시)↔go.sum, 루트Cargo.toml↔Cargo.lock. (2)package.json의*·latest·상한 없는>=범위. - 임계값: info, confidence medium. 매니페스트는 configuration 역할이라 항상 점수 밖인 보고 신호다.
- finding: 매니페스트 파일 위치, value = 유동 버전 수(해당 시).
HYG-15: 잔여 플레이스홀더 (Placeholder Artifact)
- 정의: 제품 코드에 남은 생성 템플릿 흔적 — 자격증명 자리표시자(
your_api_key류),changeme/replace_me,lorem ipsum, AI 상용구("as an AI language model"). AI 산출물이 검토 없이 병합될 때 남는 대표 흔적이다(18-ax-ai-productivity-metrics.md 축 A). - 근거: Potdar & Shihab (2014)의 자인된 기술부채(SATD)의 생성 시대 변형; SEC-01(실제 비밀)과 상보 — 이 지표는 "비밀이 있어야 할 곳에 자리표시자가 남은" 반대 방향을 잡는다.
- 계산: production 역할 코드 파일의 라인에서 보수적 토큰 목록을 대소문자 무시 매칭. 테스트·example/sample/template/demo/docs 경로는 자리표시자가 의도이므로 제외. 라인당 최대 1건.
- 임계값: info·confidence medium 캘리브레이션(감점 없음). 코퍼스 오탐 측정 후 승격 검토.
- finding: 해당 라인, Symbol = 매칭 토큰.
HYG-16: 중복 설명 주석 (Redundant Comment)
- 정의: 바로 다음 코드 라인을 그대로 읽어주는 주석 — LLM 산출물의 대표 스타일. 주석은 "무엇"이 아니라 "왜"를 설명해야 한다.
- 근거: Coleman et al. (1994)의 주석 밀도 지표의 질적 보완; GitClear (2025)의 주석 인플레이션 관측.
- 계산: production 파일의 전체 라인 주석을 다음 코드 라인과 토큰 비교(식별자 camelCase/snake_case 분해, 불용어 제외)해 주석 토큰의 ≥70%가 코드 라인에 존재하면 중복으로 판정. SATD·주석 처리 코드(HYG-08 영역)·pragma 제외. 파일당 중복 주석 ≥5건일 때 요약 1건.
- 임계값: info, confidence low(감점 없음, 캘리브레이션). value = 중복 주석 수.
HYG-17: 미해석 내부 import (Broken Internal Import)
- 정의: 프로젝트 내부를 가리키는 상대 경로 import가 발견된 어떤 파일로도 해석되지 않는 경우 — 환각 참조·파일 이동 잔재의 코드 레벨 관측치.
- 근거: CWE-1078(코딩 표준 위반) 계열; 동적 로딩 언어(Python/JS)에서는 CI가 잡지 못하고 런타임까지 잠복한다.
- 계산: 상대 경로 import만 검사(JS/TS
./·../— 확장자·index 변형 시도, Python 선행 점 상대 import —__init__.py있는 패키지 한정). bare specifier·alias·tsconfig paths는 대상 밖이므로 alias 오탐이 구조적으로 없다. - 임계값: info, confidence low(감점 없음, 캘리브레이션).
- finding: import 라인.
HYG-18: 동어반복 테스트 (Tautological Test)
- 정의: 단언은 존재하지만 항상 참이라 어떤 결함도 잡지 못하는 테스트 — HYG-13(단언 부재)의 쌍이다.
- 근거: van Deursen, Moonen, van den Bergh & Kok (2001, *Refactoring Test Code*, XP2001); trivial/tautological test는 커버리지 수치만 부풀리는 대표 테스트 냄새다.
- 계산(테스트 파일): (1) 리터럴 참 단언 —
assert(true),assert True,assertTrue(true),expect(true).toBe(true)/.toBeTruthy(),require.True(t, true); (2) 동일 인자 동등 단언 —assertEqual(x, x), testifyEqual(t, x, x)등 두 인자가 텍스트로 동일한 경우. - 임계값: info, confidence medium. 테스트 파일은 점수 모집단 밖이라 점수 영향 없는 보고 신호다.
- finding: 해당 단언 라인.
메트릭 ↔ README 요구사항 매핑
| README 요구 | 메트릭 |
|---|---|
| 하나의 파일이 너무 큼 (BIG CLOC) | SIZE-01, SIZE-04 |
| SOLID 위반 구조 | SIZE-04(SRP), ARCH-03(DIP/SDP), CPLX-02 |
| 모듈 간 순환 참조 | ARCH-01, ARCH-02 |
| 함수가 지나치게 복잡 | CPLX-01, CPLX-02, CPLX-03 |
| 중복 코드 비율 | DUP-01, DUP-02 |
| 보안 무시 | SEC-01 ~ SEC-08 |
| 메모리/자원 관리 엉망 | RES-01 ~ RES-03 |
| 잠재적 위험 | HYG-01 ~ HYG-12 |
| 주석 처리된 코드 / 미구현 스텁 / 디버그 잔재 / 명명 비일관성 / 검사기 억제 (바이브코딩 잔재) | HYG-08, HYG-09, HYG-10, HYG-11, HYG-12 |
참고 문헌 (요약)
- McCabe, T. (1976). "A Complexity Measure." *IEEE TSE*.
- Halstead, M. (1977). *Elements of Software Science*. Elsevier.
- Basili, V. & Perricone, B. (1984). "Software Errors and Complexity." *CACM*.
- Oman, P. & Hagemeister, J. (1992); Coleman, D. et al. (1994). Maintainability Index. *IEEE Computer*.
- Chidamber, S. & Kemerer, C. (1994). "A Metrics Suite for Object Oriented Design." *IEEE TSE*.
- Martin, R. (1994/2002). Stability metrics (Ca/Ce/I). *Agile Software Development*.
- Baker, B. (1995). "On Finding Duplication and Near-Duplication in Large Software Systems." *WCRE*.
- Lakos, J. (1996). *Large-Scale C++ Software Design*. Addison-Wesley.
- Baxter, I. et al. (1998). "Clone Detection Using Abstract Syntax Trees." *ICSM*.
- Hovemeyer, D. & Pugh, W. (2004). "Finding Bugs is Easy." *OOPSLA*.
- Lanza, M. & Marinescu, R. (2006). *Object-Oriented Metrics in Practice*. Springer.
- MacCormack, A., Rusnak, J. & Baldwin, C. (2006). "Exploring the Structure of Complex Software Designs." *Management Science* (Propagation Cost — ARCH-06).
- Roy, C. & Cordy, J. (2007). "A Survey on Software Clone Detection Research."
- Hindle, A. et al. (2008). "Reading Beside the Lines: Indentation as a Proxy for Complexity Metrics." *ICPC*.
- Juergens, E. et al. (2009). "Do Code Clones Matter?" *ICSE*.
- Clements, P. et al. (2010). *Documenting Software Architectures: Views and Beyond*, 2nd ed. Addison-Wesley (Module viewtype/레이어드 뷰 — ARCH-09).
- Letouzey, J.-L. (2012). "The SQALE Method for Evaluating Technical Debt." *MTD Workshop*.
- Potdar, A. & Shihab, E. (2014). "An Exploratory Study on Self-Admitted Technical Debt." *ICSME*.
- Yuan, D. et al. (2014). "Simple Testing Can Prevent Most Critical Failures." *OSDI*.
- Arcelli Fontana, F., Pigazzini, I. et al. (2017). "Arcan: A Tool for Architectural Smells Detection." *ICSA* (Hub-Like Dependency — ARCH-05).
- Campbell, G. A. (2018). "Cognitive Complexity." SonarSource 백서.
- Pearce, H. et al. (2022). "Asleep at the Keyboard? Assessing the Security of GitHub Copilot's Code Contributions." *IEEE S&P*.
- Perry, N. et al. (2023). "Do Users Write More Insecure Code with AI Assistants?" *ACM CCS*.
- GitClear (2024). "Coding on Copilot: AI's Effect on Code Quality." 산업 보고서.
- MITRE CWE-770/CWE-400/CWE-362/CWE-561. Common Weakness Enumeration.
- ISO/IEC 25010:2023. *Systems and software engineering — Systems and software Quality Requirements and Evaluation (SQuaRE) — Product quality model* — maintainability/analysability.
- ISO/IEC/IEEE 29119. Software testing standards.
- SonarSource rule S109. "Magic numbers should not be used."
- SonarSource rule S125. "Sections of code should not be commented out." (HYG-08)
- SonarSource rule S3717. "Track uses of NotImplementedException." (HYG-09)
- MITRE CWE-489. Active Debug Code. (HYG-10)
- Deissenboeck, F. & Pizka, M. (2006). "Concise and consistent naming." *Software Quality Journal* 14(3). (HYG-11)
- SonarSource rule S1309. "@SuppressWarnings should not be used"; typescript-eslint
ban-ts-comment; mypywarn_unused_ignores. (HYG-12)