If someone else built your MVP, the day they hand it over is the cheapest moment to find out what you actually received. After that, every gap costs a message, an invoice or a rewrite.
An independent engineer wrote on dev.to that "What if you disappear?" is the right question to ask an independent engineer, and that it deserves a real answer rather than reassurance. A repository you can set up and run without the original developer is a big part of that answer.
Here is a small script that checks the basics. It uses only the Python standard library and git, so you can run it on any repo you can clone.
What it checks
- A README that explains setup (it looks for "install", "setup" or "getting started")
-
.envis not committed, and a.env.exampleor.env.samplelists the config - A dependency lockfile is present (npm, yarn, pnpm, Poetry, pip, Bundler, Composer, Go)
- Some tests exist, and there is a CI config
- No obvious secrets in tracked files (AWS access keys, Stripe live keys, private key blocks)
- Who wrote the commits, by count
The script
#!/usr/bin/env python3 """Check an MVP repo before you accept the handover. Stdlib only.""" import re, subprocess, sys from collections import Counter from pathlib import Path SECRET_PATTERNS = { "AWS access key": re.compile(r"AKIA[0-9A-Z]{16}"), "Stripe live key": re.compile(r"sk_live_[0-9a-zA-Z]{10,}"), "Private key block": re.compile(r"-----BEGIN [A-Z ]*PRIVATE KEY-----"), } LOCKFILES = ["package-lock.json", "yarn.lock", "pnpm-lock.yaml", "poetry.lock", "requirements.txt", "Pipfile.lock", "Gemfile.lock", "composer.lock", "go.sum"] def git(repo, *args): return subprocess.run(["git", "-C", repo, *args], capture_output=True, text=True, check=True).stdout def check(repo): root = Path(repo) tracked = git(repo, "ls-files").splitlines() results = [] readme = next((root / f for f in tracked if f.lower().startswith("readme")), None) text = readme.read_text(errors="ignore").lower() if readme else "" results.append(("README explains setup", bool(readme) and any(w in text for w in ("install", "setup", "getting started")))) results.append((".env is NOT committed", not any(Path(f).name == ".env" for f in tracked))) results.append((".env.example lists the config", any(Path(f).name in (".env.example", ".env.sample") for f in tracked))) results.append(("Dependency lockfile present", any(Path(f).name in LOCKFILES for f in tracked))) results.append(("Some tests exist", any(re.search(r"(^|/)(tests?|__tests__|spec)(/|$)|test_|\.test\.|_spec\.", f) for f in tracked))) results.append(("CI config present", any(f.startswith((".github/workflows/", ".gitlab-ci", ".circleci/")) for f in tracked))) leaks = [] for f in tracked: p = root / f if not p.is_file() or p.stat().st_size > 1_000_000: continue body = p.read_text(errors="ignore") leaks += [f"{f}: {name}" for name, rx in SECRET_PATTERNS.items() if rx.search(body)] results.append(("No obvious secrets in tracked files", not leaks)) authors = Counter(git(repo, "log", "--format=%an").splitlines()) return results, leaks, authors if __name__ == "__main__": repo = sys.argv[1] if len(sys.argv) > 1 else "." results, leaks, authors = check(repo) for label, ok in results: print(f"[{'PASS' if ok else 'FAIL'}] {label}") for leak in leaks: print(f" possible secret -> {leak}") total = sum(authors.values()) print(f"\nCommit authors ({total} commits):") for name, n in authors.most_common(): print(f" {name:<24} {n:>5} ({n / total:.0%})") sys.exit(0 if all(ok for _, ok in results) else 1) Run it against a fresh clone:
git clone <your-repo-url> mvp && python3 handover_check.py mvp The exit code is 0 only when every check passes, so you can also drop it into CI. A minimal GitHub Actions job looks like this:
name: handover-check on: [push] jobs: check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # full history, so the author count is real - run: python3 handover_check.py . fetch-depth: 0 matters: a shallow clone only has the latest commit, and the author list would show one name no matter who did the work.
A note on the secret scan: it only reads the files tracked in the current commit, and only three patterns. It is a smoke test, not a replacement for a proper scanner across the full history. If the repo is going to live for years, run a dedicated tool once over every commit as part of the handover.
Sample output
This is the script run against one of our own internal tool repos. That repo is a set of agent scripts with no app to deploy, and the script says so plainly:
[FAIL] README explains setup [PASS] .env is NOT committed [FAIL] .env.example lists the config [FAIL] Dependency lockfile present [FAIL] Some tests exist [FAIL] CI config present [PASS] No obvious secrets in tracked files Commit authors (401 commits): Tarun 401 (100%) For an internal script folder, that is a choice we made. For a product you paid someone to build, every FAIL is a question for the builder before the final payment.
Reading the results
Secrets. If the scan finds a key, rotate it with the provider first. Deleting the file does not remove it from git history.
The author list. If one name wrote all the commits, everything the product needs has to be in the repo and the README, because that person is your only other source. If the names don't match the people you were introduced to, ask who wrote the code.
Missing .env.example. You'll find out what config the app needs the day it crashes in production. Ask for the full list of environment variables, and where each value comes from, now.
No tests, no CI. For a concept-testing MVP this can be fine. Just know it, and budget for it before you scale.
What a script can't check
It can't tell you whether the repo is in your name, whether you have admin access to the hosting, domain and payment accounts, or whether the code is any good. Those need a person: you for the accounts, and a second developer for a short review. The checklist in Freelancer vs Agency for MVP: Ask Who Writes Code covers the questions to ask before you pay, in the same spirit.
Extend it as you like: add your own secret patterns, check for a LICENSE, or fail the build when the last commit is older than the invoice.
Posted from the Mantis team account under the Gigzman organization. Gigzman is a small agency that builds MVPs, founded by Tarun Kumar, so we have a stake in handovers being clean. This post was drafted with AI assistance; the script was run and its output pasted unedited.
