@luanpdd/kit-mcp 1.30.1 → 1.31.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.
- package/LICENSE +21 -21
- package/README.md +168 -168
- package/gates/agent-no-recursive-dispatch.md +84 -82
- package/kit/COMANDOS.md +138 -138
- package/kit/README.md +76 -76
- package/kit/agents/advisor-researcher.md +107 -106
- package/kit/agents/ai-mutation-tester.md +1 -0
- package/kit/agents/assumptions-analyzer.md +108 -107
- package/kit/agents/audit-log-implementer.md +314 -313
- package/kit/agents/auditor-consistencia-isolamento.md +414 -413
- package/kit/agents/b2b-saas-architect.md +157 -156
- package/kit/agents/burn-rate-forecaster.md +1 -0
- package/kit/agents/cascading-failures-auditor.md +299 -298
- package/kit/agents/codebase-mapper.md +769 -768
- package/kit/agents/crm-pipeline-implementer.md +257 -256
- package/kit/agents/debugger.md +814 -813
- package/kit/agents/detector-tenant-quente.md +338 -337
- package/kit/agents/evolution-go-integrator.md +201 -200
- package/kit/agents/example-reviewer.md +22 -21
- package/kit/agents/executor.md +565 -564
- package/kit/agents/golden-signals-instrumenter.md +1 -0
- package/kit/agents/incident-investigator.md +1 -0
- package/kit/agents/integration-checker.md +201 -200
- package/kit/agents/invite-flow-implementer.md +190 -189
- package/kit/agents/legacy-characterizer.md +369 -368
- package/kit/agents/lgpd-compliance-auditor.md +296 -295
- package/kit/agents/load-shedding-instrumenter.md +1 -0
- package/kit/agents/multi-tenant-isolation-auditor.md +254 -253
- package/kit/agents/multi-tenant-rls-writer.md +341 -340
- package/kit/agents/nyquist-auditor.md +179 -178
- package/kit/agents/observability-coverage-auditor.md +316 -315
- package/kit/agents/observability-instrumenter.md +1 -0
- package/kit/agents/omm-auditor.md +1 -0
- package/kit/agents/org-onboarding-implementer.md +224 -223
- package/kit/agents/payload-capture-instrumenter.md +274 -273
- package/kit/agents/phase-researcher.md +697 -696
- package/kit/agents/plan-checker.md +273 -272
- package/kit/agents/planner.md +923 -922
- package/kit/agents/postmortem-writer.md +1 -0
- package/kit/agents/project-researcher.md +653 -652
- package/kit/agents/prr-conductor.md +1 -0
- package/kit/agents/refactor-safety-auditor.md +405 -404
- package/kit/agents/release-pipeline-auditor.md +1 -0
- package/kit/agents/research-synthesizer.md +246 -245
- package/kit/agents/roadmapper.md +678 -677
- package/kit/agents/schema-checker.md +1 -0
- package/kit/agents/seam-finder.md +360 -359
- package/kit/agents/shotgun-surgery-detector.md +350 -349
- package/kit/agents/slo-engineer.md +1 -0
- package/kit/agents/storytelling-analyst.md +1 -0
- package/kit/agents/supabase-architect.md +1 -0
- package/kit/agents/supabase-auth-bootstrapper.md +1 -0
- package/kit/agents/supabase-branching-architect.md +563 -562
- package/kit/agents/supabase-cicd-pipeline-implementer.md +778 -777
- package/kit/agents/supabase-column-privileges-writer.md +400 -399
- package/kit/agents/supabase-edge-fn-tester.md +2 -1
- package/kit/agents/supabase-edge-fn-writer.md +2 -1
- package/kit/agents/supabase-migration-writer.md +386 -385
- package/kit/agents/supabase-rbac-implementer.md +393 -392
- package/kit/agents/supabase-realtime-implementer.md +364 -363
- package/kit/agents/supabase-rls-hardener.md +522 -521
- package/kit/agents/supabase-rls-writer.md +324 -323
- package/kit/agents/supabase-roles-implementer.md +356 -355
- package/kit/agents/supabase-storage-implementer.md +1 -0
- package/kit/agents/super-admin-implementer.md +282 -281
- package/kit/agents/toil-auditor.md +1 -0
- package/kit/agents/ui-auditor.md +438 -437
- package/kit/agents/ui-checker.md +303 -302
- package/kit/agents/ui-researcher.md +356 -355
- package/kit/agents/user-profiler.md +176 -175
- package/kit/agents/validador-evolucao-schema.md +336 -335
- package/kit/agents/verifier.md +729 -728
- package/kit/commands/adicionar-backlog.md +75 -75
- package/kit/commands/adicionar-fase.md +42 -42
- package/kit/commands/adicionar-tarefa.md +45 -45
- package/kit/commands/adicionar-testes.md +41 -41
- package/kit/commands/ajuda.md +21 -21
- package/kit/commands/atualizar.md +37 -37
- package/kit/commands/auditar-cascading.md +111 -111
- package/kit/commands/auditar-marco.md +179 -179
- package/kit/commands/auditar-observabilidade-cobertura.md +183 -183
- package/kit/commands/auditar-refactor.md +219 -219
- package/kit/commands/auditar-release.md +109 -109
- package/kit/commands/auditar-uat.md +23 -23
- package/kit/commands/autonomo.md +40 -40
- package/kit/commands/branch-pr.md +24 -24
- package/kit/commands/burn-rate-status.md +408 -408
- package/kit/commands/capturar-payloads.md +193 -193
- package/kit/commands/caracterizar.md +212 -212
- package/kit/commands/concluir-marco.md +247 -247
- package/kit/commands/configuracoes.md +36 -36
- package/kit/commands/dados-distribuidos.md +188 -188
- package/kit/commands/definir-perfil.md +10 -10
- package/kit/commands/depurar.md +190 -190
- package/kit/commands/detectar-duplicacao.md +197 -197
- package/kit/commands/discutir-fase.md +131 -131
- package/kit/commands/encontrar-seams.md +136 -136
- package/kit/commands/entrar-discord.md +17 -17
- package/kit/commands/estatisticas.md +18 -18
- package/kit/commands/example-greeting.md +33 -33
- package/kit/commands/executar-fase.md +58 -58
- package/kit/commands/expresso.md +56 -56
- package/kit/commands/fase-ui.md +34 -34
- package/kit/commands/fazer.md +57 -57
- package/kit/commands/fio.md +125 -125
- package/kit/commands/fluxos-trabalho.md +64 -64
- package/kit/commands/forense.md +176 -176
- package/kit/commands/gerenciador.md +38 -38
- package/kit/commands/inserir-fase.md +31 -31
- package/kit/commands/legacy.md +263 -263
- package/kit/commands/limpeza.md +17 -17
- package/kit/commands/listar-hipoteses-fase.md +45 -45
- package/kit/commands/listar-workspaces.md +18 -18
- package/kit/commands/load-shedding.md +117 -117
- package/kit/commands/mapear-codebase.md +70 -70
- package/kit/commands/multi-tenant.md +163 -163
- package/kit/commands/nota.md +33 -33
- package/kit/commands/novo-marco.md +43 -43
- package/kit/commands/novo-projeto.md +41 -41
- package/kit/commands/novo-workspace.md +43 -43
- package/kit/commands/pausar-trabalho.md +37 -37
- package/kit/commands/perfil-usuario.md +45 -45
- package/kit/commands/pesquisar-fase.md +195 -195
- package/kit/commands/planejar-fase.md +67 -67
- package/kit/commands/planejar-lacunas.md +33 -33
- package/kit/commands/plantar-ideia.md +25 -25
- package/kit/commands/progresso.md +24 -24
- package/kit/commands/proximo.md +30 -30
- package/kit/commands/publicar.md +490 -490
- package/kit/commands/rapido.md +35 -35
- package/kit/commands/reaplicar-patches.md +124 -124
- package/kit/commands/refactor-seguro.md +321 -321
- package/kit/commands/relatorio-sessao.md +19 -19
- package/kit/commands/remover-fase.md +31 -31
- package/kit/commands/remover-workspace.md +26 -26
- package/kit/commands/resumo-marco.md +50 -50
- package/kit/commands/retomar-trabalho.md +40 -40
- package/kit/commands/revisar-backlog.md +60 -60
- package/kit/commands/revisar-ui.md +32 -32
- package/kit/commands/revisar.md +37 -37
- package/kit/commands/saude.md +21 -21
- package/kit/commands/setup-notion.md +93 -93
- package/kit/commands/storytelling.md +179 -179
- package/kit/commands/sync-main.md +68 -68
- package/kit/commands/validar-fase.md +35 -35
- package/kit/commands/verificar-tarefas.md +44 -44
- package/kit/commands/verificar-trabalho.md +64 -64
- package/kit/file-manifest.json +82 -81
- package/kit/framework/bin/lib/commands.cjs +959 -959
- package/kit/framework/bin/lib/config.cjs +442 -442
- package/kit/framework/bin/lib/core.cjs +1230 -1230
- package/kit/framework/bin/lib/frontmatter.cjs +336 -336
- package/kit/framework/bin/lib/init.cjs +1442 -1442
- package/kit/framework/bin/lib/milestone.cjs +252 -252
- package/kit/framework/bin/lib/model-profiles.cjs +68 -68
- package/kit/framework/bin/lib/phase.cjs +888 -888
- package/kit/framework/bin/lib/profile-output.cjs +952 -952
- package/kit/framework/bin/lib/profile-pipeline.cjs +539 -539
- package/kit/framework/bin/lib/roadmap.cjs +329 -329
- package/kit/framework/bin/lib/security.cjs +382 -382
- package/kit/framework/bin/lib/state.cjs +1031 -1031
- package/kit/framework/bin/lib/template.cjs +222 -222
- package/kit/framework/bin/lib/uat.cjs +282 -282
- package/kit/framework/bin/lib/verify.cjs +888 -888
- package/kit/framework/bin/lib/workstream.cjs +491 -491
- package/kit/framework/bin/tools.cjs +918 -918
- package/kit/framework/commands/workstreams.md +63 -63
- package/kit/framework/references/checkpoints.md +778 -778
- package/kit/framework/references/continuation-format.md +249 -249
- package/kit/framework/references/decimal-phase-calculation.md +64 -64
- package/kit/framework/references/git-integration.md +295 -295
- package/kit/framework/references/git-planning-commit.md +38 -38
- package/kit/framework/references/model-profile-resolution.md +36 -36
- package/kit/framework/references/model-profiles.md +139 -139
- package/kit/framework/references/phase-argument-parsing.md +61 -61
- package/kit/framework/references/planning-config.md +202 -202
- package/kit/framework/references/questioning.md +162 -162
- package/kit/framework/references/tdd.md +263 -263
- package/kit/framework/references/ui-brand.md +160 -160
- package/kit/framework/references/user-profiling.md +657 -657
- package/kit/framework/references/verification-patterns.md +612 -612
- package/kit/framework/references/workstream-flag.md +58 -58
- package/kit/framework/templates/DEBUG.md +164 -164
- package/kit/framework/templates/UAT.md +265 -265
- package/kit/framework/templates/UI-SPEC.md +100 -100
- package/kit/framework/templates/VALIDATION.md +76 -76
- package/kit/framework/templates/claude-md.md +122 -122
- package/kit/framework/templates/codebase/architecture.md +185 -185
- package/kit/framework/templates/codebase/concerns.md +205 -205
- package/kit/framework/templates/codebase/conventions.md +204 -204
- package/kit/framework/templates/codebase/integrations.md +192 -192
- package/kit/framework/templates/codebase/stack.md +158 -158
- package/kit/framework/templates/codebase/structure.md +199 -199
- package/kit/framework/templates/codebase/testing.md +301 -301
- package/kit/framework/templates/config.json +44 -44
- package/kit/framework/templates/context.md +352 -352
- package/kit/framework/templates/continue-here.md +78 -78
- package/kit/framework/templates/copilot-instructions.md +7 -7
- package/kit/framework/templates/debug-subagent-prompt.md +91 -91
- package/kit/framework/templates/dev-preferences.md +20 -20
- package/kit/framework/templates/discovery.md +146 -146
- package/kit/framework/templates/discussion-log.md +63 -63
- package/kit/framework/templates/milestone-archive.md +123 -123
- package/kit/framework/templates/milestone.md +115 -115
- package/kit/framework/templates/phase-prompt.md +610 -610
- package/kit/framework/templates/planner-subagent-prompt.md +117 -117
- package/kit/framework/templates/project.md +186 -186
- package/kit/framework/templates/requirements.md +231 -231
- package/kit/framework/templates/research-project/ARCHITECTURE.md +204 -204
- package/kit/framework/templates/research-project/FEATURES.md +147 -147
- package/kit/framework/templates/research-project/PITFALLS.md +200 -200
- package/kit/framework/templates/research-project/STACK.md +120 -120
- package/kit/framework/templates/research-project/SUMMARY.md +170 -170
- package/kit/framework/templates/research.md +419 -419
- package/kit/framework/templates/retrospective.md +54 -54
- package/kit/framework/templates/roadmap.md +202 -202
- package/kit/framework/templates/state.md +176 -176
- package/kit/framework/templates/summary-complex.md +59 -59
- package/kit/framework/templates/summary-minimal.md +41 -41
- package/kit/framework/templates/summary-standard.md +48 -48
- package/kit/framework/templates/summary.md +209 -209
- package/kit/framework/templates/user-profile.md +146 -146
- package/kit/framework/templates/user-setup.md +256 -256
- package/kit/framework/templates/verification-report.md +258 -258
- package/kit/framework/workflows/add-phase.md +112 -112
- package/kit/framework/workflows/add-tests.md +351 -351
- package/kit/framework/workflows/add-todo.md +158 -158
- package/kit/framework/workflows/audit-milestone.md +340 -340
- package/kit/framework/workflows/audit-uat.md +109 -109
- package/kit/framework/workflows/autonomous.md +891 -891
- package/kit/framework/workflows/check-todos.md +177 -177
- package/kit/framework/workflows/cleanup.md +152 -152
- package/kit/framework/workflows/complete-milestone.md +696 -696
- package/kit/framework/workflows/diagnose-issues.md +231 -231
- package/kit/framework/workflows/discovery-phase.md +289 -289
- package/kit/framework/workflows/discuss-phase-assumptions.md +653 -653
- package/kit/framework/workflows/discuss-phase.md +784 -784
- package/kit/framework/workflows/do.md +104 -104
- package/kit/framework/workflows/execute-phase.md +838 -838
- package/kit/framework/workflows/execute-plan.md +510 -510
- package/kit/framework/workflows/fast.md +102 -102
- package/kit/framework/workflows/forensics.md +265 -265
- package/kit/framework/workflows/health.md +181 -181
- package/kit/framework/workflows/help.md +619 -619
- package/kit/framework/workflows/insert-phase.md +130 -130
- package/kit/framework/workflows/list-phase-assumptions.md +178 -178
- package/kit/framework/workflows/list-workspaces.md +56 -56
- package/kit/framework/workflows/manager.md +362 -362
- package/kit/framework/workflows/map-codebase.md +377 -377
- package/kit/framework/workflows/milestone-summary.md +223 -223
- package/kit/framework/workflows/new-milestone.md +486 -486
- package/kit/framework/workflows/new-project.md +1159 -1159
- package/kit/framework/workflows/new-workspace.md +237 -237
- package/kit/framework/workflows/next.md +97 -97
- package/kit/framework/workflows/node-repair.md +92 -92
- package/kit/framework/workflows/note.md +156 -156
- package/kit/framework/workflows/pause-work.md +176 -176
- package/kit/framework/workflows/plan-milestone-gaps.md +273 -273
- package/kit/framework/workflows/plan-phase.md +765 -765
- package/kit/framework/workflows/plant-seed.md +169 -169
- package/kit/framework/workflows/pr-branch.md +129 -129
- package/kit/framework/workflows/profile-user.md +450 -450
- package/kit/framework/workflows/progress.md +507 -507
- package/kit/framework/workflows/quick.md +757 -757
- package/kit/framework/workflows/remove-phase.md +155 -155
- package/kit/framework/workflows/remove-workspace.md +90 -90
- package/kit/framework/workflows/research-phase.md +82 -82
- package/kit/framework/workflows/resume-project.md +326 -326
- package/kit/framework/workflows/review.md +228 -228
- package/kit/framework/workflows/session-report.md +146 -146
- package/kit/framework/workflows/settings.md +283 -283
- package/kit/framework/workflows/ship.md +228 -228
- package/kit/framework/workflows/stats.md +60 -60
- package/kit/framework/workflows/transition.md +671 -671
- package/kit/framework/workflows/ui-phase.md +302 -302
- package/kit/framework/workflows/ui-review.md +165 -165
- package/kit/framework/workflows/update.md +323 -323
- package/kit/framework/workflows/validate-phase.md +174 -174
- package/kit/framework/workflows/verify-phase.md +252 -252
- package/kit/framework/workflows/verify-work.md +637 -637
- package/kit/hooks/check-update.js +118 -118
- package/kit/hooks/context-monitor.js +163 -163
- package/kit/hooks/kit-attribution-reminder.cjs +30 -36
- package/kit/hooks/kit-router.cjs +137 -0
- package/kit/hooks/prompt-guard.js +103 -103
- package/kit/hooks/statusline.js +125 -125
- package/kit/hooks/workflow-guard.js +101 -101
- package/kit/settings.json +45 -45
- package/kit/skills/ai-prompt-characterization/SKILL.md +335 -335
- package/kit/skills/armadilhas-sistemas-distribuidos/SKILL.md +447 -447
- package/kit/skills/audit-log-multi-tenant/SKILL.md +340 -340
- package/kit/skills/b2b-saas-architecture/SKILL.md +300 -300
- package/kit/skills/consistencia-leitura-replica/SKILL.md +385 -385
- package/kit/skills/crm-lead-pipeline-patterns/SKILL.md +343 -343
- package/kit/skills/escolha-modelo-consistencia/SKILL.md +494 -494
- package/kit/skills/evolucao-schema-compativel/SKILL.md +448 -448
- package/kit/skills/evolution-go-whatsapp-integration/SKILL.md +322 -322
- package/kit/skills/example-skill/SKILL.md +42 -42
- package/kit/skills/legacy-api-only-applications/SKILL.md +358 -358
- package/kit/skills/legacy-characterization-tests/SKILL.md +330 -330
- package/kit/skills/legacy-effect-analysis/SKILL.md +331 -331
- package/kit/skills/legacy-extract-class/SKILL.md +203 -203
- package/kit/skills/legacy-programming-by-difference/SKILL.md +252 -252
- package/kit/skills/legacy-seams-and-test-harness/SKILL.md +460 -460
- package/kit/skills/legacy-shotgun-surgery/SKILL.md +286 -286
- package/kit/skills/legacy-sprout-wrap-techniques/SKILL.md +434 -434
- package/kit/skills/legacy-storytelling-naked-crc/SKILL.md +270 -270
- package/kit/skills/lgpd-multi-tenant-compliance/SKILL.md +340 -340
- package/kit/skills/member-invite-flow/SKILL.md +305 -305
- package/kit/skills/member-management-react-shadcn/SKILL.md +328 -328
- package/kit/skills/multi-tenant-performance-scaling/SKILL.md +316 -316
- package/kit/skills/multi-tenant-rls-hierarchy/SKILL.md +342 -342
- package/kit/skills/org-onboarding-flow/SKILL.md +257 -257
- package/kit/skills/org-switcher-react-pattern/SKILL.md +349 -349
- package/kit/skills/permission-gate-react-pattern/SKILL.md +271 -271
- package/kit/skills/postgres-isolamento-concorrencia/SKILL.md +552 -552
- package/kit/skills/pre-refactor-characterization/SKILL.md +421 -421
- package/kit/skills/rbac-permissions-matrix-supabase/SKILL.md +338 -338
- package/kit/skills/streams-eventos-cdc/SKILL.md +711 -711
- package/kit/skills/supabase-branching-workflow/SKILL.md +544 -544
- package/kit/skills/supabase-ci-cd-github-actions/SKILL.md +880 -880
- package/kit/skills/supabase-column-level-security/SKILL.md +426 -426
- package/kit/skills/supabase-config-toml-remotes/SKILL.md +807 -807
- package/kit/skills/supabase-custom-claims-rbac/SKILL.md +472 -472
- package/kit/skills/supabase-edge-functions/SKILL.md +1 -1
- package/kit/skills/supabase-edge-functions-auth/SKILL.md +1 -1
- package/kit/skills/supabase-edge-functions-limits/SKILL.md +1 -1
- package/kit/skills/supabase-edge-functions-mcp-server/SKILL.md +1 -1
- package/kit/skills/supabase-edge-functions-testing/SKILL.md +1 -1
- package/kit/skills/supabase-edge-runtime-builtins/SKILL.md +1 -1
- package/kit/skills/supabase-migration-repair/SKILL.md +823 -823
- package/kit/skills/supabase-migrations/SKILL.md +297 -297
- package/kit/skills/supabase-pgtap-testing/SKILL.md +1053 -1053
- package/kit/skills/supabase-postgres-roles/SKILL.md +392 -392
- package/kit/skills/supabase-realtime/SKILL.md +460 -460
- package/kit/skills/supabase-rls-defense-in-depth/SKILL.md +418 -418
- package/kit/skills/supabase-rls-policies/SKILL.md +635 -635
- package/kit/skills/super-admin-platform-pattern/SKILL.md +326 -326
- package/kit/skills/tenant-quente-mitigacao/SKILL.md +605 -605
- package/kit/skills/whatsapp-conversation-state-machine/SKILL.md +287 -287
- package/package.json +1 -1
- package/src/core/kit.js +216 -216
- package/src/core/reflect.js +247 -247
- package/src/core/reverse-sync.js +372 -372
- package/src/core/sync.js +437 -418
- package/src/core/watch.js +121 -121
- package/src/mcp-server/index.js +794 -715
|
@@ -1,434 +1,434 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: legacy-sprout-wrap-techniques
|
|
3
|
-
description: Use ao adicionar comportamento a código legado SEM tempo para colocar tudo sob test harness — Sprout Method, Sprout Class, Wrap Method, Wrap Class (cap 6 Feathers).
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Legacy — Sprout / Wrap Techniques
|
|
7
|
-
|
|
8
|
-
## Quando usar
|
|
9
|
-
|
|
10
|
-
LLM carrega esta skill quando o user precisa adicionar comportamento a código legado e (a) o código alvo é grande/complexo demais para characterization completa AGORA, e (b) tempo está apertado. Trigger phrases:
|
|
11
|
-
|
|
12
|
-
- "adicionar feature em código sem testes"
|
|
13
|
-
- "não tenho tempo para refatorar tudo, mas preciso mudar"
|
|
14
|
-
- "sprout method", "sprout class", "wrap method", "wrap class"
|
|
15
|
-
- "cap 6 Feathers", "I don't have much time"
|
|
16
|
-
- "como adicionar nova lógica sem mexer no monstro?"
|
|
17
|
-
- "decoração / wrapper", "extract para método novo"
|
|
18
|
-
|
|
19
|
-
Carrega como alternativa pragmática a `legacy-characterization-tests` quando custo de characterization completa é proibitivo.
|
|
20
|
-
|
|
21
|
-
## Regras absolutas
|
|
22
|
-
|
|
23
|
-
- **Sprout/wrap NÃO substitui characterization para REFACTOR.** Aplica apenas quando você está ADICIONANDO comportamento. Refactor de código existente sem tests = sempre characterize first.
|
|
24
|
-
- **A nova lógica DEVE ser testável isoladamente.** O ponto inteiro do sprout é levantar comportamento para classe/método novo testável. Se a nova lógica continua amarrada ao legado, o sprout falhou.
|
|
25
|
-
- **Conexão ao legado é 1 linha (sprout) ou rename + 1 linha (wrap).** Mudança no código legado é mínima e mecânica. Reviewer consegue verificar visualmente que comportamento existente foi preservado.
|
|
26
|
-
- **Sprout é preferível a wrap.** Wrap muda a interface (rename original), aumenta surface de mudança. Sprout só ADICIONA, não modifica. Reach for sprout primeiro; wrap se sprout não couber.
|
|
27
|
-
- **Não tente "limpar enquanto está aqui".** Boy scout rule **nesta versão** = adicione novo limpo, não modifique velho. Misturar = single-goal violation.
|
|
28
|
-
- **Documente débito técnico criado.** Sprout/wrap explicitamente DEIXAM código legado untested. Crie ticket/TODO para characterization futura. Sem doc = débito invisível.
|
|
29
|
-
|
|
30
|
-
## Patterns canônicos
|
|
31
|
-
|
|
32
|
-
### Pattern 1: Sprout Method — quando preferir
|
|
33
|
-
|
|
34
|
-
Adicione comportamento novo coeso, separável do legado, em método novo testável. Conecte por 1 linha.
|
|
35
|
-
|
|
36
|
-
```ts
|
|
37
|
-
// Cenário: precisamos adicionar "auditoria de tentativa de fraude"
|
|
38
|
-
// no método legado processOrder() que tem 200 linhas e zero testes.
|
|
39
|
-
|
|
40
|
-
// ANTES — método legado intocado
|
|
41
|
-
class OrderProcessor {
|
|
42
|
-
processOrder(order: Order): Result {
|
|
43
|
-
// 200 linhas de lógica legada e não-testada
|
|
44
|
-
// ...
|
|
45
|
-
return result
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// DEPOIS — sprout method (NOVO, testado isolado)
|
|
50
|
-
class OrderProcessor {
|
|
51
|
-
processOrder(order: Order): Result {
|
|
52
|
-
// 200 linhas de lógica legada e não-testada (intocadas)
|
|
53
|
-
|
|
54
|
-
// ↓ INSERÇÃO ÚNICA — 1 linha — chama sprout testável
|
|
55
|
-
auditFraudAttempt(order, this.fraudLogger)
|
|
56
|
-
|
|
57
|
-
// ...resto das 200 linhas legadas (intocadas)
|
|
58
|
-
return result
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Sprout method — fora da classe legada (preferred), com test próprio
|
|
63
|
-
export function auditFraudAttempt(order: Order, logger: FraudLogger): void {
|
|
64
|
-
if (order.amount > 10000 && order.country !== order.cardCountry) {
|
|
65
|
-
logger.flag(order.id, 'high-value-cross-border', { amount: order.amount })
|
|
66
|
-
}
|
|
67
|
-
if (order.attempts > 3) {
|
|
68
|
-
logger.flag(order.id, 'retry-spike', { attempts: order.attempts })
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Test isolado
|
|
73
|
-
test('auditFraudAttempt — high value cross-border', () => {
|
|
74
|
-
const logger = new FakeFraudLogger()
|
|
75
|
-
auditFraudAttempt(
|
|
76
|
-
{ id: 'O1', amount: 15000, country: 'BR', cardCountry: 'US', attempts: 1 },
|
|
77
|
-
logger,
|
|
78
|
-
)
|
|
79
|
-
expect(logger.flags).toEqual([{ id: 'O1', reason: 'high-value-cross-border', meta: { amount: 15000 } }])
|
|
80
|
-
})
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
**Por que funciona:** o método legado de 200 linhas continua untested, mas a NOVA lógica de fraud audit está completamente coberta. Refactor futuro do legado vai herdar o sprout intacto.
|
|
84
|
-
|
|
85
|
-
**Quando preferir:** lógica nova é coesa, ≤ 30-50 linhas, e pode receber dados via parâmetros (não precisa de side effects do legado).
|
|
86
|
-
|
|
87
|
-
### Pattern 2: Sprout Class — quando sprout method cresce
|
|
88
|
-
|
|
89
|
-
Lógica nova é grande (> 30 linhas) ou tem múltiplos métodos coesos. Promova para classe nova com test harness próprio.
|
|
90
|
-
|
|
91
|
-
```ts
|
|
92
|
-
// Cenário: fraud audit cresceu — agora precisa de retry detection,
|
|
93
|
-
// velocity checks, blacklist lookup, scoring. Vira módulo coeso.
|
|
94
|
-
|
|
95
|
-
// Sprout CLASS (não mais método solto)
|
|
96
|
-
export class FraudAuditor {
|
|
97
|
-
constructor(
|
|
98
|
-
private logger: FraudLogger,
|
|
99
|
-
private blacklist: BlacklistService,
|
|
100
|
-
private clock: () => Date = () => new Date(),
|
|
101
|
-
) {}
|
|
102
|
-
|
|
103
|
-
audit(order: Order): FraudAuditResult {
|
|
104
|
-
const flags: string[] = []
|
|
105
|
-
|
|
106
|
-
if (this.isHighValueCrossBorder(order)) flags.push('high-value-cross-border')
|
|
107
|
-
if (this.isRetrySpike(order)) flags.push('retry-spike')
|
|
108
|
-
if (this.isBlacklisted(order)) flags.push('blacklisted-customer')
|
|
109
|
-
|
|
110
|
-
flags.forEach(f => this.logger.flag(order.id, f, { ts: this.clock() }))
|
|
111
|
-
|
|
112
|
-
return {
|
|
113
|
-
score: this.computeScore(flags),
|
|
114
|
-
flags,
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
private isHighValueCrossBorder(o: Order): boolean { /* ... */ }
|
|
119
|
-
private isRetrySpike(o: Order): boolean { /* ... */ }
|
|
120
|
-
private isBlacklisted(o: Order): boolean { /* ... */ }
|
|
121
|
-
private computeScore(flags: string[]): number { /* ... */ }
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Conexão ao legado — apenas 2 linhas
|
|
125
|
-
class OrderProcessor {
|
|
126
|
-
private fraudAuditor = new FraudAuditor(this.logger, this.blacklistSvc)
|
|
127
|
-
|
|
128
|
-
processOrder(order: Order): Result {
|
|
129
|
-
// ... legado intocado ...
|
|
130
|
-
const fraudResult = this.fraudAuditor.audit(order) // ← conexão
|
|
131
|
-
if (fraudResult.score > 80) throw new FraudError(fraudResult.flags)
|
|
132
|
-
// ... legado intocado ...
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
**Quando preferir:** múltiplos behaviors relacionados, dependências múltiplas (clock, blacklist, logger), suite de testes própria justifica a classe.
|
|
138
|
-
|
|
139
|
-
### Pattern 3: Wrap Method — quando precisa MODIFICAR pré/pós condição
|
|
140
|
-
|
|
141
|
-
Sprout só adiciona comportamento DURANTE. Quando a mudança é "antes" ou "depois" de TODO o método existente, wrap é mais limpo.
|
|
142
|
-
|
|
143
|
-
```ts
|
|
144
|
-
// Cenário: precisamos enviar notificação para auditoria DEPOIS que
|
|
145
|
-
// processOrder() roda, com sucesso ou erro.
|
|
146
|
-
|
|
147
|
-
// PASSO 1 — rename mecânico (refactor IDE-assisted, zero risco)
|
|
148
|
-
class OrderProcessor {
|
|
149
|
-
processOrderLegacy(order: Order): Result { // ← renamed
|
|
150
|
-
// 200 linhas de lógica legada — intocadas
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// PASSO 2 — novo método com nome original, wrappando o legado
|
|
155
|
-
class OrderProcessor {
|
|
156
|
-
processOrder(order: Order): Result {
|
|
157
|
-
// pré-condição NOVA (testável)
|
|
158
|
-
this.notifyAuditStart(order)
|
|
159
|
-
|
|
160
|
-
let result: Result
|
|
161
|
-
try {
|
|
162
|
-
result = this.processOrderLegacy(order) // ← chamada ao legado
|
|
163
|
-
this.notifyAuditSuccess(order, result) // pós-condição NOVA
|
|
164
|
-
} catch (e) {
|
|
165
|
-
this.notifyAuditFailure(order, e) // pós-condição NOVA
|
|
166
|
-
throw e
|
|
167
|
-
}
|
|
168
|
-
return result
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
processOrderLegacy(order: Order): Result {
|
|
172
|
-
// 200 linhas de lógica legada — intocadas
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// sprout methods auxiliares (testáveis)
|
|
176
|
-
private notifyAuditStart(o: Order) { /* ... */ }
|
|
177
|
-
private notifyAuditSuccess(o: Order, r: Result) { /* ... */ }
|
|
178
|
-
private notifyAuditFailure(o: Order, e: Error) { /* ... */ }
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Test do wrap (não testa o legado)
|
|
182
|
-
test('processOrder — notifies start and success on happy path', () => {
|
|
183
|
-
const proc = makeProcessor({ legacyResult: { id: 'O1' } }) // fake do legacy
|
|
184
|
-
proc.processOrder({ id: 'O1' })
|
|
185
|
-
expect(proc.notifications).toEqual([
|
|
186
|
-
{ kind: 'start', orderId: 'O1' },
|
|
187
|
-
{ kind: 'success', orderId: 'O1' },
|
|
188
|
-
])
|
|
189
|
-
})
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
**Quando preferir:** mudança é estruturalmente "before/after" todo o legado, OR você precisa interceptar exception flow.
|
|
193
|
-
|
|
194
|
-
**Trade-off:** clientes externos chamando o método podem precisar de aviso (versionamento, deprecation) se a mudança comportamental afeta contrato. Sprout method não tem esse problema.
|
|
195
|
-
|
|
196
|
-
### Pattern 4: Wrap Class — wrap method em escala de classe
|
|
197
|
-
|
|
198
|
-
Decorator pattern aplicado pragmaticamente. Toda a classe ganha comportamento adicional sem modificar a original.
|
|
199
|
-
|
|
200
|
-
```ts
|
|
201
|
-
// Cenário: precisamos adicionar caching transparente a TODOS os métodos
|
|
202
|
-
// de PostgresOrderRepository (que não tem testes).
|
|
203
|
-
|
|
204
|
-
// ANTES — classe legada
|
|
205
|
-
class PostgresOrderRepository implements OrderRepository {
|
|
206
|
-
findById(id: string): Order { /* DB query */ }
|
|
207
|
-
save(order: Order): void { /* DB write */ }
|
|
208
|
-
findRecent(n: number): Order[] { /* DB query */ }
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// DEPOIS — wrap class (NOVA, testada)
|
|
212
|
-
class CachedOrderRepository implements OrderRepository {
|
|
213
|
-
constructor(
|
|
214
|
-
private inner: OrderRepository, // ← legado vai aqui
|
|
215
|
-
private cache: Cache,
|
|
216
|
-
) {}
|
|
217
|
-
|
|
218
|
-
findById(id: string): Order {
|
|
219
|
-
const cached = this.cache.get(`order:${id}`)
|
|
220
|
-
if (cached) return cached
|
|
221
|
-
const order = this.inner.findById(id) // delega ao legado
|
|
222
|
-
this.cache.set(`order:${id}`, order, 60_000)
|
|
223
|
-
return order
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
save(order: Order): void {
|
|
227
|
-
this.inner.save(order)
|
|
228
|
-
this.cache.invalidate(`order:${order.id}`)
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
findRecent(n: number): Order[] {
|
|
232
|
-
return this.inner.findRecent(n) // sem cache (mais difícil)
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// Em produção
|
|
237
|
-
const repo = new CachedOrderRepository(new PostgresOrderRepository(db), cache)
|
|
238
|
-
|
|
239
|
-
// Em teste — testa CachedOrderRepository com fake inner
|
|
240
|
-
test('CachedOrderRepository — cache hit avoids inner call', () => {
|
|
241
|
-
const inner = new FakeOrderRepository()
|
|
242
|
-
inner.save({ id: 'O1' } as Order)
|
|
243
|
-
const cached = new CachedOrderRepository(inner, new InMemoryCache())
|
|
244
|
-
cached.findById('O1')
|
|
245
|
-
cached.findById('O1')
|
|
246
|
-
expect(inner.findByIdCallCount).toBe(1) // segundo veio do cache
|
|
247
|
-
})
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
**Quando preferir:** comportamento atravessa todos/maioria dos métodos da classe (caching, logging, audit, retry), e clientes esperam mesma interface.
|
|
251
|
-
|
|
252
|
-
### Pattern 5: Decision tree — qual técnica?
|
|
253
|
-
|
|
254
|
-
```text
|
|
255
|
-
Você está ADICIONANDO comportamento (não modificando)?
|
|
256
|
-
├─ Não → use legacy-characterization-tests + legacy-seams-and-test-harness
|
|
257
|
-
└─ Sim →
|
|
258
|
-
Comportamento novo é coeso e separável?
|
|
259
|
-
├─ Sim →
|
|
260
|
-
│ Tamanho do comportamento novo?
|
|
261
|
-
│ ├─ ≤ 30 linhas, 1 responsabilidade → SPROUT METHOD
|
|
262
|
-
│ ├─ > 30 linhas OU múltiplas responsabilidades → SPROUT CLASS
|
|
263
|
-
│ └─ Atravessa N métodos da classe legada → WRAP CLASS
|
|
264
|
-
└─ Não, é uma transformação no fluxo existente →
|
|
265
|
-
Mudança é "antes/depois" do método inteiro?
|
|
266
|
-
├─ Sim → WRAP METHOD
|
|
267
|
-
└─ Não → essa mudança REQUER characterization. Volte ao caminho normal.
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
### Pattern 6: Effort budget de sprout/wrap
|
|
271
|
-
|
|
272
|
-
| Técnica | Custo típico | Cobertura criada | Cobertura herdada |
|
|
273
|
-
|---|---|---|---|
|
|
274
|
-
| **Sprout Method** | 30 min - 2h | 100% do novo | 0% do legado |
|
|
275
|
-
| **Sprout Class** | 2-6h | 100% do novo | 0% do legado |
|
|
276
|
-
| **Wrap Method** | 1-3h | 100% do wrap (novo + delegação) | 0% do legado interior |
|
|
277
|
-
| **Wrap Class** | 4-8h | 100% do wrapper | 0% do delegado |
|
|
278
|
-
|
|
279
|
-
Versus characterization completa do legado (semanas), sprout/wrap entrega 100% do novo em horas. Trade-off: legado continua untested.
|
|
280
|
-
|
|
281
|
-
### Pattern 7: Documentar débito explicit
|
|
282
|
-
|
|
283
|
-
Sempre que aplicar sprout/wrap, criar TODO/ticket para characterization futura:
|
|
284
|
-
|
|
285
|
-
```ts
|
|
286
|
-
// PT-BR: comentário canônico no ponto de inserção do sprout
|
|
287
|
-
class OrderProcessor {
|
|
288
|
-
processOrder(order: Order): Result {
|
|
289
|
-
// ... legado intocado ...
|
|
290
|
-
|
|
291
|
-
// [legacy-debt #issue-1234] sprout — auditFraudAttempt testado isolado;
|
|
292
|
-
// método circundante (processOrder) ainda untested. Characterization
|
|
293
|
-
// completa enquanto refactor maior em Q3/2026.
|
|
294
|
-
auditFraudAttempt(order, this.fraudLogger)
|
|
295
|
-
|
|
296
|
-
// ... legado intocado ...
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
**Sem doc, débito é invisível** — equipe esquece, próxima pessoa pensa que "está testado". Doc + ticket cria responsabilidade explícita.
|
|
302
|
-
|
|
303
|
-
## Anti-patterns
|
|
304
|
-
|
|
305
|
-
### ANTI: sprout que mexe no legado "só um pouquinho"
|
|
306
|
-
|
|
307
|
-
```text
|
|
308
|
-
ANTI: "vou inserir o sprout, e já que estou aqui, ajusto essa
|
|
309
|
-
variável local para passar para o sprout".
|
|
310
|
-
|
|
311
|
-
PROBLEMA: cada edição no legado é risco. Você não tem teste para detectar
|
|
312
|
-
regressão. "Ajustar variável" pode quebrar branch raro.
|
|
313
|
-
|
|
314
|
-
CERTO: passe APENAS o que está em escopo no ponto de inserção. Se
|
|
315
|
-
precisa de mais, aceite que o sprout não cabe ali — escolha
|
|
316
|
-
outro ponto de inserção OU technique diferente.
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
### ANTI: sprout que faz I/O direto
|
|
320
|
-
|
|
321
|
-
```text
|
|
322
|
-
ANTI: function auditFraudAttempt(order) {
|
|
323
|
-
fetch('https://internal-fraud-api/...', {...}) // ← I/O dentro
|
|
324
|
-
log.warn(...) // ← I/O dentro
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
PROBLEMA: sprout vira intestável. Você criou o problema do legado
|
|
328
|
-
numa nova localização.
|
|
329
|
-
|
|
330
|
-
CERTO: dependências passadas via parameter ou constructor injection:
|
|
331
|
-
function auditFraudAttempt(order, logger, fraudApi) { ... }
|
|
332
|
-
Em produção: chama com reais. Em teste: chama com fakes.
|
|
333
|
-
```
|
|
334
|
-
|
|
335
|
-
### ANTI: wrap method esquecendo error path
|
|
336
|
-
|
|
337
|
-
```text
|
|
338
|
-
ANTI: function processOrder(o) {
|
|
339
|
-
notifyStart(o)
|
|
340
|
-
const r = processOrderLegacy(o)
|
|
341
|
-
notifySuccess(o, r)
|
|
342
|
-
return r
|
|
343
|
-
}
|
|
344
|
-
— sem try/catch.
|
|
345
|
-
|
|
346
|
-
PROBLEMA: quando legacy throw, notifyStart já rodou mas notifyFailure
|
|
347
|
-
NÃO. Comportamento parcial. Auditoria fica incoerente.
|
|
348
|
-
|
|
349
|
-
CERTO: wrap method SEMPRE tem try/finally explícito quando
|
|
350
|
-
pré-condição roda I/O ou tem efeito observável:
|
|
351
|
-
try { r = legacy(o); notifySuccess(o, r); return r }
|
|
352
|
-
catch (e) { notifyFailure(o, e); throw e }
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
### ANTI: sprout class mas pré/pós condição vai para sprout
|
|
356
|
-
|
|
357
|
-
```text
|
|
358
|
-
ANTI: SproutFraudAuditor.audit() retorna result E também imprime log
|
|
359
|
-
com timestamp da chamada.
|
|
360
|
-
|
|
361
|
-
PROBLEMA: pré/pós-condição cross-cuts. Reviewer não vê em uma chamada
|
|
362
|
-
o que muda. Hidden side effects.
|
|
363
|
-
|
|
364
|
-
CERTO: pré/pós são responsabilidades do CALLER (legacy). Sprout faz
|
|
365
|
-
UMA coisa, retorna resultado puro, caller decide o que fazer
|
|
366
|
-
(incluindo logar). Mantém sprout testável sem mocks de log.
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
### ANTI: usar wrap class quando sprout method bastaria
|
|
370
|
-
|
|
371
|
-
```text
|
|
372
|
-
ANTI: criar ClassWrapperOrderProcessor para adicionar audit em UM
|
|
373
|
-
método.
|
|
374
|
-
|
|
375
|
-
PROBLEMA: surface change desproporcional. Outros 9 métodos da classe
|
|
376
|
-
ganham wrap inútil. Imports espalhados precisam mudar.
|
|
377
|
-
|
|
378
|
-
CERTO: comece pequeno. Sprout method primeiro. Promove a sprout class
|
|
379
|
-
quando comportamento cresce. Promove a wrap class quando
|
|
380
|
-
atravessa N métodos. Não comece com wrap class.
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
### ANTI: pular ticket de débito técnico
|
|
384
|
-
|
|
385
|
-
```text
|
|
386
|
-
ANTI: aplica sprout, sem comment, sem ticket. "Vou lembrar de testar
|
|
387
|
-
o legado depois".
|
|
388
|
-
|
|
389
|
-
PROBLEMA: 6 meses depois, ninguém sabe que processOrder() ainda é
|
|
390
|
-
untested. Próxima pessoa edita legado direto, regression
|
|
391
|
-
slips, incident.
|
|
392
|
-
|
|
393
|
-
CERTO: comment canônico no sprout point + ticket linkado. Sem isso,
|
|
394
|
-
débito é invisível e a chance de characterization futura cai
|
|
395
|
-
para perto de zero.
|
|
396
|
-
```
|
|
397
|
-
|
|
398
|
-
## Verificação
|
|
399
|
-
|
|
400
|
-
Antes de aprovar PR com sprout/wrap:
|
|
401
|
-
|
|
402
|
-
1. **Técnica certa para a mudança** — sprout (add) vs wrap (transform pre/post)
|
|
403
|
-
2. **Escopo da mudança no legado é mínimo** — sprout: 1 linha; wrap: rename + chamada
|
|
404
|
-
3. **Sprout/wrap testa isolado** — fakes para todas dependências, sem I/O real
|
|
405
|
-
4. **Coverage do novo comportamento** — 100%, não 80% (é código novo, sem desculpa)
|
|
406
|
-
5. **Error path coberto em wrap** — try/catch explícito quando aplicável
|
|
407
|
-
6. **TODO/ticket de débito criado** — comment canônico + linked issue
|
|
408
|
-
7. **Reviewer pode verificar legado intacto** — diff do legado é apenas rename ou 1 inserção
|
|
409
|
-
|
|
410
|
-
## Limiar de "sprout/wrap apropriado"
|
|
411
|
-
|
|
412
|
-
```text
|
|
413
|
-
Comportamento ADICIONADO: sim (não modificado)
|
|
414
|
-
Lógica nova testável isolada: sim (DI injected, sem I/O direto)
|
|
415
|
-
Conexão ao legado: ≤ 1-2 linhas em sprout, rename+1 em wrap
|
|
416
|
-
Cobertura do novo: 100%
|
|
417
|
-
Custo de characterization completa: documentado como > 5× custo do sprout
|
|
418
|
-
Débito técnico: ticket criado, comment canônico no código
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
Se algum critério falha → sprout/wrap não é o caminho certo. Escolha outro.
|
|
422
|
-
|
|
423
|
-
---
|
|
424
|
-
|
|
425
|
-
## Ver também
|
|
426
|
-
|
|
427
|
-
- [`_shared-legacy/glossary.md`](../_shared-legacy/glossary.md) — vocabulário canônico (sprout method/class, wrap method/class)
|
|
428
|
-
- [`legacy-characterization-tests`](../legacy-characterization-tests/SKILL.md) — para refactor (não adicionar), characterization é mandatório
|
|
429
|
-
- [`legacy-seams-and-test-harness`](../legacy-seams-and-test-harness/SKILL.md) — sprout class testável requer DI; técnicas do cap 25 dão a base
|
|
430
|
-
- [`legacy-effect-analysis`](../legacy-effect-analysis/SKILL.md) — escolha do ponto de inserção do sprout informada por effect sketch
|
|
431
|
-
- [`legacy-monster-methods`](../legacy-monster-methods/SKILL.md) — em monster method, sprout cria pé-de-cabra para refactor incremental
|
|
432
|
-
- [`pre-refactor-characterization`](../pre-refactor-characterization/SKILL.md) — gate libera sprout/wrap se cobertura do NOVO = 100%, mesmo sem characterization do legado
|
|
433
|
-
|
|
434
|
-
*Material-fonte: Working Effectively with Legacy Code — Feathers, 2004 — Cap 6: "I Don't Have Much Time and I Have to Change It".*
|
|
1
|
+
---
|
|
2
|
+
name: legacy-sprout-wrap-techniques
|
|
3
|
+
description: Use ao adicionar comportamento a código legado SEM tempo para colocar tudo sob test harness — Sprout Method, Sprout Class, Wrap Method, Wrap Class (cap 6 Feathers).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Legacy — Sprout / Wrap Techniques
|
|
7
|
+
|
|
8
|
+
## Quando usar
|
|
9
|
+
|
|
10
|
+
LLM carrega esta skill quando o user precisa adicionar comportamento a código legado e (a) o código alvo é grande/complexo demais para characterization completa AGORA, e (b) tempo está apertado. Trigger phrases:
|
|
11
|
+
|
|
12
|
+
- "adicionar feature em código sem testes"
|
|
13
|
+
- "não tenho tempo para refatorar tudo, mas preciso mudar"
|
|
14
|
+
- "sprout method", "sprout class", "wrap method", "wrap class"
|
|
15
|
+
- "cap 6 Feathers", "I don't have much time"
|
|
16
|
+
- "como adicionar nova lógica sem mexer no monstro?"
|
|
17
|
+
- "decoração / wrapper", "extract para método novo"
|
|
18
|
+
|
|
19
|
+
Carrega como alternativa pragmática a `legacy-characterization-tests` quando custo de characterization completa é proibitivo.
|
|
20
|
+
|
|
21
|
+
## Regras absolutas
|
|
22
|
+
|
|
23
|
+
- **Sprout/wrap NÃO substitui characterization para REFACTOR.** Aplica apenas quando você está ADICIONANDO comportamento. Refactor de código existente sem tests = sempre characterize first.
|
|
24
|
+
- **A nova lógica DEVE ser testável isoladamente.** O ponto inteiro do sprout é levantar comportamento para classe/método novo testável. Se a nova lógica continua amarrada ao legado, o sprout falhou.
|
|
25
|
+
- **Conexão ao legado é 1 linha (sprout) ou rename + 1 linha (wrap).** Mudança no código legado é mínima e mecânica. Reviewer consegue verificar visualmente que comportamento existente foi preservado.
|
|
26
|
+
- **Sprout é preferível a wrap.** Wrap muda a interface (rename original), aumenta surface de mudança. Sprout só ADICIONA, não modifica. Reach for sprout primeiro; wrap se sprout não couber.
|
|
27
|
+
- **Não tente "limpar enquanto está aqui".** Boy scout rule **nesta versão** = adicione novo limpo, não modifique velho. Misturar = single-goal violation.
|
|
28
|
+
- **Documente débito técnico criado.** Sprout/wrap explicitamente DEIXAM código legado untested. Crie ticket/TODO para characterization futura. Sem doc = débito invisível.
|
|
29
|
+
|
|
30
|
+
## Patterns canônicos
|
|
31
|
+
|
|
32
|
+
### Pattern 1: Sprout Method — quando preferir
|
|
33
|
+
|
|
34
|
+
Adicione comportamento novo coeso, separável do legado, em método novo testável. Conecte por 1 linha.
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
// Cenário: precisamos adicionar "auditoria de tentativa de fraude"
|
|
38
|
+
// no método legado processOrder() que tem 200 linhas e zero testes.
|
|
39
|
+
|
|
40
|
+
// ANTES — método legado intocado
|
|
41
|
+
class OrderProcessor {
|
|
42
|
+
processOrder(order: Order): Result {
|
|
43
|
+
// 200 linhas de lógica legada e não-testada
|
|
44
|
+
// ...
|
|
45
|
+
return result
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// DEPOIS — sprout method (NOVO, testado isolado)
|
|
50
|
+
class OrderProcessor {
|
|
51
|
+
processOrder(order: Order): Result {
|
|
52
|
+
// 200 linhas de lógica legada e não-testada (intocadas)
|
|
53
|
+
|
|
54
|
+
// ↓ INSERÇÃO ÚNICA — 1 linha — chama sprout testável
|
|
55
|
+
auditFraudAttempt(order, this.fraudLogger)
|
|
56
|
+
|
|
57
|
+
// ...resto das 200 linhas legadas (intocadas)
|
|
58
|
+
return result
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Sprout method — fora da classe legada (preferred), com test próprio
|
|
63
|
+
export function auditFraudAttempt(order: Order, logger: FraudLogger): void {
|
|
64
|
+
if (order.amount > 10000 && order.country !== order.cardCountry) {
|
|
65
|
+
logger.flag(order.id, 'high-value-cross-border', { amount: order.amount })
|
|
66
|
+
}
|
|
67
|
+
if (order.attempts > 3) {
|
|
68
|
+
logger.flag(order.id, 'retry-spike', { attempts: order.attempts })
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Test isolado
|
|
73
|
+
test('auditFraudAttempt — high value cross-border', () => {
|
|
74
|
+
const logger = new FakeFraudLogger()
|
|
75
|
+
auditFraudAttempt(
|
|
76
|
+
{ id: 'O1', amount: 15000, country: 'BR', cardCountry: 'US', attempts: 1 },
|
|
77
|
+
logger,
|
|
78
|
+
)
|
|
79
|
+
expect(logger.flags).toEqual([{ id: 'O1', reason: 'high-value-cross-border', meta: { amount: 15000 } }])
|
|
80
|
+
})
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Por que funciona:** o método legado de 200 linhas continua untested, mas a NOVA lógica de fraud audit está completamente coberta. Refactor futuro do legado vai herdar o sprout intacto.
|
|
84
|
+
|
|
85
|
+
**Quando preferir:** lógica nova é coesa, ≤ 30-50 linhas, e pode receber dados via parâmetros (não precisa de side effects do legado).
|
|
86
|
+
|
|
87
|
+
### Pattern 2: Sprout Class — quando sprout method cresce
|
|
88
|
+
|
|
89
|
+
Lógica nova é grande (> 30 linhas) ou tem múltiplos métodos coesos. Promova para classe nova com test harness próprio.
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
// Cenário: fraud audit cresceu — agora precisa de retry detection,
|
|
93
|
+
// velocity checks, blacklist lookup, scoring. Vira módulo coeso.
|
|
94
|
+
|
|
95
|
+
// Sprout CLASS (não mais método solto)
|
|
96
|
+
export class FraudAuditor {
|
|
97
|
+
constructor(
|
|
98
|
+
private logger: FraudLogger,
|
|
99
|
+
private blacklist: BlacklistService,
|
|
100
|
+
private clock: () => Date = () => new Date(),
|
|
101
|
+
) {}
|
|
102
|
+
|
|
103
|
+
audit(order: Order): FraudAuditResult {
|
|
104
|
+
const flags: string[] = []
|
|
105
|
+
|
|
106
|
+
if (this.isHighValueCrossBorder(order)) flags.push('high-value-cross-border')
|
|
107
|
+
if (this.isRetrySpike(order)) flags.push('retry-spike')
|
|
108
|
+
if (this.isBlacklisted(order)) flags.push('blacklisted-customer')
|
|
109
|
+
|
|
110
|
+
flags.forEach(f => this.logger.flag(order.id, f, { ts: this.clock() }))
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
score: this.computeScore(flags),
|
|
114
|
+
flags,
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private isHighValueCrossBorder(o: Order): boolean { /* ... */ }
|
|
119
|
+
private isRetrySpike(o: Order): boolean { /* ... */ }
|
|
120
|
+
private isBlacklisted(o: Order): boolean { /* ... */ }
|
|
121
|
+
private computeScore(flags: string[]): number { /* ... */ }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Conexão ao legado — apenas 2 linhas
|
|
125
|
+
class OrderProcessor {
|
|
126
|
+
private fraudAuditor = new FraudAuditor(this.logger, this.blacklistSvc)
|
|
127
|
+
|
|
128
|
+
processOrder(order: Order): Result {
|
|
129
|
+
// ... legado intocado ...
|
|
130
|
+
const fraudResult = this.fraudAuditor.audit(order) // ← conexão
|
|
131
|
+
if (fraudResult.score > 80) throw new FraudError(fraudResult.flags)
|
|
132
|
+
// ... legado intocado ...
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Quando preferir:** múltiplos behaviors relacionados, dependências múltiplas (clock, blacklist, logger), suite de testes própria justifica a classe.
|
|
138
|
+
|
|
139
|
+
### Pattern 3: Wrap Method — quando precisa MODIFICAR pré/pós condição
|
|
140
|
+
|
|
141
|
+
Sprout só adiciona comportamento DURANTE. Quando a mudança é "antes" ou "depois" de TODO o método existente, wrap é mais limpo.
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
// Cenário: precisamos enviar notificação para auditoria DEPOIS que
|
|
145
|
+
// processOrder() roda, com sucesso ou erro.
|
|
146
|
+
|
|
147
|
+
// PASSO 1 — rename mecânico (refactor IDE-assisted, zero risco)
|
|
148
|
+
class OrderProcessor {
|
|
149
|
+
processOrderLegacy(order: Order): Result { // ← renamed
|
|
150
|
+
// 200 linhas de lógica legada — intocadas
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// PASSO 2 — novo método com nome original, wrappando o legado
|
|
155
|
+
class OrderProcessor {
|
|
156
|
+
processOrder(order: Order): Result {
|
|
157
|
+
// pré-condição NOVA (testável)
|
|
158
|
+
this.notifyAuditStart(order)
|
|
159
|
+
|
|
160
|
+
let result: Result
|
|
161
|
+
try {
|
|
162
|
+
result = this.processOrderLegacy(order) // ← chamada ao legado
|
|
163
|
+
this.notifyAuditSuccess(order, result) // pós-condição NOVA
|
|
164
|
+
} catch (e) {
|
|
165
|
+
this.notifyAuditFailure(order, e) // pós-condição NOVA
|
|
166
|
+
throw e
|
|
167
|
+
}
|
|
168
|
+
return result
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
processOrderLegacy(order: Order): Result {
|
|
172
|
+
// 200 linhas de lógica legada — intocadas
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// sprout methods auxiliares (testáveis)
|
|
176
|
+
private notifyAuditStart(o: Order) { /* ... */ }
|
|
177
|
+
private notifyAuditSuccess(o: Order, r: Result) { /* ... */ }
|
|
178
|
+
private notifyAuditFailure(o: Order, e: Error) { /* ... */ }
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Test do wrap (não testa o legado)
|
|
182
|
+
test('processOrder — notifies start and success on happy path', () => {
|
|
183
|
+
const proc = makeProcessor({ legacyResult: { id: 'O1' } }) // fake do legacy
|
|
184
|
+
proc.processOrder({ id: 'O1' })
|
|
185
|
+
expect(proc.notifications).toEqual([
|
|
186
|
+
{ kind: 'start', orderId: 'O1' },
|
|
187
|
+
{ kind: 'success', orderId: 'O1' },
|
|
188
|
+
])
|
|
189
|
+
})
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Quando preferir:** mudança é estruturalmente "before/after" todo o legado, OR você precisa interceptar exception flow.
|
|
193
|
+
|
|
194
|
+
**Trade-off:** clientes externos chamando o método podem precisar de aviso (versionamento, deprecation) se a mudança comportamental afeta contrato. Sprout method não tem esse problema.
|
|
195
|
+
|
|
196
|
+
### Pattern 4: Wrap Class — wrap method em escala de classe
|
|
197
|
+
|
|
198
|
+
Decorator pattern aplicado pragmaticamente. Toda a classe ganha comportamento adicional sem modificar a original.
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
// Cenário: precisamos adicionar caching transparente a TODOS os métodos
|
|
202
|
+
// de PostgresOrderRepository (que não tem testes).
|
|
203
|
+
|
|
204
|
+
// ANTES — classe legada
|
|
205
|
+
class PostgresOrderRepository implements OrderRepository {
|
|
206
|
+
findById(id: string): Order { /* DB query */ }
|
|
207
|
+
save(order: Order): void { /* DB write */ }
|
|
208
|
+
findRecent(n: number): Order[] { /* DB query */ }
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// DEPOIS — wrap class (NOVA, testada)
|
|
212
|
+
class CachedOrderRepository implements OrderRepository {
|
|
213
|
+
constructor(
|
|
214
|
+
private inner: OrderRepository, // ← legado vai aqui
|
|
215
|
+
private cache: Cache,
|
|
216
|
+
) {}
|
|
217
|
+
|
|
218
|
+
findById(id: string): Order {
|
|
219
|
+
const cached = this.cache.get(`order:${id}`)
|
|
220
|
+
if (cached) return cached
|
|
221
|
+
const order = this.inner.findById(id) // delega ao legado
|
|
222
|
+
this.cache.set(`order:${id}`, order, 60_000)
|
|
223
|
+
return order
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
save(order: Order): void {
|
|
227
|
+
this.inner.save(order)
|
|
228
|
+
this.cache.invalidate(`order:${order.id}`)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
findRecent(n: number): Order[] {
|
|
232
|
+
return this.inner.findRecent(n) // sem cache (mais difícil)
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Em produção
|
|
237
|
+
const repo = new CachedOrderRepository(new PostgresOrderRepository(db), cache)
|
|
238
|
+
|
|
239
|
+
// Em teste — testa CachedOrderRepository com fake inner
|
|
240
|
+
test('CachedOrderRepository — cache hit avoids inner call', () => {
|
|
241
|
+
const inner = new FakeOrderRepository()
|
|
242
|
+
inner.save({ id: 'O1' } as Order)
|
|
243
|
+
const cached = new CachedOrderRepository(inner, new InMemoryCache())
|
|
244
|
+
cached.findById('O1')
|
|
245
|
+
cached.findById('O1')
|
|
246
|
+
expect(inner.findByIdCallCount).toBe(1) // segundo veio do cache
|
|
247
|
+
})
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**Quando preferir:** comportamento atravessa todos/maioria dos métodos da classe (caching, logging, audit, retry), e clientes esperam mesma interface.
|
|
251
|
+
|
|
252
|
+
### Pattern 5: Decision tree — qual técnica?
|
|
253
|
+
|
|
254
|
+
```text
|
|
255
|
+
Você está ADICIONANDO comportamento (não modificando)?
|
|
256
|
+
├─ Não → use legacy-characterization-tests + legacy-seams-and-test-harness
|
|
257
|
+
└─ Sim →
|
|
258
|
+
Comportamento novo é coeso e separável?
|
|
259
|
+
├─ Sim →
|
|
260
|
+
│ Tamanho do comportamento novo?
|
|
261
|
+
│ ├─ ≤ 30 linhas, 1 responsabilidade → SPROUT METHOD
|
|
262
|
+
│ ├─ > 30 linhas OU múltiplas responsabilidades → SPROUT CLASS
|
|
263
|
+
│ └─ Atravessa N métodos da classe legada → WRAP CLASS
|
|
264
|
+
└─ Não, é uma transformação no fluxo existente →
|
|
265
|
+
Mudança é "antes/depois" do método inteiro?
|
|
266
|
+
├─ Sim → WRAP METHOD
|
|
267
|
+
└─ Não → essa mudança REQUER characterization. Volte ao caminho normal.
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Pattern 6: Effort budget de sprout/wrap
|
|
271
|
+
|
|
272
|
+
| Técnica | Custo típico | Cobertura criada | Cobertura herdada |
|
|
273
|
+
|---|---|---|---|
|
|
274
|
+
| **Sprout Method** | 30 min - 2h | 100% do novo | 0% do legado |
|
|
275
|
+
| **Sprout Class** | 2-6h | 100% do novo | 0% do legado |
|
|
276
|
+
| **Wrap Method** | 1-3h | 100% do wrap (novo + delegação) | 0% do legado interior |
|
|
277
|
+
| **Wrap Class** | 4-8h | 100% do wrapper | 0% do delegado |
|
|
278
|
+
|
|
279
|
+
Versus characterization completa do legado (semanas), sprout/wrap entrega 100% do novo em horas. Trade-off: legado continua untested.
|
|
280
|
+
|
|
281
|
+
### Pattern 7: Documentar débito explicit
|
|
282
|
+
|
|
283
|
+
Sempre que aplicar sprout/wrap, criar TODO/ticket para characterization futura:
|
|
284
|
+
|
|
285
|
+
```ts
|
|
286
|
+
// PT-BR: comentário canônico no ponto de inserção do sprout
|
|
287
|
+
class OrderProcessor {
|
|
288
|
+
processOrder(order: Order): Result {
|
|
289
|
+
// ... legado intocado ...
|
|
290
|
+
|
|
291
|
+
// [legacy-debt #issue-1234] sprout — auditFraudAttempt testado isolado;
|
|
292
|
+
// método circundante (processOrder) ainda untested. Characterization
|
|
293
|
+
// completa enquanto refactor maior em Q3/2026.
|
|
294
|
+
auditFraudAttempt(order, this.fraudLogger)
|
|
295
|
+
|
|
296
|
+
// ... legado intocado ...
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
**Sem doc, débito é invisível** — equipe esquece, próxima pessoa pensa que "está testado". Doc + ticket cria responsabilidade explícita.
|
|
302
|
+
|
|
303
|
+
## Anti-patterns
|
|
304
|
+
|
|
305
|
+
### ANTI: sprout que mexe no legado "só um pouquinho"
|
|
306
|
+
|
|
307
|
+
```text
|
|
308
|
+
ANTI: "vou inserir o sprout, e já que estou aqui, ajusto essa
|
|
309
|
+
variável local para passar para o sprout".
|
|
310
|
+
|
|
311
|
+
PROBLEMA: cada edição no legado é risco. Você não tem teste para detectar
|
|
312
|
+
regressão. "Ajustar variável" pode quebrar branch raro.
|
|
313
|
+
|
|
314
|
+
CERTO: passe APENAS o que está em escopo no ponto de inserção. Se
|
|
315
|
+
precisa de mais, aceite que o sprout não cabe ali — escolha
|
|
316
|
+
outro ponto de inserção OU technique diferente.
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### ANTI: sprout que faz I/O direto
|
|
320
|
+
|
|
321
|
+
```text
|
|
322
|
+
ANTI: function auditFraudAttempt(order) {
|
|
323
|
+
fetch('https://internal-fraud-api/...', {...}) // ← I/O dentro
|
|
324
|
+
log.warn(...) // ← I/O dentro
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
PROBLEMA: sprout vira intestável. Você criou o problema do legado
|
|
328
|
+
numa nova localização.
|
|
329
|
+
|
|
330
|
+
CERTO: dependências passadas via parameter ou constructor injection:
|
|
331
|
+
function auditFraudAttempt(order, logger, fraudApi) { ... }
|
|
332
|
+
Em produção: chama com reais. Em teste: chama com fakes.
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### ANTI: wrap method esquecendo error path
|
|
336
|
+
|
|
337
|
+
```text
|
|
338
|
+
ANTI: function processOrder(o) {
|
|
339
|
+
notifyStart(o)
|
|
340
|
+
const r = processOrderLegacy(o)
|
|
341
|
+
notifySuccess(o, r)
|
|
342
|
+
return r
|
|
343
|
+
}
|
|
344
|
+
— sem try/catch.
|
|
345
|
+
|
|
346
|
+
PROBLEMA: quando legacy throw, notifyStart já rodou mas notifyFailure
|
|
347
|
+
NÃO. Comportamento parcial. Auditoria fica incoerente.
|
|
348
|
+
|
|
349
|
+
CERTO: wrap method SEMPRE tem try/finally explícito quando
|
|
350
|
+
pré-condição roda I/O ou tem efeito observável:
|
|
351
|
+
try { r = legacy(o); notifySuccess(o, r); return r }
|
|
352
|
+
catch (e) { notifyFailure(o, e); throw e }
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### ANTI: sprout class mas pré/pós condição vai para sprout
|
|
356
|
+
|
|
357
|
+
```text
|
|
358
|
+
ANTI: SproutFraudAuditor.audit() retorna result E também imprime log
|
|
359
|
+
com timestamp da chamada.
|
|
360
|
+
|
|
361
|
+
PROBLEMA: pré/pós-condição cross-cuts. Reviewer não vê em uma chamada
|
|
362
|
+
o que muda. Hidden side effects.
|
|
363
|
+
|
|
364
|
+
CERTO: pré/pós são responsabilidades do CALLER (legacy). Sprout faz
|
|
365
|
+
UMA coisa, retorna resultado puro, caller decide o que fazer
|
|
366
|
+
(incluindo logar). Mantém sprout testável sem mocks de log.
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### ANTI: usar wrap class quando sprout method bastaria
|
|
370
|
+
|
|
371
|
+
```text
|
|
372
|
+
ANTI: criar ClassWrapperOrderProcessor para adicionar audit em UM
|
|
373
|
+
método.
|
|
374
|
+
|
|
375
|
+
PROBLEMA: surface change desproporcional. Outros 9 métodos da classe
|
|
376
|
+
ganham wrap inútil. Imports espalhados precisam mudar.
|
|
377
|
+
|
|
378
|
+
CERTO: comece pequeno. Sprout method primeiro. Promove a sprout class
|
|
379
|
+
quando comportamento cresce. Promove a wrap class quando
|
|
380
|
+
atravessa N métodos. Não comece com wrap class.
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### ANTI: pular ticket de débito técnico
|
|
384
|
+
|
|
385
|
+
```text
|
|
386
|
+
ANTI: aplica sprout, sem comment, sem ticket. "Vou lembrar de testar
|
|
387
|
+
o legado depois".
|
|
388
|
+
|
|
389
|
+
PROBLEMA: 6 meses depois, ninguém sabe que processOrder() ainda é
|
|
390
|
+
untested. Próxima pessoa edita legado direto, regression
|
|
391
|
+
slips, incident.
|
|
392
|
+
|
|
393
|
+
CERTO: comment canônico no sprout point + ticket linkado. Sem isso,
|
|
394
|
+
débito é invisível e a chance de characterization futura cai
|
|
395
|
+
para perto de zero.
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
## Verificação
|
|
399
|
+
|
|
400
|
+
Antes de aprovar PR com sprout/wrap:
|
|
401
|
+
|
|
402
|
+
1. **Técnica certa para a mudança** — sprout (add) vs wrap (transform pre/post)
|
|
403
|
+
2. **Escopo da mudança no legado é mínimo** — sprout: 1 linha; wrap: rename + chamada
|
|
404
|
+
3. **Sprout/wrap testa isolado** — fakes para todas dependências, sem I/O real
|
|
405
|
+
4. **Coverage do novo comportamento** — 100%, não 80% (é código novo, sem desculpa)
|
|
406
|
+
5. **Error path coberto em wrap** — try/catch explícito quando aplicável
|
|
407
|
+
6. **TODO/ticket de débito criado** — comment canônico + linked issue
|
|
408
|
+
7. **Reviewer pode verificar legado intacto** — diff do legado é apenas rename ou 1 inserção
|
|
409
|
+
|
|
410
|
+
## Limiar de "sprout/wrap apropriado"
|
|
411
|
+
|
|
412
|
+
```text
|
|
413
|
+
Comportamento ADICIONADO: sim (não modificado)
|
|
414
|
+
Lógica nova testável isolada: sim (DI injected, sem I/O direto)
|
|
415
|
+
Conexão ao legado: ≤ 1-2 linhas em sprout, rename+1 em wrap
|
|
416
|
+
Cobertura do novo: 100%
|
|
417
|
+
Custo de characterization completa: documentado como > 5× custo do sprout
|
|
418
|
+
Débito técnico: ticket criado, comment canônico no código
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
Se algum critério falha → sprout/wrap não é o caminho certo. Escolha outro.
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## Ver também
|
|
426
|
+
|
|
427
|
+
- [`_shared-legacy/glossary.md`](../_shared-legacy/glossary.md) — vocabulário canônico (sprout method/class, wrap method/class)
|
|
428
|
+
- [`legacy-characterization-tests`](../legacy-characterization-tests/SKILL.md) — para refactor (não adicionar), characterization é mandatório
|
|
429
|
+
- [`legacy-seams-and-test-harness`](../legacy-seams-and-test-harness/SKILL.md) — sprout class testável requer DI; técnicas do cap 25 dão a base
|
|
430
|
+
- [`legacy-effect-analysis`](../legacy-effect-analysis/SKILL.md) — escolha do ponto de inserção do sprout informada por effect sketch
|
|
431
|
+
- [`legacy-monster-methods`](../legacy-monster-methods/SKILL.md) — em monster method, sprout cria pé-de-cabra para refactor incremental
|
|
432
|
+
- [`pre-refactor-characterization`](../pre-refactor-characterization/SKILL.md) — gate libera sprout/wrap se cobertura do NOVO = 100%, mesmo sem characterization do legado
|
|
433
|
+
|
|
434
|
+
*Material-fonte: Working Effectively with Legacy Code — Feathers, 2004 — Cap 6: "I Don't Have Much Time and I Have to Change It".*
|