Pular para o conteúdo

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 parteNão faz
os nomes no relayo que não está nele
quantos parâmetros cada ação recebeo nome dos arquivos internos
o que ela devolve, e de que tipoa ordem das ações no arquivo
o tipo do erro que ela levantao texto exato da mensagem de erro
os campos de um record exportadoum campo cujo nome começa com _
os membros de um enum exportadoa 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.

dataforge
// no fim de src/main.df
relay cpf, cnpj, email, cep, Resultado

A 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:

dataforge
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.

dataforge
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:

dataforge
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, Resultado

Acrescentar 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?

Por onde seguir#