Dev.to · 11 min read

Software Project Handover Checklist: Engineering a Clean Exit

Software Project Handover Checklist: Engineering a Clean Exit

Most teams think about the software project handover checklist at the worst moment: the week a vendor relationship ends, a contractor leaves, or an acquisition asks who owns what. By then, the expensive problems are already baked in: production running in a cloud account nobody on your side can log into, a deploy that only works from one person's laptop, secrets committed three years ago. This article treats handover as an engineering problem you design for from the first commit, so that any competent team could take over the system tomorrow. If you are still at the stage of picking a vendor, start with our guide on how to choose a software development company; this piece is about the system you build once you have one. Why handover is an architecture problem, not paperwork A handover document written at the end of a project describes the system as someone remembers it. A handover built into the repository describes the system as it actually runs, because CI checks it every week. The difference shows up in four places: Ownership: who holds the root credentials for code, cloud, DNS, app stores and payment providers. Reproducibility: whether a new engineer can go from clone to running tests to a staging deploy using only what is in the repo. State: whether the infrastructure and the data can be rebuilt or restored without tribal knowledge. Reasoning: whether the next team knows why things are the way they are, so they do not undo a decision that was made for a good reason. Each of these can be verified by a machine. That is the core idea: turn the checklist into checks. The software project handover checklist, layer by layer 1. Ownership: organizations, not people Every asset should belong to an organization account the client controls, with the vendor invited as a member with scoped, revocable access. The common anti-pattern is the opposite: the vendor creates the repository, cloud account or app store listing under its own account and promises to transfer it later. Transfers are possible, but they have side effects you should know about before you rely on them. According to GitHub's documentation on transferring a repository, moving a private repository to a GitHub Free user or organization means it loses features like protected branches and GitHub Pages, and in some cases GitHub permanently retires the old owner/name combination. Cloud accounts, mobile app listings and payment processor accounts each have their own, usually slower, transfer procedures. Creating them under the right owner from day one is cheaper than any migration. Make ownership explicit and reviewable by keeping a manifest at the repository root: # ownership.yaml - reviewed at every release assets: - name: source code system: GitHub organization acme-co owner: client admins: [cto@acme.example, ops@acme.example] - name: production cloud system: cloud account (prod) owner: client billing: client vendor_access: scoped role, time-boxed - name: domain and DNS system: registrar + DNS provider owner: client renewal: auto, client billing - name: payments system: payment processor account owner: client vendor_access: restricted API key The rule of thumb: at least two client-side people have admin on every line, and no line lists a personal email from the vendor as the only admin. 2. Reproducible builds: one entry point per task If setup requires a wiki page with fifteen manual steps, the steps will drift away from reality. A simple, durable pattern is a fixed set of scripts that every project exposes, popularized by GitHub's Scripts to Rule Them All convention: script/bootstrap # install toolchain and dependencies from lockfiles script/setup # create local database, load anonymized seed data script/test # run the full test suite script/server # start the app locally script/deploy # deploy to an environment: script/deploy staging Combine it with committed lockfiles for every package manager, pinned runtime versions (a version file or a dev container definition), and an .env.example that lists every variable the app reads, with a comment explaining where the real value lives. The test is blunt: CI must be able to run script/bootstrap and script/test on a clean machine. If CI can do it, a new team can. 3. Infrastructure as code, and the drift problem Infrastructure created by clicking in a console is the most common thing that fails to survive a handover. There are three realistic options, each with trade-offs: Infrastructure as code (Terraform, OpenTofu, Pulumi, CloudFormation): everything is reviewable and rebuildable, but it requires discipline and someone who can read it. Managed platforms (PaaS, serverless hosting): far less infrastructure to hand over, but configuration still lives in dashboards and you accept the platform's limits and pricing. Documented click-ops: fastest to start, and the hardest to verify. Acceptable only for tiny systems, with screenshots and an export of settings. Whichever you choose, the failure mode is the same: someone changes production by hand and the code stops describing reality. Detect drift on a schedule. With Terraform, plan -detailed-exitcode returns 0 when there are no changes, 1 on error and 2 when the live infrastructure differs from the code (see the terraform plan docs): #!/usr/bin/env bash # scripts/check-drift.sh set +e terraform plan -detailed-exitcode -input=false -lock=false code=$? set -e case $code in 0) echo 'No drift' ;; 2) echo 'Drift: live infrastructure differs from code'; exit 1 ;; *) echo 'Plan failed'; exit $code ;; esac If you inherit console-built resources, bring them under management with Terraform's import blocks before the handover, not after, while the people who created them can still answer questions. 4. Secrets: inventory, scan, rotate Assume every secret the outgoing team has seen is compromised in the sense that it is no longer exclusively yours. That leads to three steps. Inventory. Keep a list of every secret, where it is stored and how to rotate it. The rotation procedure is the part that is always missing: - key: PAYMENTS_SECRET_KEY stored_in: secrets manager, path prod/payments used_by: [api, worker] rotate: roll key in provider dashboard, update secret, redeploy api and worker last_rotated: filled in at handover Scan the full history. Deleting a secret in a later commit does not remove it from Git history. Tools like gitleaks scan every commit on every branch; check the README for the current CLI syntax, since subcommands have changed between versions: # scan all commits on all branches, not just the working tree gitleaks detect --source . --log-opts='--all' --report-path gitleaks.json Rotate. On handover day, rotate everything on the inventory, revoke vendor access tokens, SSH keys and personal access tokens, and remove vendor members from the organizations. Do it in a single planned window with the new team on call, because rotation is when hidden dependencies break. 5. Data: migrations in the repo, restores you have tested Schema changes belong in versioned migrations committed with the code, never applied by hand. Backups only count if a restore has been tested, so make the drill a script: # restore the latest snapshot into a throwaway database and smoke-test it ./scripts/restore-backup.sh --snapshot latest --target handover-drill DATABASE_URL=$DRILL_URL ./script/test --smoke Add a short data inventory: which tables hold personal data, which third parties receive copies of it, and how long it is retained. The receiving team needs that to answer customer and regulatory requests on their first week. 6. Knowledge: ADRs and runbooks next to the code Architecture Decision Records are short Markdown files that capture one decision each; adr.github.io collects templates and tooling. They answer the question new teams ask most: why is it built like this? # ADR-007: Generate invoices in a background job queue Status: accepted Context: invoice PDFs took long enough to block web requests at month end. Decision: enqueue generation; email the PDF when ready. Consequences: needs a worker process and retry policy; invoices are not instant. Pair them with a runbook covering the operations someone will need at 2 a.m.: how to deploy, roll back, restart workers, rotate a key, restore a backup, and where logs and alerts live. Each runbook entry should point at a script, not describe a manual procedure. Make the checklist executable The handover checks above are worth running on a schedule for the whole life of the project, not once at the end. A weekly CI job that behaves like a stranger arriving at the repo catches erosion early: name: handover-check on: schedule: - cron: '0 6 * * 1' workflow_dispatch: jobs: stranger-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - run: script/bootstrap - run: script/test - run: scripts/check-drift.sh - run: scripts/check-docs.sh # install gitleaks first, then: - run: gitleaks detect --source . --log-opts='--all' The docs check can be as simple as failing when required files disappear: #!/usr/bin/env bash # scripts/check-docs.sh for f in README.md ownership.yaml .env.example docs/runbook.md docs/adr; do [ -e $f ] || { echo missing: $f; exit 1; } done The final verification is human: before the outgoing team leaves, an engineer from the receiving team deploys a small change to production using only the repository and the runbook, while the outgoing team watches but does not touch the keyboard. Every question they ask is a gap to fix in the repo. Trade-offs worth deciding early Vendor libraries and licenses. If the code depends on the vendor's private packages or internal frameworks, the handover is incomplete. Either they are delivered with a license that lets you keep using and modifying them, or they are replaced. Generate a dependency list with licenses (an SBOM in SPDX or CycloneDX format works) and review it. Monorepo versus many repositories. A monorepo is easier to hand over as a single unit; many repositories need an index listing each one, its purpose and its deploy target. Observability vendor accounts. Logging, error tracking and uptime tools are often signed up under the vendor. Treat them like any other asset in the ownership manifest, or you lose your history on handover day. When we build custom software for clients, these are decisions we prefer to settle in the first week, because each one is cheap at the start and costly at the end. The same applies to marketing sites: domains, DNS and CMS accounts are the assets most often left in the wrong hands, which is why our website projects start with the client owning the registrar and hosting accounts. Common mistakes Accepting a ZIP file or a fresh repository without history. You lose the context of every change and any chance to scan old commits. Rotating secrets before mapping them, then discovering a cron job on a forgotten server that used the old key. Treating backups as done because the provider says they are enabled, without ever restoring one. Writing the handover document in the last week, from memory. Removing vendor access before the receiving team has done a production deploy on its own. Forgetting non-code assets: app store accounts, push notification certificates, email sending domains, analytics properties. The checklist Every asset is listed in ownership.yaml with a client-side owner and two admins. Repositories live in a client organization on a plan that keeps branch protection. script/bootstrap and script/test pass on a clean CI runner. Runtime versions and dependencies are pinned and lockfiles are committed. Infrastructure is in code and a scheduled drift check passes. The full Git history has been scanned for secrets and findings resolved. A secrets inventory with rotation steps exists, and every secret is rotated at handover. Schema migrations are versioned, and a backup restore drill has passed. ADRs cover the major decisions; a runbook covers deploy, rollback and restore. A dependency and license list has been reviewed, with no unlicensed private vendor code. The receiving team has deployed to production unassisted. Vendor access is revoked last, after the steps above. Frequently asked questions When should a software project handover checklist be started? On day one. Most items, such as account ownership, lockfiles and infrastructure as code, cost almost nothing at the start and become migrations later. Running the checks in CI throughout the project means the final handover is a verification, not a rescue. Is infrastructure as code required for a small project? Not always. A small app on a managed platform may have little infrastructure to describe. What is required is that every setting can be found, exported and reproduced, and that the accounts belong to the client. What should we do if the vendor already owns the accounts? Inventory everything first, then plan transfers one asset at a time, reading each provider's transfer documentation for side effects. Rotate secrets and revoke access only after the receiving team has confirmed it can deploy and operate the system. Originally published on Disruptive Studio.

This is a summary aggregated from Dev.to. Read the complete article on the original site:

Read full article at Dev.to

More Programming & Dev News