📦 deps(thirdparty): update snapshots
@@ -0,0 +1,690 @@
|
||||
<p align="center">
|
||||
<img src="assets/logo.svg" alt="brooks-lint" width="200">
|
||||
</p>
|
||||
|
||||
<h1 align="center">brooks-lint</h1>
|
||||
|
||||
<p align="center">
|
||||
<strong>Revisiones de código con IA fundamentadas en doce libros clásicos de ingeniería.<br>
|
||||
Consistentes. Trazables. Accionables.</strong>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="README.md">English</a> ·
|
||||
<a href="README.zh-CN.md">简体中文</a> ·
|
||||
<a href="README.zh-TW.md">繁體中文</a> ·
|
||||
<a href="README.ja.md">日本語</a> ·
|
||||
<a href="README.ko.md">한국어</a> ·
|
||||
<strong>Español</strong>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="#inicio-rápido">Inicio rápido</a> •
|
||||
<a href="#los-seis-riesgos-de-deterioro">Los seis riesgos de deterioro</a> •
|
||||
<a href="#cómo-se-ve">Cómo se ve</a> •
|
||||
<a href="#benchmark">Benchmark</a> •
|
||||
<a href="#instalación">Instalación</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-1.4.0-blue.svg" alt="Version">
|
||||
<img src="https://img.shields.io/badge/license-MIT-green.svg" alt="MIT License">
|
||||
<img src="https://img.shields.io/badge/Claude_Code-Plugin-blueviolet.svg" alt="Claude Code Plugin">
|
||||
<img src="https://img.shields.io/badge/Codex_CLI-Skill-orange.svg" alt="Codex CLI Skill">
|
||||
<img src="https://img.shields.io/github/stars/hyhmrright/brooks-lint?style=social" alt="GitHub Stars">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://trendshift.io/repositories/47738" target="_blank"><img src="https://trendshift.io/api/badge/trendshift/repositories/47738/daily?language=JavaScript" alt="Repositorio JavaScript n.º 2 del día | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="assets/banner-es.svg" alt="Tu código → 12 clásicos → 12 riesgos de deterioro → hallazgos con citas" width="900">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://hyhmrright.github.io/brooks-lint/"><img src="assets/demo.gif" alt="brooks-lint revisando código: un comando /brooks-review produce una puntuación de salud de 28/100 y hallazgos citados con el formato Síntoma → Origen → Consecuencia → Remedio" width="820"></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<strong><a href="https://hyhmrright.github.io/brooks-lint/">→ Visita el sitio web</a></strong>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
> *"Gestar un hijo lleva nueve meses, sin importar a cuántas mujeres se asigne."*
|
||||
> — Frederick Brooks, *The Mythical Man-Month* (El mítico hombre-mes) (1975)
|
||||
|
||||
**50 años después, Brooks seguía teniendo razón — y también McConnell, Fowler, Martin, Hunt & Thomas, Evans, Ousterhout, Winters, Meszaros, Osherove, Feathers y el equipo de Testing de Google.**
|
||||
|
||||
La mayoría de las herramientas de calidad de código cuentan líneas y complejidad ciclomática. **brooks-lint** va más a fondo: diagnostica tu código frente a seis dimensiones de riesgo de deterioro sintetizadas a partir de doce libros clásicos de ingeniería, produciendo cada vez hallazgos estructurados con citas de libros, etiquetas de severidad y remedios concretos.
|
||||
|
||||
Para el mapeo completo de fuente a skill, incluyendo excepciones y protecciones contra falsos positivos, consulta
|
||||
[`skills/_shared/source-coverage.md`](skills/_shared/source-coverage.md).
|
||||
|
||||
## Inicio rápido
|
||||
|
||||
```bash
|
||||
# Claude Code
|
||||
/plugin marketplace add hyhmrright/brooks-lint
|
||||
/plugin install brooks-lint@brooks-lint-marketplace
|
||||
|
||||
# Cualquier otra plataforma de Agent Skills — Cursor · Codex · Gemini · Copilot · Windsurf · OpenCode · Kiro · …
|
||||
curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- <platform>
|
||||
```
|
||||
|
||||
Luego solo pide ("revisa este PR", "audita la arquitectura") — o ejecuta un comando:
|
||||
|
||||
| Comando | Qué hace |
|
||||
|---------|--------------|
|
||||
| `/brooks-review` | Revisa un PR o diff |
|
||||
| `/brooks-audit` | Audita la arquitectura (+ grafo de dependencias en Mermaid) |
|
||||
| `/brooks-debt` | Hoja de ruta de deuda técnica priorizada |
|
||||
| `/brooks-test` | Revisión de calidad de la suite de pruebas |
|
||||
| `/brooks-health` | Panel de salud en todas las dimensiones |
|
||||
| `/brooks-sweep` | Barre todas las dimensiones y corrige hallazgos automáticamente |
|
||||
|
||||
Cada hallazgo se devuelve como **Síntoma → Origen → Consecuencia → Remedio** con una cita de libro y una puntuación de salud de 0 a 100. Las opciones completas de instalación (8 plataformas más), el uso por comando y la configuración de CI/CD están [más abajo](#instalación).
|
||||
|
||||
## Los doce libros
|
||||
|
||||
| Libro | Autor | Contribuye a |
|
||||
|------|--------|----------------|
|
||||
| *The Mythical Man-Month* | Frederick Brooks | R2, R4, R5 |
|
||||
| *Code Complete* | Steve McConnell | R1, R4 |
|
||||
| *Refactoring* | Martin Fowler | R1, R2, R3, R4, R6 |
|
||||
| *Clean Architecture* | Robert C. Martin | R2, R5 |
|
||||
| *The Pragmatic Programmer* | Hunt & Thomas | R2, R3, R4, R5, T2, T3 |
|
||||
| *Domain-Driven Design* | Eric Evans | R1, R3, R6 |
|
||||
| *A Philosophy of Software Design* | John Ousterhout | R1, R4 |
|
||||
| *Software Engineering at Google* | Winters, Manshreck & Wright | R2, R5 |
|
||||
| *The Art of Unit Testing* | Roy Osherove | T1, T2, T4, T5 |
|
||||
| *How Google Tests Software* | James A. Whittaker, Jason Arbon & Jeff Carollo | T5, T6 |
|
||||
| *Working Effectively with Legacy Code* | Michael Feathers | T4, T5, T6 |
|
||||
| *xUnit Test Patterns* | Gerard Meszaros | T1, T2, T3, T4 |
|
||||
|
||||
## Los seis riesgos de deterioro
|
||||
|
||||
brooks-lint evalúa tu código frente a **seis riesgos de deterioro del código de producción** y **seis riesgos de deterioro de la suite de pruebas**, sintetizados a partir de doce libros clásicos de ingeniería:
|
||||
|
||||
| Riesgo de deterioro | Pregunta diagnóstica | Fuentes |
|
||||
|------------|---------------------|---------|
|
||||
| 🧠 Sobrecarga cognitiva | ¿Cuánto esfuerzo mental cuesta entender esto? | Code Complete, Refactoring, DDD, Philosophy of SD |
|
||||
| 🔗 Propagación de cambios | ¿Cuántas cosas no relacionadas se rompen con un solo cambio? | Refactoring, Clean Architecture, Pragmatic, SE@Google |
|
||||
| 📋 Duplicación de conocimiento | ¿La misma decisión se expresa en varios lugares? | Pragmatic, Refactoring, DDD |
|
||||
| 🌀 Complejidad accidental | ¿El código es más complejo que el problema? | Refactoring, Code Complete, Brooks, Philosophy of SD |
|
||||
| 🏗️ Desorden de dependencias | ¿Las dependencias fluyen en una dirección consistente? | Clean Architecture, Brooks, Pragmatic, SE@Google |
|
||||
| 🗺️ Distorsión del modelo de dominio | ¿El código representa fielmente el dominio? | DDD, Refactoring |
|
||||
|
||||
> Philosophy of SD = *A Philosophy of Software Design* (Ousterhout) · SE@Google = *Software Engineering at Google* (Winters et al.)
|
||||
|
||||
## Cómo se ve
|
||||
|
||||
Dado este código:
|
||||
|
||||
```python
|
||||
class UserService:
|
||||
def update_profile(self, user_id, name, email, avatar_url):
|
||||
user = self.db.query(f"SELECT * FROM users WHERE id = {user_id}")
|
||||
user['email'] = email
|
||||
...
|
||||
if user['email'] != email: # always False — silent bug
|
||||
self.smtp.send(...)
|
||||
points = user['login_count'] * 10 + 500
|
||||
self.db.execute(f"UPDATE loyalty SET points={points} WHERE user_id={user_id}")
|
||||
```
|
||||
|
||||
brooks-lint produce:
|
||||
|
||||
---
|
||||
|
||||
**Puntuación de salud: 28/100**
|
||||
|
||||
*Este método concentra cuatro responsabilidades de negocio no relacionadas en una sola función, contiene un error lógico que suprime silenciosamente las notificaciones de cambio de correo electrónico y queda totalmente expuesto a inyección de SQL.*
|
||||
|
||||
### 🔴 Propagación de cambios — Un solo método cambia por cuatro razones de negocio no relacionadas
|
||||
**Síntoma:** `update_profile` realiza la actualización de los campos del perfil, las notificaciones de cambio de correo, el recálculo de puntos de fidelidad y la invalidación de caché, todo en el cuerpo de un mismo método.
|
||||
**Origen:** Fowler — *Refactoring* — Divergent Change (Cambio divergente); Hunt & Thomas — *The Pragmatic Programmer* — Orthogonality (Ortogonalidad)
|
||||
**Consecuencia:** Cualquier cambio en la fórmula de fidelidad arriesga romper las notificaciones de correo y viceversa. Cada edición conlleva riesgo de regresión en cuatro dominios no relacionados de forma simultánea.
|
||||
**Remedio:** Extrae `NotificationService`, `LoyaltyService` y `UserCacheInvalidator`. `UserService.update_profile` debería orquestar llamando a cada uno — no debería contener lógica de implementación propia.
|
||||
|
||||
### 🔴 Distorsión del modelo de dominio — Error lógico silencioso: la notificación de correo nunca se dispara
|
||||
**Síntoma:** `user['email'] = email` sobrescribe el valor anterior antes de `if user['email'] != email` — la condición siempre es `False`. La notificación es código muerto.
|
||||
**Origen:** McConnell — *Code Complete* — Cap. 17: Estructuras de control inusuales
|
||||
**Consecuencia:** Los usuarios nunca son notificados cuando cambia su dirección de correo. Fallo silencioso de integridad de datos — el sistema parece funcionar mientras viola una regla de negocio.
|
||||
**Remedio:** Captura `old_email = user['email']` antes de cualquier mutación. Compara contra `old_email`, no contra `user['email']`.
|
||||
|
||||
*(+ 6 hallazgos más, incluyendo inyección de SQL, desorden de dependencias y números mágicos)*
|
||||
|
||||
### Auditoría de arquitectura con grafo de dependencias
|
||||
|
||||
En el Modo 2 (Auditoría de arquitectura), brooks-lint genera un **grafo de dependencias en Mermaid** en la parte superior del informe. Los módulos se colorean según su severidad: rojo = hallazgos Critical, amarillo = Warning, verde = limpio.
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph src/api
|
||||
AuthController
|
||||
UserController
|
||||
end
|
||||
subgraph src/domain
|
||||
UserService
|
||||
OrderService
|
||||
end
|
||||
subgraph src/infra
|
||||
Database
|
||||
EmailClient
|
||||
end
|
||||
|
||||
AuthController --> UserService
|
||||
UserController --> UserService
|
||||
UserController --> OrderService
|
||||
OrderService --> UserService
|
||||
OrderService --> EmailClient
|
||||
UserService --> Database
|
||||
EmailClient -.->|circular| OrderService
|
||||
|
||||
classDef critical fill:#ff6b6b,stroke:#c92a2a,color:#fff
|
||||
classDef warning fill:#ffd43b,stroke:#e67700
|
||||
classDef clean fill:#51cf66,stroke:#2b8a3e,color:#fff
|
||||
|
||||
class OrderService,EmailClient critical
|
||||
class AuthController warning
|
||||
class UserService,UserController,Database clean
|
||||
```
|
||||
|
||||
El grafo se renderiza de forma nativa en GitHub, Notion y otros entornos Markdown — sin herramientas adicionales.
|
||||
|
||||
## Más ejemplos
|
||||
|
||||
La [Galería completa](docs/gallery.md) contiene salida real de brooks-lint en Python, TypeScript, Go y Java — incluyendo revisiones de PR, auditorías de arquitectura con grafos de dependencias en Mermaid, evaluaciones de deuda técnica y revisiones de calidad de pruebas.
|
||||
|
||||
¿Nuevo en los riesgos de deterioro? La [**Guía de campo de los riesgos de deterioro**](https://hyhmrright.github.io/brooks-lint/guide.html) explica los seis — pregunta diagnóstica, síntomas característicos, libros de origen y remedio para cada uno.
|
||||
|
||||
---
|
||||
|
||||
## Benchmark
|
||||
|
||||
Probado en 3 escenarios del mundo real (revisión de PR, auditoría de arquitectura, evaluación de deuda técnica):
|
||||
|
||||
| Criterio | brooks-lint | Claude solo |
|
||||
|-----------|:-----------:|:------------:|
|
||||
| Hallazgos estructurados (Síntoma → Origen → Consecuencia → Remedio) | ✅ 100% | ❌ 0% |
|
||||
| Citas de libros por hallazgo | ✅ 100% | ❌ 0% |
|
||||
| Etiquetas de severidad (🔴/🟡/🟢) | ✅ 100% | ❌ 0% |
|
||||
| Puntuación de salud (0–100) | ✅ 100% | ❌ 0% |
|
||||
| Detecta Propagación de cambios | ✅ 100% | ✅ 100% |
|
||||
| **Tasa de aprobación global** | **94%** | **16%** |
|
||||
|
||||
La brecha no está en lo que Claude *puede* encontrar — está en lo que encuentra de forma *consistente*, con evidencia trazable y remedios accionables cada vez.
|
||||
|
||||
### Benchmarks reproducibles
|
||||
|
||||
La tabla anterior es ilustrativa. Estas cifras son **deterministas y puedes reproducirlas localmente**:
|
||||
|
||||
**Fidelidad del parser** — la exportación a SARIF y los gates de CI dependen de parsear correctamente el informe Markdown del modelo. Frente a un **corpus congelado de 30 informes reales generados por el modelo** que abarcan los seis modos (`evals/benchmark-corpus.json`), cada uno emparejado con un inventario de hallazgos **calificado de forma independiente** (un pase de modelo separado, revisado manualmente por muestreo), el parser distribuido obtiene — ejecuta `npm run benchmark`:
|
||||
|
||||
| Métrica (n = 30, corpus congelado) | Resultado |
|
||||
|---|:---:|
|
||||
| Coincidencia exacta del conteo de severidad (parser vs. verdad calificada) | 30 / 30 |
|
||||
| Precisión / recall del código de riesgo | 100% / 100% (56 códigos a nivel de hallazgo, 0 FP / 0 FN) |
|
||||
| SARIF 2.1.0 válido emitido | 30 / 30 |
|
||||
|
||||
Como el parser es determinista y el corpus está congelado, `npm run benchmark` da a todo el mundo el mismo resultado, y `npm test` lo protege como regresión. El corpus incluye deliberadamente 9 informes de falsos positivos / compensaciones (por ejemplo, un diseño de puertos y adaptadores que *parece* un ciclo de dependencias) que deben permanecer limpios.
|
||||
|
||||
**Determinismo del scoring** — para un conjunto fijo de hallazgos (2 Critical / 3 Warning / 1 Suggestion), los presets de severidad producen exactamente las puntuaciones que predice su tabla de `common.md`: strict **34**, balanced **54**, legacy-friendly **74** — y solo `legacy-friendly` encabeza con las tres correcciones principales.
|
||||
|
||||
**Calidad del modelo** — si el modelo encuentra los riesgos *correctos* en código real se mide con la **suite de evaluaciones de 57 escenarios** (`evals/evals.json`): `npm run evals` (estructural) y `npm run evals:live` (en vivo, requiere `ANTHROPIC_API_KEY`).
|
||||
|
||||
> Alcance y honestidad: las cifras del parser son deterministas y exactamente reproducibles. Las cifras de severidad y de la suite de evaluaciones son mediciones en vivo de una sola ejecución contra el modelo y varían ligeramente entre ejecuciones. El benchmark del parser mide la fidelidad del parseo de informes (¿lee la herramienta cada hallazgo que el informe declara?), no si un hallazgo dado es "correcto". La coincidencia del conteo de severidad es la señal totalmente independiente; la concordancia de códigos de riesgo también refleja la leyenda canónica compartida de nombre→código.
|
||||
|
||||
## Cómo se compara
|
||||
|
||||
| | brooks-lint | ESLint / Pylint | GitHub Copilot Review | Claude sin más |
|
||||
|---|:---:|:---:|:---:|:---:|
|
||||
| Detecta problemas de sintaxis y estilo | — | ✅ | ✅ | ~ |
|
||||
| Cadena de diagnóstico estructurada | ✅ | ❌ | ❌ | ❌ |
|
||||
| Rastrea los hallazgos hasta libros clásicos | ✅ | ❌ | ❌ | ❌ |
|
||||
| Etiquetas de severidad consistentes | ✅ | ✅ | ~ | ❌ |
|
||||
| Perspectivas a nivel de arquitectura | ✅ | ❌ | ~ | ~ |
|
||||
| Análisis del modelo de dominio | ✅ | ❌ | ❌ | ~ |
|
||||
| Cero configuración, sin plugins que instalar | ✅ | ❌ | ✅ | ✅ |
|
||||
| Funciona con cualquier lenguaje | ✅ | ❌ | ✅ | ✅ |
|
||||
|
||||
> `~` = ocasionalmente / de forma inconsistente
|
||||
|
||||
**brooks-lint no reemplaza a tu linter.** Captura lo que los linters no pueden: deriva arquitectónica, silos de conocimiento y distorsión del modelo de dominio — los problemas que frenan a los equipos durante meses antes de que alguien lo note.
|
||||
|
||||
## Instalación
|
||||
|
||||
### Claude Code (recomendado)
|
||||
|
||||
#### Mediante el Plugin Marketplace
|
||||
```bash
|
||||
/plugin marketplace add hyhmrright/brooks-lint
|
||||
/plugin install brooks-lint@brooks-lint-marketplace
|
||||
```
|
||||
|
||||
Los comandos en forma corta (`/brooks-review`) se instalan automáticamente al iniciar la primera sesión. Para instalarlos manualmente:
|
||||
```bash
|
||||
cp commands/*.md ~/.claude/commands/
|
||||
```
|
||||
|
||||
#### Instalación manual
|
||||
```bash
|
||||
mkdir -p ~/.claude/skills/brooks-lint
|
||||
cp -r skills/* ~/.claude/skills/brooks-lint/
|
||||
```
|
||||
|
||||
### Gemini CLI
|
||||
|
||||
#### Mediante extensión
|
||||
```bash
|
||||
/extensions install https://github.com/hyhmrright/brooks-lint
|
||||
```
|
||||
|
||||
#### Instalación manual
|
||||
```bash
|
||||
mkdir -p ~/.gemini/skills
|
||||
cp -r skills/* ~/.gemini/skills/ # flat — Gemini discovers skills only one level deep
|
||||
```
|
||||
> O simplemente: `./scripts/install.sh gemini`
|
||||
|
||||
### Codex CLI
|
||||
|
||||
#### Mediante el instalador de skills (en una sesión de Codex)
|
||||
```
|
||||
Install the brooks-lint skill from hyhmrright/brooks-lint
|
||||
```
|
||||
|
||||
#### Línea de comandos
|
||||
```bash
|
||||
python3 ~/.codex/skills/.system/skill-installer/scripts/install-skill-from-github.py \
|
||||
--repo hyhmrright/brooks-lint --path skills --name brooks-lint
|
||||
```
|
||||
|
||||
#### Instalación manual
|
||||
```bash
|
||||
git clone https://github.com/hyhmrright/brooks-lint.git /tmp/brooks-lint
|
||||
mkdir -p ~/.codex/skills
|
||||
cp -r /tmp/brooks-lint/skills/* ~/.codex/skills/ # flat — matches the skill-installer layout
|
||||
```
|
||||
> O simplemente: `./scripts/install.sh codex`
|
||||
|
||||
### Más plataformas — OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid
|
||||
|
||||
brooks-lint se distribuye como [Agent Skills](https://agentskills.io) estándar. **Cualquier agente que cargue Agent
|
||||
Skills ejecuta los seis modos sin conversión alguna** — un solo comando los instala:
|
||||
|
||||
```bash
|
||||
# elige tu plataforma; --project instala en el repositorio actual en lugar de en tu configuración global
|
||||
curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- <platform>
|
||||
# <platform> = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · gemini · codex · agents
|
||||
```
|
||||
|
||||
El instalador copia los skills **de forma plana** en la carpeta correcta para tu plataforma, de modo que el framework
|
||||
compartido (`../_shared/`) siempre se resuelve — no puedes equivocarte con el diseño. Luego solo pide
|
||||
("revisa este PR", "audita la arquitectura") y el skill correspondiente se activa automáticamente desde su
|
||||
`description`. ¿Nuevo en los skills, o usas otro agente? Consulta **[docs/getting-started.md](docs/getting-started.md)**.
|
||||
|
||||
<details><summary><b>OpenCode</b></summary>
|
||||
|
||||
`./scripts/install.sh opencode` → `~/.config/opencode/skills` (también lee `~/.claude/skills` y
|
||||
`AGENTS.md`). Guía completa: [docs/opencode-setup.md](docs/opencode-setup.md).
|
||||
</details>
|
||||
|
||||
<details><summary><b>Cursor</b> (2.4+)</summary>
|
||||
|
||||
`./scripts/install.sh cursor` → `~/.cursor/skills` (también `.agents/skills`; lee `AGENTS.md`).
|
||||
Guía completa: [docs/cursor-setup.md](docs/cursor-setup.md).
|
||||
</details>
|
||||
|
||||
<details><summary><b>Windsurf</b> (Cascade)</summary>
|
||||
|
||||
`./scripts/install.sh windsurf` → `~/.codeium/windsurf/skills` (lee `AGENTS.md`).
|
||||
Guía completa: [docs/windsurf-setup.md](docs/windsurf-setup.md).
|
||||
</details>
|
||||
|
||||
<details><summary><b>Antigravity</b> (Google)</summary>
|
||||
|
||||
`./scripts/install.sh antigravity --project` → `.agent/skills` (lee `AGENTS.md` / `GEMINI.md`).
|
||||
Guía completa: [docs/antigravity-setup.md](docs/antigravity-setup.md).
|
||||
</details>
|
||||
|
||||
<details><summary><b>pi</b> (earendil-works)</summary>
|
||||
|
||||
`./scripts/install.sh pi` → `~/.pi/agent/skills`, o apunta el ajuste `skills` de pi a un clon.
|
||||
Guía completa: [docs/pi-setup.md](docs/pi-setup.md).
|
||||
</details>
|
||||
|
||||
<details><summary><b>GitHub Copilot</b></summary>
|
||||
|
||||
`./scripts/install.sh copilot --project` → `.github/skills` (también detecta automáticamente `.claude/skills`; lee
|
||||
`AGENTS.md`). Guía completa: [docs/copilot-setup.md](docs/copilot-setup.md).
|
||||
</details>
|
||||
|
||||
<details><summary><b>Kiro</b> (AWS)</summary>
|
||||
|
||||
`./scripts/install.sh kiro` → `~/.kiro/skills` (registra automáticamente `/brooks-review`; lee `AGENTS.md`).
|
||||
Guía completa: [docs/kiro-setup.md](docs/kiro-setup.md).
|
||||
</details>
|
||||
|
||||
<details><summary><b>Factory Droid</b></summary>
|
||||
|
||||
`./scripts/install.sh droid` → `~/.factory/skills` (registra `/brooks-review`; lee `AGENTS.md`).
|
||||
Guía completa: [docs/factory-droid-setup.md](docs/factory-droid-setup.md).
|
||||
</details>
|
||||
|
||||
> **🧪 Estado de verificación.** Claude Code, Gemini CLI y Codex CLI están verificados por el mantenedor. Las ocho
|
||||
> plataformas anteriores están documentadas a partir de la especificación oficial de skills de cada herramienta y verificadas a nivel
|
||||
> de diseño de archivos (el instalador está probado), pero el mantenedor aún no las ha ejecutado de extremo a extremo en cada plataforma. ¿Probaste
|
||||
> alguna — funciona **o** está rota? [Abre un issue](https://github.com/hyhmrright/brooks-lint/issues/new) con
|
||||
> la plataforma, la versión y lo que viste. ¿Otro agente de Agent Skills? Casi con certeza funciona de la misma
|
||||
> manera — cuéntanoslo y lo añadiremos.
|
||||
|
||||
## Comandos de barra
|
||||
|
||||
### Claude Code
|
||||
| Comando | Forma corta | Acción |
|
||||
|---------|------------|--------|
|
||||
| `/brooks-lint:brooks-review` | `/brooks-review` | Revisión de código a nivel de PR |
|
||||
| `/brooks-lint:brooks-audit` | `/brooks-audit` | Auditoría completa de arquitectura |
|
||||
| `/brooks-lint:brooks-debt` | `/brooks-debt` | Evaluación de deuda técnica |
|
||||
| `/brooks-lint:brooks-test` | `/brooks-test` | Revisión de salud de la suite de pruebas |
|
||||
| `/brooks-lint:brooks-health` | `/brooks-health` | Panel de salud — las cuatro dimensiones |
|
||||
| `/brooks-lint:brooks-sweep` | `/brooks-sweep` | Barrido completo — analiza todas las dimensiones y corrige hallazgos automáticamente |
|
||||
|
||||
> Los comandos en forma corta se instalan automáticamente al iniciar la primera sesión, mediante el hook session-start.
|
||||
|
||||
### Gemini CLI
|
||||
| Comando | Acción |
|
||||
|---------|--------|
|
||||
| `/brooks-review` | Revisión de código a nivel de PR |
|
||||
| `/brooks-audit` | Auditoría completa de arquitectura |
|
||||
| `/brooks-debt` | Evaluación de deuda técnica |
|
||||
| `/brooks-test` | Revisión de salud de la suite de pruebas |
|
||||
| `/brooks-health` | Panel de salud — las cuatro dimensiones |
|
||||
| `/brooks-sweep` | Barrido completo — analiza todas las dimensiones y corrige hallazgos automáticamente |
|
||||
|
||||
### Codex CLI
|
||||
|
||||
| Comando | Acción |
|
||||
|---------|--------|
|
||||
| `$brooks-review` | Revisión de código a nivel de PR |
|
||||
| `$brooks-audit` | Auditoría completa de arquitectura |
|
||||
| `$brooks-debt` | Evaluación de deuda técnica |
|
||||
| `$brooks-test` | Revisión de salud de la suite de pruebas |
|
||||
| `$brooks-health` | Panel de salud — las cuatro dimensiones |
|
||||
| `$brooks-sweep` | Barrido completo — analiza todas las dimensiones y corrige hallazgos automáticamente |
|
||||
|
||||
Los skills también se activan automáticamente cuando hablas de calidad de código, arquitectura, mantenibilidad o salud de las pruebas.
|
||||
|
||||
### OpenCode · Cursor · Antigravity · pi
|
||||
|
||||
Estas plataformas invocan los Agent Skills automáticamente a partir del `description` de cada skill — solo pide
|
||||
("revisa este PR", "audita la arquitectura", "¿dónde está nuestra peor deuda técnica?") y se ejecuta el modo
|
||||
correspondiente. Para una invocación explícita, usa la sintaxis de comando de skill de la plataforma (por ejemplo, pi registra cada skill
|
||||
como `/skill:brooks-review`; Cursor y OpenCode exponen `/brooks-review` una vez que el skill es descubierto).
|
||||
|
||||
## Uso
|
||||
|
||||
### Revisión de PR
|
||||
|
||||
```
|
||||
/brooks-review # Claude Code (forma corta) / Gemini CLI
|
||||
/brooks-lint:brooks-review # Claude Code (forma completa)
|
||||
$brooks-review # Codex CLI
|
||||
```
|
||||
|
||||
Pega un diff o apunta la IA a los archivos modificados. Diagnostica cada uno de los seis riesgos de deterioro con hallazgos específicos en el formato Síntoma → Origen → Consecuencia → Remedio.
|
||||
|
||||
### Auditoría de arquitectura
|
||||
|
||||
```
|
||||
/brooks-audit # Claude Code (forma corta) / Gemini CLI
|
||||
/brooks-lint:brooks-audit # Claude Code (forma completa)
|
||||
$brooks-audit # Codex CLI
|
||||
```
|
||||
|
||||
Describe la estructura de tu proyecto o comparte archivos clave. Mapea las dependencias entre módulos, identifica dependencias circulares y comprueba la alineación con la Ley de Conway.
|
||||
|
||||
### Evaluación de deuda técnica
|
||||
|
||||
```
|
||||
/brooks-debt # Claude Code (forma corta) / Gemini CLI
|
||||
/brooks-lint:brooks-debt # Claude Code (forma completa)
|
||||
$brooks-debt # Codex CLI
|
||||
```
|
||||
|
||||
Clasifica tu deuda según los seis riesgos de deterioro, puntúa cada hallazgo por prioridad de Dolor × Alcance y produce una hoja de ruta de pago priorizada con clasificación Critical / Scheduled / Monitored.
|
||||
|
||||
### Revisión de calidad de pruebas
|
||||
|
||||
```
|
||||
/brooks-test # Claude Code (forma corta) / Gemini CLI
|
||||
/brooks-lint:brooks-test # Claude Code (forma completa)
|
||||
$brooks-test # Codex CLI
|
||||
```
|
||||
|
||||
Audita tu suite de pruebas frente a seis riesgos de deterioro del espacio de pruebas — Oscuridad de la prueba, Fragilidad de la prueba, Duplicación de la prueba, Abuso de mocks, Ilusión de cobertura y Desajuste de arquitectura — provenientes de xUnit Test Patterns, The Art of Unit Testing, How Google Tests Software y Working Effectively with Legacy Code. Las revisiones de PR también incluyen automáticamente un Paso 7 ligero de Comprobación rápida de pruebas (omitido para diffs solo de documentación o que no son de código de producción).
|
||||
|
||||
### Panel de salud
|
||||
|
||||
```
|
||||
/brooks-health # Claude Code (forma corta) / Gemini CLI
|
||||
/brooks-lint:brooks-health # Claude Code (forma completa)
|
||||
$brooks-health # Codex CLI
|
||||
```
|
||||
|
||||
Ejecuta escaneos abreviados en las cuatro dimensiones de calidad y produce una puntuación de salud compuesta y ponderada (0–100). Úsalo antes de un release, al incorporar a un nuevo equipo, o siempre que quieras un informe panorámico de "¿cómo vamos?". Para un diagnóstico más profundo en cualquier dimensión, usa en su lugar el skill enfocado.
|
||||
|
||||
### Barrido completo
|
||||
|
||||
```
|
||||
/brooks-sweep # Claude Code (forma corta) / Gemini CLI
|
||||
/brooks-lint:brooks-sweep # Claude Code (forma completa)
|
||||
$brooks-sweep # Codex CLI
|
||||
```
|
||||
|
||||
Ejecuta un escaneo unificado de todos los riesgos de deterioro de producción (R1–R6) y de pruebas (T1–T6), además de la arquitectura, en una sola pasada, y luego aplica las correcciones: los cambios seguros se aplican automáticamente de inmediato, los cambios multiarchivo o que tocan interfaces requieren confirmación, y las decisiones arquitectónicas complejas se marcan como elementos manuales. Produce un Registro de correcciones, el delta de la puntuación de salud y una lista de elementos residuales.
|
||||
|
||||
## Configuración
|
||||
|
||||
Coloca un `.brooks-lint.yaml` en la raíz de tu proyecto para personalizar el comportamiento de la revisión:
|
||||
|
||||
```yaml
|
||||
version: 1
|
||||
|
||||
strictness: balanced # strict | balanced (default) | legacy-friendly — softer scoring for legacy code
|
||||
|
||||
disable:
|
||||
- T5 # skip coverage metrics check — we don't enforce coverage
|
||||
|
||||
severity:
|
||||
R1: suggestion # downgrade Cognitive Overload findings for this domain
|
||||
|
||||
ignore:
|
||||
- "**/*.generated.*"
|
||||
- "**/vendor/**"
|
||||
|
||||
# custom_risks: # define project-specific Cx codes — see skills/_shared/custom-risks-guide.md
|
||||
# suppress: # downgrade specific findings by risk + path (e.g. accepted legacy debt)
|
||||
```
|
||||
|
||||
Copia [`.brooks-lint.example.yaml`](.brooks-lint.example.yaml) como punto de partida.
|
||||
Todos los ajustes son opcionales — omite el archivo por completo para el comportamiento por defecto.
|
||||
|
||||
| Ajuste | Descripción |
|
||||
|---------|-------------|
|
||||
| `strictness` | Preset de scoring: `strict`, `balanced` (por defecto) o `legacy-friendly` (deducciones más ligeras, encabeza con las correcciones principales) |
|
||||
| `disable` | Códigos de riesgo a omitir (`R1`–`R6`, `T1`–`T6`) |
|
||||
| `severity` | Sobrescribe el nivel de severidad (`critical` / `warning` / `suggestion`) |
|
||||
| `ignore` | Patrones glob de archivos a excluir |
|
||||
| `focus` | Evalúa solo estos códigos de riesgo (no se puede combinar con `disable`) |
|
||||
| `custom_risks` | Define códigos de riesgo específicos del proyecto (`C1`, `C2`, …) — consulta [`custom-risks-guide.md`](skills/_shared/custom-risks-guide.md) |
|
||||
| `suppress` | Rebaja hallazgos específicos por riesgo + ruta (fecha `expires:` opcional) |
|
||||
|
||||
---
|
||||
|
||||
## ¿Por qué estos libros, por qué ahora?
|
||||
|
||||
En la era de la programación asistida por IA, escribimos más código y más rápido que nunca. Pero las ideas de seis décadas de ingeniería de software no han cambiado:
|
||||
|
||||
> *"La complejidad del software es una propiedad esencial, no accidental."*
|
||||
> — Frederick Brooks
|
||||
|
||||
La IA puede ayudarte a escribir código más rápido, pero no puede decirte si estás construyendo una catedral o un pozo de brea. **brooks-lint cierra esa brecha** — lleva la sabiduría tan duramente ganada de doce libros clásicos de ingeniería a tu flujo de trabajo de desarrollo moderno.
|
||||
|
||||
Los riesgos de deterioro que identificaron estos autores son más relevantes que nunca:
|
||||
- **Añadir asistentes de IA** no soluciona la sobrecarga cognitiva ni la distorsión del modelo de dominio
|
||||
- **Generar más código** aumenta la propagación de cambios y la duplicación de conocimiento
|
||||
- **Ir más rápido** vuelve aún más peligrosas la complejidad accidental y el desorden de dependencias
|
||||
|
||||
## Estructura del proyecto
|
||||
|
||||
```
|
||||
brooks-lint/
|
||||
├── .claude-plugin/ # Claude Code plugin metadata
|
||||
├── .codex-plugin/ # Codex CLI plugin metadata
|
||||
├── skills/
|
||||
│ ├── _shared/ # Shared framework files
|
||||
│ │ ├── common.md # Iron Law, Project Config, Report Template, Health Score
|
||||
│ │ ├── source-coverage.md # 12-book coverage matrix, tradeoffs, false-positive guards
|
||||
│ │ ├── decay-risks.md # Six decay risks with symptoms and book citations
|
||||
│ │ ├── test-decay-risks.md # Six test-space decay risks with book citations
|
||||
│ │ ├── remedy-guide.md # --fix mode: actionable Remedy enhancement rules
|
||||
│ │ └── custom-risks-guide.md # Template for project-specific risk codes
|
||||
│ ├── brooks-review/ # Mode 1: PR Review
|
||||
│ │ ├── SKILL.md
|
||||
│ │ └── pr-review-guide.md
|
||||
│ ├── brooks-audit/ # Mode 2: Architecture Audit
|
||||
│ │ ├── SKILL.md
|
||||
│ │ └── architecture-guide.md
|
||||
│ ├── brooks-debt/ # Mode 3: Tech Debt Assessment
|
||||
│ │ ├── SKILL.md
|
||||
│ │ └── debt-guide.md
|
||||
│ ├── brooks-test/ # Mode 4: Test Quality Review
|
||||
│ │ ├── SKILL.md
|
||||
│ │ └── test-guide.md
|
||||
│ ├── brooks-health/ # Mode 5: Health Dashboard
|
||||
│ │ ├── SKILL.md
|
||||
│ │ └── health-guide.md
|
||||
│ └── brooks-sweep/ # Mode 6: Full Sweep & Auto-Fix
|
||||
│ ├── SKILL.md
|
||||
│ └── sweep-guide.md
|
||||
├── hooks/ # SessionStart hook
|
||||
├── commands/ # Short-form command wrappers (auto-installed by hook)
|
||||
├── evals/ # Benchmark test cases
|
||||
│ └── evals.json
|
||||
└── assets/
|
||||
└── logo.svg
|
||||
```
|
||||
|
||||
## Integración con CI/CD
|
||||
|
||||
Automatiza brooks-lint en cada PR usando la GitHub Action:
|
||||
|
||||
```yaml
|
||||
# .github/workflows/brooks-lint.yml
|
||||
name: Brooks-Lint PR Review
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
brooks-lint:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: hyhmrright/brooks-lint/.github/actions/brooks-lint@main
|
||||
with:
|
||||
mode: review
|
||||
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
fail-below: 70
|
||||
```
|
||||
|
||||
Consulta [`docs/github-action-example.yml`](docs/github-action-example.yml) para la plantilla completa.
|
||||
|
||||
La action publica la revisión como un comentario del PR y, opcionalmente, hace fallar el check si la puntuación de salud cae por debajo de un umbral. Si `.brooks-lint-history.json` está confirmado en tu repositorio, el comentario también incluye un delta de tendencia (p. ej., "85 → 82 (−3) en las últimas 3 ejecuciones").
|
||||
|
||||
**Gates de calidad y Code Scanning.** Más allá de `fail-below`, la action expone:
|
||||
|
||||
```yaml
|
||||
with:
|
||||
mode: review
|
||||
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
fail-on: critical # fail on any Critical finding (none | warning | critical)
|
||||
fail-on-regression: true # fail if the Health Score dropped vs the last run
|
||||
sarif-file: brooks-lint.sarif # also upload findings to GitHub Code Scanning
|
||||
```
|
||||
|
||||
`fail-on-regression` lee `.brooks-lint-history.json`, así que confirma ese archivo para imponer "sin nuevas regresiones". Definir `sarif-file` hace que los hallazgos aparezcan en línea en la pestaña **Files changed** del PR y requiere el permiso `security-events: write` en el job.
|
||||
|
||||
**Coste:** ~$0,05–0,15 por ejecución de PR, según el tamaño del diff y el modelo. Se recomienda ejecutar solo en eventos `pull_request`.
|
||||
|
||||
## Hoja de ruta
|
||||
|
||||
> **Estado actual (v1.4):** base de 12 libros, 6 riesgos de deterioro de producción (R1–R6) + 6 riesgos de deterioro de pruebas (T1–T6), 6 skills — Revisión de PR, Auditoría de arquitectura, Deuda técnica, Calidad de pruebas, Panel de salud, Barrido completo — además de gates de calidad de CI, salida SARIF para GitHub Code Scanning, presets de severidad y un benchmark reproducible de fidelidad del parser. Las entradas anteriores más abajo describen hitos históricos, no el conjunto de funciones actual.
|
||||
|
||||
- [x] **v0.2**: Infraestructura de plugin (`.claude-plugin/`, hooks, comandos de barra)
|
||||
- [x] **v0.3**: Ocho dimensiones de Brooks, puntuación de completitud de la documentación
|
||||
- [x] **v0.4**: Framework de seis libros, dimensiones de riesgo de deterioro, cadena de diagnóstico, suite de benchmark
|
||||
- [x] **v0.5**: Revisión de calidad de pruebas (Modo 4) — cuatro libros de testing, seis riesgos de deterioro de pruebas
|
||||
- [x] **v0.6**: Grafo de dependencias en Mermaid en la Auditoría de arquitectura
|
||||
- [x] **v0.7**: Configuración de proyecto `.brooks-lint.yaml`, contexto proactivo del Modo 2, expansión a 10 libros
|
||||
- [x] **v0.8**: Arquitectura de skills independientes con comandos con espacio de nombres
|
||||
- [x] **v0.9**: Validación de pasos, alcance de diff automático, panel `/brooks-health`, seguimiento de tendencias, modo triage, remedios `--fix`, informe de incorporación, GitHub Action
|
||||
- [x] **v1.0**: Automatización de evaluaciones (`run-evals-live.mjs`), extensión de riesgos personalizados (códigos `Cx`)
|
||||
- [x] **v1.1**: Skill de Barrido completo (`brooks-sweep`) — corrección automática unificada multidimensión
|
||||
- [x] **v1.2**: Pipeline de barrido autónomo, propagación de versión con `npm run bump`
|
||||
- [x] **v1.3**: Metadatos de marketplace de Codex, instalador de un solo comando para múltiples plataformas de agentes, README bilingüe + sitio de aterrizaje
|
||||
- [x] **v1.4**: Salida SARIF para GitHub Code Scanning, gates de CI de severidad + regresión, presets de severidad (strict/balanced/legacy-friendly), suite de evaluaciones de 57 escenarios, benchmark reproducible de fidelidad del parser (`npm run benchmark`)
|
||||
|
||||
¿Quieres ayudar? Las mejores contribuciones ahora mismo son nuevos casos de prueba de evaluación y mejores patrones de síntomas de riesgo de deterioro. Consulta [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||
|
||||
## Contribuir
|
||||
|
||||
Consulta [CONTRIBUTING.md](CONTRIBUTING.md) para saber cómo añadir hallazgos, mejorar guías o ampliar la suite de benchmark.
|
||||
|
||||
Ejecuta `/brooks-review` en tu propio PR — revisamos las contribuciones con la herramienta que estamos construyendo.
|
||||
|
||||
## Licencia
|
||||
|
||||
Licencia MIT — consulta [LICENSE](LICENSE) para más detalles.
|
||||
|
||||
## Agradecimientos
|
||||
|
||||
Este proyecto se apoya en los hombros de doce gigantes:
|
||||
|
||||
**Framework de código de producción**
|
||||
- Frederick P. Brooks Jr. — *The Mythical Man-Month* (1975, Edición Aniversario 1995)
|
||||
- Steve McConnell — *Code Complete* (1993, 2.ª ed. 2004)
|
||||
- Martin Fowler — *Refactoring* (1999, 2.ª ed. 2018)
|
||||
- Robert C. Martin — *Clean Architecture* (2017)
|
||||
- Andrew Hunt & David Thomas — *The Pragmatic Programmer* (1999, Edición 20.º Aniversario 2019)
|
||||
- Eric Evans — *Domain-Driven Design* (2003)
|
||||
- John Ousterhout — *A Philosophy of Software Design* (2018)
|
||||
- Titus Winters, Tom Manshreck y Hyrum Wright — *Software Engineering at Google* (2020)
|
||||
|
||||
**Framework de calidad de pruebas**
|
||||
- Gerard Meszaros — *xUnit Test Patterns* (2007)
|
||||
- Roy Osherove — *The Art of Unit Testing* (2009, 3.ª ed. 2023)
|
||||
- Google Engineering — *How Google Tests Software* (2012)
|
||||
- Michael Feathers — *Working Effectively with Legacy Code* (2004)
|
||||
|
||||
Los riesgos de deterioro codificados en esta herramienta son nuestra síntesis de sus ideas, aplicada a la evaluación moderna de la calidad del código.
|
||||
|
||||
---
|
||||
|
||||
## Historial de estrellas
|
||||
|
||||
[](https://star-history.com/#hyhmrright/brooks-lint&Date)
|
||||
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
<strong>⭐ Si esta herramienta te ayudó a ver tu base de código de otra manera, ¡dale una estrella!</strong>
|
||||
</p>
|
||||
@@ -0,0 +1,690 @@
|
||||
<p align="center">
|
||||
<img src="assets/logo.svg" alt="brooks-lint" width="200">
|
||||
</p>
|
||||
|
||||
<h1 align="center">brooks-lint</h1>
|
||||
|
||||
<p align="center">
|
||||
<strong>十二冊の古典的ソフトウェア工学書に根ざした AI コードレビュー。<br>
|
||||
一貫性があり、追跡可能で、実行に移せる。</strong>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="README.md">English</a> ·
|
||||
<a href="README.zh-CN.md">简体中文</a> ·
|
||||
<a href="README.zh-TW.md">繁體中文</a> ·
|
||||
<strong>日本語</strong> ·
|
||||
<a href="README.ko.md">한국어</a> ·
|
||||
<a href="README.es.md">Español</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="#クイックスタート">クイックスタート</a> •
|
||||
<a href="#六つの劣化リスク">六つの劣化リスク</a> •
|
||||
<a href="#出力イメージ">出力イメージ</a> •
|
||||
<a href="#ベンチマーク">ベンチマーク</a> •
|
||||
<a href="#インストール">インストール</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-1.4.0-blue.svg" alt="Version">
|
||||
<img src="https://img.shields.io/badge/license-MIT-green.svg" alt="MIT License">
|
||||
<img src="https://img.shields.io/badge/Claude_Code-Plugin-blueviolet.svg" alt="Claude Code Plugin">
|
||||
<img src="https://img.shields.io/badge/Codex_CLI-Skill-orange.svg" alt="Codex CLI Skill">
|
||||
<img src="https://img.shields.io/github/stars/hyhmrright/brooks-lint?style=social" alt="GitHub Stars">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://trendshift.io/repositories/47738" target="_blank"><img src="https://trendshift.io/api/badge/trendshift/repositories/47738/daily?language=JavaScript" alt="本日の JavaScript リポジトリ第 2 位 | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="assets/banner-ja.svg" alt="あなたのコード → 十二冊の古典 → 十二の劣化リスク → 出典付きの指摘" width="900">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://hyhmrright.github.io/brooks-lint/"><img src="assets/demo.gif" alt="brooks-lint がコードをレビューする様子:1 つの /brooks-review コマンドで 28/100 の健全性スコアと、書籍を引用した 症状 → 根源 → 結果 → 対策 の指摘を生成" width="820"></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<strong><a href="https://hyhmrright.github.io/brooks-lint/">→ ウェブサイトを見る</a></strong>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
> *"一人の子を産むのに九か月かかるのは、何人の女性を割り当てても変わらない。"*
|
||||
> — Frederick Brooks, *The Mythical Man-Month*(人月の神話、1975)
|
||||
|
||||
**50 年が経った今も Brooks は正しかった——そして McConnell、Fowler、Martin、Hunt & Thomas、Evans、Ousterhout、Winters、Meszaros、Osherove、Feathers、そして Google のテストチームもまた正しかった。**
|
||||
|
||||
ほとんどのコード品質ツールは行数と循環的複雑度を数えるだけです。**brooks-lint** はさらに踏み込みます——十二冊の古典的ソフトウェア工学書から統合した六つの劣化リスク次元に照らしてコードを診断し、毎回、書籍の出典・重大度ラベル・具体的な対策を備えた構造化された指摘を生成します。
|
||||
|
||||
例外や誤検知ガードを含む「出典—スキル」の完全なマッピングは、
|
||||
[`skills/_shared/source-coverage.md`](skills/_shared/source-coverage.md) を参照してください。
|
||||
|
||||
## クイックスタート
|
||||
|
||||
```bash
|
||||
# Claude Code
|
||||
/plugin marketplace add hyhmrright/brooks-lint
|
||||
/plugin install brooks-lint@brooks-lint-marketplace
|
||||
|
||||
# その他あらゆる Agent Skills プラットフォーム — Cursor · Codex · Gemini · Copilot · Windsurf · OpenCode · Kiro · …
|
||||
curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- <platform>
|
||||
```
|
||||
|
||||
あとは話しかけるだけ(「この PR をレビューして」「アーキテクチャを監査して」)——あるいはコマンドを実行します。
|
||||
|
||||
| コマンド | 機能 |
|
||||
|---------|--------------|
|
||||
| `/brooks-review` | PR または diff をレビュー |
|
||||
| `/brooks-audit` | アーキテクチャを監査(+ Mermaid 依存関係グラフ) |
|
||||
| `/brooks-debt` | 優先順位付けされた技術的負債のロードマップ |
|
||||
| `/brooks-test` | テストスイートの品質レビュー |
|
||||
| `/brooks-health` | 全次元を横断する健全性ダッシュボード |
|
||||
| `/brooks-sweep` | 全次元をスイープし、指摘を自動修正 |
|
||||
|
||||
すべての指摘は **症状 → 根源 → 結果 → 対策** の形式で、書籍の出典と 0〜100 の健全性スコアとともに返されます。完全なインストール方法(さらに 8 つのプラットフォーム)、コマンドごとの使い方、CI/CD のセットアップは[以下](#インストール)を参照してください。
|
||||
|
||||
## 十二冊の書籍
|
||||
|
||||
| 書籍 | 著者 | 寄与する先 |
|
||||
|------|--------|----------------|
|
||||
| *The Mythical Man-Month*(人月の神話) | Frederick Brooks | R2, R4, R5 |
|
||||
| *Code Complete*(コードコンプリート) | Steve McConnell | R1, R4 |
|
||||
| *Refactoring*(リファクタリング) | Martin Fowler | R1, R2, R3, R4, R6 |
|
||||
| *Clean Architecture*(クリーンアーキテクチャ) | Robert C. Martin | R2, R5 |
|
||||
| *The Pragmatic Programmer*(達人プログラマー) | Hunt & Thomas | R2, R3, R4, R5, T2, T3 |
|
||||
| *Domain-Driven Design*(エリック・エヴァンスのドメイン駆動設計) | Eric Evans | R1, R3, R6 |
|
||||
| *A Philosophy of Software Design*(ソフトウェア設計の哲学) | John Ousterhout | R1, R4 |
|
||||
| *Software Engineering at Google*(Google のソフトウェアエンジニアリング) | Winters, Manshreck & Wright | R2, R5 |
|
||||
| *The Art of Unit Testing*(単体テストの考え方/使い方) | Roy Osherove | T1, T2, T4, T5 |
|
||||
| *How Google Tests Software*(テストから見えてくるグーグルのソフトウェア開発) | James A. Whittaker, Jason Arbon & Jeff Carollo | T5, T6 |
|
||||
| *Working Effectively with Legacy Code*(レガシーコード改善ガイド) | Michael Feathers | T4, T5, T6 |
|
||||
| *xUnit Test Patterns*(xUnit テストパターン) | Gerard Meszaros | T1, T2, T3, T4 |
|
||||
|
||||
## 六つの劣化リスク
|
||||
|
||||
brooks-lint は、十二冊の古典的ソフトウェア工学書から統合した**六つの本番コード劣化リスク**と**六つのテストスイート劣化リスク**の観点から、あなたのコードを評価します。
|
||||
|
||||
| 劣化リスク | 診断のための問い | 出典 |
|
||||
|------------|---------------------|---------|
|
||||
| 🧠 認知過負荷 | これを理解するのにどれだけの精神的労力が要るか? | Code Complete, Refactoring, DDD, Philosophy of SD |
|
||||
| 🔗 変更の波及 | 1 つの変更でいくつの無関係なものが壊れるか? | Refactoring, Clean Architecture, Pragmatic, SE@Google |
|
||||
| 📋 知識の重複 | 同じ決定が複数の場所で表現されていないか? | Pragmatic, Refactoring, DDD |
|
||||
| 🌀 偶発的複雑性 | コードは問題そのものより複雑になっていないか? | Refactoring, Code Complete, Brooks, Philosophy of SD |
|
||||
| 🏗️ 依存関係の無秩序 | 依存は一貫した方向に流れているか? | Clean Architecture, Brooks, Pragmatic, SE@Google |
|
||||
| 🗺️ ドメインモデルの歪み | コードはドメインを忠実に表現しているか? | DDD, Refactoring |
|
||||
|
||||
> Philosophy of SD = *A Philosophy of Software Design*(Ousterhout) · SE@Google = *Software Engineering at Google*(Winters ほか)
|
||||
|
||||
## 出力イメージ
|
||||
|
||||
次のコードが与えられたとき:
|
||||
|
||||
```python
|
||||
class UserService:
|
||||
def update_profile(self, user_id, name, email, avatar_url):
|
||||
user = self.db.query(f"SELECT * FROM users WHERE id = {user_id}")
|
||||
user['email'] = email
|
||||
...
|
||||
if user['email'] != email: # always False — silent bug
|
||||
self.smtp.send(...)
|
||||
points = user['login_count'] * 10 + 500
|
||||
self.db.execute(f"UPDATE loyalty SET points={points} WHERE user_id={user_id}")
|
||||
```
|
||||
|
||||
brooks-lint は次を生成します:
|
||||
|
||||
---
|
||||
|
||||
**健全性スコア:28/100**
|
||||
|
||||
*このメソッドは四つの無関係なビジネス責務を 1 つの関数に集約し、メールアドレス変更通知を静かに握りつぶすロジックバグを含み、SQL インジェクションに対して無防備です。*
|
||||
|
||||
### 🔴 変更の波及 — 単一のメソッドが四つの無関係なビジネス理由で変更される
|
||||
**症状:** `update_profile` は、プロフィール項目の更新、メールアドレス変更通知、ロイヤルティポイントの再計算、キャッシュの無効化を、すべて 1 つのメソッド本体で実行しています。
|
||||
**根源:** Fowler — *Refactoring* — Divergent Change(発散的変更);Hunt & Thomas — *The Pragmatic Programmer* — Orthogonality(直交性)
|
||||
**結果:** ロイヤルティの計算式を変更すると、メール通知を壊すおそれがあり、その逆もまた然りです。すべての編集が、四つの無関係なドメインに同時にまたがる回帰リスクを背負います。
|
||||
**対策:** `NotificationService`、`LoyaltyService`、`UserCacheInvalidator` を抽出します。`UserService.update_profile` はそれぞれを呼び出してオーケストレーションするだけにし、自身は実装ロジックを一切持たないようにします。
|
||||
|
||||
### 🔴 ドメインモデルの歪み — 静かなロジックバグ:メール通知が決して発火しない
|
||||
**症状:** `user['email'] = email` が `if user['email'] != email` より前に古い値を上書きするため、条件は常に `False` です。通知はデッドコードです。
|
||||
**根源:** McConnell — *Code Complete* — 第 17 章:変則的な制御構造
|
||||
**結果:** ユーザーはメールアドレスを変更しても決して通知されません。静かなデータ整合性の破綻です——システムは正常に動作しているように見えながら、ビジネスルールに違反しています。
|
||||
**対策:** いかなる変更の前にも `old_email = user['email']` を捕捉します。`user['email']` ではなく `old_email` と比較してください。
|
||||
|
||||
*(SQL インジェクション、依存関係の無秩序、マジックナンバーを含む、さらに 6 件の指摘)*
|
||||
|
||||
### 依存関係グラフ付きのアーキテクチャ監査
|
||||
|
||||
モード 2(アーキテクチャ監査)では、brooks-lint はレポートの先頭に **Mermaid 依存関係グラフ** を生成します。モジュールは重大度で色分けされます:赤 = Critical の指摘、黄 = Warning、緑 = クリーン。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph src/api
|
||||
AuthController
|
||||
UserController
|
||||
end
|
||||
subgraph src/domain
|
||||
UserService
|
||||
OrderService
|
||||
end
|
||||
subgraph src/infra
|
||||
Database
|
||||
EmailClient
|
||||
end
|
||||
|
||||
AuthController --> UserService
|
||||
UserController --> UserService
|
||||
UserController --> OrderService
|
||||
OrderService --> UserService
|
||||
OrderService --> EmailClient
|
||||
UserService --> Database
|
||||
EmailClient -.->|circular| OrderService
|
||||
|
||||
classDef critical fill:#ff6b6b,stroke:#c92a2a,color:#fff
|
||||
classDef warning fill:#ffd43b,stroke:#e67700
|
||||
classDef clean fill:#51cf66,stroke:#2b8a3e,color:#fff
|
||||
|
||||
class OrderService,EmailClient critical
|
||||
class AuthController warning
|
||||
class UserService,UserController,Database clean
|
||||
```
|
||||
|
||||
このグラフは GitHub、Notion、その他の Markdown 環境でネイティブにレンダリングされます——追加のツールは不要です。
|
||||
|
||||
## さらなる例を見る
|
||||
|
||||
[完全ギャラリー](docs/gallery.md) には、Python、TypeScript、Go、Java にわたる brooks-lint の実際の出力が収められています——PR レビュー、Mermaid 依存関係グラフ付きのアーキテクチャ監査、技術的負債の評価、テスト品質レビューを含みます。
|
||||
|
||||
劣化リスクが初めてですか?[**劣化リスク実践ガイド**](https://hyhmrright.github.io/brooks-lint/guide.html) が六つすべてを解説します——それぞれの診断のための問い、典型的な症状、出典書籍、そして対策。
|
||||
|
||||
---
|
||||
|
||||
## ベンチマーク
|
||||
|
||||
3 つの実世界シナリオ(PR レビュー、アーキテクチャ監査、技術的負債の評価)でテストしました:
|
||||
|
||||
| 評価項目 | brooks-lint | Claude 単独 |
|
||||
|-----------|:-----------:|:------------:|
|
||||
| 構造化された指摘(症状 → 根源 → 結果 → 対策) | ✅ 100% | ❌ 0% |
|
||||
| 指摘ごとの書籍引用 | ✅ 100% | ❌ 0% |
|
||||
| 重大度ラベル(🔴/🟡/🟢) | ✅ 100% | ❌ 0% |
|
||||
| 健全性スコア(0〜100) | ✅ 100% | ❌ 0% |
|
||||
| 「変更の波及」を検出 | ✅ 100% | ✅ 100% |
|
||||
| **総合合格率** | **94%** | **16%** |
|
||||
|
||||
差は Claude が何を見つけ*られる*かではありません——何を*毎回一貫して*、追跡可能な根拠と実行可能な対策とともに見つけるか、です。
|
||||
|
||||
### 再現可能なベンチマーク
|
||||
|
||||
上の表は説明用です。次の数値は**決定論的であり、ローカルで再現できます**:
|
||||
|
||||
**パーサー忠実度** — SARIF エクスポートと CI ゲートは、モデルの Markdown レポートを正しく解析できることに依存しています。全六モードにまたがる**30 件の実在するモデル生成レポートの凍結コーパス**(`evals/benchmark-corpus.json`)に対して——各レポートには**独立して採点された**指摘インベントリ(別のモデルパスによるもので、手作業でスポットチェック済み)が対になっています——出荷されているパーサーは次のスコアを出します。`npm run benchmark` を実行してください:
|
||||
|
||||
| 指標(n = 30、凍結コーパス) | 結果 |
|
||||
|---|:---:|
|
||||
| 重大度カウントの完全一致(パーサー vs 採点済み真値) | 30 / 30 |
|
||||
| リスクコードの precision / recall | 100% / 100%(56 件の finding レベルコード、0 FP / 0 FN) |
|
||||
| 妥当な SARIF 2.1.0 の出力 | 30 / 30 |
|
||||
|
||||
パーサーは決定論的で、コーパスは凍結されているため、`npm run benchmark` は誰に対しても同じ結果を返し、`npm test` がこれを回帰として守ります。このコーパスには、クリーンなままであるべき 9 件の誤検知 / トレードオフレポート(例:依存サイクルの*ように見える*ポートとアダプターの設計)が意図的に含まれています。
|
||||
|
||||
**スコアリングの決定論性** — 固定された指摘集合(2 Critical / 3 Warning / 1 Suggestion)に対し、厳格度プリセットは `common.md` の表が予測する通りのスコアを正確に算出します:strict **34**、balanced **54**、legacy-friendly **74**——そして上位三件の修正を先頭に示すのは `legacy-friendly` だけです。
|
||||
|
||||
**モデル品質** — モデルが実際のコードで*正しい*リスクを見つけられるかは、**57 シナリオの eval スイート**(`evals/evals.json`)で測定されます:`npm run evals`(構造)と `npm run evals:live`(ライブ、`ANTHROPIC_API_KEY` が必要)。
|
||||
|
||||
> 範囲と誠実さについて:パーサーの数値は決定論的で、正確に再現可能です。厳格度と eval スイートの数値はモデルに対する単発のライブ測定で、実行ごとにわずかに変動します。パーサーのベンチマークが測るのはレポート解析の忠実度(ツールはレポートに書かれたすべての指摘を読み取れるか)であって、ある指摘が「正しい」かどうかではありません。重大度カウントの一致は完全に独立したシグナルです。リスクコードの一致は、共有された正規の name→code 凡例も反映しています。
|
||||
|
||||
## 比較
|
||||
|
||||
| | brooks-lint | ESLint / Pylint | GitHub Copilot Review | 素の Claude |
|
||||
|---|:---:|:---:|:---:|:---:|
|
||||
| 構文・スタイルの問題を検出 | — | ✅ | ✅ | ~ |
|
||||
| 構造化された診断チェーン | ✅ | ❌ | ❌ | ❌ |
|
||||
| 指摘を古典書籍まで遡る | ✅ | ❌ | ❌ | ❌ |
|
||||
| 一貫した重大度ラベル | ✅ | ✅ | ~ | ❌ |
|
||||
| アーキテクチャレベルの洞察 | ✅ | ❌ | ~ | ~ |
|
||||
| ドメインモデル分析 | ✅ | ❌ | ❌ | ~ |
|
||||
| 設定不要、インストールするプラグインなし | ✅ | ❌ | ✅ | ✅ |
|
||||
| あらゆる言語で動作 | ✅ | ❌ | ✅ | ✅ |
|
||||
|
||||
> `~` = 時々 / 一貫しない
|
||||
|
||||
**brooks-lint はあなたの linter を置き換えるものではありません。** それが捉えるのは linter には捉えられないもの——アーキテクチャのドリフト、知識のサイロ化、ドメインモデルの歪みです。これらは、誰かが気づくまで何か月もチームの足を引っ張る問題です。
|
||||
|
||||
## インストール
|
||||
|
||||
### Claude Code(推奨)
|
||||
|
||||
#### プラグインマーケットプレイス経由
|
||||
```bash
|
||||
/plugin marketplace add hyhmrright/brooks-lint
|
||||
/plugin install brooks-lint@brooks-lint-marketplace
|
||||
```
|
||||
|
||||
短縮形コマンド(`/brooks-review`)は、最初のセッション開始時に自動インストールされます。手動でインストールするには:
|
||||
```bash
|
||||
cp commands/*.md ~/.claude/commands/
|
||||
```
|
||||
|
||||
#### 手動インストール
|
||||
```bash
|
||||
mkdir -p ~/.claude/skills/brooks-lint
|
||||
cp -r skills/* ~/.claude/skills/brooks-lint/
|
||||
```
|
||||
|
||||
### Gemini CLI
|
||||
|
||||
#### 拡張機能経由
|
||||
```bash
|
||||
/extensions install https://github.com/hyhmrright/brooks-lint
|
||||
```
|
||||
|
||||
#### 手動インストール
|
||||
```bash
|
||||
mkdir -p ~/.gemini/skills
|
||||
cp -r skills/* ~/.gemini/skills/ # フラット — Gemini はスキルを 1 階層深さまでしか発見しない
|
||||
```
|
||||
> または単に:`./scripts/install.sh gemini`
|
||||
|
||||
### Codex CLI
|
||||
|
||||
#### スキルインストーラー経由(Codex セッション内)
|
||||
```
|
||||
Install the brooks-lint skill from hyhmrright/brooks-lint
|
||||
```
|
||||
|
||||
#### コマンドライン
|
||||
```bash
|
||||
python3 ~/.codex/skills/.system/skill-installer/scripts/install-skill-from-github.py \
|
||||
--repo hyhmrright/brooks-lint --path skills --name brooks-lint
|
||||
```
|
||||
|
||||
#### 手動インストール
|
||||
```bash
|
||||
git clone https://github.com/hyhmrright/brooks-lint.git /tmp/brooks-lint
|
||||
mkdir -p ~/.codex/skills
|
||||
cp -r /tmp/brooks-lint/skills/* ~/.codex/skills/ # フラット — スキルインストーラーのレイアウトと一致
|
||||
```
|
||||
> または単に:`./scripts/install.sh codex`
|
||||
|
||||
### さらなるプラットフォーム — OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid
|
||||
|
||||
brooks-lint は標準的な [Agent Skills](https://agentskills.io) として配布されています。**Agent
|
||||
Skills を読み込むエージェントなら、どれも変換なしで六つすべてのモードを実行できます**——1 つのコマンドでインストールできます:
|
||||
|
||||
```bash
|
||||
# プラットフォームを選択;--project はグローバル設定ではなく現在のリポジトリにインストール
|
||||
curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- <platform>
|
||||
# <platform> = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · gemini · codex · agents
|
||||
```
|
||||
|
||||
インストーラーはスキルをあなたのプラットフォームに適したフォルダへ**フラット**にコピーするため、共有フレームワーク
|
||||
(`../_shared/`)は常に正しく解決されます——レイアウトを間違えようがありません。あとは話しかけるだけ
|
||||
(「この PR をレビューして」「アーキテクチャを監査して」)で、該当するスキルがその
|
||||
`description` に基づいて自動的にトリガーされます。スキルが初めて、または別のエージェントをお使いですか? **[docs/getting-started.md](docs/getting-started.md)** を参照してください。
|
||||
|
||||
<details><summary><b>OpenCode</b></summary>
|
||||
|
||||
`./scripts/install.sh opencode` → `~/.config/opencode/skills`(`~/.claude/skills` と
|
||||
`AGENTS.md` も読み取ります)。完全ガイド:[docs/opencode-setup.md](docs/opencode-setup.md)。
|
||||
</details>
|
||||
|
||||
<details><summary><b>Cursor</b>(2.4+)</summary>
|
||||
|
||||
`./scripts/install.sh cursor` → `~/.cursor/skills`(`.agents/skills` も;`AGENTS.md` を読み取ります)。
|
||||
完全ガイド:[docs/cursor-setup.md](docs/cursor-setup.md)。
|
||||
</details>
|
||||
|
||||
<details><summary><b>Windsurf</b>(Cascade)</summary>
|
||||
|
||||
`./scripts/install.sh windsurf` → `~/.codeium/windsurf/skills`(`AGENTS.md` を読み取ります)。
|
||||
完全ガイド:[docs/windsurf-setup.md](docs/windsurf-setup.md)。
|
||||
</details>
|
||||
|
||||
<details><summary><b>Antigravity</b>(Google)</summary>
|
||||
|
||||
`./scripts/install.sh antigravity --project` → `.agent/skills`(`AGENTS.md` / `GEMINI.md` を読み取ります)。
|
||||
完全ガイド:[docs/antigravity-setup.md](docs/antigravity-setup.md)。
|
||||
</details>
|
||||
|
||||
<details><summary><b>pi</b>(earendil-works)</summary>
|
||||
|
||||
`./scripts/install.sh pi` → `~/.pi/agent/skills`、または pi の `skills` 設定をクローンに向けます。
|
||||
完全ガイド:[docs/pi-setup.md](docs/pi-setup.md)。
|
||||
</details>
|
||||
|
||||
<details><summary><b>GitHub Copilot</b></summary>
|
||||
|
||||
`./scripts/install.sh copilot --project` → `.github/skills`(`.claude/skills` も自動検出;
|
||||
`AGENTS.md` を読み取ります)。完全ガイド:[docs/copilot-setup.md](docs/copilot-setup.md)。
|
||||
</details>
|
||||
|
||||
<details><summary><b>Kiro</b>(AWS)</summary>
|
||||
|
||||
`./scripts/install.sh kiro` → `~/.kiro/skills`(`/brooks-review` を自動登録;`AGENTS.md` を読み取ります)。
|
||||
完全ガイド:[docs/kiro-setup.md](docs/kiro-setup.md)。
|
||||
</details>
|
||||
|
||||
<details><summary><b>Factory Droid</b></summary>
|
||||
|
||||
`./scripts/install.sh droid` → `~/.factory/skills`(`/brooks-review` を登録;`AGENTS.md` を読み取ります)。
|
||||
完全ガイド:[docs/factory-droid-setup.md](docs/factory-droid-setup.md)。
|
||||
</details>
|
||||
|
||||
> **🧪 検証状況。** Claude Code、Gemini CLI、Codex CLI はメンテナーによって検証済みです。上記の八つの
|
||||
> プラットフォームは各ツールの公式スキル仕様から文書化され、ファイルレイアウトのレベルで検証されています
|
||||
> (インストーラーはテスト済み)が、メンテナーがすべてのプラットフォームでエンドツーエンドに実行したわけ
|
||||
> ではまだありません。どれかを試した——動いた **または** 壊れた? プラットフォーム、バージョン、見たこと
|
||||
> を添えて [issue を立ててください](https://github.com/hyhmrright/brooks-lint/issues/new)。別の
|
||||
> Agent-Skills エージェント? ほぼ確実に同じように動作します——お知らせいただければ追加します。
|
||||
|
||||
## スラッシュコマンド
|
||||
|
||||
### Claude Code
|
||||
| コマンド | 短縮形 | アクション |
|
||||
|---------|------------|--------|
|
||||
| `/brooks-lint:brooks-review` | `/brooks-review` | PR レベルのコードレビュー |
|
||||
| `/brooks-lint:brooks-audit` | `/brooks-audit` | 完全なアーキテクチャ監査 |
|
||||
| `/brooks-lint:brooks-debt` | `/brooks-debt` | 技術的負債の評価 |
|
||||
| `/brooks-lint:brooks-test` | `/brooks-test` | テストスイートの健全性レビュー |
|
||||
| `/brooks-lint:brooks-health` | `/brooks-health` | 健全性ダッシュボード — 全四次元 |
|
||||
| `/brooks-lint:brooks-sweep` | `/brooks-sweep` | 全面スイープ — 全次元を分析し指摘を自動修正 |
|
||||
|
||||
> 短縮形コマンドは、session-start フックによって最初のセッション開始時に自動インストールされます。
|
||||
|
||||
### Gemini CLI
|
||||
| コマンド | アクション |
|
||||
|---------|--------|
|
||||
| `/brooks-review` | PR レベルのコードレビュー |
|
||||
| `/brooks-audit` | 完全なアーキテクチャ監査 |
|
||||
| `/brooks-debt` | 技術的負債の評価 |
|
||||
| `/brooks-test` | テストスイートの健全性レビュー |
|
||||
| `/brooks-health` | 健全性ダッシュボード — 全四次元 |
|
||||
| `/brooks-sweep` | 全面スイープ — 全次元を分析し指摘を自動修正 |
|
||||
|
||||
### Codex CLI
|
||||
|
||||
| コマンド | アクション |
|
||||
|---------|--------|
|
||||
| `$brooks-review` | PR レベルのコードレビュー |
|
||||
| `$brooks-audit` | 完全なアーキテクチャ監査 |
|
||||
| `$brooks-debt` | 技術的負債の評価 |
|
||||
| `$brooks-test` | テストスイートの健全性レビュー |
|
||||
| `$brooks-health` | 健全性ダッシュボード — 全四次元 |
|
||||
| `$brooks-sweep` | 全面スイープ — 全次元を分析し指摘を自動修正 |
|
||||
|
||||
コード品質、アーキテクチャ、保守性、テストの健全性について話すと、これらのスキルは自動的にもトリガーされます。
|
||||
|
||||
### OpenCode · Cursor · Antigravity · pi
|
||||
|
||||
これらのプラットフォームは、各スキルの `description` に基づいて Agent Skills を自動的に呼び出します——話しかけるだけ
|
||||
(「この PR をレビューして」「アーキテクチャを監査して」「うちの最悪の技術的負債はどこ?」)で、該当するモードが
|
||||
実行されます。明示的に呼び出すには、各プラットフォームのスキルコマンド構文を使います(例:pi は各スキルを
|
||||
`/skill:brooks-review` として登録します;Cursor と OpenCode はスキルが発見されると `/brooks-review` を公開します)。
|
||||
|
||||
## 使い方
|
||||
|
||||
### PR レビュー
|
||||
|
||||
```
|
||||
/brooks-review # Claude Code(短縮形)/ Gemini CLI
|
||||
/brooks-lint:brooks-review # Claude Code(完全形)
|
||||
$brooks-review # Codex CLI
|
||||
```
|
||||
|
||||
diff を貼り付けるか、AI を変更されたファイルに向けてください。六つの劣化リスクそれぞれを、症状 → 根源 → 結果 → 対策 の形式で具体的な指摘とともに診断します。
|
||||
|
||||
### アーキテクチャ監査
|
||||
|
||||
```
|
||||
/brooks-audit # Claude Code(短縮形)/ Gemini CLI
|
||||
/brooks-lint:brooks-audit # Claude Code(完全形)
|
||||
$brooks-audit # Codex CLI
|
||||
```
|
||||
|
||||
プロジェクト構造を説明するか、主要なファイルを共有してください。モジュールの依存関係をマッピングし、循環依存を特定し、コンウェイの法則との整合性をチェックします。
|
||||
|
||||
### 技術的負債の評価
|
||||
|
||||
```
|
||||
/brooks-debt # Claude Code(短縮形)/ Gemini CLI
|
||||
/brooks-lint:brooks-debt # Claude Code(完全形)
|
||||
$brooks-debt # Codex CLI
|
||||
```
|
||||
|
||||
あなたの負債を六つの劣化リスクにわたって分類し、各指摘を 痛み × 広がり の優先度で採点し、Critical / Scheduled / Monitored の分類を備えた優先順位付き返済ロードマップを生成します。
|
||||
|
||||
### テスト品質レビュー
|
||||
|
||||
```
|
||||
/brooks-test # Claude Code(短縮形)/ Gemini CLI
|
||||
/brooks-lint:brooks-test # Claude Code(完全形)
|
||||
$brooks-test # Codex CLI
|
||||
```
|
||||
|
||||
あなたのテストスイートを、六つのテスト空間の劣化リスク——テストの不透明性、テストの脆さ、テストの重複、モックの濫用、カバレッジの幻想、アーキテクチャの不一致——に照らして監査します。出典は xUnit Test Patterns、The Art of Unit Testing、How Google Tests Software、Working Effectively with Legacy Code です。PR レビューには、軽量な Step 7 のクイックテストチェックも自動的に含まれます(ドキュメントのみ、または非本番の diff ではスキップ)。
|
||||
|
||||
### 健全性ダッシュボード
|
||||
|
||||
```
|
||||
/brooks-health # Claude Code(短縮形)/ Gemini CLI
|
||||
/brooks-lint:brooks-health # Claude Code(完全形)
|
||||
$brooks-health # Codex CLI
|
||||
```
|
||||
|
||||
全四つの品質次元にわたって簡略化されたスキャンを実行し、重み付けされた総合健全性スコア(0〜100)を生成します。リリース前、新しいチームのオンボーディング時、あるいは「うちは今どうなっている?」という全体像レポートが欲しいときに使ってください。いずれかの次元についてより深い診断が必要な場合は、代わりに専用スキルを使ってください。
|
||||
|
||||
### 全面スイープ
|
||||
|
||||
```
|
||||
/brooks-sweep # Claude Code(短縮形)/ Gemini CLI
|
||||
/brooks-lint:brooks-sweep # Claude Code(完全形)
|
||||
$brooks-sweep # Codex CLI
|
||||
```
|
||||
|
||||
すべての本番(R1–R6)とテスト(T1–T6)の劣化リスク、加えてアーキテクチャを一度のパスでスキャンし、その後修正を適用します:安全な変更は即座に自動適用され、複数ファイルにまたがる、またはインターフェースに触れる変更は確認を必要とし、複雑なアーキテクチャ上の決定は手動対応項目としてフラグが立てられます。修正ログ、健全性スコアの差分、残存項目リストを出力します。
|
||||
|
||||
## 設定
|
||||
|
||||
レビューの挙動をカスタマイズするには、プロジェクトのルートに `.brooks-lint.yaml` を置きます:
|
||||
|
||||
```yaml
|
||||
version: 1
|
||||
|
||||
strictness: balanced # strict | balanced (default) | legacy-friendly — softer scoring for legacy code
|
||||
|
||||
disable:
|
||||
- T5 # skip coverage metrics check — we don't enforce coverage
|
||||
|
||||
severity:
|
||||
R1: suggestion # downgrade Cognitive Overload findings for this domain
|
||||
|
||||
ignore:
|
||||
- "**/*.generated.*"
|
||||
- "**/vendor/**"
|
||||
|
||||
# custom_risks: # define project-specific Cx codes — see skills/_shared/custom-risks-guide.md
|
||||
# suppress: # downgrade specific findings by risk + path (e.g. accepted legacy debt)
|
||||
```
|
||||
|
||||
出発点として [`.brooks-lint.example.yaml`](.brooks-lint.example.yaml) をコピーしてください。
|
||||
すべての設定は任意です——ファイルを完全に省略すればデフォルトの挙動になります。
|
||||
|
||||
| 設定 | 説明 |
|
||||
|---------|-------------|
|
||||
| `strictness` | スコアリングプリセット:`strict`、`balanced`(デフォルト)、または `legacy-friendly`(軽めの減点で、上位の修正を先頭に示す) |
|
||||
| `disable` | スキップするリスクコード(`R1`–`R6`、`T1`–`T6`) |
|
||||
| `severity` | 重大度ティアを上書き(`critical` / `warning` / `suggestion`) |
|
||||
| `ignore` | 除外するファイルの glob パターン |
|
||||
| `focus` | これらのリスクコードのみを評価(`disable` とは併用不可) |
|
||||
| `custom_risks` | プロジェクト固有のリスクコードを定義(`C1`、`C2`、…)——[`custom-risks-guide.md`](skills/_shared/custom-risks-guide.md) を参照 |
|
||||
| `suppress` | リスク + パスで特定の指摘を格下げ(任意の `expires:` 日付付き) |
|
||||
|
||||
---
|
||||
|
||||
## なぜこれらの書籍か、なぜ今か?
|
||||
|
||||
AI 支援コーディングの時代において、私たちはこれまで以上に速く、多くのコードを書いています。しかし、六十年にわたるソフトウェア工学の洞察は変わっていません:
|
||||
|
||||
> *"ソフトウェアの複雑性は本質的な性質であって、偶発的な性質ではない。"*
|
||||
> — Frederick Brooks
|
||||
|
||||
AI はコードをより速く書く手助けはできても、あなたが大聖堂を建てているのかタールの穴を掘っているのかは教えてくれません。**brooks-lint はそのギャップを埋めます**——十二冊の古典的ソフトウェア工学書から得られた、苦労して獲得された知恵を、あなたの現代的な開発ワークフローに持ち込みます。
|
||||
|
||||
これらの著者が特定した劣化リスクは、かつてないほど切実です:
|
||||
- **AI アシスタントを追加しても** 認知過負荷やドメインモデルの歪みは解消されません
|
||||
- **より多くのコードを生成すると** 変更の波及と知識の重複が増大します
|
||||
- **より速く動くことは** 偶発的複雑性と依存関係の無秩序をいっそう危険にします
|
||||
|
||||
## プロジェクト構成
|
||||
|
||||
```
|
||||
brooks-lint/
|
||||
├── .claude-plugin/ # Claude Code plugin metadata
|
||||
├── .codex-plugin/ # Codex CLI plugin metadata
|
||||
├── skills/
|
||||
│ ├── _shared/ # Shared framework files
|
||||
│ │ ├── common.md # Iron Law, Project Config, Report Template, Health Score
|
||||
│ │ ├── source-coverage.md # 12-book coverage matrix, tradeoffs, false-positive guards
|
||||
│ │ ├── decay-risks.md # Six decay risks with symptoms and book citations
|
||||
│ │ ├── test-decay-risks.md # Six test-space decay risks with book citations
|
||||
│ │ ├── remedy-guide.md # --fix mode: actionable Remedy enhancement rules
|
||||
│ │ └── custom-risks-guide.md # Template for project-specific risk codes
|
||||
│ ├── brooks-review/ # Mode 1: PR Review
|
||||
│ │ ├── SKILL.md
|
||||
│ │ └── pr-review-guide.md
|
||||
│ ├── brooks-audit/ # Mode 2: Architecture Audit
|
||||
│ │ ├── SKILL.md
|
||||
│ │ └── architecture-guide.md
|
||||
│ ├── brooks-debt/ # Mode 3: Tech Debt Assessment
|
||||
│ │ ├── SKILL.md
|
||||
│ │ └── debt-guide.md
|
||||
│ ├── brooks-test/ # Mode 4: Test Quality Review
|
||||
│ │ ├── SKILL.md
|
||||
│ │ └── test-guide.md
|
||||
│ ├── brooks-health/ # Mode 5: Health Dashboard
|
||||
│ │ ├── SKILL.md
|
||||
│ │ └── health-guide.md
|
||||
│ └── brooks-sweep/ # Mode 6: Full Sweep & Auto-Fix
|
||||
│ ├── SKILL.md
|
||||
│ └── sweep-guide.md
|
||||
├── hooks/ # SessionStart hook
|
||||
├── commands/ # Short-form command wrappers (auto-installed by hook)
|
||||
├── evals/ # Benchmark test cases
|
||||
│ └── evals.json
|
||||
└── assets/
|
||||
└── logo.svg
|
||||
```
|
||||
|
||||
## CI/CD 統合
|
||||
|
||||
GitHub Action を使って、すべての PR で brooks-lint を自動実行します:
|
||||
|
||||
```yaml
|
||||
# .github/workflows/brooks-lint.yml
|
||||
name: Brooks-Lint PR Review
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
brooks-lint:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: hyhmrright/brooks-lint/.github/actions/brooks-lint@main
|
||||
with:
|
||||
mode: review
|
||||
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
fail-below: 70
|
||||
```
|
||||
|
||||
完全なテンプレートは [`docs/github-action-example.yml`](docs/github-action-example.yml) を参照してください。
|
||||
|
||||
この Action はレビューを PR コメントとして投稿し、必要に応じて健全性スコアがしきい値を下回った場合にチェックを失敗させます。`.brooks-lint-history.json` がリポジトリにコミットされていれば、コメントにはトレンドの差分も含まれます(例:「85 → 82(−3)、直近 3 回の実行」)。
|
||||
|
||||
**品質ゲートと Code Scanning。** `fail-below` に加えて、この Action は次を公開しています:
|
||||
|
||||
```yaml
|
||||
with:
|
||||
mode: review
|
||||
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
fail-on: critical # fail on any Critical finding (none | warning | critical)
|
||||
fail-on-regression: true # fail if the Health Score dropped vs the last run
|
||||
sarif-file: brooks-lint.sarif # also upload findings to GitHub Code Scanning
|
||||
```
|
||||
|
||||
`fail-on-regression` は `.brooks-lint-history.json` を読み取るため、そのファイルをコミットすれば「新たな回帰なし」を強制できます。`sarif-file` を設定すると、指摘が PR の **Files changed** タブにインラインで表示されるようになり、ジョブに `security-events: write` 権限が必要になります。
|
||||
|
||||
**コスト:** PR 実行ごとにおよそ $0.05〜0.15、diff のサイズとモデルによります。`pull_request` イベントのみで実行することを推奨します。
|
||||
|
||||
## ロードマップ
|
||||
|
||||
> **現状(v1.4):** 12 冊の書籍を土台に、6 つの本番劣化リスク(R1–R6)+ 6 つのテスト劣化リスク(T1–T6)、6 つのスキル——PR レビュー、アーキテクチャ監査、技術的負債、テスト品質、健全性ダッシュボード、全面スイープ——に加え、CI 品質ゲート、GitHub Code Scanning 向けの SARIF 出力、厳格度プリセット、そして再現可能なパーサー忠実度ベンチマーク。下の以前のエントリは歴史的なマイルストーンを記述したものであり、現在の機能セットではありません。
|
||||
|
||||
- [x] **v0.2**:プラグインインフラ(`.claude-plugin/`、フック、スラッシュコマンド)
|
||||
- [x] **v0.3**:八つの Brooks 次元、ドキュメント完全性スコアリング
|
||||
- [x] **v0.4**:六冊の書籍フレームワーク、劣化リスク次元、診断チェーン、ベンチマークスイート
|
||||
- [x] **v0.5**:テスト品質レビュー(モード 4)——四冊のテスト書籍、六つのテスト劣化リスク
|
||||
- [x] **v0.6**:アーキテクチャ監査における Mermaid 依存関係グラフ
|
||||
- [x] **v0.7**:`.brooks-lint.yaml` プロジェクト設定、モード 2 のプロアクティブコンテキスト、10 冊への拡張
|
||||
- [x] **v0.8**:名前空間付きコマンドを備えた独立スキルアーキテクチャ
|
||||
- [x] **v0.9**:ステップ検証、自動 diff スコープ、`/brooks-health` ダッシュボード、トレンド追跡、トリアージモード、`--fix` 対策、オンボーディングレポート、GitHub Action
|
||||
- [x] **v1.0**:eval 自動化(`run-evals-live.mjs`)、カスタムリスク拡張(`Cx` コード)
|
||||
- [x] **v1.1**:全面スイープスキル(`brooks-sweep`)——統合された多次元自動修正
|
||||
- [x] **v1.2**:自律的なスイープパイプライン、`npm run bump` によるバージョン伝播
|
||||
- [x] **v1.3**:Codex マーケットプレイスメタデータ、複数のエージェントプラットフォーム向けのワンコマンドインストーラー、バイリンガル README + ランディングサイト
|
||||
- [x] **v1.4**:GitHub Code Scanning 向けの SARIF 出力、CI の severity + 回帰ゲート、厳格度プリセット(strict/balanced/legacy-friendly)、57 シナリオの eval スイート、再現可能なパーサー忠実度ベンチマーク(`npm run benchmark`)
|
||||
|
||||
手伝いたいですか? 今もっとも価値ある貢献は、新しい eval テストケースと、より優れた劣化リスクの症状パターンです。[CONTRIBUTING.md](CONTRIBUTING.md) を参照してください。
|
||||
|
||||
## 貢献
|
||||
|
||||
指摘の追加、ガイドの改善、ベンチマークスイートの拡張の方法は [CONTRIBUTING.md](CONTRIBUTING.md) を参照してください。
|
||||
|
||||
あなた自身の PR で `/brooks-review` を実行してください——私たちは、作っているそのツールで貢献をレビューしています。
|
||||
|
||||
## ライセンス
|
||||
|
||||
MIT License——詳細は [LICENSE](LICENSE) を参照してください。
|
||||
|
||||
## 謝辞
|
||||
|
||||
このプロジェクトは十二人の巨人の肩の上に立っています:
|
||||
|
||||
**本番コードフレームワーク**
|
||||
- Frederick P. Brooks Jr. — *The Mythical Man-Month*(1975、記念版 1995)
|
||||
- Steve McConnell — *Code Complete*(1993、第 2 版 2004)
|
||||
- Martin Fowler — *Refactoring*(1999、第 2 版 2018)
|
||||
- Robert C. Martin — *Clean Architecture*(2017)
|
||||
- Andrew Hunt & David Thomas — *The Pragmatic Programmer*(1999、20 周年版 2019)
|
||||
- Eric Evans — *Domain-Driven Design*(2003)
|
||||
- John Ousterhout — *A Philosophy of Software Design*(2018)
|
||||
- Titus Winters, Tom Manshreck, Hyrum Wright — *Software Engineering at Google*(2020)
|
||||
|
||||
**テスト品質フレームワーク**
|
||||
- Gerard Meszaros — *xUnit Test Patterns*(2007)
|
||||
- Roy Osherove — *The Art of Unit Testing*(2009、第 3 版 2023)
|
||||
- Google Engineering — *How Google Tests Software*(2012)
|
||||
- Michael Feathers — *Working Effectively with Legacy Code*(2004)
|
||||
|
||||
このツールにエンコードされた劣化リスクは、彼らのアイデアを統合し、現代のコード品質評価に応用した私たちの成果です。
|
||||
|
||||
---
|
||||
|
||||
## スター履歴
|
||||
|
||||
[](https://star-history.com/#hyhmrright/brooks-lint&Date)
|
||||
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
<strong>⭐ このツールがあなたのコードベースを違った目で見る助けになったなら、スターをお願いします!</strong>
|
||||
</p>
|
||||
@@ -0,0 +1,691 @@
|
||||
<p align="center">
|
||||
<img src="assets/logo.svg" alt="brooks-lint" width="200">
|
||||
</p>
|
||||
|
||||
<h1 align="center">brooks-lint</h1>
|
||||
|
||||
<p align="center">
|
||||
<strong>열두 권의 고전 엔지니어링 도서에 뿌리를 둔 AI 코드 리뷰.<br>
|
||||
일관적이고, 추적 가능하며, 실행 가능합니다.</strong>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="README.md">English</a> ·
|
||||
<a href="README.zh-CN.md">简体中文</a> ·
|
||||
<a href="README.zh-TW.md">繁體中文</a> ·
|
||||
<a href="README.ja.md">日本語</a> ·
|
||||
<strong>한국어</strong> ·
|
||||
<a href="README.es.md">Español</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="#빠른-시작">빠른 시작</a> •
|
||||
<a href="#여섯-가지-쇠퇴-위험">여섯 가지 쇠퇴 위험</a> •
|
||||
<a href="#실제-결과물">실제 결과물</a> •
|
||||
<a href="#벤치마크">벤치마크</a> •
|
||||
<a href="#설치">설치</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-1.4.0-blue.svg" alt="Version">
|
||||
<img src="https://img.shields.io/badge/license-MIT-green.svg" alt="MIT License">
|
||||
<img src="https://img.shields.io/badge/Claude_Code-Plugin-blueviolet.svg" alt="Claude Code Plugin">
|
||||
<img src="https://img.shields.io/badge/Codex_CLI-Skill-orange.svg" alt="Codex CLI Skill">
|
||||
<img src="https://img.shields.io/github/stars/hyhmrright/brooks-lint?style=social" alt="GitHub Stars">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://trendshift.io/repositories/47738" target="_blank"><img src="https://trendshift.io/api/badge/trendshift/repositories/47738/daily?language=JavaScript" alt="Trendshift 오늘의 JavaScript 저장소 2위" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="assets/banner-ko.svg" alt="당신의 코드 → 12권의 고전 → 12가지 쇠퇴 위험 → 출처가 명시된 진단" width="900">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://hyhmrright.github.io/brooks-lint/"><img src="assets/demo.gif" alt="brooks-lint의 코드 리뷰: /brooks-review 명령 한 번으로 28/100 건강 점수와 출처가 인용된 증상 → 근원 → 결과 → 처방 진단을 산출" width="820"></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<strong><a href="https://hyhmrright.github.io/brooks-lint/">→ 웹사이트 방문하기</a></strong>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
> *"아이를 낳는 데는 아홉 달이 걸린다. 몇 명의 여성을 투입하든 마찬가지다."*
|
||||
> — Frederick Brooks, *The Mythical Man-Month*(맨먼스 미신, 1975)
|
||||
|
||||
**50년이 지난 지금도 Brooks는 여전히 옳았습니다 — 그리고 McConnell, Fowler, Martin, Hunt & Thomas, Evans, Ousterhout, Winters, Meszaros, Osherove, Feathers, 그리고 Google 테스팅 팀 역시 마찬가지였습니다.**
|
||||
|
||||
대부분의 코드 품질 도구는 줄 수와 순환 복잡도만 셉니다. **brooks-lint**는 한 걸음 더 나아갑니다 — 열두 권의 고전 엔지니어링 도서에서 종합한 여섯 가지 쇠퇴 위험 차원에 비추어 당신의 코드를 진단하며, 매번 도서 출처, 심각도 라벨, 구체적인 처방이 담긴 구조화된 진단을 산출합니다.
|
||||
|
||||
예외와 오탐 방지 장치를 포함한 전체 "출처-스킬" 매핑은
|
||||
[`skills/_shared/source-coverage.md`](skills/_shared/source-coverage.md)를 참고하세요.
|
||||
|
||||
## 빠른 시작
|
||||
|
||||
```bash
|
||||
# Claude Code
|
||||
/plugin marketplace add hyhmrright/brooks-lint
|
||||
/plugin install brooks-lint@brooks-lint-marketplace
|
||||
|
||||
# Any other Agent Skills platform — Cursor · Codex · Gemini · Copilot · Windsurf · OpenCode · Kiro · …
|
||||
curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- <platform>
|
||||
```
|
||||
|
||||
설치한 뒤에는 그냥 요청하거나("이 PR을 리뷰해줘", "아키텍처를 감사해줘") — 명령을 실행하세요:
|
||||
|
||||
| 명령 | 하는 일 |
|
||||
|---------|--------------|
|
||||
| `/brooks-review` | PR 또는 diff 리뷰 |
|
||||
| `/brooks-audit` | 아키텍처 감사(+ Mermaid 의존성 그래프) |
|
||||
| `/brooks-debt` | 우선순위가 매겨진 기술 부채 로드맵 |
|
||||
| `/brooks-test` | 테스트 스위트 품질 리뷰 |
|
||||
| `/brooks-health` | 모든 차원에 걸친 건강 대시보드 |
|
||||
| `/brooks-sweep` | 전 차원을 훑어 진단 결과를 자동 수정 |
|
||||
|
||||
모든 진단은 도서 출처와 0–100 건강 점수와 함께 **증상 → 근원 → 결과 → 처방** 형태로 돌아옵니다. 전체 설치 옵션(추가 8개 플랫폼), 명령별 사용법, CI/CD 설정은 [아래](#설치)를 참고하세요.
|
||||
|
||||
## 열두 권의 책
|
||||
|
||||
| 책 | 저자 | 기여하는 위험 |
|
||||
|------|--------|----------------|
|
||||
| *The Mythical Man-Month*(맨먼스 미신) | Frederick Brooks | R2, R4, R5 |
|
||||
| *Code Complete*(코드 컴플리트) | Steve McConnell | R1, R4 |
|
||||
| *Refactoring*(리팩토링) | Martin Fowler | R1, R2, R3, R4, R6 |
|
||||
| *Clean Architecture*(클린 아키텍처) | Robert C. Martin | R2, R5 |
|
||||
| *The Pragmatic Programmer*(실용주의 프로그래머) | Hunt & Thomas | R2, R3, R4, R5, T2, T3 |
|
||||
| *Domain-Driven Design*(도메인 주도 설계) | Eric Evans | R1, R3, R6 |
|
||||
| *A Philosophy of Software Design*(소프트웨어 설계의 철학) | John Ousterhout | R1, R4 |
|
||||
| *Software Engineering at Google*(구글 엔지니어링 best practice) | Winters, Manshreck & Wright | R2, R5 |
|
||||
| *The Art of Unit Testing*(단위 테스트의 기술) | Roy Osherove | T1, T2, T4, T5 |
|
||||
| *How Google Tests Software*(구글은 소프트웨어를 어떻게 테스트하는가) | James A. Whittaker, Jason Arbon & Jeff Carollo | T5, T6 |
|
||||
| *Working Effectively with Legacy Code*(레거시 코드 활용 전략) | Michael Feathers | T4, T5, T6 |
|
||||
| *xUnit Test Patterns*(xUnit 테스트 패턴) | Gerard Meszaros | T1, T2, T3, T4 |
|
||||
|
||||
## 여섯 가지 쇠퇴 위험
|
||||
|
||||
brooks-lint는 열두 권의 고전 엔지니어링 도서에서 종합한 **여섯 가지 프로덕션 코드 쇠퇴 위험**과 **여섯 가지 테스트 스위트 쇠퇴 위험**의 관점에서 당신의 코드를 평가합니다:
|
||||
|
||||
| 쇠퇴 위험 | 진단 질문 | 출처 |
|
||||
|------------|---------------------|---------|
|
||||
| 🧠 인지 과부하 | 이 코드를 이해하는 데 얼마나 많은 정신적 노력이 드는가? | Code Complete, Refactoring, DDD, Philosophy of SD |
|
||||
| 🔗 변경 전파 | 한 곳을 고치면 관련 없는 것이 얼마나 깨지는가? | Refactoring, Clean Architecture, Pragmatic, SE@Google |
|
||||
| 📋 지식 중복 | 같은 결정이 여러 곳에서 표현되고 있는가? | Pragmatic, Refactoring, DDD |
|
||||
| 🌀 우발적 복잡도 | 코드가 문제 자체보다 더 복잡한가? | Refactoring, Code Complete, Brooks, Philosophy of SD |
|
||||
| 🏗️ 의존성 무질서 | 의존성이 일관된 방향으로 흐르는가? | Clean Architecture, Brooks, Pragmatic, SE@Google |
|
||||
| 🗺️ 도메인 모델 왜곡 | 코드가 도메인을 충실히 표현하는가? | DDD, Refactoring |
|
||||
|
||||
> Philosophy of SD = *A Philosophy of Software Design*(Ousterhout) · SE@Google = *Software Engineering at Google*(Winters 외)
|
||||
|
||||
## 실제 결과물
|
||||
|
||||
다음 코드가 주어졌을 때:
|
||||
|
||||
```python
|
||||
class UserService:
|
||||
def update_profile(self, user_id, name, email, avatar_url):
|
||||
user = self.db.query(f"SELECT * FROM users WHERE id = {user_id}")
|
||||
user['email'] = email
|
||||
...
|
||||
if user['email'] != email: # always False — silent bug
|
||||
self.smtp.send(...)
|
||||
points = user['login_count'] * 10 + 500
|
||||
self.db.execute(f"UPDATE loyalty SET points={points} WHERE user_id={user_id}")
|
||||
```
|
||||
|
||||
brooks-lint는 다음을 산출합니다:
|
||||
|
||||
---
|
||||
|
||||
**건강 점수: 28/100**
|
||||
|
||||
*이 메서드는 서로 무관한 네 가지 비즈니스 책임을 하나의 함수에 집중시키고, 이메일 변경 알림을 조용히 억제하는 논리 버그를 포함하며, SQL 인젝션에 무방비로 노출되어 있습니다.*
|
||||
|
||||
### 🔴 변경 전파 — 단일 메서드가 서로 무관한 네 가지 비즈니스 이유로 변경됨
|
||||
**증상:** `update_profile`이 프로필 필드 업데이트, 이메일 변경 알림, 적립 포인트 재계산, 캐시 무효화를 모두 하나의 메서드 본문에서 수행합니다.
|
||||
**근원:** Fowler — *Refactoring* — 발산적 변경(Divergent Change); Hunt & Thomas — *The Pragmatic Programmer* — 직교성(Orthogonality)
|
||||
**결과:** 적립 포인트 공식에 대한 어떤 변경이든 이메일 알림을 깨뜨릴 위험이 있고, 그 반대도 마찬가지입니다. 모든 수정이 서로 무관한 네 도메인에 걸친 회귀 위험을 동시에 떠안습니다.
|
||||
**처방:** `NotificationService`, `LoyaltyService`, `UserCacheInvalidator`를 추출하세요. `UserService.update_profile`은 각각을 호출하며 조율하는 역할만 해야 하며 — 그 자체로는 어떤 구현 로직도 보유해서는 안 됩니다.
|
||||
|
||||
### 🔴 도메인 모델 왜곡 — 조용한 논리 버그: 이메일 알림이 결코 발송되지 않음
|
||||
**증상:** `user['email'] = email`이 `if user['email'] != email`보다 먼저 옛 값을 덮어쓰므로 — 조건이 항상 `False`입니다. 알림은 죽은 코드입니다.
|
||||
**근원:** McConnell — *Code Complete* — 17장: 비정상적 제어 구조
|
||||
**결과:** 사용자가 이메일 주소를 변경해도 결코 알림을 받지 못합니다. 조용한 데이터 무결성 실패입니다 — 시스템은 정상 동작하는 것처럼 보이지만 실제로는 비즈니스 규칙을 위반하고 있습니다.
|
||||
**처방:** 어떤 변경이든 그 전에 `old_email = user['email']`을 포착하세요. `user['email']`이 아니라 `old_email`과 비교하세요.
|
||||
|
||||
*(SQL 인젝션, 의존성 무질서, 매직 넘버를 포함해 6개 진단 추가)*
|
||||
|
||||
### 의존성 그래프를 포함한 아키텍처 감사
|
||||
|
||||
모드 2(아키텍처 감사)에서 brooks-lint는 보고서 상단에 **Mermaid 의존성 그래프**를 생성합니다. 모듈은 심각도에 따라 색으로 구분됩니다: 빨강 = Critical 진단, 노랑 = Warning, 초록 = 깨끗함.
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph src/api
|
||||
AuthController
|
||||
UserController
|
||||
end
|
||||
subgraph src/domain
|
||||
UserService
|
||||
OrderService
|
||||
end
|
||||
subgraph src/infra
|
||||
Database
|
||||
EmailClient
|
||||
end
|
||||
|
||||
AuthController --> UserService
|
||||
UserController --> UserService
|
||||
UserController --> OrderService
|
||||
OrderService --> UserService
|
||||
OrderService --> EmailClient
|
||||
UserService --> Database
|
||||
EmailClient -.->|circular| OrderService
|
||||
|
||||
classDef critical fill:#ff6b6b,stroke:#c92a2a,color:#fff
|
||||
classDef warning fill:#ffd43b,stroke:#e67700
|
||||
classDef clean fill:#51cf66,stroke:#2b8a3e,color:#fff
|
||||
|
||||
class OrderService,EmailClient critical
|
||||
class AuthController warning
|
||||
class UserService,UserController,Database clean
|
||||
```
|
||||
|
||||
이 그래프는 GitHub, Notion 등 Markdown 환경에서 별도 도구 없이 네이티브로 렌더링됩니다.
|
||||
|
||||
## 더 많은 예시 보기
|
||||
|
||||
[전체 갤러리](docs/gallery.md)에는 Python, TypeScript, Go, Java에 걸친 실제 brooks-lint 출력이 담겨 있습니다 — PR 리뷰, Mermaid 의존성 그래프가 포함된 아키텍처 감사, 기술 부채 평가, 테스트 품질 리뷰를 망라합니다.
|
||||
|
||||
쇠퇴 위험이 처음이신가요? [**쇠퇴 위험 실전 가이드**](https://hyhmrright.github.io/brooks-lint/guide.html)가 여섯 가지를 모두 설명합니다 — 각각의 진단 질문, 대표 증상, 출처 도서, 처방을 다룹니다.
|
||||
|
||||
---
|
||||
|
||||
## 벤치마크
|
||||
|
||||
3개의 실제 시나리오(PR 리뷰, 아키텍처 감사, 기술 부채 평가)에서 테스트했습니다:
|
||||
|
||||
| 평가 항목 | brooks-lint | Claude 단독 |
|
||||
|-----------|:-----------:|:------------:|
|
||||
| 구조화된 진단(증상 → 근원 → 결과 → 처방) | ✅ 100% | ❌ 0% |
|
||||
| 진단마다 도서 출처 | ✅ 100% | ❌ 0% |
|
||||
| 심각도 라벨(🔴/🟡/🟢) | ✅ 100% | ❌ 0% |
|
||||
| 건강 점수(0–100) | ✅ 100% | ❌ 0% |
|
||||
| 변경 전파 탐지 | ✅ 100% | ✅ 100% |
|
||||
| **전체 통과율** | **94%** | **16%** |
|
||||
|
||||
격차는 Claude가 무엇을 *발견할 수 있는가*가 아니라 — 매번 추적 가능한 근거와 실행 가능한 처방을 곁들여 무엇을 *일관되게* 발견하는가에 있습니다.
|
||||
|
||||
### 재현 가능한 벤치마크
|
||||
|
||||
위 표는 예시용입니다. 아래 수치들은 **확정적이며 로컬에서 직접 재현할 수 있습니다**:
|
||||
|
||||
**파서 충실도** — SARIF 내보내기와 CI 게이트는 모델의 Markdown 보고서를 올바르게 파싱하는 데 달려 있습니다. 여섯 가지 모드 전체를 아우르는 **30개의 실제 모델 생성 보고서로 구성된 동결 코퍼스**(`evals/benchmark-corpus.json`)에 대해, 각각 **독립적으로 채점된** 진단 목록(별도의 모델 패스로 채점한 뒤 수작업으로 표본 검증)과 짝지어, 실제 배포되는 파서의 점수는 다음과 같습니다 — `npm run benchmark`를 실행하세요:
|
||||
|
||||
| 지표(n = 30, 동결 코퍼스) | 결과 |
|
||||
|---|:---:|
|
||||
| 심각도 카운트 정확 일치(파서 vs 채점된 진실값) | 30 / 30 |
|
||||
| 위험 코드 precision / recall | 100% / 100%(56개 finding-level 코드, 0 FP / 0 FN) |
|
||||
| 유효한 SARIF 2.1.0 산출 | 30 / 30 |
|
||||
|
||||
파서가 결정론적이고 코퍼스가 동결되어 있기 때문에 `npm run benchmark`는 누구에게나 동일한 결과를 주며, `npm test`가 이를 회귀 테스트로 지킵니다. 이 코퍼스는 깨끗하게 유지되어야 하는 9개의 오탐 / 트레이드오프 보고서(예: 의존성 순환처럼 *보이지만* 실제로는 포트-앤-어댑터 설계인 경우)를 의도적으로 포함합니다.
|
||||
|
||||
**점수 결정성** — 고정된 진단 집합(2 Critical / 3 Warning / 1 Suggestion)에 대해, strictness 프리셋은 각자의 `common.md` 표가 예측하는 점수를 정확히 산출합니다: strict **34**, balanced **54**, legacy-friendly **74** — 그리고 `legacy-friendly`만이 상위 세 개의 수정으로 시작합니다.
|
||||
|
||||
**모델 품질** — 모델이 실제 코드에서 *올바른* 위험을 찾아내는지는 **57개 시나리오 eval 스위트**(`evals/evals.json`)로 측정합니다: `npm run evals`(구조 검증)와 `npm run evals:live`(실측, `ANTHROPIC_API_KEY` 필요).
|
||||
|
||||
> 범위와 정직성: 파서 수치는 결정론적이며 정확히 재현됩니다. strictness 및 eval 스위트 수치는 모델에 대한 단일 실행 실측치로, 실행마다 약간씩 변동합니다. 파서 벤치마크는 보고서 파싱 충실도(도구가 보고서에 기재된 모든 진단을 읽어내는가?)를 측정하는 것이지, 특정 진단이 "옳은지"를 측정하는 것이 아닙니다. 심각도 카운트 일치가 완전히 독립적인 신호입니다; 위험 코드 일치는 공유된 표준 name→code 범례 또한 반영합니다.
|
||||
|
||||
## 비교 우위
|
||||
|
||||
| | brooks-lint | ESLint / Pylint | GitHub Copilot Review | 순수 Claude |
|
||||
|---|:---:|:---:|:---:|:---:|
|
||||
| 문법 및 스타일 문제 탐지 | — | ✅ | ✅ | ~ |
|
||||
| 구조화된 진단 체인 | ✅ | ❌ | ❌ | ❌ |
|
||||
| 진단을 고전 도서로 추적 | ✅ | ❌ | ❌ | ❌ |
|
||||
| 일관된 심각도 라벨 | ✅ | ✅ | ~ | ❌ |
|
||||
| 아키텍처 수준의 통찰 | ✅ | ❌ | ~ | ~ |
|
||||
| 도메인 모델 분석 | ✅ | ❌ | ❌ | ~ |
|
||||
| 무설정, 설치할 플러그인 없음 | ✅ | ❌ | ✅ | ✅ |
|
||||
| 어떤 언어에서도 동작 | ✅ | ❌ | ✅ | ✅ |
|
||||
|
||||
> `~` = 가끔 / 일관되지 않음
|
||||
|
||||
**brooks-lint는 당신의 linter를 대체하지 않습니다.** 그것은 linter가 잡을 수 없는 것을 포착합니다: 아키텍처 표류, 지식 사일로, 도메인 모델 왜곡 — 누군가 알아채기 전 몇 달 동안 팀을 더디게 만드는 문제들입니다.
|
||||
|
||||
## 설치
|
||||
|
||||
### Claude Code(권장)
|
||||
|
||||
#### 플러그인 마켓플레이스를 통해
|
||||
```bash
|
||||
/plugin marketplace add hyhmrright/brooks-lint
|
||||
/plugin install brooks-lint@brooks-lint-marketplace
|
||||
```
|
||||
|
||||
단축 명령(`/brooks-review`)은 첫 세션 시작 시 자동으로 설치됩니다. 수동으로 설치하려면:
|
||||
```bash
|
||||
cp commands/*.md ~/.claude/commands/
|
||||
```
|
||||
|
||||
#### 수동 설치
|
||||
```bash
|
||||
mkdir -p ~/.claude/skills/brooks-lint
|
||||
cp -r skills/* ~/.claude/skills/brooks-lint/
|
||||
```
|
||||
|
||||
### Gemini CLI
|
||||
|
||||
#### 확장을 통해
|
||||
```bash
|
||||
/extensions install https://github.com/hyhmrright/brooks-lint
|
||||
```
|
||||
|
||||
#### 수동 설치
|
||||
```bash
|
||||
mkdir -p ~/.gemini/skills
|
||||
cp -r skills/* ~/.gemini/skills/ # flat — Gemini discovers skills only one level deep
|
||||
```
|
||||
> 또는 간단히: `./scripts/install.sh gemini`
|
||||
|
||||
### Codex CLI
|
||||
|
||||
#### 스킬 설치기를 통해(Codex 세션 안에서)
|
||||
```
|
||||
Install the brooks-lint skill from hyhmrright/brooks-lint
|
||||
```
|
||||
|
||||
#### 커맨드 라인
|
||||
```bash
|
||||
python3 ~/.codex/skills/.system/skill-installer/scripts/install-skill-from-github.py \
|
||||
--repo hyhmrright/brooks-lint --path skills --name brooks-lint
|
||||
```
|
||||
|
||||
#### 수동 설치
|
||||
```bash
|
||||
git clone https://github.com/hyhmrright/brooks-lint.git /tmp/brooks-lint
|
||||
mkdir -p ~/.codex/skills
|
||||
cp -r /tmp/brooks-lint/skills/* ~/.codex/skills/ # flat — matches the skill-installer layout
|
||||
```
|
||||
> 또는 간단히: `./scripts/install.sh codex`
|
||||
|
||||
### 더 많은 플랫폼 — OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid
|
||||
|
||||
brooks-lint는 표준 [Agent Skills](https://agentskills.io) 형태로 배포됩니다. **Agent
|
||||
Skills를 로드하는 모든 에이전트는 변환 없이 여섯 가지 모드를 모두 실행합니다** — 한 줄의 명령으로 설치됩니다:
|
||||
|
||||
```bash
|
||||
# pick your platform; --project installs into the current repo instead of your global config
|
||||
curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- <platform>
|
||||
# <platform> = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · gemini · codex · agents
|
||||
```
|
||||
|
||||
설치기는 스킬을 당신의 플랫폼에 맞는 폴더로 **평평하게** 복사하므로, 공유
|
||||
프레임워크(`../_shared/`)가 항상 올바르게 해석됩니다 — 레이아웃을 잘못 잡을 수가 없습니다. 그런 다음 그냥 요청하면
|
||||
("이 PR을 리뷰해줘", "아키텍처를 감사해줘"), 일치하는 스킬이 자신의
|
||||
`description`에서 자동으로 트리거됩니다. 스킬이 처음이거나 다른 에이전트를 쓰시나요? **[docs/getting-started.md](docs/getting-started.md)**를 참고하세요.
|
||||
|
||||
<details><summary><b>OpenCode</b></summary>
|
||||
|
||||
`./scripts/install.sh opencode` → `~/.config/opencode/skills`(`~/.claude/skills`와
|
||||
`AGENTS.md`도 읽음). 전체 가이드: [docs/opencode-setup.md](docs/opencode-setup.md).
|
||||
</details>
|
||||
|
||||
<details><summary><b>Cursor</b> (2.4+)</summary>
|
||||
|
||||
`./scripts/install.sh cursor` → `~/.cursor/skills`(`.agents/skills`도; `AGENTS.md` 읽음).
|
||||
전체 가이드: [docs/cursor-setup.md](docs/cursor-setup.md).
|
||||
</details>
|
||||
|
||||
<details><summary><b>Windsurf</b> (Cascade)</summary>
|
||||
|
||||
`./scripts/install.sh windsurf` → `~/.codeium/windsurf/skills`(`AGENTS.md` 읽음).
|
||||
전체 가이드: [docs/windsurf-setup.md](docs/windsurf-setup.md).
|
||||
</details>
|
||||
|
||||
<details><summary><b>Antigravity</b> (Google)</summary>
|
||||
|
||||
`./scripts/install.sh antigravity --project` → `.agent/skills`(`AGENTS.md` / `GEMINI.md` 읽음).
|
||||
전체 가이드: [docs/antigravity-setup.md](docs/antigravity-setup.md).
|
||||
</details>
|
||||
|
||||
<details><summary><b>pi</b> (earendil-works)</summary>
|
||||
|
||||
`./scripts/install.sh pi` → `~/.pi/agent/skills`, 또는 pi의 `skills` 설정이 클론을 가리키게 하세요.
|
||||
전체 가이드: [docs/pi-setup.md](docs/pi-setup.md).
|
||||
</details>
|
||||
|
||||
<details><summary><b>GitHub Copilot</b></summary>
|
||||
|
||||
`./scripts/install.sh copilot --project` → `.github/skills`(`.claude/skills`도 자동 감지; `AGENTS.md`
|
||||
읽음). 전체 가이드: [docs/copilot-setup.md](docs/copilot-setup.md).
|
||||
</details>
|
||||
|
||||
<details><summary><b>Kiro</b> (AWS)</summary>
|
||||
|
||||
`./scripts/install.sh kiro` → `~/.kiro/skills`(`/brooks-review` 자동 등록; `AGENTS.md` 읽음).
|
||||
전체 가이드: [docs/kiro-setup.md](docs/kiro-setup.md).
|
||||
</details>
|
||||
|
||||
<details><summary><b>Factory Droid</b></summary>
|
||||
|
||||
`./scripts/install.sh droid` → `~/.factory/skills`(`/brooks-review` 등록; `AGENTS.md` 읽음).
|
||||
전체 가이드: [docs/factory-droid-setup.md](docs/factory-droid-setup.md).
|
||||
</details>
|
||||
|
||||
> **🧪 검증 상태.** Claude Code, Gemini CLI, Codex CLI는 메인테이너가 검증했습니다. 위
|
||||
> 여덟 개 플랫폼은 각 도구의 공식 스킬 명세를 토대로 문서화되었고 파일 레이아웃
|
||||
> 수준에서 검증되었으나(설치기는 테스트되었음), 메인테이너가 모든 플랫폼에서 end-to-end로 직접 실행해 보지는
|
||||
> 못했습니다. 어떤 것을 시도해 보셨나요 — 잘 되든 **안 되든**? 플랫폼, 버전, 본 결과를 담아
|
||||
> [이슈를 열어 주세요](https://github.com/hyhmrright/brooks-lint/issues/new).
|
||||
> 다른 Agent-Skills 에이전트인가요? 거의 확실히 같은
|
||||
> 방식으로 동작합니다 — 알려주시면 추가하겠습니다.
|
||||
|
||||
## 슬래시 명령
|
||||
|
||||
### Claude Code
|
||||
| 명령 | 단축형 | 동작 |
|
||||
|---------|------------|--------|
|
||||
| `/brooks-lint:brooks-review` | `/brooks-review` | PR 수준 코드 리뷰 |
|
||||
| `/brooks-lint:brooks-audit` | `/brooks-audit` | 전체 아키텍처 감사 |
|
||||
| `/brooks-lint:brooks-debt` | `/brooks-debt` | 기술 부채 평가 |
|
||||
| `/brooks-lint:brooks-test` | `/brooks-test` | 테스트 스위트 건강 리뷰 |
|
||||
| `/brooks-lint:brooks-health` | `/brooks-health` | 건강 대시보드 — 네 가지 차원 전체 |
|
||||
| `/brooks-lint:brooks-sweep` | `/brooks-sweep` | 전면 스윕 — 모든 차원을 분석하고 진단을 자동 수정 |
|
||||
|
||||
> 단축 명령은 session-start 훅이 첫 세션 시작 시 자동으로 설치합니다.
|
||||
|
||||
### Gemini CLI
|
||||
| 명령 | 동작 |
|
||||
|---------|--------|
|
||||
| `/brooks-review` | PR 수준 코드 리뷰 |
|
||||
| `/brooks-audit` | 전체 아키텍처 감사 |
|
||||
| `/brooks-debt` | 기술 부채 평가 |
|
||||
| `/brooks-test` | 테스트 스위트 건강 리뷰 |
|
||||
| `/brooks-health` | 건강 대시보드 — 네 가지 차원 전체 |
|
||||
| `/brooks-sweep` | 전면 스윕 — 모든 차원을 분석하고 진단을 자동 수정 |
|
||||
|
||||
### Codex CLI
|
||||
|
||||
| 명령 | 동작 |
|
||||
|---------|--------|
|
||||
| `$brooks-review` | PR 수준 코드 리뷰 |
|
||||
| `$brooks-audit` | 전체 아키텍처 감사 |
|
||||
| `$brooks-debt` | 기술 부채 평가 |
|
||||
| `$brooks-test` | 테스트 스위트 건강 리뷰 |
|
||||
| `$brooks-health` | 건강 대시보드 — 네 가지 차원 전체 |
|
||||
| `$brooks-sweep` | 전면 스윕 — 모든 차원을 분석하고 진단을 자동 수정 |
|
||||
|
||||
코드 품질, 아키텍처, 유지보수성, 테스트 건강에 대해 이야기할 때도 이 스킬들은 자동으로 트리거됩니다.
|
||||
|
||||
### OpenCode · Cursor · Antigravity · pi
|
||||
|
||||
이 플랫폼들은 각 스킬의 `description`에서 Agent Skills를 자동으로 호출합니다 — 그냥 요청하면
|
||||
("이 PR을 리뷰해줘", "아키텍처를 감사해줘", "우리의 최악의 기술 부채는 어디 있지?") 일치하는 모드가
|
||||
실행됩니다. 명시적으로 호출하려면 해당 플랫폼의 스킬 명령 구문을 사용하세요(예: pi는 각 스킬을
|
||||
`/skill:brooks-review`로 등록하고; Cursor와 OpenCode는 스킬이 발견되면 `/brooks-review`를 노출합니다).
|
||||
|
||||
## 사용법
|
||||
|
||||
### PR 리뷰
|
||||
|
||||
```
|
||||
/brooks-review # Claude Code (short form) / Gemini CLI
|
||||
/brooks-lint:brooks-review # Claude Code (full form)
|
||||
$brooks-review # Codex CLI
|
||||
```
|
||||
|
||||
diff를 붙여 넣거나 AI가 변경된 파일을 가리키게 하세요. 여섯 가지 쇠퇴 위험 각각을 증상 → 근원 → 결과 → 처방 형식의 구체적인 진단으로 진단합니다.
|
||||
|
||||
### 아키텍처 감사
|
||||
|
||||
```
|
||||
/brooks-audit # Claude Code (short form) / Gemini CLI
|
||||
/brooks-lint:brooks-audit # Claude Code (full form)
|
||||
$brooks-audit # Codex CLI
|
||||
```
|
||||
|
||||
프로젝트 구조를 설명하거나 핵심 파일을 공유하세요. 모듈 의존성을 매핑하고, 순환 의존성을 식별하며, Conway의 법칙 정합성을 점검합니다.
|
||||
|
||||
### 기술 부채 평가
|
||||
|
||||
```
|
||||
/brooks-debt # Claude Code (short form) / Gemini CLI
|
||||
/brooks-lint:brooks-debt # Claude Code (full form)
|
||||
$brooks-debt # Codex CLI
|
||||
```
|
||||
|
||||
부채를 여섯 가지 쇠퇴 위험에 걸쳐 분류하고, 각 진단을 Pain × Spread 우선순위로 점수화하며, Critical / Scheduled / Monitored 분류가 담긴 우선순위 상환 로드맵을 산출합니다.
|
||||
|
||||
### 테스트 품질 리뷰
|
||||
|
||||
```
|
||||
/brooks-test # Claude Code (short form) / Gemini CLI
|
||||
/brooks-lint:brooks-test # Claude Code (full form)
|
||||
$brooks-test # Codex CLI
|
||||
```
|
||||
|
||||
테스트 스위트를 여섯 가지 테스트 공간 쇠퇴 위험에 비추어 감사합니다 — 테스트 모호성, 테스트 취약성, 테스트 중복, Mock 남용, 커버리지 환상, 아키텍처 불일치 — 출처는 xUnit Test Patterns, The Art of Unit Testing, How Google Tests Software, Working Effectively with Legacy Code입니다. PR 리뷰에는 가벼운 Step 7 빠른 테스트 점검도 자동으로 포함됩니다(문서 전용 또는 비프로덕션 diff에서는 건너뜀).
|
||||
|
||||
### 건강 대시보드
|
||||
|
||||
```
|
||||
/brooks-health # Claude Code (short form) / Gemini CLI
|
||||
/brooks-lint:brooks-health # Claude Code (full form)
|
||||
$brooks-health # Codex CLI
|
||||
```
|
||||
|
||||
네 가지 품질 차원 전체에 걸쳐 축약된 스캔을 실행하고 가중 종합 건강 점수(0–100)를 산출합니다. 릴리스 전에, 새 팀을 온보딩할 때, 또는 "우리는 어떻게 하고 있나?"에 대한 큰 그림 보고서가 필요할 때 사용하세요. 특정 차원에 대한 심층 진단이 필요하면 대신 해당 전문 스킬을 사용하세요.
|
||||
|
||||
### 전면 스윕
|
||||
|
||||
```
|
||||
/brooks-sweep # Claude Code (short form) / Gemini CLI
|
||||
/brooks-lint:brooks-sweep # Claude Code (full form)
|
||||
$brooks-sweep # Codex CLI
|
||||
```
|
||||
|
||||
모든 프로덕션(R1–R6)과 테스트(T1–T6) 쇠퇴 위험 그리고 아키텍처를 한 번에 통합 스캔한 뒤 수정을 적용합니다: 안전한 변경은 즉시 자동 적용되고, 여러 파일에 걸치거나 인터페이스를 건드리는 변경은 확인이 필요하며, 복잡한 아키텍처 결정은 수동 처리 항목으로 표시됩니다. 수정 로그, 건강 점수 변화량, 잔여 항목 목록을 출력합니다.
|
||||
|
||||
## 설정
|
||||
|
||||
리뷰 동작을 커스터마이즈하려면 프로젝트 루트에 `.brooks-lint.yaml`을 두세요:
|
||||
|
||||
```yaml
|
||||
version: 1
|
||||
|
||||
strictness: balanced # strict | balanced (default) | legacy-friendly — softer scoring for legacy code
|
||||
|
||||
disable:
|
||||
- T5 # skip coverage metrics check — we don't enforce coverage
|
||||
|
||||
severity:
|
||||
R1: suggestion # downgrade Cognitive Overload findings for this domain
|
||||
|
||||
ignore:
|
||||
- "**/*.generated.*"
|
||||
- "**/vendor/**"
|
||||
|
||||
# custom_risks: # define project-specific Cx codes — see skills/_shared/custom-risks-guide.md
|
||||
# suppress: # downgrade specific findings by risk + path (e.g. accepted legacy debt)
|
||||
```
|
||||
|
||||
[`.brooks-lint.example.yaml`](.brooks-lint.example.yaml)을 출발점으로 복사하세요.
|
||||
모든 설정은 선택 사항입니다 — 파일을 통째로 생략하면 기본 동작이 적용됩니다.
|
||||
|
||||
| 설정 | 설명 |
|
||||
|---------|-------------|
|
||||
| `strictness` | 점수 프리셋: `strict`, `balanced`(기본), 또는 `legacy-friendly`(더 가벼운 감점, 상위 수정으로 시작) |
|
||||
| `disable` | 건너뛸 위험 코드(`R1`–`R6`, `T1`–`T6`) |
|
||||
| `severity` | 심각도 등급 재정의(`critical` / `warning` / `suggestion`) |
|
||||
| `ignore` | 제외할 파일의 glob 패턴 |
|
||||
| `focus` | 이 위험 코드만 평가(`disable`과 함께 사용 불가) |
|
||||
| `custom_risks` | 프로젝트 전용 위험 코드 정의(`C1`, `C2`, …) — [`custom-risks-guide.md`](skills/_shared/custom-risks-guide.md) 참고 |
|
||||
| `suppress` | 위험 코드 + 경로로 특정 진단의 심각도 하향(선택적 `expires:` 만료일) |
|
||||
|
||||
---
|
||||
|
||||
## 왜 이 책들인가, 왜 지금인가?
|
||||
|
||||
AI 보조 코딩의 시대에 우리는 그 어느 때보다 빠르게, 더 많은 코드를 쓰고 있습니다. 하지만 60년에 걸친 소프트웨어 엔지니어링의 통찰은 변하지 않았습니다:
|
||||
|
||||
> *"소프트웨어의 복잡성은 우발적 속성이 아니라 본질적 속성이다."*
|
||||
> — Frederick Brooks
|
||||
|
||||
AI는 코드를 더 빨리 쓰도록 도와줄 수 있지만, 당신이 대성당을 짓고 있는지 타르 구덩이를 파고 있는지는 알려주지 못합니다. **brooks-lint는 그 간극을 메웁니다** — 열두 권의 고전 엔지니어링 도서에서 어렵게 얻은 지혜를 당신의 현대적인 개발 워크플로로 가져옵니다.
|
||||
|
||||
이 저자들이 식별한 쇠퇴 위험은 그 어느 때보다 더 적실합니다:
|
||||
- **AI 어시스턴트를 추가한다고** 인지 과부하나 도메인 모델 왜곡이 고쳐지지는 않습니다
|
||||
- **더 많은 코드를 생성하면** 변경 전파와 지식 중복이 늘어납니다
|
||||
- **더 빨리 움직이는 것은** 우발적 복잡도와 의존성 무질서를 한층 더 위험하게 만듭니다
|
||||
|
||||
## 프로젝트 구조
|
||||
|
||||
```
|
||||
brooks-lint/
|
||||
├── .claude-plugin/ # Claude Code plugin metadata
|
||||
├── .codex-plugin/ # Codex CLI plugin metadata
|
||||
├── skills/
|
||||
│ ├── _shared/ # Shared framework files
|
||||
│ │ ├── common.md # Iron Law, Project Config, Report Template, Health Score
|
||||
│ │ ├── source-coverage.md # 12-book coverage matrix, tradeoffs, false-positive guards
|
||||
│ │ ├── decay-risks.md # Six decay risks with symptoms and book citations
|
||||
│ │ ├── test-decay-risks.md # Six test-space decay risks with book citations
|
||||
│ │ ├── remedy-guide.md # --fix mode: actionable Remedy enhancement rules
|
||||
│ │ └── custom-risks-guide.md # Template for project-specific risk codes
|
||||
│ ├── brooks-review/ # Mode 1: PR Review
|
||||
│ │ ├── SKILL.md
|
||||
│ │ └── pr-review-guide.md
|
||||
│ ├── brooks-audit/ # Mode 2: Architecture Audit
|
||||
│ │ ├── SKILL.md
|
||||
│ │ └── architecture-guide.md
|
||||
│ ├── brooks-debt/ # Mode 3: Tech Debt Assessment
|
||||
│ │ ├── SKILL.md
|
||||
│ │ └── debt-guide.md
|
||||
│ ├── brooks-test/ # Mode 4: Test Quality Review
|
||||
│ │ ├── SKILL.md
|
||||
│ │ └── test-guide.md
|
||||
│ ├── brooks-health/ # Mode 5: Health Dashboard
|
||||
│ │ ├── SKILL.md
|
||||
│ │ └── health-guide.md
|
||||
│ └── brooks-sweep/ # Mode 6: Full Sweep & Auto-Fix
|
||||
│ ├── SKILL.md
|
||||
│ └── sweep-guide.md
|
||||
├── hooks/ # SessionStart hook
|
||||
├── commands/ # Short-form command wrappers (auto-installed by hook)
|
||||
├── evals/ # Benchmark test cases
|
||||
│ └── evals.json
|
||||
└── assets/
|
||||
└── logo.svg
|
||||
```
|
||||
|
||||
## CI/CD 통합
|
||||
|
||||
GitHub Action을 사용해 모든 PR에서 brooks-lint를 자동으로 실행하세요:
|
||||
|
||||
```yaml
|
||||
# .github/workflows/brooks-lint.yml
|
||||
name: Brooks-Lint PR Review
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
brooks-lint:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: hyhmrright/brooks-lint/.github/actions/brooks-lint@main
|
||||
with:
|
||||
mode: review
|
||||
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
fail-below: 70
|
||||
```
|
||||
|
||||
전체 템플릿은 [`docs/github-action-example.yml`](docs/github-action-example.yml)을 참고하세요.
|
||||
|
||||
이 Action은 리뷰를 PR 댓글로 게시하며, 건강 점수가 임계값 아래로 떨어지면 선택적으로 체크를 실패시킵니다. `.brooks-lint-history.json`이 저장소에 커밋되어 있으면 댓글에 추세 변화도 포함됩니다(예: "85 → 82 (−3) over last 3 runs").
|
||||
|
||||
**품질 게이트와 Code Scanning.** `fail-below` 외에도 이 Action은 다음을 제공합니다:
|
||||
|
||||
```yaml
|
||||
with:
|
||||
mode: review
|
||||
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
fail-on: critical # fail on any Critical finding (none | warning | critical)
|
||||
fail-on-regression: true # fail if the Health Score dropped vs the last run
|
||||
sarif-file: brooks-lint.sarif # also upload findings to GitHub Code Scanning
|
||||
```
|
||||
|
||||
`fail-on-regression`은 `.brooks-lint-history.json`을 읽으므로, "새로운 회귀 없음"을 강제하려면 그 파일을 커밋하세요. `sarif-file`을 설정하면 진단이 PR의 **Files changed** 탭에 인라인으로 나타나며, job에 `security-events: write` 권한이 필요합니다.
|
||||
|
||||
**비용:** PR 실행당 약 $0.05–0.15로, diff 크기와 모델에 따라 다릅니다. `pull_request` 이벤트에서만 실행할 것을 권장합니다.
|
||||
|
||||
## 로드맵
|
||||
|
||||
> **현재 상태(v1.4):** 12권 도서 기반, 6가지 프로덕션 쇠퇴 위험(R1–R6) + 6가지 테스트 쇠퇴 위험(T1–T6), 6개 스킬 — PR 리뷰, 아키텍처 감사, 기술 부채, 테스트 품질, 건강 대시보드, 전면 스윕 — 여기에 CI 품질 게이트, GitHub Code Scanning용 SARIF 출력, strictness 프리셋, 재현 가능한 파서 충실도 벤치마크를 더했습니다. 아래의 이전 항목들은 현재 기능 집합이 아니라 역사적 마일스톤을 기술합니다.
|
||||
|
||||
- [x] **v0.2**: 플러그인 인프라(`.claude-plugin/`, 훅, 슬래시 명령)
|
||||
- [x] **v0.3**: 여덟 가지 Brooks 차원, 문서 완성도 점수화
|
||||
- [x] **v0.4**: 여섯 권 도서 프레임워크, 쇠퇴 위험 차원, 진단 체인, 벤치마크 스위트
|
||||
- [x] **v0.5**: 테스트 품질 리뷰(모드 4) — 네 권의 테스트 도서, 여섯 가지 테스트 쇠퇴 위험
|
||||
- [x] **v0.6**: 아키텍처 감사의 Mermaid 의존성 그래프
|
||||
- [x] **v0.7**: `.brooks-lint.yaml` 프로젝트 설정, 모드 2 능동 컨텍스트, 10권 도서로 확장
|
||||
- [x] **v0.8**: 네임스페이스 명령을 갖춘 독립 스킬 아키텍처
|
||||
- [x] **v0.9**: 단계 검증, 자동 diff 범위, `/brooks-health` 대시보드, 추세 추적, 트리아지 모드, `--fix` 처방, 온보딩 보고서, GitHub Action
|
||||
- [x] **v1.0**: eval 자동화(`run-evals-live.mjs`), 커스텀 위험 확장(`Cx` 코드)
|
||||
- [x] **v1.1**: 전면 스윕 스킬(`brooks-sweep`) — 통합 다차원 자동 수정
|
||||
- [x] **v1.2**: 자율 스윕 파이프라인, `npm run bump` 버전 전파
|
||||
- [x] **v1.3**: Codex 마켓플레이스 메타데이터, 여러 에이전트 플랫폼을 위한 한 줄 명령 설치기, 다국어 README + 랜딩 사이트
|
||||
- [x] **v1.4**: GitHub Code Scanning용 SARIF 출력, CI severity + 회귀 게이트, strictness 프리셋(strict/balanced/legacy-friendly), 57개 시나리오 eval 스위트, 재현 가능한 파서 충실도 벤치마크(`npm run benchmark`)
|
||||
|
||||
도움을 주고 싶으신가요? 지금 가장 좋은 기여는 새로운 eval 테스트 케이스와 개선된 쇠퇴 위험 증상 패턴입니다. [CONTRIBUTING.md](CONTRIBUTING.md)를 참고하세요.
|
||||
|
||||
## 기여
|
||||
|
||||
진단을 추가하거나, 가이드를 개선하거나, 벤치마크 스위트를 확장하는 방법은 [CONTRIBUTING.md](CONTRIBUTING.md)를 참고하세요.
|
||||
|
||||
당신 자신의 PR에 `/brooks-review`를 실행해 보세요 — 우리는 우리가 만들고 있는 도구로 기여를 리뷰합니다.
|
||||
|
||||
## 라이선스
|
||||
|
||||
MIT License — 자세한 내용은 [LICENSE](LICENSE)를 참고하세요.
|
||||
|
||||
## 감사의 말
|
||||
|
||||
이 프로젝트는 열두 거인의 어깨 위에 서 있습니다:
|
||||
|
||||
**프로덕션 코드 프레임워크**
|
||||
- Frederick P. Brooks Jr. — *The Mythical Man-Month*(1975, 기념판 1995)
|
||||
- Steve McConnell — *Code Complete*(1993, 2판 2004)
|
||||
- Martin Fowler — *Refactoring*(1999, 2판 2018)
|
||||
- Robert C. Martin — *Clean Architecture*(2017)
|
||||
- Andrew Hunt & David Thomas — *The Pragmatic Programmer*(1999, 20주년판 2019)
|
||||
- Eric Evans — *Domain-Driven Design*(2003)
|
||||
- John Ousterhout — *A Philosophy of Software Design*(2018)
|
||||
- Titus Winters, Tom Manshreck, Hyrum Wright — *Software Engineering at Google*(2020)
|
||||
|
||||
**테스트 품질 프레임워크**
|
||||
- Gerard Meszaros — *xUnit Test Patterns*(2007)
|
||||
- Roy Osherove — *The Art of Unit Testing*(2009, 3판 2023)
|
||||
- Google Engineering — *How Google Tests Software*(2012)
|
||||
- Michael Feathers — *Working Effectively with Legacy Code*(2004)
|
||||
|
||||
이 도구에 인코딩된 쇠퇴 위험은 그들의 사상을 현대 코드 품질 평가에 적용한 우리의 종합입니다.
|
||||
|
||||
---
|
||||
|
||||
## Star 히스토리
|
||||
|
||||
[](https://star-history.com/#hyhmrright/brooks-lint&Date)
|
||||
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
<strong>⭐ 이 도구가 당신의 코드베이스를 다르게 보는 데 도움이 되었다면, star를 눌러 주세요!</strong>
|
||||
</p>
|
||||
@@ -10,10 +10,16 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<strong>English</strong> · <a href="README.zh-CN.md">简体中文</a>
|
||||
<strong>English</strong> ·
|
||||
<a href="README.zh-CN.md">简体中文</a> ·
|
||||
<a href="README.zh-TW.md">繁體中文</a> ·
|
||||
<a href="README.ja.md">日本語</a> ·
|
||||
<a href="README.ko.md">한국어</a> ·
|
||||
<a href="README.es.md">Español</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="#quick-start">Quick Start</a> •
|
||||
<a href="#the-six-decay-risks">The Six Decay Risks</a> •
|
||||
<a href="#what-it-looks-like">What It Looks Like</a> •
|
||||
<a href="#benchmark">Benchmark</a> •
|
||||
@@ -28,6 +34,14 @@
|
||||
<img src="https://img.shields.io/github/stars/hyhmrright/brooks-lint?style=social" alt="GitHub Stars">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://trendshift.io/repositories/47738" target="_blank"><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>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="assets/banner-en.svg" alt="Your code → 12 classics → 12 decay risks → cited findings" width="900">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://hyhmrright.github.io/brooks-lint/"><img src="assets/demo.gif" alt="brooks-lint reviewing code: a /brooks-review command produces a 28/100 health score and cited Symptom → Source → Consequence → Remedy findings" width="820"></a>
|
||||
</p>
|
||||
@@ -48,6 +62,30 @@ Most code quality tools count lines and cyclomatic complexity. **brooks-lint** g
|
||||
For the full source-to-skill mapping, including exceptions and false-positive guards, see
|
||||
[`skills/_shared/source-coverage.md`](skills/_shared/source-coverage.md).
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Claude Code
|
||||
/plugin marketplace add hyhmrright/brooks-lint
|
||||
/plugin install brooks-lint@brooks-lint-marketplace
|
||||
|
||||
# Any other Agent Skills platform — Cursor · Codex · Gemini · Copilot · Windsurf · OpenCode · Kiro · …
|
||||
curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- <platform>
|
||||
```
|
||||
|
||||
Then just ask ("review this PR", "audit the architecture") — or run a command:
|
||||
|
||||
| Command | What it does |
|
||||
|---------|--------------|
|
||||
| `/brooks-review` | Review a PR or diff |
|
||||
| `/brooks-audit` | Audit architecture (+ Mermaid dependency graph) |
|
||||
| `/brooks-debt` | Prioritized tech-debt roadmap |
|
||||
| `/brooks-test` | Test-suite quality review |
|
||||
| `/brooks-health` | Health dashboard across all dimensions |
|
||||
| `/brooks-sweep` | Sweep every dimension and auto-fix findings |
|
||||
|
||||
Every finding comes back as **Symptom → Source → Consequence → Remedy** with a book citation and a 0–100 Health Score. Full install options (8 more platforms), per-command usage, and CI/CD setup are [below](#installation).
|
||||
|
||||
## The Twelve Books
|
||||
|
||||
| Book | Author | Contributes to |
|
||||
|
||||
@@ -10,10 +10,16 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="README.md">English</a> · <strong>简体中文</strong>
|
||||
<a href="README.md">English</a> ·
|
||||
<strong>简体中文</strong> ·
|
||||
<a href="README.zh-TW.md">繁體中文</a> ·
|
||||
<a href="README.ja.md">日本語</a> ·
|
||||
<a href="README.ko.md">한국어</a> ·
|
||||
<a href="README.es.md">Español</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="#快速上手">快速上手</a> •
|
||||
<a href="#六类衰退风险">六类衰退风险</a> •
|
||||
<a href="#实际效果">实际效果</a> •
|
||||
<a href="#基准测试">基准测试</a> •
|
||||
@@ -28,6 +34,14 @@
|
||||
<img src="https://img.shields.io/github/stars/hyhmrright/brooks-lint?style=social" alt="GitHub Stars">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://trendshift.io/repositories/47738" target="_blank"><img src="https://trendshift.io/api/badge/trendshift/repositories/47738/daily?language=JavaScript" alt="Trendshift 当日 JavaScript 仓库榜第 2 名" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="assets/banner-zh-CN.svg" alt="你的代码 → 十二本经典 → 十二类衰退风险 → 带出处的结论" width="900">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://hyhmrright.github.io/brooks-lint/"><img src="assets/demo.gif" alt="brooks-lint 审查代码:一条 /brooks-review 命令产出 28/100 健康分以及引用书目的 症状 → 根源 → 后果 → 对策 诊断" width="820"></a>
|
||||
</p>
|
||||
@@ -48,6 +62,30 @@
|
||||
完整的"书目—技能"映射(含例外与误报防护),见
|
||||
[`skills/_shared/source-coverage.md`](skills/_shared/source-coverage.md)。
|
||||
|
||||
## 快速上手
|
||||
|
||||
```bash
|
||||
# Claude Code
|
||||
/plugin marketplace add hyhmrright/brooks-lint
|
||||
/plugin install brooks-lint@brooks-lint-marketplace
|
||||
|
||||
# 其他任意 Agent Skills 平台 —— Cursor · Codex · Gemini · Copilot · Windsurf · OpenCode · Kiro · …
|
||||
curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- <平台>
|
||||
```
|
||||
|
||||
装好后直接开口("审查这个 PR""审计架构")——或运行命令:
|
||||
|
||||
| 命令 | 作用 |
|
||||
|------|------|
|
||||
| `/brooks-review` | 审查一个 PR 或 diff |
|
||||
| `/brooks-audit` | 架构审计(含 Mermaid 依赖图) |
|
||||
| `/brooks-debt` | 排好优先级的技术债路线图 |
|
||||
| `/brooks-test` | 测试套件质量审查 |
|
||||
| `/brooks-health` | 跨所有维度的健康看板 |
|
||||
| `/brooks-sweep` | 全维度扫描并自动修复 |
|
||||
|
||||
每条诊断都以 **症状 → 根源 → 后果 → 对策** 返回,附书目出处和 0–100 健康分。完整安装方式(另外 8 个平台)、逐命令用法、CI/CD 配置见[下文](#安装)。
|
||||
|
||||
## 十二本书
|
||||
|
||||
| 书名 | 作者 | 贡献于 |
|
||||
|
||||
@@ -0,0 +1,686 @@
|
||||
<p align="center">
|
||||
<img src="assets/logo.svg" alt="brooks-lint" width="200">
|
||||
</p>
|
||||
|
||||
<h1 align="center">brooks-lint</h1>
|
||||
|
||||
<p align="center">
|
||||
<strong>植根於十二本經典工程著作的 AI 程式碼審查。<br>
|
||||
一致、可溯源、可落地。</strong>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="README.md">English</a> ·
|
||||
<a href="README.zh-CN.md">简体中文</a> ·
|
||||
<strong>繁體中文</strong> ·
|
||||
<a href="README.ja.md">日本語</a> ·
|
||||
<a href="README.ko.md">한국어</a> ·
|
||||
<a href="README.es.md">Español</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="#快速上手">快速上手</a> •
|
||||
<a href="#六類衰退風險">六類衰退風險</a> •
|
||||
<a href="#實際效果">實際效果</a> •
|
||||
<a href="#基準測試">基準測試</a> •
|
||||
<a href="#安裝">安裝</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-1.4.0-blue.svg" alt="Version">
|
||||
<img src="https://img.shields.io/badge/license-MIT-green.svg" alt="MIT License">
|
||||
<img src="https://img.shields.io/badge/Claude_Code-Plugin-blueviolet.svg" alt="Claude Code Plugin">
|
||||
<img src="https://img.shields.io/badge/Codex_CLI-Skill-orange.svg" alt="Codex CLI Skill">
|
||||
<img src="https://img.shields.io/github/stars/hyhmrright/brooks-lint?style=social" alt="GitHub Stars">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://trendshift.io/repositories/47738" target="_blank"><img src="https://trendshift.io/api/badge/trendshift/repositories/47738/daily?language=JavaScript" alt="Trendshift 當日 JavaScript 儲存庫排行榜第 2 名" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="assets/banner-zh-TW.svg" alt="你的程式碼 → 十二本經典 → 十二類衰退風險 → 帶出處的結論" width="900">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://hyhmrright.github.io/brooks-lint/"><img src="assets/demo.gif" alt="brooks-lint 審查程式碼:一條 /brooks-review 命令產出 28/100 健康分以及引用書目的 症狀 → 根源 → 後果 → 對策 診斷" width="820"></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<strong><a href="https://hyhmrright.github.io/brooks-lint/">→ 造訪官網</a></strong>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
> *"一個孩子要十月懷胎,無論派多少人去都一樣。"*
|
||||
> —— Frederick Brooks,《人月神話》(1975)
|
||||
|
||||
**五十年過去,Brooks 依然正確——McConnell、Fowler、Martin、Hunt & Thomas、Evans、Ousterhout、Winters、Meszaros、Osherove、Feathers 以及 Google 測試團隊同樣如此。**
|
||||
|
||||
大多數程式碼品質工具只數行數和循環複雜度。**brooks-lint** 更進一步——它對照六個衰退風險維度(綜合自十二本經典工程著作)診斷你的程式碼,每一次都產出帶書目出處、嚴重度標籤和具體對策的結構化診斷。
|
||||
|
||||
完整的「書目—技能」對應(含例外與誤報防護),見
|
||||
[`skills/_shared/source-coverage.md`](skills/_shared/source-coverage.md)。
|
||||
|
||||
## 快速上手
|
||||
|
||||
```bash
|
||||
# Claude Code
|
||||
/plugin marketplace add hyhmrright/brooks-lint
|
||||
/plugin install brooks-lint@brooks-lint-marketplace
|
||||
|
||||
# 其他任意 Agent Skills 平台 —— Cursor · Codex · Gemini · Copilot · Windsurf · OpenCode · Kiro · …
|
||||
curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- <平台>
|
||||
```
|
||||
|
||||
裝好後直接開口(「審查這個 PR」「稽核架構」)——或執行命令:
|
||||
|
||||
| 命令 | 作用 |
|
||||
|------|------|
|
||||
| `/brooks-review` | 審查一個 PR 或 diff |
|
||||
| `/brooks-audit` | 架構稽核(含 Mermaid 相依圖) |
|
||||
| `/brooks-debt` | 排好優先順序的技術債路線圖 |
|
||||
| `/brooks-test` | 測試套件品質審查 |
|
||||
| `/brooks-health` | 跨所有維度的健康儀表板 |
|
||||
| `/brooks-sweep` | 全維度掃描並自動修復 |
|
||||
|
||||
每條診斷都以 **症狀 → 根源 → 後果 → 對策** 回傳,附書目出處和 0–100 健康分。完整安裝方式(另外 8 個平台)、逐命令用法、CI/CD 設定見[下文](#安裝)。
|
||||
|
||||
## 十二本書
|
||||
|
||||
| 書名 | 作者 | 貢獻於 |
|
||||
|------|--------|----------------|
|
||||
| *The Mythical Man-Month*(人月神話) | Frederick Brooks | R2、R4、R5 |
|
||||
| *Code Complete*(程式碼大全) | Steve McConnell | R1、R4 |
|
||||
| *Refactoring*(重構) | Martin Fowler | R1、R2、R3、R4、R6 |
|
||||
| *Clean Architecture*(無瑕的程式碼:整潔的軟體設計與架構篇) | Robert C. Martin | R2、R5 |
|
||||
| *The Pragmatic Programmer*(務實的程式設計師) | Hunt & Thomas | R2、R3、R4、R5、T2、T3 |
|
||||
| *Domain-Driven Design*(領域驅動設計) | Eric Evans | R1、R3、R6 |
|
||||
| *A Philosophy of Software Design*(軟體設計的哲學) | John Ousterhout | R1、R4 |
|
||||
| *Software Engineering at Google*(Google 軟體工程) | Winters, Manshreck & Wright | R2、R5 |
|
||||
| *The Art of Unit Testing*(單元測試的藝術) | Roy Osherove | T1、T2、T4、T5 |
|
||||
| *How Google Tests Software*(Google 測試之道) | Whittaker, Arbon & Carollo | T5、T6 |
|
||||
| *Working Effectively with Legacy Code*(修改軟體的藝術) | Michael Feathers | T4、T5、T6 |
|
||||
| *xUnit Test Patterns*(xUnit 測試模式) | Gerard Meszaros | T1、T2、T3、T4 |
|
||||
|
||||
## 六類衰退風險
|
||||
|
||||
brooks-lint 從**六類生產程式碼衰退風險**和**六類測試程式碼衰退風險**兩個角度評估你的程式碼,這些維度綜合自十二本經典工程著作:
|
||||
|
||||
| 衰退風險 | 診斷問題 | 出處 |
|
||||
|------------|---------------------|---------|
|
||||
| 🧠 認知過載 | 理解這段程式碼要花多少腦力? | Code Complete、Refactoring、DDD、Philosophy of SD |
|
||||
| 🔗 變更擴散 | 改一處會牽連多少不相干的東西? | Refactoring、Clean Architecture、Pragmatic、SE@Google |
|
||||
| 📋 知識重複 | 同一個決策是否在多處被表達? | Pragmatic、Refactoring、DDD |
|
||||
| 🌀 偶發複雜度 | 程式碼是否比問題本身更複雜? | Refactoring、Code Complete、Brooks、Philosophy of SD |
|
||||
| 🏗️ 相依失序 | 相依關係是否朝一致的方向流動? | Clean Architecture、Brooks、Pragmatic、SE@Google |
|
||||
| 🗺️ 領域模型失真 | 程式碼是否忠實地表達了業務領域? | DDD、Refactoring |
|
||||
|
||||
> Philosophy of SD = *A Philosophy of Software Design*(Ousterhout) · SE@Google = *Software Engineering at Google*(Winters 等)
|
||||
|
||||
## 實際效果
|
||||
|
||||
給定這段程式碼:
|
||||
|
||||
```python
|
||||
class UserService:
|
||||
def update_profile(self, user_id, name, email, avatar_url):
|
||||
user = self.db.query(f"SELECT * FROM users WHERE id = {user_id}")
|
||||
user['email'] = email
|
||||
...
|
||||
if user['email'] != email: # 永遠為 False —— 隱性 bug
|
||||
self.smtp.send(...)
|
||||
points = user['login_count'] * 10 + 500
|
||||
self.db.execute(f"UPDATE loyalty SET points={points} WHERE user_id={user_id}")
|
||||
```
|
||||
|
||||
brooks-lint 產出:
|
||||
|
||||
---
|
||||
|
||||
**健康分:28/100**
|
||||
|
||||
*這個方法把四個不相干的業務職責塞進同一個函式,含有一個會靜默吞掉「信箱變更通知」的邏輯 bug,並且對 SQL 注入門戶大開。*
|
||||
|
||||
### 🔴 變更擴散 —— 單個方法因四個不相干的業務原因而改動
|
||||
**症狀:** `update_profile` 在同一個方法主體裡完成資料欄位更新、信箱變更通知、點數重算和快取失效。
|
||||
**根源:** Fowler — *Refactoring* — 發散式變更(Divergent Change);Hunt & Thomas — *The Pragmatic Programmer* — 正交性(Orthogonality)
|
||||
**後果:** 任何對點數公式的改動都可能破壞郵件通知,反之亦然。每次修改都同時背負著四個不相干領域的回歸風險。
|
||||
**對策:** 抽出 `NotificationService`、`LoyaltyService` 和 `UserCacheInvalidator`。`UserService.update_profile` 應只做編排、逐一呼叫它們——本身不持有任何實作邏輯。
|
||||
|
||||
### 🔴 領域模型失真 —— 隱性邏輯 bug:信箱通知永不觸發
|
||||
**症狀:** `user['email'] = email` 在 `if user['email'] != email` 之前就覆寫了舊值——條件恆為 `False`,通知是死程式碼。
|
||||
**根源:** McConnell — *Code Complete* — 第 17 章:非常規控制結構
|
||||
**後果:** 使用者改信箱時永遠收不到通知。這是靜默的資料完整性失效——系統看似正常運作,實則違反了業務規則。
|
||||
**對策:** 在任何修改之前先擷取 `old_email = user['email']`,拿它(而非 `user['email']`)做比較。
|
||||
|
||||
*(另有 6 條診斷,含 SQL 注入、相依失序、魔術數字)*
|
||||
|
||||
### 帶相依圖的架構審查
|
||||
|
||||
在模式 2(架構審查)中,brooks-lint 會在報告頂部產生一張 **Mermaid 相依圖**。模組按嚴重度著色:紅=Critical,黃=Warning,綠=乾淨。
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph src/api
|
||||
AuthController
|
||||
UserController
|
||||
end
|
||||
subgraph src/domain
|
||||
UserService
|
||||
OrderService
|
||||
end
|
||||
subgraph src/infra
|
||||
Database
|
||||
EmailClient
|
||||
end
|
||||
|
||||
AuthController --> UserService
|
||||
UserController --> UserService
|
||||
UserController --> OrderService
|
||||
OrderService --> UserService
|
||||
OrderService --> EmailClient
|
||||
UserService --> Database
|
||||
EmailClient -.->|circular| OrderService
|
||||
|
||||
classDef critical fill:#ff6b6b,stroke:#c92a2a,color:#fff
|
||||
classDef warning fill:#ffd43b,stroke:#e67700
|
||||
classDef clean fill:#51cf66,stroke:#2b8a3e,color:#fff
|
||||
|
||||
class OrderService,EmailClient critical
|
||||
class AuthController warning
|
||||
class UserService,UserController,Database clean
|
||||
```
|
||||
|
||||
該圖在 GitHub、Notion 等 Markdown 環境中原生算繪——無需額外工具。
|
||||
|
||||
## 更多範例
|
||||
|
||||
[完整畫廊](docs/gallery.md) 收錄了 brooks-lint 在 Python、TypeScript、Go、Java 上的真實輸出——涵蓋 PR 審查、帶 Mermaid 相依圖的架構審查、技術債評估和測試品質審查。
|
||||
|
||||
初次接觸這些衰退風險?[**衰退風險實戰指南**](https://hyhmrright.github.io/brooks-lint/guide.html) 逐一講解全部六類——每類的診斷問題、代表症狀、出處書目與對策。
|
||||
|
||||
---
|
||||
|
||||
## 基準測試
|
||||
|
||||
在 3 個真實情境(PR 審查、架構審查、技術債評估)上測試:
|
||||
|
||||
| 評估項 | brooks-lint | 僅用 Claude |
|
||||
|-----------|:-----------:|:------------:|
|
||||
| 結構化診斷(症狀 → 根源 → 後果 → 對策) | ✅ 100% | ❌ 0% |
|
||||
| 每條診斷帶書目出處 | ✅ 100% | ❌ 0% |
|
||||
| 嚴重度標籤(🔴/🟡/🟢) | ✅ 100% | ❌ 0% |
|
||||
| 健康分(0–100) | ✅ 100% | ❌ 0% |
|
||||
| 識別「變更擴散」 | ✅ 100% | ✅ 100% |
|
||||
| **整體通過率** | **94%** | **16%** |
|
||||
|
||||
差距不在於 Claude *能不能*發現問題——而在於它能否*每一次都穩定地*發現,並附上可溯源的證據和可落地的對策。
|
||||
|
||||
### 可復現基準
|
||||
|
||||
上表是示意性的。下面這些數字**確定、可在本地復算**:
|
||||
|
||||
**parser 保真度** —— SARIF 輸出與 CI 閘門都依賴於正確解析模型的 Markdown 報告。在一個**凍結的 30 份真實模型報告語料**上(涵蓋全部六種 mode,`evals/benchmark-corpus.json`),每份都配有**獨立評分**的發現清單(由另一遍模型評分、並經人工抽查),實際發布的 parser 跑分如下——執行 `npm run benchmark`:
|
||||
|
||||
| 指標(n = 30,凍結語料) | 結果 |
|
||||
|---|:---:|
|
||||
| 嚴重度計數精確吻合(parser vs 人工標註真值) | 30 / 30 |
|
||||
| 風險碼 precision / recall | 100% / 100%(56 個 finding-level 碼,0 偽陽 / 0 偽陰) |
|
||||
| 產出合法 SARIF 2.1.0 | 30 / 30 |
|
||||
|
||||
由於 parser 是確定性的、語料是凍結的,`npm run benchmark` 對任何人都給出相同結果,`npm test` 也將其作為回歸守衛。該語料**有意**包含 9 份偽陽性 / tradeoff 報告(例如一個*看起來像*循環相依、實則是埠與配接器(ports-and-adapters)的設計),它們必須保持乾淨。
|
||||
|
||||
**評分確定性** —— 給定一組固定發現(2 Critical / 3 Warning / 1 Suggestion),三個 strictness 預設產出的分數與其 `common.md` 表的預測分毫不差:strict **34**、balanced **54**、legacy-friendly **74**——且只有 `legacy-friendly` 會優先列出前三高槓桿修復。
|
||||
|
||||
**模型品質** —— 模型能否在真實程式碼上找到*正確的*風險,由 **57 情境 eval 套件**(`evals/evals.json`)衡量:`npm run evals`(結構校驗)與 `npm run evals:live`(實測,需 `ANTHROPIC_API_KEY`)。
|
||||
|
||||
> 範圍與誠實說明:parser 數字是確定性的、可精確復算;strictness 與 eval 套件的數字是對模型的單次實測,會有輕微跑動差異。parser 基準衡量的是報告解析保真度(工具是否讀出了報告裡寫的每條發現),而非某條發現「是否正確」。嚴重度計數吻合是完全獨立的訊號;風險碼一致性還反映了 parser 與 grader 共用同一套權威 name→code 對應。
|
||||
|
||||
## 橫向對比
|
||||
|
||||
| | brooks-lint | ESLint / Pylint | GitHub Copilot Review | 原生 Claude |
|
||||
|---|:---:|:---:|:---:|:---:|
|
||||
| 偵測語法與風格問題 | — | ✅ | ✅ | ~ |
|
||||
| 結構化診斷鏈 | ✅ | ❌ | ❌ | ❌ |
|
||||
| 將診斷溯源到經典著作 | ✅ | ❌ | ❌ | ❌ |
|
||||
| 一致的嚴重度標籤 | ✅ | ✅ | ~ | ❌ |
|
||||
| 架構層面的洞察 | ✅ | ❌ | ~ | ~ |
|
||||
| 領域模型分析 | ✅ | ❌ | ❌ | ~ |
|
||||
| 零設定、無需安裝外掛 | ✅ | ❌ | ✅ | ✅ |
|
||||
| 適用於任何語言 | ✅ | ❌ | ✅ | ✅ |
|
||||
|
||||
> `~` = 偶爾 / 不穩定
|
||||
|
||||
**brooks-lint 不是要取代你的 linter。** 它捕捉的是 linter 抓不到的東西:架構漂移、知識孤島、領域模型失真——這些問題往往在無人察覺的幾個月裡持續拖慢團隊。
|
||||
|
||||
## 安裝
|
||||
|
||||
### Claude Code(推薦)
|
||||
|
||||
#### 透過外掛市集
|
||||
```bash
|
||||
/plugin marketplace add hyhmrright/brooks-lint
|
||||
/plugin install brooks-lint@brooks-lint-marketplace
|
||||
```
|
||||
|
||||
短命令(`/brooks-review`)會在首次工作階段啟動時自動安裝。手動安裝:
|
||||
```bash
|
||||
cp commands/*.md ~/.claude/commands/
|
||||
```
|
||||
|
||||
#### 手動安裝
|
||||
```bash
|
||||
mkdir -p ~/.claude/skills/brooks-lint
|
||||
cp -r skills/* ~/.claude/skills/brooks-lint/
|
||||
```
|
||||
|
||||
### Gemini CLI
|
||||
|
||||
#### 透過擴充功能
|
||||
```bash
|
||||
/extensions install https://github.com/hyhmrright/brooks-lint
|
||||
```
|
||||
|
||||
#### 手動安裝
|
||||
```bash
|
||||
mkdir -p ~/.gemini/skills
|
||||
cp -r skills/* ~/.gemini/skills/ # 扁平——Gemini 只探索一層深的技能
|
||||
```
|
||||
> 或直接:`./scripts/install.sh gemini`
|
||||
|
||||
### Codex CLI
|
||||
|
||||
#### 透過技能安裝器(在 Codex 工作階段中)
|
||||
```
|
||||
Install the brooks-lint skill from hyhmrright/brooks-lint
|
||||
```
|
||||
|
||||
#### 命令列
|
||||
```bash
|
||||
python3 ~/.codex/skills/.system/skill-installer/scripts/install-skill-from-github.py \
|
||||
--repo hyhmrright/brooks-lint --path skills --name brooks-lint
|
||||
```
|
||||
|
||||
#### 手動安裝
|
||||
```bash
|
||||
git clone https://github.com/hyhmrright/brooks-lint.git /tmp/brooks-lint
|
||||
mkdir -p ~/.codex/skills
|
||||
cp -r /tmp/brooks-lint/skills/* ~/.codex/skills/ # 扁平——與技能安裝器佈局一致
|
||||
```
|
||||
> 或直接:`./scripts/install.sh codex`
|
||||
|
||||
### 更多平台——OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid
|
||||
|
||||
brooks-lint 以標準 [Agent Skills](https://agentskills.io) 形式散布。**任何載入 Agent Skills 的 agent
|
||||
都能無需任何轉換執行全部六種模式**——一條命令即可安裝:
|
||||
|
||||
```bash
|
||||
# 選擇你的平台;加 --project 裝進當前儲存庫而非全域設定
|
||||
curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- <平台>
|
||||
# <平台> = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · gemini · codex · agents
|
||||
```
|
||||
|
||||
安裝器會把技能**扁平**複製進該平台對應的資料夾,讓共享框架(`../_shared/`)始終正確解析——你不可能裝錯佈局。
|
||||
裝好後直接提問(「審查這個 PR」、「稽核架構」),對應技能就會依據 `description` 自動觸發。
|
||||
不熟悉 skills、或用的是別的 agent?見 **[docs/getting-started.md](docs/getting-started.md)**。
|
||||
|
||||
<details><summary><b>OpenCode</b></summary>
|
||||
|
||||
`./scripts/install.sh opencode` → `~/.config/opencode/skills`(同時讀取 `~/.claude/skills` 與
|
||||
`AGENTS.md`)。完整指南:[docs/opencode-setup.md](docs/opencode-setup.md)。
|
||||
</details>
|
||||
|
||||
<details><summary><b>Cursor</b>(2.4+)</summary>
|
||||
|
||||
`./scripts/install.sh cursor` → `~/.cursor/skills`(也讀 `.agents/skills`;讀取 `AGENTS.md`)。
|
||||
完整指南:[docs/cursor-setup.md](docs/cursor-setup.md)。
|
||||
</details>
|
||||
|
||||
<details><summary><b>Windsurf</b>(Cascade)</summary>
|
||||
|
||||
`./scripts/install.sh windsurf` → `~/.codeium/windsurf/skills`(讀取 `AGENTS.md`)。
|
||||
完整指南:[docs/windsurf-setup.md](docs/windsurf-setup.md)。
|
||||
</details>
|
||||
|
||||
<details><summary><b>Antigravity</b>(Google)</summary>
|
||||
|
||||
`./scripts/install.sh antigravity --project` → `.agent/skills`(讀取 `AGENTS.md` / `GEMINI.md`)。
|
||||
完整指南:[docs/antigravity-setup.md](docs/antigravity-setup.md)。
|
||||
</details>
|
||||
|
||||
<details><summary><b>pi</b>(earendil-works)</summary>
|
||||
|
||||
`./scripts/install.sh pi` → `~/.pi/agent/skills`,或讓 pi 的 `skills` 設定指向一個克隆。
|
||||
完整指南:[docs/pi-setup.md](docs/pi-setup.md)。
|
||||
</details>
|
||||
|
||||
<details><summary><b>GitHub Copilot</b></summary>
|
||||
|
||||
`./scripts/install.sh copilot --project` → `.github/skills`(也自動辨識 `.claude/skills`;讀取
|
||||
`AGENTS.md`)。完整指南:[docs/copilot-setup.md](docs/copilot-setup.md)。
|
||||
</details>
|
||||
|
||||
<details><summary><b>Kiro</b>(AWS)</summary>
|
||||
|
||||
`./scripts/install.sh kiro` → `~/.kiro/skills`(自動註冊 `/brooks-review`;讀取 `AGENTS.md`)。
|
||||
完整指南:[docs/kiro-setup.md](docs/kiro-setup.md)。
|
||||
</details>
|
||||
|
||||
<details><summary><b>Factory Droid</b></summary>
|
||||
|
||||
`./scripts/install.sh droid` → `~/.factory/skills`(註冊 `/brooks-review`;讀取 `AGENTS.md`)。
|
||||
完整指南:[docs/factory-droid-setup.md](docs/factory-droid-setup.md)。
|
||||
</details>
|
||||
|
||||
> **🧪 驗證狀態。** Claude Code、Gemini CLI、Codex CLI 已由維護者驗證。上面八個平台依據各工具官方技能規範撰寫,
|
||||
> 並已在檔案佈局層面驗證(安裝器經過測試),但維護者尚未在每個平台端到端實跑。在某平台試過了——無論成功**還是**失敗?
|
||||
> 請[提一個 issue](https://github.com/hyhmrright/brooks-lint/issues/new),附上平台、版本和你看到的結果。
|
||||
> 用的是其他相容 Agent Skills 的 agent?它幾乎肯定以同樣方式運作——告訴我們,我們會補上。
|
||||
|
||||
## 斜線命令
|
||||
|
||||
### Claude Code
|
||||
| 命令 | 短命令 | 作用 |
|
||||
|---------|------------|--------|
|
||||
| `/brooks-lint:brooks-review` | `/brooks-review` | PR 級程式碼審查 |
|
||||
| `/brooks-lint:brooks-audit` | `/brooks-audit` | 完整架構審查 |
|
||||
| `/brooks-lint:brooks-debt` | `/brooks-debt` | 技術債評估 |
|
||||
| `/brooks-lint:brooks-test` | `/brooks-test` | 測試套件健康審查 |
|
||||
| `/brooks-lint:brooks-health` | `/brooks-health` | 健康儀表板——全部四個維度 |
|
||||
| `/brooks-lint:brooks-sweep` | `/brooks-sweep` | 全面掃描——分析所有維度並自動修復 |
|
||||
|
||||
> 短命令由 session-start 掛鉤在首次工作階段啟動時自動安裝。
|
||||
|
||||
### Gemini CLI
|
||||
| 命令 | 作用 |
|
||||
|---------|--------|
|
||||
| `/brooks-review` | PR 級程式碼審查 |
|
||||
| `/brooks-audit` | 完整架構審查 |
|
||||
| `/brooks-debt` | 技術債評估 |
|
||||
| `/brooks-test` | 測試套件健康審查 |
|
||||
| `/brooks-health` | 健康儀表板——全部四個維度 |
|
||||
| `/brooks-sweep` | 全面掃描——分析所有維度並自動修復 |
|
||||
|
||||
### Codex CLI
|
||||
|
||||
| 命令 | 作用 |
|
||||
|---------|--------|
|
||||
| `$brooks-review` | PR 級程式碼審查 |
|
||||
| `$brooks-audit` | 完整架構審查 |
|
||||
| `$brooks-debt` | 技術債評估 |
|
||||
| `$brooks-test` | 測試套件健康審查 |
|
||||
| `$brooks-health` | 健康儀表板——全部四個維度 |
|
||||
| `$brooks-sweep` | 全面掃描——分析所有維度並自動修復 |
|
||||
|
||||
當你討論程式碼品質、架構、可維護性或測試健康時,這些技能也會自動觸發。
|
||||
|
||||
### OpenCode · Cursor · Antigravity · pi
|
||||
|
||||
這些平台依據每個技能的 `description` 自動呼叫 Agent Skills——直接提問(「審查這個 PR」、「稽核架構」、
|
||||
「我們最糟的技術債在哪」)就會執行對應模式。需要顯式呼叫時,使用各平台的技能命令語法(例如 pi 把每個技能註冊為
|
||||
`/skill:brooks-review`;Cursor 與 OpenCode 在技能被探索到後暴露 `/brooks-review`)。
|
||||
|
||||
## 使用
|
||||
|
||||
### PR 審查
|
||||
|
||||
```
|
||||
/brooks-review # Claude Code(短命令)/ Gemini CLI
|
||||
/brooks-lint:brooks-review # Claude Code(完整形式)
|
||||
$brooks-review # Codex CLI
|
||||
```
|
||||
|
||||
貼上一段 diff,或讓 AI 指向改動的檔案。它會以 症狀 → 根源 → 後果 → 對策 的格式,逐一診斷六類衰退風險並給出具體診斷。
|
||||
|
||||
### 架構審查
|
||||
|
||||
```
|
||||
/brooks-audit # Claude Code(短命令)/ Gemini CLI
|
||||
/brooks-lint:brooks-audit # Claude Code(完整形式)
|
||||
$brooks-audit # Codex CLI
|
||||
```
|
||||
|
||||
描述你的專案結構或分享關鍵檔案。它會梳理模組相依、識別循環相依,並檢查是否符合康威定律。
|
||||
|
||||
### 技術債評估
|
||||
|
||||
```
|
||||
/brooks-debt # Claude Code(短命令)/ Gemini CLI
|
||||
/brooks-lint:brooks-debt # Claude Code(完整形式)
|
||||
$brooks-debt # Codex CLI
|
||||
```
|
||||
|
||||
按六類衰退風險對技術債分類,以 痛感 × 擴散面 為每條診斷打優先順序,產出帶 Critical / Scheduled / Monitored 分級的償還路線圖。
|
||||
|
||||
### 測試品質審查
|
||||
|
||||
```
|
||||
/brooks-test # Claude Code(短命令)/ Gemini CLI
|
||||
/brooks-lint:brooks-test # Claude Code(完整形式)
|
||||
$brooks-test # Codex CLI
|
||||
```
|
||||
|
||||
對照六類測試空間衰退風險審查你的測試套件——測試晦澀、測試脆弱、測試重複、Mock 濫用、覆蓋率幻覺、架構錯配——出處為 xUnit Test Patterns、The Art of Unit Testing、How Google Tests Software 和 Working Effectively with Legacy Code。PR 審查還會自動包含一個輕量的第 7 步快速測試檢查(對純文件或非生產程式碼 diff 會略過)。
|
||||
|
||||
### 健康儀表板
|
||||
|
||||
```
|
||||
/brooks-health # Claude Code(短命令)/ Gemini CLI
|
||||
/brooks-lint:brooks-health # Claude Code(完整形式)
|
||||
$brooks-health # Codex CLI
|
||||
```
|
||||
|
||||
對全部四個品質維度做精簡掃描,產出加權綜合健康分(0–100)。適合發版前、新團隊上手時,或任何你想要一份「我們現在怎麼樣?」全局報告的場景。需要某個維度的深度診斷時,請改用對應的專項技能。
|
||||
|
||||
### 全面掃描
|
||||
|
||||
```
|
||||
/brooks-sweep # Claude Code(短命令)/ Gemini CLI
|
||||
/brooks-lint:brooks-sweep # Claude Code(完整形式)
|
||||
$brooks-sweep # Codex CLI
|
||||
```
|
||||
|
||||
一次性掃描全部生產(R1–R6)與測試(T1–T6)衰退風險以及架構,然後施加修復:安全改動立即自動套用,跨檔案或觸及介面的改動需確認,複雜的架構決策則標記為人工處理項。輸出修復記錄、健康分變化和遺留項清單。
|
||||
|
||||
## 設定
|
||||
|
||||
在專案根目錄放一個 `.brooks-lint.yaml` 來客製化審查行為:
|
||||
|
||||
```yaml
|
||||
version: 1
|
||||
|
||||
strictness: balanced # strict | balanced(預設)| legacy-friendly——對遺留程式碼更寬鬆的評分
|
||||
|
||||
disable:
|
||||
- T5 # 略過覆蓋率指標檢查——我們不強制覆蓋率
|
||||
|
||||
severity:
|
||||
R1: suggestion # 在該領域下調「認知過載」診斷的嚴重度
|
||||
|
||||
ignore:
|
||||
- "**/*.generated.*"
|
||||
- "**/vendor/**"
|
||||
|
||||
# custom_risks: # 定義專案專屬 Cx 風險碼——見 skills/_shared/custom-risks-guide.md
|
||||
# suppress: # 按風險碼 + 路徑下調特定診斷(如已接受的遺留債務)
|
||||
```
|
||||
|
||||
可複製 [`.brooks-lint.example.yaml`](.brooks-lint.example.yaml) 作為起點。
|
||||
所有設定均為選用——完全省略該檔案即使用預設行為。
|
||||
|
||||
| 設定 | 說明 |
|
||||
|---------|-------------|
|
||||
| `strictness` | 評分預設:`strict`、`balanced`(預設)或 `legacy-friendly`(更輕的扣分,並優先列出高槓桿修復項) |
|
||||
| `disable` | 要略過的風險碼(`R1`–`R6`、`T1`–`T6`) |
|
||||
| `severity` | 覆寫嚴重度等級(`critical` / `warning` / `suggestion`) |
|
||||
| `ignore` | 要排除的檔案 glob 模式 |
|
||||
| `focus` | 只評估這些風險碼(不能與 `disable` 同時使用) |
|
||||
| `custom_risks` | 定義專案專屬風險碼(`C1`、`C2`……)——見 [`custom-risks-guide.md`](skills/_shared/custom-risks-guide.md) |
|
||||
| `suppress` | 按風險碼 + 路徑下調特定診斷的嚴重度(可帶 `expires:` 到期日期) |
|
||||
|
||||
---
|
||||
|
||||
## 為什麼是這些書,為什麼是現在?
|
||||
|
||||
在 AI 輔助編程的時代,我們寫程式碼比以往任何時候都更快、更多。但六十年軟體工程沉澱下來的洞見並沒有改變:
|
||||
|
||||
> *"軟體的複雜性是本質屬性,而非偶然屬性。"*
|
||||
> —— Frederick Brooks
|
||||
|
||||
AI 能幫你更快地寫程式碼,卻無法告訴你正在建造的是大教堂還是焦油坑。**brooks-lint 彌合了這道鴻溝**——它把十二本經典工程著作中得來不易的智慧,帶進你現代的開發工作流程。
|
||||
|
||||
這些作者識別出的衰退風險,如今比以往更切題:
|
||||
- **接入 AI 助手** 並不能修復認知過載或領域模型失真
|
||||
- **產生更多程式碼** 會加劇變更擴散和知識重複
|
||||
- **跑得更快** 讓偶發複雜度和相依失序更加危險
|
||||
|
||||
## 專案結構
|
||||
|
||||
```
|
||||
brooks-lint/
|
||||
├── .claude-plugin/ # Claude Code 外掛中繼資料
|
||||
├── .codex-plugin/ # Codex CLI 外掛中繼資料
|
||||
├── skills/
|
||||
│ ├── _shared/ # 共享框架檔案
|
||||
│ │ ├── common.md # 鐵律、專案設定、報告範本、健康分
|
||||
│ │ ├── source-coverage.md # 12 本書涵蓋矩陣、權衡、誤報防護
|
||||
│ │ ├── decay-risks.md # 六類衰退風險及症狀與書目出處
|
||||
│ │ ├── test-decay-risks.md # 六類測試空間衰退風險及書目出處
|
||||
│ │ ├── remedy-guide.md # --fix 模式:可落地的對策強化規則
|
||||
│ │ └── custom-risks-guide.md # 專案自訂風險碼範本
|
||||
│ ├── brooks-review/ # 模式 1:PR 審查
|
||||
│ │ ├── SKILL.md
|
||||
│ │ └── pr-review-guide.md
|
||||
│ ├── brooks-audit/ # 模式 2:架構審查
|
||||
│ │ ├── SKILL.md
|
||||
│ │ └── architecture-guide.md
|
||||
│ ├── brooks-debt/ # 模式 3:技術債評估
|
||||
│ │ ├── SKILL.md
|
||||
│ │ └── debt-guide.md
|
||||
│ ├── brooks-test/ # 模式 4:測試品質審查
|
||||
│ │ ├── SKILL.md
|
||||
│ │ └── test-guide.md
|
||||
│ ├── brooks-health/ # 模式 5:健康儀表板
|
||||
│ │ ├── SKILL.md
|
||||
│ │ └── health-guide.md
|
||||
│ └── brooks-sweep/ # 模式 6:全面掃描與自動修復
|
||||
│ ├── SKILL.md
|
||||
│ └── sweep-guide.md
|
||||
├── hooks/ # SessionStart 掛鉤
|
||||
├── commands/ # 短命令包裝(由掛鉤自動安裝)
|
||||
├── evals/ # 基準測試案例
|
||||
│ └── evals.json
|
||||
└── assets/
|
||||
└── logo.svg
|
||||
```
|
||||
|
||||
## CI/CD 整合
|
||||
|
||||
用 GitHub Action 在每個 PR 上自動執行 brooks-lint:
|
||||
|
||||
```yaml
|
||||
# .github/workflows/brooks-lint.yml
|
||||
name: Brooks-Lint PR Review
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
brooks-lint:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: hyhmrright/brooks-lint/.github/actions/brooks-lint@main
|
||||
with:
|
||||
mode: review
|
||||
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
fail-below: 70
|
||||
```
|
||||
|
||||
完整範本見 [`docs/github-action-example.yml`](docs/github-action-example.yml)。
|
||||
|
||||
該 Action 會把審查結果作為 PR 留言發布,並可在健康分跌破閾值時讓檢查失敗。若儲存庫中提交了 `.brooks-lint-history.json`,留言還會包含趨勢變化(如 "85 → 82(−3),近 3 次執行")。
|
||||
|
||||
**品質閘門與 Code Scanning。** 除 `fail-below` 外,該 Action 還提供:
|
||||
|
||||
```yaml
|
||||
with:
|
||||
mode: review
|
||||
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
fail-on: critical # 出現任何 Critical 即失敗(none | warning | critical)
|
||||
fail-on-regression: true # 健康分較上次執行下降則失敗
|
||||
sarif-file: brooks-lint.sarif # 同時把診斷上傳到 GitHub Code Scanning
|
||||
```
|
||||
|
||||
`fail-on-regression` 讀取 `.brooks-lint-history.json`,因此提交該檔案即可強制「無新增回歸」。設定 `sarif-file` 會讓診斷直接顯示在 PR 的 **Files changed** 分頁,並需要 job 具備 `security-events: write` 權限。
|
||||
|
||||
**成本:** 每次 PR 執行約 $0.05–0.15,取決於 diff 大小和模型。建議僅在 `pull_request` 事件上執行。
|
||||
|
||||
## 路線圖
|
||||
|
||||
> **目前狀態(v1.4):** 12 本書地基,6 類生產衰退風險(R1–R6)+ 6 類測試衰退風險(T1–T6),6 個技能——PR 審查、架構審查、技術債、測試品質、健康儀表板、全量掃描——外加 CI 品質閘門、面向 GitHub Code Scanning 的 SARIF 輸出、嚴格度預設,以及一個可復現的 parser 保真度基準。下方較早的條目記錄的是歷史里程碑,而非目前功能集。
|
||||
|
||||
- [x] **v0.2**:外掛基礎設施(`.claude-plugin/`、掛鉤、斜線命令)
|
||||
- [x] **v0.3**:八個 Brooks 維度、文件完整度評分
|
||||
- [x] **v0.4**:六本書框架、衰退風險維度、診斷鏈、基準套件
|
||||
- [x] **v0.5**:測試品質審查(模式 4)——四本測試書、六類測試衰退風險
|
||||
- [x] **v0.6**:架構審查中的 Mermaid 相依圖
|
||||
- [x] **v0.7**:`.brooks-lint.yaml` 專案設定、模式 2 主動上下文、擴展到 10 本書
|
||||
- [x] **v0.8**:帶命名空間命令的獨立技能架構
|
||||
- [x] **v0.9**:步驟校驗、自動 diff 範圍、`/brooks-health` 儀表板、趨勢追蹤、分診模式、`--fix` 對策、上手報告、GitHub Action
|
||||
- [x] **v1.0**:評測自動化(`run-evals-live.mjs`)、自訂風險擴充(`Cx` 碼)
|
||||
- [x] **v1.1**:全量掃描技能(`brooks-sweep`)——跨維度統一分析 + 自動修復
|
||||
- [x] **v1.2**:自主化 sweep 管線、`npm run bump` 版本傳播
|
||||
- [x] **v1.3**:Codex 市集中繼資料、多平台一鍵安裝指令碼、雙語 README + 落地頁
|
||||
- [x] **v1.4**:SARIF 輸出(GitHub Code Scanning)、CI severity + 回歸閘門、嚴格度預設(strict/balanced/legacy-friendly)、57 情境 eval 套件、可復現的 parser 保真度基準(`npm run benchmark`)
|
||||
|
||||
想出一份力?現在最有價值的貢獻是新的評測案例和更好的衰退風險症狀模式。見 [CONTRIBUTING.md](CONTRIBUTING.md)。
|
||||
|
||||
## 貢獻
|
||||
|
||||
如何新增診斷、改進指南或擴充基準套件,見 [CONTRIBUTING.md](CONTRIBUTING.md)。
|
||||
|
||||
在你自己的 PR 上跑一遍 `/brooks-review`——我們用正在打造的工具來審查貢獻。
|
||||
|
||||
## 授權條款
|
||||
|
||||
MIT License——詳見 [LICENSE](LICENSE)。
|
||||
|
||||
## 致謝
|
||||
|
||||
本專案站在十二位巨人的肩膀上:
|
||||
|
||||
**生產程式碼框架**
|
||||
- Frederick P. Brooks Jr. — *The Mythical Man-Month*(1975,紀念版 1995)
|
||||
- Steve McConnell — *Code Complete*(1993,第 2 版 2004)
|
||||
- Martin Fowler — *Refactoring*(1999,第 2 版 2018)
|
||||
- Robert C. Martin — *Clean Architecture*(2017)
|
||||
- Andrew Hunt & David Thomas — *The Pragmatic Programmer*(1999,20 週年版 2019)
|
||||
- Eric Evans — *Domain-Driven Design*(2003)
|
||||
- John Ousterhout — *A Philosophy of Software Design*(2018)
|
||||
- Titus Winters、Tom Manshreck、Hyrum Wright — *Software Engineering at Google*(2020)
|
||||
|
||||
**測試品質框架**
|
||||
- Gerard Meszaros — *xUnit Test Patterns*(2007)
|
||||
- Roy Osherove — *The Art of Unit Testing*(2009,第 3 版 2023)
|
||||
- Google Engineering — *How Google Tests Software*(2012)
|
||||
- Michael Feathers — *Working Effectively with Legacy Code*(2004)
|
||||
|
||||
本工具中編碼的衰退風險,是我們對他們思想的綜合,並應用於現代程式碼品質評估。
|
||||
|
||||
---
|
||||
|
||||
## Star 歷史
|
||||
|
||||
[](https://star-history.com/#hyhmrright/brooks-lint&Date)
|
||||
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
<strong>⭐ 如果這個工具讓你以不同的眼光看待自己的程式碼庫,請給它點個 star!</strong>
|
||||
</p>
|
||||
@@ -1,8 +1,8 @@
|
||||
# Source
|
||||
|
||||
- Repo: https://github.com/hyhmrright/brooks-lint
|
||||
- Ref: c77dcc655d7a39f2cf7134e7255434b9e07f5716
|
||||
- Ref: f4af0b27315ca3bdcdcdefb5ee547d1ea8b40ad9
|
||||
- Remove-Paths:
|
||||
- Snapshot: 2026-06-21
|
||||
- Snapshot: 2026-06-27
|
||||
- Sync-Mode: copy_skill_dirs
|
||||
- Notes: vendored into playbook branch thirdparty/skill
|
||||
|
||||
@@ -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 |
@@ -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 |
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
// Generates the "diagnostic pipeline" hero banner as one SVG per language.
|
||||
// Layout is identical across languages; only the node text changes.
|
||||
// Run: node scripts/gen-banner.mjs → writes assets/banner-<lang>.svg
|
||||
import { writeFileSync } from "node:fs";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { dirname, join } from "node:path";
|
||||
|
||||
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
// Mirrored to both dirs, matching the repo's logo.svg / demo.gif convention:
|
||||
// assets/ for the READMEs, docs/ for the GitHub Pages site.
|
||||
const OUT_DIRS = ["assets", "docs"].map((d) => join(ROOT, d));
|
||||
|
||||
// Four-stage pipeline: input → sources → risks → output.
|
||||
// Each node carries an accent dot color drawn from the logo palette.
|
||||
const ACCENTS = ["#3b82f6", "#6366f1", "#b07d2b", "#22c55e"];
|
||||
|
||||
// main = short headline, sub = supporting detail. Kept short so centered
|
||||
// text never overflows the fixed node width across scripts (CJK + Latin).
|
||||
const LANGS = {
|
||||
"en": [
|
||||
{ main: "Your code", sub: "PR or whole repo" },
|
||||
{ main: "12 classics", sub: "Brooks · Fowler · Martin" },
|
||||
{ main: "12 decay risks", sub: "R1–R6 · T1–T6" },
|
||||
{ main: "Cited findings", sub: "Symptom → Remedy" },
|
||||
],
|
||||
"zh-CN": [
|
||||
{ main: "你的代码", sub: "PR 或整个仓库" },
|
||||
{ main: "十二本经典", sub: "布鲁克斯 · 福勒 · 马丁" },
|
||||
{ main: "十二类衰退风险", sub: "R1–R6 · T1–T6" },
|
||||
{ main: "带出处的结论", sub: "症状 → 对策" },
|
||||
],
|
||||
"zh-TW": [
|
||||
{ main: "你的程式碼", sub: "PR 或整個儲存庫" },
|
||||
{ main: "十二本經典", sub: "布魯克斯 · 福勒 · 馬丁" },
|
||||
{ main: "十二類衰退風險", sub: "R1–R6 · T1–T6" },
|
||||
{ main: "帶出處的結論", sub: "症狀 → 對策" },
|
||||
],
|
||||
"ja": [
|
||||
{ main: "あなたのコード", sub: "PR / リポジトリ全体" },
|
||||
{ main: "古典12冊", sub: "Brooks · Fowler · Martin" },
|
||||
{ main: "12の劣化リスク", sub: "R1–R6 · T1–T6" },
|
||||
{ main: "出典付きの指摘", sub: "症状 → 対策" },
|
||||
],
|
||||
"ko": [
|
||||
{ main: "당신의 코드", sub: "PR / 전체 저장소" },
|
||||
{ main: "고전 12권", sub: "Brooks · Fowler · Martin" },
|
||||
{ main: "12가지 리스크", sub: "R1–R6 · T1–T6" },
|
||||
{ main: "출처 있는 진단", sub: "증상 → 처방" },
|
||||
],
|
||||
"es": [
|
||||
{ main: "Tu código", sub: "PR o repo completo" },
|
||||
{ main: "12 clásicos", sub: "Brooks · Fowler · Martin" },
|
||||
{ main: "12 riesgos", sub: "de deterioro · R1–T6" },
|
||||
{ main: "Hallazgos", sub: "Síntoma → Remedio" },
|
||||
],
|
||||
};
|
||||
|
||||
const W = 1040, H = 200;
|
||||
const NODE_W = 210, NODE_H = 128, NODE_Y = 36;
|
||||
const GAP = (W - 40 - NODE_W * 4) / 3; // even gaps, 20px outer margin
|
||||
const FONT = `-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans", "Microsoft YaHei", "Malgun Gothic", sans-serif`;
|
||||
|
||||
const esc = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
const nodeX = (i) => Math.round(20 + i * (NODE_W + GAP));
|
||||
|
||||
function node(i, { main, sub }) {
|
||||
const x = nodeX(i), cx = x + NODE_W / 2;
|
||||
return `
|
||||
<g>
|
||||
<rect x="${x}" y="${NODE_Y}" width="${NODE_W}" height="${NODE_H}" rx="16"
|
||||
fill="#fffdf8" stroke="#e3d8c2" stroke-width="1.5"/>
|
||||
<rect x="${x}" y="${NODE_Y + NODE_H - 4}" width="${NODE_W}" height="4" rx="2" fill="${ACCENTS[i]}"/>
|
||||
<circle cx="${cx}" cy="${NODE_Y + 30}" r="7" fill="${ACCENTS[i]}"/>
|
||||
<text x="${cx}" y="${NODE_Y + 72}" text-anchor="middle" font-family='${FONT}'
|
||||
font-size="19" font-weight="700" fill="#2a2520">${esc(main)}</text>
|
||||
<text x="${cx}" y="${NODE_Y + 98}" text-anchor="middle" font-family='${FONT}'
|
||||
font-size="13" fill="#5c5347">${esc(sub)}</text>
|
||||
</g>`;
|
||||
}
|
||||
|
||||
function arrow(i) {
|
||||
const start = nodeX(i) + NODE_W, end = nodeX(i + 1);
|
||||
const y = NODE_Y + NODE_H / 2;
|
||||
const mid = (start + end) / 2;
|
||||
return `
|
||||
<g stroke="#b07d2b" stroke-width="2.5" fill="none" stroke-linecap="round">
|
||||
<line x1="${start + 6}" y1="${y}" x2="${end - 10}" y2="${y}"/>
|
||||
<polyline points="${end - 16},${y - 6} ${end - 8},${y} ${end - 16},${y + 6}" stroke-linejoin="round"/>
|
||||
</g>`;
|
||||
}
|
||||
|
||||
function svg(nodes) {
|
||||
const body = nodes.map((n, i) => node(i, n)).join("") +
|
||||
[0, 1, 2].map(arrow).join("");
|
||||
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${W} ${H}" width="${W}" height="${H}" role="img" aria-label="brooks-lint diagnostic pipeline">${body}
|
||||
</svg>
|
||||
`;
|
||||
}
|
||||
|
||||
for (const [lang, nodes] of Object.entries(LANGS)) {
|
||||
const content = svg(nodes);
|
||||
for (const dir of OUT_DIRS) {
|
||||
const file = join(dir, `banner-${lang}.svg`);
|
||||
writeFileSync(file, content);
|
||||
console.log(`wrote ${file}`);
|
||||
}
|
||||
}
|
||||