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.

Quando esta spec se aplica

Triggers primários

Todos os triggers

Corpo da especificação

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, -1 em loops/index
  • 2 em 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.kmd R3)
  • 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:

  1. ☐ Naming segue code/naming.kmd (variables, classes, files)
  2. ☐ Funções dentro de length/complexity limits (code/functions.kmd)
  3. ☐ Errors handled per code/error-handling.kmd (no swallow, no log+throw)
  4. ☐ Comments seguem WHY-not-WHAT (code/comments.kmd); TODO com ticket
  5. ☐ Imports ordenados/grouped (code/imports.kmd)
  6. ☐ Sem magic numbers (extraídos pra constants)
  7. ☐ Sem hardcoded secrets / credentials (env/vault)
  8. ☐ Sem global mutable state novo (DI ou encapsulação)
  9. ☐ Tests cobrem golden path + edge cases
  10. ☐ 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
  • code/comments.kmd — TODO format, commented-out code
  • code/error-handling.kmd — generic catch, log+throw
  • code/functions.kmd — length, complexity, boolean trap
  • code/imports.kmd — wildcard, side effects
  • code/naming.kmd — magic number = unnamed constant
  • code/languages/koda-style.kmd — 5 APs Koda
  • policies/reuse-first.kmd — não reinventar
  • policies/hyperscale-first.kmd — premature opt boundary
  • policies/security.kmd — secrets handling

Referências