Skip to content

studio

Handover is a deliverable, not an email

8 min read

The usual handover is a zip file, a page of notes, and an email with credentials in it. Some weeks later a client engineer discovers the deploy only works from one laptop, and the vendor who left is answering questions for free out of embarrassment.

Our terms put the repository and the cloud accounts in the client's name from the first commit, so there is no handover event. That reads like a simplification and it is mostly a relocation of work. Everything a handover would have contained still has to exist. It just has to exist from the beginning, when it is cheap. Here is what that takes, and what it costs.

Ownership starts on day one

Create the accounts in the client's org rather than moving them later

Day one is spent on accounts. The client creates the GitHub organization, the cloud account, the Vercel team, and registers the domain under their own registrar login, with their own payment method. We are invited as members. It takes an afternoon and it feels like nothing is happening.

The alternative is to build in our accounts and transfer at the end. The specific mess of doing that:

  • A GitHub organization. Repository transfers leave redirects, which is the easy part. Actions secrets and environment secrets do not travel. Branch protection and rulesets get re-created by hand. Deploy keys, webhooks, and installed GitHub Apps are re-authorized against the new org. Access granted through teams evaporates, so every person is added again. Actions billing and runner configuration are org-level and start over.
  • A Vercel project. Moving a project to another team means relinking the Git integration, re-adding environment variables per environment, resetting deployment protection, and moving domains separately, each of which needs its DNS records verified again on the other side.
  • A domain. Registrar transfers need the domain unlocked, an authorization code, and a reachable address on the registrant contact. There is a sixty day lock after registration and after any previous transfer, which is a fact people discover on the day they need to move.
  • A cloud account. Moving an AWS account between organizations needs the current payer to cooperate and the account detached from policies it inherited. Anything not moved with the account gets rebuilt, and some of it cannot be rebuilt under the same name: S3 bucket names are globally unique, hosted zone identifiers change, and IAM and KMS policies that name an account identifier all need editing.

None of that is difficult. All of it is a week nobody planned, landing in the week the relationship is ending, which is the worst available week for unplanned work. Done on day one it is an afternoon.

There is a second effect worth naming. Billing sits on the client's card from the first deploy, so the running cost of the system is visible while it is being built, by the people choosing the architecture. That changes decisions.

Secrets live somewhere the client controls

Credentials do not travel through chat and do not sit in a password manager we own. They live in the client's secret store: their parameter store, their team's environment variables, their vault.

The test is one question. If every account belonging to our team were disabled tonight, could the client deploy tomorrow morning? Any answer that routes through us means the secrets are in the wrong place.

Two practical pieces make that hold. The application reads its configuration at boot and exits immediately naming the key that is missing, rather than starting and misbehaving under load an hour later. And a checked-in .env.example lists every key with a comment saying where the value comes from and who can issue a new one. The key names are not the secret. The thing that actually breaks a handover is thirty values nobody can source again.

We also rotate one credential on purpose during the build, usually around week three. A rotation procedure nobody has run is a paragraph, not a procedure.

Decision records, written while the reasoning is still available

A decision record written in week one is a different document from the same record written in month four. In week one the rejected alternative and the constraint that killed it are still in the room. By month four people remember the decision and have quietly reconstructed a reason for it that sounds better than the real one.

What belongs in a decision record:

  • The decision itself, one sentence, present tense.
  • The date, and who was in the room.
  • The constraint that forced it. Usually a deadline, an existing team's skills, a cost ceiling, or a system nobody controls.
  • The options considered, with the specific reason each one lost. This is the part carrying all the value and the part always left out.
  • The consequences accepted. A decision with no listed downside was not a decision, it was a preference.
  • The trigger to revisit: a condition, not a date.

What does not belong: API documentation, which goes stale and lives next to the code; anything the code states more precisely; a narrative of who argued for what; and plans. A decision record describes a choice already made, not work intended.

A concrete one from our own products. Tumbuhku runs as three decoupled repositories: a Go backend, a Next.js admin backoffice, and a Next.js app for parents. The record says the split exists because the backoffice and the parent app have different release rhythms and different authentication surfaces; the accepted cost is a type contract maintained by hand across the boundary and three pipelines to keep green; the trigger to revisit is that contract changing more than roughly twice a month, which would mean the boundary sits in the wrong place. Someone reading that a year later can check whether the condition has been met. That is the only job a decision record has.

They live in the repository as numbered files under docs/decisions/ and are immutable once merged. Reversing a decision means a new record that supersedes the old one, so the history stays readable instead of becoming a document that has always agreed with the present.

A runbook written by someone who has run the thing

Documentation describes how a system is meant to work. A runbook is written after doing the thing, while the author is still annoyed by the parts that surprised them. The two are not substitutes.

  • Deploy: the actual command or button, and what a normal one looks like in the logs.
  • Rollback: the command, and a duration that was measured once, deliberately.
  • Restore from backup: where the backup lives, how old the newest one can be, and how long a restore took the one time someone did it.
  • Each alert that can fire, what it means, and the first thing to open. Tumbuhku runs OpenTelemetry and Sentry side by side, so the runbook says which of the two to open for which symptom. That is the sentence someone needs at two in the morning.
  • Credential rotation, per credential, in order, including the ones that require a restart.
  • Every manual step still remaining, with a note on why it is still manual. An honest list of the ugly parts is worth more than a clean document that omits them.

Every timing claim gets measured once. A restore takes about an hour is a useful sentence when it came from a stopwatch and a dangerous one when it came from an assumption.

The last week is a rehearsal, and you keep off the keyboard

In the final week the client's engineer deploys to production while we watch. Then rolls it back. Then rotates a credential. Then takes one alert from firing to closed. We answer questions and we do not touch the keyboard, because every place they get stuck is a defect in the documentation, and those defects only appear if we let people get stuck.

Each one gets fixed the same day by the person who hit it, which produces better text than we would have written from imagination. A rehearsal usually surfaces three or four things, and one of them is always a piece of access we had without noticing we had it.

A handover you have not rehearsed is a promise. It may well be a good promise. You have no evidence either way, and the test happens later, without you, during an incident.

What this costs

Two days at the start, spent on accounts and access instead of code, at the moment a client is most eager to see something. We explain what the two days buy, and it is still uncomfortable for both sides.

Then friction for the rest of the engagement. We are members, not owners, so anything needing an owner needs someone on the client's side to be reachable. Most of that lands in the first two weeks and then fades, but it is real, and owning everything ourselves would be faster.

The larger cost is leverage. A vendor holding the accounts holds a standing reason to be called back, and that is worth money. Giving it up is a business decision rather than a virtue. We make it because retention bought with friction shows up in the work: it changes what gets written down, how much gets automated, and how honest the architecture is allowed to be. We would rather be called back for the other reason, and that preference has a price we pay every engagement.

The rule is a question you can ask on any ordinary Tuesday, at any point in a project. Pick a credential, a deploy, an account, a piece of knowledge that lives in one person's head. Ask who could handle it tomorrow morning if our team disappeared overnight. Every answer that requires us is handover work that has not been done, and it is due now, while there is time to do it properly, rather than in the last week when it turns back into a promise.

Working on something similar?

Tell us what you are running into. We are happy to compare notes.