jvoegele
Bond (Design by Contract for Elixir) - 1.3.0 released: contract inheritance & refinement
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.
Links
First Post!
jvoegele
For anyone curious about future directions for Bond, I’ve created some forward-looking issues in GitHub issue tracker:
Popular in News & Updates
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









