DEV Community

Cover image for Program Derived Addresses (PDAs): The Solana Feature Every Web2 Developer Should Understand
Samuel Emmanuel
Samuel Emmanuel

Posted on

Program Derived Addresses (PDAs): The Solana Feature Every Web2 Developer Should Understand

If you're coming from a Web2 background, one of the first things that feels strange about Solana is that applications don't always create accounts using private keys.
Instead, they often use something called a "Program Derived Address (PDA)".

At first, that sounded complicated to me.
After building several Anchor programs during my #100DaysOfSolana challenge, I realized PDAs aren't magic at all they solve a problem that every backend developer already understands.

Let's look at them through a Web2 lens.

How We Usually Think in Web2

Imagine you're building a SaaS application.

You have a table like this:

User ID Counter
101 5
205 12
318 0

Whenever a request arrives, you already know the user's ID.

You don't generate a random database key every time. Instead, you derive which row belongs to the user.
Your application always knows exactly where that user's data lives.

The Problem on Solana

My first Anchor counter program didn't work like that.

To create a counter, I generated a brand new keypair for the account. That worked... but it meant the client had to remember that keypair forever.
Lose it, and you lose the ability to easily locate your counter account.
That approach is fine for tutorials but not for real applications.

Enter Program Derived Addresses

A PDA is an account whose address is deterministically derived from:

  • one or more seeds
  • your program ID
  • a bump value For example:
seeds = [
    b"counter",
    user.key().as_ref(),
]
Enter fullscreen mode Exit fullscreen mode

Every time the same user calls the program, Anchor derives the exact same address.

No database lookup.
No random account generation.
No extra storage needed to remember where the account lives.

The Web2 Analogy

Imagine this function:

getCounter(userId)
Enter fullscreen mode Exit fullscreen mode

Internally your backend might do:

SELECT *
FROM counters
WHERE user_id = ?
Enter fullscreen mode Exit fullscreen mode

The user ID uniquely identifies the row.

PDAs work almost the same way.
Instead of looking up a database row, Solana derives an account address from predictable inputs.

It's closer to computing a deterministic primary key than generating a UUID.

Why This Is Powerful

Suppose Alice opens your app.

Anchor derives:

("counter", Alice)
Enter fullscreen mode Exit fullscreen mode

Bob opens the app.

Anchor derives:

("counter", Bob)
Enter fullscreen mode Exit fullscreen mode

Both users automatically get different accounts.

No collisions.
No bookkeeping.

Your program always knows where each user's state belongs.

Where Anchor Helps

One thing I really appreciate about Anchor is how much boilerplate disappears.
Instead of manually checking addresses, ownership, and account creation, you simply declare constraints:

#[account(
    init,
    payer = user,
    seeds = [b"counter", user.key().as_ref()],
    bump
)]
Enter fullscreen mode Exit fullscreen mode

Anchor then:

  • derives the PDA
  • computes the canonical bump
  • creates the account
  • allocates storage
  • makes the program sign for it

All before your instruction handler even runs.

Security Comes from Constraints

Another thing that clicked for me was that authorization often lives in account constraints rather than business logic.
For example:

#[account(
    has_one = user
)]
Enter fullscreen mode Exit fullscreen mode

or

#[account(
    constraint = !config.paused
)]
Enter fullscreen mode Exit fullscreen mode

Instead of writing lots of validation code inside the handler, Anchor verifies everything first.
If validation fails, the transaction never reaches your business logic.
As a backend developer, it reminded me of middleware that rejects unauthorized requests before your controller executes.

The Bigger Picture

One thing the Solana journey has taught me is that blockchain development isn't about throwing away Web2 knowledge.

It's about applying familiar backend concepts in a different environment.
Database rows become accounts.
Primary keys become deterministic addresses.
Middleware becomes account constraints.

Tables become on-chain state.
Once I started viewing Solana through concepts I already understood, everything became much easier to reason about.

What I'm Building

This article is based on one of the programs I built during my #100DaysOfSolana challenge.

The project evolved from:

  • a simple counter
  • to stateful accounts
  • to authorization with has_one
  • to negative testing with LiteSVM
  • to per-user PDAs
  • to configuration PDAs
  • to reclaiming rent by closing PDA accounts

Each step felt surprisingly familiar once I translated it into Web2 concepts.

Final Thoughts

If you're a Web2 developer curious about Solana, don't start by trying to memorize blockchain terminology.
Instead, ask:

"What problem is this solving that I already solve every day?"
More often than not, there's a familiar backend concept hiding underneath.

That's what finally made PDAs click for me.

Thanks for reading!

I'm documenting my learning journey through #100DaysOfSolana, sharing what I build and translating blockchain concepts into language that backend developers already understand.

If you're learning Solana too, I'd love to connect and learn together.

Top comments (0)