Bond 1.3.0 is out.
The big story since the 1.0 release: Bond now does contract inheritance, and as of 1.3.0, Eiffel-style contract refinement. This is Design by Contract meeting the Liskov Substitution Principle β an abstraction declares contracts, and its implementations inherit (and can deliberately refine) them.
def deps do
[{:bond, "~> 1.3"}]
end
New to Bond? It brings Design by Contract to Elixir β @pre/@post/@invariant checked at runtime, with failure messages that tell you exactly what was violated and why. The Bond 1.0.0 announcement and the getting-started guide are the best on-ramps.
Contract inheritance (1.2.0)
A behaviour or protocol is a promise about a family of implementations; a contract is the formal content of that promise. Bond now lets you state it once, on the abstraction, and enforces it across every implementation β present and future.
Behaviours β declare @pre/@post on the @callback, and implementers inherit them:
defmodule Ledger do
use Bond.Behaviour
@pre positive_amount: amount > 0
@post non_negative: result >= 0
@callback withdraw(balance :: non_neg_integer, amount :: pos_integer) :: non_neg_integer
end
defmodule BankAccount do
use Bond, behaviours: [Ledger]
@impl true
def withdraw(balance, amount) when amount <= balance, do: balance - amount
end
BankAccount.withdraw/2 now enforces Ledgerβs contract β though it appears nowhere in BankAccount β and a violation is attributed back to the source behaviour.
Protocols β declare contracts on a defprotocol, enforced once at the dispatch boundary, so implementations stay completely ordinary (no Bond awareness required):
defprotocol Sized do
use Bond.Protocol
@post non_negative: result >= 0
def size(data)
end
Every call through Sized.size/1 checks the contract, whichever implementation runs β and it survives protocol consolidation.
New in 1.3.0: Eiffel-style refinement
By default an implementation inherits its contracts verbatim. 1.3.0 lets it deliberately refine them, following Eiffelβs behavioural-subtyping rules β with two distinct keywords that make the (counterintuitive) variance explicit:
@pre_weakenweakens the precondition: effective precondition =inherited or weaken(preconditions may only weaken down a hierarchy β contravariance).@post_strengthenstrengthens the postcondition: effective postcondition =inherited and strengthen(postconditions may only strengthen β covariance).
defmodule SavingsAccount do
use Bond, behaviours: [Ledger]
@impl true
@pre_weaken zero_ok: amount == 0 # also accept a no-op zero withdrawal
@post_strengthen audited: log_exists?(result)
def withdraw(balance, amount), do: ...
end
The distinct keywords are the teaching: using or to weaken a precondition is exactly the Liskov-safe direction, even though it reads backwards at first. Refinement works for protocol implementations too, via use Bond.Protocol.Impl in the defimpl. (Plain @pre/@post on an inherited operation remains a compile error β that syntax was reserved precisely so refinement could slot in with zero migration debt.)
Full rules and examples: the Contract Inheritance guide.
Also since 1.0
- 1.1.0 β a performance pass: contract checks now gate through
:persistent_term(~2.6Γ cheaper when enabled), plus a newBond.Configruntime API for toggling contract kinds at runtime. - 1.2.1 β
<~pattern bindings are now correctly accepted in inherited-contract reference validation, and the protocol-contracts guide was merged into a single unified Contract Inheritance guide.
Stability
Everything since 1.0 is additive β no breaking changes β so 1.3.0 is a normal minor release, and the new surface is covered by Bondβs stability guarantees. Compatibility is verified across Elixir 1.16β1.20 in CI.
Thanks & feedback
The contract-inheritance design was shaped by dogfooding it in a real application and by feedback from the earlier threads β thank you. Questions, bug reports, and ideas are very welcome here or on the issue tracker.






















