O contrato de uma biblioteca
O que o relay promete, o que a assinatura promete, e o que quebra quem depende de você.
O contrato de uma biblioteca é tudo o que alguém pode escrever hoje e esperar que continue funcionando amanhã. Ele é maior do que parece — e a maior parte dele nunca foi escrita em lugar nenhum.
O que entra no contrato#
| Faz parte | Não faz |
|---|---|
os nomes no relay | o que não está nele |
| quantos parâmetros cada ação recebe | o nome dos arquivos internos |
| o que ela devolve, e de que tipo | a ordem das ações no arquivo |
| o tipo do erro que ela levanta | o texto exato da mensagem de erro |
os campos de um record exportado | um campo cujo nome começa com _ |
os membros de um enum exportado | a implementação de qualquer método |
Escreva o `relay` cedo#
Enquanto não há relay, tudo é público — e cada coisa que alguém descobre e passa a usar vira contrato sem você saber. O relay é o momento em que você decide, e quanto mais cedo, menor o estrago.
// no fim de src/main.df
relay cpf, cnpj, email, cep, ResultadoA partir daí o analisador ajuda: V.interna() passa a ser acusado antes de rodar, na máquina de quem usa.
Assine o que você promete#
Os tipos declarados atravessam o adopt. Uma ação sem anotação promete menos, e o check de quem usa fica cego:
action formatar(valor, casas): // promete pouco
yield round(valor, casas)
action formatar(valor: Float, casas: Integer := 2) -> Float:
yield round(valor, casas)Com a segunda forma, V.formatar("12", 2) é acusado na máquina de quem chamou, com a linha certa — e a mensagem cita o seu arquivo como origem da declaração.
Erros: levante o seu, não o de dentro#
Se a sua biblioteca deixa vazar o erro do Arcane.Database que ela usa por dentro, o banco virou parte do seu contrato — e trocá-lo numa versão de correção quebraria quem tratava aquele erro.
action buscar(id: Integer) -> Vault:
monitor:
yield consultar(id)
handle Error as e:
// o erro do domínio, com a causa preservada
trigger $"nao foi possivel buscar o registro {id}"A causa do erro original continua acessível em .causa, e o relatório desenha as duas camadas: quem usa vê o que a sua biblioteca prometeu e o que de fato aconteceu.
O que um valor devolvido promete#
Devolver um record promete os campos dele; devolver um Vault promete as chaves — e um vault é mais fácil de mudar por engano. Para um retorno estável, prefira record:
record Resultado:
valido: Boolean
motivo: String := ""
action cpf(texto: String) -> Resultado:
given len(texto) smaller 11:
yield Resultado(no, "o CPF precisa de 11 dígitos")
yield Resultado(yes)
relay cpf, ResultadoAcrescentar um campo com padrão a um record é compatível; remover ou renomear um não é. E o record exportado precisa ir no relay: sem ele, quem usa recebe o valor e não consegue nomear o tipo.
A lista de conferência antes da 1.0.0#
- Todo nome público está num
relay? - Toda ação pública tem tipos nos parâmetros e no retorno?
- Todo erro que sai da biblioteca é seu, e não de uma dependência?
- O topo dos arquivos não faz nada além de declarar?
- Os testes exercitam a biblioteca pelo nome público (
adopt minha-lib)? - O README tem um exemplo que roda?