Functions & Methods
Design de funções/métodos cross-language: length limits (soft 30 / hard 60), argument count ≤4, early-return preferred sobre nested, default values per-lang, pure vs impure marking, side effects documentadas, cyclomatic complexity (soft 10 / hard 15).
Spec — Functions & Methods
Facet *ode*do Koder Design.
Cobre design de funções/métodos cross-language. Naming de funções em
code/naming.kmdR3.
R1 — Length limits
| Limite | Soft (warning) | Hard (error) |
|---|---|---|
| Lines per function | 30 | 60 |
| Lines per method | 30 | 60 |
Comentários e linhas em branco contam como 0.5 (linter usa LOC efetivo).
*efatorar quando passa*
- Extract helper function (mais comum)
- Quebrar por etapa lógica (parsing → validation → execution)
- Quando é table-driven (loop sobre data), pode ser maior — exceção
documentada via comment
# ❌ Função de 80 linhas com 5 responsabilidades misturadas
def process_request(req):
# parsing (15 linhas)
...
# validation (20 linhas)
...
# business logic (30 linhas)
...
# serialization (15 linhas)
...
# ✅ Quebrada por etapa
def process_request(req):
parsed = _parse(req)
_validate(parsed)
result = _execute(parsed)
return _serialize(result)R2 — Argument count
Soft limit: * positional args* Hard limit: **
Mais que isso → *grupar via*
| Padrão | Linguagem |
|---|---|
| *ptions struct* | Go (idiom), Rust |
| *amed arguments* | Python (kwarg=), Dart ({required ...}) |
| *uilder pattern* | Java/Kotlin, Rust (idiomático pra config) |
| *onfig object* | JS/TS |
// ❌
func CreateUser(name, email, phone, address string, age int, isAdmin, isActive bool) (*User, error)
// ✅ Options struct
type CreateUserOpts struct {
Name, Email, Phone, Address string
Age int
IsAdmin, IsActive bool
}
func CreateUser(opts CreateUserOpts) (*User, error)# ❌
def create_user(name, email, phone, address, age, is_admin, is_active):
...
# ✅ Named-only
def create_user(*, name, email, phone, address, age,
is_admin=False, is_active=True):
...*eceivers/self*não contam (r *Repo, self, this).
R3 — Early return / guard clauses preferido
# ❌ Pyramid of doom
def authenticate(user):
if user is not None:
if user.is_active:
if user.has_password():
if check_password(user, password):
return Session(user)
else:
return None
else:
return None
else:
return None
else:
return None
# ✅ Guard clauses
def authenticate(user):
if user is None:
return None
if not user.is_active:
return None
if not user.has_password():
return None
if not check_password(user, password):
return None
return Session(user)Vale pra todas as linguagens com early-return. Reduz nesting + cyclomatic complexity.
R4 — Default values
| Linguagem | Suporta? | Padrão |
|---|---|---|
| Python | sim | def f(x, y=10): (não usar mutable default — =None) |
| Rust | não (usa Option<T>) |
f(x: i32, y: Option<i32>) { let y = y.unwrap_or(10); } |
| Go | não | usar options struct ou variadic |
| Dart | sim | void f(int x, [int y = 10]) ou {int y = 10} |
| JS/TS | sim | function f(x, y = 10) |
| Koda | sim | def f(x, y = 10) |
*utable default proibido em Python*
# ❌ Default compartilhado entre chamadas (gotcha clássico)
def append(item, items=[]):
items.append(item)
return items
# ✅
def append(item, items=None):
if items is None:
items = []
items.append(item)
return itemsR5 — Pure vs impure
*ure function* sem side effects (não muta argumento, não toca global, não faz IO), output depende só do input.
*mpure function* tem side effects (muta state, IO, randomness, time).
Marker convention
- *efault = pure* nome simples (
compute_total,format_email) - *mpure*com side effect óbvio: nome verb-ish (
save_user,send_email,log_event) — o nome *enuncia* - *mpure*com side effect não-óbvio: comentário *brigatório*
def get_user(user_id):
"""Get user by id.
Side effect: increments cache hit/miss counter.
"""
counter.increment(...)
return cache.get(user_id) or db.get(user_id)R6 — Single Responsibility
Cada função faz *ma coisa*bem-definida. Heurística: o nome da função é uma frase verb que descreve *ma*ação. Se você precisa de "and" no nome (load_and_validate_config), é provavelmente 2 funções.
# ❌
def load_and_validate_config(path):
raw = read_file(path)
parsed = parse_toml(raw)
if 'database' not in parsed:
raise ValueError(...)
if 'auth' not in parsed:
raise ValueError(...)
return parsed
# ✅
def load_config(path):
return parse_toml(read_file(path))
def validate_config(config):
for required in ('database', 'auth'):
if required not in config:
raise ValueError(...)R7 — Cyclomatic complexity
Soft: *0*(warning). Hard: *5*(error).
Cada if/else/case/loop/try/and/or dentro da função adiciona +1.
Refatorar via:
- Extract function
- Replace conditional com polymorphism / dispatch table
- Early return reduz
elsebranches
Tools per-lang: gocyclo (Go), radon (Python), cyclomatic (generic), dart_code_metrics (Dart), eslint complexity (JS/TS).
R8 — Receiver / self conventions
| Linguagem | Convenção |
|---|---|
| Go | Single-letter (r *Repo, c Config); consistent across methods of same type |
| Rust | &self (read-only), &mut self (mutate), self (consume) |
| Python | self sempre primeiro arg (idiom) |
| Dart | implicit this; usar this. só pra desambiguar |
| Koda | self implícito; método define receiver |
| JS/TS | this implicit; arrow vs regular afeta binding |
R9 — Side effects documentation
Funções que tocam IO/state externo *evem*
- Ter nome que denuncia (R5)
- Doc-comment listando o side effect explícito quando não óbvio
- Tratar erro per
error-handling.kmd - Considerar se cabe injection de dependência (
def fetch(http_client)vs
def fetch()que cria HTTP client por dentro — testabilidade)
R10 — Naming verbs
Cross-link com code/naming.kmd R3. Resumo:
- Function/method: *erb phrase*(
get_user,compute_total) - Boolean function: *redicate*(
is_active,has_permission) - Class/Type: *oun*(
User,OrderSummary)
Anti-patterns
AP-F1 — God function
200 linhas, >10 args, >20 cyclomatic. Refatorar imediatamente.
AP-F2 — Boolean trap
// ❌ Sem ler a sig, é impossível dizer o que cada bool faz
createUser(name, email, true, false, true);
// ✅ Named/options
createUser({name, email, isAdmin: true, isVerified: false, sendWelcome: true});AP-F3 — Hidden side effect com nome puro
# ❌ Nome sugere pure, mas escreve no disco
def format_email(addr):
cache.write(addr, ...) # surpresa
return addr.lower().strip()AP-F4 — Premature option parameter
Adicionar parâmetro opcional "pra caso de algum caller precisar" sem caller real. YAGNI. Adicionar quando o segundo caller chegar.
AP-F5 — Output via mutated argument (em linguagens com return)
# ❌
def add_total(items, result):
result['total'] = sum(items)
# ✅
def total(items):
return sum(items)Exceção: Go onde mutation é idiomática (SortSlice(s, less)) ou quando alocar é caro (hot path).
Audit deterministic
functions-audit.sh:
- Function lines > soft → warning; > hard → error
- Args > 4 → warning; > 6 → error
- Cyclomatic > 10 → warning; > 15 → error
- Mutable default arg (Python) → error
- Boolean position arg > 2 → warning (suggest named/options)
Cross-link
code/naming.kmd— naming conventioncode/error-handling.kmd— exceptions/result patternscode/comments.kmd— quando documentarcode/anti-patterns.kmd— patterns proibidos