Here’s something most new Web3 developers have trouble understanding: when you ship code, it can’t be patched and updated later. This is a big departure from the iterative development method that we all know and love — where we break development into smaller parts and build it up over many iterations. By adding features one-by-one, we can improve app functionality over time — all while growing a user base. In Web3.0, this just cannot be done.

Iterative development lets us build in stages 🔗

We’ll talk about Web3’s waterfall method in a moment. But first, a primer on iterative development is in order. There are a few advantages to building applications this way:

  1. Detect Errors Early

    By building software in stages, we can build, test, and ship code piece-by-piece. This lets us find errors in the early stages and fix them as we go, avoiding downstream consequences, such as a few small errors impacting the full application.

  2. Faster User Feedback

    Since we ship the app in iterations, iterative development lets us get feedback quickly and cyclically. Not only does this let us make necessary bug fixes, but it also helps us shape the future roadmap based on real-time feedback from users.

  3. Less Time Planning

    Iterative development saves us time on writing elaborate documentation, so we can spend more time building and testing and less time writing and theorizing.

But this changes in Web3 🔗

Of course, blockchain changes everything — including the possibility for iterative development. Most new software developers are shocked to learn this, but it makes sense once you get a handle on how smart contracts work.

Let’s use the Ethereum blockchain as an example.

Ethereum Primer

Ethereum is a globally accessible state machine that can only be updated via consensus. As an immutable state machine, we can write state (i.e., data) to it, but we can’t update the state. That means:

  • Everything we write to the blockchain is permanent.
  • Ethereum smart contracts are immutable; they can’t be altered once created.

There’s a good reason for this: smart contracts let us create an unbreakable contract among participants. But it also means we’re stuck with the contracts forever. So, bug fixes and improvements just aren’t possible.

If you’re coming from Web 2.0, you might be wondering why anyone would develop something that can’t be updated. It might even be scary to think about. This is why many crypto projects take months and sometimes years before deploying their app onto the blockchain — any bug or vulnerability in the smart contract could cost millions of dollars.

There are countless examples of such hacks, which you can read about here, here, here, here… and many more which you will find with a simple Google search.

What if we absolutely need to make an update? 🔗

That brings us to our next question: what if we absolutely need to make an update?

Let’s say a developer made a million-dollar mistake; someone hacked their smart contracts and drained a lot of money. Of course, something must be done to stop other users from using those smart contracts — but this is a very tedious process.

Here’s what typically happens in this scenario:

Step 1 🔗

‍The second you discover the vulnerability, you must immediately tell your users and pause your smart contracts (of course, this assumes you built in logic that lets you do this). By pausing the smart contacts, you do two things: first, make it explicit that users shouldn’t use them, and second, prevent attackers from taking advantage of users unaware of the vulnerability.

Step 2‍ 🔗

Next, you need to recover your data so you can migrate to the new smart contracts. Remember that in Web3, your smart contracts store both the logic and data for your application. When you deploy a new smart contract with updated logic (that fixes your vulnerability), you need to recover that data; otherwise, everything would be erased. Examples of data you would recover are:

  • account balances of your users
  • public variables stored in the smart contract
  • private variables stored in the smart contracts‍

‍Step 3‍ 🔗

Next, you write and initiate your new contracts with the recovered data. If you have very little data, this process can be done in one transaction. However, if you have a lot of data, then it must be broken up into many smaller transactions. Recall that writing data to the Ethereum blockchain costs money, which is paid in “Gas.” Ethereum has a “GasLimit” for each transaction; if the gas cost of a transaction exceeds this limit, miners won’t include it in a block.

Step 4‍ 🔗

Deploying a new contract means that the contract address has changed. So, you now need to update all contracts that interacted with the old contract with the new address. Moreover, you need to let your users know that this is the new contract they should use. This requires a lot of social coordination, and it’s not always straightforward, but it’s very possible.

The benefit of this tedious approach is that we aren’t sacrificing immutability, which is a fundamental property of smart contracts. However, there’s no denying the tedium of migrating data and convincing users to move over. That’s why anyone deploying smart contracts onto the blockchain must create a migration plan before deployment — that way, they have a backup plan if they discover a vulnerability down the line.

Building upgradeable smart contracts: A backdoor approach 🔗

While the manual approach we just discussed works, it still isn’t ideal. There are a lot of examples in which millions of dollars worth of Ether were stolen or hacked which could have been saved if we could update the smart contracts. Having the ability to upgrade contracts is useful not only for iterative development, but also for fixing serious bugs that could wipe away people’s savings.

This is why the Ethereum community (specifically, OpenZeppelin) came up with the concept of an “upgradeable smart contract.”