@luanpdd/kit-mcp 1.10.0 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/gates/ai-prompt-stability.md +120 -0
  2. package/gates/legacy-refactor-safety.md +178 -0
  3. package/gates/observability-coverage.md +151 -0
  4. package/gates/release-pipeline-policy.md +132 -0
  5. package/kit/COMANDOS.md +15 -0
  6. package/kit/agents/ai-mutation-tester.md +298 -0
  7. package/kit/agents/cascading-failures-auditor.md +306 -0
  8. package/kit/agents/executor.md +13 -0
  9. package/kit/agents/legacy-characterizer.md +378 -0
  10. package/kit/agents/load-shedding-instrumenter.md +297 -0
  11. package/kit/agents/observability-coverage-auditor.md +325 -0
  12. package/kit/agents/omm-auditor.md +47 -0
  13. package/kit/agents/payload-capture-instrumenter.md +283 -0
  14. package/kit/agents/planner.md +29 -0
  15. package/kit/agents/prr-conductor.md +8 -0
  16. package/kit/agents/refactor-safety-auditor.md +414 -0
  17. package/kit/agents/release-pipeline-auditor.md +360 -0
  18. package/kit/agents/seam-finder.md +367 -0
  19. package/kit/agents/shotgun-surgery-detector.md +359 -0
  20. package/kit/agents/storytelling-analyst.md +309 -0
  21. package/kit/agents/supabase-edge-fn-writer.md +12 -0
  22. package/kit/agents/verifier.md +30 -0
  23. package/kit/commands/auditar-cascading.md +111 -0
  24. package/kit/commands/auditar-marco.md +44 -1
  25. package/kit/commands/auditar-observabilidade-cobertura.md +183 -0
  26. package/kit/commands/auditar-refactor.md +219 -0
  27. package/kit/commands/auditar-release.md +109 -0
  28. package/kit/commands/capturar-payloads.md +193 -0
  29. package/kit/commands/caracterizar-prompt.md +195 -0
  30. package/kit/commands/caracterizar.md +212 -0
  31. package/kit/commands/concluir-marco.md +41 -1
  32. package/kit/commands/detectar-duplicacao.md +197 -0
  33. package/kit/commands/discutir-fase.md +41 -0
  34. package/kit/commands/encontrar-seams.md +136 -0
  35. package/kit/commands/forense.md +40 -1
  36. package/kit/commands/legacy.md +263 -0
  37. package/kit/commands/load-shedding.md +117 -0
  38. package/kit/commands/observabilidade.md +2 -0
  39. package/kit/commands/refactor-seguro.md +321 -0
  40. package/kit/commands/sre.md +3 -0
  41. package/kit/commands/storytelling.md +179 -0
  42. package/kit/skills/_shared-legacy/glossary.md +389 -0
  43. package/kit/skills/_shared-sre/glossary.md +139 -0
  44. package/kit/skills/ai-prompt-characterization/SKILL.md +335 -0
  45. package/kit/skills/cascading-failures/SKILL.md +307 -0
  46. package/kit/skills/four-golden-signals/SKILL.md +17 -0
  47. package/kit/skills/hermetic-builds/SKILL.md +323 -0
  48. package/kit/skills/legacy-api-only-applications/SKILL.md +358 -0
  49. package/kit/skills/legacy-characterization-tests/SKILL.md +330 -0
  50. package/kit/skills/legacy-effect-analysis/SKILL.md +331 -0
  51. package/kit/skills/legacy-extract-class/SKILL.md +203 -0
  52. package/kit/skills/legacy-monster-methods/SKILL.md +444 -0
  53. package/kit/skills/legacy-programming-by-difference/SKILL.md +252 -0
  54. package/kit/skills/legacy-seams-and-test-harness/SKILL.md +460 -0
  55. package/kit/skills/legacy-shotgun-surgery/SKILL.md +286 -0
  56. package/kit/skills/legacy-sprout-wrap-techniques/SKILL.md +434 -0
  57. package/kit/skills/legacy-storytelling-naked-crc/SKILL.md +270 -0
  58. package/kit/skills/llm-as-dependency/SKILL.md +436 -0
  59. package/kit/skills/load-shedding-graceful-degradation/SKILL.md +396 -0
  60. package/kit/skills/pre-refactor-characterization/SKILL.md +421 -0
  61. package/kit/skills/release-engineering/SKILL.md +367 -0
  62. package/kit/skills/retry-strategies/SKILL.md +372 -0
  63. package/package.json +2 -2
@@ -0,0 +1,203 @@
1
+ ---
2
+ name: legacy-extract-class
3
+ description: Use ao identificar classes "muito grandes" (cap 20 Feathers) com responsibility hot spots — extract class para separar responsabilidades. Aplicado a domain classes Supabase (OrderService → OrderValidator + Repository + Notifier).
4
+ ---
5
+
6
+ # Legacy — Extract Class
7
+
8
+ ## Quando usar
9
+
10
+ LLM carrega esta skill quando uma classe ultrapassa thresholds de tamanho/complexidade ou tem múltiplas responsabilidades distintas. Trigger phrases:
11
+
12
+ - "essa classe é grande demais", "this class is too big"
13
+ - "extract class", "extrair classe", "split class"
14
+ - "responsibility hot spot", "single responsibility"
15
+ - "cap 20 Feathers"
16
+ - "OrderService faz validação, persistência E notificação"
17
+ - arquivo de classe > 300 linhas com múltiplos métodos públicos não-relacionados
18
+
19
+ ## Regras absolutas
20
+
21
+ - **Heurísticas de "classe muito grande":** > 300 linhas; > 10 fields; > 12 métodos públicos; > 3 responsabilidades distintas (hot spots). Pelo menos 2 dos 4 = candidato.
22
+ - **Extract class != extract method.** Extract method move bloco contíguo dentro da mesma classe; extract class move responsabilidade INTEIRA para classe nova com state próprio.
23
+ - **Identifique o "hot spot" primeiro.** Cluster de fields + métodos que se referenciam mais entre si do que com o resto da classe = unidade extraível.
24
+ - **Compilação verde a cada commit.** Pequenos passos: (1) criar classe nova vazia; (2) mover 1 field; (3) ajustar callers; (4) repetir.
25
+ - **Não introduza herança especulativa.** Extract class produz composição (classe original tem field do tipo extraído); herança só se faz parte do design intencional.
26
+ - **Aplicação canônica em Supabase domain classes:** `OrderService` que faz validação + persistência + notificação + audit → extract para `OrderValidator`, `OrderRepository`, `OrderNotifier`, `OrderAuditor` (modernização: SRP em layer de domínio).
27
+
28
+ ## Patterns canônicos
29
+
30
+ ### Pattern 1: Detecção de hot spots
31
+
32
+ ```text
33
+ Para cada field e método da classe alvo:
34
+ Crie matriz de coupling — quem usa quem.
35
+ Cluster fields/métodos que SE REFERENCIAM mais.
36
+ Cluster com 2+ fields E 3+ métodos = hot spot extraível.
37
+
38
+ Exemplo — OrderService 450 linhas:
39
+ Cluster A (validação):
40
+ fields: validators[], strictMode
41
+ methods: validate, validateField, addCustomValidator
42
+ Cluster B (persistência):
43
+ fields: db, cache
44
+ methods: save, findById, findRecent
45
+ Cluster C (notificação):
46
+ fields: notifier, templateEngine
47
+ methods: notifyCustomer, formatNotification
48
+
49
+ → 3 hot spots → 3 extract class candidatos.
50
+ ```
51
+
52
+ ### Pattern 2: Workflow de extract class
53
+
54
+ ```text
55
+ 1. Criar classe nova vazia com nome descritivo
56
+ class OrderValidator {}
57
+
58
+ 2. Mover 1 field (e seus referentes diretos)
59
+ - Mover field
60
+ - Adicionar field correspondente na classe original APONTANDO para nova
61
+ - Ajustar todos os usos (this.X → this.validator.X)
62
+ - Compilar verde
63
+
64
+ 3. Mover 1 método relacionado
65
+ - Mover método (com edits triviais para acessar fields da classe nova)
66
+ - Atualizar callers
67
+ - Compilar verde
68
+
69
+ 4. Repetir para cada field/método do hot spot
70
+ 5. Adicionar test harness para classe nova (agora menor → fácil testar)
71
+ 6. (Opcional) extract interface se existem múltiplas implementações
72
+ ```
73
+
74
+ ### Pattern 3: Aplicação canônica Supabase
75
+
76
+ ```ts
77
+ // ANTES — OrderService 450 linhas, 4 responsabilidades
78
+ class OrderService {
79
+ constructor(private db: SupabaseClient, private notifier: SmtpClient) {}
80
+
81
+ // validation
82
+ validate(order: Order): ValidationResult { /* 80 linhas */ }
83
+ validateField(field: string, value: any) { /* ... */ }
84
+
85
+ // persistence
86
+ async save(order: Order) { await this.db.from('orders').insert(...) }
87
+ async findById(id: string) { return this.db.from('orders').select().eq('id', id) }
88
+
89
+ // notification
90
+ async notifyCustomer(order: Order) { /* template + smtp send */ }
91
+
92
+ // audit
93
+ async logAudit(action: string, order: Order) { /* db audit_log insert */ }
94
+
95
+ // orchestration (entrypoint)
96
+ async place(order: Order): Promise<Result> {
97
+ const v = this.validate(order)
98
+ if (!v.ok) return { error: v.errors }
99
+ await this.save(order)
100
+ await this.notifyCustomer(order)
101
+ await this.logAudit('placed', order)
102
+ return { ok: true }
103
+ }
104
+ }
105
+
106
+ // DEPOIS — 4 classes coesas + orquestrador enxuto
107
+ class OrderValidator { validate(order: Order): ValidationResult { /* 60 linhas */ } }
108
+
109
+ class OrderRepository {
110
+ constructor(private db: SupabaseClient) {}
111
+ async save(order: Order) { /* ... */ }
112
+ async findById(id: string) { /* ... */ }
113
+ }
114
+
115
+ class OrderNotifier {
116
+ constructor(private smtp: SmtpClient) {}
117
+ async notifyCustomer(order: Order) { /* ... */ }
118
+ }
119
+
120
+ class OrderAuditor {
121
+ constructor(private db: SupabaseClient) {}
122
+ async log(action: string, order: Order) { /* ... */ }
123
+ }
124
+
125
+ class OrderService {
126
+ constructor(
127
+ private validator = new OrderValidator(),
128
+ private repo: OrderRepository,
129
+ private notifier: OrderNotifier,
130
+ private auditor: OrderAuditor,
131
+ ) {}
132
+
133
+ async place(order: Order): Promise<Result> {
134
+ const v = this.validator.validate(order)
135
+ if (!v.ok) return { error: v.errors }
136
+ await this.repo.save(order)
137
+ await this.notifier.notifyCustomer(order)
138
+ await this.auditor.log('placed', order)
139
+ return { ok: true }
140
+ }
141
+ }
142
+ // OrderService final: ~25 linhas. Cada classe extraída testável isolada.
143
+ ```
144
+
145
+ ### Pattern 4: Effort budget
146
+
147
+ | Tamanho classe | Hot spots típicos | Esforço extract class | Output |
148
+ |---|---|---|---|
149
+ | 300-500 linhas | 2-3 | 1-2 dias | classe principal + 2-3 extracted classes |
150
+ | 500-1000 linhas | 3-5 | 3-7 dias | classe principal + 3-5 extracted; tests acumulados |
151
+ | > 1000 linhas | 5+ | 1-3 semanas (pode requerer scratch refactoring antes) | considere extract interface se múltiplas implementações |
152
+
153
+ ## Anti-patterns
154
+
155
+ ### ANTI: extract class antes de characterization
156
+
157
+ ```text
158
+ ANTI: identificou hot spots e move tudo de uma vez.
159
+
160
+ PROBLEMA: extract class MOVE state e métodos. Sem characterization,
161
+ regressão silenciosa em ordem de chamadas, side effects,
162
+ state compartilhado.
163
+
164
+ CERTO: characterize primeiro (cap 13). Snapshots da classe ANTES.
165
+ Extract incremental. Snapshots devem continuar verdes a cada
166
+ commit (comportamento idêntico).
167
+ ```
168
+
169
+ ### ANTI: extract class sem hot spot real
170
+
171
+ ```text
172
+ ANTI: "vou extrair Helper class só pra reduzir linhas".
173
+
174
+ PROBLEMA: classe nova sem coesão. Helper recebe métodos
175
+ arbitrários. Reviewer não consegue justificar a divisão.
176
+ Resultado: god class virou god class + helper class.
177
+
178
+ CERTO: hot spot REAL = cluster de fields + métodos com forte coupling
179
+ interno. Sem cluster, não extract — refactor outro
180
+ (extract method, eliminar dead code).
181
+ ```
182
+
183
+ ## Verificação
184
+
185
+ 1. Hot spots identificados via matriz de coupling
186
+ 2. Compilação verde a cada commit
187
+ 3. Extracted classes têm coesão alta (fields + métodos relacionam-se)
188
+ 4. Classe original encolheu significativamente (≥ 30%)
189
+ 5. Tests acumulados nas classes extraídas (≥ 60% coverage)
190
+
191
+ ---
192
+
193
+ ## Ver também
194
+
195
+ - [`_shared-legacy/glossary.md`](../_shared-legacy/glossary.md) — vocabulário canônico
196
+ - [`legacy-characterization-tests`](../legacy-characterization-tests/SKILL.md) — characterize ANTES de extract class
197
+ - [`legacy-monster-methods`](../legacy-monster-methods/SKILL.md) — extract method é precursor; extract class lida com classe inteira
198
+ - [`legacy-effect-analysis`](../legacy-effect-analysis/SKILL.md) — sketch identifica hot spots antes do extract
199
+ - [`legacy-shotgun-surgery`](../legacy-shotgun-surgery/SKILL.md) — duplicação cross-class detecta candidatos a extract class
200
+ - [`supabase-architect`](../../agents/supabase-architect.md) (v1.8) — referencia extract class quando design domain inicial cresce além do esperado
201
+
202
+ *Material-fonte: Working Effectively with Legacy Code — Feathers, 2004 — Cap 20: "This Class Is Too Big and I Don't Want It to Get Any Bigger".*
203
+ *Modernização (2026):* Aplicação canônica em domain classes Supabase com SRP retroativo.
@@ -0,0 +1,444 @@
1
+ ---
2
+ name: legacy-monster-methods
3
+ description: Use ao refatorar método > 100 linhas sem testes — scratch refactoring, single-goal editing, safe extraction (cap 22 Feathers). Padrões para domesticar bulleted vs snarled methods.
4
+ ---
5
+
6
+ # Legacy — Monster Methods
7
+
8
+ ## Quando usar
9
+
10
+ LLM carrega esta skill quando o user encontra método absurdamente longo (> 100 linhas) que precisa ser modificado. Trigger phrases:
11
+
12
+ - "esse método tem [N] linhas, preciso mudar"
13
+ - "monster method", "método monstro", "método gigante"
14
+ - "scratch refactoring", "refactor de rascunho"
15
+ - "single-goal editing", "edição com objetivo único"
16
+ - "extract method em método sem testes"
17
+ - "cap 22 Feathers"
18
+ - "bulleted method", "snarled method"
19
+
20
+ Carrega quando `legacy-characterization-tests` é insuficiente isoladamente — método é grande demais para enumerar inputs, mas precisa ser refatorado para ficar testável.
21
+
22
+ ## Regras absolutas
23
+
24
+ - **Monster method = > 100 linhas OU > 5 níveis de aninhamento.** Pelo menos 1 dos dois. Heurística empírica do livro.
25
+ - **Distinguir bulleted vs snarled.** Bulleted = linhas longas mas planas (mais fácil). Snarled = nesting profundo, control flow complexo (mais difícil). Estratégias diferentes.
26
+ - **Scratch refactoring é DESCARTÁVEL.** Branch lixo, refactor estético para entender, depois `git checkout main` e descarte tudo. Só conhecimento adquirido viaja para a refatoração real.
27
+ - **Single-goal editing.** UMA mudança por commit/PR. Renomear OR extrair OR mover, NUNCA os 3 juntos. Cada commit é mecânico, isolado, revisável.
28
+ - **Extract method em monster sem teste = SAFE EXTRACTION.** Apenas levantar bloco contíguo + capturar variáveis usadas como parâmetros. SEM mover lógica entre escopos. SEM mudar comportamento. Lê → executa → escreve idênticamente antes/depois.
29
+ - **Compilação verde após CADA commit.** Passo grande demais = você não está fazendo single-goal. Quebre menor.
30
+ - **Não introduza tests "no meio" do refactor.** Refactor preserva comportamento → mesmo set de tests roda verde antes/durante/depois. Test novo significa novo comportamento (PR diferente).
31
+ - **Pequenos testes em método extraído saem GRÁTIS.** Após extract method, o método extraído é menor, tipicamente puro. Test acumulado durante refactor incremental.
32
+
33
+ ## Patterns canônicos
34
+
35
+ ### Pattern 1: Bulleted vs Snarled — diagnóstico
36
+
37
+ ```text
38
+ BULLETED METHOD
39
+ ===============
40
+ public function processOrder(order) {
41
+ validateOrder(order)
42
+ computeTotals(order)
43
+ applyDiscounts(order)
44
+ saveOrder(order)
45
+ notifyCustomer(order)
46
+ publishEvent(order)
47
+ updateAnalytics(order)
48
+ returnReceipt(order)
49
+ }
50
+ ↑ 8 linhas, todas no mesmo nível. Cada linha é praticamente extract-method-ready.
51
+
52
+ SNARLED METHOD
53
+ ==============
54
+ public function processOrder(order) {
55
+ if (order != null) {
56
+ if (order.items != null && order.items.length > 0) {
57
+ var total = 0
58
+ for (var item of order.items) {
59
+ if (item.discount) {
60
+ if (item.discount.type === 'percent') {
61
+ total += item.price * (1 - item.discount.value / 100) * item.qty
62
+ } else if (item.discount.type === 'fixed') {
63
+ total += (item.price - item.discount.value) * item.qty
64
+ } else {
65
+ total += item.price * item.qty
66
+ }
67
+ } else {
68
+ total += item.price * item.qty
69
+ }
70
+ }
71
+ // ... mais 80 linhas com nesting similar
72
+ }
73
+ }
74
+ }
75
+ ↑ control flow profundamente aninhado. Extract method não é trivial — variáveis cruzam scopes.
76
+ ```
77
+
78
+ **Diagnóstico:**
79
+ - Conte linhas no mesmo nível de indentação. Se > 70% das linhas estão no nível 1-2 → BULLETED.
80
+ - Conte profundidade máxima de aninhamento. Se ≥ 5 → SNARLED.
81
+
82
+ **Estratégias divergem** — abaixo.
83
+
84
+ ### Pattern 2: Scratch refactoring (cap 22)
85
+
86
+ Refactor descartável para ENTENDER o método antes de mudá-lo.
87
+
88
+ ```text
89
+ 1. Cria branch `scratch/<method-name>-explore`
90
+ 2. Refatora à vontade — extract aleatório, rename especulativo,
91
+ reformata estética. Goal: tornar legível para você.
92
+ 3. Lê o resultado, entende o que método faz.
93
+ 4. NÃO commita nem PR-a essa branch.
94
+ 5. `git checkout main`. Volta ao código original intocado.
95
+ 6. AGORA você tem mental model. Refatoração real começa com
96
+ passos pequenos disciplinados (single-goal editing).
97
+ ```
98
+
99
+ **Por que descartável:** scratch é estética + quebra contracts. Manter seria perigoso. Só conhecimento adquirido vai para a real.
100
+
101
+ **Quando vale o tempo:** método > 200 linhas + você nunca tinha mexido nele. < 100 linhas raramente vale (lê direto resolve).
102
+
103
+ ### Pattern 3: Single-goal editing — atomic refactoring
104
+
105
+ Cada commit faz UMA coisa. Disciplina absoluta.
106
+
107
+ ```text
108
+ ✓ COMMIT 1: rename method (mecânico, IDE-assisted)
109
+ - Antes: processOrder
110
+ - Depois: processOrderLegacy
111
+ - Diff: rename refactor automático
112
+
113
+ ✓ COMMIT 2: extract method (mecânico)
114
+ - Antes: linhas 42-58 inline em processOrderLegacy
115
+ - Depois: extracted como computeTotals(items)
116
+ - Diff: 17 linhas → 1 chamada
117
+
118
+ ✓ COMMIT 3: extract method (mecânico)
119
+ - Antes: linhas 60-85 inline
120
+ - Depois: extracted como applyDiscounts(items, discounts)
121
+ - Diff: 26 linhas → 1 chamada
122
+
123
+ ✗ COMMIT NÃO-FEITO: extract + rename + add validation
124
+ Múltiplos goals num commit = no single-goal. Difícil revisar.
125
+ ```
126
+
127
+ **Princípio:** cada commit é REVERTIBLE individualmente. Se PR3 tem bug, revert PR3 só, não a sequência inteira.
128
+
129
+ ### Pattern 4: Safe extraction em método sem testes
130
+
131
+ Extract method preservando comportamento sem ter test que valide.
132
+
133
+ ```text
134
+ SAFE EXTRACTION CHECKLIST
135
+ =========================
136
+ □ Bloco a extrair é CONTÍGUO (sem control flow saindo do meio)
137
+ □ Variáveis lidas dentro do bloco mas declaradas fora → parâmetros
138
+ □ Variáveis ESCRITAS dentro do bloco mas usadas fora → return values
139
+ □ Bloco não tem `return`, `throw`, `break`, `continue` que afete escopo externo
140
+ (se tem: extract não é seguro, escolha menor)
141
+ □ Type signatures preservadas (parâmetros e retorno bem-tipados)
142
+ □ Comportamento idêntico — antes do extract: lê X, computa Y, escreve Z;
143
+ depois: chama método extraído que lê X, computa Y, escreve Z
144
+
145
+ PROCESSO MECÂNICO
146
+ =================
147
+ 1. Selecionar bloco contíguo (10-30 linhas tipicamente)
148
+ 2. Identificar variáveis lidas vs escritas no bloco
149
+ 3. Lidas-mas-não-escritas → parâmetros (in)
150
+ 4. Escritas-mas-usadas-fora → adicionar a return value (out)
151
+ 5. IDE: extract method (Cmd+Opt+M no IntelliJ; "Refactor: Extract Function" no VS Code)
152
+ 6. Compilar — verde?
153
+ 7. Run smoke (qualquer comando manual que rodava antes) — comportamento idêntico?
154
+ 8. Commit. Próximo bloco.
155
+ ```
156
+
157
+ **Insight:** safe extraction é seguro mesmo SEM testes porque é PURELY MECHANICAL. IDE faz com 100% de fidelidade. Risco residual = bug do IDE (raríssimo) ou erro humano em copiar manualmente (não use cópia manual — use extract automation).
158
+
159
+ ### Pattern 5: Domando bulleted method
160
+
161
+ Bulleted é fácil. Workflow:
162
+
163
+ ```text
164
+ 1. Cada linha do método é uma "frase" → cada uma vira método extraído.
165
+ Antes: processOrder() com 30 linhas no nível 1
166
+ Depois: processOrder() com 8 chamadas, cada uma para um helper
167
+
168
+ 2. Após extract, helpers são MENORES → tests acumulam grátis:
169
+ public computeTotals(order) {
170
+ // 8 linhas — testável isolado agora
171
+ }
172
+ public applyDiscounts(order, codes) {
173
+ // 12 linhas — testável isolado agora
174
+ }
175
+
176
+ 3. Tests acumulados cobrem comportamento UNIT:
177
+ test('computeTotals — com 1 item', () => { ... })
178
+ test('computeTotals — com items vazios', () => { ... })
179
+
180
+ 4. processOrder() agora é orquestração — test acaba sendo
181
+ integration tipo:
182
+ test('processOrder — happy path orchestration', () => {
183
+ processOrder(typicalOrder)
184
+ expect(saved).toBeDefined()
185
+ expect(notifications).toHaveLength(1)
186
+ })
187
+
188
+ 5. Iterativamente, código vira pirâmide de tests bem definida.
189
+ ```
190
+
191
+ **Esforço típico:** método 100 linhas bulleted → 6-8 commits, 1-2 dias para refactor + 5-10 testes acumulados grátis.
192
+
193
+ ### Pattern 6: Domando snarled method
194
+
195
+ Snarled é difícil. Estratégia: APLAINAR primeiro, depois extrair.
196
+
197
+ ```text
198
+ PASSO 1 — Achatar via guard clauses (early return)
199
+ Antes: Depois:
200
+ if (order != null) { if (order == null) return ERROR
201
+ if (order.items != null) { if (order.items == null) return ERROR
202
+ if (order.items.length > 0) { if (order.items.length === 0) return EMPTY
203
+ // 80 linhas // 80 linhas (agora sem nesting)
204
+ }
205
+ }
206
+ }
207
+ ↑ 3 níveis viraram 0 níveis. Pure mechanical. Sem mudança comportamental.
208
+
209
+ PASSO 2 — Extract method em loop interno
210
+ Antes: Depois:
211
+ for (item of items) { for (item of items) {
212
+ if (item.discount) { total += computeItemTotal(item)
213
+ if (item.discount.type === 'percent') { ↑ 1 chamada
214
+ total += item.price * (1 - ...) * item.qty
215
+ } else if (...) {
216
+ ...
217
+ }
218
+ } else {
219
+ total += item.price * item.qty
220
+ }
221
+ }
222
+
223
+ function computeItemTotal(item) {
224
+ if (!item.discount) return item.price * item.qty
225
+ if (item.discount.type === 'percent')
226
+ return item.price * (1 - item.discount.value / 100) * item.qty
227
+ if (item.discount.type === 'fixed')
228
+ return (item.price - item.discount.value) * item.qty
229
+ return item.price * item.qty
230
+ }
231
+ ↑ Loop de 12 linhas → 3 linhas + função pura testável.
232
+
233
+ PASSO 3 — Iterar até nível máximo de aninhamento ≤ 2.
234
+
235
+ PASSO 4 — Tests no método extraído (puro, fácil de testar):
236
+ test('computeItemTotal — sem desconto', ...)
237
+ test('computeItemTotal — desconto percentual', ...)
238
+ test('computeItemTotal — desconto fixo', ...)
239
+ test('computeItemTotal — discount.type desconhecido', ...)
240
+ ```
241
+
242
+ **Esforço típico:** método 150 linhas snarled → 12-20 commits, 3-7 dias para refactor + 15-25 testes acumulados.
243
+
244
+ ### Pattern 7: Sequência canônica de tipos de single-goal commit
245
+
246
+ Em ordem do mais SEGURO ao mais ARRISCADO. Faça nessa ordem.
247
+
248
+ ```text
249
+ SEGURO ↓
250
+ ======================================================
251
+ 1. RENAME (variable, method, class, file)
252
+ IDE-assisted, mecânico, comportamento idêntico
253
+
254
+ 2. SAFE EXTRACTION (extract method/variable)
255
+ Selecione bloco contíguo, IDE extract, sem mover lógica entre scopes
256
+
257
+ 3. MOVE METHOD (entre classes)
258
+ Apenas se método não usa state da origem (puro relativo à classe)
259
+
260
+ 4. INTRODUCE PARAMETER OBJECT
261
+ Agrupar parâmetros relacionados em DTO. Mecânico.
262
+
263
+ 5. INVERT DEPENDENCY (constructor injection)
264
+ New X() interno → recebe X externo. Quebra encapsulation, mas
265
+ comportamento permanece (se default-arg usado).
266
+
267
+ 6. CHANGE METHOD SIGNATURE
268
+ Adicionar/remover parâmetro. Risk: callers podem passar wrong.
269
+
270
+ 7. ALGORITHM REPLACEMENT
271
+ Mudar IMPLEMENTAÇÃO mantendo contrato. Risk médio — characterization
272
+ tests obrigatórios.
273
+
274
+ 8. CONTRACT CHANGE
275
+ Mudar pre-condition/post-condition. Risk alto — todos os callers
276
+ precisam ser inspecionados.
277
+ ↑ ARRISCADO
278
+ ======================================================
279
+ ```
280
+
281
+ Stop em #5 ou #6 para refactor "limpa-e-vai". #7 e #8 = mudança comportamental, exigem characterization completa.
282
+
283
+ ### Pattern 8: Effort budget de monster method
284
+
285
+ | Tamanho | Tipo | Esforço de refactor | Output esperado |
286
+ |---|---|---|---|
287
+ | 100-150 linhas, bulleted | extract methods + acumular tests | 1-2 dias | 5-10 helpers + 5-10 tests |
288
+ | 100-150 linhas, snarled | flatten + extract | 3-5 dias | 5-10 helpers + 10-15 tests |
289
+ | 150-300 linhas, bulleted | extract class após methods | 3-7 dias | 5-10 helpers + nova classe + 10-15 tests |
290
+ | 150-300 linhas, snarled | scratch refactor + flatten + extract | 1-2 semanas | classe + 15-25 tests |
291
+ | > 300 linhas | só com aprovação stakeholder, alocação dedicada | 2-4 semanas | reescrever via sprout class? |
292
+
293
+ **Heurística:** método > 300 linhas raramente vale refactor incremental. Considere sprout class — encapsular comportamento NOVO em classe nova, deixar legado intocado, eventually deprecate.
294
+
295
+ ### Pattern 9: Cobertura emergente
296
+
297
+ Refactor de monster method NÃO precisa de characterization completa upfront se você usa safe extraction (mecânica). Mas teste se acumula:
298
+
299
+ ```text
300
+ PRE-REFACTOR (T0)
301
+ Coverage: 0% (untested)
302
+ Confiança: baixa
303
+
304
+ DURANTE REFACTOR (T1-Tn)
305
+ Cada extract method → método extraído fica testável (puro/menor)
306
+ Adicione 1-3 unit tests do extracted antes de seguir
307
+ Coverage cresce 5-10% por extract
308
+
309
+ PÓS-REFACTOR (Tfinal)
310
+ Original 100 linhas → 1 método orquestrador + 8 helpers
311
+ Cobertura: 60-80% (helpers cobertos; orquestrador integration)
312
+ Confiança: alta
313
+ ```
314
+
315
+ **Sem essa disciplina:** refactor termina, código mais limpo, mas cobertura ainda 0%. Próxima mudança volta ao mesmo dilema.
316
+
317
+ ## Anti-patterns
318
+
319
+ ### ANTI: refactor monstro em 1 PR
320
+
321
+ ```text
322
+ ANTI: PR de 1500 linhas — extracted 12 methods + renamed 8 variables
323
+ + moved 3 fields + fixed 2 bugs + adicionou 25 tests.
324
+
325
+ PROBLEMA: PR não-revisável. Reviewer aprova "no fé". CI verde diz
326
+ pouco — branch coverage caiu. Revert é all-or-nothing.
327
+
328
+ CERTO: 12-25 commits/PRs em sequência. Cada um single-goal,
329
+ ≤ 100 linhas, mecânico, revertível, com proof of correctness
330
+ (compila + roda).
331
+ ```
332
+
333
+ ### ANTI: misturar refactor + bug fix + feature
334
+
335
+ ```text
336
+ ANTI: enquanto refatora, "ah esse if pode ser melhor", "esse loop
337
+ podia usar reduce", "ah aqui tem um bug, conserto".
338
+
339
+ PROBLEMA: você quebrou single-goal em ~5 lugares. Reviewer não consegue
340
+ identificar o que é refactor (preserva) vs bug fix (muda).
341
+ Se algo quebra, bisect aponta para PR mas não isola causa.
342
+
343
+ CERTO: anote bugs encontrados num arquivo `BUGS-FOUND.md`. Não
344
+ conserte agora. Após refactor terminar, faz PRs separados
345
+ para cada fix com test do comportamento correto.
346
+ ```
347
+
348
+ ### ANTI: extract method + mover lógica
349
+
350
+ ```text
351
+ ANTI: extract method, mas durante extraction "noto" que lógica é
352
+ melhor em outro escopo, então move pra lá durante o extract.
353
+
354
+ PROBLEMA: comportamento mudou. Não é mais SAFE extraction. Você
355
+ precisava de characterization mas pulou.
356
+
357
+ CERTO: 2 PRs sequenciais.
358
+ PR1 — extract method (idêntico, no mesmo escopo)
359
+ PR2 — move method (com test que valida em ambos os contextos)
360
+ ```
361
+
362
+ ### ANTI: scratch refactoring committed
363
+
364
+ ```text
365
+ ANTI: scratch ficou bom, mantenho-o em PR.
366
+
367
+ PROBLEMA: scratch fez mudanças não-mecânicas (estéticas, especulativas).
368
+ Sem characterization, não há prova de comportamento idêntico.
369
+ Você acabou de fazer "edit and pray" disfarçado de refactor.
370
+
371
+ CERTO: scratch é descartável. SEMPRE. Real refactor recomeça do
372
+ código original com passos disciplinados.
373
+ ```
374
+
375
+ ### ANTI: extract APENAS para mais legibilidade, sem reduzir tamanho
376
+
377
+ ```text
378
+ ANTI: extract de 1 linha para método com nome descritivo "para ficar
379
+ mais claro". Original tinha 200 linhas, agora tem 195 + 1 linha
380
+ em método novo.
381
+
382
+ PROBLEMA: 5 minutos para reviewer entender a chamada extra. Tamanho
383
+ do monster diminuiu 1%. Trade desfavorável.
384
+
385
+ CERTO: extract de 10-30 linhas mínimo. Bloco coeso e separável,
386
+ não single statement. Linha solta com nome longo é
387
+ refactoring teatral.
388
+ ```
389
+
390
+ ### ANTI: tentar testar tudo upfront
391
+
392
+ ```text
393
+ ANTI: "vou characterize completo das 200 linhas em todos os 30
394
+ inputs antes de tocar uma vírgula".
395
+
396
+ PROBLEMA: 200 linhas com 30 inputs = 1-2 semanas de characterization.
397
+ Stakeholder cancela. Refactor nunca acontece. Status quo
398
+ eterno.
399
+
400
+ CERTO: characterization MÍNIMA viável (5-10 inputs nos pontos óbvios).
401
+ Refactor mecânico (safe extraction, rename) que PRESERVA
402
+ comportamento. Acumula testes em helpers extraídos. Cobertura
403
+ emerge organicamente.
404
+ ```
405
+
406
+ ## Verificação
407
+
408
+ Antes de declarar refactor de monster method completo:
409
+
410
+ 1. **Tipo identificado** — bulleted vs snarled
411
+ 2. **Tamanho original < 100 linhas após refactor** — se ainda > 100, refactor não terminou
412
+ 3. **Cada commit é single-goal** — rename OR extract OR move, nunca múltiplos
413
+ 4. **Compilação verde a cada commit** — passos pequenos, mecânicos
414
+ 5. **Smoke run após cada commit** — comportamento preservado
415
+ 6. **Tests acumulados nos helpers extraídos** — coverage cresceu de 0% para ≥ 50%
416
+ 7. **Bugs encontrados anotados, NÃO consertados durante refactor** — fix em PRs separados
417
+ 8. **Sem scratch committed** — só conhecimento adquirido viajou
418
+
419
+ ## Limiar de "pronto para feature change pós-refactor"
420
+
421
+ ```text
422
+ Linhas do método principal: ≤ 100 (idealmente ≤ 50)
423
+ Profundidade máxima de aninhamento: ≤ 3
424
+ Helpers extraídos: 5-15 (cada ≤ 30 linhas)
425
+ Coverage do método principal: ≥ 50%
426
+ Coverage dos helpers: ≥ 70%
427
+ Bugs encontrados: anotados em BUGS-FOUND.md
428
+ PRs: cada single-goal, ≤ 100 linhas, revertíveis
429
+ ```
430
+
431
+ Atingidos? Agora a feature change pode acontecer com confiança normal de TDD.
432
+
433
+ ---
434
+
435
+ ## Ver também
436
+
437
+ - [`_shared-legacy/glossary.md`](../_shared-legacy/glossary.md) — vocabulário (monster method, bulleted vs snarled, scratch, single-goal, safe extraction)
438
+ - [`legacy-characterization-tests`](../legacy-characterization-tests/SKILL.md) — para mudanças COMPORTAMENTAIS, characterization é obrigatório (não basta safe extraction)
439
+ - [`legacy-seams-and-test-harness`](../legacy-seams-and-test-harness/SKILL.md) — break-deps é pré-requisito quando helpers extraídos têm I/O
440
+ - [`legacy-effect-analysis`](../legacy-effect-analysis/SKILL.md) — sketch dentro do monster ajuda a escolher onde extrair
441
+ - [`legacy-sprout-wrap-techniques`](../legacy-sprout-wrap-techniques/SKILL.md) — quando monster > 300 linhas, sprout class para novo comportamento sem refatorar
442
+ - [`pre-refactor-characterization`](../pre-refactor-characterization/SKILL.md) — gate distingue safe extraction (livre) de behavioral change (requer characterization)
443
+
444
+ *Material-fonte: Working Effectively with Legacy Code — Feathers, 2004 — Cap 22: "I Need to Change a Monster Method and I Can't Write Tests for It".*