Building Better Platforms Through Better Documentation
Observation: we kept the document and deleted the code
Our old admin console had grown for years. It had its own server and its own copy of every table name, and it drifted from the backend every time a field changed. The rebuild replaced it with a thin shell that loads screens owned by each backend module. (The architecture itself is covered in A Console That Cannot Drift.)
Here is the part I want to talk about. The rebuild went like this:
- A design doc set the direction: modules own their screens, the shell is only a shell.
- A prototype was built against it and run end to end on 7 Oct 2026.
- Everything the prototype taught us went back into a platform guide: the rules, the contracts, the file layout, what broke and why.
- The prototype was discarded on 8 Oct 2026, so the production build would start from the guide and a written plan, not from prototype code.
The prototype did its job. It found real problems, like a dependency whose exports didn't behave the way the runtime expected, and a versioning mistake where a CSS-only change could slip past. But each of those lessons was only valuable once it was written down as a rule. Code that "happens to work" doesn't tell the next person which parts were deliberate.
[embed: node/c8f0c28f-2065]
The prototype fed its lessons into the guide and was then retired; production work starts from the guide.
Insight: rebuilds need documents more than new projects do
On a brand-new project, missing docs hurt later. On a rebuild, a migration or a big redesign, they hurt immediately. Here's why.
1. A migration has two systems at once. During the migration, the old console and the new one run side by side. Someone has to know which legacy page maps to which new module, in what order they migrate, and what changes for each. Our guide points to an inventory where every legacy page is mapped to the module that owns its data, its new pages and its permissions. Without that, migration becomes guesswork.
2. Old behaviour is invisible knowledge. The legacy system does things nobody remembers deciding. If you don't write them down before rebuilding, you either lose them or rediscover them in production. Our guide even records a styling decision for exactly this reason: new screens keep the legacy look during cutover, so operators see one design while pages move.
3. Decisions without reasons get re-argued. A rebuild involves dozens of choices. Our guide's decision log records each one with a date, who decided and why. When someone asks "why didn't we just use a micro-frontend framework?", the answer is already written, with the alternatives that were compared.
4. More people build than designed. Seniors design a rebuild, but juniors, other teams and now AI coding agents do much of the building. They follow what is written, not what was said in a meeting. Our guide has a section of rules written specifically for AI agents, each with a check behind it.
5. "Done" and "not done" blur. Mid-migration, a lot is half-built. A clear "Not yet built" list, with why each item waits and where it's tracked, stops people from assuming something exists because the prototype had it.
While both consoles run, the document is the only place that knows how they relate.
Recommendation: what a rebuild doc should contain
The guide I learned from is long, but its shape is simple and reusable. If you're starting a rebuild, a migration or a large feature, these are the sections I'd copy:
Section | What it holds | Why it earns its place |
|---|---|---|
The one rule | A single sentence the whole design follows from | Every later decision can be checked against it |
Vocabulary | Each term, what it means, where it lives in the code | New people stop guessing what "module" or "page" means |
Contracts | The interfaces between parts, with examples | Teams can build in parallel without breaking each other |
A step-by-step checklist | "Adding a screen" from plan to ship | A junior can deliver without asking ten questions |
Decision log | Date, decision, who, alternatives considered | Stops the same debate from happening twice |
Not yet built | Item, why it waits, where it's tracked | Nobody mistakes a prototype feature for a finished one |
Things that bite | Known traps and their fixes | Saves the next person an afternoon |
Authorship and inputs | Who gave direction, what was decided, what was AI-drafted | Makes it clear what is verified and what still needs review |
A few habits made ours work:
- Write rules with a check behind them. "Modules must not import each other" is a wish. A lint rule that fails the build is a rule.
- Show snippets, not dumps. A five-line example of the contract teaches more than a whole file.
- Date your decisions. "Decided 2026-10-08" tells a reader instantly whether a line is fresh or historic.
- Update the doc in the same pull request as the code. Our checklist has a docs step right before "ship": if you changed a contract, you update the guide.
Why we check the schema, not just write it down
Here's the uncomfortable truth I learned: documents drift too. You write "every page must name an action it requires", someone forgets once, and nobody notices for weeks. A rule that only lives in a doc is a hope.
So the most useful habit in our guide is this: every important rule is also a check that runs. The doc says what the rule is and why. The check makes sure nobody, including future you, can quietly break it. Our guide's rules section even says each line is a hard requirement with a check behind it, and none may be softened just to make a build pass.
These are the kinds of schema checks that made the biggest difference for us:
- Checked when the app starts. Every module's declared pages are validated at boot: page ids and paths must be unique, every required action must actually be declared, every parent page must exist. A typo fails loudly on your machine, not silently in front of an operator.
- API snapshots. Each module's management API is saved as a snapshot, and every change regenerates it so the diff is reviewed like code. The contract test fails if the API surface changed without a version bump, if a version was bumped without a new snapshot, or if the snapshot is stale.
- Contract tests over every route. A test walks every management route and checks it declares its action and refuses the wrong kind of token, so a new route can't skip the rules.
- Lint rules for boundaries. "Modules don't import each other's internals" isn't a reminder in a meeting. It's a lint rule that fails the build.
A small example of what a boot-time check catches:
PageSpec(id="pet", path="pets/:pet_id", requires=["pets.veiw"])
# boot fails: requires names an undeclared action "pets.veiw"As a junior, I found this freeing rather than strict. I didn't have to remember every rule from the doc, because the checks remembered for me. The doc explains the rule. The schema check enforces it. You really need both.
What it changed for me as a junior engineer
I'll be honest: the first time I opened the guide, it felt like a lot. But it changed how I work in three concrete ways.
I could start without waiting. The "adding a screen" checklist told me where every file goes, what to import, which tests to write and what "done" looks like. I didn't need a senior free for an hour to get going.
I asked better questions. Instead of "how does this work?", I could ask "the guide says X, but my case needs Y. Should I add a method to the contract or is there another way?" That's a much faster conversation for both sides.
I knew what not to touch. The guide says plainly to ask before inventing a new operation, flag or group. Without that line, I would probably have invented one.
The biggest lesson: documentation isn't the boring part after the real work. In a rebuild, it is the real work, and the code is how you prove it right.
Behind that, operators use our admin console to answer pet parents' questions from the same data the app shows. As we rebuild and migrate systems like this, good documentation is what lets a small team move fast without losing what the old system knew. It keeps every new screen honest to the product, so a pet parent always gets an answer from an accurate picture of their pet.
Key takeaways
- In a rebuild, the prototype is the experiment and the document is the result. Keep the lessons, not the throwaway code.
- Rebuilds carry hidden knowledge. Write down what the old system does before you replace it.
- Record decisions with dates and reasons, so they don't get argued twice.
- Write for the people who will build, including juniors, other teams and AI agents.
- Make "not yet built" explicit, so nobody ships on top of something that doesn't exist.
FAQs
Isn't writing the doc first slower than just building? For a small fix, yes. For a rebuild or a migration, no. Building without a doc means every person rediscovers the same decisions, and the time you "saved" comes back as rework and long review threads.
Why throw away a prototype that works? Because a prototype is built to learn, not to last. It takes shortcuts on purpose. Once its lessons are written into the guide, starting production from the guide means you keep the lessons without inheriting the shortcuts.
How long should a rebuild document be? As long as it needs to be, and no longer. Start with the one rule, the vocabulary, the contracts and a step-by-step checklist. Add the decision log and the "not yet built" list as you go.
How do you stop the document from going out of date? Two ways. Update the doc in the same change as the code it describes, and turn important rules into schema checks, tests or lint rules, so drift fails the build instead of hiding.
What is a schema check, in simple words? A small automatic test that compares what the code declares against the rules. For example: "every page must name an action that actually exists." If it doesn't match, the app refuses to start or the build fails.
I'm a junior. Should I be editing the design doc? Yes, carefully. Fix things you find unclear, add traps you hit to the "things that bite" list, and ask before changing a rule or a contract. Juniors are often the best people to spot what the doc forgot to explain.
Can AI coding tools follow the doc? They can, if the rules are written clearly and each one has a check behind it. Our guide keeps a separate block of rules meant for AI agents for exactly this reason.