Building production Web3 apps: the parts that actually break
The smart contract is the easy part. Here is what really decides whether an onchain product ships: wallet onboarding, the off-chain to on-chain seam, and security.

Short answer: a production Web3 app rarely breaks inside the smart contract. The contract is the small, testable part. It breaks in the seams around it: getting normal users into a wallet, keeping your app and the chain in agreement when the chain is slow and final, and holding the security line everywhere you sign a transaction or derive a key. Get those three right and the product ships. Get them wrong and no amount of clever Solidity saves you.
I build onchain products end to end, from the off-chain engine down to the settlement contract. This is the map I wish I had starting out: the three problems that recur on every project, in the order they bite, each linked to a full deep-dive where I show how I solved it.
The contract is the small, testable part. Everything hard lives in the seams around it.
What actually makes a Web3 app hard to build?
The chain is the easy part. A Web3 app is hard because it straddles a boundary: a fast, mutable, off-chain world (your app, your users, your database) talking to a slow, final, public on-chain world (the contract, the mempool, the block). Almost every real bug lives on that boundary, not inside the contract.
Three problems show up on every onchain product I have built, and they are what the rest of this guide is about:
- Wallet onboarding. Most people will never write down a seed phrase. If your first screen demands one, you have already lost them.
- The off-chain to on-chain seam. Your app decides something in milliseconds. The chain confirms it in seconds, sometimes reorders it, and occasionally reverts it. Keeping both sides honest is the actual job.
- Security. Every place you sign a transaction, derive a key, or trust an input is a place the whole product can be drained.
How do you onboard users who will never hold a seed phrase?
Give them a real wallet without ever showing them a seed phrase. The cleanest approach I have shipped derives a genuine, recoverable Ethereum account from something the user already has, like their login, using a key derivation function (KDF). They sign in the way they always do, a real key pair exists underneath, and you never store a private key you could lose or leak.
The parts you must get exactly right are which inputs feed the derivation, how you salt and stretch them, and what happens when the user changes their password. Those details are the whole difference between smooth onboarding and an account that silently becomes unrecoverable.
I wrote the full build, KDF choices and recovery edge cases included, in A wallet from a password.
How do you keep onchain actions reliable when the chain is slow and final?
Treat every onchain action as a request that might be retried, reordered, or confirmed late, and design for that from line one. The trap is treating a submitted transaction as a done one. It is not done until it is confirmed, and even then a reorg can move it.
| A transaction that looks done | A transaction that is done |
|---|---|
| Broadcast to the mempool | Confirmed in a block |
| Has a hash | Survived enough confirmations |
| Your UI showed a spinner-then-check | Reconciled against on-chain state |
Three tools carry the weight: idempotency (a retry never double-submits), explicit ordering (actions settle in the sequence you intended), and reconciliation (your off-chain view is periodically forced back into agreement with the chain). An idempotency key on every intent is the single highest-leverage guard:
// One stable key per intent. A retry with the same key is a no-op,
// so a timeout or a resend can never settle the same action twice.
const key = idempotencyKey(userId, intentId);
if (await alreadySubmitted(key)) return existingReceipt(key);I led an onchain trading platform across exactly this seam, where a fast off-chain signal engine had to hand off to a slow settlement layer without ever losing or duplicating a trade. Full write-up: Turning algorithmic signals into reliable onchain trades.
How do you keep a Web3 product secure?
Assume every input is hostile and every signing path is a target. The blast radius in Web3 is larger than in a normal web app: a mistake does not corrupt a row, it moves money no one can claw back. So the security work concentrates at the boundaries, not spread thin across the app.
In Web3, a bug does not corrupt a row. It moves money nobody can claw back.
The habits that matter most in practice:
- Never hold a raw private key you do not have to. Derive deterministically so there is nothing to leak.
- Validate and bound every value before it reaches a contract call.
- Make destructive actions idempotent, so a replay cannot repeat them.
Custody and onboarding are where this shows up first, which is why the wallet from a password build spends most of its length on the parts you have to get right rather than the happy path. The login side of the same boundary has its own traps, which hardening wallet login walks through check by check.
Does the tech stack matter more than the idea?
No. The stack decides how well you build the thing; the idea decides whether the thing is worth building. I have watched technically flawless products go nowhere on a thin idea, and rough MVPs win on a right one. Web3 does not change this. A chain is a tool in service of a product, never the product itself.
I make that case, from years of building with founders, in The idea is the asset.
Key takeaways
- The smart contract is the small, testable part. Production Web3 apps break in the seams around it.
- Onboard users into a real wallet without a seed phrase, or you lose them on the first screen.
- Make every onchain action idempotent, ordered, and reconciled. A submitted transaction is not a settled one.
- Concentrate security at the signing and key-derivation boundaries, because the blast radius is money.
- The stack serves the idea. Choose it to ship the idea well, not the other way around.
Questions you might have next
- Do I need my own smart contracts to build a Web3 app? Often no. Many products compose existing contracts and put their real engineering into onboarding, the off-chain seam, and security. Write contracts only when you genuinely need custom onchain logic.
- Where do most Web3 bugs actually come from? The off-chain to on-chain boundary: retries, reorgs, and ordering. Very few come from a tested contract.
- What should I build first? The onboarding path. If a real user cannot get a wallet and take one action, nothing else you build matters yet.
Everything discussed here comes from shipped products. The full list, from crypto trading platforms to streaming apps, is on the projects page.