DEV Community

Nabeel Hassan
Nabeel Hassan

Posted on Originally published at nullstud.io

Your README Is a Lie Until Someone Else Runs It

I run a small studio, which means every project I ship ends on the same question: can the client actually keep this without me?

For a long time I assumed the answer lived in the contract. It does not. I have watched a company hold the intellectual property in the strictest legal sense and still be completely stuck, because the thing was live on a hosting account registered to somebody who had moved on.

Ownership turned out to be three separate things, and most agreements only buy one of them.

Three kinds of ownership

Legal ownership is the IP clause. Assignment on final payment, with clear boundaries around any pre-existing tooling the builder brings along and around open-source components. It is the easy layer, and it is the only one most contracts actually cover.

Custodial ownership is the repository. Not a zip of the final state. The full history, every branch, and the issue and pull-request record. History is not sentimental. It is how the next engineer finds out why a line exists before deleting it.

Operational ownership is whether you can build the software on a clean machine, deploy it, rotate a credential, and fix something at nine on a Friday night. Everything above it is theoretical until that one is true.

Engineers care about the third layer. It is also the one nobody writes into a statement of work.

The failure is almost never the code

The most common ownership failure I see has nothing to do with source files. It is that the product runs on accounts registered to the builder.

The list is longer than people expect. Cloud or hosting. Domain registrar and DNS. Apple and Google developer accounts for anything in a store. Analytics and error monitoring. Email and SMS sending. The payment processor. Every third-party API key. And for anything with a phone attached, the numbers themselves, plus the recordings and transcripts.

The rule I now enforce from day one is boring and it works: accounts are created by the client organisation, in their name, on their billing, and we get invited in. Not the other way round.

Retrofitting this is possible, but each item becomes its own small project. An app store transfer, a number port and a domain move are three different procedures with three different waiting periods, and at least one of them will need a person who has since left. I have done that cleanup. It is not technically hard, it is just slow in a way that always lands during a week when something else is on fire.

The drill that settles it

None of the above proves anything until somebody tries it. So here is the test, and it is the single most useful hour I spend on any project.

Take a person who did not build the software. Another engineer, a contractor, a friendly second opinion. Give them the repository and the access list, nothing else, and no access to the original team. Ask them to:

  1. Get it running on a clean machine.
  2. Deploy it to a scratch environment.
  3. Make a trivial visible change.
  4. Ship it.

Time-box the whole thing to a day.

What comes back is never abstract. It is always a specific list, and it is usually some version of this:

  • An environment variable nobody documented.
  • A database migration that has to be run by hand.
  • A key that only exists on one laptop.
  • A build step that quietly depends on a tool version.
  • A service that was configured once in a web console and captured nowhere.

I want to be honest about something here, because it would be easy to write this as advice for other people. My own projects fail this drill too. Every time I have run it on something I built, it has found at least one item. The point of the drill is not to prove the work was clean. It is that each of those findings is cheap to fix while the people who created it are still on the project, and expensive to fix later.

Which is exactly why it belongs before final payment rather than after it.

Why the README lies

The reason this keeps happening is not laziness. It is that the setup guide is written by the person whose machine is already set up.

Every implicit dependency is invisible from inside that machine. The Node version that happens to be active. The CLI that was installed a year ago for something else. The .env that was never in the repo because of course it was not. You cannot see the gap by rereading your own README, because you are the one person on earth who does not need it.

The drill is essentially a compiler for documentation. It turns "should work" into a diff.

What I hand over now

The list I actually work through, condensed:

  • The repository, full history, all branches, issue tracker export if it lives somewhere the client can be added to.
  • A README a stranger can follow. Not a product description. The exact steps from a clean machine to a running app, including versions of anything that has to be installed first.
  • Environments and deployment, written down. If a release is a sequence of manual clicks one person knows, that is a finding, not a document.
  • Secrets in a manager the client owns, with a note on what each one is for. Credentials in a chat thread are not a handover.
  • Infrastructure as files, rather than as the shape of somebody's console.
  • The data. Schema, a documented export path, and a backup that someone on their side has actually restored once. Software can be rebuilt. A ledger cannot.
  • Source files, not exports. Design files, and for 3D or immersive work the original assets, not the optimised output.
  • A dependency and licence list, flagging anything with a commercial tier or a renewal date.
  • A short decisions note, including the compromises. Two pages of "this looks strange because of X" saves a future team a month.
  • The access list itself. Every account and service, who owns it, who is admin, what it costs. This is the document that turns a pile of logins into something a business can manage.

Where lock-in is legitimate

Not every dependency is a trap, and pretending otherwise leads to worse software. You are not going to own your telephony provider, your game engine, your cloud, or your auth service. Choosing them is normal engineering. When we build SDKs that ship inside somebody else's product, depending on the platforms those SDKs run on is the job, not a failure.

The line I use is this: you do not have to own the platform, but you must own the configuration, the data and the exit path. The account is yours. The settings are documented or exportable. Your data comes out in a usable form on demand. Somebody has thought about what a move would involve, even if nobody intends to make one.

The real lock-in is knowledge

Here is the uncomfortable part. The code was never the hard thing to transfer.

A team that has held a system for two years knows things no repository contains. Which service is flaky on Mondays. Why that retry exists. Which client email triggered the weird validation rule. That is true of an internal team as much as an external one, which means "we built it in-house" is not protection. It is the same bus factor with a different org chart.

Documentation and the drill are how you convert some of that into something transferable. Not all of it. Some of it just walks out.

None of this is adversarial and none of it is about trust. I want clients portable, because a client who could leave and does not is a much better reference than a client who cannot. It is also the only version of this business I know how to run without dreading the offboarding conversation. If it helps, the longer buyer-side version of this, including the questions worth asking before signing anything, is on the Null Studio blog.

So, a genuine question, and I am curious how many people are in the same position I was: when did you last try to run one of your own projects from a clean machine?

Top comments (0)