How I Built a Counter Program in Anchor and Learned to Trust My Tests
I spent a week building a counter program in Anchor — the Rust framework for writing Solana programs. By the end I had two instructions, one authorization constraint, and a test suite I could actually trust. Here is what I built, how I tested it, and the moment I proved the tests were real. Start Here: The Accounts Struct If you come from Web2, this is the part that looks the strangest: #[derive(Accounts)] pub struct Initialize { #[account( init, payer = authority, space = 8 + Counter::INIT_SPACE, )] pub counter : Account , #[account(mut)] pub authority : Signer , pub system_program : Program , } In a Web2 backend, your handler receives a request object and talks to a database. On Solana, there is no database; there are accounts. Every account your instruction needs to read or write must be declared upfront, before the handler runs. Anchor validates them before your code ever executes. Here is what each field does: counter — the account being created. The init constraint tells Anchor to make a CPI to the System Program, allocate 8 + Counter::INIT_SPACE bytes, and fund it from authority . The 8 is for the discriminator Anchor stamps on every account so the program can later verify "this is mine." authority — the wallet signing and paying for the transaction. mut because its SOL balance is decreasing to fund the new account. system_program — required any time you create accounts. Anchor checks that the address matches the real System Program. The accounts struct is the schema. The handler is the logic. The Handlers pub fn initialize ( ctx : Context ) -> Result { let counter = & mut ctx .accounts.counter ; counter .authority = ctx .accounts.authority .key (); counter .count = 0 ; Ok (()) } ctx.accounts gives you typed access to every account declared in the struct. The handler is short because Anchor already did the hard work: allocating the account, checking the signer, paying the rent. Your code just sets the initial values. pub fn increment ( ctx : Context ) -> Resu