Code Anti-patterns
code specs/code/anti-patterns.kmd
Catálogo cross-language de anti-patterns proibidos: eval/dynamic exec sem audit, magic numbers, god classes, deep nesting, mutable global state, singleton sem DI, catching genérico, null propagation, premature optimization, boolean trap. Linter config canônica per linguagem. Code review checklist top-10. Cross-link com APs específicos das per-language style specs.
When this spec applies
Primary triggers
- Verificar conformidade de código com Koder Design Code facet
All triggers
- Code review verificando anti-patterns
- Detectar smell em código existente
- Configurar linter / static analyzer per linguagem
- Definir code review checklist
Specification body
Spec — Code Anti-patterns
Facet Code do Koder Design.
Catálogo cross-language. APs específicos de uma linguagem vivem em
code/languages/<lang>-style.kmd(ex: 5 APs Koda).
Cross-language anti-patterns
AP-X1 — eval / dynamic exec sem audit
# ❌
result = eval(user_input)
exec(template.format(...))
// ❌
new Function(userCode)();
window.eval(snippet);
Risco: code injection / RCE / arbitrary code exec. Exceção: tooling de dev (REPL Koda, sandbox Koru) — exige audit explícito + sandbox.
AP-X2 — Magic numbers
# ❌
if user.age >= 18: ...
sleep(86400)
buffer = bytearray(4096)
# ✅
LEGAL_AGE = 18
SECONDS_PER_DAY = 86400
DEFAULT_BUFFER_BYTES = 4096
if user.age >= LEGAL_AGE: ...
sleep(SECONDS_PER_DAY)
buffer = bytearray(DEFAULT_BUFFER_BYTES)
Exceções aceitas sem extrair:
0,1,-1em loops/index2em divisão por 2 (binary search)- HTTP status codes (
200,404) — convenção universal
AP-X3 — God class / God function
Coberto em code/functions.kmd AP-F1. Resumo: classe com >500
linhas ou >20 métodos públicos = quebrar por responsabilidade.
AP-X4 — Deep nesting (> 4 níveis)
# ❌
def f():
if a:
for x in xs:
if b:
while c:
if d: # 5 níveis
...
Refatorar via:
- Early return / guard clauses (
code/functions.kmdR3) - Extract function pra blocos internos
- Replace conditional com dispatch (dict/match/polymorphism)
AP-X5 — Mutable global state
# ❌ Global muta entre tests; race condition em concurrent
_cache = {}
def get(k):
return _cache.get(k)
def set(k, v):
_cache[k] = v
# ✅ Encapsulated, dependência injetável
class Cache:
def __init__(self):
self._data = {}
def get(self, k): return self._data.get(k)
def set(self, k, v): self._data[k] = v
# main()
cache = Cache()
handler = Handler(cache=cache)
Exceções: constantes immutable (PI, config readonly carregada
no boot).
AP-X6 — Singleton sem dependency injection
# ❌ Hard-coded singleton; impossível mock em teste
class DatabaseClient:
_instance = None
@classmethod
def get(cls):
if cls._instance is None:
cls._instance = cls(...)
return cls._instance
# Caller
def handler(req):
db = DatabaseClient.get() # hard-coupled
...
# ✅ DI
def handler(req, db):
...
# main wires the singleton
db = DatabaseClient(...)
app.add_route("/", lambda req: handler(req, db))
AP-X7 — Catching Exception/Error genérico
Coberto em code/error-handling.kmd AP-E1.
AP-X8 — Null/nil propagation sem null-check
# ❌ Pode quebrar em qualquer ponto da chain
result = user.profile.address.city.upper()
# ✅ Optional chaining ou validação
city = (user.profile.address.city.upper()
if user and user.profile and user.profile.address
else None)
# OU usar Optional/Maybe pattern
city = (Some(user)
.map(lambda u: u.profile)
.map(lambda p: p.address)
.map(lambda a: a.city.upper())
.unwrap_or(None))
Per linguagem:
- TS: optional chaining (
user?.profile?.address?.city?.toUpperCase()) - Rust:
Option<T>+?operator - Dart:
user?.profile?.address?.city?.toUpperCase() - Kotlin: similar
- Koda:
user&.profile&.address&.city&.upcase
AP-X9 — Premature optimization
# ❌ Otimização micro sem profile
# "Trocou map por list comprehension porque é mais rápido (sem medir)"
# "Cacheou resultado em variável (single use, sem ganho)"
# "Usou regex compilada (chamada 1 vez)"
# ✅ Default código clear; otimizar quando profile mostra hot path
result = [transform(x) for x in items]
Cross-link com policies/hyperscale-first.kmd: otimização que não
pede esforço extra (escolher estrutura O(n log n) em vez de O(n²)
quando ambos têm 1 linha) é encorajada — outro lado do default.
AP-X10 — Boolean trap
Coberto em code/functions.kmd AP-F2.
AP-X11 — Print/log para debug não-removido
# ❌ Production code com print de debug
def handler(req):
print(f"DEBUG: req = {req}") # ← esquecido
...
Linter detecta print()/console.log()/fmt.Println() em src/
(allowed em test/ e cmd/
AP-X12 — Commented-out code
Coberto em code/comments.kmd.
AP-X13 — Copy-paste programming sem extract
Mesmo bloco de 5+ linhas duplicado em ≥3 lugares = extract function.
# ❌ Repetido 4 vezes
user = db.query("SELECT * FROM users WHERE id = ?", uid)
if not user:
log.warn("user not found", uid=uid)
metrics.miss("user.lookup")
return None
return User.from_row(user)
# ✅ Extract
def fetch_user(uid):
user = db.query("SELECT * FROM users WHERE id = ?", uid)
if not user:
log.warn("user not found", uid=uid)
metrics.miss("user.lookup")
return None
return User.from_row(user)
AP-X14 — Reinventar wheel sem checar code/reuse-first.kmd
Implementar caching, retry, validação, parsing de string canônica
(email, URL, IP) sem checar se SDK Koder já tem. Cross-link com
policies/reuse-first.kmd 3-question check.
AP-X15 — TODO sem ticket
Coberto em code/comments.kmd R7.
AP-X16 — Side-effect import/init não-óbvio
# ❌ Import-time side effect
# foo.py
print("Loading foo") # ← roda na primeira import; surpresa
db.connect() # ← muda global state na import
# ✅ Lazy
def init():
db.connect()
AP-X17 — Race condition em concurrent code
Acesso compartilhado sem sync (lock/mutex/channel). Per linguagem:
- Go: race detector (
go test -race) - Rust: compiler já bloqueia naturalmente
- Python: GIL não salva — async/await + shared state ainda race-y
- JS: single-threaded mas async pode sequenciar wrong
AP-X18 — Hardcoded credentials/secrets
# ❌ Nunca
API_KEY = "sk_live_abc123..."
DATABASE_URL = "postgres://user:pass@host/db"
# ✅ Env / vault
API_KEY = os.environ["API_KEY"]
DATABASE_URL = vault.get("DATABASE_URL")
Linter falha em strings que casam com pattern de secret (key, token, password literal).
Per-language anti-patterns (cross-reference)
Cada code/languages/<lang>-style.kmd lista APs específicos:
| Lang | APs específicos | Localização |
|---|---|---|
| Koda | AP1 array aliasing, AP2 implicit globals, AP3 string concat loop, AP4 mutate during iter, AP5 runtime research file | code/languages/koda-style.kmd |
| Go | (futuro) zero-value misuse, ignored error, goroutine leak | code/languages/go-style.kmd |
| Rust | (futuro) unwrap em production, unsafe sem Comment Safety | code/languages/rust-style.kmd |
| Python | (futuro) mutable default, late binding em closure | code/languages/python-style.kmd |
| Dart | (futuro) FutureBuilder rebuild loop, BuildContext após async gap | code/languages/dart-style.kmd |
Linter config canônica per linguagem
Templates em meta/docs/stack/specs/code/linter-configs/:
| Linguagem | Config canônica | Arquivo |
|---|---|---|
| Go | golangci-lint |
golangci.template.yml |
| Rust | clippy.toml + cargo deny |
clippy.template.toml |
| Python | ruff (substitui flake8/black/isort) |
ruff.template.toml |
| Dart | analysis_options.yaml |
analysis_options.template.yaml |
| JS/TS | eslint flat config |
eslint.config.template.mjs |
| Shell | shellcheck (opções via flag) |
.shellcheckrc.template |
Princípio: cada projeto novo copia o template canônico; overrides exigem comment explicando por que.
Code review checklist (top-10)
Imprimir/usar em todo PR review:
- ☐ Naming segue
code/naming.kmd(variables, classes, files) - ☐ Funções dentro de length/complexity limits (
code/functions.kmd) - ☐ Errors handled per
code/error-handling.kmd(no swallow, no log+throw) - ☐ Comments seguem WHY-not-WHAT (
code/comments.kmd); TODO com ticket - ☐ Imports ordenados/grouped (
code/imports.kmd) - ☐ Sem magic numbers (extraídos pra constants)
- ☐ Sem hardcoded secrets / credentials (env/vault)
- ☐ Sem global mutable state novo (DI ou encapsulação)
- ☐ Tests cobrem golden path + edge cases
- ☐ Doc comment em export pública (Module/Class/Function)
Audit deterministic
anti-patterns-audit.sh agrega:
- Magic numbers detector (allowlist de 0/1/-1/2/HTTP codes)
eval/exec/Function/globalThis['eval']detector- Hardcoded secret detector (regex high-entropy + known prefixes)
- Print/console.log em src/ sem allowlist
- TODO sem ticket após 30 dias
- Cyclomatic complexity por function (delegate
code/functions.kmd) - Deep nesting > 4 levels
Cross-link
code/comments.kmd— TODO format, commented-out codecode/error-handling.kmd— generic catch, log+throwcode/functions.kmd— length, complexity, boolean trapcode/imports.kmd— wildcard, side effectscode/naming.kmd— magic number = unnamed constantcode/languages/koda-style.kmd— 5 APs Kodapolicies/reuse-first.kmd— não reinventarpolicies/hyperscale-first.kmd— premature opt boundarypolicies/security.kmd— secrets handling
References
rfcs/design-RFC-001-koder-design-system.kmdspecs/code/comments.kmdspecs/code/error-handling.kmdspecs/code/functions.kmdspecs/code/naming.kmdspecs/code/imports.kmdspecs/code/languages/koda-style.kmdpolicies/hyperscale-first.kmdpolicies/design-governance.kmd