« Back to Index

API vs ABI

View original Gist on GitHub

Tags: #API #ABI

API vs ABI.md

Summary

An API is a compile time interface (e.g. using non-project functionality provided by an external library). Where as an ABI is a runtime interface (e.g. an interface used by a program during execution, where the program is internally communicating with machine code).

Explanation

An ABI is just an API for two machines, expressed more strictly and at a lower level than an API would be — thus Application Binary Interface vs. Application Programing Interface.

It’s not true that every communication layer between (for example) an SDK and something else is necessarily going through an ABI.

The distinction is that ABI is only relevant to binary interactions (e.g. an SDK communicating with a separate binary program; that linking process occurs at runtime, through an ABI) rather than just interactions in a general sense (e.g. an SDK communicating with an external REST API).

Contrived Example

An ABI incompatible change is if I change a method A#m() from taking a String as an argument to String... argument.

This is not ABI compatible because you have to recompile any code that is calling that function, but it is API compatible as you can resolve the issue by recompiling the caller without any code changes in the caller.

Find more examples here: https://stackoverflow.com/questions/3784389/difference-between-api-and-abi

Real Life Example

We had a rust crate foo that depended on another crate bar (i.e. foo would interact with bar) and so both had to be available on the user’s system.

A user would write their own rust program that consumed foo. As part of setting up their environment we’d ensure that bar got installed (as it was a required for foo to work), but the user didn’t need to know about bar (and for the most part they didn’t know about it). They only knew that foo provided an API they wanted to use, and so the fact that foo called bar internally didn’t matter to the user (they just had to make sure bar existed on their system).

Remember that the user’s program, and the foo and bar dependencies are all written in rust code, but ultimately for them to be used together the user has to compile their program (and the dependencies) to machine code.

So imagine we have foo at version 1, and bar at version 1. Both work fine together.

Now a change to bar is released as version 2, which is incompatible with foo version 1.

When the user installs foo and bar, they get foo version 1 and bar version 2. This means their rust program breaks because the interactions between the foo crate and bar are broken.

Subsequently the user would need to install an updated version of foo which was compatible with bar. This meant when releasing changes to bar we had to ensure that our environment setup scripts would ensure that the correct version of foo would be installed alongside the appropriate version of bar. The user still knew nothing about this, as they only cared about foo.