
Convex Migrations
Plan and execute Convex schema changes—new fields, backfills, and deprecations—without breaking live queries or clients.
Overview
Convex Migrations is an agent skill most often used in Build (also Operate) that guides schema migration strategies for Convex apps—including new fields, backfills, and deprecations.
Install
npx skills add https://github.com/waynesutton/convexskills --skill convex-migrationsWhat is this skill?
- Covers adding fields, backfilling existing documents, and removing deprecated fields on Convex
- Aligns migration steps with schema versioning and safe rollout for reactive queries
- Reduces downtime risk when solo builders ship schema changes to production Convex deployments
- Pairs with Convex dev workflow (schema push, staged transforms, validation)
Adoption & trust: 2k installs on skills.sh; 402 GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You need to change Convex table shapes in a live app but lack a safe order of operations for backfill, deploy, and client cutover.
Who is it for?
Solo builders shipping incremental Convex schema changes on SaaS or API products with existing user data.
Skip if: Teams still choosing a database or writing one-time imports from scratch with no production Convex deployment yet.
When should I use this skill?
You are evolving a Convex application schema with new fields, backfills, or deprecated field removal.
What do I get? / Deliverables
You get a staged migration plan aligned with Convex schema updates so documents, indexes, and clients stay consistent through each rollout step.
- Ordered migration steps
- Backfill approach
- Deprecation and cutover checklist
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
Canonical shelf is Build because migrations are defined alongside evolving backend data models before and during feature work. Backend subphase fits Convex table/schema evolution, validators, and mutation-driven backfills tied to server functions.
Where it fits
Add a new indexed field to users before shipping a profile feature.
Backfill nullable flags on legacy rows after noticing inconsistent client behavior.
Run a final migration checklist before promoting schema changes tied to a release.
How it compares
Use for Convex-specific document migrations instead of generic SQL Alembic-style playbooks that do not match Convex validators and functions.
Common Questions / FAQ
Who is convex-migrations for?
Indie developers and small teams on Convex who own backend schema and need repeatable evolution patterns without a dedicated DBA.
When should I use convex-migrations?
Use it in Build when adding or renaming fields and in Operate when backfilling or cleaning deprecated fields before the next feature ship.
Is convex-migrations safe to install?
Review the Security Audits panel on this Prism page and treat migration steps that touch production data as high-impact; run in dev first.
SKILL.md
READMESKILL.md - Convex Migrations
interface: icon_small: "./assets/small-logo.svg" icon_large: "./assets/large-logo.png" <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> <g clip-path="url(#clip0_3_23)"> <g clip-path="url(#clip1_3_23)"> <path d="M10.0643 12.5735C12.3769 12.3166 14.5572 11.0843 15.7577 9.02756C15.1892 14.1148 9.62646 17.3302 5.08583 15.356C4.66743 15.1746 4.30728 14.8728 4.06013 14.4848C3.03973 12.8825 2.7043 10.8437 3.18626 8.99344C4.56327 11.37 7.3632 12.8267 10.0643 12.5735Z" fill="#F3B01C"/> <path d="M3.1018 7.50072C2.16436 9.66714 2.12376 12.2034 3.27303 14.2907C-0.771507 11.2479 -0.72737 4.7362 3.2236 1.72378C3.58904 1.44535 4.02333 1.2801 4.47881 1.25494C6.3519 1.15614 8.25501 1.88006 9.58963 3.22909C6.87799 3.25604 4.23695 4.99308 3.1018 7.50072Z" fill="#8D2676"/> <path d="M10.8974 3.89562C9.52924 1.98794 7.38779 0.68921 5.04156 0.649695C9.57686 -1.40888 15.1555 1.92867 15.7629 6.86314C15.8194 7.32119 15.7452 7.78824 15.5421 8.20138C14.6948 9.92223 13.1236 11.2569 11.2876 11.7508C12.6328 9.25579 12.4668 6.20748 10.8974 3.89562Z" fill="#EE342F"/> </g> </g> <defs> <clipPath id="clip0_3_23"> <rect width="16" height="16" fill="white"/> </clipPath> <clipPath id="clip1_3_23"> <rect width="16" height="16" fill="white"/> </clipPath> </defs> </svg> --- name: convex-migrations displayName: Convex Migrations description: Schema migration strategies for evolving applications including adding new fields, backfilling data, removing deprecated fields, index migrations, and zero-downtime migration patterns version: 1.0.0 author: Convex tags: [convex, migrations, schema, database, data-modeling] --- # Convex Migrations Evolve your Convex database schema safely with patterns for adding fields, backfilling data, removing deprecated fields, and maintaining zero-downtime deployments. ## Documentation Sources Before implementing, do not assume; fetch the latest documentation: - Primary: https://docs.convex.dev/database/schemas - Schema Overview: https://docs.convex.dev/database - Migration Patterns: https://stack.convex.dev/migrate-data-postgres-to-convex - For broader context: https://docs.convex.dev/llms.txt ## Instructions ### Migration Philosophy Convex handles schema evolution differently than traditional databases: - No explicit migration files or commands - Schema changes deploy instantly with `npx convex dev` - Existing data is not automatically transformed - Use optional fields and backfill mutations for safe migrations ### Adding New Fields Start with optional fields, then backfill: ```typescript // Step 1: Add optional field to schema // convex/schema.ts import { defineSchema, defineTable } from "convex/server"; import { v } from "convex/values"; export default defineSchema({ users: defineTable({ name: v.string(), email: v.string(), // New field - start as optional avatarUrl: v.optional(v.string()), }), }); ``` ```typescript // Step 2: Update code to handle both cases // convex/users.ts import { query } from "./_generated/server"; import { v } from "convex/values"; export const getUser = query({ args: { userId: v.id("users") }, returns: v.union( v.object({ _id: v.id("users"), name: v.string(), email: v.string(), avatarUrl: v.union(v.string(), v.null()), }), v.null() ), handler: async (ctx, args) => { const user = await ctx.db.get(args.userId); if (!user) return null; return { _id: user._id, name: user.name, email: user.email, // Handle missing field gracefully avatarUrl: user.avatarUrl ?? null, }; }, }); ``` ```typescript // Step 3: Backfill existing documents // convex/migrations.ts import { internalMutation } from "./_generated/server"; import { internal } from "./_generated/api"; import { v } from "convex/values"; const BATCH_SIZE = 100; export const backfillAvatarUrl = internalMutation({ args: { cursor: v.optional(v.string()), }, returns: v.object({