When users leave, their data should leave with them: export and portability for vibe projects
Export is a move, not a download button. This guide covers scope, formats, the request-to-expiry flow, and true portability — plus the permission boundaries that keep export from becoming a leak.

The conclusion first: export is a move, not a download button
Many vibe projects treat data export as a button: the user clicks, the system spits out a file, done. Then a real user actually tries to leave and take their data elsewhere, and the gaps show up everywhere: attachments missing from the export, timestamps without time zones, comments exported but likes left behind, JSON that only the team's own program can parse. A sloppy export does not just frustrate the departing user; it erodes the trust of the users who stay.
This guide treats export as a move: the user is packing up everything they own and settling somewhere new. A move has to answer four questions: what gets packed, how it is packed, whether anything gets lost on the way, and whether it can be unpacked at the destination. The guide follows those four questions and ends with a launch checklist. All examples in this article are fictional; product names and numbers illustrate approaches only and do not refer to any real product.
Step one: define the scope — what actually counts as the user's data
An unclear scope is the most common export failure. Build the list from "everything the user created or accumulated in your product," not from your database tables. A typical list includes: profile and settings, published content (articles, posts, comments, replies), uploaded files and images, saved items, likes, and follow relationships, orders and billing records, message history, and app-generated data such as statistics, badges, or learning progress.
As you list, mark each item in one of three categories. "Always export": content and personal data the user created themselves — leaving any of it out makes the export incomplete. "Conditionally export": content involving other people, such as comments under someone else's post or group chat history — export the user's own messages and participation, not the entire group chat handed to one person. "Explicitly not exported": internal logs, risk scores, other people's private information, and copyrighted third-party material. Writing the third category into your help docs is far better than waiting for users to ask why something is missing.
One dimension is easy to miss: relationships. Individual records export easily; the relationships between them do not. An article and its comments, a project and its collaborators, an order and its invoice — if those links are lost, the export is a pile of loose pages. Draw a relationship map before building export: which entities must travel together, which associations need ID mappings preserved, and which relationships are honestly expected to break on arrival (follow relationships, for instance, do not exist on a new platform by default). Tell users in advance.
Step two: choose formats — machine-readable and human-readable together
One basic principle governs format choice: provide both a machine-readable version and a human-readable one. Use JSON or CSV for structured data: JSON suits nested structures (content plus comments plus metadata), CSV suits tabular data (orders, bills, activity logs). Export files and images in their original format — do not transcode away original quality. If originals are large, offer splits by type or by time period.
The human-readable version is often where the experience is won: an HTML archive the user can browse without any tool, plus a README at the top of the package explaining what each file is, which time zone the timestamps use, and where the ID mappings live. When users cannot open or understand an export, the problem is usually not the data — it is the missing instruction sheet.
Two details must be fixed in advance: time and encoding. Use UTC with ISO formatting for every timestamp, and state the time zone explicitly. Never mix local time with UTC, and never give raw timestamps without a readable time. Use UTF-8 throughout; for CSV, UTF-8 with a BOM remains the most reliable compromise for the classic problem of spreadsheets mangling non-ASCII text on open. Sanitize filenames derived from user input — strip special characters, enforce length limits, and never let a raw user-supplied title become a file path.
Step three: design the flow — request, prepare, download, expire
A complete export flow has four stages, and each needs clear user feedback. Request: the user finds "Export my data" in settings, confirms the scope, and submits. Do not bury the entry three menus deep, and do not require emailing support to export — export is a basic user right, not a support privilege.
Prepare: small exports can generate synchronously; large ones must be asynchronous. An async job needs a queryable status: queued, generating, ready, failed. If generation takes more than a few minutes, notify the user by email or in-app notification instead of leaving them staring at a spinner. On failure, give an actionable message — "some files are too large, try split export" — not a bare "something went wrong, please retry."
Download: give the download link a sensible lifetime, such as 7 days, after which the link dies and the file is deleted, and state the lifetime and file size on the page. Re-verify permissions before download: the link must be accessible only to the person who requested it, never a guessable bare URL. Log the download for audit: who, when, and what scope. Expire: expired files must actually be deleted, not merely unlinked. Write the deletion policy into your privacy docs, or "we delete it" becomes an unverifiable claim.
A fictional example: suppose a notes product called "Green Sprout Notes" builds export. Its package looks like this: a top-level README describing structure and time zones; notes.json with all notes and tags; comments.json; an attachments/ folder with original images and files; orders.csv; profile.json. The user gets an email 3 minutes after requesting, and the link is valid for 7 days. One user has 20,000 photos, so the system automatically splits by year into 4 packages to avoid a single oversized download. The example is fictional, but the structure is directly reusable: README plus structured data plus original files plus splits — all four are required.
Step four: portability — can it be opened at the destination
Export is only half the move; the other half is whether the data can be opened somewhere new. If the format is only readable by your own program, that is not portability — it is a lock with a different address. Portability works in three layers. First, prefer standard formats: vCard for contacts, Markdown or HTML for articles, CSV for tables, iCalendar for calendar data. Where a standard format exists, do not invent a private one.
Second, preserve semantics: alongside field values, keep field meanings. Whether an article is draft or published, whether a comment is public or private-only — these states only mean something on a new platform if they travel with the data. Bare values without semantics force the new platform to guess. Third, provide import guidance: explain how the exported files map onto common platforms, or at minimum document the file structure clearly enough that a capable user can write a conversion script. You do not need importers for every platform, but the structure documentation must be complete.
One ordering issue is often overlooked: deletion versus export. When a user deletes their account, prompt for export first and complete it before deleting — never delete first and ask later. The safer pattern is a "pending deletion" state: the account enters a grace period after export is offered, and real data removal happens only after that period passes. Get the order wrong and the data is gone forever.
Permissions, privacy, and boundaries: export must not become a leak
Export itself can become a security hole, so several boundaries must hold. First, users export only their own data: export endpoints must filter by the logged-in user, and any endpoint that exports someone else's data given a user ID is a disaster. Second, shared-content boundaries: users export only content they are entitled to see — one export must not carry away private groups, unpublished drafts, or other people's direct messages.
Third, sensitive fields must be excluded: password hashes, session tokens, API keys, and internal risk fields must never appear in an export package. Fourth, export requests need abuse protection: rate limits and re-authentication for sensitive exports (re-entering a password or confirming by email), so a stolen account cannot be emptied in one shot. Fifth, keep audit trails: export records, deletion records, and request records, so disputes have evidence.
A final reminder: do not turn export into a last-ditch retention trick. Some products bury the export entry, or make the format deliberately awkward, hoping users stay because they cannot leave. That may hold a few people short-term, but it destroys reputation long-term. The more graceful portability is, the more willing users are to come in — because they know that if they ever leave, their things leave with them.
Launch checklist
Check every item before launch: does the scope list cover every data type the user created; are other people's content and internal fields excluded; are JSON/CSV and a human-readable version both provided; are times unified on UTC and encoding on UTF-8; are async status, notifications, and failure messages complete; does the download link have a lifetime and a permission check; are expired files actually deleted; does account deletion export before deleting; are export endpoints filtered by the logged-in user and rate-limited; is a README and structure documentation included in the package. Only when all ten pass is export genuinely usable.
Comments (0)
Related articles

Release notes are a letter to users, not a copy of the changelog: after reading, users should know what changed, how it affects them, and whether they must do anything. This guide gives a four-section structure, plain-language rules, version-number discipline, per-channel adaptations, five anti-patterns, and a fill-in template.

Tiers answer who you are, points answer how much you have saved. This guide cuts three tiers by scenario, sets earn-spend-expiry rules on one page, handles upgrades and downgrades gracefully, and closes four anti-abuse holes before launch.

Payments integration gets you paid; invoices and receipts keep the people who paid. This guide builds receipt automation at payment time, a self-serve invoice flow, billing line items, proration, refund documents, and a seven-point launch checklist.