Isaac Emmanuel

2026.10.27

Write code like your mistakes are permanent

What smart contracts taught me about engineering everything else

Originally published on Medium.

Early in my career I spent time writing Ethereum smart contracts, including token contracts for in-game purchases and reward pools for eSports tournaments. I don’t write much Solidity these days. But that period changed how I write every other kind of code, and I think every engineer would benefit from spending some time building something where mistakes are permanent.

No hotfix, no rollback

Most of us work with a safety net we barely notice. If something breaks in production, we ship a fix. If data is corrupted, we restore a backup. If a deploy goes wrong, we roll back.

Smart contracts remove that net. Once a contract is deployed, its code is, for practical purposes, fixed. There are upgrade patterns, but they come with their own complexity and risks. And if a bug lets someone take funds, there’s usually no customer support line, no chargeback and no “we’ll patch it on Monday.” The money is gone.

On top of that, the code is public. The reward pools I worked on were sitting on a public blockchain, visible to anyone, holding real value and waiting for someone to find a flaw in the access control.

That pressure is clarifying.

Lesson 1: assume every function will be called by an attacker

In a typical web app, we tend to think about who’s supposed to call a function: the frontend, an admin, an internal service.

In a smart contract, anyone can call any public function, with any arguments, at any time, in any order. The question stops being “who is supposed to call this?” and becomes “what happens when the worst possible person calls this, in the worst possible way?”

I still review API endpoints that way. If an endpoint exists, I assume someone will find it and call it with arguments I didn’t expect. Authorisation has to be enforced in the function itself, not implied by the UI that normally calls it.

Lesson 2: make permissions explicit, never implied

Many contract exploits come from permissions that were assumed rather than enforced: a function that was meant to be admin-only but lacked a check, or an initialisation step anyone could call.

The habit I picked up was to write permissions down explicitly for every function, using clear, well-understood access control patterns, and to treat any function without an explicit permission check as a bug until proven otherwise.

That habit transfers directly to backend systems. Insecure direct object references, where changing an ID in a request returns someone else’s data, are the web equivalent. They happen when ownership is assumed rather than checked.

Lesson 3: keep logic small enough to reason about

Complex contracts are dangerous contracts. Every additional branch, external call and piece of state is another way for things to interact unexpectedly.

The best contracts I saw were boring. They did one thing, clearly, with as little cleverness as possible. Anything complex lived outside the contract, where it could be changed.

In backend work, I apply the same rule to anything security-sensitive: authentication, authorisation, money movement, data deletion. Keep the critical path small and obvious. Put the clever stuff somewhere it can fail safely.

Lesson 4: a second pair of eyes before anything goes live

When mistakes are permanent, review stops being a formality. Contracts that hold value get reviewed carefully, tested heavily and often audited externally before deployment.

Most backend code doesn’t need an external audit. But the principle scales down. For the parts of a system where mistakes are hardest to undo, such as migrations that delete data, payment logic, permission changes and anything touching security, slow down and get another set of eyes on it.

Lesson 5: think in terms of irreversible actions

The deepest lesson is a way of sorting work. Some actions are easy to undo. Some are expensive to undo. Some can’t be undone at all.

Smart contracts made me sensitive to that last category, and I now look for it everywhere:

  • Sending an email or notification can’t be unsent
  • Charging a card is hard to fully reverse
  • Deleting data without a backup is final
  • Publishing something public can’t be truly retracted
  • An AI agent taking an action on a user’s behalf may not be reversible

That last one is especially relevant today. As we give AI agents tools that act in the real world, the same thinking applies. Irreversible actions deserve extra checks, narrower permissions and often a human confirmation.

Why it’s worth the experience

You don’t have to become a blockchain developer. But spend some time, even on a side project, building something where you can’t fix mistakes after the fact. It changes your instincts. You start asking better questions, writing smaller functions and checking permissions you used to assume.

I came back from that work a different engineer. I still review every endpoint like it’s guarding a reward pool.