jvoegele

jvoegele

Bond (Design by Contract for Elixir) - 1.3.0 released: contract inheritance & refinement

Bond 1.3.0 is out. :tada: 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_weaken weakens the precondition: effective precondition = inherited or weaken (preconditions may only weaken down a hierarchy — contravariance).
  • @post_strengthen strengthens 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 new Bond.Config runtime 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

jvoegele

For anyone curious about future directions for Bond, I’ve created some forward-looking issues in GitHub issue tracker:

Where Next?

Popular in News & Updates Top

zachdaniel
Had a great time at Jax.Ex! Gave a talk called “Model your Domain, Derive the Rest”. Thanks to the folks at HashRocket for having me!
New
bartblast
After building Hologram and sharing updates across various places, I’ve realized there’s a lot happening that doesn’t always make it to t...
New
zachdaniel
Hey folks! AshEvents Release We’ve just released the first version of AshEvents, an Event Sourcing tool for Ash Framework apps. Check o...
New
fhunleth
We recently released Nerves 1.5.0 and corresponding updates to the Nerves new project generator, nerves_bootstrap and our official system...
New
DominikWolek
Hey everyone! The Membrane Framework has a new website! It contains tutorials, blog posts, and other resources you’ll find handy while u...
New
bartblast
Hey Elixir community! :waving_hand: First, I owe you all an apology. There’s a running joke among my friends that Hologram is like nucle...
New
zachdaniel
Hey folks! In order to give the new AshSqlite a trial run and make sure its got some real usage, AshHq’s multi-package search has been re...
New
hugobarauna
This post introduces the new data features in Livebook 0.9: fast data exploration through integration with Explorer, interactive data tab...
New
bartblast
Hey there! :slight_smile: We need help completing Elixir’s browser runtime by porting some Erlang functions to JavaScript. Hologram aut...
New
zachdaniel
Hey folks! I’ll be teasing some interesting bits going into Ash 3.0 while I work on it, and this is post #1! You can follow along with t...
New

Other popular topics Top

axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 48475 226
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 29703 241
New
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
AstonJ
Please see the new poll here: Which code editor or IDE do you use? (Poll) (2022 Edition) It’s been a while since we first asked this, I...
208 31307 143
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New

We're in Beta

About us Mission Statement