Writing the same library twice
I wrote the same library twice in two languages. The second time I could say what makes progress visible on a surface too large to see.
Motivation
An SDK wraps somebody else's system so you can use it from your own language. Here the system is a blockchain, which accepts a fixed set of messages: send these coins, stake this amount, submit this vote. The SDK exposes each as a function, plus the machinery for building a message, signing it, sending it, and reading what came back.
What makes it hard is the surface:
- hundreds of small tedious pieces
- with no order among them
- and no point at which any one is visibly done
So on any morning there is no answer to which piece is next. Written three times here, in Python, TypeScript and Java, the ordering question never produced one. What the surface lacks is a finish signal, not an order.
Starting in the code
The Python library started from serializer and deserializer classes, the boring code that turns a message into bytes and back, rather than from a design document.
- The rule, as I put it that March. Get directly into the code as soon as possible without worrying whether it is best practice, because it can be updated later.
- The generalisation. The minimum viable product is often not what you think it is, and if you are not doing the thing you named, it may not be the right one to strive for.
- The not-doing is itself the measurement. A first step you keep not taking is usually not the first step. The real one is whichever small boring piece you would actually start this morning.
Finding the API shape by using it
Serializers tell you what to type on the first morning and nothing about what the library looks like from outside. Two sources for the interface shape:
- The system underneath. The default, because the structure already exists and copying it takes no thought. Produces an accurate wrapper that is unpleasant to hold: the system is organised around how it stores things, the programmer around what they are trying to do.
- A real application written against it. The April task for the JavaScript one. Awkward parts show up as awkwardness in your own code, before anyone else has to live with them.
Depth, breadth, and tests
Knowing the shape does not shrink the surface: hundreds of pieces, no order among them, no point at which any one is visibly done. Three ways to move across it:
- Depth. Complete one class fully. Take the type that represents a coin, implement every method it should have, finish it. Bottom up.
- Breadth. Cover the basics of every core class before finishing any of them. Top down.
- Tests. Neither, and the one that matters, because they mark that something has been completed.
Depth against breadth is the ordinary tradeoff and either answer works. The third item answers a different question, which is what counts as done: on a surface this size you can work hard for four days and be unable to say what is finished, because "finished" has no edge anywhere in the work.
A passing test is the smallest object in that landscape with a definite boundary:
- it did not exist this morning and exists now
- it goes on telling you tomorrow if you break it
- it is load-bearing for morale before it is load-bearing for correctness
The shape generalises to any surface too large to hold in your head: manufacture units that can be visibly done. A workout log does the same job for the same reason, which is the note I made two days after this one: tracking the workouts is what made me start doing them, because it felt like something was actually being done.
One design worth copying. CosmJS, the JavaScript library for Cosmos chains, carries chain balances as integers too large for JavaScript's native number type, so every library has to decide how to hold them and how to stop a caller rounding one by accident. Theirs had thought about it harder than mine had, which is what depth-first catches and breadth-first does not, because it only shows up when you finish a type instead of sketching it.