Building the tools for a chain nobody was building on
A chain had smart contracts and nothing to build them with. This is what a developer needs and the order the constraints let me build it in.
Motivation
A blockchain keeps a shared record that many machines agree on. Every change is appended and nothing is ever removed, because a record that can be quietly edited is not a record anyone would trust.
That property is exactly wrong for the person writing the programs. The development loop is:
- run a broken thing
- watch it fail
- fix one line
- run it again from a clean start
Forty times before lunch, on a machine whose entire purpose is never to forget. So the platform needs a private copy of the whole network that runs on your laptop and can be wiped, and until that copy exists it is only usable by people patient enough to work without it.
Terra's chain was about to gain the ability to run programs at all, and nothing around that ability existed yet. Documentation and tooling were the thing to own.
The extension seam
A contract system that is new is worth building on precisely because it has not settled, which is how I put it that June: the platform gives a unique opportunity to play with the specifics of how a contract executes, because it gives a simple basic model to extend.
The contract system compiles programs to WebAssembly, a compact instruction format originally built so code could run inside a browser, and hands each program a small, fixed set of functions for talking to the outside world. A short list of functions is a seam. Anything that narrow can be reimplemented, wrapped, logged, or replaced, and the program on the other side cannot tell the difference. Every tool below is built on that seam.
The documentation blocker
A seam is worth nothing to anybody who cannot find it, which puts documentation first among the products rather than last. The documentation stalled for most of a month against a blocker worth stating exactly:
Wrote the smart contract documentation with TerraCLI -- I realized that the thing holding me back from finishing writing was being blocked by not having a web interface for Terra smart contracts.. LESSON IN THERE: Figure out what's blocking you and resolve whether or not that will be there in time.
The web interface did not exist and had no date. The documentation did not need it. Only one version of the documentation needed it, the one with screenshots, where the reader clicks a button. Written against the command line instead, every instruction is a line the reader can copy, and the whole thing finishes that week.
The general form has two halves that people collapse into one:
- Finding what is blocking you. The easy half.
- Working out whether the blocker will arrive in time. The half that changes what you do.
A dependency with no date is not a dependency. It is a decision you have not made yet, sitting in the schedule disguised as something outside your control.
Working backwards from the end state
The general version is to start working backwards, focus on what you have to do, and then iterate on the prerequisites. The worked example is the same documentation: the process was to research as much as possible about smart contracts, and the right process was to start with the document and an outline.
The two behave differently under pressure:
- Research has no completion condition, so it expands to fill whatever time you give it and always feels productive, which is what makes it such a comfortable place to hide.
- An outline is inspectable, and every gap in it is a specific question rather than a general inadequacy, so it converts an unbounded activity into a list.
The town hall outline
The end state for the platform was not a document. A town hall outline puts the diagnosis and the products on the same page, which is what makes it the useful artifact of that year.
The diagnosis was that the chain was boring, in the sense that you could only swap, send, and stake. The only way to build with it was to interact with the blockchain, and you could not build applications on top. Talking to a system across a wire and building inside it are different activities, and a platform that only supports the first has users but no ecosystem, because nothing anybody makes can live there.
The two things named to fix it split the developer's problem in half:
- localterra, running a private Terra testnet with FCD, Station and Finder hooked up, which is the data service, the wallet and the block explorer, so you could easily test complicated contracts with resettable state. The one-line version later in the same outline is a one-click private testnet and ecosystem.
- create-terra-dapp, the quickest way to launch. localterra covers running and testing; this covers starting.
Resettable state is the load-bearing phrase, because it is the append-only property from the top of this post, deliberately broken, in a copy of the network where breaking it is safe.
LocalTerra shipped with documentation and wired up to Station, and the wiring is what decided whether it was usable, since a private network the wallet cannot talk to is one you still cannot build against. The same principle, stated bluntly in capitals at the time, is that people will buy anything with a good interface. Every decision in this post is a choice about what the developer touches.
Where the instrumentation belongs
A resettable network gives the developer somewhere to run a contract and says nothing about what they can see while it runs. The next version of LocalTerra listens to debug messages, and where the listening happens decides what the tool can see.
- Logging inside the contract. The obvious move, and it has two costs. It changes the thing you are trying to observe, and it only works for programs you are allowed to edit, which rules out every contract somebody else deployed, which is most of the ones you actually want to understand.
- Listening in the node. Inverts both. The local network reports what any contract did, whoever wrote it, because the contract is not the thing being asked.
The relocation generalises: put the instrumentation at the boundary the program runs against, not inside the program.
The project layout
A network to run against and a way to see inside it still leave the developer arranging their own project by hand. The thing being replaced was a set of TypeScript scripts that automated contract development tasks and was, in the draft's own words, probably insufficient as a tool.
A bag of task scripts automates the steps you already know to run and carries no opinion about how a project is laid out, so each of these stays somebody's manual habit:
- architecture
- development
- testing
- documentation
- deployment
The replacement is a Rust toolchain addressing all five, starting from one command, cargo houston new contract <dir-name>, whose output layout the draft writes out in full:
- contracts/
- contract1/ # each contract is a crate
- src/
- lib.rs
- contract.rs # contract logic
- tests.rs # unit tests
- Cargo.toml
- integration-tests/
- test_XXX.rs
- docs/ # generated by `cargo houston docs build`
- scripts/ # invoke Houston functions programmatically
- deploy.rs
- migrate.rs
- Cargo.toml # workspace config and the Houston manifestThree decisions are visible in that tree:
- Unit tests sit next to the code they test; integration tests get their own root, because they run against a deployed contract rather than a function.
docs/is generated rather than written, so it is an output and nobody edits it by hand.- Deployment lives in
scripts/deploy.rs, which makes shipping a program in the project rather than a paragraph in a README that drifts out of date the first time somebody changes a flag.
One line in that draft is a question rather than a decision. Should users be able to enter information in a prompt? Prompting is discoverable and stops a newcomer having to know the flags; flags are scriptable and a prompt blocks anything running unattended. It has no theoretically correct answer, which is the ordinary case for the decisions that determine what the person at the keyboard actually types.