📦 deps(thirdparty): update snapshots
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1040 200" width="1040" height="200" role="img" aria-label="brooks-lint diagnostic pipeline">
|
||||
<g>
|
||||
<rect x="20" y="36" width="210" height="128" rx="16"
|
||||
fill="#fffdf8" stroke="#e3d8c2" stroke-width="1.5"/>
|
||||
<rect x="20" y="160" width="210" height="4" rx="2" fill="#3b82f6"/>
|
||||
<circle cx="125" cy="66" r="7" fill="#3b82f6"/>
|
||||
<text x="125" y="108" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="19" font-weight="700" fill="#2a2520">Your code</text>
|
||||
<text x="125" y="134" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="13" fill="#5c5347">PR or whole repo</text>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="283" y="36" width="210" height="128" rx="16"
|
||||
fill="#fffdf8" stroke="#e3d8c2" stroke-width="1.5"/>
|
||||
<rect x="283" y="160" width="210" height="4" rx="2" fill="#6366f1"/>
|
||||
<circle cx="388" cy="66" r="7" fill="#6366f1"/>
|
||||
<text x="388" y="108" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="19" font-weight="700" fill="#2a2520">12 classics</text>
|
||||
<text x="388" y="134" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="13" fill="#5c5347">Brooks · Fowler · Martin</text>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="547" y="36" width="210" height="128" rx="16"
|
||||
fill="#fffdf8" stroke="#e3d8c2" stroke-width="1.5"/>
|
||||
<rect x="547" y="160" width="210" height="4" rx="2" fill="#b07d2b"/>
|
||||
<circle cx="652" cy="66" r="7" fill="#b07d2b"/>
|
||||
<text x="652" y="108" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="19" font-weight="700" fill="#2a2520">12 decay risks</text>
|
||||
<text x="652" y="134" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="13" fill="#5c5347">R1–R6 · T1–T6</text>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="810" y="36" width="210" height="128" rx="16"
|
||||
fill="#fffdf8" stroke="#e3d8c2" stroke-width="1.5"/>
|
||||
<rect x="810" y="160" width="210" height="4" rx="2" fill="#22c55e"/>
|
||||
<circle cx="915" cy="66" r="7" fill="#22c55e"/>
|
||||
<text x="915" y="108" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="19" font-weight="700" fill="#2a2520">Cited findings</text>
|
||||
<text x="915" y="134" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="13" fill="#5c5347">Symptom → Remedy</text>
|
||||
</g>
|
||||
<g stroke="#b07d2b" stroke-width="2.5" fill="none" stroke-linecap="round">
|
||||
<line x1="236" y1="100" x2="273" y2="100"/>
|
||||
<polyline points="267,94 275,100 267,106" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<g stroke="#b07d2b" stroke-width="2.5" fill="none" stroke-linecap="round">
|
||||
<line x1="499" y1="100" x2="537" y2="100"/>
|
||||
<polyline points="531,94 539,100 531,106" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<g stroke="#b07d2b" stroke-width="2.5" fill="none" stroke-linecap="round">
|
||||
<line x1="763" y1="100" x2="800" y2="100"/>
|
||||
<polyline points="794,94 802,100 794,106" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,54 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1040 200" width="1040" height="200" role="img" aria-label="brooks-lint diagnostic pipeline">
|
||||
<g>
|
||||
<rect x="20" y="36" width="210" height="128" rx="16"
|
||||
fill="#fffdf8" stroke="#e3d8c2" stroke-width="1.5"/>
|
||||
<rect x="20" y="160" width="210" height="4" rx="2" fill="#3b82f6"/>
|
||||
<circle cx="125" cy="66" r="7" fill="#3b82f6"/>
|
||||
<text x="125" y="108" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="19" font-weight="700" fill="#2a2520">Tu código</text>
|
||||
<text x="125" y="134" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="13" fill="#5c5347">PR o repo completo</text>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="283" y="36" width="210" height="128" rx="16"
|
||||
fill="#fffdf8" stroke="#e3d8c2" stroke-width="1.5"/>
|
||||
<rect x="283" y="160" width="210" height="4" rx="2" fill="#6366f1"/>
|
||||
<circle cx="388" cy="66" r="7" fill="#6366f1"/>
|
||||
<text x="388" y="108" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="19" font-weight="700" fill="#2a2520">12 clásicos</text>
|
||||
<text x="388" y="134" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="13" fill="#5c5347">Brooks · Fowler · Martin</text>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="547" y="36" width="210" height="128" rx="16"
|
||||
fill="#fffdf8" stroke="#e3d8c2" stroke-width="1.5"/>
|
||||
<rect x="547" y="160" width="210" height="4" rx="2" fill="#b07d2b"/>
|
||||
<circle cx="652" cy="66" r="7" fill="#b07d2b"/>
|
||||
<text x="652" y="108" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="19" font-weight="700" fill="#2a2520">12 riesgos</text>
|
||||
<text x="652" y="134" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="13" fill="#5c5347">de deterioro · R1–T6</text>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="810" y="36" width="210" height="128" rx="16"
|
||||
fill="#fffdf8" stroke="#e3d8c2" stroke-width="1.5"/>
|
||||
<rect x="810" y="160" width="210" height="4" rx="2" fill="#22c55e"/>
|
||||
<circle cx="915" cy="66" r="7" fill="#22c55e"/>
|
||||
<text x="915" y="108" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="19" font-weight="700" fill="#2a2520">Hallazgos</text>
|
||||
<text x="915" y="134" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="13" fill="#5c5347">Síntoma → Remedio</text>
|
||||
</g>
|
||||
<g stroke="#b07d2b" stroke-width="2.5" fill="none" stroke-linecap="round">
|
||||
<line x1="236" y1="100" x2="273" y2="100"/>
|
||||
<polyline points="267,94 275,100 267,106" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<g stroke="#b07d2b" stroke-width="2.5" fill="none" stroke-linecap="round">
|
||||
<line x1="499" y1="100" x2="537" y2="100"/>
|
||||
<polyline points="531,94 539,100 531,106" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<g stroke="#b07d2b" stroke-width="2.5" fill="none" stroke-linecap="round">
|
||||
<line x1="763" y1="100" x2="800" y2="100"/>
|
||||
<polyline points="794,94 802,100 794,106" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,54 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1040 200" width="1040" height="200" role="img" aria-label="brooks-lint diagnostic pipeline">
|
||||
<g>
|
||||
<rect x="20" y="36" width="210" height="128" rx="16"
|
||||
fill="#fffdf8" stroke="#e3d8c2" stroke-width="1.5"/>
|
||||
<rect x="20" y="160" width="210" height="4" rx="2" fill="#3b82f6"/>
|
||||
<circle cx="125" cy="66" r="7" fill="#3b82f6"/>
|
||||
<text x="125" y="108" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="19" font-weight="700" fill="#2a2520">あなたのコード</text>
|
||||
<text x="125" y="134" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="13" fill="#5c5347">PR / リポジトリ全体</text>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="283" y="36" width="210" height="128" rx="16"
|
||||
fill="#fffdf8" stroke="#e3d8c2" stroke-width="1.5"/>
|
||||
<rect x="283" y="160" width="210" height="4" rx="2" fill="#6366f1"/>
|
||||
<circle cx="388" cy="66" r="7" fill="#6366f1"/>
|
||||
<text x="388" y="108" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="19" font-weight="700" fill="#2a2520">古典12冊</text>
|
||||
<text x="388" y="134" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="13" fill="#5c5347">Brooks · Fowler · Martin</text>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="547" y="36" width="210" height="128" rx="16"
|
||||
fill="#fffdf8" stroke="#e3d8c2" stroke-width="1.5"/>
|
||||
<rect x="547" y="160" width="210" height="4" rx="2" fill="#b07d2b"/>
|
||||
<circle cx="652" cy="66" r="7" fill="#b07d2b"/>
|
||||
<text x="652" y="108" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="19" font-weight="700" fill="#2a2520">12の劣化リスク</text>
|
||||
<text x="652" y="134" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="13" fill="#5c5347">R1–R6 · T1–T6</text>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="810" y="36" width="210" height="128" rx="16"
|
||||
fill="#fffdf8" stroke="#e3d8c2" stroke-width="1.5"/>
|
||||
<rect x="810" y="160" width="210" height="4" rx="2" fill="#22c55e"/>
|
||||
<circle cx="915" cy="66" r="7" fill="#22c55e"/>
|
||||
<text x="915" y="108" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="19" font-weight="700" fill="#2a2520">出典付きの指摘</text>
|
||||
<text x="915" y="134" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="13" fill="#5c5347">症状 → 対策</text>
|
||||
</g>
|
||||
<g stroke="#b07d2b" stroke-width="2.5" fill="none" stroke-linecap="round">
|
||||
<line x1="236" y1="100" x2="273" y2="100"/>
|
||||
<polyline points="267,94 275,100 267,106" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<g stroke="#b07d2b" stroke-width="2.5" fill="none" stroke-linecap="round">
|
||||
<line x1="499" y1="100" x2="537" y2="100"/>
|
||||
<polyline points="531,94 539,100 531,106" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<g stroke="#b07d2b" stroke-width="2.5" fill="none" stroke-linecap="round">
|
||||
<line x1="763" y1="100" x2="800" y2="100"/>
|
||||
<polyline points="794,94 802,100 794,106" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,54 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1040 200" width="1040" height="200" role="img" aria-label="brooks-lint diagnostic pipeline">
|
||||
<g>
|
||||
<rect x="20" y="36" width="210" height="128" rx="16"
|
||||
fill="#fffdf8" stroke="#e3d8c2" stroke-width="1.5"/>
|
||||
<rect x="20" y="160" width="210" height="4" rx="2" fill="#3b82f6"/>
|
||||
<circle cx="125" cy="66" r="7" fill="#3b82f6"/>
|
||||
<text x="125" y="108" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="19" font-weight="700" fill="#2a2520">당신의 코드</text>
|
||||
<text x="125" y="134" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="13" fill="#5c5347">PR / 전체 저장소</text>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="283" y="36" width="210" height="128" rx="16"
|
||||
fill="#fffdf8" stroke="#e3d8c2" stroke-width="1.5"/>
|
||||
<rect x="283" y="160" width="210" height="4" rx="2" fill="#6366f1"/>
|
||||
<circle cx="388" cy="66" r="7" fill="#6366f1"/>
|
||||
<text x="388" y="108" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="19" font-weight="700" fill="#2a2520">고전 12권</text>
|
||||
<text x="388" y="134" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="13" fill="#5c5347">Brooks · Fowler · Martin</text>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="547" y="36" width="210" height="128" rx="16"
|
||||
fill="#fffdf8" stroke="#e3d8c2" stroke-width="1.5"/>
|
||||
<rect x="547" y="160" width="210" height="4" rx="2" fill="#b07d2b"/>
|
||||
<circle cx="652" cy="66" r="7" fill="#b07d2b"/>
|
||||
<text x="652" y="108" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="19" font-weight="700" fill="#2a2520">12가지 리스크</text>
|
||||
<text x="652" y="134" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="13" fill="#5c5347">R1–R6 · T1–T6</text>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="810" y="36" width="210" height="128" rx="16"
|
||||
fill="#fffdf8" stroke="#e3d8c2" stroke-width="1.5"/>
|
||||
<rect x="810" y="160" width="210" height="4" rx="2" fill="#22c55e"/>
|
||||
<circle cx="915" cy="66" r="7" fill="#22c55e"/>
|
||||
<text x="915" y="108" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="19" font-weight="700" fill="#2a2520">출처 있는 진단</text>
|
||||
<text x="915" y="134" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="13" fill="#5c5347">증상 → 처방</text>
|
||||
</g>
|
||||
<g stroke="#b07d2b" stroke-width="2.5" fill="none" stroke-linecap="round">
|
||||
<line x1="236" y1="100" x2="273" y2="100"/>
|
||||
<polyline points="267,94 275,100 267,106" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<g stroke="#b07d2b" stroke-width="2.5" fill="none" stroke-linecap="round">
|
||||
<line x1="499" y1="100" x2="537" y2="100"/>
|
||||
<polyline points="531,94 539,100 531,106" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<g stroke="#b07d2b" stroke-width="2.5" fill="none" stroke-linecap="round">
|
||||
<line x1="763" y1="100" x2="800" y2="100"/>
|
||||
<polyline points="794,94 802,100 794,106" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,54 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1040 200" width="1040" height="200" role="img" aria-label="brooks-lint diagnostic pipeline">
|
||||
<g>
|
||||
<rect x="20" y="36" width="210" height="128" rx="16"
|
||||
fill="#fffdf8" stroke="#e3d8c2" stroke-width="1.5"/>
|
||||
<rect x="20" y="160" width="210" height="4" rx="2" fill="#3b82f6"/>
|
||||
<circle cx="125" cy="66" r="7" fill="#3b82f6"/>
|
||||
<text x="125" y="108" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="19" font-weight="700" fill="#2a2520">你的代码</text>
|
||||
<text x="125" y="134" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="13" fill="#5c5347">PR 或整个仓库</text>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="283" y="36" width="210" height="128" rx="16"
|
||||
fill="#fffdf8" stroke="#e3d8c2" stroke-width="1.5"/>
|
||||
<rect x="283" y="160" width="210" height="4" rx="2" fill="#6366f1"/>
|
||||
<circle cx="388" cy="66" r="7" fill="#6366f1"/>
|
||||
<text x="388" y="108" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="19" font-weight="700" fill="#2a2520">十二本经典</text>
|
||||
<text x="388" y="134" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="13" fill="#5c5347">布鲁克斯 · 福勒 · 马丁</text>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="547" y="36" width="210" height="128" rx="16"
|
||||
fill="#fffdf8" stroke="#e3d8c2" stroke-width="1.5"/>
|
||||
<rect x="547" y="160" width="210" height="4" rx="2" fill="#b07d2b"/>
|
||||
<circle cx="652" cy="66" r="7" fill="#b07d2b"/>
|
||||
<text x="652" y="108" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="19" font-weight="700" fill="#2a2520">十二类衰退风险</text>
|
||||
<text x="652" y="134" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="13" fill="#5c5347">R1–R6 · T1–T6</text>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="810" y="36" width="210" height="128" rx="16"
|
||||
fill="#fffdf8" stroke="#e3d8c2" stroke-width="1.5"/>
|
||||
<rect x="810" y="160" width="210" height="4" rx="2" fill="#22c55e"/>
|
||||
<circle cx="915" cy="66" r="7" fill="#22c55e"/>
|
||||
<text x="915" y="108" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="19" font-weight="700" fill="#2a2520">带出处的结论</text>
|
||||
<text x="915" y="134" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="13" fill="#5c5347">症状 → 对策</text>
|
||||
</g>
|
||||
<g stroke="#b07d2b" stroke-width="2.5" fill="none" stroke-linecap="round">
|
||||
<line x1="236" y1="100" x2="273" y2="100"/>
|
||||
<polyline points="267,94 275,100 267,106" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<g stroke="#b07d2b" stroke-width="2.5" fill="none" stroke-linecap="round">
|
||||
<line x1="499" y1="100" x2="537" y2="100"/>
|
||||
<polyline points="531,94 539,100 531,106" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<g stroke="#b07d2b" stroke-width="2.5" fill="none" stroke-linecap="round">
|
||||
<line x1="763" y1="100" x2="800" y2="100"/>
|
||||
<polyline points="794,94 802,100 794,106" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,54 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1040 200" width="1040" height="200" role="img" aria-label="brooks-lint diagnostic pipeline">
|
||||
<g>
|
||||
<rect x="20" y="36" width="210" height="128" rx="16"
|
||||
fill="#fffdf8" stroke="#e3d8c2" stroke-width="1.5"/>
|
||||
<rect x="20" y="160" width="210" height="4" rx="2" fill="#3b82f6"/>
|
||||
<circle cx="125" cy="66" r="7" fill="#3b82f6"/>
|
||||
<text x="125" y="108" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="19" font-weight="700" fill="#2a2520">你的程式碼</text>
|
||||
<text x="125" y="134" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="13" fill="#5c5347">PR 或整個儲存庫</text>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="283" y="36" width="210" height="128" rx="16"
|
||||
fill="#fffdf8" stroke="#e3d8c2" stroke-width="1.5"/>
|
||||
<rect x="283" y="160" width="210" height="4" rx="2" fill="#6366f1"/>
|
||||
<circle cx="388" cy="66" r="7" fill="#6366f1"/>
|
||||
<text x="388" y="108" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="19" font-weight="700" fill="#2a2520">十二本經典</text>
|
||||
<text x="388" y="134" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="13" fill="#5c5347">布魯克斯 · 福勒 · 馬丁</text>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="547" y="36" width="210" height="128" rx="16"
|
||||
fill="#fffdf8" stroke="#e3d8c2" stroke-width="1.5"/>
|
||||
<rect x="547" y="160" width="210" height="4" rx="2" fill="#b07d2b"/>
|
||||
<circle cx="652" cy="66" r="7" fill="#b07d2b"/>
|
||||
<text x="652" y="108" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="19" font-weight="700" fill="#2a2520">十二類衰退風險</text>
|
||||
<text x="652" y="134" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="13" fill="#5c5347">R1–R6 · T1–T6</text>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="810" y="36" width="210" height="128" rx="16"
|
||||
fill="#fffdf8" stroke="#e3d8c2" stroke-width="1.5"/>
|
||||
<rect x="810" y="160" width="210" height="4" rx="2" fill="#22c55e"/>
|
||||
<circle cx="915" cy="66" r="7" fill="#22c55e"/>
|
||||
<text x="915" y="108" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="19" font-weight="700" fill="#2a2520">帶出處的結論</text>
|
||||
<text x="915" y="134" text-anchor="middle" font-family='-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif'
|
||||
font-size="13" fill="#5c5347">症狀 → 對策</text>
|
||||
</g>
|
||||
<g stroke="#b07d2b" stroke-width="2.5" fill="none" stroke-linecap="round">
|
||||
<line x1="236" y1="100" x2="273" y2="100"/>
|
||||
<polyline points="267,94 275,100 267,106" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<g stroke="#b07d2b" stroke-width="2.5" fill="none" stroke-linecap="round">
|
||||
<line x1="499" y1="100" x2="537" y2="100"/>
|
||||
<polyline points="531,94 539,100 531,106" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<g stroke="#b07d2b" stroke-width="2.5" fill="none" stroke-linecap="round">
|
||||
<line x1="763" y1="100" x2="800" y2="100"/>
|
||||
<polyline points="794,94 802,100 794,106" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
+159
-111
@@ -92,6 +92,9 @@
|
||||
.foot-links{display:flex;gap:20px;flex-wrap:wrap;} .foot-links a{color:var(--ink-soft);font-size:14px;}
|
||||
.foot-note{font-size:13px;color:var(--ink-soft);}
|
||||
.hidden{display:none !important;}
|
||||
.lang-toggle{display:inline-flex;border:1px solid var(--rule);border-radius:999px;overflow:hidden;font-size:13px;background:var(--card);}
|
||||
.lang-toggle button{border:none;background:transparent;padding:5px 11px;cursor:pointer;color:var(--ink-soft);font-family:var(--sans);font-weight:600;font-size:12.5px;}
|
||||
.lang-toggle button.active{background:var(--ink);color:var(--paper-2);}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -100,20 +103,28 @@
|
||||
<div class="nav-in">
|
||||
<a class="brand" href="index.html"><img src="logo.svg" alt="">brooks-lint</a>
|
||||
<div class="nav-links">
|
||||
<a href="index.html">← Home</a>
|
||||
<a href="index.html" data-en="← Home" data-zh="← 首页" data-zhtw="← 首頁" data-ja="← ホーム" data-ko="← 홈" data-es="← Inicio">← Home</a>
|
||||
<a href="https://github.com/hyhmrright/brooks-lint" target="_blank" rel="noopener">GitHub ↗</a>
|
||||
<span class="lang-toggle">
|
||||
<button data-set-lang="en">EN</button>
|
||||
<button data-set-lang="zh">中</button>
|
||||
<button data-set-lang="zhtw">繁</button>
|
||||
<button data-set-lang="ja">日</button>
|
||||
<button data-set-lang="ko">한</button>
|
||||
<button data-set-lang="es">ES</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<header class="hero">
|
||||
<div class="wrap">
|
||||
<h1>The Gallery</h1>
|
||||
<p>Real diagnostic output from brooks-lint — generated by running the skill, then lightly abridged. Four languages, four review modes, every finding in the Iron Law form.</p>
|
||||
<h1 data-en="The Gallery" data-zh="画廊" data-zhtw="範例展示" data-ja="ギャラリー" data-ko="갤러리" data-es="La Galería">The Gallery</h1>
|
||||
<p data-en="Real diagnostic output from brooks-lint — generated by running the skill, then lightly abridged. Four languages, four review modes, every finding in the Iron Law form." data-zh="brooks-lint 的真实诊断输出——通过运行该技能生成,再略作精简。四种语言、四种审查模式,每条发现都遵循铁律形式。" data-zhtw="brooks-lint 的真實診斷輸出——透過執行該技能產生,再略作精簡。四種語言、四種審查模式,每條發現都遵循鐵律形式。" data-ja="brooks-lint の実際の診断出力——スキルを実行して生成し、軽く要約したものです。4 つの言語、4 つのレビューモード、すべての指摘が鉄則の形式に従っています。" data-ko="brooks-lint의 실제 진단 출력—스킬을 실행하여 생성한 뒤 약간 다듬었습니다. 4개 언어, 4개 리뷰 모드, 모든 발견이 철칙 형식을 따릅니다." data-es="Salida diagnóstica real de brooks-lint — generada ejecutando la skill y luego ligeramente abreviada. Cuatro lenguajes, cuatro modos de revisión, cada hallazgo en la forma de la Ley de Hierro.">Real diagnostic output from brooks-lint — generated by running the skill, then lightly abridged. Four languages, four review modes, every finding in the Iron Law form.</p>
|
||||
<div class="meta-row">
|
||||
<span class="meta-pill"><b>8</b> worked examples</span>
|
||||
<span class="meta-pill"><b>4</b> review modes</span>
|
||||
<span class="meta-pill"><b>4</b> languages</span>
|
||||
<span class="meta-pill" data-en="<b>8</b> worked examples" data-zh="<b>8</b> 个示例" data-zhtw="<b>8</b> 個範例" data-ja="<b>8</b> 件の実例" data-ko="<b>8</b>개 예제" data-es="<b>8</b> ejemplos prácticos"><b>8</b> worked examples</span>
|
||||
<span class="meta-pill" data-en="<b>4</b> review modes" data-zh="<b>4</b> 种审查模式" data-zhtw="<b>4</b> 種審查模式" data-ja="<b>4</b> つのレビューモード" data-ko="<b>4</b>개 리뷰 모드" data-es="<b>4</b> modos de revisión"><b>4</b> review modes</span>
|
||||
<span class="meta-pill" data-en="<b>4</b> languages" data-zh="<b>4</b> 种语言" data-zhtw="<b>4</b> 種語言" data-ja="<b>4</b> つの言語" data-ko="<b>4</b>개 언어" data-es="<b>4</b> lenguajes"><b>4</b> languages</span>
|
||||
<span class="meta-pill">Python · TypeScript · Go · Java</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -121,11 +132,11 @@
|
||||
|
||||
<div class="wrap">
|
||||
<div class="filters" id="filters">
|
||||
<button data-filter="all" class="active">All</button>
|
||||
<button data-filter="review">PR Review</button>
|
||||
<button data-filter="audit">Architecture</button>
|
||||
<button data-filter="debt">Tech Debt</button>
|
||||
<button data-filter="test">Test Quality</button>
|
||||
<button data-filter="all" class="active" data-en="All" data-zh="全部" data-zhtw="全部" data-ja="すべて" data-ko="전체" data-es="Todo">All</button>
|
||||
<button data-filter="review" data-en="PR Review" data-zh="PR 审查" data-zhtw="PR 審查" data-ja="PR レビュー" data-ko="PR 리뷰" data-es="Revisión de PR">PR Review</button>
|
||||
<button data-filter="audit" data-en="Architecture" data-zh="架构审计" data-zhtw="架構審計" data-ja="アーキテクチャ監査" data-ko="아키텍처 감사" data-es="Auditoría de Arquitectura">Architecture</button>
|
||||
<button data-filter="debt" data-en="Tech Debt" data-zh="技术债" data-zhtw="技術債" data-ja="技術的負債" data-ko="기술 부채" data-es="Deuda Técnica">Tech Debt</button>
|
||||
<button data-filter="test" data-en="Test Quality" data-zh="测试质量" data-zhtw="測試品質" data-ja="テスト品質" data-ko="테스트 품질" data-es="Calidad de Pruebas">Test Quality</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -133,18 +144,18 @@
|
||||
|
||||
<!-- ============ PR REVIEW ============ -->
|
||||
<section class="mode-group" data-mode="review">
|
||||
<h2 class="mode-title">PR Review <span class="badge">Mode 1</span></h2>
|
||||
<p class="mode-sub">Diagnoses a diff against the six production decay risks (R1–R6).</p>
|
||||
<h2 class="mode-title"><span data-en="PR Review" data-zh="PR 审查" data-zhtw="PR 審查" data-ja="PR レビュー" data-ko="PR 리뷰" data-es="Revisión de PR">PR Review</span> <span class="badge">Mode 1</span></h2>
|
||||
<p class="mode-sub" data-en="Diagnoses a diff against the six production decay risks (R1–R6)." data-zh="对照六种生产衰退风险(R1–R6)诊断一份 diff。" data-zhtw="對照六種生產衰退風險(R1–R6)診斷一份 diff。" data-ja="6 つの本番劣化リスク(R1–R6)に照らして diff を診断します。" data-ko="여섯 가지 운영 부패 위험(R1–R6)에 대해 diff를 진단합니다." data-es="Diagnostica un diff frente a los seis riesgos de decadencia en producción (R1–R6).">Diagnoses a diff against the six production decay risks (R1–R6).</p>
|
||||
|
||||
<article class="case">
|
||||
<div class="case-head">
|
||||
<span class="lang ts">TypeScript</span>
|
||||
<h3>Seven-service payment processor</h3>
|
||||
<h3 data-en="Seven-service payment processor" data-zh="七服务支付处理器" data-zhtw="七服務支付處理器" data-ja="7 サービスの決済プロセッサ" data-ko="일곱 개 서비스 결제 처리기" data-es="Procesador de pagos con siete servicios">Seven-service payment processor</h3>
|
||||
<span class="hs mid">55 / 100</span>
|
||||
</div>
|
||||
<p class="case-summary">A single method orchestrates seven services, creating a structural coupling trap where any change to payment, fraud, inventory, or notification touches the same method.</p>
|
||||
<p class="case-summary" data-en="A single method orchestrates seven services, creating a structural coupling trap where any change to payment, fraud, inventory, or notification touches the same method." data-zh="单个方法编排七个服务,形成结构性耦合陷阱——对支付、欺诈、库存或通知的任何改动都会触及同一个方法。" data-zhtw="單個方法編排七個服務,形成結構性耦合陷阱——對支付、詐騙、庫存或通知的任何改動都會觸及同一個方法。" data-ja="単一のメソッドが 7 つのサービスを統括し、決済・不正検知・在庫・通知のいずれを変更しても同じメソッドに手を入れることになる構造的結合の罠を生んでいます。" data-ko="단일 메서드가 일곱 개의 서비스를 조율하여, 결제·사기·재고·알림 중 무엇을 변경하든 같은 메서드를 건드리게 되는 구조적 결합의 함정을 만듭니다." data-es="Un solo método orquesta siete servicios, creando una trampa de acoplamiento estructural en la que cualquier cambio en pago, fraude, inventario o notificación toca el mismo método.">A single method orchestrates seven services, creating a structural coupling trap where any change to payment, fraud, inventory, or notification touches the same method.</p>
|
||||
<details>
|
||||
<summary>▸ Input code</summary>
|
||||
<summary data-en="▸ Input code" data-zh="▸ 输入代码" data-zhtw="▸ 輸入程式碼" data-ja="▸ 入力コード" data-ko="▸ 입력 코드" data-es="▸ Código de entrada">▸ Input code</summary>
|
||||
<pre class="code">class PaymentProcessor {
|
||||
constructor(
|
||||
private db, private stripe, private mailer, private inventory,
|
||||
@@ -170,43 +181,43 @@
|
||||
}</pre>
|
||||
</details>
|
||||
<div class="finding">
|
||||
<h4>🔴 Change Propagation — Seven-service constructor signals a God Class</h4>
|
||||
<div class="ln sy"><b>Symptom:</b> One class injects seven dependencies; one method orchestrates all of them.</div>
|
||||
<div class="ln sr"><b>Source:</b> Fowler — Refactoring — Divergent Change; Martin — Clean Architecture — SRP</div>
|
||||
<div class="ln rx"><b>Remedy:</b> Decompose into FraudCheckService, InventoryDeductionService, PaymentNotifier — inject 3, not 7.</div>
|
||||
<h4 data-en="🔴 Change Propagation — Seven-service constructor signals a God Class" data-zh="🔴 变更扩散 — 七服务构造函数暴露出上帝类" data-zhtw="🔴 變更擴散 — 七服務建構子暴露出上帝類別" data-ja="🔴 変更の波及 — 7 サービスのコンストラクタが神クラスを示唆" data-ko="🔴 변경 전파 — 일곱 서비스 생성자가 갓 클래스를 드러냄" data-es="🔴 Propagación de cambios — El constructor de siete servicios delata una God Class">🔴 Change Propagation — Seven-service constructor signals a God Class</h4>
|
||||
<div class="ln sy" data-en="<b>Symptom:</b> One class injects seven dependencies; one method orchestrates all of them." data-zh="<b>症状:</b> 一个类注入了七个依赖;一个方法编排了全部七个。" data-zhtw="<b>症狀:</b> 一個類別注入了七個依賴;一個方法編排了全部七個。" data-ja="<b>症状:</b> 一つのクラスが 7 つの依存を注入し、一つのメソッドがそのすべてを統括しています。" data-ko="<b>증상:</b> 한 클래스가 일곱 개의 의존성을 주입하고, 한 메서드가 그 전부를 조율합니다." data-es="<b>Síntoma:</b> Una clase inyecta siete dependencias; un método las orquesta todas."><b>Symptom:</b> One class injects seven dependencies; one method orchestrates all of them.</div>
|
||||
<div class="ln sr" data-en="<b>Source:</b> Fowler — Refactoring — Divergent Change; Martin — Clean Architecture — SRP" data-zh="<b>根源:</b> Fowler — Refactoring — 发散式变更(Divergent Change);Martin — Clean Architecture — SRP" data-zhtw="<b>根源:</b> Fowler — Refactoring — 發散式變更(Divergent Change);Martin — Clean Architecture — SRP" data-ja="<b>根源:</b> Fowler — Refactoring — 発散的変更(Divergent Change);Martin — Clean Architecture — SRP" data-ko="<b>근원:</b> Fowler — Refactoring — 발산적 변경(Divergent Change); Martin — Clean Architecture — SRP" data-es="<b>Origen:</b> Fowler — Refactoring — Divergent Change (Cambio divergente); Martin — Clean Architecture — SRP"><b>Source:</b> Fowler — Refactoring — Divergent Change; Martin — Clean Architecture — SRP</div>
|
||||
<div class="ln rx" data-en="<b>Remedy:</b> Decompose into FraudCheckService, InventoryDeductionService, PaymentNotifier — inject 3, not 7." data-zh="<b>修复:</b> 拆分为 FraudCheckService、InventoryDeductionService、PaymentNotifier——注入 3 个,而非 7 个。" data-zhtw="<b>對策:</b> 拆分為 FraudCheckService、InventoryDeductionService、PaymentNotifier——注入 3 個,而非 7 個。" data-ja="<b>対策:</b> FraudCheckService、InventoryDeductionService、PaymentNotifier に分解し、7 つではなく 3 つを注入します。" data-ko="<b>처방:</b> FraudCheckService, InventoryDeductionService, PaymentNotifier로 분해하여 7개가 아닌 3개를 주입하세요." data-es="<b>Remedio:</b> Descompón en FraudCheckService, InventoryDeductionService, PaymentNotifier — inyecta 3, no 7."><b>Remedy:</b> Decompose into FraudCheckService, InventoryDeductionService, PaymentNotifier — inject 3, not 7.</div>
|
||||
</div>
|
||||
<div class="finding">
|
||||
<h4>🔴 Change Propagation — Inventory loop embeds warehouse notification policy</h4>
|
||||
<div class="ln sy"><b>Symptom:</b> A low-stock check (<code>< 10</code>) fires a warehouse email with hardcoded recipient inside the payment loop.</div>
|
||||
<div class="ln sr"><b>Source:</b> Fowler — Refactoring — Shotgun Surgery; Hunt & Thomas — Orthogonality</div>
|
||||
<div class="ln rx"><b>Remedy:</b> Publish a <code>StockLevelChanged</code> domain event a separate notifier subscribes to.</div>
|
||||
<h4 data-en="🔴 Change Propagation — Inventory loop embeds warehouse notification policy" data-zh="🔴 变更扩散 — 库存循环里嵌入了仓库通知策略" data-zhtw="🔴 變更擴散 — 庫存迴圈裡嵌入了倉庫通知策略" data-ja="🔴 変更の波及 — 在庫ループに倉庫通知ポリシーが埋め込まれている" data-ko="🔴 변경 전파 — 재고 루프에 창고 알림 정책이 박혀 있음" data-es="🔴 Propagación de cambios — El bucle de inventario incrusta la política de notificación del almacén">🔴 Change Propagation — Inventory loop embeds warehouse notification policy</h4>
|
||||
<div class="ln sy" data-en="<b>Symptom:</b> A low-stock check (<code>< 10</code>) fires a warehouse email with hardcoded recipient inside the payment loop." data-zh="<b>症状:</b> 一处低库存检查(<code>< 10</code>)在支付循环内部发出收件人写死的仓库邮件。" data-zhtw="<b>症狀:</b> 一處低庫存檢查(<code>< 10</code>)在支付迴圈內部發出收件人寫死的倉庫郵件。" data-ja="<b>症状:</b> 低在庫チェック(<code>< 10</code>)が決済ループの内側で、宛先をハードコードした倉庫メールを送信しています。" data-ko="<b>증상:</b> 저재고 검사(<code>< 10</code>)가 결제 루프 내부에서 수신자가 하드코딩된 창고 이메일을 발송합니다." data-es="<b>Síntoma:</b> Una comprobación de bajo stock (<code>< 10</code>) envía un correo al almacén con destinatario codificado dentro del bucle de pago."><b>Symptom:</b> A low-stock check (<code>< 10</code>) fires a warehouse email with hardcoded recipient inside the payment loop.</div>
|
||||
<div class="ln sr" data-en="<b>Source:</b> Fowler — Refactoring — Shotgun Surgery; Hunt & Thomas — Orthogonality" data-zh="<b>根源:</b> Fowler — Refactoring — 霰弹式修改(Shotgun Surgery);Hunt & Thomas — 正交性(Orthogonality)" data-zhtw="<b>根源:</b> Fowler — Refactoring — 散彈式修改(Shotgun Surgery);Hunt & Thomas — 正交性(Orthogonality)" data-ja="<b>根源:</b> Fowler — Refactoring — ショットガン手術(Shotgun Surgery);Hunt & Thomas — 直交性(Orthogonality)" data-ko="<b>근원:</b> Fowler — Refactoring — 산탄총 수술(Shotgun Surgery); Hunt & Thomas — 직교성(Orthogonality)" data-es="<b>Origen:</b> Fowler — Refactoring — Shotgun Surgery (Cirugía con escopeta); Hunt & Thomas — Orthogonality (Ortogonalidad)"><b>Source:</b> Fowler — Refactoring — Shotgun Surgery; Hunt & Thomas — Orthogonality</div>
|
||||
<div class="ln rx" data-en="<b>Remedy:</b> Publish a <code>StockLevelChanged</code> domain event a separate notifier subscribes to." data-zh="<b>修复:</b> 发布一个 <code>StockLevelChanged</code> 领域事件,由独立的通知器订阅。" data-zhtw="<b>對策:</b> 發布一個 <code>StockLevelChanged</code> 領域事件,由獨立的通知器訂閱。" data-ja="<b>対策:</b> <code>StockLevelChanged</code> ドメインイベントを発行し、別の通知コンポーネントが購読するようにします。" data-ko="<b>처방:</b> <code>StockLevelChanged</code> 도메인 이벤트를 발행하고 별도의 알림 컴포넌트가 구독하게 하세요." data-es="<b>Remedio:</b> Publica un evento de dominio <code>StockLevelChanged</code> al que se suscriba un notificador independiente."><b>Remedy:</b> Publish a <code>StockLevelChanged</code> domain event a separate notifier subscribes to.</div>
|
||||
</div>
|
||||
<div class="finding">
|
||||
<h4>🟡 Knowledge Duplication — <code>order.total + tax</code> computed three times</h4>
|
||||
<div class="ln sy"><b>Symptom:</b> The same expression appears on three lines with no shared name.</div>
|
||||
<div class="ln sr"><b>Source:</b> Hunt & Thomas — DRY; Fowler — Refactoring — Duplicate Code</div>
|
||||
<div class="ln rx"><b>Remedy:</b> Expose a computed <code>order.grandTotal</code>.</div>
|
||||
<h4 data-en="🟡 Knowledge Duplication — <code>order.total + tax</code> computed three times" data-zh="🟡 知识重复 — <code>order.total + tax</code> 被计算了三次" data-zhtw="🟡 知識重複 — <code>order.total + tax</code> 被計算了三次" data-ja="🟡 知識の重複 — <code>order.total + tax</code> が 3 回計算されている" data-ko="🟡 지식 중복 — <code>order.total + tax</code>가 세 번 계산됨" data-es="🟡 Duplicación de conocimiento — <code>order.total + tax</code> se calcula tres veces">🟡 Knowledge Duplication — <code>order.total + tax</code> computed three times</h4>
|
||||
<div class="ln sy" data-en="<b>Symptom:</b> The same expression appears on three lines with no shared name." data-zh="<b>症状:</b> 同一个表达式出现在三行里,没有共享的命名。" data-zhtw="<b>症狀:</b> 同一個運算式出現在三行裡,沒有共享的命名。" data-ja="<b>症状:</b> 同じ式が共有された名前を持たないまま 3 行に現れています。" data-ko="<b>증상:</b> 같은 표현식이 공유된 이름 없이 세 줄에 나타납니다." data-es="<b>Síntoma:</b> La misma expresión aparece en tres líneas sin un nombre compartido."><b>Symptom:</b> The same expression appears on three lines with no shared name.</div>
|
||||
<div class="ln sr" data-en="<b>Source:</b> Hunt & Thomas — DRY; Fowler — Refactoring — Duplicate Code" data-zh="<b>根源:</b> Hunt & Thomas — DRY;Fowler — Refactoring — 重复代码(Duplicate Code)" data-zhtw="<b>根源:</b> Hunt & Thomas — DRY;Fowler — Refactoring — 重複程式碼(Duplicate Code)" data-ja="<b>根源:</b> Hunt & Thomas — DRY;Fowler — Refactoring — 重複コード(Duplicate Code)" data-ko="<b>근원:</b> Hunt & Thomas — DRY; Fowler — Refactoring — 중복 코드(Duplicate Code)" data-es="<b>Origen:</b> Hunt & Thomas — DRY; Fowler — Refactoring — Duplicate Code (Código duplicado)"><b>Source:</b> Hunt & Thomas — DRY; Fowler — Refactoring — Duplicate Code</div>
|
||||
<div class="ln rx" data-en="<b>Remedy:</b> Expose a computed <code>order.grandTotal</code>." data-zh="<b>修复:</b> 暴露一个计算得出的 <code>order.grandTotal</code>。" data-zhtw="<b>對策:</b> 暴露一個計算得出的 <code>order.grandTotal</code>。" data-ja="<b>対策:</b> 計算済みの <code>order.grandTotal</code> を公開します。" data-ko="<b>처방:</b> 계산된 <code>order.grandTotal</code>을 노출하세요." data-es="<b>Remedio:</b> Expón un <code>order.grandTotal</code> calculado."><b>Remedy:</b> Expose a computed <code>order.grandTotal</code>.</div>
|
||||
</div>
|
||||
<div class="finding">
|
||||
<h4>🟡 Domain Model Distortion — <code>Order</code> is a mutable data bag</h4>
|
||||
<div class="ln sy"><b>Symptom:</b> <code>order.tax</code>, <code>order.status</code>, <code>order.chargeId</code> are all set externally; the object holds no behaviour.</div>
|
||||
<div class="ln rx"><b>Remedy:</b> Give <code>Order</code> state-transition methods: <code>order.recordPayment(chargeId)</code>.</div>
|
||||
<h4 data-en="🟡 Domain Model Distortion — <code>Order</code> is a mutable data bag" data-zh="🟡 领域模型失真 — <code>Order</code> 是一个可变的数据袋" data-zhtw="🟡 領域模型失真 — <code>Order</code> 是一個可變的資料袋" data-ja="🟡 ドメインモデルの歪み — <code>Order</code> が可変のデータ袋になっている" data-ko="🟡 도메인 모델 왜곡 — <code>Order</code>가 가변 데이터 가방임" data-es="🟡 Distorsión del modelo de dominio — <code>Order</code> es un saco de datos mutable">🟡 Domain Model Distortion — <code>Order</code> is a mutable data bag</h4>
|
||||
<div class="ln sy" data-en="<b>Symptom:</b> <code>order.tax</code>, <code>order.status</code>, <code>order.chargeId</code> are all set externally; the object holds no behaviour." data-zh="<b>症状:</b> <code>order.tax</code>、<code>order.status</code>、<code>order.chargeId</code> 全部由外部赋值;该对象不持有任何行为。" data-zhtw="<b>症狀:</b> <code>order.tax</code>、<code>order.status</code>、<code>order.chargeId</code> 全部由外部賦值;該物件不持有任何行為。" data-ja="<b>症状:</b> <code>order.tax</code>、<code>order.status</code>、<code>order.chargeId</code> はすべて外部から設定され、このオブジェクトは振る舞いを一切持ちません。" data-ko="<b>증상:</b> <code>order.tax</code>, <code>order.status</code>, <code>order.chargeId</code>가 모두 외부에서 설정되며, 객체는 어떤 행위도 갖지 않습니다." data-es="<b>Síntoma:</b> <code>order.tax</code>, <code>order.status</code>, <code>order.chargeId</code> se asignan todos externamente; el objeto no contiene comportamiento."><b>Symptom:</b> <code>order.tax</code>, <code>order.status</code>, <code>order.chargeId</code> are all set externally; the object holds no behaviour.</div>
|
||||
<div class="ln rx" data-en="<b>Remedy:</b> Give <code>Order</code> state-transition methods: <code>order.recordPayment(chargeId)</code>." data-zh="<b>修复:</b> 为 <code>Order</code> 赋予状态转换方法:<code>order.recordPayment(chargeId)</code>。" data-zhtw="<b>對策:</b> 為 <code>Order</code> 賦予狀態轉換方法:<code>order.recordPayment(chargeId)</code>。" data-ja="<b>対策:</b> <code>Order</code> に状態遷移メソッドを与えます:<code>order.recordPayment(chargeId)</code>。" data-ko="<b>처방:</b> <code>Order</code>에 상태 전이 메서드를 부여하세요: <code>order.recordPayment(chargeId)</code>." data-es="<b>Remedio:</b> Dota a <code>Order</code> de métodos de transición de estado: <code>order.recordPayment(chargeId)</code>."><b>Remedy:</b> Give <code>Order</code> state-transition methods: <code>order.recordPayment(chargeId)</code>.</div>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<!-- ============ ARCHITECTURE ============ -->
|
||||
<section class="mode-group" data-mode="audit">
|
||||
<h2 class="mode-title">Architecture Audit <span class="badge">Mode 2</span></h2>
|
||||
<p class="mode-sub">Maps module dependencies, renders a colour-coded graph, and flags layering and cycle violations.</p>
|
||||
<h2 class="mode-title"><span data-en="Architecture Audit" data-zh="架构审计" data-zhtw="架構審計" data-ja="アーキテクチャ監査" data-ko="아키텍처 감사" data-es="Auditoría de Arquitectura">Architecture Audit</span> <span class="badge">Mode 2</span></h2>
|
||||
<p class="mode-sub" data-en="Maps module dependencies, renders a colour-coded graph, and flags layering and cycle violations." data-zh="梳理模块依赖,渲染一张颜色编码的依赖图,并标出分层与环路违规。" data-zhtw="梳理模組依賴,渲染一張顏色編碼的依賴圖,並標出分層與環路違規。" data-ja="モジュールの依存関係をマッピングし、色分けしたグラフを描画して、レイヤリングと循環の違反を指摘します。" data-ko="모듈 의존성을 매핑하고, 색상으로 구분된 그래프를 렌더링하며, 계층 및 순환 위반을 표시합니다." data-es="Mapea las dependencias entre módulos, genera un grafo codificado por colores y señala violaciones de capas y ciclos.">Maps module dependencies, renders a colour-coded graph, and flags layering and cycle violations.</p>
|
||||
|
||||
<article class="case">
|
||||
<div class="case-head">
|
||||
<span class="lang ts">TypeScript</span>
|
||||
<h3>Dependency Inversion violation</h3>
|
||||
<h3 data-en="Dependency Inversion violation" data-zh="依赖倒置违规" data-zhtw="依賴反轉違規" data-ja="依存性逆転の違反" data-ko="의존성 역전 위반" data-es="Violación de la inversión de dependencias">Dependency Inversion violation</h3>
|
||||
<span class="hs mid">50 / 100</span>
|
||||
</div>
|
||||
<p class="case-summary">Domain entities import infrastructure directly, and a near-cycle forms between Product and PricingService.</p>
|
||||
<p class="case-summary" data-en="Domain entities import infrastructure directly, and a near-cycle forms between Product and PricingService." data-zh="领域实体直接导入基础设施,且 Product 与 PricingService 之间形成了一个近乎环路的依赖。" data-zhtw="領域實體直接匯入基礎設施,且 Product 與 PricingService 之間形成了一個近乎環路的依賴。" data-ja="ドメインエンティティがインフラを直接インポートし、Product と PricingService の間にほぼ循環が形成されています。" data-ko="도메인 엔티티가 인프라를 직접 임포트하며, Product와 PricingService 사이에 거의 순환에 가까운 의존이 형성됩니다." data-es="Las entidades de dominio importan la infraestructura directamente, y se forma un casi-ciclo entre Product y PricingService.">Domain entities import infrastructure directly, and a near-cycle forms between Product and PricingService.</p>
|
||||
<pre class="mermaid">graph TD
|
||||
subgraph API["API Layer"]
|
||||
OrderController; UserController
|
||||
@@ -237,26 +248,26 @@
|
||||
class OrderService,PricingService,UserService warning
|
||||
class OrderController,UserController,PostgresClient,RedisCache,StripeClient clean</pre>
|
||||
<div class="finding">
|
||||
<h4>🔴 Dependency Disorder — Domain layer directly imports infrastructure</h4>
|
||||
<div class="ln sy"><b>Symptom:</b> <code>Order.ts</code> imports <code>PostgresClient</code>; <code>User.ts</code> imports <code>RedisCache</code>.</div>
|
||||
<div class="ln sr"><b>Source:</b> Martin — Clean Architecture — Dependency Inversion Principle</div>
|
||||
<div class="ln rx"><b>Remedy:</b> Define <code>IOrderRepository</code>/<code>IUserRepository</code> in the domain; move infra refs to <code>infra/</code>.</div>
|
||||
<h4 data-en="🔴 Dependency Disorder — Domain layer directly imports infrastructure" data-zh="🔴 依赖失序 — 领域层直接导入基础设施" data-zhtw="🔴 依賴失序 — 領域層直接匯入基礎設施" data-ja="🔴 依存関係の無秩序 — ドメイン層がインフラを直接インポートしている" data-ko="🔴 의존성 무질서 — 도메인 계층이 인프라를 직접 임포트함" data-es="🔴 Desorden de dependencias — La capa de dominio importa la infraestructura directamente">🔴 Dependency Disorder — Domain layer directly imports infrastructure</h4>
|
||||
<div class="ln sy" data-en="<b>Symptom:</b> <code>Order.ts</code> imports <code>PostgresClient</code>; <code>User.ts</code> imports <code>RedisCache</code>." data-zh="<b>症状:</b> <code>Order.ts</code> 导入了 <code>PostgresClient</code>;<code>User.ts</code> 导入了 <code>RedisCache</code>。" data-zhtw="<b>症狀:</b> <code>Order.ts</code> 匯入了 <code>PostgresClient</code>;<code>User.ts</code> 匯入了 <code>RedisCache</code>。" data-ja="<b>症状:</b> <code>Order.ts</code> が <code>PostgresClient</code> を、<code>User.ts</code> が <code>RedisCache</code> をインポートしています。" data-ko="<b>증상:</b> <code>Order.ts</code>가 <code>PostgresClient</code>를, <code>User.ts</code>가 <code>RedisCache</code>를 임포트합니다." data-es="<b>Síntoma:</b> <code>Order.ts</code> importa <code>PostgresClient</code>; <code>User.ts</code> importa <code>RedisCache</code>."><b>Symptom:</b> <code>Order.ts</code> imports <code>PostgresClient</code>; <code>User.ts</code> imports <code>RedisCache</code>.</div>
|
||||
<div class="ln sr" data-en="<b>Source:</b> Martin — Clean Architecture — Dependency Inversion Principle" data-zh="<b>根源:</b> Martin — Clean Architecture — 依赖倒置原则(Dependency Inversion Principle)" data-zhtw="<b>根源:</b> Martin — Clean Architecture — 依賴反轉原則(Dependency Inversion Principle)" data-ja="<b>根源:</b> Martin — Clean Architecture — 依存性逆転の原則(Dependency Inversion Principle)" data-ko="<b>근원:</b> Martin — Clean Architecture — 의존성 역전 원칙(Dependency Inversion Principle)" data-es="<b>Origen:</b> Martin — Clean Architecture — Dependency Inversion Principle (Principio de inversión de dependencias)"><b>Source:</b> Martin — Clean Architecture — Dependency Inversion Principle</div>
|
||||
<div class="ln rx" data-en="<b>Remedy:</b> Define <code>IOrderRepository</code>/<code>IUserRepository</code> in the domain; move infra refs to <code>infra/</code>." data-zh="<b>修复:</b> 在领域层定义 <code>IOrderRepository</code>/<code>IUserRepository</code>;将基础设施引用移到 <code>infra/</code>。" data-zhtw="<b>對策:</b> 在領域層定義 <code>IOrderRepository</code>/<code>IUserRepository</code>;將基礎設施引用移到 <code>infra/</code>。" data-ja="<b>対策:</b> ドメインに <code>IOrderRepository</code>/<code>IUserRepository</code> を定義し、インフラ参照を <code>infra/</code> へ移します。" data-ko="<b>처방:</b> 도메인에 <code>IOrderRepository</code>/<code>IUserRepository</code>를 정의하고 인프라 참조를 <code>infra/</code>로 옮기세요." data-es="<b>Remedio:</b> Define <code>IOrderRepository</code>/<code>IUserRepository</code> en el dominio; mueve las referencias de infraestructura a <code>infra/</code>."><b>Remedy:</b> Define <code>IOrderRepository</code>/<code>IUserRepository</code> in the domain; move infra refs to <code>infra/</code>.</div>
|
||||
</div>
|
||||
<div class="finding">
|
||||
<h4>🔴 Dependency Disorder — Product → PricingService (upward dependency)</h4>
|
||||
<div class="ln sy"><b>Symptom:</b> Near-cycle <code>PricingService → Product → PricingService</code>.</div>
|
||||
<div class="ln sr"><b>Source:</b> Martin — Clean Architecture — Acyclic Dependencies Principle</div>
|
||||
<div class="ln rx"><b>Remedy:</b> Pass pricing as a value object or define <code>IPricingPolicy</code> in the domain.</div>
|
||||
<h4 data-en="🔴 Dependency Disorder — Product → PricingService (upward dependency)" data-zh="🔴 依赖失序 — Product → PricingService(向上依赖)" data-zhtw="🔴 依賴失序 — Product → PricingService(向上依賴)" data-ja="🔴 依存関係の無秩序 — Product → PricingService(上向きの依存)" data-ko="🔴 의존성 무질서 — Product → PricingService(상향 의존)" data-es="🔴 Desorden de dependencias — Product → PricingService (dependencia ascendente)">🔴 Dependency Disorder — Product → PricingService (upward dependency)</h4>
|
||||
<div class="ln sy" data-en="<b>Symptom:</b> Near-cycle <code>PricingService → Product → PricingService</code>." data-zh="<b>症状:</b> 近乎环路 <code>PricingService → Product → PricingService</code>。" data-zhtw="<b>症狀:</b> 近乎環路 <code>PricingService → Product → PricingService</code>。" data-ja="<b>症状:</b> ほぼ循環 <code>PricingService → Product → PricingService</code>。" data-ko="<b>증상:</b> 거의 순환 <code>PricingService → Product → PricingService</code>." data-es="<b>Síntoma:</b> Casi-ciclo <code>PricingService → Product → PricingService</code>."><b>Symptom:</b> Near-cycle <code>PricingService → Product → PricingService</code>.</div>
|
||||
<div class="ln sr" data-en="<b>Source:</b> Martin — Clean Architecture — Acyclic Dependencies Principle" data-zh="<b>根源:</b> Martin — Clean Architecture — 无环依赖原则(Acyclic Dependencies Principle)" data-zhtw="<b>根源:</b> Martin — Clean Architecture — 無環依賴原則(Acyclic Dependencies Principle)" data-ja="<b>根源:</b> Martin — Clean Architecture — 非循環依存の原則(Acyclic Dependencies Principle)" data-ko="<b>근원:</b> Martin — Clean Architecture — 비순환 의존성 원칙(Acyclic Dependencies Principle)" data-es="<b>Origen:</b> Martin — Clean Architecture — Acyclic Dependencies Principle (Principio de dependencias acíclicas)"><b>Source:</b> Martin — Clean Architecture — Acyclic Dependencies Principle</div>
|
||||
<div class="ln rx" data-en="<b>Remedy:</b> Pass pricing as a value object or define <code>IPricingPolicy</code> in the domain." data-zh="<b>修复:</b> 将定价作为值对象传入,或在领域层定义 <code>IPricingPolicy</code>。" data-zhtw="<b>對策:</b> 將定價作為值物件傳入,或在領域層定義 <code>IPricingPolicy</code>。" data-ja="<b>対策:</b> 価格を値オブジェクトとして渡すか、ドメインに <code>IPricingPolicy</code> を定義します。" data-ko="<b>처방:</b> 가격을 값 객체로 전달하거나 도메인에 <code>IPricingPolicy</code>를 정의하세요." data-es="<b>Remedio:</b> Pasa el precio como objeto de valor o define <code>IPricingPolicy</code> en el dominio."><b>Remedy:</b> Pass pricing as a value object or define <code>IPricingPolicy</code> in the domain.</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="case">
|
||||
<div class="case-head">
|
||||
<span class="lang go">Go</span>
|
||||
<h3>Circular dependency across packages</h3>
|
||||
<h3 data-en="Circular dependency across packages" data-zh="跨包的循环依赖" data-zhtw="跨套件的循環依賴" data-ja="パッケージをまたぐ循環依存" data-ko="패키지 간 순환 의존성" data-es="Dependencia circular entre paquetes">Circular dependency across packages</h3>
|
||||
<span class="hs bad">45 / 100</span>
|
||||
</div>
|
||||
<p class="case-summary"><code>auth → user → notification → auth</code> forms a strongly connected component — Go refuses to compile it.</p>
|
||||
<p class="case-summary" data-en="<code>auth → user → notification → auth</code> forms a strongly connected component — Go refuses to compile it." data-zh="<code>auth → user → notification → auth</code> 构成一个强连通分量——Go 拒绝编译它。" data-zhtw="<code>auth → user → notification → auth</code> 構成一個強連通分量——Go 拒絕編譯它。" data-ja="<code>auth → user → notification → auth</code> が強連結成分を形成しており、Go はコンパイルを拒否します。" data-ko="<code>auth → user → notification → auth</code>가 강한 연결 요소를 형성하여 Go가 컴파일을 거부합니다." data-es="<code>auth → user → notification → auth</code> forma un componente fuertemente conexo — Go se niega a compilarlo."><code>auth → user → notification → auth</code> forms a strongly connected component — Go refuses to compile it.</p>
|
||||
<pre class="mermaid">graph TD
|
||||
subgraph pkg["pkg/"]
|
||||
auth["auth"]; user["user"]; notification["notification"]; billing["billing"]
|
||||
@@ -270,26 +281,26 @@
|
||||
class auth,user,notification critical
|
||||
class billing warning</pre>
|
||||
<div class="finding">
|
||||
<h4>🔴 Dependency Disorder — Circular dependency auth → user → notification → auth</h4>
|
||||
<div class="ln sy"><b>Symptom:</b> Three packages form a cycle; none compile, test, or deploy independently.</div>
|
||||
<div class="ln sr"><b>Source:</b> Martin — Clean Architecture — Acyclic Dependencies Principle</div>
|
||||
<div class="ln rx"><b>Remedy:</b> Extract interfaces into <code>pkg/contracts</code>; each package implements the interface its consumer defines.</div>
|
||||
<h4 data-en="🔴 Dependency Disorder — Circular dependency auth → user → notification → auth" data-zh="🔴 依赖失序 — 循环依赖 auth → user → notification → auth" data-zhtw="🔴 依賴失序 — 循環依賴 auth → user → notification → auth" data-ja="🔴 依存関係の無秩序 — 循環依存 auth → user → notification → auth" data-ko="🔴 의존성 무질서 — 순환 의존 auth → user → notification → auth" data-es="🔴 Desorden de dependencias — Dependencia circular auth → user → notification → auth">🔴 Dependency Disorder — Circular dependency auth → user → notification → auth</h4>
|
||||
<div class="ln sy" data-en="<b>Symptom:</b> Three packages form a cycle; none compile, test, or deploy independently." data-zh="<b>症状:</b> 三个包构成一个环;没有一个能独立编译、测试或部署。" data-zhtw="<b>症狀:</b> 三個套件構成一個環;沒有一個能獨立編譯、測試或部署。" data-ja="<b>症状:</b> 3 つのパッケージが循環を形成し、どれも独立してコンパイル・テスト・デプロイできません。" data-ko="<b>증상:</b> 세 패키지가 순환을 형성하여 어느 것도 독립적으로 컴파일·테스트·배포되지 않습니다." data-es="<b>Síntoma:</b> Tres paquetes forman un ciclo; ninguno compila, prueba ni despliega de forma independiente."><b>Symptom:</b> Three packages form a cycle; none compile, test, or deploy independently.</div>
|
||||
<div class="ln sr" data-en="<b>Source:</b> Martin — Clean Architecture — Acyclic Dependencies Principle" data-zh="<b>根源:</b> Martin — Clean Architecture — 无环依赖原则(Acyclic Dependencies Principle)" data-zhtw="<b>根源:</b> Martin — Clean Architecture — 無環依賴原則(Acyclic Dependencies Principle)" data-ja="<b>根源:</b> Martin — Clean Architecture — 非循環依存の原則(Acyclic Dependencies Principle)" data-ko="<b>근원:</b> Martin — Clean Architecture — 비순환 의존성 원칙(Acyclic Dependencies Principle)" data-es="<b>Origen:</b> Martin — Clean Architecture — Acyclic Dependencies Principle (Principio de dependencias acíclicas)"><b>Source:</b> Martin — Clean Architecture — Acyclic Dependencies Principle</div>
|
||||
<div class="ln rx" data-en="<b>Remedy:</b> Extract interfaces into <code>pkg/contracts</code>; each package implements the interface its consumer defines." data-zh="<b>修复:</b> 将接口抽取到 <code>pkg/contracts</code>;每个包实现由其消费者定义的接口。" data-zhtw="<b>對策:</b> 將介面抽取到 <code>pkg/contracts</code>;每個套件實作由其消費者定義的介面。" data-ja="<b>対策:</b> インターフェースを <code>pkg/contracts</code> に抽出し、各パッケージは消費側が定義したインターフェースを実装します。" data-ko="<b>처방:</b> 인터페이스를 <code>pkg/contracts</code>로 추출하고, 각 패키지는 소비자가 정의한 인터페이스를 구현하게 하세요." data-es="<b>Remedio:</b> Extrae las interfaces a <code>pkg/contracts</code>; cada paquete implementa la interfaz que define su consumidor."><b>Remedy:</b> Extract interfaces into <code>pkg/contracts</code>; each package implements the interface its consumer defines.</div>
|
||||
</div>
|
||||
<div class="finding">
|
||||
<h4>🟡 Domain Model Distortion — Bounded contexts crossed with no anti-corruption layer</h4>
|
||||
<div class="ln sy"><b>Symptom:</b> Identity, profile, and notification contexts import each other with no translation layer.</div>
|
||||
<div class="ln sr"><b>Source:</b> Evans — DDD — Bounded Context; Anti-Corruption Layer</div>
|
||||
<div class="ln rx"><b>Remedy:</b> Define thin adapters at each context boundary.</div>
|
||||
<h4 data-en="🟡 Domain Model Distortion — Bounded contexts crossed with no anti-corruption layer" data-zh="🟡 领域模型失真 — 跨越限界上下文却没有防腐层" data-zhtw="🟡 領域模型失真 — 跨越界限上下文卻沒有防腐層" data-ja="🟡 ドメインモデルの歪み — 腐敗防止層なしに境界づけられたコンテキストを越境" data-ko="🟡 도메인 모델 왜곡 — 부패 방지 계층 없이 경계 컨텍스트를 넘나듦" data-es="🟡 Distorsión del modelo de dominio — Se cruzan contextos delimitados sin capa anticorrupción">🟡 Domain Model Distortion — Bounded contexts crossed with no anti-corruption layer</h4>
|
||||
<div class="ln sy" data-en="<b>Symptom:</b> Identity, profile, and notification contexts import each other with no translation layer." data-zh="<b>症状:</b> 身份、资料和通知三个上下文相互导入,没有任何转换层。" data-zhtw="<b>症狀:</b> 身分、資料和通知三個上下文相互匯入,沒有任何轉換層。" data-ja="<b>症状:</b> アイデンティティ・プロフィール・通知の各コンテキストが、変換層なしに相互にインポートしています。" data-ko="<b>증상:</b> 신원, 프로필, 알림 컨텍스트가 변환 계층 없이 서로를 임포트합니다." data-es="<b>Síntoma:</b> Los contextos de identidad, perfil y notificación se importan entre sí sin capa de traducción."><b>Symptom:</b> Identity, profile, and notification contexts import each other with no translation layer.</div>
|
||||
<div class="ln sr" data-en="<b>Source:</b> Evans — DDD — Bounded Context; Anti-Corruption Layer" data-zh="<b>根源:</b> Evans — DDD — 限界上下文(Bounded Context);防腐层(Anti-Corruption Layer)" data-zhtw="<b>根源:</b> Evans — DDD — 界限上下文(Bounded Context);防腐層(Anti-Corruption Layer)" data-ja="<b>根源:</b> Evans — DDD — 境界づけられたコンテキスト(Bounded Context);腐敗防止層(Anti-Corruption Layer)" data-ko="<b>근원:</b> Evans — DDD — 경계 컨텍스트(Bounded Context); 부패 방지 계층(Anti-Corruption Layer)" data-es="<b>Origen:</b> Evans — DDD — Bounded Context (Contexto delimitado); Anti-Corruption Layer (Capa anticorrupción)"><b>Source:</b> Evans — DDD — Bounded Context; Anti-Corruption Layer</div>
|
||||
<div class="ln rx" data-en="<b>Remedy:</b> Define thin adapters at each context boundary." data-zh="<b>修复:</b> 在每个上下文边界处定义轻量适配器。" data-zhtw="<b>對策:</b> 在每個上下文邊界處定義輕量轉接器。" data-ja="<b>対策:</b> 各コンテキスト境界に薄いアダプタを定義します。" data-ko="<b>처방:</b> 각 컨텍스트 경계에 얇은 어댑터를 정의하세요." data-es="<b>Remedio:</b> Define adaptadores delgados en cada límite de contexto."><b>Remedy:</b> Define thin adapters at each context boundary.</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="case">
|
||||
<div class="case-head">
|
||||
<span class="lang java">Java</span>
|
||||
<h3>Textbook Clean Architecture</h3>
|
||||
<h3 data-en="Textbook Clean Architecture" data-zh="教科书级的整洁架构" data-zhtw="教科書級的整潔架構" data-ja="教科書どおりのクリーンアーキテクチャ" data-ko="교과서적인 클린 아키텍처" data-es="Clean Architecture de manual">Textbook Clean Architecture</h3>
|
||||
<span class="hs good">98 / 100</span>
|
||||
</div>
|
||||
<p class="case-summary">Dependencies flow inward, infra implements domain ports, no cycles. brooks-lint reports clean code as clean — and still offers one forward-looking suggestion.</p>
|
||||
<p class="case-summary" data-en="Dependencies flow inward, infra implements domain ports, no cycles. brooks-lint reports clean code as clean — and still offers one forward-looking suggestion." data-zh="依赖向内流动,基础设施实现领域端口,没有环路。brooks-lint 将整洁代码如实评为整洁——并仍给出一条前瞻性建议。" data-zhtw="依賴向內流動,基礎設施實作領域埠,沒有環路。brooks-lint 將整潔程式碼如實評為整潔——並仍給出一條前瞻性建議。" data-ja="依存は内側へ流れ、インフラがドメインのポートを実装し、循環はありません。brooks-lint はクリーンなコードをクリーンと報告し——それでも先を見据えた提案を一つ示します。" data-ko="의존성이 안쪽으로 흐르고, 인프라가 도메인 포트를 구현하며, 순환이 없습니다. brooks-lint는 깨끗한 코드를 깨끗하다고 보고하면서도 미래를 내다보는 제안 하나를 제시합니다." data-es="Las dependencias fluyen hacia dentro, la infraestructura implementa los puertos del dominio, sin ciclos. brooks-lint informa que el código limpio es limpio — y aun así ofrece una sugerencia de cara al futuro.">Dependencies flow inward, infra implements domain ports, no cycles. brooks-lint reports clean code as clean — and still offers one forward-looking suggestion.</p>
|
||||
<pre class="mermaid">graph TD
|
||||
subgraph API["API Layer"]
|
||||
OrderController; UserController
|
||||
@@ -314,71 +325,76 @@
|
||||
classDef clean fill:#51cf66,stroke:#2b8a3e,color:#fff
|
||||
class OrderController,UserController,OrderService,UserService,OrderModel,UserModel,OrderRepository,UserRepository,JpaOrderRepository,JpaUserRepository clean</pre>
|
||||
<div class="finding">
|
||||
<h4>🟢 Suggestion — Monitor application service growth</h4>
|
||||
<div class="ln sy"><b>Symptom:</b> <code>OrderService</code> and <code>UserService</code> are symmetric siblings that may accrue responsibilities without a split policy.</div>
|
||||
<div class="ln sr"><b>Source:</b> Brooks — The Mythical Man-Month — Conceptual Integrity</div>
|
||||
<div class="ln rx"><b>Remedy:</b> Document a "one service per use-case cluster" rule now, before the pattern calcifies.</div>
|
||||
<h4 data-en="🟢 Suggestion — Monitor application service growth" data-zh="🟢 建议 — 监控应用服务的膨胀" data-zhtw="🟢 建議 — 監控應用服務的膨脹" data-ja="🟢 提案 — アプリケーションサービスの肥大化を監視する" data-ko="🟢 제안 — 애플리케이션 서비스의 비대화를 모니터링하라" data-es="🟢 Sugerencia — Vigila el crecimiento de los servicios de aplicación">🟢 Suggestion — Monitor application service growth</h4>
|
||||
<div class="ln sy" data-en="<b>Symptom:</b> <code>OrderService</code> and <code>UserService</code> are symmetric siblings that may accrue responsibilities without a split policy." data-zh="<b>症状:</b> <code>OrderService</code> 与 <code>UserService</code> 是对称的同级,若无拆分策略可能不断累积职责。" data-zhtw="<b>症狀:</b> <code>OrderService</code> 與 <code>UserService</code> 是對稱的同級,若無拆分策略可能不斷累積職責。" data-ja="<b>症状:</b> <code>OrderService</code> と <code>UserService</code> は対称的な兄弟で、分割の方針がないと責務を蓄積しかねません。" data-ko="<b>증상:</b> <code>OrderService</code>와 <code>UserService</code>는 대칭적인 형제로, 분할 정책이 없으면 책임이 쌓일 수 있습니다." data-es="<b>Síntoma:</b> <code>OrderService</code> y <code>UserService</code> son hermanos simétricos que pueden acumular responsabilidades sin una política de división."><b>Symptom:</b> <code>OrderService</code> and <code>UserService</code> are symmetric siblings that may accrue responsibilities without a split policy.</div>
|
||||
<div class="ln sr" data-en="<b>Source:</b> Brooks — The Mythical Man-Month — Conceptual Integrity" data-zh="<b>根源:</b> Brooks — The Mythical Man-Month — 概念完整性(Conceptual Integrity)" data-zhtw="<b>根源:</b> Brooks — The Mythical Man-Month — 概念完整性(Conceptual Integrity)" data-ja="<b>根源:</b> Brooks — The Mythical Man-Month — 概念的完全性(Conceptual Integrity)" data-ko="<b>근원:</b> Brooks — The Mythical Man-Month — 개념적 무결성(Conceptual Integrity)" data-es="<b>Origen:</b> Brooks — The Mythical Man-Month — Conceptual Integrity (Integridad conceptual)"><b>Source:</b> Brooks — The Mythical Man-Month — Conceptual Integrity</div>
|
||||
<div class="ln rx" data-en="<b>Remedy:</b> Document a "one service per use-case cluster" rule now, before the pattern calcifies." data-zh="<b>修复:</b> 趁该模式尚未固化,现在就把“每个用例簇一个服务”的规则记录下来。" data-zhtw="<b>對策:</b> 趁該模式尚未固化,現在就把「每個用例叢集一個服務」的規則記錄下來。" data-ja="<b>対策:</b> パターンが固定化する前に、いま「ユースケースのまとまりごとに 1 サービス」という規則を文書化します。" data-ko="<b>처방:</b> 패턴이 굳어지기 전에 지금 “유스케이스 클러스터당 서비스 하나” 규칙을 문서화하세요." data-es="<b>Remedio:</b> Documenta ahora una regla de "un servicio por clúster de casos de uso", antes de que el patrón se calcifique."><b>Remedy:</b> Document a "one service per use-case cluster" rule now, before the pattern calcifies.</div>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<!-- ============ TECH DEBT ============ -->
|
||||
<section class="mode-group" data-mode="debt">
|
||||
<h2 class="mode-title">Tech Debt Assessment <span class="badge">Mode 3</span></h2>
|
||||
<p class="mode-sub">Classifies debt across the decay risks and scores each finding by Pain × Spread priority.</p>
|
||||
<h2 class="mode-title"><span data-en="Tech Debt Assessment" data-zh="技术债评估" data-zhtw="技術債評估" data-ja="技術的負債の評価" data-ko="기술 부채 평가" data-es="Evaluación de Deuda Técnica">Tech Debt Assessment</span> <span class="badge">Mode 3</span></h2>
|
||||
<p class="mode-sub" data-en="Classifies debt across the decay risks and scores each finding by Pain × Spread priority." data-zh="按各类衰退风险对债务分类,并以 Pain × Spread 优先级为每条发现评分。" data-zhtw="按各類衰退風險對債務分類,並以 Pain × Spread 優先級為每條發現評分。" data-ja="債務を各劣化リスクに分類し、各指摘を Pain × Spread の優先度でスコア付けします。" data-ko="부채를 각 부패 위험별로 분류하고, 각 발견을 Pain × Spread 우선순위로 점수화합니다." data-es="Clasifica la deuda según los riesgos de decadencia y puntúa cada hallazgo por prioridad Pain × Spread.">Classifies debt across the decay risks and scores each finding by Pain × Spread priority.</p>
|
||||
|
||||
<article class="case">
|
||||
<div class="case-head">
|
||||
<span class="lang java">Java</span>
|
||||
<h3>Shotgun Surgery across six files</h3>
|
||||
<h3 data-en="Shotgun Surgery across six files" data-zh="散落六个文件的霰弹式修改" data-zhtw="散落六個檔案的散彈式修改" data-ja="6 ファイルにまたがるショットガン手術" data-ko="여섯 파일에 흩어진 산탄총 수술" data-es="Cirugía con escopeta en seis archivos">Shotgun Surgery across six files</h3>
|
||||
<span class="hs mid">56 / 100</span>
|
||||
</div>
|
||||
<p class="case-summary">Adding a currency means editing six files in six unrelated layers — three Critical findings that share one root cause.</p>
|
||||
<p class="case-summary" data-en="Adding a currency means editing six files in six unrelated layers — three Critical findings that share one root cause." data-zh="新增一种货币意味着要在六个不相关的层里修改六个文件——三条共享同一根因的 Critical 发现。" data-zhtw="新增一種貨幣意味著要在六個不相關的層裡修改六個檔案——三條共享同一根因的 Critical 發現。" data-ja="通貨を 1 つ追加するには、無関係な 6 つのレイヤーで 6 ファイルを編集することになります——同じ根本原因を共有する 3 件の Critical 指摘です。" data-ko="통화 하나를 추가하려면 서로 무관한 여섯 계층에서 여섯 파일을 수정해야 합니다 — 하나의 근본 원인을 공유하는 세 건의 Critical 발견입니다." data-es="Añadir una moneda implica editar seis archivos en seis capas no relacionadas — tres hallazgos Critical que comparten una sola causa raíz.">Adding a currency means editing six files in six unrelated layers — three Critical findings that share one root cause.</p>
|
||||
<table class="dbt">
|
||||
<thead><tr><th>Risk</th><th>Findings</th><th>Avg Priority</th><th>Classification</th></tr></thead>
|
||||
<thead><tr>
|
||||
<th data-en="Risk" data-zh="风险" data-zhtw="風險" data-ja="リスク" data-ko="위험" data-es="Riesgo">Risk</th>
|
||||
<th data-en="Findings" data-zh="发现数" data-zhtw="發現數" data-ja="指摘数" data-ko="발견 수" data-es="Hallazgos">Findings</th>
|
||||
<th data-en="Avg Priority" data-zh="平均优先级" data-zhtw="平均優先級" data-ja="平均優先度" data-ko="평균 우선순위" data-es="Prioridad media">Avg Priority</th>
|
||||
<th data-en="Classification" data-zh="分类" data-zhtw="分類" data-ja="分類" data-ko="분류" data-es="Clasificación">Classification</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<tr><td>Change Propagation</td><td>2</td><td>6.5</td><td>Mixed (1 Critical + 1 Scheduled)</td></tr>
|
||||
<tr><td>Knowledge Duplication</td><td>1</td><td>9.0</td><td>Critical</td></tr>
|
||||
<tr><td>Domain Model Distortion</td><td>1</td><td>9.0</td><td>Critical</td></tr>
|
||||
<tr><td>Cognitive Overload</td><td>1</td><td>6.0</td><td>Scheduled</td></tr>
|
||||
<tr><td data-en="Change Propagation" data-zh="变更扩散" data-zhtw="變更擴散" data-ja="変更の波及" data-ko="변경 전파" data-es="Propagación de cambios">Change Propagation</td><td>2</td><td>6.5</td><td data-en="Mixed (1 Critical + 1 Scheduled)" data-zh="混合(1 严重 + 1 计划处理)" data-zhtw="混合(1 嚴重 + 1 計畫處理)" data-ja="混在(重大 1 + 計画対応 1)" data-ko="혼합 (심각 1 + 예정 1)" data-es="Mixto (1 Crítico + 1 Programado)">Mixed (1 Critical + 1 Scheduled)</td></tr>
|
||||
<tr><td data-en="Knowledge Duplication" data-zh="知识重复" data-zhtw="知識重複" data-ja="知識の重複" data-ko="지식 중복" data-es="Duplicación de conocimiento">Knowledge Duplication</td><td>1</td><td>9.0</td><td data-en="Critical" data-zh="严重" data-zhtw="嚴重" data-ja="重大" data-ko="심각" data-es="Crítico">Critical</td></tr>
|
||||
<tr><td data-en="Domain Model Distortion" data-zh="领域模型失真" data-zhtw="領域模型失真" data-ja="ドメインモデルの歪み" data-ko="도메인 모델 왜곡" data-es="Distorsión del modelo de dominio">Domain Model Distortion</td><td>1</td><td>9.0</td><td data-en="Critical" data-zh="严重" data-zhtw="嚴重" data-ja="重大" data-ko="심각" data-es="Crítico">Critical</td></tr>
|
||||
<tr><td data-en="Cognitive Overload" data-zh="认知过载" data-zhtw="認知過載" data-ja="認知的過負荷" data-ko="인지 과부하" data-es="Sobrecarga cognitiva">Cognitive Overload</td><td>1</td><td>6.0</td><td data-en="Scheduled" data-zh="计划处理" data-zhtw="計畫處理" data-ja="計画対応" data-ko="예정" data-es="Programado">Scheduled</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="finding">
|
||||
<h4>🔴 Change Propagation — Shotgun Surgery across six modules <span class="pri">Pain × Spread: 9</span></h4>
|
||||
<div class="ln sy"><b>Symptom:</b> Adding EUR requires editing 6 files in 6 distinct layers with no architectural relationship.</div>
|
||||
<div class="ln sr"><b>Source:</b> Fowler — Refactoring — Shotgun Surgery; Hunt & Thomas — Orthogonality</div>
|
||||
<div class="ln rx"><b>Remedy:</b> Introduce a <code>Money</code> value object and a <code>MoneyFormatter</code> service.</div>
|
||||
<h4 data-en="🔴 Change Propagation — Shotgun Surgery across six modules <span class="pri">Pain × Spread: 9</span>" data-zh="🔴 变更扩散 — 散落六个模块的霰弹式修改 <span class="pri">Pain × Spread: 9</span>" data-zhtw="🔴 變更擴散 — 散落六個模組的散彈式修改 <span class="pri">Pain × Spread: 9</span>" data-ja="🔴 変更の波及 — 6 モジュールにまたがるショットガン手術 <span class="pri">Pain × Spread: 9</span>" data-ko="🔴 변경 전파 — 여섯 모듈에 흩어진 산탄총 수술 <span class="pri">Pain × Spread: 9</span>" data-es="🔴 Propagación de cambios — Cirugía con escopeta en seis módulos <span class="pri">Pain × Spread: 9</span>">🔴 Change Propagation — Shotgun Surgery across six modules <span class="pri">Pain × Spread: 9</span></h4>
|
||||
<div class="ln sy" data-en="<b>Symptom:</b> Adding EUR requires editing 6 files in 6 distinct layers with no architectural relationship." data-zh="<b>症状:</b> 新增 EUR 需要在 6 个互不相关的层里修改 6 个文件。" data-zhtw="<b>症狀:</b> 新增 EUR 需要在 6 個互不相關的層裡修改 6 個檔案。" data-ja="<b>症状:</b> EUR の追加には、アーキテクチャ上の関連がない 6 つのレイヤーで 6 ファイルを編集する必要があります。" data-ko="<b>증상:</b> EUR를 추가하려면 아키텍처상 관련이 없는 여섯 계층에서 여섯 파일을 수정해야 합니다." data-es="<b>Síntoma:</b> Añadir EUR requiere editar 6 archivos en 6 capas distintas sin relación arquitectónica."><b>Symptom:</b> Adding EUR requires editing 6 files in 6 distinct layers with no architectural relationship.</div>
|
||||
<div class="ln sr" data-en="<b>Source:</b> Fowler — Refactoring — Shotgun Surgery; Hunt & Thomas — Orthogonality" data-zh="<b>根源:</b> Fowler — Refactoring — 霰弹式修改(Shotgun Surgery);Hunt & Thomas — 正交性(Orthogonality)" data-zhtw="<b>根源:</b> Fowler — Refactoring — 散彈式修改(Shotgun Surgery);Hunt & Thomas — 正交性(Orthogonality)" data-ja="<b>根源:</b> Fowler — Refactoring — ショットガン手術(Shotgun Surgery);Hunt & Thomas — 直交性(Orthogonality)" data-ko="<b>근원:</b> Fowler — Refactoring — 산탄총 수술(Shotgun Surgery); Hunt & Thomas — 직교성(Orthogonality)" data-es="<b>Origen:</b> Fowler — Refactoring — Shotgun Surgery (Cirugía con escopeta); Hunt & Thomas — Orthogonality (Ortogonalidad)"><b>Source:</b> Fowler — Refactoring — Shotgun Surgery; Hunt & Thomas — Orthogonality</div>
|
||||
<div class="ln rx" data-en="<b>Remedy:</b> Introduce a <code>Money</code> value object and a <code>MoneyFormatter</code> service." data-zh="<b>修复:</b> 引入一个 <code>Money</code> 值对象和一个 <code>MoneyFormatter</code> 服务。" data-zhtw="<b>對策:</b> 引入一個 <code>Money</code> 值物件和一個 <code>MoneyFormatter</code> 服務。" data-ja="<b>対策:</b> <code>Money</code> 値オブジェクトと <code>MoneyFormatter</code> サービスを導入します。" data-ko="<b>처방:</b> <code>Money</code> 값 객체와 <code>MoneyFormatter</code> 서비스를 도입하세요." data-es="<b>Remedio:</b> Introduce un objeto de valor <code>Money</code> y un servicio <code>MoneyFormatter</code>."><b>Remedy:</b> Introduce a <code>Money</code> value object and a <code>MoneyFormatter</code> service.</div>
|
||||
</div>
|
||||
<div class="finding">
|
||||
<h4>🔴 Knowledge Duplication — <code>$</code> as a magic literal in five files <span class="pri">Pain × Spread: 9</span></h4>
|
||||
<div class="ln sy"><b>Symptom:</b> The string <code>"$"</code> appears in 5 independent locations with no shared constant.</div>
|
||||
<div class="ln sr"><b>Source:</b> Hunt & Thomas — DRY; McConnell — Code Complete — Ch. 12</div>
|
||||
<div class="ln rx"><b>Remedy:</b> Use <code>Currency.getSymbol(Locale)</code> in <code>MoneyFormatter</code>; remove all <code>"$"</code> literals.</div>
|
||||
<h4 data-en="🔴 Knowledge Duplication — <code>$</code> as a magic literal in five files <span class="pri">Pain × Spread: 9</span>" data-zh="🔴 知识重复 — <code>$</code> 作为魔法字面量散落在五个文件 <span class="pri">Pain × Spread: 9</span>" data-zhtw="🔴 知識重複 — <code>$</code> 作為魔法字面量散落在五個檔案 <span class="pri">Pain × Spread: 9</span>" data-ja="🔴 知識の重複 — <code>$</code> がマジックリテラルとして 5 ファイルに散在 <span class="pri">Pain × Spread: 9</span>" data-ko="🔴 지식 중복 — <code>$</code>가 매직 리터럴로 다섯 파일에 흩어짐 <span class="pri">Pain × Spread: 9</span>" data-es="🔴 Duplicación de conocimiento — <code>$</code> como literal mágico en cinco archivos <span class="pri">Pain × Spread: 9</span>">🔴 Knowledge Duplication — <code>$</code> as a magic literal in five files <span class="pri">Pain × Spread: 9</span></h4>
|
||||
<div class="ln sy" data-en="<b>Symptom:</b> The string <code>"$"</code> appears in 5 independent locations with no shared constant." data-zh="<b>症状:</b> 字符串 <code>"$"</code> 出现在 5 个互不相关的位置,没有共享常量。" data-zhtw="<b>症狀:</b> 字串 <code>"$"</code> 出現在 5 個互不相關的位置,沒有共享常數。" data-ja="<b>症状:</b> 文字列 <code>"$"</code> が共有定数を持たないまま 5 つの独立した箇所に現れています。" data-ko="<b>증상:</b> 문자열 <code>"$"</code>가 공유 상수 없이 다섯 개의 독립된 위치에 나타납니다." data-es="<b>Síntoma:</b> La cadena <code>"$"</code> aparece en 5 ubicaciones independientes sin una constante compartida."><b>Symptom:</b> The string <code>"$"</code> appears in 5 independent locations with no shared constant.</div>
|
||||
<div class="ln sr" data-en="<b>Source:</b> Hunt & Thomas — DRY; McConnell — Code Complete — Ch. 12" data-zh="<b>根源:</b> Hunt & Thomas — DRY;McConnell — Code Complete — 第 12 章" data-zhtw="<b>根源:</b> Hunt & Thomas — DRY;McConnell — Code Complete — 第 12 章" data-ja="<b>根源:</b> Hunt & Thomas — DRY;McConnell — Code Complete — 第 12 章" data-ko="<b>근원:</b> Hunt & Thomas — DRY; McConnell — Code Complete — 12장" data-es="<b>Origen:</b> Hunt & Thomas — DRY; McConnell — Code Complete — Cap. 12"><b>Source:</b> Hunt & Thomas — DRY; McConnell — Code Complete — Ch. 12</div>
|
||||
<div class="ln rx" data-en="<b>Remedy:</b> Use <code>Currency.getSymbol(Locale)</code> in <code>MoneyFormatter</code>; remove all <code>"$"</code> literals." data-zh="<b>修复:</b> 在 <code>MoneyFormatter</code> 中使用 <code>Currency.getSymbol(Locale)</code>;移除所有 <code>"$"</code> 字面量。" data-zhtw="<b>對策:</b> 在 <code>MoneyFormatter</code> 中使用 <code>Currency.getSymbol(Locale)</code>;移除所有 <code>"$"</code> 字面量。" data-ja="<b>対策:</b> <code>MoneyFormatter</code> で <code>Currency.getSymbol(Locale)</code> を使い、すべての <code>"$"</code> リテラルを削除します。" data-ko="<b>처방:</b> <code>MoneyFormatter</code>에서 <code>Currency.getSymbol(Locale)</code>를 사용하고 모든 <code>"$"</code> 리터럴을 제거하세요." data-es="<b>Remedio:</b> Usa <code>Currency.getSymbol(Locale)</code> en <code>MoneyFormatter</code>; elimina todos los literales <code>"$"</code>."><b>Remedy:</b> Use <code>Currency.getSymbol(Locale)</code> in <code>MoneyFormatter</code>; remove all <code>"$"</code> literals.</div>
|
||||
</div>
|
||||
<div class="finding">
|
||||
<h4>🔴 Domain Model Distortion — No <code>Money</code> type exists <span class="pri">Pain × Spread: 9</span></h4>
|
||||
<div class="ln sy"><b>Symptom:</b> All price/amount fields are raw <code>double</code>.</div>
|
||||
<div class="ln sr"><b>Source:</b> Evans — DDD — Domain Model; Fowler — Refactoring — Data Class</div>
|
||||
<div class="ln rx"><b>Remedy:</b> Introduce <code>record Money(BigDecimal amount, Currency currency)</code>.</div>
|
||||
<h4 data-en="🔴 Domain Model Distortion — No <code>Money</code> type exists <span class="pri">Pain × Spread: 9</span>" data-zh="🔴 领域模型失真 — 不存在 <code>Money</code> 类型 <span class="pri">Pain × Spread: 9</span>" data-zhtw="🔴 領域模型失真 — 不存在 <code>Money</code> 型別 <span class="pri">Pain × Spread: 9</span>" data-ja="🔴 ドメインモデルの歪み — <code>Money</code> 型が存在しない <span class="pri">Pain × Spread: 9</span>" data-ko="🔴 도메인 모델 왜곡 — <code>Money</code> 타입이 존재하지 않음 <span class="pri">Pain × Spread: 9</span>" data-es="🔴 Distorsión del modelo de dominio — No existe un tipo <code>Money</code> <span class="pri">Pain × Spread: 9</span>">🔴 Domain Model Distortion — No <code>Money</code> type exists <span class="pri">Pain × Spread: 9</span></h4>
|
||||
<div class="ln sy" data-en="<b>Symptom:</b> All price/amount fields are raw <code>double</code>." data-zh="<b>症状:</b> 所有价格/金额字段都是裸 <code>double</code>。" data-zhtw="<b>症狀:</b> 所有價格/金額欄位都是裸 <code>double</code>。" data-ja="<b>症状:</b> すべての価格・金額フィールドが生の <code>double</code> です。" data-ko="<b>증상:</b> 모든 가격/금액 필드가 원시 <code>double</code>입니다." data-es="<b>Síntoma:</b> Todos los campos de precio/importe son <code>double</code> sin envolver."><b>Symptom:</b> All price/amount fields are raw <code>double</code>.</div>
|
||||
<div class="ln sr" data-en="<b>Source:</b> Evans — DDD — Domain Model; Fowler — Refactoring — Data Class" data-zh="<b>根源:</b> Evans — DDD — 领域模型(Domain Model);Fowler — Refactoring — 数据类(Data Class)" data-zhtw="<b>根源:</b> Evans — DDD — 領域模型(Domain Model);Fowler — Refactoring — 資料類別(Data Class)" data-ja="<b>根源:</b> Evans — DDD — ドメインモデル(Domain Model);Fowler — Refactoring — データクラス(Data Class)" data-ko="<b>근원:</b> Evans — DDD — 도메인 모델(Domain Model); Fowler — Refactoring — 데이터 클래스(Data Class)" data-es="<b>Origen:</b> Evans — DDD — Domain Model (Modelo de dominio); Fowler — Refactoring — Data Class (Clase de datos)"><b>Source:</b> Evans — DDD — Domain Model; Fowler — Refactoring — Data Class</div>
|
||||
<div class="ln rx" data-en="<b>Remedy:</b> Introduce <code>record Money(BigDecimal amount, Currency currency)</code>." data-zh="<b>修复:</b> 引入 <code>record Money(BigDecimal amount, Currency currency)</code>。" data-zhtw="<b>對策:</b> 引入 <code>record Money(BigDecimal amount, Currency currency)</code>。" data-ja="<b>対策:</b> <code>record Money(BigDecimal amount, Currency currency)</code> を導入します。" data-ko="<b>처방:</b> <code>record Money(BigDecimal amount, Currency currency)</code>를 도입하세요." data-es="<b>Remedio:</b> Introduce <code>record Money(BigDecimal amount, Currency currency)</code>."><b>Remedy:</b> Introduce <code>record Money(BigDecimal amount, Currency currency)</code>.</div>
|
||||
</div>
|
||||
<div class="note"><b>Recommended focus:</b> All three Critical findings share one root cause — the absence of a <code>Money</code> value object. One intervention collapses three findings.</div>
|
||||
<div class="note" data-en="<b>Recommended focus:</b> All three Critical findings share one root cause — the absence of a <code>Money</code> value object. One intervention collapses three findings." data-zh="<b>建议聚焦:</b> 三条 Critical 发现共享同一根因——缺少 <code>Money</code> 值对象。一次干预即可消解三条发现。" data-zhtw="<b>建議聚焦:</b> 三條 Critical 發現共享同一根因——缺少 <code>Money</code> 值物件。一次介入即可消解三條發現。" data-ja="<b>推奨フォーカス:</b> 3 件の Critical 指摘は同じ根本原因——<code>Money</code> 値オブジェクトの欠如——を共有しています。一度の介入で 3 件の指摘が解消されます。" data-ko="<b>권장 초점:</b> 세 건의 Critical 발견은 하나의 근본 원인—<code>Money</code> 값 객체의 부재—을 공유합니다. 한 번의 개입으로 세 발견이 한꺼번에 해소됩니다." data-es="<b>Foco recomendado:</b> Los tres hallazgos Critical comparten una sola causa raíz — la ausencia de un objeto de valor <code>Money</code>. Una sola intervención colapsa tres hallazgos."><b>Recommended focus:</b> All three Critical findings share one root cause — the absence of a <code>Money</code> value object. One intervention collapses three findings.</div>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<!-- ============ TEST QUALITY ============ -->
|
||||
<section class="mode-group" data-mode="test">
|
||||
<h2 class="mode-title">Test Quality Review <span class="badge">Mode 4</span></h2>
|
||||
<p class="mode-sub">Audits an existing suite against six test-space decay risks (T1–T6).</p>
|
||||
<h2 class="mode-title"><span data-en="Test Quality Review" data-zh="测试质量审查" data-zhtw="測試品質審查" data-ja="テスト品質レビュー" data-ko="테스트 품질 리뷰" data-es="Revisión de Calidad de Pruebas">Test Quality Review</span> <span class="badge">Mode 4</span></h2>
|
||||
<p class="mode-sub" data-en="Audits an existing suite against six test-space decay risks (T1–T6)." data-zh="对照六种测试空间衰退风险(T1–T6)审计现有测试套件。" data-zhtw="對照六種測試空間衰退風險(T1–T6)審計現有測試套件。" data-ja="6 つのテスト領域の劣化リスク(T1–T6)に照らして既存のスイートを監査します。" data-ko="여섯 가지 테스트 영역 부패 위험(T1–T6)에 대해 기존 스위트를 감사합니다." data-es="Audita una suite existente frente a seis riesgos de decadencia del espacio de pruebas (T1–T6).">Audits an existing suite against six test-space decay risks (T1–T6).</p>
|
||||
|
||||
<article class="case">
|
||||
<div class="case-head">
|
||||
<span class="lang ts">TypeScript</span>
|
||||
<h3>Mock abuse</h3>
|
||||
<h3 data-en="Mock abuse" data-zh="Mock 滥用" data-zhtw="Mock 濫用" data-ja="モックの乱用" data-ko="목 남용" data-es="Abuso de mocks">Mock abuse</h3>
|
||||
<span class="hs mid">60 / 100</span>
|
||||
</div>
|
||||
<p class="case-summary">Seven mocks per test, 14 lines of setup vs 6 of assertions — the service is never tested against a real collaborator, and the return value is never checked.</p>
|
||||
<p class="case-summary" data-en="Seven mocks per test, 14 lines of setup vs 6 of assertions — the service is never tested against a real collaborator, and the return value is never checked." data-zh="每个测试七个 mock,14 行 setup 对 6 行断言——该服务从未对真实协作者做过测试,返回值也从未被检查。" data-zhtw="每個測試七個 mock,14 行 setup 對 6 行斷言——該服務從未對真實協作者做過測試,回傳值也從未被檢查。" data-ja="テストごとに 7 つのモック、14 行のセットアップに対して 6 行のアサーション——このサービスは実際の協力オブジェクトに対して一度もテストされず、戻り値も検証されていません。" data-ko="테스트당 일곱 개의 목, 설정 14줄 대 단언 6줄 — 이 서비스는 실제 협력자에 대해 한 번도 테스트되지 않으며 반환값도 검사되지 않습니다." data-es="Siete mocks por prueba, 14 líneas de preparación frente a 6 de aserciones — el servicio nunca se prueba contra un colaborador real y el valor de retorno nunca se comprueba.">Seven mocks per test, 14 lines of setup vs 6 of assertions — the service is never tested against a real collaborator, and the return value is never checked.</p>
|
||||
<details>
|
||||
<summary>▸ Input test</summary>
|
||||
<summary data-en="▸ Input test" data-zh="▸ 输入测试" data-zhtw="▸ 輸入測試" data-ja="▸ 入力テスト" data-ko="▸ 입력 테스트" data-es="▸ Prueba de entrada">▸ Input test</summary>
|
||||
<pre class="code">it('should place an order successfully', () => {
|
||||
const mockDb = mock<Database>();
|
||||
const mockPayment = mock<PaymentGateway>();
|
||||
@@ -399,28 +415,28 @@
|
||||
});</pre>
|
||||
</details>
|
||||
<div class="finding">
|
||||
<h4>🔴 Mock Abuse — Seven mocks per test; setup dominates logic</h4>
|
||||
<div class="ln sy"><b>Symptom:</b> 7 mock objects; 14 lines of setup vs 6 of assertions. No real collaborator is ever exercised.</div>
|
||||
<div class="ln sr"><b>Source:</b> Osherove — The Art of Unit Testing (mock count > 3); Meszaros — xUnit Test Patterns</div>
|
||||
<div class="ln rx"><b>Remedy:</b> Reduce mocks to ≤ 3, use in-memory fakes, assert on <code>result</code> first.</div>
|
||||
<h4 data-en="🔴 Mock Abuse — Seven mocks per test; setup dominates logic" data-zh="🔴 Mock 滥用 — 每个测试七个 mock;setup 盖过了逻辑" data-zhtw="🔴 Mock 濫用 — 每個測試七個 mock;setup 蓋過了邏輯" data-ja="🔴 モックの乱用 — テストごとに 7 つのモック;セットアップがロジックを圧倒" data-ko="🔴 목 남용 — 테스트당 일곱 개의 목; 설정이 로직을 압도함" data-es="🔴 Abuso de mocks — Siete mocks por prueba; la preparación domina la lógica">🔴 Mock Abuse — Seven mocks per test; setup dominates logic</h4>
|
||||
<div class="ln sy" data-en="<b>Symptom:</b> 7 mock objects; 14 lines of setup vs 6 of assertions. No real collaborator is ever exercised." data-zh="<b>症状:</b> 7 个 mock 对象;14 行 setup 对 6 行断言。从未运行任何真实协作者。" data-zhtw="<b>症狀:</b> 7 個 mock 物件;14 行 setup 對 6 行斷言。從未執行任何真實協作者。" data-ja="<b>症状:</b> 7 つのモックオブジェクト;14 行のセットアップに対して 6 行のアサーション。実際の協力オブジェクトは一度も動かされません。" data-ko="<b>증상:</b> 일곱 개의 목 객체; 설정 14줄 대 단언 6줄. 실제 협력자는 한 번도 실행되지 않습니다." data-es="<b>Síntoma:</b> 7 objetos mock; 14 líneas de preparación frente a 6 de aserciones. Nunca se ejercita un colaborador real."><b>Symptom:</b> 7 mock objects; 14 lines of setup vs 6 of assertions. No real collaborator is ever exercised.</div>
|
||||
<div class="ln sr" data-en="<b>Source:</b> Osherove — The Art of Unit Testing (mock count > 3); Meszaros — xUnit Test Patterns" data-zh="<b>根源:</b> Osherove — The Art of Unit Testing(mock 数量 > 3);Meszaros — xUnit Test Patterns" data-zhtw="<b>根源:</b> Osherove — The Art of Unit Testing(mock 數量 > 3);Meszaros — xUnit Test Patterns" data-ja="<b>根源:</b> Osherove — The Art of Unit Testing(モック数 > 3);Meszaros — xUnit Test Patterns" data-ko="<b>근원:</b> Osherove — The Art of Unit Testing(목 개수 > 3); Meszaros — xUnit Test Patterns" data-es="<b>Origen:</b> Osherove — The Art of Unit Testing (número de mocks > 3); Meszaros — xUnit Test Patterns"><b>Source:</b> Osherove — The Art of Unit Testing (mock count > 3); Meszaros — xUnit Test Patterns</div>
|
||||
<div class="ln rx" data-en="<b>Remedy:</b> Reduce mocks to ≤ 3, use in-memory fakes, assert on <code>result</code> first." data-zh="<b>修复:</b> 将 mock 减少到 ≤ 3,使用内存级 fake,先对 <code>result</code> 断言。" data-zhtw="<b>對策:</b> 將 mock 減少到 ≤ 3,使用記憶體級 fake,先對 <code>result</code> 斷言。" data-ja="<b>対策:</b> モックを 3 つ以下に減らし、インメモリの fake を使い、まず <code>result</code> をアサートします。" data-ko="<b>처방:</b> 목을 3개 이하로 줄이고, 인메모리 fake를 사용하며, 먼저 <code>result</code>를 단언하세요." data-es="<b>Remedio:</b> Reduce los mocks a ≤ 3, usa fakes en memoria y verifica primero <code>result</code>."><b>Remedy:</b> Reduce mocks to ≤ 3, use in-memory fakes, assert on <code>result</code> first.</div>
|
||||
</div>
|
||||
<div class="finding">
|
||||
<h4>🔴 Mock Abuse — All six assertions verify mock calls, not behaviour</h4>
|
||||
<div class="ln sy"><b>Symptom:</b> Every assertion is <code>toHaveBeenCalledWith</code>; <code>result</code> is captured but never asserted.</div>
|
||||
<div class="ln cq"><b>Consequence:</b> A <code>placeOrder</code> that calls every mock yet returns <code>null</code> or double-charges still passes.</div>
|
||||
<div class="ln rx"><b>Remedy:</b> Assert observable output: <code>expect(result.status).toBe('confirmed')</code>.</div>
|
||||
<h4 data-en="🔴 Mock Abuse — All six assertions verify mock calls, not behaviour" data-zh="🔴 Mock 滥用 — 全部六条断言验证的是 mock 调用,而非行为" data-zhtw="🔴 Mock 濫用 — 全部六條斷言驗證的是 mock 呼叫,而非行為" data-ja="🔴 モックの乱用 — 6 つのアサーションはすべて振る舞いではなくモック呼び出しを検証" data-ko="🔴 목 남용 — 여섯 단언 모두 행위가 아니라 목 호출을 검증함" data-es="🔴 Abuso de mocks — Las seis aserciones verifican llamadas a mocks, no comportamiento">🔴 Mock Abuse — All six assertions verify mock calls, not behaviour</h4>
|
||||
<div class="ln sy" data-en="<b>Symptom:</b> Every assertion is <code>toHaveBeenCalledWith</code>; <code>result</code> is captured but never asserted." data-zh="<b>症状:</b> 每条断言都是 <code>toHaveBeenCalledWith</code>;<code>result</code> 被捕获却从未被断言。" data-zhtw="<b>症狀:</b> 每條斷言都是 <code>toHaveBeenCalledWith</code>;<code>result</code> 被捕獲卻從未被斷言。" data-ja="<b>症状:</b> アサーションはすべて <code>toHaveBeenCalledWith</code> で、<code>result</code> は受け取られるものの一度もアサートされません。" data-ko="<b>증상:</b> 모든 단언이 <code>toHaveBeenCalledWith</code>이며, <code>result</code>는 받아두지만 한 번도 단언되지 않습니다." data-es="<b>Síntoma:</b> Cada aserción es <code>toHaveBeenCalledWith</code>; <code>result</code> se captura pero nunca se verifica."><b>Symptom:</b> Every assertion is <code>toHaveBeenCalledWith</code>; <code>result</code> is captured but never asserted.</div>
|
||||
<div class="ln cq" data-en="<b>Consequence:</b> A <code>placeOrder</code> that calls every mock yet returns <code>null</code> or double-charges still passes." data-zh="<b>后果:</b> 一个调用了每个 mock 却返回 <code>null</code> 或重复扣款的 <code>placeOrder</code> 依然能通过测试。" data-zhtw="<b>後果:</b> 一個呼叫了每個 mock 卻回傳 <code>null</code> 或重複扣款的 <code>placeOrder</code> 依然能通過測試。" data-ja="<b>結果:</b> すべてのモックを呼び出しつつ <code>null</code> を返したり二重請求したりする <code>placeOrder</code> でもテストが通ってしまいます。" data-ko="<b>결과:</b> 모든 목을 호출하면서도 <code>null</code>을 반환하거나 이중 청구하는 <code>placeOrder</code>도 여전히 통과합니다." data-es="<b>Consecuencia:</b> Un <code>placeOrder</code> que llama a todos los mocks pero devuelve <code>null</code> o cobra dos veces sigue pasando."><b>Consequence:</b> A <code>placeOrder</code> that calls every mock yet returns <code>null</code> or double-charges still passes.</div>
|
||||
<div class="ln rx" data-en="<b>Remedy:</b> Assert observable output: <code>expect(result.status).toBe('confirmed')</code>." data-zh="<b>修复:</b> 断言可观察的输出:<code>expect(result.status).toBe('confirmed')</code>。" data-zhtw="<b>對策:</b> 斷言可觀察的輸出:<code>expect(result.status).toBe('confirmed')</code>。" data-ja="<b>対策:</b> 観測可能な出力をアサートします:<code>expect(result.status).toBe('confirmed')</code>。" data-ko="<b>처방:</b> 관찰 가능한 출력을 단언하세요: <code>expect(result.status).toBe('confirmed')</code>." data-es="<b>Remedio:</b> Verifica la salida observable: <code>expect(result.status).toBe('confirmed')</code>."><b>Remedy:</b> Assert observable output: <code>expect(result.status).toBe('confirmed')</code>.</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="case">
|
||||
<div class="case-head">
|
||||
<span class="lang py">Python</span>
|
||||
<h3>Inverted test pyramid</h3>
|
||||
<h3 data-en="Inverted test pyramid" data-zh="倒置的测试金字塔" data-zhtw="倒置的測試金字塔" data-ja="逆さまのテストピラミッド" data-ko="역전된 테스트 피라미드" data-es="Pirámide de pruebas invertida">Inverted test pyramid</h3>
|
||||
<span class="hs mid">55 / 100</span>
|
||||
</div>
|
||||
<p class="case-summary">Only 16% of tests are unit tests; an E2E-heavy suite takes ~9 minutes and blocks fast CI feedback.</p>
|
||||
<p class="case-summary" data-en="Only 16% of tests are unit tests; an E2E-heavy suite takes ~9 minutes and blocks fast CI feedback." data-zh="只有 16% 的测试是单元测试;偏重 E2E 的套件耗时约 9 分钟,阻碍了 CI 的快速反馈。" data-zhtw="只有 16% 的測試是單元測試;偏重 E2E 的套件耗時約 9 分鐘,阻礙了 CI 的快速回饋。" data-ja="ユニットテストは全体のわずか 16%。E2E に偏ったスイートは約 9 分かかり、CI の高速なフィードバックを妨げます。" data-ko="단위 테스트는 전체의 16%에 불과합니다; E2E에 치우친 스위트는 약 9분이 걸려 CI의 빠른 피드백을 가로막습니다." data-es="Solo el 16% de las pruebas son unitarias; una suite cargada de E2E tarda ~9 minutos y bloquea la retroalimentación rápida de CI.">Only 16% of tests are unit tests; an E2E-heavy suite takes ~9 minutes and blocks fast CI feedback.</p>
|
||||
<details>
|
||||
<summary>▸ Suite overview</summary>
|
||||
<summary data-en="▸ Suite overview" data-zh="▸ 套件概览" data-zhtw="▸ 套件概覽" data-ja="▸ スイート概要" data-ko="▸ 스위트 개요" data-es="▸ Resumen de la suite">▸ Suite overview</summary>
|
||||
<pre class="code">tests/
|
||||
├── e2e/ 47 tests, avg 8s each (~6 min)
|
||||
├── integration/ 83 tests, avg 2s each (~3 min)
|
||||
@@ -431,21 +447,21 @@ Actual ratio: Unit 16% : Integration 54% : E2E 30%
|
||||
Target ratio: Unit 70% : Integration 20% : E2E 10%</pre>
|
||||
</details>
|
||||
<div class="finding">
|
||||
<h4>🔴 Architecture Mismatch — Fully inverted test pyramid</h4>
|
||||
<div class="ln sy"><b>Symptom:</b> Only 24 of 154 tests (16%) are unit tests; E2E + integration = 84%.</div>
|
||||
<div class="ln sr"><b>Source:</b> Google — How Google Tests Software — 70:20:10; Meszaros — xUnit Test Patterns</div>
|
||||
<div class="ln rx"><b>Remedy:</b> Target 70% unit; reduce E2E to 5–8 critical smoke tests.</div>
|
||||
<h4 data-en="🔴 Architecture Mismatch — Fully inverted test pyramid" data-zh="🔴 架构错配 — 完全倒置的测试金字塔" data-zhtw="🔴 架構錯配 — 完全倒置的測試金字塔" data-ja="🔴 アーキテクチャの不一致 — 完全に逆さまのテストピラミッド" data-ko="🔴 아키텍처 불일치 — 완전히 역전된 테스트 피라미드" data-es="🔴 Desajuste de arquitectura — Pirámide de pruebas totalmente invertida">🔴 Architecture Mismatch — Fully inverted test pyramid</h4>
|
||||
<div class="ln sy" data-en="<b>Symptom:</b> Only 24 of 154 tests (16%) are unit tests; E2E + integration = 84%." data-zh="<b>症状:</b> 154 个测试中只有 24 个(16%)是单元测试;E2E + 集成测试占 84%。" data-zhtw="<b>症狀:</b> 154 個測試中只有 24 個(16%)是單元測試;E2E + 整合測試佔 84%。" data-ja="<b>症状:</b> 154 件のテストのうちユニットテストはわずか 24 件(16%);E2E + 統合テストで 84% です。" data-ko="<b>증상:</b> 154개 테스트 중 단위 테스트는 24개(16%)뿐이며, E2E + 통합이 84%입니다." data-es="<b>Síntoma:</b> Solo 24 de 154 pruebas (16%) son unitarias; E2E + integración = 84%."><b>Symptom:</b> Only 24 of 154 tests (16%) are unit tests; E2E + integration = 84%.</div>
|
||||
<div class="ln sr" data-en="<b>Source:</b> Google — How Google Tests Software — 70:20:10; Meszaros — xUnit Test Patterns" data-zh="<b>根源:</b> Google — How Google Tests Software — 70:20:10;Meszaros — xUnit Test Patterns" data-zhtw="<b>根源:</b> Google — How Google Tests Software — 70:20:10;Meszaros — xUnit Test Patterns" data-ja="<b>根源:</b> Google — How Google Tests Software — 70:20:10;Meszaros — xUnit Test Patterns" data-ko="<b>근원:</b> Google — How Google Tests Software — 70:20:10; Meszaros — xUnit Test Patterns" data-es="<b>Origen:</b> Google — How Google Tests Software — 70:20:10; Meszaros — xUnit Test Patterns"><b>Source:</b> Google — How Google Tests Software — 70:20:10; Meszaros — xUnit Test Patterns</div>
|
||||
<div class="ln rx" data-en="<b>Remedy:</b> Target 70% unit; reduce E2E to 5–8 critical smoke tests." data-zh="<b>修复:</b> 目标 70% 单元测试;将 E2E 缩减到 5–8 个关键冒烟测试。" data-zhtw="<b>對策:</b> 目標 70% 單元測試;將 E2E 縮減到 5–8 個關鍵冒煙測試。" data-ja="<b>対策:</b> ユニットテスト 70% を目標にし、E2E を 5~8 件の重要なスモークテストに削減します。" data-ko="<b>처방:</b> 단위 테스트 70%를 목표로 하고, E2E를 핵심 스모크 테스트 5~8개로 줄이세요." data-es="<b>Remedio:</b> Apunta a un 70% de pruebas unitarias; reduce E2E a 5–8 pruebas de humo críticas."><b>Remedy:</b> Target 70% unit; reduce E2E to 5–8 critical smoke tests.</div>
|
||||
</div>
|
||||
<div class="finding">
|
||||
<h4>🔴 Architecture Mismatch — 9-minute suite blocks CI fast-feedback</h4>
|
||||
<div class="ln sy"><b>Symptom:</b> Full suite ~542s, dominated by 8s E2E tests.</div>
|
||||
<div class="ln rx"><b>Remedy:</b> Split CI: (1) unit only, < 60s, blocks merge; (2) integration + E2E async, non-blocking.</div>
|
||||
<h4 data-en="🔴 Architecture Mismatch — 9-minute suite blocks CI fast-feedback" data-zh="🔴 架构错配 — 9 分钟的套件阻碍了 CI 快速反馈" data-zhtw="🔴 架構錯配 — 9 分鐘的套件阻礙了 CI 快速回饋" data-ja="🔴 アーキテクチャの不一致 — 9 分のスイートが CI の高速フィードバックを妨げる" data-ko="🔴 아키텍처 불일치 — 9분짜리 스위트가 CI 빠른 피드백을 가로막음" data-es="🔴 Desajuste de arquitectura — La suite de 9 minutos bloquea la retroalimentación rápida de CI">🔴 Architecture Mismatch — 9-minute suite blocks CI fast-feedback</h4>
|
||||
<div class="ln sy" data-en="<b>Symptom:</b> Full suite ~542s, dominated by 8s E2E tests." data-zh="<b>症状:</b> 完整套件约 542 秒,由耗时 8 秒的 E2E 测试主导。" data-zhtw="<b>症狀:</b> 完整套件約 542 秒,由耗時 8 秒的 E2E 測試主導。" data-ja="<b>症状:</b> スイート全体で約 542 秒、8 秒かかる E2E テストが大半を占めます。" data-ko="<b>증상:</b> 전체 스위트 약 542초로, 8초짜리 E2E 테스트가 대부분을 차지합니다." data-es="<b>Síntoma:</b> Suite completa ~542s, dominada por pruebas E2E de 8s."><b>Symptom:</b> Full suite ~542s, dominated by 8s E2E tests.</div>
|
||||
<div class="ln rx" data-en="<b>Remedy:</b> Split CI: (1) unit only, < 60s, blocks merge; (2) integration + E2E async, non-blocking." data-zh="<b>修复:</b> 拆分 CI:(1) 仅单元测试,< 60 秒,阻塞合并;(2) 集成 + E2E 异步运行,不阻塞。" data-zhtw="<b>對策:</b> 拆分 CI:(1) 僅單元測試,< 60 秒,阻塞合併;(2) 整合 + E2E 非同步執行,不阻塞。" data-ja="<b>対策:</b> CI を分割します:(1) ユニットのみ、< 60 秒、マージをブロック;(2) 統合 + E2E は非同期で非ブロッキング。" data-ko="<b>처방:</b> CI를 분리하세요: (1) 단위만, < 60초, 병합 차단; (2) 통합 + E2E 비동기, 비차단." data-es="<b>Remedio:</b> Divide el CI: (1) solo unitarias, < 60s, bloquea el merge; (2) integración + E2E asíncronas, sin bloquear."><b>Remedy:</b> Split CI: (1) unit only, < 60s, blocks merge; (2) integration + E2E async, non-blocking.</div>
|
||||
</div>
|
||||
<div class="finding">
|
||||
<h4>🟡 Coverage Illusion — Core domain untested at unit level</h4>
|
||||
<div class="ln sy"><b>Symptom:</b> <code>tests/unit/</code> covers only validators and formatters — no checkout, login, order, or payment.</div>
|
||||
<div class="ln sr"><b>Source:</b> Feathers — Working Effectively with Legacy Code</div>
|
||||
<div class="ln rx"><b>Remedy:</b> Start with <code>test_checkout_flow.py</code> and <code>test_payment_api.py</code>.</div>
|
||||
<h4 data-en="🟡 Coverage Illusion — Core domain untested at unit level" data-zh="🟡 覆盖率幻觉 — 核心领域在单元层面未被测试" data-zhtw="🟡 覆蓋率幻覺 — 核心領域在單元層面未被測試" data-ja="🟡 カバレッジの錯覚 — コアドメインがユニットレベルで未テスト" data-ko="🟡 커버리지 착시 — 핵심 도메인이 단위 수준에서 미검증" data-es="🟡 Ilusión de cobertura — El dominio central no se prueba a nivel unitario">🟡 Coverage Illusion — Core domain untested at unit level</h4>
|
||||
<div class="ln sy" data-en="<b>Symptom:</b> <code>tests/unit/</code> covers only validators and formatters — no checkout, login, order, or payment." data-zh="<b>症状:</b> <code>tests/unit/</code> 只覆盖了校验器和格式化器——没有结账、登录、订单或支付。" data-zhtw="<b>症狀:</b> <code>tests/unit/</code> 只覆蓋了驗證器和格式化器——沒有結帳、登入、訂單或支付。" data-ja="<b>症状:</b> <code>tests/unit/</code> はバリデータとフォーマッタしかカバーしておらず、チェックアウト・ログイン・注文・決済はありません。" data-ko="<b>증상:</b> <code>tests/unit/</code>는 검증기와 포매터만 커버하며 결제, 로그인, 주문, 결제 처리는 없습니다." data-es="<b>Síntoma:</b> <code>tests/unit/</code> solo cubre validadores y formateadores — nada de checkout, login, pedidos ni pagos."><b>Symptom:</b> <code>tests/unit/</code> covers only validators and formatters — no checkout, login, order, or payment.</div>
|
||||
<div class="ln sr" data-en="<b>Source:</b> Feathers — Working Effectively with Legacy Code" data-zh="<b>根源:</b> Feathers — Working Effectively with Legacy Code" data-zhtw="<b>根源:</b> Feathers — Working Effectively with Legacy Code" data-ja="<b>根源:</b> Feathers — Working Effectively with Legacy Code" data-ko="<b>근원:</b> Feathers — Working Effectively with Legacy Code" data-es="<b>Origen:</b> Feathers — Working Effectively with Legacy Code"><b>Source:</b> Feathers — Working Effectively with Legacy Code</div>
|
||||
<div class="ln rx" data-en="<b>Remedy:</b> Start with <code>test_checkout_flow.py</code> and <code>test_payment_api.py</code>." data-zh="<b>修复:</b> 从 <code>test_checkout_flow.py</code> 和 <code>test_payment_api.py</code> 开始。" data-zhtw="<b>對策:</b> 從 <code>test_checkout_flow.py</code> 和 <code>test_payment_api.py</code> 開始。" data-ja="<b>対策:</b> <code>test_checkout_flow.py</code> と <code>test_payment_api.py</code> から始めます。" data-ko="<b>처방:</b> <code>test_checkout_flow.py</code>와 <code>test_payment_api.py</code>부터 시작하세요." data-es="<b>Remedio:</b> Empieza con <code>test_checkout_flow.py</code> y <code>test_payment_api.py</code>."><b>Remedy:</b> Start with <code>test_checkout_flow.py</code> and <code>test_payment_api.py</code>.</div>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
@@ -455,11 +471,11 @@ Target ratio: Unit 70% : Integration 20% : E2E 10%</pre>
|
||||
<footer>
|
||||
<div class="foot-in">
|
||||
<div class="foot-links">
|
||||
<a href="index.html">Home</a>
|
||||
<a href="index.html" data-en="Home" data-zh="首页" data-zhtw="首頁" data-ja="ホーム" data-ko="홈" data-es="Inicio">Home</a>
|
||||
<a href="https://github.com/hyhmrright/brooks-lint" target="_blank" rel="noopener">GitHub</a>
|
||||
<a href="https://github.com/hyhmrright/brooks-lint/blob/main/docs/gallery.md" target="_blank" rel="noopener">Gallery source (Markdown)</a>
|
||||
<a href="https://github.com/hyhmrright/brooks-lint/blob/main/docs/gallery.md" target="_blank" rel="noopener" data-en="Gallery source (Markdown)" data-zh="画廊源文件(Markdown)" data-zhtw="範例展示原始檔(Markdown)" data-ja="ギャラリーのソース(Markdown)" data-ko="갤러리 소스 (Markdown)" data-es="Fuente de la galería (Markdown)">Gallery source (Markdown)</a>
|
||||
</div>
|
||||
<div class="foot-note">MIT License — Built by hyhmrright</div>
|
||||
<div class="foot-note" data-en="MIT License — Built by hyhmrright" data-zh="MIT 许可证 — 由 hyhmrright 打造" data-zhtw="MIT 授權 — 由 hyhmrright 打造" data-ja="MIT ライセンス — hyhmrright が制作" data-ko="MIT 라이선스 — hyhmrright 제작" data-es="Licencia MIT — Creado por hyhmrright">MIT License — Built by hyhmrright</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -491,5 +507,37 @@ Target ratio: Unit 70% : Integration 20% : E2E 10%</pre>
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<script>
|
||||
(function () {
|
||||
var STORE = "brooks-lint-lang";
|
||||
var HTML_LANG = { en:"en", zh:"zh-CN", zhtw:"zh-TW", ja:"ja", ko:"ko", es:"es" };
|
||||
function apply(lang) {
|
||||
document.documentElement.lang = HTML_LANG[lang] || "en";
|
||||
document.querySelectorAll("[data-en]").forEach(function (el) {
|
||||
var v = el.getAttribute("data-" + lang);
|
||||
if (v != null) el.innerHTML = v;
|
||||
});
|
||||
document.querySelectorAll(".lang-toggle button").forEach(function (b) {
|
||||
b.classList.toggle("active", b.getAttribute("data-set-lang") === lang);
|
||||
});
|
||||
try { localStorage.setItem(STORE, lang); } catch (e) {}
|
||||
}
|
||||
document.querySelectorAll("[data-set-lang]").forEach(function (b) {
|
||||
b.addEventListener("click", function () { apply(b.getAttribute("data-set-lang")); });
|
||||
});
|
||||
var saved;
|
||||
try { saved = localStorage.getItem(STORE); } catch (e) {}
|
||||
if (!saved) {
|
||||
var nav = (navigator.language || "").toLowerCase();
|
||||
saved = (nav.indexOf("zh-tw")===0||nav.indexOf("zh-hant")===0||nav.indexOf("zh-hk")===0||nav.indexOf("zh-mo")===0) ? "zhtw"
|
||||
: nav.indexOf("zh")===0 ? "zh"
|
||||
: nav.indexOf("ja")===0 ? "ja"
|
||||
: nav.indexOf("ko")===0 ? "ko"
|
||||
: nav.indexOf("es")===0 ? "es"
|
||||
: "en";
|
||||
}
|
||||
apply(saved);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+132
-89
@@ -71,6 +71,9 @@
|
||||
.cta a{display:inline-block;margin-top:14px;background:var(--gold);color:#1c160f;font-weight:700;text-decoration:none;padding:10px 22px;border-radius:8px;}
|
||||
footer{margin-top:56px;padding-top:24px;border-top:1px solid var(--rule);font-size:14px;color:var(--ink-soft);text-align:center;}
|
||||
footer a{color:var(--blue);text-decoration:none;}
|
||||
.lang-toggle{display:inline-flex;border:1px solid var(--rule);border-radius:999px;overflow:hidden;font-size:13px;background:var(--card);vertical-align:middle;}
|
||||
.lang-toggle button{border:none;background:transparent;padding:4px 9px;cursor:pointer;color:var(--ink-soft);font-family:var(--sans);font-weight:600;font-size:12.5px;}
|
||||
.lang-toggle button.active{background:var(--ink);color:var(--paper);}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -78,142 +81,182 @@
|
||||
<header class="top">
|
||||
<img src="logo.svg" alt="brooks-lint logo">
|
||||
<a href="index.html">brooks-lint</a>
|
||||
<nav class="crumb"><a href="index.html">Home</a> · <a href="gallery.html">Gallery</a> · <a href="https://github.com/hyhmrright/brooks-lint">GitHub</a></nav>
|
||||
<nav class="crumb"><a href="index.html" data-en="Home" data-zh="首页" data-zhtw="首頁" data-ja="ホーム" data-ko="홈" data-es="Inicio">Home</a> · <a href="gallery.html" data-en="Gallery" data-zh="示例画廊" data-zhtw="範例展示" data-ja="ギャラリー" data-ko="갤러리" data-es="Galería">Gallery</a> · <a href="https://github.com/hyhmrright/brooks-lint">GitHub</a></nav>
|
||||
<span class="lang-toggle">
|
||||
<button data-set-lang="en">EN</button>
|
||||
<button data-set-lang="zh">中</button>
|
||||
<button data-set-lang="zhtw">繁</button>
|
||||
<button data-set-lang="ja">日</button>
|
||||
<button data-set-lang="ko">한</button>
|
||||
<button data-set-lang="es">ES</button>
|
||||
</span>
|
||||
</header>
|
||||
|
||||
<h1>The Six Code Decay Risks</h1>
|
||||
<p class="lede">A field guide to the ways production code rots — and the classic engineering books that named each one fifty years before your codebase did it again.</p>
|
||||
<p class="meta">Most linters count lines and cyclomatic complexity. They measure the <em>surface</em>. Decay happens underneath: in how responsibilities tangle, how knowledge duplicates, how dependencies invert. These six patterns are a synthesis of twelve classic software engineering books, applied to modern code review.</p>
|
||||
<h1 data-en="The Six Code Decay Risks" data-zh="六大代码腐化风险" data-zhtw="六大程式碼衰退風險" data-ja="六つのコード劣化リスク" data-ko="여섯 가지 코드 쇠퇴 위험" data-es="Los seis riesgos de deterioro del código">The Six Code Decay Risks</h1>
|
||||
<p class="lede" data-en="A field guide to the ways production code rots — and the classic engineering books that named each one fifty years before your codebase did it again." data-zh="一份关于生产代码如何腐化的实战指南——以及那些早在你的代码库重蹈覆辙五十年前就为每种腐化命名的经典工程著作。" data-zhtw="一份關於生產程式碼如何衰退的實戰指南——以及那些早在你的程式碼庫重蹈覆轍五十年前就為每種衰退命名的經典工程著作。" data-ja="本番コードが腐っていく道筋への実践的なフィールドガイド——そして、あなたのコードベースが再び同じ過ちを犯す五十年前に、それぞれを名付けた古典的なエンジニアリングの名著。" data-ko="프로덕션 코드가 썩어가는 방식에 대한 실전 가이드 — 그리고 당신의 코드베이스가 같은 일을 반복하기 오십 년 전에 각각을 명명한 고전 엔지니어링 명저들." data-es="Una guía de campo sobre las formas en que el código de producción se pudre — y los libros clásicos de ingeniería que nombraron cada una cincuenta años antes de que tu base de código lo repitiera.">A field guide to the ways production code rots — and the classic engineering books that named each one fifty years before your codebase did it again.</p>
|
||||
<p class="meta" data-en="Most linters count lines and cyclomatic complexity. They measure the <em>surface</em>. Decay happens underneath: in how responsibilities tangle, how knowledge duplicates, how dependencies invert. These six patterns are a synthesis of twelve classic software engineering books, applied to modern code review." data-zh="多数 linter 只统计行数和圈复杂度。它们衡量的是<em>表面</em>。腐化发生在底层:在于职责如何纠缠、知识如何重复、依赖如何倒置。这六种模式是对十二本经典软件工程著作的综合,应用于现代代码评审。" data-zhtw="多數 linter 只統計行數和圈複雜度。它們衡量的是<em>表面</em>。衰退發生在底層:在於職責如何糾纏、知識如何重複、依賴如何倒置。這六種模式是對十二本經典軟體工程著作的綜合,應用於現代程式碼審查。" data-ja="ほとんどのリンターは行数とサイクロマティック複雑度を数えます。それらが測るのは<em>表面</em>です。劣化はその下で起こります——責務がどう絡まり、知識がどう重複し、依存がどう逆転するか。これら六つのパターンは、十二冊の古典的なソフトウェア工学の名著を統合し、現代のコードレビューに適用したものです。" data-ko="대부분의 린터는 줄 수와 순환 복잡도를 셉니다. 그것들은 <em>표면</em>을 측정합니다. 쇠퇴는 그 아래에서 일어납니다: 책임이 어떻게 엉키고, 지식이 어떻게 중복되며, 의존성이 어떻게 뒤집히는지에서. 이 여섯 가지 패턴은 열두 권의 고전 소프트웨어 공학 명저를 종합하여 현대 코드 리뷰에 적용한 것입니다." data-es="La mayoría de los linters cuentan líneas y complejidad ciclomática. Miden la <em>superficie</em>. El deterioro ocurre debajo: en cómo se enredan las responsabilidades, cómo se duplica el conocimiento, cómo se invierten las dependencias. Estos seis patrones son una síntesis de doce libros clásicos de ingeniería de software, aplicados a la revisión de código moderna.">Most linters count lines and cyclomatic complexity. They measure the <em>surface</em>. Decay happens underneath: in how responsibilities tangle, how knowledge duplicates, how dependencies invert. These six patterns are a synthesis of twelve classic software engineering books, applied to modern code review.</p>
|
||||
|
||||
<div class="toc">
|
||||
<h4>The six risks</h4>
|
||||
<h4 data-en="The six risks" data-zh="六大风险" data-zhtw="六大風險" data-ja="六つのリスク" data-ko="여섯 가지 위험" data-es="Los seis riesgos">The six risks</h4>
|
||||
<ol>
|
||||
<li><a href="#r1">Cognitive Overload</a> — how hard is this to understand?</li>
|
||||
<li><a href="#r2">Change Propagation</a> — what breaks when you touch one thing?</li>
|
||||
<li><a href="#r3">Knowledge Duplication</a> — is one decision expressed in many places?</li>
|
||||
<li><a href="#r4">Accidental Complexity</a> — is the code harder than the problem?</li>
|
||||
<li><a href="#r5">Dependency Disorder</a> — do dependencies flow one way?</li>
|
||||
<li><a href="#r6">Domain Model Distortion</a> — does the code mean what the business means?</li>
|
||||
<li data-en="<a href="#r1">Cognitive Overload</a> — how hard is this to understand?" data-zh="<a href="#r1">认知过载</a> — 这有多难理解?" data-zhtw="<a href="#r1">認知過載</a> — 這有多難理解?" data-ja="<a href="#r1">認知的過負荷</a> — これはどれほど理解しにくいか?" data-ko="<a href="#r1">인지 과부하</a> — 이것은 이해하기가 얼마나 어려운가?" data-es="<a href="#r1">Sobrecarga cognitiva</a> — ¿qué tan difícil es entender esto?"><a href="#r1">Cognitive Overload</a> — how hard is this to understand?</li>
|
||||
<li data-en="<a href="#r2">Change Propagation</a> — what breaks when you touch one thing?" data-zh="<a href="#r2">变更扩散</a> — 改动一处时会有什么跟着坏掉?" data-zhtw="<a href="#r2">變更擴散</a> — 改動一處時會有什麼跟著壞掉?" data-ja="<a href="#r2">変更の波及</a> — 一箇所を触ると何が壊れるか?" data-ko="<a href="#r2">변경 전파</a> — 한 곳을 건드리면 무엇이 깨지는가?" data-es="<a href="#r2">Propagación de cambios</a> — ¿qué se rompe cuando tocas una sola cosa?"><a href="#r2">Change Propagation</a> — what breaks when you touch one thing?</li>
|
||||
<li data-en="<a href="#r3">Knowledge Duplication</a> — is one decision expressed in many places?" data-zh="<a href="#r3">知识重复</a> — 一个决策是否在多处表达?" data-zhtw="<a href="#r3">知識重複</a> — 一個決策是否在多處表達?" data-ja="<a href="#r3">知識の重複</a> — 一つの決定が多くの場所で表現されていないか?" data-ko="<a href="#r3">지식 중복</a> — 하나의 결정이 여러 곳에 표현되어 있는가?" data-es="<a href="#r3">Duplicación de conocimiento</a> — ¿una sola decisión se expresa en muchos lugares?"><a href="#r3">Knowledge Duplication</a> — is one decision expressed in many places?</li>
|
||||
<li data-en="<a href="#r4">Accidental Complexity</a> — is the code harder than the problem?" data-zh="<a href="#r4">偶发复杂度</a> — 代码是否比问题本身更复杂?" data-zhtw="<a href="#r4">偶發複雜度</a> — 程式碼是否比問題本身更複雜?" data-ja="<a href="#r4">偶有的複雑性</a> — コードは問題そのものより難しくなっていないか?" data-ko="<a href="#r4">우발적 복잡성</a> — 코드가 문제보다 더 어려운가?" data-es="<a href="#r4">Complejidad accidental</a> — ¿el código es más difícil que el problema?"><a href="#r4">Accidental Complexity</a> — is the code harder than the problem?</li>
|
||||
<li data-en="<a href="#r5">Dependency Disorder</a> — do dependencies flow one way?" data-zh="<a href="#r5">依赖失序</a> — 依赖是否朝一个方向流动?" data-zhtw="<a href="#r5">依賴失序</a> — 依賴是否朝一個方向流動?" data-ja="<a href="#r5">依存関係の無秩序</a> — 依存は一方向に流れているか?" data-ko="<a href="#r5">의존성 무질서</a> — 의존성이 한 방향으로 흐르는가?" data-es="<a href="#r5">Desorden de dependencias</a> — ¿las dependencias fluyen en una sola dirección?"><a href="#r5">Dependency Disorder</a> — do dependencies flow one way?</li>
|
||||
<li data-en="<a href="#r6">Domain Model Distortion</a> — does the code mean what the business means?" data-zh="<a href="#r6">领域模型失真</a> — 代码表达的含义是否与业务一致?" data-zhtw="<a href="#r6">領域模型失真</a> — 程式碼表達的含義是否與業務一致?" data-ja="<a href="#r6">ドメインモデルの歪み</a> — コードはビジネスが意味するものを表しているか?" data-ko="<a href="#r6">도메인 모델 왜곡</a> — 코드가 비즈니스가 의미하는 바를 뜻하는가?" data-es="<a href="#r6">Distorsión del modelo de dominio</a> — ¿el código significa lo que el negocio significa?"><a href="#r6">Domain Model Distortion</a> — does the code mean what the business means?</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<p>Each risk below follows the same shape brooks-lint uses for every finding — the <strong>Iron Law</strong>: <em>Symptom → Source → Consequence → Remedy</em>. A symptom you can see in the diff, a source you can cite to a book, a consequence that explains why it matters, and a remedy you can actually apply.</p>
|
||||
<p data-en="Each risk below follows the same shape brooks-lint uses for every finding — the <strong>Iron Law</strong>: <em>Symptom → Source → Consequence → Remedy</em>. A symptom you can see in the diff, a source you can cite to a book, a consequence that explains why it matters, and a remedy you can actually apply." data-zh="下面每条风险都遵循 brooks-lint 用于每条结论的同一形式——<strong>铁律</strong>:<em>症状 → 根源 → 后果 → 修复</em>。一个你能在 diff 中看到的症状,一个你能引用到书的根源,一个解释其为何重要的后果,以及一个你真正能落地的修复。" data-zhtw="下面每條風險都遵循 brooks-lint 用於每條結論的同一形式——<strong>鐵律</strong>:<em>症狀 → 根源 → 後果 → 對策</em>。一個你能在 diff 中看到的症狀,一個你能引用到書的根源,一個解釋其為何重要的後果,以及一個你真正能落地的對策。" data-ja="以下の各リスクは、brooks-lint がすべての指摘で用いるのと同じ形——<strong>鉄の掟</strong>:<em>症状 → 根源 → 結果 → 対策</em>に従います。diff で見える症状、書籍に引用できる根源、なぜ重要かを説明する結果、そして実際に適用できる対策。" data-ko="아래의 각 위험은 brooks-lint가 모든 발견에 사용하는 동일한 형태——<strong>철칙</strong>:<em>증상 → 근원 → 결과 → 처방</em>을 따릅니다. diff에서 볼 수 있는 증상, 책에 인용할 수 있는 근원, 왜 중요한지 설명하는 결과, 그리고 실제로 적용할 수 있는 처방." data-es="Cada riesgo a continuación sigue la misma forma que brooks-lint usa para cada hallazgo — la <strong>Ley de Hierro</strong>: <em>Síntoma → Origen → Consecuencia → Remedio</em>. Un síntoma que puedes ver en el diff, un origen que puedes citar de un libro, una consecuencia que explica por qué importa, y un remedio que realmente puedes aplicar.">Each risk below follows the same shape brooks-lint uses for every finding — the <strong>Iron Law</strong>: <em>Symptom → Source → Consequence → Remedy</em>. A symptom you can see in the diff, a source you can cite to a book, a consequence that explains why it matters, and a remedy you can actually apply.</p>
|
||||
|
||||
<h2 id="r1">1 · Cognitive Overload <span class="code">R1</span></h2>
|
||||
<h2 id="r1"><span data-en="1 · Cognitive Overload" data-zh="1 · 认知过载" data-zhtw="1 · 認知過載" data-ja="1 · 認知的過負荷" data-ko="1 · 인지 과부하" data-es="1 · Sobrecarga cognitiva">1 · Cognitive Overload</span> <span class="code">R1</span></h2>
|
||||
<div class="risk">
|
||||
<p class="q">Diagnostic question: How much mental effort does a human need to understand this?</p>
|
||||
<p>Working memory holds about four chunks at once. Code that exceeds that budget causes mistakes, invites avoidance, and blocks the very refactoring that would fix it.</p>
|
||||
<h3>Signature symptoms</h3>
|
||||
<p class="q" data-en="Diagnostic question: How much mental effort does a human need to understand this?" data-zh="诊断问题:理解这段代码,人需要付出多少脑力?" data-zhtw="診斷問題:理解這段程式碼,人需要付出多少腦力?" data-ja="診断的質問:これを理解するのに人はどれだけの精神的労力を要するか?" data-ko="진단 질문: 이것을 이해하는 데 사람은 얼마나 많은 정신적 노력이 필요한가?" data-es="Pregunta diagnóstica: ¿cuánto esfuerzo mental necesita una persona para entender esto?">Diagnostic question: How much mental effort does a human need to understand this?</p>
|
||||
<p data-en="Working memory holds about four chunks at once. Code that exceeds that budget causes mistakes, invites avoidance, and blocks the very refactoring that would fix it." data-zh="工作记忆一次大约只能容纳四个组块。超出这一预算的代码会引发错误、令人回避,并阻碍本该修复它的重构。" data-zhtw="工作記憶一次大約只能容納四個組塊。超出這一預算的程式碼會引發錯誤、令人迴避,並阻礙本該修復它的重構。" data-ja="ワーキングメモリは一度に約四つのまとまりしか保持できません。その予算を超えるコードはミスを招き、敬遠され、それを直すはずのリファクタリングを妨げます。" data-ko="작업 기억은 한 번에 약 네 개의 덩어리만 담을 수 있습니다. 그 예산을 초과하는 코드는 실수를 유발하고, 회피를 부르며, 그것을 고칠 리팩터링 자체를 가로막습니다." data-es="La memoria de trabajo retiene unos cuatro fragmentos a la vez. El código que excede ese presupuesto provoca errores, invita a evitarlo y bloquea la propia refactorización que lo arreglaría.">Working memory holds about four chunks at once. Code that exceeds that budget causes mistakes, invites avoidance, and blocks the very refactoring that would fix it.</p>
|
||||
<h3 data-en="Signature symptoms" data-zh="典型症状" data-zhtw="典型症狀" data-ja="典型的な症状" data-ko="대표적인 증상" data-es="Síntomas característicos">Signature symptoms</h3>
|
||||
<ul>
|
||||
<li>Functions that mix multiple levels of abstraction in one body, or run past ~20 lines</li>
|
||||
<li>Nesting deeper than three levels; boolean conditions with three or more clauses</li>
|
||||
<li>Long parameter lists, flag arguments, and "train-wreck" chains like <code>a.getB().getC().doD()</code></li>
|
||||
<li><strong>Primitive obsession</strong> — domain concepts smuggled through as <code>String</code>/<code>int</code> instead of purpose-built types</li>
|
||||
<li><strong>Shallow modules</strong> — an interface as complex as the functionality it hides</li>
|
||||
<li data-en="Functions that mix multiple levels of abstraction in one body, or run past ~20 lines" data-zh="在一个函数体内混合多个抽象层级,或长度超过约 20 行的函数" data-zhtw="在一個函式體內混合多個抽象層級,或長度超過約 20 行的函式" data-ja="一つの本体に複数の抽象レベルが混在する、または約20行を超える関数" data-ko="한 본문에 여러 추상화 수준이 섞이거나, 약 20줄을 넘는 함수" data-es="Funciones que mezclan varios niveles de abstracción en un mismo cuerpo, o que superan las ~20 líneas">Functions that mix multiple levels of abstraction in one body, or run past ~20 lines</li>
|
||||
<li data-en="Nesting deeper than three levels; boolean conditions with three or more clauses" data-zh="嵌套超过三层;带有三个或更多子句的布尔条件" data-zhtw="巢狀超過三層;帶有三個或更多子句的布林條件" data-ja="三段を超えるネスト、三つ以上の節を持つ真偽条件" data-ko="세 단계보다 깊은 중첩, 절이 셋 이상인 불리언 조건" data-es="Anidamiento de más de tres niveles; condiciones booleanas con tres o más cláusulas">Nesting deeper than three levels; boolean conditions with three or more clauses</li>
|
||||
<li data-en="Long parameter lists, flag arguments, and "train-wreck" chains like <code>a.getB().getC().doD()</code>" data-zh="过长的参数列表、标志参数,以及像 <code>a.getB().getC().doD()</code> 这样的“火车失事”式链式调用" data-zhtw="過長的參數列表、旗標參數,以及像 <code>a.getB().getC().doD()</code> 這樣的「火車失事」式鏈式呼叫" data-ja="長い引数リスト、フラグ引数、そして <code>a.getB().getC().doD()</code> のような「列車事故」式の連鎖" data-ko="긴 매개변수 목록, 플래그 인자, 그리고 <code>a.getB().getC().doD()</code> 같은 「열차 전복」식 연쇄 호출" data-es="Listas de parámetros largas, argumentos de bandera y cadenas tipo «choque de tren» como <code>a.getB().getC().doD()</code>">Long parameter lists, flag arguments, and "train-wreck" chains like <code>a.getB().getC().doD()</code></li>
|
||||
<li data-en="<strong>Primitive obsession</strong> — domain concepts smuggled through as <code>String</code>/<code>int</code> instead of purpose-built types" data-zh="<strong>基本类型偏执</strong> — 把领域概念用 <code>String</code>/<code>int</code> 这类基本类型偷偷传递,而非专门构建的类型" data-zhtw="<strong>基本型別偏執</strong> — 把領域概念用 <code>String</code>/<code>int</code> 這類基本型別偷偷傳遞,而非專門構建的型別" data-ja="<strong>基本型への執着</strong> — ドメイン概念を専用の型ではなく <code>String</code>/<code>int</code> として紛れ込ませる" data-ko="<strong>기본형 집착</strong> — 도메인 개념을 목적에 맞는 타입 대신 <code>String</code>/<code>int</code> 같은 기본형으로 몰래 전달" data-es="<strong>Obsesión por primitivos</strong> — conceptos de dominio colados como <code>String</code>/<code>int</code> en vez de tipos creados a propósito"><strong>Primitive obsession</strong> — domain concepts smuggled through as <code>String</code>/<code>int</code> instead of purpose-built types</li>
|
||||
<li data-en="<strong>Shallow modules</strong> — an interface as complex as the functionality it hides" data-zh="<strong>浅模块</strong> — 接口与其所隐藏的功能一样复杂" data-zhtw="<strong>淺模組</strong> — 介面與其所隱藏的功能一樣複雜" data-ja="<strong>浅いモジュール</strong> — 隠している機能と同じくらい複雑なインターフェース" data-ko="<strong>얕은 모듈</strong> — 감추는 기능만큼이나 복잡한 인터페이스" data-es="<strong>Módulos superficiales</strong> — una interfaz tan compleja como la funcionalidad que oculta"><strong>Shallow modules</strong> — an interface as complex as the functionality it hides</li>
|
||||
</ul>
|
||||
<p class="src"><b>Sources:</b> Fowler, <em>Refactoring</em> (Long Method, Long Parameter List, Message Chains, Primitive Obsession) · McConnell, <em>Code Complete</em> (high-quality routines, the power of variable names) · Ousterhout, <em>A Philosophy of Software Design</em> (deep modules) · Evans, <em>Domain-Driven Design</em> (ubiquitous language).</p>
|
||||
<h3>Remedy</h3>
|
||||
<p>Extract methods until each one operates at a single level of abstraction. Replace primitives with value types. Make modules <em>deep</em>: a simple interface over substantial functionality, not a thin wrapper.</p>
|
||||
<p class="src" data-en="<b>Sources:</b> Fowler, <em>Refactoring</em> (Long Method, Long Parameter List, Message Chains, Primitive Obsession) · McConnell, <em>Code Complete</em> (high-quality routines, the power of variable names) · Ousterhout, <em>A Philosophy of Software Design</em> (deep modules) · Evans, <em>Domain-Driven Design</em> (ubiquitous language)." data-zh="<b>根源:</b>Fowler,<em>Refactoring</em>(过长函数、过长参数列表、消息链、基本类型偏执)· McConnell,<em>Code Complete</em>(高质量子程序、变量命名的力量)· Ousterhout,<em>A Philosophy of Software Design</em>(深模块)· Evans,<em>Domain-Driven Design</em>(统一语言)。" data-zhtw="<b>根源:</b>Fowler,<em>Refactoring</em>(過長函式、過長參數列表、訊息鏈、基本型別偏執)· McConnell,<em>Code Complete</em>(高品質子程式、變數命名的力量)· Ousterhout,<em>A Philosophy of Software Design</em>(深模組)· Evans,<em>Domain-Driven Design</em>(統一語言)。" data-ja="<b>根源:</b>Fowler、<em>Refactoring</em>(長いメソッド、長い引数リスト、メッセージの連鎖、基本型への執着)· McConnell、<em>Code Complete</em>(高品質なルーチン、変数名の力)· Ousterhout、<em>A Philosophy of Software Design</em>(深いモジュール)· Evans、<em>Domain-Driven Design</em>(ユビキタス言語)。" data-ko="<b>근원:</b> Fowler, <em>Refactoring</em>(긴 메서드, 긴 매개변수 목록, 메시지 체인, 기본형 집착) · McConnell, <em>Code Complete</em>(고품질 루틴, 변수 이름의 힘) · Ousterhout, <em>A Philosophy of Software Design</em>(깊은 모듈) · Evans, <em>Domain-Driven Design</em>(유비쿼터스 언어)." data-es="<b>Fuentes:</b> Fowler, <em>Refactoring</em> (Método largo, Lista de parámetros larga, Cadenas de mensajes, Obsesión por primitivos) · McConnell, <em>Code Complete</em> (rutinas de alta calidad, el poder de los nombres de variables) · Ousterhout, <em>A Philosophy of Software Design</em> (módulos profundos) · Evans, <em>Domain-Driven Design</em> (lenguaje ubicuo)."><b>Sources:</b> Fowler, <em>Refactoring</em> (Long Method, Long Parameter List, Message Chains, Primitive Obsession) · McConnell, <em>Code Complete</em> (high-quality routines, the power of variable names) · Ousterhout, <em>A Philosophy of Software Design</em> (deep modules) · Evans, <em>Domain-Driven Design</em> (ubiquitous language).</p>
|
||||
<h3 data-en="Remedy" data-zh="修复" data-zhtw="對策" data-ja="対策" data-ko="처방" data-es="Remedio">Remedy</h3>
|
||||
<p data-en="Extract methods until each one operates at a single level of abstraction. Replace primitives with value types. Make modules <em>deep</em>: a simple interface over substantial functionality, not a thin wrapper." data-zh="不断提炼方法,直到每个方法都只在单一抽象层级上工作。用值类型替换基本类型。让模块变得<em>深</em>:在丰富的功能之上提供简单的接口,而不是一层薄薄的包装。" data-zhtw="不斷提煉方法,直到每個方法都只在單一抽象層級上工作。用值型別替換基本型別。讓模組變得<em>深</em>:在豐富的功能之上提供簡單的介面,而不是一層薄薄的包裝。" data-ja="各メソッドが単一の抽象レベルで動作するまでメソッドを抽出する。基本型を値型に置き換える。モジュールを<em>深く</em>する——薄いラッパーではなく、十分な機能の上にシンプルなインターフェースを。" data-ko="각 메서드가 단일 추상화 수준에서 작동할 때까지 메서드를 추출하라. 기본형을 값 타입으로 교체하라. 모듈을 <em>깊게</em> 만들어라: 얇은 래퍼가 아니라 충분한 기능 위의 단순한 인터페이스로." data-es="Extrae métodos hasta que cada uno opere en un solo nivel de abstracción. Reemplaza los primitivos por tipos de valor. Haz que los módulos sean <em>profundos</em>: una interfaz simple sobre una funcionalidad sustancial, no una envoltura delgada.">Extract methods until each one operates at a single level of abstraction. Replace primitives with value types. Make modules <em>deep</em>: a simple interface over substantial functionality, not a thin wrapper.</p>
|
||||
</div>
|
||||
|
||||
<h2 id="r2">2 · Change Propagation <span class="code">R2</span></h2>
|
||||
<h2 id="r2"><span data-en="2 · Change Propagation" data-zh="2 · 变更扩散" data-zhtw="2 · 變更擴散" data-ja="2 · 変更の波及" data-ko="2 · 변경 전파" data-es="2 · Propagación de cambios">2 · Change Propagation</span> <span class="code">R2</span></h2>
|
||||
<div class="risk">
|
||||
<p class="q">Diagnostic question: How many unrelated things break when you change one thing?</p>
|
||||
<p>This is the most expensive decay risk because it compounds: every future edit inherits the blast radius of the last one.</p>
|
||||
<h3>Signature symptoms</h3>
|
||||
<p class="q" data-en="Diagnostic question: How many unrelated things break when you change one thing?" data-zh="诊断问题:当你改动一处时,会有多少不相关的东西跟着坏掉?" data-zhtw="診斷問題:當你改動一處時,會有多少不相關的東西跟著壞掉?" data-ja="診断的質問:一つを変更したとき、無関係なものがいくつ壊れるか?" data-ko="진단 질문: 한 가지를 바꿀 때 무관한 것들이 얼마나 많이 깨지는가?" data-es="Pregunta diagnóstica: ¿cuántas cosas no relacionadas se rompen cuando cambias una sola cosa?">Diagnostic question: How many unrelated things break when you change one thing?</p>
|
||||
<p data-en="This is the most expensive decay risk because it compounds: every future edit inherits the blast radius of the last one." data-zh="这是代价最高的腐化风险,因为它会复利累积:未来每一次修改都会继承上一次修改的波及范围。" data-zhtw="這是代價最高的衰退風險,因為它會複利累積:未來每一次修改都會繼承上一次修改的波及範圍。" data-ja="これは最もコストの高い劣化リスクです。なぜなら複利的に膨らむからです——将来のすべての編集が、前回の編集の影響範囲を引き継ぎます。" data-ko="이것은 가장 비용이 큰 쇠퇴 위험입니다. 복리처럼 누적되기 때문입니다: 미래의 모든 수정이 직전 수정의 폭발 반경을 물려받습니다." data-es="Este es el riesgo de deterioro más costoso porque se acumula: cada edición futura hereda el radio de impacto de la anterior.">This is the most expensive decay risk because it compounds: every future edit inherits the blast radius of the last one.</p>
|
||||
<h3 data-en="Signature symptoms" data-zh="典型症状" data-zhtw="典型症狀" data-ja="典型的な症状" data-ko="대표적인 증상" data-es="Síntomas característicos">Signature symptoms</h3>
|
||||
<ul>
|
||||
<li>Modifying one feature forces edits across more than three files in unrelated modules</li>
|
||||
<li>One class changes for several different business reasons (Single Responsibility violation)</li>
|
||||
<li>Shotgun surgery: a single conceptual change scattered across many small edits</li>
|
||||
<li data-en="Modifying one feature forces edits across more than three files in unrelated modules" data-zh="修改一个功能时被迫改动不相关模块中超过三个文件" data-zhtw="修改一個功能時被迫改動不相關模組中超過三個檔案" data-ja="一つの機能を変更すると、無関係なモジュールの三つを超えるファイルの編集を強いられる" data-ko="한 기능을 수정하면 무관한 모듈의 세 개가 넘는 파일을 고쳐야 한다" data-es="Modificar una funcionalidad obliga a editar más de tres archivos en módulos no relacionados">Modifying one feature forces edits across more than three files in unrelated modules</li>
|
||||
<li data-en="One class changes for several different business reasons (Single Responsibility violation)" data-zh="一个类因多个不同的业务原因而改动(违反单一职责)" data-zhtw="一個類別因多個不同的業務原因而改動(違反單一職責)" data-ja="一つのクラスが複数の異なるビジネス上の理由で変更される(単一責任の違反)" data-ko="하나의 클래스가 여러 다른 비즈니스 이유로 변경된다(단일 책임 위반)" data-es="Una clase cambia por varias razones de negocio distintas (violación de Responsabilidad Única)">One class changes for several different business reasons (Single Responsibility violation)</li>
|
||||
<li data-en="Shotgun surgery: a single conceptual change scattered across many small edits" data-zh="霰弹式修改:一个概念上的变更被分散成许多处小修改" data-zhtw="散彈式修改:一個概念上的變更被分散成許多處小修改" data-ja="ショットガン手術:一つの概念的な変更が多くの小さな編集に散らばる" data-ko="산탄총 수술: 하나의 개념적 변경이 여러 작은 수정으로 흩어진다" data-es="Cirugía de escopeta: un único cambio conceptual disperso en muchas ediciones pequeñas">Shotgun surgery: a single conceptual change scattered across many small edits</li>
|
||||
</ul>
|
||||
<p class="src"><b>Sources:</b> Fowler, <em>Refactoring</em> (Divergent Change, Shotgun Surgery) · Martin, <em>Clean Architecture</em> (Single Responsibility) · Hunt & Thomas, <em>The Pragmatic Programmer</em> (orthogonality) · Winters et al., <em>Software Engineering at Google</em>.</p>
|
||||
<h3>Remedy</h3>
|
||||
<p>Separate responsibilities into focused units and let a thin orchestrator call them. Aim for orthogonality — changing the loyalty formula should never risk breaking email notifications.</p>
|
||||
<p class="src" data-en="<b>Sources:</b> Fowler, <em>Refactoring</em> (Divergent Change, Shotgun Surgery) · Martin, <em>Clean Architecture</em> (Single Responsibility) · Hunt & Thomas, <em>The Pragmatic Programmer</em> (orthogonality) · Winters et al., <em>Software Engineering at Google</em>." data-zh="<b>根源:</b>Fowler,<em>Refactoring</em>(发散式变化、霰弹式修改)· Martin,<em>Clean Architecture</em>(单一职责)· Hunt & Thomas,<em>The Pragmatic Programmer</em>(正交性)· Winters et al.,<em>Software Engineering at Google</em>。" data-zhtw="<b>根源:</b>Fowler,<em>Refactoring</em>(發散式變化、散彈式修改)· Martin,<em>Clean Architecture</em>(單一職責)· Hunt & Thomas,<em>The Pragmatic Programmer</em>(正交性)· Winters et al.,<em>Software Engineering at Google</em>。" data-ja="<b>根源:</b>Fowler、<em>Refactoring</em>(発散的変更、ショットガン手術)· Martin、<em>Clean Architecture</em>(単一責任)· Hunt & Thomas、<em>The Pragmatic Programmer</em>(直交性)· Winters et al.、<em>Software Engineering at Google</em>。" data-ko="<b>근원:</b> Fowler, <em>Refactoring</em>(발산적 변경, 산탄총 수술) · Martin, <em>Clean Architecture</em>(단일 책임) · Hunt & Thomas, <em>The Pragmatic Programmer</em>(직교성) · Winters et al., <em>Software Engineering at Google</em>." data-es="<b>Fuentes:</b> Fowler, <em>Refactoring</em> (Cambio divergente, Cirugía de escopeta) · Martin, <em>Clean Architecture</em> (Responsabilidad Única) · Hunt & Thomas, <em>The Pragmatic Programmer</em> (ortogonalidad) · Winters et al., <em>Software Engineering at Google</em>."><b>Sources:</b> Fowler, <em>Refactoring</em> (Divergent Change, Shotgun Surgery) · Martin, <em>Clean Architecture</em> (Single Responsibility) · Hunt & Thomas, <em>The Pragmatic Programmer</em> (orthogonality) · Winters et al., <em>Software Engineering at Google</em>.</p>
|
||||
<h3 data-en="Remedy" data-zh="修复" data-zhtw="對策" data-ja="対策" data-ko="처방" data-es="Remedio">Remedy</h3>
|
||||
<p data-en="Separate responsibilities into focused units and let a thin orchestrator call them. Aim for orthogonality — changing the loyalty formula should never risk breaking email notifications." data-zh="把职责拆分为聚焦的单元,让一个轻量的编排者去调用它们。追求正交性——改动积分公式绝不应有可能破坏邮件通知。" data-zhtw="把職責拆分為聚焦的單元,讓一個輕量的編排者去呼叫它們。追求正交性——改動積分公式絕不應有可能破壞郵件通知。" data-ja="責務を焦点を絞った単位に分け、薄いオーケストレーターにそれらを呼び出させる。直交性を目指す——ロイヤルティの計算式を変えることが、メール通知を壊す危険につながってはならない。" data-ko="책임을 집중된 단위로 분리하고 얇은 오케스트레이터가 그것들을 호출하게 하라. 직교성을 목표로 하라 — 적립 공식을 바꾸는 것이 이메일 알림을 깨뜨릴 위험이 되어선 안 된다." data-es="Separa las responsabilidades en unidades enfocadas y deja que un orquestador delgado las invoque. Busca la ortogonalidad — cambiar la fórmula de fidelización nunca debería arriesgar romper las notificaciones por correo.">Separate responsibilities into focused units and let a thin orchestrator call them. Aim for orthogonality — changing the loyalty formula should never risk breaking email notifications.</p>
|
||||
</div>
|
||||
|
||||
<h2 id="r3">3 · Knowledge Duplication <span class="code">R3</span></h2>
|
||||
<h2 id="r3"><span data-en="3 · Knowledge Duplication" data-zh="3 · 知识重复" data-zhtw="3 · 知識重複" data-ja="3 · 知識の重複" data-ko="3 · 지식 중복" data-es="3 · Duplicación de conocimiento">3 · Knowledge Duplication</span> <span class="code">R3</span></h2>
|
||||
<div class="risk">
|
||||
<p class="q">Diagnostic question: Is the same decision expressed in more than one place?</p>
|
||||
<p>DRY is about <em>knowledge</em>, not text. Two identical-looking blocks that encode different decisions are fine; one decision copied into three files is a latent bug waiting for the day someone updates two of them.</p>
|
||||
<h3>Signature symptoms</h3>
|
||||
<p class="q" data-en="Diagnostic question: Is the same decision expressed in more than one place?" data-zh="诊断问题:同一个决策是否在不止一个地方被表达?" data-zhtw="診斷問題:同一個決策是否在不止一個地方被表達?" data-ja="診断的質問:同じ決定が複数の場所で表現されていないか?" data-ko="진단 질문: 같은 결정이 두 곳 이상에서 표현되고 있는가?" data-es="Pregunta diagnóstica: ¿la misma decisión se expresa en más de un lugar?">Diagnostic question: Is the same decision expressed in more than one place?</p>
|
||||
<p data-en="DRY is about <em>knowledge</em>, not text. Two identical-looking blocks that encode different decisions are fine; one decision copied into three files is a latent bug waiting for the day someone updates two of them." data-zh="DRY 关乎的是<em>知识</em>,而非文本。两段看起来相同却编码了不同决策的代码块没有问题;而一个决策被复制到三个文件里,则是一个潜伏的 bug,只等某天有人只更新了其中两处。" data-zhtw="DRY 關乎的是<em>知識</em>,而非文字。兩段看起來相同卻編碼了不同決策的程式碼塊沒有問題;而一個決策被複製到三個檔案裡,則是一個潛伏的 bug,只等某天有人只更新了其中兩處。" data-ja="DRY は<em>知識</em>に関するものであって、テキストではありません。見た目が同じでも異なる決定を符号化している二つのブロックは問題ありません。一つの決定が三つのファイルにコピーされていれば、それは誰かがそのうち二つだけを更新する日を待つ潜在的なバグです。" data-ko="DRY는 텍스트가 아니라 <em>지식</em>에 관한 것입니다. 똑같아 보이지만 서로 다른 결정을 담은 두 블록은 괜찮습니다. 하나의 결정이 세 파일에 복사되어 있다면, 누군가 그중 둘만 갱신하는 날을 기다리는 잠재적 버그입니다." data-es="DRY trata sobre el <em>conocimiento</em>, no sobre el texto. Dos bloques de aspecto idéntico que codifican decisiones distintas están bien; una decisión copiada en tres archivos es un error latente que espera el día en que alguien actualice solo dos de ellos.">DRY is about <em>knowledge</em>, not text. Two identical-looking blocks that encode different decisions are fine; one decision copied into three files is a latent bug waiting for the day someone updates two of them.</p>
|
||||
<h3 data-en="Signature symptoms" data-zh="典型症状" data-zhtw="典型症狀" data-ja="典型的な症状" data-ko="대표적인 증상" data-es="Síntomas característicos">Signature symptoms</h3>
|
||||
<ul>
|
||||
<li>The same logic copy-pasted across files or functions</li>
|
||||
<li>One concept named differently in different parts of the codebase</li>
|
||||
<li>Business rules (tax rates, validation limits) hard-coded in multiple spots</li>
|
||||
<li data-en="The same logic copy-pasted across files or functions" data-zh="同样的逻辑被复制粘贴到多个文件或函数中" data-zhtw="同樣的邏輯被複製貼上到多個檔案或函式中" data-ja="同じロジックがファイルや関数にコピー&ペーストされている" data-ko="같은 로직이 여러 파일이나 함수에 복사·붙여넣기되어 있다" data-es="La misma lógica copiada y pegada entre archivos o funciones">The same logic copy-pasted across files or functions</li>
|
||||
<li data-en="One concept named differently in different parts of the codebase" data-zh="同一个概念在代码库的不同部分有不同的命名" data-zhtw="同一個概念在程式碼庫的不同部分有不同的命名" data-ja="一つの概念がコードベースの異なる部分で異なる名前で呼ばれている" data-ko="하나의 개념이 코드베이스의 다른 부분에서 다르게 명명되어 있다" data-es="Un mismo concepto nombrado de forma distinta en diferentes partes de la base de código">One concept named differently in different parts of the codebase</li>
|
||||
<li data-en="Business rules (tax rates, validation limits) hard-coded in multiple spots" data-zh="业务规则(税率、校验上限)被硬编码在多个位置" data-zhtw="業務規則(稅率、驗證上限)被硬編碼在多個位置" data-ja="ビジネスルール(税率、検証の上限値)が複数の箇所にハードコードされている" data-ko="비즈니스 규칙(세율, 검증 한도)이 여러 곳에 하드코딩되어 있다" data-es="Reglas de negocio (tasas de impuestos, límites de validación) codificadas a mano en varios sitios">Business rules (tax rates, validation limits) hard-coded in multiple spots</li>
|
||||
</ul>
|
||||
<p class="src"><b>Sources:</b> Hunt & Thomas, <em>The Pragmatic Programmer</em> (DRY) · Fowler, <em>Refactoring</em> (Duplicated Code) · Evans, <em>Domain-Driven Design</em>.</p>
|
||||
<h3>Remedy</h3>
|
||||
<p>Give each decision a single authoritative home. Extract shared logic; unify vocabulary so one concept has one name everywhere.</p>
|
||||
<p class="src" data-en="<b>Sources:</b> Hunt & Thomas, <em>The Pragmatic Programmer</em> (DRY) · Fowler, <em>Refactoring</em> (Duplicated Code) · Evans, <em>Domain-Driven Design</em>." data-zh="<b>根源:</b>Hunt & Thomas,<em>The Pragmatic Programmer</em>(DRY)· Fowler,<em>Refactoring</em>(重复代码)· Evans,<em>Domain-Driven Design</em>。" data-zhtw="<b>根源:</b>Hunt & Thomas,<em>The Pragmatic Programmer</em>(DRY)· Fowler,<em>Refactoring</em>(重複程式碼)· Evans,<em>Domain-Driven Design</em>。" data-ja="<b>根源:</b>Hunt & Thomas、<em>The Pragmatic Programmer</em>(DRY)· Fowler、<em>Refactoring</em>(重複したコード)· Evans、<em>Domain-Driven Design</em>。" data-ko="<b>근원:</b> Hunt & Thomas, <em>The Pragmatic Programmer</em>(DRY) · Fowler, <em>Refactoring</em>(중복 코드) · Evans, <em>Domain-Driven Design</em>." data-es="<b>Fuentes:</b> Hunt & Thomas, <em>The Pragmatic Programmer</em> (DRY) · Fowler, <em>Refactoring</em> (Código duplicado) · Evans, <em>Domain-Driven Design</em>."><b>Sources:</b> Hunt & Thomas, <em>The Pragmatic Programmer</em> (DRY) · Fowler, <em>Refactoring</em> (Duplicated Code) · Evans, <em>Domain-Driven Design</em>.</p>
|
||||
<h3 data-en="Remedy" data-zh="修复" data-zhtw="對策" data-ja="対策" data-ko="처방" data-es="Remedio">Remedy</h3>
|
||||
<p data-en="Give each decision a single authoritative home. Extract shared logic; unify vocabulary so one concept has one name everywhere." data-zh="为每个决策设立一个权威的归属地。提炼共享逻辑;统一词汇,让一个概念在任何地方都只有一个名字。" data-zhtw="為每個決策設立一個權威的歸屬地。提煉共享邏輯;統一詞彙,讓一個概念在任何地方都只有一個名字。" data-ja="各決定に唯一の権威ある住処を与える。共有ロジックを抽出し、語彙を統一して、一つの概念がどこでも一つの名前を持つようにする。" data-ko="각 결정에 단 하나의 권위 있는 거처를 부여하라. 공유 로직을 추출하고, 어휘를 통일해 하나의 개념이 어디서나 하나의 이름을 갖게 하라." data-es="Da a cada decisión un único hogar autoritativo. Extrae la lógica compartida; unifica el vocabulario para que un concepto tenga un solo nombre en todas partes.">Give each decision a single authoritative home. Extract shared logic; unify vocabulary so one concept has one name everywhere.</p>
|
||||
</div>
|
||||
|
||||
<h2 id="r4">4 · Accidental Complexity <span class="code">R4</span></h2>
|
||||
<h2 id="r4"><span data-en="4 · Accidental Complexity" data-zh="4 · 偶发复杂度" data-zhtw="4 · 偶發複雜度" data-ja="4 · 偶有的複雑性" data-ko="4 · 우발적 복잡성" data-es="4 · Complejidad accidental">4 · Accidental Complexity</span> <span class="code">R4</span></h2>
|
||||
<div class="risk">
|
||||
<p class="q">Diagnostic question: Is the code more complex than the problem it solves?</p>
|
||||
<p>Brooks distinguished <em>essential</em> complexity (inherent to the problem) from <em>accidental</em> complexity (introduced by our solution). The second kind is the only kind you can delete.</p>
|
||||
<h3>Signature symptoms</h3>
|
||||
<p class="q" data-en="Diagnostic question: Is the code more complex than the problem it solves?" data-zh="诊断问题:代码是否比它所解决的问题更复杂?" data-zhtw="診斷問題:程式碼是否比它所解決的問題更複雜?" data-ja="診断的質問:コードは、それが解く問題よりも複雑になっていないか?" data-ko="진단 질문: 코드가 해결하려는 문제보다 더 복잡한가?" data-es="Pregunta diagnóstica: ¿el código es más complejo que el problema que resuelve?">Diagnostic question: Is the code more complex than the problem it solves?</p>
|
||||
<p data-en="Brooks distinguished <em>essential</em> complexity (inherent to the problem) from <em>accidental</em> complexity (introduced by our solution). The second kind is the only kind you can delete." data-zh="Brooks 区分了<em>本质</em>复杂度(问题本身固有的)与<em>偶发</em>复杂度(由我们的解决方案引入的)。后者才是你唯一能删除的那种。" data-zhtw="Brooks 區分了<em>本質</em>複雜度(問題本身固有的)與<em>偶發</em>複雜度(由我們的解決方案引入的)。後者才是你唯一能刪除的那種。" data-ja="Brooks は<em>本質的</em>複雑性(問題に固有のもの)と<em>偶有的</em>複雑性(我々の解決策が持ち込んだもの)を区別しました。後者こそが、削除できる唯一の種類です。" data-ko="Brooks는 <em>본질적</em> 복잡성(문제에 내재한)과 <em>우발적</em> 복잡성(우리 해법이 끌어들인)을 구분했습니다. 두 번째 종류만이 당신이 삭제할 수 있는 유일한 것입니다." data-es="Brooks distinguió la complejidad <em>esencial</em> (inherente al problema) de la complejidad <em>accidental</em> (introducida por nuestra solución). La segunda es la única que puedes eliminar.">Brooks distinguished <em>essential</em> complexity (inherent to the problem) from <em>accidental</em> complexity (introduced by our solution). The second kind is the only kind you can delete.</p>
|
||||
<h3 data-en="Signature symptoms" data-zh="典型症状" data-zhtw="典型症狀" data-ja="典型的な症状" data-ko="대표적인 증상" data-es="Síntomas característicos">Signature symptoms</h3>
|
||||
<ul>
|
||||
<li>Abstractions built "for future use" with no current consumer (speculative generality)</li>
|
||||
<li>Classes that barely justify their existence — a wrapper around a single call</li>
|
||||
<li>Configuration, indirection, or patterns out of proportion to the actual requirement</li>
|
||||
<li data-en="Abstractions built "for future use" with no current consumer (speculative generality)" data-zh="为“将来使用”而构建、却没有当前消费者的抽象(投机性通用化)" data-zhtw="為「將來使用」而構建、卻沒有當前消費者的抽象(投機性通用化)" data-ja="「将来のため」に作られたが現在の利用者がいない抽象(投機的一般化)" data-ko="현재 사용자가 없는데 「미래를 위해」 만든 추상화(투기적 일반화)" data-es="Abstracciones construidas «para uso futuro» sin ningún consumidor actual (generalidad especulativa)">Abstractions built "for future use" with no current consumer (speculative generality)</li>
|
||||
<li data-en="Classes that barely justify their existence — a wrapper around a single call" data-zh="几乎无法证明自身存在价值的类——只是对单次调用的一层包装" data-zhtw="幾乎無法證明自身存在價值的類別——只是對單次呼叫的一層包裝" data-ja="存在をかろうじてしか正当化できないクラス——単一の呼び出しを包むだけのラッパー" data-ko="존재를 거의 정당화하지 못하는 클래스 — 단일 호출을 감싼 래퍼" data-es="Clases que apenas justifican su existencia — una envoltura en torno a una sola llamada">Classes that barely justify their existence — a wrapper around a single call</li>
|
||||
<li data-en="Configuration, indirection, or patterns out of proportion to the actual requirement" data-zh="与实际需求不成比例的配置、间接层或设计模式" data-zhtw="與實際需求不成比例的設定、間接層或設計模式" data-ja="実際の要件に不釣り合いな設定、間接化、あるいはパターン" data-ko="실제 요구사항에 비해 과도한 설정, 간접 계층, 또는 패턴" data-es="Configuración, indirección o patrones desproporcionados respecto al requisito real">Configuration, indirection, or patterns out of proportion to the actual requirement</li>
|
||||
</ul>
|
||||
<p class="src"><b>Sources:</b> Brooks, <em>The Mythical Man-Month</em> (essential vs. accidental complexity) · Fowler, <em>Refactoring</em> (Speculative Generality) · Ousterhout, <em>A Philosophy of Software Design</em>.</p>
|
||||
<h3>Remedy</h3>
|
||||
<p>Delete speculative abstractions until a second real consumer appears. Prefer the simplest design that solves today's problem — you can always add structure when the need is concrete.</p>
|
||||
<p class="src" data-en="<b>Sources:</b> Brooks, <em>The Mythical Man-Month</em> (essential vs. accidental complexity) · Fowler, <em>Refactoring</em> (Speculative Generality) · Ousterhout, <em>A Philosophy of Software Design</em>." data-zh="<b>根源:</b>Brooks,<em>The Mythical Man-Month</em>(本质复杂度与偶发复杂度)· Fowler,<em>Refactoring</em>(投机性通用化)· Ousterhout,<em>A Philosophy of Software Design</em>。" data-zhtw="<b>根源:</b>Brooks,<em>The Mythical Man-Month</em>(本質複雜度與偶發複雜度)· Fowler,<em>Refactoring</em>(投機性通用化)· Ousterhout,<em>A Philosophy of Software Design</em>。" data-ja="<b>根源:</b>Brooks、<em>The Mythical Man-Month</em>(本質的複雑性と偶有的複雑性)· Fowler、<em>Refactoring</em>(投機的一般化)· Ousterhout、<em>A Philosophy of Software Design</em>。" data-ko="<b>근원:</b> Brooks, <em>The Mythical Man-Month</em>(본질적 대 우발적 복잡성) · Fowler, <em>Refactoring</em>(투기적 일반화) · Ousterhout, <em>A Philosophy of Software Design</em>." data-es="<b>Fuentes:</b> Brooks, <em>The Mythical Man-Month</em> (complejidad esencial vs. accidental) · Fowler, <em>Refactoring</em> (Generalidad especulativa) · Ousterhout, <em>A Philosophy of Software Design</em>."><b>Sources:</b> Brooks, <em>The Mythical Man-Month</em> (essential vs. accidental complexity) · Fowler, <em>Refactoring</em> (Speculative Generality) · Ousterhout, <em>A Philosophy of Software Design</em>.</p>
|
||||
<h3 data-en="Remedy" data-zh="修复" data-zhtw="對策" data-ja="対策" data-ko="처방" data-es="Remedio">Remedy</h3>
|
||||
<p data-en="Delete speculative abstractions until a second real consumer appears. Prefer the simplest design that solves today's problem — you can always add structure when the need is concrete." data-zh="在第二个真实消费者出现之前,删除那些投机性的抽象。优先选择能解决今天问题的最简设计——等需求变得具体时,你随时可以再加结构。" data-zhtw="在第二個真實消費者出現之前,刪除那些投機性的抽象。優先選擇能解決今天問題的最簡設計——等需求變得具體時,你隨時可以再加結構。" data-ja="二人目の本当の利用者が現れるまで、投機的な抽象は削除する。今日の問題を解く最もシンプルな設計を選ぶ——必要が具体的になれば、いつでも構造を足せる。" data-ko="두 번째 실제 사용자가 나타날 때까지 투기적 추상화를 삭제하라. 오늘의 문제를 푸는 가장 단순한 설계를 선호하라 — 필요가 구체화되면 언제든 구조를 더할 수 있다." data-es="Elimina las abstracciones especulativas hasta que aparezca un segundo consumidor real. Prefiere el diseño más simple que resuelva el problema de hoy — siempre puedes añadir estructura cuando la necesidad sea concreta.">Delete speculative abstractions until a second real consumer appears. Prefer the simplest design that solves today's problem — you can always add structure when the need is concrete.</p>
|
||||
</div>
|
||||
|
||||
<h2 id="r5">5 · Dependency Disorder <span class="code">R5</span></h2>
|
||||
<h2 id="r5"><span data-en="5 · Dependency Disorder" data-zh="5 · 依赖失序" data-zhtw="5 · 依賴失序" data-ja="5 · 依存関係の無秩序" data-ko="5 · 의존성 무질서" data-es="5 · Desorden de dependencias">5 · Dependency Disorder</span> <span class="code">R5</span></h2>
|
||||
<div class="risk">
|
||||
<p class="q">Diagnostic question: Do dependencies flow in a consistent, predictable direction?</p>
|
||||
<p>Architecture is mostly about the direction of the arrows. When high-level policy depends on low-level detail, the detail starts driving the design.</p>
|
||||
<h3>Signature symptoms</h3>
|
||||
<p class="q" data-en="Diagnostic question: Do dependencies flow in a consistent, predictable direction?" data-zh="诊断问题:依赖是否朝着一致、可预测的方向流动?" data-zhtw="診斷問題:依賴是否朝著一致、可預測的方向流動?" data-ja="診断的質問:依存は一貫した予測可能な方向に流れているか?" data-ko="진단 질문: 의존성이 일관되고 예측 가능한 방향으로 흐르는가?" data-es="Pregunta diagnóstica: ¿las dependencias fluyen en una dirección consistente y predecible?">Diagnostic question: Do dependencies flow in a consistent, predictable direction?</p>
|
||||
<p data-en="Architecture is mostly about the direction of the arrows. When high-level policy depends on low-level detail, the detail starts driving the design." data-zh="架构在很大程度上就是关于箭头的方向。当高层策略依赖于低层细节时,细节就开始主导设计。" data-zhtw="架構在很大程度上就是關於箭頭的方向。當高層策略依賴於低層細節時,細節就開始主導設計。" data-ja="アーキテクチャとは、主に矢印の向きの問題です。高レベルの方針が低レベルの詳細に依存すると、詳細が設計を駆動し始めます。" data-ko="아키텍처는 대부분 화살표의 방향에 관한 것입니다. 고수준 정책이 저수준 세부사항에 의존하면, 세부사항이 설계를 주도하기 시작합니다." data-es="La arquitectura trata sobre todo de la dirección de las flechas. Cuando la política de alto nivel depende del detalle de bajo nivel, el detalle empieza a dirigir el diseño.">Architecture is mostly about the direction of the arrows. When high-level policy depends on low-level detail, the detail starts driving the design.</p>
|
||||
<h3 data-en="Signature symptoms" data-zh="典型症状" data-zhtw="典型症狀" data-ja="典型的な症状" data-ko="대표적인 증상" data-es="Síntomas característicos">Signature symptoms</h3>
|
||||
<ul>
|
||||
<li>Circular dependencies between modules or packages</li>
|
||||
<li>High-level business logic importing directly from low-level infrastructure (DB drivers, HTTP clients)</li>
|
||||
<li>The domain layer reaching out to frameworks instead of the other way around</li>
|
||||
<li data-en="Circular dependencies between modules or packages" data-zh="模块或包之间存在循环依赖" data-zhtw="模組或套件之間存在循環依賴" data-ja="モジュールやパッケージ間の循環依存" data-ko="모듈이나 패키지 간의 순환 의존성" data-es="Dependencias circulares entre módulos o paquetes">Circular dependencies between modules or packages</li>
|
||||
<li data-en="High-level business logic importing directly from low-level infrastructure (DB drivers, HTTP clients)" data-zh="高层业务逻辑直接从低层基础设施(数据库驱动、HTTP 客户端)导入" data-zhtw="高層業務邏輯直接從低層基礎設施(資料庫驅動、HTTP 客戶端)匯入" data-ja="高レベルのビジネスロジックが低レベルのインフラ(DBドライバ、HTTPクライアント)から直接インポートしている" data-ko="고수준 비즈니스 로직이 저수준 인프라(DB 드라이버, HTTP 클라이언트)에서 직접 가져온다" data-es="Lógica de negocio de alto nivel que importa directamente de infraestructura de bajo nivel (drivers de BD, clientes HTTP)">High-level business logic importing directly from low-level infrastructure (DB drivers, HTTP clients)</li>
|
||||
<li data-en="The domain layer reaching out to frameworks instead of the other way around" data-zh="领域层主动去依赖框架,而不是反过来" data-zhtw="領域層主動去依賴框架,而不是反過來" data-ja="ドメイン層がフレームワークに手を伸ばしている——本来は逆であるべき" data-ko="도메인 계층이 프레임워크에 손을 뻗는다 — 반대가 되어야 하는데" data-es="La capa de dominio recurriendo a los frameworks en vez de al revés">The domain layer reaching out to frameworks instead of the other way around</li>
|
||||
</ul>
|
||||
<p class="src"><b>Sources:</b> Martin, <em>Clean Architecture</em> (Dependency Inversion, the Dependency Rule) · Brooks, <em>The Mythical Man-Month</em> · Winters et al., <em>Software Engineering at Google</em>.</p>
|
||||
<h3>Remedy</h3>
|
||||
<p>Invert the dependency: define an interface the high-level code owns, and let infrastructure implement it. Break cycles by extracting the shared abstraction both sides can depend on.</p>
|
||||
<p class="src" data-en="<b>Sources:</b> Martin, <em>Clean Architecture</em> (Dependency Inversion, the Dependency Rule) · Brooks, <em>The Mythical Man-Month</em> · Winters et al., <em>Software Engineering at Google</em>." data-zh="<b>根源:</b>Martin,<em>Clean Architecture</em>(依赖倒置、依赖规则)· Brooks,<em>The Mythical Man-Month</em> · Winters et al.,<em>Software Engineering at Google</em>。" data-zhtw="<b>根源:</b>Martin,<em>Clean Architecture</em>(依賴反轉、依賴規則)· Brooks,<em>The Mythical Man-Month</em> · Winters et al.,<em>Software Engineering at Google</em>。" data-ja="<b>根源:</b>Martin、<em>Clean Architecture</em>(依存性逆転、依存性のルール)· Brooks、<em>The Mythical Man-Month</em> · Winters et al.、<em>Software Engineering at Google</em>。" data-ko="<b>근원:</b> Martin, <em>Clean Architecture</em>(의존성 역전, 의존성 규칙) · Brooks, <em>The Mythical Man-Month</em> · Winters et al., <em>Software Engineering at Google</em>." data-es="<b>Fuentes:</b> Martin, <em>Clean Architecture</em> (Inversión de dependencias, la Regla de Dependencia) · Brooks, <em>The Mythical Man-Month</em> · Winters et al., <em>Software Engineering at Google</em>."><b>Sources:</b> Martin, <em>Clean Architecture</em> (Dependency Inversion, the Dependency Rule) · Brooks, <em>The Mythical Man-Month</em> · Winters et al., <em>Software Engineering at Google</em>.</p>
|
||||
<h3 data-en="Remedy" data-zh="修复" data-zhtw="對策" data-ja="対策" data-ko="처방" data-es="Remedio">Remedy</h3>
|
||||
<p data-en="Invert the dependency: define an interface the high-level code owns, and let infrastructure implement it. Break cycles by extracting the shared abstraction both sides can depend on." data-zh="倒置依赖:定义一个由高层代码拥有的接口,让基础设施去实现它。通过提炼出双方都能依赖的共享抽象来打破循环。" data-zhtw="反轉依賴:定義一個由高層程式碼擁有的介面,讓基礎設施去實作它。透過提煉出雙方都能依賴的共享抽象來打破循環。" data-ja="依存を逆転させる——高レベルのコードが所有するインターフェースを定義し、インフラにそれを実装させる。両者が依存できる共有の抽象を抽出して循環を断ち切る。" data-ko="의존성을 역전하라: 고수준 코드가 소유하는 인터페이스를 정의하고, 인프라가 그것을 구현하게 하라. 양쪽이 의존할 수 있는 공유 추상을 추출해 순환을 끊어라." data-es="Invierte la dependencia: define una interfaz que posea el código de alto nivel y deja que la infraestructura la implemente. Rompe los ciclos extrayendo la abstracción compartida de la que ambos lados puedan depender.">Invert the dependency: define an interface the high-level code owns, and let infrastructure implement it. Break cycles by extracting the shared abstraction both sides can depend on.</p>
|
||||
</div>
|
||||
|
||||
<h2 id="r6">6 · Domain Model Distortion <span class="code">R6</span></h2>
|
||||
<h2 id="r6"><span data-en="6 · Domain Model Distortion" data-zh="6 · 领域模型失真" data-zhtw="6 · 領域模型失真" data-ja="6 · ドメインモデルの歪み" data-ko="6 · 도메인 모델 왜곡" data-es="6 · Distorsión del modelo de dominio">6 · Domain Model Distortion</span> <span class="code">R6</span></h2>
|
||||
<div class="risk">
|
||||
<p class="q">Diagnostic question: Does the code faithfully represent the problem it is solving?</p>
|
||||
<p>When the model drifts from the domain, every reader has to translate in their head — and translation errors become bugs.</p>
|
||||
<h3>Signature symptoms</h3>
|
||||
<p class="q" data-en="Diagnostic question: Does the code faithfully represent the problem it is solving?" data-zh="诊断问题:代码是否忠实地表达了它正在解决的问题?" data-zhtw="診斷問題:程式碼是否忠實地表達了它正在解決的問題?" data-ja="診断的質問:コードは、それが解いている問題を忠実に表現しているか?" data-ko="진단 질문: 코드가 해결하고 있는 문제를 충실하게 표현하는가?" data-es="Pregunta diagnóstica: ¿el código representa fielmente el problema que está resolviendo?">Diagnostic question: Does the code faithfully represent the problem it is solving?</p>
|
||||
<p data-en="When the model drifts from the domain, every reader has to translate in their head — and translation errors become bugs." data-zh="当模型与领域产生偏离时,每个读代码的人都得在脑子里做一次翻译——而翻译错误就会变成 bug。" data-zhtw="當模型與領域產生偏離時,每個讀程式碼的人都得在腦子裡做一次翻譯——而翻譯錯誤就會變成 bug。" data-ja="モデルがドメインから乖離すると、読み手は皆、頭の中で翻訳しなければならなくなり——翻訳ミスがバグになります。" data-ko="모델이 도메인에서 멀어지면, 모든 독자가 머릿속에서 번역을 해야 하고 — 번역 오류는 버그가 됩니다." data-es="Cuando el modelo se aleja del dominio, cada lector tiene que traducir mentalmente — y los errores de traducción se convierten en bugs.">When the model drifts from the domain, every reader has to translate in their head — and translation errors become bugs.</p>
|
||||
<h3 data-en="Signature symptoms" data-zh="典型症状" data-zhtw="典型症狀" data-ja="典型的な症状" data-ko="대표적인 증상" data-es="Síntomas característicos">Signature symptoms</h3>
|
||||
<ul>
|
||||
<li>Anemic models: business logic scattered across services while domain objects hold only getters and setters</li>
|
||||
<li>Names that don't match what business stakeholders actually call the concept</li>
|
||||
<li>Logic bugs hiding in code that <em>reads</em> plausibly but doesn't model reality (e.g. comparing a value to itself after overwriting it)</li>
|
||||
<li data-en="Anemic models: business logic scattered across services while domain objects hold only getters and setters" data-zh="贫血模型:业务逻辑散落在各个服务中,而领域对象只持有 getter 和 setter" data-zhtw="貧血模型:業務邏輯散落在各個服務中,而領域物件只持有 getter 和 setter" data-ja="貧血モデル:ビジネスロジックがサービス全体に散らばり、ドメインオブジェクトは getter と setter しか持たない" data-ko="빈약한 모델: 비즈니스 로직이 여러 서비스에 흩어져 있고 도메인 객체는 getter와 setter만 가진다" data-es="Modelos anémicos: la lógica de negocio dispersa entre servicios mientras los objetos de dominio solo tienen getters y setters">Anemic models: business logic scattered across services while domain objects hold only getters and setters</li>
|
||||
<li data-en="Names that don't match what business stakeholders actually call the concept" data-zh="命名与业务相关方对该概念的实际称呼不一致" data-zhtw="命名與業務相關方對該概念的實際稱呼不一致" data-ja="ビジネスの関係者が実際にその概念を呼ぶ名前と一致しない命名" data-ko="비즈니스 이해관계자가 실제로 그 개념을 부르는 이름과 맞지 않는 명명" data-es="Nombres que no coinciden con cómo los interesados del negocio llaman realmente al concepto">Names that don't match what business stakeholders actually call the concept</li>
|
||||
<li data-en="Logic bugs hiding in code that <em>reads</em> plausibly but doesn't model reality (e.g. comparing a value to itself after overwriting it)" data-zh="逻辑 bug 藏在那些<em>读起来</em>合理却没有真实建模的代码里(例如在覆盖某个值之后又把它和自身比较)" data-zhtw="邏輯 bug 藏在那些<em>讀起來</em>合理卻沒有真實建模的程式碼裡(例如在覆寫某個值之後又把它和自身比較)" data-ja="<em>読めば</em>もっともらしいが現実をモデル化していないコードに潜む論理バグ(例:ある値を上書きした後にそれ自身と比較する)" data-ko="<em>읽기에는</em> 그럴듯하지만 현실을 모델링하지 못한 코드에 숨은 논리 버그(예: 어떤 값을 덮어쓴 뒤 자기 자신과 비교하기)" data-es="Errores de lógica ocultos en código que <em>se lee</em> de forma plausible pero no modela la realidad (p. ej. comparar un valor consigo mismo después de sobrescribirlo)">Logic bugs hiding in code that <em>reads</em> plausibly but doesn't model reality (e.g. comparing a value to itself after overwriting it)</li>
|
||||
</ul>
|
||||
<p class="src"><b>Sources:</b> Evans, <em>Domain-Driven Design</em> (rich models, ubiquitous language) · Fowler, <em>Refactoring</em> (Anemic Domain Model).</p>
|
||||
<h3>Remedy</h3>
|
||||
<p>Move behaviour onto the objects that own the data. Align every name with the language the business uses. Make illegal states unrepresentable.</p>
|
||||
<p class="src" data-en="<b>Sources:</b> Evans, <em>Domain-Driven Design</em> (rich models, ubiquitous language) · Fowler, <em>Refactoring</em> (Anemic Domain Model)." data-zh="<b>根源:</b>Evans,<em>Domain-Driven Design</em>(充血模型、统一语言)· Fowler,<em>Refactoring</em>(贫血领域模型)。" data-zhtw="<b>根源:</b>Evans,<em>Domain-Driven Design</em>(充血模型、統一語言)· Fowler,<em>Refactoring</em>(貧血領域模型)。" data-ja="<b>根源:</b>Evans、<em>Domain-Driven Design</em>(リッチモデル、ユビキタス言語)· Fowler、<em>Refactoring</em>(ドメインモデル貧血症)。" data-ko="<b>근원:</b> Evans, <em>Domain-Driven Design</em>(풍부한 모델, 유비쿼터스 언어) · Fowler, <em>Refactoring</em>(빈약한 도메인 모델)." data-es="<b>Fuentes:</b> Evans, <em>Domain-Driven Design</em> (modelos ricos, lenguaje ubicuo) · Fowler, <em>Refactoring</em> (Modelo de dominio anémico)."><b>Sources:</b> Evans, <em>Domain-Driven Design</em> (rich models, ubiquitous language) · Fowler, <em>Refactoring</em> (Anemic Domain Model).</p>
|
||||
<h3 data-en="Remedy" data-zh="修复" data-zhtw="對策" data-ja="対策" data-ko="처방" data-es="Remedio">Remedy</h3>
|
||||
<p data-en="Move behaviour onto the objects that own the data. Align every name with the language the business uses. Make illegal states unrepresentable." data-zh="把行为移动到拥有数据的对象上。让每个命名都与业务使用的语言对齐。让非法状态无法被表示。" data-zhtw="把行為移動到擁有資料的物件上。讓每個命名都與業務使用的語言對齊。讓非法狀態無法被表示。" data-ja="振る舞いを、データを所有するオブジェクトへ移す。あらゆる名前をビジネスが使う言葉に揃える。不正な状態を表現できないようにする。" data-ko="행위를 데이터를 소유한 객체로 옮겨라. 모든 이름을 비즈니스가 쓰는 언어에 맞춰라. 불가능한 상태를 표현할 수 없게 만들어라." data-es="Mueve el comportamiento a los objetos que poseen los datos. Alinea cada nombre con el lenguaje que usa el negocio. Haz que los estados ilegales sean irrepresentables.">Move behaviour onto the objects that own the data. Align every name with the language the business uses. Make illegal states unrepresentable.</p>
|
||||
</div>
|
||||
|
||||
<h2>And the test suite decays too</h2>
|
||||
<p>brooks-lint applies the same lens to tests, with six parallel risks (T1–T6) drawn from <em>xUnit Test Patterns</em>, <em>The Art of Unit Testing</em>, <em>How Google Tests Software</em>, and <em>Working Effectively with Legacy Code</em>:</p>
|
||||
<h2 data-en="And the test suite decays too" data-zh="测试套件也会腐化" data-zhtw="測試套件也會衰退" data-ja="テストスイートもまた劣化する" data-ko="테스트 스위트도 쇠퇴한다" data-es="Y la suite de pruebas también se deteriora">And the test suite decays too</h2>
|
||||
<p data-en="brooks-lint applies the same lens to tests, with six parallel risks (T1–T6) drawn from <em>xUnit Test Patterns</em>, <em>The Art of Unit Testing</em>, <em>How Google Tests Software</em>, and <em>Working Effectively with Legacy Code</em>:" data-zh="brooks-lint 用同样的视角审视测试,提出六个对应的风险(T1–T6),取自 <em>xUnit Test Patterns</em>、<em>The Art of Unit Testing</em>、<em>How Google Tests Software</em> 和 <em>Working Effectively with Legacy Code</em>:" data-zhtw="brooks-lint 用同樣的視角審視測試,提出六個對應的風險(T1–T6),取自 <em>xUnit Test Patterns</em>、<em>The Art of Unit Testing</em>、<em>How Google Tests Software</em> 和 <em>Working Effectively with Legacy Code</em>:" data-ja="brooks-lint は同じレンズをテストにも当て、<em>xUnit Test Patterns</em>、<em>The Art of Unit Testing</em>、<em>How Google Tests Software</em>、<em>Working Effectively with Legacy Code</em> から導いた六つの並行するリスク(T1–T6)を扱います:" data-ko="brooks-lint은 같은 렌즈를 테스트에도 적용해, <em>xUnit Test Patterns</em>, <em>The Art of Unit Testing</em>, <em>How Google Tests Software</em>, <em>Working Effectively with Legacy Code</em>에서 끌어낸 여섯 가지 병렬 위험(T1–T6)을 다룹니다:" data-es="brooks-lint aplica la misma mirada a las pruebas, con seis riesgos paralelos (T1–T6) extraídos de <em>xUnit Test Patterns</em>, <em>The Art of Unit Testing</em>, <em>How Google Tests Software</em> y <em>Working Effectively with Legacy Code</em>:">brooks-lint applies the same lens to tests, with six parallel risks (T1–T6) drawn from <em>xUnit Test Patterns</em>, <em>The Art of Unit Testing</em>, <em>How Google Tests Software</em>, and <em>Working Effectively with Legacy Code</em>:</p>
|
||||
<table>
|
||||
<tr><th>Risk</th><th>The question it asks</th></tr>
|
||||
<tr><td>Test Obscurity</td><td>Can you tell what a test verifies without reading its implementation?</td></tr>
|
||||
<tr><td>Test Brittleness</td><td>Does a refactor that preserves behaviour still break the test?</td></tr>
|
||||
<tr><td>Test Duplication</td><td>Is the same setup or assertion copied across many tests?</td></tr>
|
||||
<tr><td>Mock Abuse</td><td>Does the test verify interactions instead of outcomes?</td></tr>
|
||||
<tr><td>Coverage Illusion</td><td>Does high coverage hide tests that assert nothing meaningful?</td></tr>
|
||||
<tr><td>Architecture Mismatch</td><td>Do the tests fight the code's seams instead of using them?</td></tr>
|
||||
<tr><th data-en="Risk" data-zh="风险" data-zhtw="風險" data-ja="リスク" data-ko="위험" data-es="Riesgo">Risk</th><th data-en="The question it asks" data-zh="它提出的问题" data-zhtw="它提出的問題" data-ja="それが問う質問" data-ko="그것이 던지는 질문" data-es="La pregunta que plantea">The question it asks</th></tr>
|
||||
<tr><td data-en="Test Obscurity" data-zh="测试晦涩" data-zhtw="測試晦澀" data-ja="テストの不明瞭さ" data-ko="테스트 불명확성" data-es="Opacidad de las pruebas">Test Obscurity</td><td data-en="Can you tell what a test verifies without reading its implementation?" data-zh="不读实现就能看出一个测试在验证什么吗?" data-zhtw="不讀實作就能看出一個測試在驗證什麼嗎?" data-ja="実装を読まずに、そのテストが何を検証しているか分かるか?" data-ko="구현을 읽지 않고도 테스트가 무엇을 검증하는지 알 수 있는가?" data-es="¿Puedes saber qué verifica una prueba sin leer su implementación?">Can you tell what a test verifies without reading its implementation?</td></tr>
|
||||
<tr><td data-en="Test Brittleness" data-zh="测试脆弱" data-zhtw="測試脆弱" data-ja="テストの脆弱性" data-ko="테스트 취약성" data-es="Fragilidad de las pruebas">Test Brittleness</td><td data-en="Does a refactor that preserves behaviour still break the test?" data-zh="一次保持行为不变的重构是否仍会让测试失败?" data-zhtw="一次保持行為不變的重構是否仍會讓測試失敗?" data-ja="振る舞いを保つリファクタリングでも、テストが壊れてしまうか?" data-ko="동작을 보존하는 리팩터링인데도 테스트가 깨지는가?" data-es="¿Una refactorización que preserva el comportamiento aún rompe la prueba?">Does a refactor that preserves behaviour still break the test?</td></tr>
|
||||
<tr><td data-en="Test Duplication" data-zh="测试重复" data-zhtw="測試重複" data-ja="テストの重複" data-ko="테스트 중복" data-es="Duplicación de pruebas">Test Duplication</td><td data-en="Is the same setup or assertion copied across many tests?" data-zh="同样的 setup 或断言是否被复制到许多测试中?" data-zhtw="同樣的 setup 或斷言是否被複製到許多測試中?" data-ja="同じセットアップやアサーションが多くのテストにコピーされていないか?" data-ko="같은 셋업이나 단언이 여러 테스트에 복사되어 있는가?" data-es="¿La misma preparación o aserción se copia en muchas pruebas?">Is the same setup or assertion copied across many tests?</td></tr>
|
||||
<tr><td data-en="Mock Abuse" data-zh="模拟滥用" data-zhtw="模擬濫用" data-ja="モックの乱用" data-ko="목 남용" data-es="Abuso de mocks">Mock Abuse</td><td data-en="Does the test verify interactions instead of outcomes?" data-zh="测试验证的是交互过程而非最终结果吗?" data-zhtw="測試驗證的是互動過程而非最終結果嗎?" data-ja="テストは結果ではなく相互作用を検証していないか?" data-ko="테스트가 결과 대신 상호작용을 검증하는가?" data-es="¿La prueba verifica interacciones en lugar de resultados?">Does the test verify interactions instead of outcomes?</td></tr>
|
||||
<tr><td data-en="Coverage Illusion" data-zh="覆盖率幻觉" data-zhtw="覆蓋率幻覺" data-ja="カバレッジの幻想" data-ko="커버리지 착각" data-es="Ilusión de cobertura">Coverage Illusion</td><td data-en="Does high coverage hide tests that assert nothing meaningful?" data-zh="高覆盖率是否掩盖了那些没有做任何有意义断言的测试?" data-zhtw="高覆蓋率是否掩蓋了那些沒有做任何有意義斷言的測試?" data-ja="高いカバレッジが、意味のあるアサーションを何もしていないテストを隠していないか?" data-ko="높은 커버리지가 의미 있는 단언을 전혀 하지 않는 테스트를 가리고 있는가?" data-es="¿La alta cobertura oculta pruebas que no afirman nada significativo?">Does high coverage hide tests that assert nothing meaningful?</td></tr>
|
||||
<tr><td data-en="Architecture Mismatch" data-zh="架构错配" data-zhtw="架構錯配" data-ja="アーキテクチャの不整合" data-ko="아키텍처 불일치" data-es="Desajuste de arquitectura">Architecture Mismatch</td><td data-en="Do the tests fight the code's seams instead of using them?" data-zh="测试是在对抗代码的接缝,而不是利用它们吗?" data-zhtw="測試是在對抗程式碼的接縫,而不是利用它們嗎?" data-ja="テストはコードの継ぎ目を使うのではなく、それと戦っていないか?" data-ko="테스트가 코드의 이음새를 활용하는 대신 그것과 싸우고 있는가?" data-es="¿Las pruebas luchan contra las costuras del código en vez de usarlas?">Do the tests fight the code's seams instead of using them?</td></tr>
|
||||
</table>
|
||||
|
||||
<div class="cta">
|
||||
<h3>See it run on your own code</h3>
|
||||
<p>brooks-lint is a free, open-source plugin for Claude Code, Gemini CLI, and Codex. Install it and review any file in one command:</p>
|
||||
<p><code>/plugin marketplace add hyhmrright/brooks-lint</code><br><code>/brooks-review</code></p>
|
||||
<a href="https://github.com/hyhmrright/brooks-lint">⭐ Get it on GitHub</a>
|
||||
<h3 data-en="See it run on your own code" data-zh="在你自己的代码上看看效果" data-zhtw="在你自己的程式碼上看看效果" data-ja="あなた自身のコードで動かしてみる" data-ko="당신의 코드에서 직접 실행해 보세요" data-es="Míralo funcionar en tu propio código">See it run on your own code</h3>
|
||||
<p data-en="brooks-lint is a free, open-source plugin for Claude Code, Gemini CLI, and Codex. Install it and review any file in one command:" data-zh="brooks-lint 是一个免费、开源的插件,支持 Claude Code、Gemini CLI 和 Codex。安装后,一条命令即可评审任意文件:" data-zhtw="brooks-lint 是一個免費、開源的外掛,支援 Claude Code、Gemini CLI 和 Codex。安裝後,一條指令即可審查任意檔案:" data-ja="brooks-lint は Claude Code、Gemini CLI、Codex 向けの無料・オープンソースのプラグインです。インストールすれば、一つのコマンドで任意のファイルをレビューできます:" data-ko="brooks-lint은 Claude Code, Gemini CLI, Codex를 위한 무료 오픈소스 플러그인입니다. 설치하면 명령 하나로 어떤 파일이든 리뷰할 수 있습니다:" data-es="brooks-lint es un plugin gratuito y de código abierto para Claude Code, Gemini CLI y Codex. Instálalo y revisa cualquier archivo con un solo comando:">brooks-lint is a free, open-source plugin for Claude Code, Gemini CLI, and Codex. Install it and review any file in one command:</p>
|
||||
<p data-en="<code>/plugin marketplace add hyhmrright/brooks-lint</code><br><code>/brooks-review</code>" data-zh="<code>/plugin marketplace add hyhmrright/brooks-lint</code><br><code>/brooks-review</code>" data-zhtw="<code>/plugin marketplace add hyhmrright/brooks-lint</code><br><code>/brooks-review</code>" data-ja="<code>/plugin marketplace add hyhmrright/brooks-lint</code><br><code>/brooks-review</code>" data-ko="<code>/plugin marketplace add hyhmrright/brooks-lint</code><br><code>/brooks-review</code>" data-es="<code>/plugin marketplace add hyhmrright/brooks-lint</code><br><code>/brooks-review</code>"><code>/plugin marketplace add hyhmrright/brooks-lint</code><br><code>/brooks-review</code></p>
|
||||
<a href="https://github.com/hyhmrright/brooks-lint" data-en="⭐ Get it on GitHub" data-zh="⭐ 在 GitHub 上获取" data-zhtw="⭐ 在 GitHub 上取得" data-ja="⭐ GitHub で入手する" data-ko="⭐ GitHub에서 받기" data-es="⭐ Conseguir en GitHub">⭐ Get it on GitHub</a>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<a href="index.html">Home</a> · <a href="gallery.html">Worked-example gallery</a> · <a href="https://github.com/hyhmrright/brooks-lint">Source & docs</a><br>
|
||||
brooks-lint — AI code reviews grounded in twelve classic engineering books · MIT licensed
|
||||
<a href="index.html" data-en="Home" data-zh="首页" data-zhtw="首頁" data-ja="ホーム" data-ko="홈" data-es="Inicio">Home</a> · <a href="gallery.html" data-en="Worked-example gallery" data-zh="实战示例画廊" data-zhtw="實戰範例展示" data-ja="実例ギャラリー" data-ko="실전 예제 갤러리" data-es="Galería de ejemplos resueltos">Worked-example gallery</a> · <a href="https://github.com/hyhmrright/brooks-lint" data-en="Source & docs" data-zh="源码与文档" data-zhtw="原始碼與文件" data-ja="ソースとドキュメント" data-ko="소스 및 문서" data-es="Código y documentación">Source & docs</a><br>
|
||||
<span data-en="brooks-lint — AI code reviews grounded in twelve classic engineering books · MIT licensed" data-zh="brooks-lint — 扎根于十二本经典工程著作的 AI 代码评审 · MIT 许可" data-zhtw="brooks-lint — 扎根於十二本經典工程著作的 AI 程式碼審查 · MIT 授權" data-ja="brooks-lint — 十二冊の古典的なエンジニアリング名著に基づく AI コードレビュー · MIT ライセンス" data-ko="brooks-lint — 열두 권의 고전 엔지니어링 명저에 기반한 AI 코드 리뷰 · MIT 라이선스" data-es="brooks-lint — revisiones de código con IA basadas en doce libros clásicos de ingeniería · licencia MIT">brooks-lint — AI code reviews grounded in twelve classic engineering books · MIT licensed</span>
|
||||
</footer>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
var STORE = "brooks-lint-lang";
|
||||
var HTML_LANG = { en:"en", zh:"zh-CN", zhtw:"zh-TW", ja:"ja", ko:"ko", es:"es" };
|
||||
function apply(lang) {
|
||||
document.documentElement.lang = HTML_LANG[lang] || "en";
|
||||
document.querySelectorAll("[data-en]").forEach(function (el) {
|
||||
var v = el.getAttribute("data-" + lang);
|
||||
if (v != null) el.innerHTML = v;
|
||||
});
|
||||
document.querySelectorAll(".lang-toggle button").forEach(function (b) {
|
||||
b.classList.toggle("active", b.getAttribute("data-set-lang") === lang);
|
||||
});
|
||||
try { localStorage.setItem(STORE, lang); } catch (e) {}
|
||||
}
|
||||
document.querySelectorAll("[data-set-lang]").forEach(function (b) {
|
||||
b.addEventListener("click", function () { apply(b.getAttribute("data-set-lang")); });
|
||||
});
|
||||
var saved;
|
||||
try { saved = localStorage.getItem(STORE); } catch (e) {}
|
||||
if (!saved) {
|
||||
var nav = (navigator.language || "").toLowerCase();
|
||||
saved = (nav.indexOf("zh-tw")===0||nav.indexOf("zh-hant")===0||nav.indexOf("zh-hk")===0||nav.indexOf("zh-mo")===0) ? "zhtw"
|
||||
: nav.indexOf("zh")===0 ? "zh"
|
||||
: nav.indexOf("ja")===0 ? "ja"
|
||||
: nav.indexOf("ko")===0 ? "ko"
|
||||
: nav.indexOf("es")===0 ? "es"
|
||||
: "en";
|
||||
}
|
||||
apply(saved);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+117
-87
@@ -28,7 +28,7 @@
|
||||
"description": "AI code reviews grounded in twelve classic software engineering books. Decay-risk diagnostics with book citations, severity labels, and six analysis modes including full-sweep auto-fix.",
|
||||
"url": "https://hyhmrright.github.io/brooks-lint/",
|
||||
"image": "https://hyhmrright.github.io/brooks-lint/hero.png",
|
||||
"softwareVersion": "1.3.0",
|
||||
"softwareVersion": "1.4.0",
|
||||
"license": "https://github.com/hyhmrright/brooks-lint/blob/main/LICENSE",
|
||||
"codeRepository": "https://github.com/hyhmrright/brooks-lint",
|
||||
"keywords": "AI code review, code quality, tech debt, architecture audit, test quality, Claude Code plugin, refactoring, clean architecture",
|
||||
@@ -219,15 +219,19 @@
|
||||
<div class="wrap nav-in">
|
||||
<a class="brand" href="#top"><img src="logo.svg" alt="brooks-lint logo">brooks-lint</a>
|
||||
<div class="nav-links">
|
||||
<a class="hide-sm" href="#risks" data-en="Decay Risks" data-zh="腐化风险">Decay Risks</a>
|
||||
<a class="hide-sm" href="#sample" data-en="Example" data-zh="示例">Example</a>
|
||||
<a class="hide-sm" href="#benchmark" data-en="Benchmark" data-zh="基准">Benchmark</a>
|
||||
<a class="hide-sm" href="gallery.html" data-en="Gallery" data-zh="画廊">Gallery</a>
|
||||
<a class="hide-sm" href="guide.html" data-en="Guide" data-zh="指南">Guide</a>
|
||||
<a class="hide-sm" href="#install" data-en="Install" data-zh="安装">Install</a>
|
||||
<a class="hide-sm" href="#risks" data-en="Decay Risks" data-zhtw="衰退風險" data-ja="劣化リスク" data-ko="쇠퇴 위험" data-es="Riesgos de deterioro" data-zh="腐化风险">Decay Risks</a>
|
||||
<a class="hide-sm" href="#sample" data-en="Example" data-zhtw="範例" data-ja="実例" data-ko="예시" data-es="Ejemplo" data-zh="示例">Example</a>
|
||||
<a class="hide-sm" href="#benchmark" data-en="Benchmark" data-zhtw="基準測試" data-ja="ベンチマーク" data-ko="벤치마크" data-es="Benchmark" data-zh="基准">Benchmark</a>
|
||||
<a class="hide-sm" href="gallery.html" data-en="Gallery" data-zhtw="範例展示" data-ja="ギャラリー" data-ko="갤러리" data-es="Galería" data-zh="画廊">Gallery</a>
|
||||
<a class="hide-sm" href="guide.html" data-en="Guide" data-zhtw="指南" data-ja="ガイド" data-ko="가이드" data-es="Guía" data-zh="指南">Guide</a>
|
||||
<a class="hide-sm" href="#install" data-en="Install" data-zhtw="安裝" data-ja="インストール" data-ko="설치" data-es="Instalación" data-zh="安装">Install</a>
|
||||
<span class="lang-toggle">
|
||||
<button data-set-lang="en">EN</button>
|
||||
<button data-set-lang="zh">中文</button>
|
||||
<button data-set-lang="zh">中</button>
|
||||
<button data-set-lang="zhtw">繁</button>
|
||||
<button data-set-lang="ja">日</button>
|
||||
<button data-set-lang="ko">한</button>
|
||||
<button data-set-lang="es">ES</button>
|
||||
</span>
|
||||
<a href="https://github.com/hyhmrright/brooks-lint" target="_blank" rel="noopener">GitHub ↗</a>
|
||||
</div>
|
||||
@@ -239,23 +243,35 @@
|
||||
<!-- HERO -->
|
||||
<header class="hero">
|
||||
<div class="wrap">
|
||||
<span class="eyebrow" data-en="Claude Code · Codex · Gemini plugin" data-zh="Claude Code · Codex · Gemini 插件">Claude Code · Codex · Gemini plugin</span>
|
||||
<h1 data-en="AI code reviews grounded in twelve classic engineering books"
|
||||
<span class="eyebrow" data-en="Claude Code · Codex · Gemini plugin" data-zhtw="Claude Code · Codex · Gemini 外掛" data-ja="Claude Code · Codex · Gemini プラグイン" data-ko="Claude Code · Codex · Gemini 플러그인" data-es="Plugin para Claude Code · Codex · Gemini" data-zh="Claude Code · Codex · Gemini 插件">Claude Code · Codex · Gemini plugin</span>
|
||||
<h1 data-en="AI code reviews grounded in twelve classic engineering books" data-zhtw="扎根於十二本經典工程著作的 AI 程式碼審查" data-ja="十二冊の古典的工学書に根ざした AI コードレビュー" data-ko="열두 권의 고전 엔지니어링 도서에 뿌리내린 AI 코드 리뷰" data-es="Revisiones de código con IA fundamentadas en doce libros clásicos de ingeniería"
|
||||
data-zh="扎根于十二本经典工程著作的 AI 代码审查">AI code reviews grounded in twelve classic engineering books</h1>
|
||||
<p class="lede"
|
||||
data-en="Most tools count lines and complexity. brooks-lint diagnoses your code against twelve decay risks synthesized from the classics — every finding cited, scored, and remedied."
|
||||
data-en="Most tools count lines and complexity. brooks-lint diagnoses your code against twelve decay risks synthesized from the classics — every finding cited, scored, and remedied." data-zhtw="多數工具只計算行數和複雜度。brooks-lint 對照從經典提煉的十二類衰退風險逐一診斷你的程式碼——每條結論都有出處、有評分、有對策。" data-ja="ほとんどのツールは行数と複雑度を数えるだけです。brooks-lint は古典から統合した十二の劣化リスクに照らしてコードを診断します——すべての指摘に出典、スコア、対策が付きます。" data-ko="대부분의 도구는 줄 수와 복잡도만 셉니다. brooks-lint는 고전에서 종합한 열두 가지 쇠퇴 위험에 비추어 코드를 진단합니다 — 모든 진단에 출처, 점수, 처방이 따릅니다." data-es="La mayoría de las herramientas cuentan líneas y complejidad. brooks-lint diagnostica tu código frente a doce riesgos de deterioro sintetizados de los clásicos — cada hallazgo citado, puntuado y con remedio."
|
||||
data-zh="多数工具只统计行数和复杂度。brooks-lint 把你的代码对照从十二本经典提炼的腐化风险逐一诊断——每条结论都有出处、有评分、有修复方案。">
|
||||
Most tools count lines and complexity. brooks-lint diagnoses your code against twelve decay risks synthesized from the classics — every finding cited, scored, and remedied.
|
||||
</p>
|
||||
<blockquote class="quote"
|
||||
data-en="“The bearing of a child takes nine months, no matter how many women are assigned.”"
|
||||
data-en="“The bearing of a child takes nine months, no matter how many women are assigned.”" data-zhtw="「一個孩子的孕育需要九個月,無論你派多少人去。」" data-ja="「一人の子を産むには九か月かかる。何人の女性を割り当てようとも。」" data-ko="“아이를 낳는 데는 아홉 달이 걸린다. 몇 명의 여성을 투입하든 마찬가지다.”" data-es="“Gestar un niño toma nueve meses, sin importar cuántas mujeres se asignen.”"
|
||||
data-zh="“一个孩子的孕育需要九个月,无论你派多少人去。”">
|
||||
“The bearing of a child takes nine months, no matter how many women are assigned.”
|
||||
<cite data-en="— Frederick Brooks, The Mythical Man-Month (1975)" data-zh="—— 弗雷德里克·布鲁克斯,《人月神话》(1975)">— Frederick Brooks, The Mythical Man-Month (1975)</cite>
|
||||
<cite data-en="— Frederick Brooks, The Mythical Man-Month (1975)" data-zhtw="—— 弗雷德里克·布魯克斯,《人月神話》(1975)" data-ja="— フレデリック・ブルックス『人月の神話』(1975)" data-ko="— 프레더릭 브룩스, 『맨먼스 미신』(1975)" data-es="— Frederick Brooks, The Mythical Man-Month (1975)" data-zh="—— 弗雷德里克·布鲁克斯,《人月神话》(1975)">— Frederick Brooks, The Mythical Man-Month (1975)</cite>
|
||||
</blockquote>
|
||||
<div class="cta-row">
|
||||
<a class="btn btn-primary" href="#install" data-en="Get Started" data-zh="开始使用">Get Started</a>
|
||||
<a class="btn btn-ghost" href="https://github.com/hyhmrright/brooks-lint" target="_blank" rel="noopener" data-en="View on GitHub" data-zh="GitHub 查看">View on GitHub</a>
|
||||
<a class="btn btn-primary" href="#install" data-en="Get Started" data-zhtw="開始使用" data-ja="はじめる" data-ko="시작하기" data-es="Empezar" data-zh="开始使用">Get Started</a>
|
||||
<a class="btn btn-ghost" href="https://github.com/hyhmrright/brooks-lint" target="_blank" rel="noopener" data-en="View on GitHub" data-zhtw="在 GitHub 查看" data-ja="GitHub で見る" data-ko="GitHub에서 보기" data-es="Ver en GitHub" data-zh="GitHub 查看">View on GitHub</a>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 26px; text-align: center;">
|
||||
<a href="https://trendshift.io/repositories/47738" target="_blank" rel="noopener"><img src="https://trendshift.io/api/badge/trendshift/repositories/47738/daily?language=JavaScript" alt="#2 JavaScript Repository of the Day | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"></a>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 30px; text-align: center;">
|
||||
<img src="banner-en.svg"
|
||||
data-src-en="banner-en.svg" data-src-zh="banner-zh-CN.svg" data-src-zhtw="banner-zh-TW.svg"
|
||||
data-src-ja="banner-ja.svg" data-src-ko="banner-ko.svg" data-src-es="banner-es.svg"
|
||||
alt="Your code → 12 classics → 12 decay risks → cited findings"
|
||||
style="width: 100%; max-width: 820px; height: auto;">
|
||||
</div>
|
||||
|
||||
<div class="hero-shot">
|
||||
@@ -263,10 +279,10 @@
|
||||
</div>
|
||||
|
||||
<div class="stats">
|
||||
<div class="stat"><b>12</b><span data-en="classic books" data-zh="本经典著作">classic books</span></div>
|
||||
<div class="stat"><b>12</b><span data-en="decay risks (R1–R6 · T1–T6)" data-zh="腐化风险 (R1–R6 · T1–T6)">decay risks (R1–R6 · T1–T6)</span></div>
|
||||
<div class="stat"><b>94%</b><span data-en="benchmark pass rate" data-zh="基准通过率">benchmark pass rate</span></div>
|
||||
<div class="stat"><b>6</b><span data-en="independent skills" data-zh="个独立技能">independent skills</span></div>
|
||||
<div class="stat"><b>12</b><span data-en="classic books" data-zhtw="本經典著作" data-ja="冊の古典" data-ko="권의 고전" data-es="libros clásicos" data-zh="本经典著作">classic books</span></div>
|
||||
<div class="stat"><b>12</b><span data-en="decay risks (R1–R6 · T1–T6)" data-zhtw="衰退風險(R1–R6 · T1–T6)" data-ja="劣化リスク(R1–R6 · T1–T6)" data-ko="쇠퇴 위험 (R1–R6 · T1–T6)" data-es="riesgos de deterioro (R1–R6 · T1–T6)" data-zh="腐化风险 (R1–R6 · T1–T6)">decay risks (R1–R6 · T1–T6)</span></div>
|
||||
<div class="stat"><b>94%</b><span data-en="benchmark pass rate" data-zhtw="基準通過率" data-ja="ベンチマーク合格率" data-ko="벤치마크 통과율" data-es="tasa de aprobación del benchmark" data-zh="基准通过率">benchmark pass rate</span></div>
|
||||
<div class="stat"><b>6</b><span data-en="independent skills" data-zhtw="個獨立技能" data-ja="つの独立したスキル" data-ko="개의 독립 스킬" data-es="skills independientes" data-zh="个独立技能">independent skills</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
@@ -275,33 +291,33 @@
|
||||
<section>
|
||||
<div class="wrap">
|
||||
<div class="section-head">
|
||||
<h2 data-en="Not a linter. A second opinion from the canon." data-zh="不是 linter,是来自经典的第二意见。">Not a linter. A second opinion from the canon.</h2>
|
||||
<p data-en="Linters catch syntax. brooks-lint catches architectural drift, knowledge silos, and domain distortion — the slow problems that cost teams months."
|
||||
<h2 data-en="Not a linter. A second opinion from the canon." data-zhtw="不是 linter,而是來自經典的第二意見。" data-ja="linter ではありません。古典からのセカンドオピニオンです。" data-ko="linter가 아닙니다. 고전이 주는 세컨드 오피니언입니다." data-es="No es un linter. Una segunda opinión desde el canon." data-zh="不是 linter,是来自经典的第二意见。">Not a linter. A second opinion from the canon.</h2>
|
||||
<p data-en="Linters catch syntax. brooks-lint catches architectural drift, knowledge silos, and domain distortion — the slow problems that cost teams months." data-zhtw="linter 抓語法。brooks-lint 抓架構漂移、知識孤島和領域失真——那些拖垮團隊數月的慢性問題。" data-ja="linter は構文を捉えます。brooks-lint はアーキテクチャの漂流、知識のサイロ化、ドメインの歪みを捉えます——チームに数か月を費やさせる遅効性の問題です。" data-ko="linter는 문법을 잡습니다. brooks-lint는 아키텍처 표류, 지식 사일로, 도메인 왜곡을 잡습니다 — 팀에 수개월을 허비하게 하는 느린 문제들입니다." data-es="Los linters detectan sintaxis. brooks-lint detecta la deriva arquitectónica, los silos de conocimiento y la distorsión del dominio — los problemas lentos que cuestan meses a los equipos."
|
||||
data-zh="linter 抓语法。brooks-lint 抓架构漂移、知识孤岛和领域失真——那些拖垮团队数月却无人察觉的慢性病。">Linters catch syntax. brooks-lint catches architectural drift, knowledge silos, and domain distortion — the slow problems that cost teams months.</p>
|
||||
</div>
|
||||
<div class="grid-4">
|
||||
<div class="feature">
|
||||
<span class="ic">⚖️</span>
|
||||
<h3 data-en="The Iron Law" data-zh="铁律格式">The Iron Law</h3>
|
||||
<p data-en="Every finding follows one shape: Symptom → Source → Consequence → Remedy. No vague vibes, ever."
|
||||
<h3 data-en="The Iron Law" data-zhtw="鐵律" data-ja="鉄の掟" data-ko="철칙" data-es="La Ley de Hierro" data-zh="铁律格式">The Iron Law</h3>
|
||||
<p data-en="Every finding follows one shape: Symptom → Source → Consequence → Remedy. No vague vibes, ever." data-zhtw="每條結論遵循同一形態:症狀 → 根源 → 後果 → 對策。絕不含糊其辭。" data-ja="すべての指摘が同じ形をとります:症状 → 根源 → 結果 → 対策。曖昧な感想は一切ありません。" data-ko="모든 진단은 하나의 형태를 따릅니다: 증상 → 근원 → 결과 → 처방. 모호한 느낌은 결코 없습니다." data-es="Cada hallazgo sigue una sola forma: Síntoma → Origen → Consecuencia → Remedio. Nunca vaguedades."
|
||||
data-zh="每条结论遵循同一形态:症状 → 根源 → 后果 → 修复。绝不含糊其辞。">Every finding follows one shape: Symptom → Source → Consequence → Remedy. No vague vibes, ever.</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<span class="ic">📚</span>
|
||||
<h3 data-en="Cited to the books" data-zh="书有出处">Cited to the books</h3>
|
||||
<p data-en="Brooks, Fowler, Martin, Ousterhout, Evans, Feathers, Meszaros and more — each finding names the author and principle."
|
||||
<h3 data-en="Cited to the books" data-zhtw="引用書目出處" data-ja="書籍に出典あり" data-ko="도서 출처 인용" data-es="Citado a los libros" data-zh="书有出处">Cited to the books</h3>
|
||||
<p data-en="Brooks, Fowler, Martin, Ousterhout, Evans, Feathers, Meszaros and more — each finding names the author and principle." data-zhtw="布魯克斯、Fowler、Martin、Ousterhout、Evans、Feathers、Meszaros 等——每條結論都點名作者與原則。" data-ja="Brooks、Fowler、Martin、Ousterhout、Evans、Feathers、Meszaros など——各指摘が著者と原則を明示します。" data-ko="Brooks, Fowler, Martin, Ousterhout, Evans, Feathers, Meszaros 등 — 각 진단이 저자와 원칙을 명시합니다." data-es="Brooks, Fowler, Martin, Ousterhout, Evans, Feathers, Meszaros y más — cada hallazgo nombra al autor y el principio."
|
||||
data-zh="布鲁克斯、Fowler、Martin、Ousterhout、Evans、Feathers、Meszaros 等——每条结论都点名作者与原则。">Brooks, Fowler, Martin, Ousterhout, Evans, Feathers, Meszaros and more — each finding names the author and principle.</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<span class="ic">🎯</span>
|
||||
<h3 data-en="Six focused skills" data-zh="六个专注技能">Six focused skills</h3>
|
||||
<p data-en="PR Review, Architecture Audit, Tech Debt, Test Quality, Health Dashboard, and a Full Sweep that auto-fixes."
|
||||
<h3 data-en="Six focused skills" data-zhtw="六個專注技能" data-ja="六つの専門スキル" data-ko="여섯 가지 집중 스킬" data-es="Seis skills enfocadas" data-zh="六个专注技能">Six focused skills</h3>
|
||||
<p data-en="PR Review, Architecture Audit, Tech Debt, Test Quality, Health Dashboard, and a Full Sweep that auto-fixes." data-zhtw="PR 審查、架構審計、技術債、測試品質、健康儀表板,以及會自動修復的全面掃描。" data-ja="PR レビュー、アーキテクチャ監査、技術的負債、テスト品質、健全性ダッシュボード、そして自動修復するフルスイープ。" data-ko="PR 리뷰, 아키텍처 감사, 기술 부채, 테스트 품질, 건강 대시보드, 그리고 자동 수정하는 전체 스윕." data-es="Revisión de PR, Auditoría de Arquitectura, Deuda Técnica, Calidad de Pruebas, Panel de Salud y un Barrido Completo que corrige automáticamente."
|
||||
data-zh="PR 审查、架构审计、技术债、测试质量、健康仪表盘,以及会自动修复的全面扫描。">PR Review, Architecture Audit, Tech Debt, Test Quality, Health Dashboard, and a Full Sweep that auto-fixes.</p>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<span class="ic">🔌</span>
|
||||
<h3 data-en="Zero config, any language" data-zh="零配置·任意语言">Zero config, any language</h3>
|
||||
<p data-en="Works in Claude Code, Codex CLI, and Gemini CLI. No plugins to wire up, no language limits."
|
||||
<h3 data-en="Zero config, any language" data-zhtw="零設定,任意語言" data-ja="設定ゼロ、あらゆる言語" data-ko="설정 제로, 모든 언어" data-es="Cero configuración, cualquier lenguaje" data-zh="零配置·任意语言">Zero config, any language</h3>
|
||||
<p data-en="Works in Claude Code, Codex CLI, and Gemini CLI. No plugins to wire up, no language limits." data-zhtw="支援 Claude Code、Codex CLI 與 Gemini CLI。無需接線設定,不限語言。" data-ja="Claude Code、Codex CLI、Gemini CLI で動作します。配線するプラグインも、言語の制限もありません。" data-ko="Claude Code, Codex CLI, Gemini CLI에서 작동합니다. 연결할 플러그인도, 언어 제한도 없습니다." data-es="Funciona en Claude Code, Codex CLI y Gemini CLI. Sin plugins que conectar, sin límites de lenguaje."
|
||||
data-zh="支持 Claude Code、Codex CLI、Gemini CLI。无需接线,不限语言。">Works in Claude Code, Codex CLI, and Gemini CLI. No plugins to wire up, no language limits.</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -312,45 +328,45 @@
|
||||
<section id="risks" class="alt">
|
||||
<div class="wrap">
|
||||
<div class="section-head">
|
||||
<h2 data-en="The Six Production Decay Risks" data-zh="六大生产代码腐化风险">The Six Production Decay Risks</h2>
|
||||
<p data-en="Synthesized from the twelve books. Six more (T1–T6) cover test-suite decay."
|
||||
<h2 data-en="The Six Production Decay Risks" data-zhtw="六類生產程式碼衰退風險" data-ja="六つの本番コード劣化リスク" data-ko="여섯 가지 프로덕션 코드 쇠퇴 위험" data-es="Los seis riesgos de deterioro del código de producción" data-zh="六大生产代码腐化风险">The Six Production Decay Risks</h2>
|
||||
<p data-en="Synthesized from the twelve books. Six more (T1–T6) cover test-suite decay." data-zhtw="提煉自十二本經典。另有六項(T1–T6)涵蓋測試套件的衰退。" data-ja="十二冊の書籍から統合。さらに六つ(T1–T6)がテストスイートの劣化を扱います。" data-ko="열두 권의 책에서 종합. 추가 여섯 가지(T1–T6)가 테스트 스위트 쇠퇴를 다룹니다." data-es="Sintetizados a partir de los doce libros. Otros seis (T1–T6) cubren el deterioro de la suite de pruebas."
|
||||
data-zh="提炼自十二本经典。另有六项(T1–T6)覆盖测试套件的腐化。">Synthesized from the twelve books. Six more (T1–T6) cover test-suite decay.</p>
|
||||
</div>
|
||||
<div class="grid-3">
|
||||
<div class="risk">
|
||||
<span class="code">R1</span>
|
||||
<h3>🧠 <span data-en="Cognitive Overload" data-zh="认知过载">Cognitive Overload</span></h3>
|
||||
<p data-en="How much mental effort does it take to understand this?" data-zh="理解这段代码要耗费多少脑力?">How much mental effort does it take to understand this?</p>
|
||||
<h3>🧠 <span data-en="Cognitive Overload" data-zhtw="認知過載" data-ja="認知的過負荷" data-ko="인지 과부하" data-es="Sobrecarga cognitiva" data-zh="认知过载">Cognitive Overload</span></h3>
|
||||
<p data-en="How much mental effort does it take to understand this?" data-zhtw="理解這段程式碼要耗費多少腦力?" data-ja="これを理解するのにどれだけの精神的労力がかかるか?" data-ko="이것을 이해하는 데 얼마나 많은 정신적 노력이 드는가?" data-es="¿Cuánto esfuerzo mental cuesta entender esto?" data-zh="理解这段代码要耗费多少脑力?">How much mental effort does it take to understand this?</p>
|
||||
<div class="src">Code Complete · Refactoring · DDD · Philosophy of SD</div>
|
||||
</div>
|
||||
<div class="risk">
|
||||
<span class="code">R2</span>
|
||||
<h3>🔗 <span data-en="Change Propagation" data-zh="变更扩散">Change Propagation</span></h3>
|
||||
<p data-en="How many unrelated things break on one change?" data-zh="改一处会牵连多少无关之物崩坏?">How many unrelated things break on one change?</p>
|
||||
<h3>🔗 <span data-en="Change Propagation" data-zhtw="變更擴散" data-ja="変更の波及" data-ko="변경 전파" data-es="Propagación de cambios" data-zh="变更扩散">Change Propagation</span></h3>
|
||||
<p data-en="How many unrelated things break on one change?" data-zhtw="改一處會牽連多少無關之物崩壞?" data-ja="一つの変更でいくつの無関係なものが壊れるか?" data-ko="한 번의 변경으로 무관한 것이 얼마나 깨지는가?" data-es="¿Cuántas cosas no relacionadas se rompen con un solo cambio?" data-zh="改一处会牵连多少无关之物崩坏?">How many unrelated things break on one change?</p>
|
||||
<div class="src">Refactoring · Clean Architecture · Pragmatic · SE@Google</div>
|
||||
</div>
|
||||
<div class="risk">
|
||||
<span class="code">R3</span>
|
||||
<h3>📋 <span data-en="Knowledge Duplication" data-zh="知识重复">Knowledge Duplication</span></h3>
|
||||
<p data-en="Is the same decision expressed in multiple places?" data-zh="同一个决策是否散落在多处表达?">Is the same decision expressed in multiple places?</p>
|
||||
<h3>📋 <span data-en="Knowledge Duplication" data-zhtw="知識重複" data-ja="知識の重複" data-ko="지식 중복" data-es="Duplicación de conocimiento" data-zh="知识重复">Knowledge Duplication</span></h3>
|
||||
<p data-en="Is the same decision expressed in multiple places?" data-zhtw="同一個決策是否散落在多處表達?" data-ja="同じ決定が複数の場所で表現されていないか?" data-ko="같은 결정이 여러 곳에서 표현되고 있는가?" data-es="¿La misma decisión se expresa en varios lugares?" data-zh="同一个决策是否散落在多处表达?">Is the same decision expressed in multiple places?</p>
|
||||
<div class="src">Pragmatic · Refactoring · DDD</div>
|
||||
</div>
|
||||
<div class="risk">
|
||||
<span class="code">R4</span>
|
||||
<h3>🌀 <span data-en="Accidental Complexity" data-zh="偶发复杂度">Accidental Complexity</span></h3>
|
||||
<p data-en="Is the code more complex than the problem itself?" data-zh="代码是否比问题本身更复杂?">Is the code more complex than the problem itself?</p>
|
||||
<h3>🌀 <span data-en="Accidental Complexity" data-zhtw="偶發複雜度" data-ja="偶有的複雑性" data-ko="우발적 복잡성" data-es="Complejidad accidental" data-zh="偶发复杂度">Accidental Complexity</span></h3>
|
||||
<p data-en="Is the code more complex than the problem itself?" data-zhtw="程式碼是否比問題本身更複雜?" data-ja="コードは問題そのものより複雑になっていないか?" data-ko="코드가 문제 자체보다 더 복잡한가?" data-es="¿El código es más complejo que el propio problema?" data-zh="代码是否比问题本身更复杂?">Is the code more complex than the problem itself?</p>
|
||||
<div class="src">Refactoring · Code Complete · Brooks · Philosophy of SD</div>
|
||||
</div>
|
||||
<div class="risk">
|
||||
<span class="code">R5</span>
|
||||
<h3>🏗️ <span data-en="Dependency Disorder" data-zh="依赖失序">Dependency Disorder</span></h3>
|
||||
<p data-en="Do dependencies flow in a consistent direction?" data-zh="依赖是否朝一致的方向流动?">Do dependencies flow in a consistent direction?</p>
|
||||
<h3>🏗️ <span data-en="Dependency Disorder" data-zhtw="依賴失序" data-ja="依存関係の無秩序" data-ko="의존성 무질서" data-es="Desorden de dependencias" data-zh="依赖失序">Dependency Disorder</span></h3>
|
||||
<p data-en="Do dependencies flow in a consistent direction?" data-zhtw="依賴是否朝一致的方向流動?" data-ja="依存関係は一貫した方向に流れているか?" data-ko="의존성이 일관된 방향으로 흐르는가?" data-es="¿Las dependencias fluyen en una dirección consistente?" data-zh="依赖是否朝一致的方向流动?">Do dependencies flow in a consistent direction?</p>
|
||||
<div class="src">Clean Architecture · Brooks · Pragmatic · SE@Google</div>
|
||||
</div>
|
||||
<div class="risk">
|
||||
<span class="code">R6</span>
|
||||
<h3>🗺️ <span data-en="Domain Model Distortion" data-zh="领域模型失真">Domain Model Distortion</span></h3>
|
||||
<p data-en="Does the code faithfully represent the domain?" data-zh="代码是否忠实地表达了业务领域?">Does the code faithfully represent the domain?</p>
|
||||
<h3>🗺️ <span data-en="Domain Model Distortion" data-zhtw="領域模型失真" data-ja="ドメインモデルの歪み" data-ko="도메인 모델 왜곡" data-es="Distorsión del modelo de dominio" data-zh="领域模型失真">Domain Model Distortion</span></h3>
|
||||
<p data-en="Does the code faithfully represent the domain?" data-zhtw="程式碼是否忠實地表達了業務領域?" data-ja="コードはドメインを忠実に表現しているか?" data-ko="코드가 도메인을 충실하게 표현하는가?" data-es="¿El código representa fielmente el dominio?" data-zh="代码是否忠实地表达了业务领域?">Does the code faithfully represent the domain?</p>
|
||||
<div class="src">DDD · Refactoring</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -361,47 +377,47 @@
|
||||
<section id="sample">
|
||||
<div class="wrap">
|
||||
<div class="section-head">
|
||||
<h2 data-en="What a finding looks like" data-zh="一条结论长什么样">What a finding looks like</h2>
|
||||
<p data-en="Same messy method, two of the eight findings brooks-lint produces — each one cited and actionable."
|
||||
<h2 data-en="What a finding looks like" data-zhtw="一條結論長什麼樣" data-ja="指摘はこんな形" data-ko="진단은 이런 모습입니다" data-es="Cómo se ve un hallazgo" data-zh="一条结论长什么样">What a finding looks like</h2>
|
||||
<p data-en="Same messy method, two of the eight findings brooks-lint produces — each one cited and actionable." data-zhtw="同一段混亂方法,brooks-lint 給出的八條結論中的兩條——每條都有出處、可執行。" data-ja="同じ雑然としたメソッドから、brooks-lint が生成する八つの指摘のうちの二つ——いずれも出典付きで実行可能です。" data-ko="같은 지저분한 메서드에서 brooks-lint가 산출하는 여덟 진단 중 둘 — 각각 출처가 있고 실행 가능합니다." data-es="El mismo método desordenado, dos de los ocho hallazgos que produce brooks-lint — cada uno citado y accionable."
|
||||
data-zh="同一段混乱方法,brooks-lint 给出的八条结论中的两条——每条都有出处、可执行。">Same messy method, two of the eight findings brooks-lint produces — each one cited and actionable.</p>
|
||||
</div>
|
||||
<div class="sample">
|
||||
<div class="score" data-en="Health Score: <b>28/100</b>" data-zh="健康分:<b>28/100</b>">Health Score: <b>28/100</b></div>
|
||||
<div class="score" data-en="Health Score: <b>28/100</b>" data-zhtw="健康分:<b>28/100</b>" data-ja="健全性スコア:<b>28/100</b>" data-ko="건강 점수: <b>28/100</b>" data-es="Puntuación de salud: <b>28/100</b>" data-zh="健康分:<b>28/100</b>">Health Score: <b>28/100</b></div>
|
||||
<div class="summary"
|
||||
data-en="This method concentrates four unrelated business responsibilities, hides a logic bug that silently suppresses email notifications, and is wide open to SQL injection."
|
||||
data-en="This method concentrates four unrelated business responsibilities, hides a logic bug that silently suppresses email notifications, and is wide open to SQL injection." data-zhtw="這個方法把四項互不相關的業務職責堆在一起,藏著一個讓郵件通知靜默失效的邏輯 bug,並且對 SQL 注入門戶大開。" data-ja="このメソッドは無関係な四つのビジネス責務を一つに集約し、メール通知を静かに抑制するロジックバグを隠し、SQL インジェクションに対して無防備です。" data-ko="이 메서드는 서로 무관한 네 가지 비즈니스 책임을 한데 모으고, 이메일 알림을 조용히 억제하는 로직 버그를 숨기며, SQL 인젝션에 무방비로 노출되어 있습니다." data-es="Este método concentra cuatro responsabilidades de negocio no relacionadas, oculta un error lógico que suprime silenciosamente las notificaciones por correo y queda totalmente expuesto a inyección SQL."
|
||||
data-zh="这个方法把四项互不相关的业务职责堆在一起,藏着一个让邮件通知静默失效的逻辑 bug,并且对 SQL 注入门户大开。">
|
||||
This method concentrates four unrelated business responsibilities, hides a logic bug that silently suppresses email notifications, and is wide open to SQL injection.
|
||||
</div>
|
||||
|
||||
<div class="finding">
|
||||
<h4><span class="dot">🔴</span> R2 — <span data-en="One method changes for four unrelated reasons" data-zh="一个方法因四个无关原因而改动">One method changes for four unrelated reasons</span></h4>
|
||||
<div class="row"><b data-en="Symptom:" data-zh="症状:">Symptom:</b>
|
||||
<span data-en="update_profile does field updates, email notifications, loyalty recalculation, and cache invalidation in one body."
|
||||
<h4><span class="dot">🔴</span> R2 — <span data-en="One method changes for four unrelated reasons" data-zhtw="一個方法因四個無關原因而改動" data-ja="一つのメソッドが四つの無関係な理由で変更される" data-ko="한 메서드가 네 가지 무관한 이유로 변경됨" data-es="Un método cambia por cuatro razones no relacionadas" data-zh="一个方法因四个无关原因而改动">One method changes for four unrelated reasons</span></h4>
|
||||
<div class="row"><b data-en="Symptom:" data-zhtw="症狀:" data-ja="症状:" data-ko="증상:" data-es="Síntoma:" data-zh="症状:">Symptom:</b>
|
||||
<span data-en="update_profile does field updates, email notifications, loyalty recalculation, and cache invalidation in one body." data-zhtw="update_profile 在一個方法主體裡同時做欄位更新、郵件通知、點數重算和快取失效。" data-ja="update_profile は、フィールド更新、メール通知、ロイヤルティの再計算、キャッシュ無効化を一つの本体で行っています。" data-ko="update_profile이 필드 업데이트, 이메일 알림, 적립 포인트 재계산, 캐시 무효화를 하나의 본문에서 수행합니다." data-es="update_profile hace actualizaciones de campos, notificaciones por correo, recálculo de fidelidad e invalidación de caché en un solo cuerpo."
|
||||
data-zh="update_profile 在一个方法体里同时做字段更新、邮件通知、积分重算和缓存失效。">update_profile does field updates, email notifications, loyalty recalculation, and cache invalidation in one body.</span></div>
|
||||
<div class="row src"><b data-en="Source:" data-zh="根源:">Source:</b>
|
||||
<span data-en="Fowler — Refactoring — Divergent Change; Hunt & Thomas — Orthogonality"
|
||||
<div class="row src"><b data-en="Source:" data-zhtw="根源:" data-ja="根源:" data-ko="근원:" data-es="Origen:" data-zh="根源:">Source:</b>
|
||||
<span data-en="Fowler — Refactoring — Divergent Change; Hunt & Thomas — Orthogonality" data-zhtw="Fowler — Refactoring — 發散式變更(Divergent Change);Hunt & Thomas — 正交性(Orthogonality)" data-ja="Fowler — Refactoring — 発散的変更(Divergent Change);Hunt & Thomas — 直交性(Orthogonality)" data-ko="Fowler — Refactoring — 발산적 변경(Divergent Change); Hunt & Thomas — 직교성(Orthogonality)" data-es="Fowler — Refactoring — Divergent Change (Cambio divergente); Hunt & Thomas — Orthogonality (Ortogonalidad)"
|
||||
data-zh="Fowler《重构》——发散式变化;Hunt & Thomas——正交性">Fowler — Refactoring — Divergent Change; Hunt & Thomas — Orthogonality</span></div>
|
||||
<div class="row"><b data-en="Consequence:" data-zh="后果:">Consequence:</b>
|
||||
<span data-en="A change to the loyalty formula risks breaking email notifications. Every edit carries regression risk across four domains."
|
||||
<div class="row"><b data-en="Consequence:" data-zhtw="後果:" data-ja="結果:" data-ko="결과:" data-es="Consecuencia:" data-zh="后果:">Consequence:</b>
|
||||
<span data-en="A change to the loyalty formula risks breaking email notifications. Every edit carries regression risk across four domains." data-zhtw="改一下點數公式就可能弄壞郵件通知。每次編輯都在四個領域同時承擔回歸風險。" data-ja="ロイヤルティの計算式を変更すると、メール通知を壊すおそれがあります。すべての編集が四つのドメインにまたがる回帰リスクを背負います。" data-ko="적립 포인트 공식을 변경하면 이메일 알림이 깨질 위험이 있습니다. 모든 수정이 네 도메인에 걸친 회귀 위험을 떠안습니다." data-es="Un cambio en la fórmula de fidelidad arriesga romper las notificaciones por correo. Cada edición conlleva riesgo de regresión en cuatro dominios."
|
||||
data-zh="改一下积分公式就可能弄坏邮件通知。每次编辑都在四个领域同时承担回归风险。">A change to the loyalty formula risks breaking email notifications. Every edit carries regression risk across four domains.</span></div>
|
||||
<div class="row fix"><b data-en="Remedy:" data-zh="修复:">Remedy:</b>
|
||||
<span data-en="Extract NotificationService, LoyaltyService, UserCacheInvalidator. update_profile should orchestrate, not implement."
|
||||
<div class="row fix"><b data-en="Remedy:" data-zhtw="對策:" data-ja="対策:" data-ko="처방:" data-es="Remedio:" data-zh="修复:">Remedy:</b>
|
||||
<span data-en="Extract NotificationService, LoyaltyService, UserCacheInvalidator. update_profile should orchestrate, not implement." data-zhtw="抽出 NotificationService、LoyaltyService、UserCacheInvalidator。update_profile 應當編排而非實作。" data-ja="NotificationService、LoyaltyService、UserCacheInvalidator を抽出します。update_profile は実装ではなくオーケストレーションすべきです。" data-ko="NotificationService, LoyaltyService, UserCacheInvalidator를 추출하세요. update_profile은 구현이 아니라 조율해야 합니다." data-es="Extrae NotificationService, LoyaltyService, UserCacheInvalidator. update_profile debería orquestar, no implementar."
|
||||
data-zh="抽出 NotificationService、LoyaltyService、UserCacheInvalidator。update_profile 应当编排而非实现。">Extract NotificationService, LoyaltyService, UserCacheInvalidator. update_profile should orchestrate, not implement.</span></div>
|
||||
</div>
|
||||
|
||||
<div class="finding">
|
||||
<h4><span class="dot">🔴</span> R6 — <span data-en="Silent logic bug: notification never fires" data-zh="静默逻辑 bug:通知永不触发">Silent logic bug: notification never fires</span></h4>
|
||||
<div class="row"><b data-en="Symptom:" data-zh="症状:">Symptom:</b>
|
||||
<span data-en="user['email'] = email runs before if user['email'] != email — the condition is always False, the code is dead."
|
||||
<h4><span class="dot">🔴</span> R6 — <span data-en="Silent logic bug: notification never fires" data-zhtw="靜默邏輯 bug:通知永不觸發" data-ja="静かなロジックバグ:通知が決して発火しない" data-ko="조용한 로직 버그: 알림이 결코 발생하지 않음" data-es="Error lógico silencioso: la notificación nunca se dispara" data-zh="静默逻辑 bug:通知永不触发">Silent logic bug: notification never fires</span></h4>
|
||||
<div class="row"><b data-en="Symptom:" data-zhtw="症狀:" data-ja="症状:" data-ko="증상:" data-es="Síntoma:" data-zh="症状:">Symptom:</b>
|
||||
<span data-en="user['email'] = email runs before if user['email'] != email — the condition is always False, the code is dead." data-zhtw="user['email'] = email 在 if user['email'] != email 之前執行——條件永遠為 False,該分支是死程式碼。" data-ja="user['email'] = email が if user['email'] != email より前に実行されるため——条件は常に False で、このコードはデッドコードです。" data-ko="user['email'] = email이 if user['email'] != email보다 먼저 실행되어 — 조건이 항상 False이며, 그 코드는 죽은 코드입니다." data-es="user['email'] = email se ejecuta antes de if user['email'] != email — la condición siempre es False, el código está muerto."
|
||||
data-zh="user['email'] = email 在 if user['email'] != email 之前执行——条件永远为 False,该分支是死代码。">user['email'] = email runs before if user['email'] != email — the condition is always False, the code is dead.</span></div>
|
||||
<div class="row src"><b data-en="Source:" data-zh="根源:">Source:</b>
|
||||
<span data-en="McConnell — Code Complete — Ch. 17: Unusual Control Structures"
|
||||
<div class="row src"><b data-en="Source:" data-zhtw="根源:" data-ja="根源:" data-ko="근원:" data-es="Origen:" data-zh="根源:">Source:</b>
|
||||
<span data-en="McConnell — Code Complete — Ch. 17: Unusual Control Structures" data-zhtw="McConnell — Code Complete — 第 17 章:非常規控制結構" data-ja="McConnell — Code Complete — 第 17 章:特殊な制御構造" data-ko="McConnell — Code Complete — 17장: 비정상적인 제어 구조" data-es="McConnell — Code Complete — Cap. 17: Estructuras de control inusuales"
|
||||
data-zh="McConnell《代码大全》——第 17 章:非常规控制结构">McConnell — Code Complete — Ch. 17: Unusual Control Structures</span></div>
|
||||
<div class="row"><b data-en="Consequence:" data-zh="后果:">Consequence:</b>
|
||||
<span data-en="Users are never notified when their email changes. A business rule is silently violated while the system looks fine."
|
||||
<div class="row"><b data-en="Consequence:" data-zhtw="後果:" data-ja="結果:" data-ko="결과:" data-es="Consecuencia:" data-zh="后果:">Consequence:</b>
|
||||
<span data-en="Users are never notified when their email changes. A business rule is silently violated while the system looks fine." data-zhtw="使用者改信箱時永遠收不到通知。系統看似正常,業務規則卻被悄悄違反。" data-ja="ユーザーはメールアドレスを変更しても決して通知されません。システムは正常に見えながら、ビジネスルールが静かに破られています。" data-ko="사용자가 이메일을 변경해도 결코 알림을 받지 못합니다. 시스템은 정상으로 보이지만 비즈니스 규칙이 조용히 위반됩니다." data-es="Los usuarios nunca son notificados cuando cambia su correo. Una regla de negocio se viola silenciosamente mientras el sistema parece estar bien."
|
||||
data-zh="用户改邮箱时永远收不到通知。系统看似正常,业务规则却被悄悄违反。">Users are never notified when their email changes. A business rule is silently violated while the system looks fine.</span></div>
|
||||
<div class="row fix"><b data-en="Remedy:" data-zh="修复:">Remedy:</b>
|
||||
<span data-en="Capture old_email before any mutation. Compare against old_email, not the already-overwritten value."
|
||||
<div class="row fix"><b data-en="Remedy:" data-zhtw="對策:" data-ja="対策:" data-ko="처방:" data-es="Remedio:" data-zh="修复:">Remedy:</b>
|
||||
<span data-en="Capture old_email before any mutation. Compare against old_email, not the already-overwritten value." data-zhtw="在任何修改前先捕獲 old_email,與 old_email 比較,而非已被覆寫的值。" data-ja="変更の前に old_email を捕捉します。すでに上書きされた値ではなく old_email と比較してください。" data-ko="어떤 변경보다 먼저 old_email을 포착하세요. 이미 덮어쓴 값이 아니라 old_email과 비교하세요." data-es="Captura old_email antes de cualquier mutación. Compara contra old_email, no contra el valor ya sobrescrito."
|
||||
data-zh="在任何修改前先捕获 old_email,与 old_email 比较,而非已被覆盖的值。">Capture old_email before any mutation. Compare against old_email, not the already-overwritten value.</span></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -412,25 +428,25 @@
|
||||
<section id="benchmark" class="alt">
|
||||
<div class="wrap">
|
||||
<div class="section-head">
|
||||
<h2 data-en="Consistency is the point" data-zh="一致性才是重点">Consistency is the point</h2>
|
||||
<p data-en="Tested across PR review, architecture audit, and tech debt scenarios. The gap isn't what Claude can find — it's what it finds every single time, with evidence."
|
||||
<h2 data-en="Consistency is the point" data-zhtw="一致性才是重點" data-ja="一貫性こそが肝心" data-ko="일관성이 핵심입니다" data-es="La consistencia es lo que importa" data-zh="一致性才是重点">Consistency is the point</h2>
|
||||
<p data-en="Tested across PR review, architecture audit, and tech debt scenarios. The gap isn't what Claude can find — it's what it finds every single time, with evidence." data-zhtw="在 PR 審查、架構審計、技術債三類場景上測試。差距不在於 Claude 能找到什麼,而在於它能否每一次都帶著證據找到。" data-ja="PR レビュー、アーキテクチャ監査、技術的負債のシナリオで検証しました。差は Claude が何を見つけられるかではなく——毎回、証拠とともに何を見つけるかにあります。" data-ko="PR 리뷰, 아키텍처 감사, 기술 부채 시나리오에서 테스트했습니다. 격차는 Claude가 무엇을 찾을 수 있느냐가 아니라 — 매번 증거와 함께 무엇을 찾아내느냐에 있습니다." data-es="Probado en escenarios de revisión de PR, auditoría de arquitectura y deuda técnica. La diferencia no es lo que Claude puede encontrar, sino lo que encuentra cada vez, con evidencia."
|
||||
data-zh="在 PR 审查、架构审计、技术债三类场景上测试。差距不在于 Claude 能找到什么,而在于它能否每一次都带着证据找到。">Tested across PR review, architecture audit, and tech debt scenarios. The gap isn't what Claude can find — it's what it finds every single time, with evidence.</p>
|
||||
</div>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-en="Criterion" data-zh="评判标准">Criterion</th>
|
||||
<th data-en="Criterion" data-zhtw="評判標準" data-ja="評価基準" data-ko="평가 기준" data-es="Criterio" data-zh="评判标准">Criterion</th>
|
||||
<th class="c">brooks-lint</th>
|
||||
<th class="c" data-en="Claude alone" data-zh="纯 Claude">Claude alone</th>
|
||||
<th class="c" data-en="Claude alone" data-zhtw="純 Claude" data-ja="Claude 単体" data-ko="Claude 단독" data-es="Claude solo" data-zh="纯 Claude">Claude alone</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td data-en="Structured Symptom→Source→Consequence→Remedy" data-zh="结构化 症状→根源→后果→修复">Structured Symptom→Source→Consequence→Remedy</td><td class="c yes">100%</td><td class="c no">0%</td></tr>
|
||||
<tr><td data-en="Book citation per finding" data-zh="每条结论附书目出处">Book citation per finding</td><td class="c yes">100%</td><td class="c no">0%</td></tr>
|
||||
<tr><td data-en="Consistent severity labels 🔴🟡🟢" data-zh="一致的严重度标签 🔴🟡🟢">Consistent severity labels 🔴🟡🟢</td><td class="c yes">100%</td><td class="c no">0%</td></tr>
|
||||
<tr><td data-en="Health Score (0–100)" data-zh="健康分(0–100)">Health Score (0–100)</td><td class="c yes">100%</td><td class="c no">0%</td></tr>
|
||||
<tr><td data-en="Overall pass rate" data-zh="总体通过率">Overall pass rate</td><td class="c">94%</td><td class="c">16%</td></tr>
|
||||
<tr><td data-en="Structured Symptom→Source→Consequence→Remedy" data-zhtw="結構化 症狀→根源→後果→對策" data-ja="構造化された 症状→根源→結果→対策" data-ko="구조화된 증상→근원→결과→처방" data-es="Síntoma→Origen→Consecuencia→Remedio estructurado" data-zh="结构化 症状→根源→后果→修复">Structured Symptom→Source→Consequence→Remedy</td><td class="c yes">100%</td><td class="c no">0%</td></tr>
|
||||
<tr><td data-en="Book citation per finding" data-zhtw="每條結論附書目出處" data-ja="指摘ごとの書籍出典" data-ko="진단마다 도서 출처" data-es="Cita de libro por hallazgo" data-zh="每条结论附书目出处">Book citation per finding</td><td class="c yes">100%</td><td class="c no">0%</td></tr>
|
||||
<tr><td data-en="Consistent severity labels 🔴🟡🟢" data-zhtw="一致的嚴重度標籤 🔴🟡🟢" data-ja="一貫した重大度ラベル 🔴🟡🟢" data-ko="일관된 심각도 라벨 🔴🟡🟢" data-es="Etiquetas de severidad consistentes 🔴🟡🟢" data-zh="一致的严重度标签 🔴🟡🟢">Consistent severity labels 🔴🟡🟢</td><td class="c yes">100%</td><td class="c no">0%</td></tr>
|
||||
<tr><td data-en="Health Score (0–100)" data-zhtw="健康分(0–100)" data-ja="健全性スコア(0〜100)" data-ko="건강 점수(0–100)" data-es="Puntuación de salud (0–100)" data-zh="健康分(0–100)">Health Score (0–100)</td><td class="c yes">100%</td><td class="c no">0%</td></tr>
|
||||
<tr><td data-en="Overall pass rate" data-zhtw="總體通過率" data-ja="総合合格率" data-ko="전체 통과율" data-es="Tasa de aprobación global" data-zh="总体通过率">Overall pass rate</td><td class="c">94%</td><td class="c">16%</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -441,8 +457,8 @@
|
||||
<section>
|
||||
<div class="wrap">
|
||||
<div class="section-head">
|
||||
<h2 data-en="Standing on twelve giants" data-zh="站在十二位巨人的肩上">Standing on twelve giants</h2>
|
||||
<p data-en="The decay risks are our synthesis of their ideas, applied to modern code quality."
|
||||
<h2 data-en="Standing on twelve giants" data-zhtw="站在十二位巨人的肩上" data-ja="十二人の巨人の肩の上に" data-ko="열두 거인의 어깨 위에서" data-es="Sobre los hombros de doce gigantes" data-zh="站在十二位巨人的肩上">Standing on twelve giants</h2>
|
||||
<p data-en="The decay risks are our synthesis of their ideas, applied to modern code quality." data-zhtw="這些衰退風險是我們對他們思想的綜合,應用於現代程式碼品質評估。" data-ja="これらの劣化リスクは、彼らの思想を統合し、現代のコード品質に適用したものです。" data-ko="이 쇠퇴 위험들은 그들의 사상을 종합하여 현대 코드 품질에 적용한 것입니다." data-es="Los riesgos de deterioro son nuestra síntesis de sus ideas, aplicada a la calidad del código moderno."
|
||||
data-zh="这些腐化风险是我们对他们思想的综合,应用于现代代码质量评估。">The decay risks are our synthesis of their ideas, applied to modern code quality.</p>
|
||||
</div>
|
||||
<div class="books">
|
||||
@@ -466,8 +482,8 @@
|
||||
<section id="install" class="alt">
|
||||
<div class="wrap">
|
||||
<div class="section-head">
|
||||
<h2 data-en="Get started in seconds" data-zh="几秒钟即可上手">Get started in seconds</h2>
|
||||
<p data-en="Pick your tool. Then just ask it to review your code, audit your architecture, or assess tech debt."
|
||||
<h2 data-en="Get started in seconds" data-zhtw="幾秒鐘即可上手" data-ja="数秒で始められます" data-ko="몇 초 만에 시작하세요" data-es="Empieza en segundos" data-zh="几秒钟即可上手">Get started in seconds</h2>
|
||||
<p data-en="Pick your tool. Then just ask it to review your code, audit your architecture, or assess tech debt." data-zhtw="選擇你的工具,然後讓它審查程式碼、審計架構或評估技術債即可。" data-ja="ツールを選び、あとはコードのレビュー、アーキテクチャの監査、技術的負債の評価を頼むだけです。" data-ko="도구를 고르세요. 그런 다음 코드 리뷰, 아키텍처 감사, 기술 부채 평가를 요청하기만 하면 됩니다." data-es="Elige tu herramienta. Luego solo pídele que revise tu código, audite tu arquitectura o evalúe la deuda técnica."
|
||||
data-zh="选择你的工具,然后让它审查代码、审计架构或评估技术债即可。">Pick your tool. Then just ask it to review your code, audit your architecture, or assess tech debt.</p>
|
||||
</div>
|
||||
<div class="install-grid">
|
||||
@@ -487,12 +503,12 @@
|
||||
<button class="copy-btn" data-copy="Install the brooks-lint skill from hyhmrright/brooks-lint">Copy</button><span class="cmt"># just say this in a Codex session</span>
|
||||
Install the brooks-lint skill from hyhmrright/brooks-lint</div>
|
||||
|
||||
<div class="install-label" data-en="Then, in any session:" data-zh="然后,在任意会话里:">Then, in any session:</div>
|
||||
<div class="install-label" data-en="Then, in any session:" data-zhtw="然後,在任意工作階段裡:" data-ja="そして、任意のセッションで:" data-ko="그런 다음, 어떤 세션에서든:" data-es="Luego, en cualquier sesión:" data-zh="然后,在任意会话里:">Then, in any session:</div>
|
||||
<div class="code-block">
|
||||
<button class="copy-btn" data-copy="/brooks-review">Copy</button><span class="tok">/brooks-review</span> <span class="cmt"># or /brooks-audit · /brooks-debt · /brooks-test · /brooks-health · /brooks-sweep</span></div>
|
||||
</div>
|
||||
<div style="text-align:center; margin-top:34px;">
|
||||
<a class="btn btn-primary" href="https://github.com/hyhmrright/brooks-lint" target="_blank" rel="noopener" data-en="Star on GitHub ⭐" data-zh="去 GitHub 点个 Star ⭐">Star on GitHub ⭐</a>
|
||||
<a class="btn btn-primary" href="https://github.com/hyhmrright/brooks-lint" target="_blank" rel="noopener" data-en="Star on GitHub ⭐" data-zhtw="去 GitHub 點個 Star ⭐" data-ja="GitHub でスターを ⭐" data-ko="GitHub에서 Star 누르기 ⭐" data-es="Dale una estrella en GitHub ⭐" data-zh="去 GitHub 点个 Star ⭐">Star on GitHub ⭐</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -503,24 +519,29 @@ Install the brooks-lint skill from hyhmrright/brooks-lint</div>
|
||||
<div class="wrap foot-in">
|
||||
<div class="foot-links">
|
||||
<a href="https://github.com/hyhmrright/brooks-lint" target="_blank" rel="noopener">GitHub</a>
|
||||
<a href="gallery.html" data-en="Gallery" data-zh="示例画廊">Gallery</a>
|
||||
<a href="guide.html" data-en="Decay Risk Guide" data-zh="腐化风险指南">Decay Risk Guide</a>
|
||||
<a href="https://github.com/hyhmrright/brooks-lint/blob/main/CONTRIBUTING.md" target="_blank" rel="noopener" data-en="Contributing" data-zh="参与贡献">Contributing</a>
|
||||
<a href="https://github.com/hyhmrright/brooks-lint/blob/main/CHANGELOG.md" target="_blank" rel="noopener" data-en="Changelog" data-zh="更新日志">Changelog</a>
|
||||
<a href="gallery.html" data-en="Gallery" data-zhtw="範例展示" data-ja="ギャラリー" data-ko="갤러리" data-es="Galería" data-zh="示例画廊">Gallery</a>
|
||||
<a href="guide.html" data-en="Decay Risk Guide" data-zhtw="衰退風險指南" data-ja="劣化リスクガイド" data-ko="쇠퇴 위험 가이드" data-es="Guía de riesgos de deterioro" data-zh="腐化风险指南">Decay Risk Guide</a>
|
||||
<a href="https://github.com/hyhmrright/brooks-lint/blob/main/CONTRIBUTING.md" target="_blank" rel="noopener" data-en="Contributing" data-zhtw="參與貢獻" data-ja="コントリビュート" data-ko="기여하기" data-es="Contribuir" data-zh="参与贡献">Contributing</a>
|
||||
<a href="https://github.com/hyhmrright/brooks-lint/blob/main/CHANGELOG.md" target="_blank" rel="noopener" data-en="Changelog" data-zhtw="更新日誌" data-ja="変更履歴" data-ko="변경 로그" data-es="Registro de cambios" data-zh="更新日志">Changelog</a>
|
||||
</div>
|
||||
<div class="foot-note" data-en="MIT License — Built by hyhmrright" data-zh="MIT 许可证 — 由 hyhmrright 构建">MIT License — Built by hyhmrright</div>
|
||||
<div class="foot-note" data-en="MIT License — Built by hyhmrright" data-zhtw="MIT 授權 — 由 hyhmrright 打造" data-ja="MIT ライセンス — hyhmrright が構築" data-ko="MIT 라이선스 — hyhmrright 제작" data-es="Licencia MIT — Creado por hyhmrright" data-zh="MIT 许可证 — 由 hyhmrright 构建">MIT License — Built by hyhmrright</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
var STORE = "brooks-lint-lang";
|
||||
var HTML_LANG = { en: "en", zh: "zh-CN", zhtw: "zh-TW", ja: "ja", ko: "ko", es: "es" };
|
||||
function apply(lang) {
|
||||
document.documentElement.lang = (lang === "zh") ? "zh-CN" : "en";
|
||||
document.documentElement.lang = HTML_LANG[lang] || "en";
|
||||
document.querySelectorAll("[data-en]").forEach(function (el) {
|
||||
var v = el.getAttribute("data-" + lang);
|
||||
if (v != null) el.innerHTML = v;
|
||||
});
|
||||
document.querySelectorAll("[data-src-en]").forEach(function (el) {
|
||||
var s = el.getAttribute("data-src-" + lang);
|
||||
if (s != null) el.setAttribute("src", s);
|
||||
});
|
||||
document.querySelectorAll(".lang-toggle button").forEach(function (b) {
|
||||
b.classList.toggle("active", b.getAttribute("data-set-lang") === lang);
|
||||
});
|
||||
@@ -531,7 +552,16 @@ Install the brooks-lint skill from hyhmrright/brooks-lint</div>
|
||||
});
|
||||
var saved;
|
||||
try { saved = localStorage.getItem(STORE); } catch (e) {}
|
||||
if (!saved) saved = (navigator.language || "").toLowerCase().indexOf("zh") === 0 ? "zh" : "en";
|
||||
if (!saved) {
|
||||
var nav = (navigator.language || "").toLowerCase();
|
||||
saved = (nav.indexOf("zh-tw") === 0 || nav.indexOf("zh-hant") === 0
|
||||
|| nav.indexOf("zh-hk") === 0 || nav.indexOf("zh-mo") === 0) ? "zhtw"
|
||||
: nav.indexOf("zh") === 0 ? "zh"
|
||||
: nav.indexOf("ja") === 0 ? "ja"
|
||||
: nav.indexOf("ko") === 0 ? "ko"
|
||||
: nav.indexOf("es") === 0 ? "es"
|
||||
: "en";
|
||||
}
|
||||
apply(saved);
|
||||
|
||||
// copy buttons
|
||||
|
||||
Reference in New Issue
Block a user