Accounts
Everything on Solana is an account. Wallets are accounts. Token balances are accounts. Deployed programs are accounts. The same handful of fields make up every one of them. Once this clicks, half of Solana stops being confusing.
The one-sentence version
A useful way to picture the Solana state is as a giant file system. Each file has a name, some contents, an owner that decides who can edit it, and a small storage fee paid up front. On Solana, those files are called accounts. The chain holds millions of them, the runtime knows how to read and write them, and every program you'll ever write does its work by handing accounts around.
What's actually inside an account
Each account has five things and only five things.
An address, which is a public key written as a Base58 string. The address is how anyone refers to the account. You look it up by address the same way you'd open a file by its path.
A lamports balance. Lamports are the smallest unit of SOL, one billionth of a SOL. Every account holds some lamports. For a wallet, this is the user's balance. For other kinds of accounts, the lamports cover the storage cost.
A data field. A flat blob of bytes, sized at creation, anywhere from zero bytes up to ten megabytes. The meaning of those bytes is entirely up to the owner program. The runtime treats it as opaque.
An owner, which is the address of one specific program. That program is the only one allowed to modify the data field. The runtime checks this on every transaction before any code touches the account.
An executable flag, true or false. If true, the data is compiled program code that can be invoked. If false, the data is just bytes that someone reads and writes.
That's the entire structure. There is nothing else.
Three accounts you'll meet on day one
The strength of this design shows up when you see how many different things fit into the same five fields. Look at three concrete examples.
Alice's wallet. When someone "has a Solana address," what they actually have is an account owned by the System Program, the built-in program that handles SOL transfers. The data field is empty. The lamports field is Alice's SOL balance. The executable flag is false. That's a wallet.
Alice's USDC balance. Token balances do not live inside the user's wallet. They live in their own separate accounts, owned by the Token Program. The data field contains 165 bytes laying out the mint that issued the token, the user who owns the balance, and the current amount. The lamports field holds just enough to cover the storage cost. The executable flag is false. Alice does not own this account in the runtime sense, the Token Program does. Alice is recorded inside the data as the authorized user, and the Token Program enforces that nobody else can move the balance.
A deployed program. When you build and deploy a program to Solana, the deployment produces an account at a fresh address. The data field contains the compiled bytecode, often a few hundred kilobytes of it. The owner is a system-level program called the BPF Loader, which is the only thing allowed to modify program code. The executable flag is true. That flag is how the runtime knows to treat this account's data as code rather than as plain bytes.
Same five fields, three completely different roles.
The owner field is the security model
The single most important field on any account is the owner. Reads on Solana are public, anyone can fetch an account by address and see its contents. Writes go through one path. The runtime checks, before any program code runs, that any account marked writable in the transaction is owned by the program trying to modify it. If the check fails, the transaction is rejected before the program even starts.
This is what keeps your token balance safe. The Token Program owns your USDC balance account. When you sign a transaction asking the Token Program to move 50 USDC from your balance to Bob's, the Token Program runs, checks that you authorized the move, and updates both balance accounts. If a different program tries to write to your balance account directly, the runtime stops it before the program runs.
When you write a program of your own, the same rules apply to you. The accounts your program creates are owned by your program. No other program can touch their data. You don't write defensive code to check who's calling you. The runtime has already filtered out anyone who shouldn't be there.
Programs are accounts too
The thing that takes the longest to internalize is that programs themselves are just accounts. When you deploy a program, you produce an account. The account's address becomes the program's identifier, the one you reference whenever you want to call into it. The data field holds the compiled bytecode. The executable flag is true.
Calling a program is the act of submitting a transaction that points at the program's address and provides the accounts the program needs to work with. The runtime sees the executable flag, loads the bytecode from the data field, and runs it with the listed accounts as inputs.
This is why there is no separate "contract address space" the way some other chains have. There is one address space, with one kind of account. Some of them happen to hold code instead of data.
What you'll be doing with accounts
When you write your first program, you will be creating accounts, reading from them, and writing to them. You will set up each account at a deterministic address by deriving it from a seed, decide how many bytes its data field should hold, and pay the lamports needed to keep it on chain. The runtime will check the ownership and the access lists on every call. Most of the bugs new Solana developers hit come from getting one of these wrong, which is why every piece of the model is worth understanding clearly before you write code that depends on it.