Back to blog

Developer Guides

How Developers Can Share a Project ZIP With Clients Fast

Share a project ZIP with clients without leaking .env files, node_modules, or secrets. A practical handoff checklist.

August 3, 20268 min read
How Developers Can Share a Project ZIP With Clients Fast

A client asks for "the code" or "the latest build folder" and they want it today. You could add them to the git host, but procurement has not finished, their IT blocks GitHub, or they only need a snapshot for a contractor to review. So you make a zip. That zip is either a clean handoff or a leak waiting to be unzipped.

Sharing a project archive is a delivery problem, not just a compression problem. Clients open whatever you send. They will not notice that you included `.env`, a production dump, or 400 MB of `node_modules`. This guide is a practical way to build a zip you are willing to let leave the building, plus a checklist for the message that goes with it.

Decide what the client actually needs

"Send the project" can mean four different artifacts:

  • Source snapshot for another developer to run locally.
  • Built static export (for example `dist/` or `out/`) they can host or preview.
  • Design + code sample of one feature, not the whole monorepo.
  • Handover package with docs, credentials process (not the secrets themselves), and run instructions.

Ask one clarifying question before you zip: should they run it, read it, or deploy it? A reader does not need `node_modules`. A deployer needs environment variable names, not your production values. A reviewer may only need one service directory from a monorepo.

If they can accept a git invite, prefer that. History, diffs, and access revocation are better in a real host than in a zip sitting in someone's Downloads folder for a year.

Zip hygiene: what to exclude every time

Build the archive from a clean export, not from a right-click on your working tree. A working tree contains machine-local junk and secrets you stopped seeing months ago.

Always exclude:

  • `.env`, `.env.*`, `.env.local`, `.env.production`
  • `credentials.json`, `service-account*.json`, cloud key files
  • `.pem`, `.p12`, `.jks`, `id_rsa`, `.keystore`
  • `.git/config` remotes that embed tokens (prefer a git bundle or a host invite if they need history)
  • `node_modules/`, `.pnpm-store/`, `.yarn/cache/`, vendor caches
  • `.next/`, `dist/` unless the client asked for a built preview
  • `__pycache__/`, `.venv/`, `venv/`, `target/` for compiled languages unless they asked for binaries
  • IDE folders they do not use (`.idea/` is optional; `.vscode/` may contain local paths)
  • Database dumps, `*.sqlite` with real user data, `uploads/` with customer files
  • `.DS_Store`, `Thumbs.db`, editor swap files

Usually exclude unless they asked:

  • Large media that already lives in object storage
  • E2E recordings and Playwright traces
  • Internal RFCs that name other clients

A good pattern is `git archive` from a tagged commit, then add a `HANDOFF.md` you write for humans. `git archive` will not pack ignored build artifacts if your `.gitignore` is honest. It also will not save you if you committed a secret last year. Search the tree for `API_KEY`, `BEGIN PRIVATE KEY`, and `AKIA` before you ship.

Secrets are not only .env files

Clients (and whoever they forward the zip to) can find:

  • Hard-coded tokens in test fixtures
  • Stripe or Twilio keys in old config samples
  • Production URLs with embedded basic-auth
  • Customer names in seed data
  • Internal Slack webhook URLs in deploy scripts

Replace real values with placeholders: `POSTGRES_PASSWORD=change-me`. List every required variable in `HANDOFF.md` with a one-line purpose. If they need working credentials, send those through a password manager or a one-time secret link, never inside the zip.

How to build a zip that opens cleanly

Small process, fewer support emails:

  1. Commit or stash so you know what you are exporting.
  2. Export from git or copy into a fresh folder named `client-project-YYYY-MM-DD/`.
  3. Add `HANDOFF.md` (stack, Node/Java version, how to install, how to run, what is not included).
  4. Add `.nvmrc`, `.python-version`, or a `runtime.txt` equivalent if you have one.
  5. Include a sample env file only: `.env.example`.
  6. Zip the folder so the archive contains one top-level directory, not 400 loose files.
  7. Note the uncompressed and compressed size. If it is huge, you left `node_modules` or a video in.
  8. Compute a checksum if the project is sensitive or large.

On Unix-like systems, something in the spirit of `zip -r project.zip client-project-2026-08-03 -x ".env" -x "/node_modules/*"` is a start, but a dedicated export folder is safer than a long exclude list you will forget to update.

Monorepos and "just that app"

Clients rarely need the whole cargo cult of packages. If they hired you for a marketing site, do not send the internal billing service. Either:

  • Export the one package plus the lockfile and workspace snippets it needs, or
  • Send a minimal reproduction with the public API mocked.

Document which workspace commands they must run. A zip of `/packages/web` without the root `package.json` is a support ticket, not a handoff.

How to deliver the archive

Project zips are often too large for email. Chat apps may accept the file and then fail on the client's guest Wi-Fi.

Options that usually work:

  • Git host guest access with a read-only deploy key or a time-boxed collaborator seat.
  • Signed object-storage URL from the bucket you already use, expiry measured in hours or a few days.
  • A temporary browser room when the client cannot create accounts and you are both available. [PeerPizza](/) is one browser-based option: no account, room code, optional PIN, chat plus files, chunked upload without a fixed per-file size cap, and automatic cleanup after about two hours idle. Keep your own copy. Rooms are temporary on purpose.
  • Courier USB only for air-gapped or legally picky environments, with the drive encrypted.

Tell the client how long the link or room lasts in the same message as the code. If they wait until next week, a two-hour room will be empty. That is not a failure of the zip. That is a mismatch of lifetime and expectation.

Client handoff checklist

Send this as the cover note, not as a thought in your head:

  • What the zip is (source vs built preview) and the date of the snapshot
  • Git tag or commit SHA if it came from version control
  • Runtime versions (Node, Java, Python, OS assumptions)
  • Install and run commands that you actually ran on a clean folder
  • Required third-party accounts (auth provider, maps key) and who pays for them
  • Environment variable names, with secrets delivered separately
  • What you deliberately omitted (`node_modules`, production data, keys)
  • License and whether they may hire another vendor to modify it
  • Who to ping for 48 hours if `npm install` fails
  • When the download location expires
  • Reminder that they should keep their own backup after download

If legal ownership is unclear, do not "just zip it." A contract line about deliverables is cheaper than an argument about a repo that also contains another customer's theme.

What good looks like vs a panic zip

A panic zip is your `~/code/app` folder, including `.env.production`, `node_modules`, and a database named `real-users.sql`. A good zip is a dated folder, an example env, a handoff note, and a size that makes sense for source.

Review the archive yourself: unzip it into `/tmp`, install, run. If you cannot, the client cannot. Fix the handoff on your machine, not in their inbox.

Troubleshooting project zip handoffs

The client says the zip is empty or "corrupted"

  • They double-clicked a partial download. Compare file size and checksum.
  • macOS Archive Utility sometimes chokes on very large zips or unusual permissions. Have them try `unzip` in Terminal, or send a `.tar.gz` as a second option.
  • You zipped a shortcut or an unsynced cloud placeholder instead of the real folder. Confirm the bytes on disk.

`npm install` or build fails on their laptop

  • You omitted the lockfile or sent the wrong package manager's lockfile.
  • Native addons need build tools they do not have. Mention Xcode CLT, Visual Studio Build Tools, or Docker as the supported path.
  • Your README assumes global CLIs (`poetry`, `fnm`, `java`) that are not installed. Pin versions.
  • You accidentally shipped `node_modules` for the wrong platform. Do not ship it.

They cannot receive the file

  • Email and Slack size limits. Use a signed URL or a temporary room instead of splitting into `part1.zip` / `part2.zip` unless you have no other choice.
  • Their IT blocks file hosts. Ask which domains are allowed before you upload.
  • They are on a phone. A 200 MB source zip is a laptop job. Say so.

You already sent secrets

  • Rotate every credential that was in the archive. Do not debate whether they "probably will not look."
  • Invalidate the download link or abandon the room.
  • Send a replacement zip and a short note that the previous archive is withdrawn.
  • Check git history if that zip was built from a commit that still contains the secret.

Delivery options compared

MethodClient account neededEasy to revokeGood for huge reposTypical use
Git host inviteUsually yesYesYes (they clone)Preferred when IT allows it
Signed cloud URLNoExpire the URLYesAsync handoff
Temporary chat + file roomNoRoom expiresPractical for snapshotsSame-day send
Email attachmentNoNoPoorTiny docs only
Encrypted USBNoPhysicalYesAir-gapped / legal

Final thoughts

A project zip is a snapshot of trust. Exclude secrets, caches, and customer data. Export from a known commit. Write the runbook you wish every vendor had sent you. Deliver through a channel whose lifetime matches the client's calendar, and keep the canonical copy in git. If they only needed a preview, send a built folder or a staging URL instead of your entire working tree. Fast handoff is not the same as a reckless zip—and clients remember which one you chose.

Try PeerPizza: free temporary rooms to chat and share files — no signup, room codes, optional PIN, auto cleanup. Open home