The IDL
A deployed Solana program is a binary on the chain. There is no way to look at the binary and discover what functions it exposes, what arguments they take, or what accounts they need. The IDL is the file that fills that gap. It's a JSON description of your program's public interface, generated by the Anchor compiler from your Rust source, and it's how every client that talks to your program knows how to call it. If you've worked with OpenAPI, gRPC, or TypeScript type declarations, you already have the right picture.
Why the IDL exists
On most platforms you've worked with, the thing you deploy and the thing you describe are the same thing. A Python or Node server exposes its routes through code that's still readable when the service is running. The TypeScript compiler bundles type information alongside JavaScript output. Reflection and introspection are taken for granted.
Solana is different. What gets deployed is compiled BPF bytecode. The validators run it, the runtime calls into it, but the bytecode itself carries no information about what's inside. There's no manifest of public functions. There's no schema of expected argument types. There's no list of which accounts each function needs. From the outside, your program is a black box with one entry point that takes a buffer of bytes.
This is fine for the runtime, which only needs to execute the code. It is not fine for anyone trying to call the program from a wallet, a frontend, a backend service, or another program. They need to know which function the bytes are supposed to invoke and what each argument means. The IDL is the file that tells them.
When you run anchor build, the compiler does two jobs. The first is to compile the Rust source into BPF bytecode, producing the .so file that gets deployed to the chain. The second is to walk through your source and emit a JSON description of every public interface it found, producing the IDL file. The two artifacts go to different places. The binary is uploaded to the chain. The IDL stays in your repo and gets bundled with whatever client app talks to the program.
The closest analog from other ecosystems is OpenAPI. Your Solana program is like a backend service. The IDL is like the OpenAPI spec. The TypeScript client is like the SDK generated from that spec. Same pattern. Same reason it exists. The difference is that on Solana, the spec is mandatory for any nontrivial client work, since there is no other way to introspect the program.
What's inside
The IDL is a single JSON file with a stable shape. You almost never read it by hand, but knowing its structure helps when something goes wrong.
The top of the file carries metadata: the program's address, its name, its version. Below that, the file is organized into a handful of arrays that each describe one kind of thing your program exposes.
The instructions array is the most important. There is one entry for each handler in your #[program] module. Each entry names the instruction, lists the accounts it expects in order, and describes the arguments it takes with their types. This is what tells a client "to call deposit, you need to pass these accounts and these argument values, in this order."
The accounts array lists every account type you defined with #[account]. The types array describes the field layout of each one. These two together let a client deserialize the raw bytes of an on-chain account back into a typed object. When your TypeScript code reads vault.total and gets a number back, it's because the IDL described the layout of Vault precisely enough for the client to decode the bytes.
The errors array carries the custom errors you defined with #[error_code]. When a transaction fails, the runtime returns an error number. The client uses the IDL to map that number back to a meaningful name and message.
You will never write any of this yourself. The whole file is regenerated every time you run anchor build. If you change a handler's arguments in Rust, the IDL changes to match. If you rename an account struct, the IDL renames it too. The Rust source is the source of truth. The IDL is a downstream artifact, like a build output.
How clients consume the IDL
The reason all of this matters in practice is the client side. The Anchor TypeScript SDK, the most common way to talk to Solana programs from a web app, is designed around the IDL.
You import the JSON file in your TypeScript code. You hand it to a Program constructor along with a provider that holds the wallet and connection. The result is a fully typed client object whose methods correspond one-to-one with the instructions in your program. Calling program.methods.deposit(100).accounts({ vault, depositor }).rpc() is the equivalent of building a raw transaction, packing the right bytes, listing the right accounts, signing it, and broadcasting it. The IDL is what makes the typed call possible.
Behind the scenes, the SDK looks up deposit in the IDL's instructions array, finds its argument schema and account list, serializes your arguments according to that schema, builds an instruction with the right shape, packages it in a transaction, signs it with the provider's wallet, and sends it to the RPC endpoint. All the work the lectures on instructions and transactions described, the SDK is doing for you, using the IDL as its map.
The same pattern applies in other languages. There is a Python SDK, a Go SDK, a Swift SDK, all of which consume the IDL in the same way. The IDL is the universal description that lets each of those clients work with your program without coordinating directly with you.
Treating the IDL as a deliverable
When you publish a Solana program, you publish two things, even if you only think about one. The deployed binary is the program. The IDL is the program's public API surface. Anyone integrating with your program will read your IDL, generate a client from it, and write code against the resulting types. Breaking changes to the IDL break their code.
Versioning the IDL the way you'd version a library helps. Many production programs keep a history of IDL versions in their repo so integrators can pin to a known shape. When you rename a field or add a new instruction, the IDL changes in ways that show up as a diff. Reviewing that diff during code review is one of the cheapest ways to catch accidental API breaks before they ship.
The IDL is also what powers explorers like Solscan and SolanaFM. When you click on a transaction and see a readable name like "deposit" with decoded arguments, the explorer is reading the program's IDL to do that decoding. Programs without a published IDL show up as opaque byte arrays. Publishing your IDL is the cheapest way to make your program legible to the rest of the ecosystem.