[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"header-categories":3,"post-database-migrations-zero-downtime":73,"parsed-post-11c6b6cc-3e90-45cc-af5a-6d98045575b2":147},{"success":4,"data":5},true,{"items":6,"pagination":70},[7,16,24,32,40,48,56,63],{"id":8,"name":9,"description":10,"parentId":11,"createdAt":12,"updatedAt":12,"parent":11,"children":13,"_count":14},"ai-future","AI & The Future","Artificial intelligence and emerging technology",null,"2026-07-24T06:02:45.990Z",[],{"posts":15},1,{"id":17,"name":18,"description":19,"parentId":11,"createdAt":20,"updatedAt":20,"parent":11,"children":21,"_count":22},"tools-productivity","Tools & Productivity","Developer tools and workflow optimization","2026-07-24T06:02:45.712Z",[],{"posts":23},7,{"id":25,"name":26,"description":27,"parentId":11,"createdAt":28,"updatedAt":28,"parent":11,"children":29,"_count":30},"career","Career & Growth","Professional development for developers","2026-07-24T06:02:45.486Z",[],{"posts":31},3,{"id":33,"name":34,"description":35,"parentId":11,"createdAt":36,"updatedAt":36,"parent":11,"children":37,"_count":38},"opinion","Opinion & Analysis","Industry analysis and technical opinions","2026-07-24T06:02:45.272Z",[],{"posts":39},6,{"id":41,"name":42,"description":43,"parentId":11,"createdAt":44,"updatedAt":44,"parent":11,"children":45,"_count":46},"tutorials","Tutorials","Step-by-step learning guides","2026-07-24T06:02:44.965Z",[],{"posts":47},21,{"id":49,"name":50,"description":51,"parentId":11,"createdAt":52,"updatedAt":52,"parent":11,"children":53,"_count":54},"software-engineering","Software Engineering","Best practices, patterns, and principles","2026-07-24T06:02:44.689Z",[],{"posts":55},24,{"id":57,"name":58,"description":59,"parentId":11,"createdAt":60,"updatedAt":60,"parent":11,"children":61,"_count":62},"devops-cloud","DevOps & Cloud","Infrastructure, deployment, and cloud computing","2026-07-24T06:02:44.453Z",[],{"posts":39},{"id":64,"name":65,"description":66,"parentId":11,"createdAt":67,"updatedAt":67,"parent":11,"children":68,"_count":69},"web-development","Web Development","Frontend and backend web development tutorials and guides","2026-07-24T06:02:44.169Z",[],{"posts":55},{"page":15,"limit":71,"total":71,"totalPages":15,"hasNext":72,"hasPrev":72},8,false,{"success":4,"data":74},{"id":75,"slug":76,"title":77,"excerpt":78,"content":79,"status":80,"visibility":81,"featuredImage":82,"canonicalUrl":11,"readingTime":83,"viewCount":84,"commentEnabled":4,"publishedAt":85,"scheduledAt":11,"createdAt":86,"updatedAt":87,"seoTitle":88,"seoDescription":78,"seoKeywords":11,"authorId":89,"author":90,"coAuthors":95,"tags":96,"categories":112,"comments":115,"_count":116,"relatedPosts":118},"11c6b6cc-3e90-45cc-af5a-6d98045575b2","database-migrations-zero-downtime","Database Migrations: Strategies for Zero-Downtime Deployments","Learn how to evolve your database schema without breaking production — expand-and-contract pattern, backfills, and rollback strategies.","## The Problem\n\nYou need to rename a column. The naive approach:\n\n1. Deploy migration that renames the column\n2. Deploy app code that uses the new name\n\nBut between steps 1 and 2, your running app references the old column name. Errors. Downtime.\n\n## The Expand-and-Contract Pattern\n\n**Phase 1: Expand** — Add the new column alongside the old one.\n\n```sql\nALTER TABLE users ADD COLUMN full_name TEXT;\n-- Backfill\nUPDATE users SET full_name = name;\n-- Add trigger to keep both in sync\nCREATE TRIGGER sync_name\n  BEFORE INSERT OR UPDATE ON users\n  FOR EACH ROW EXECUTE FUNCTION sync_name_columns();\n```\n\n**Phase 2: Migrate** — Update app code to use the new column.\n\n```typescript\n\u002F\u002F Before: user.name\n\u002F\u002F After: user.fullName\nconst user = await prisma.user.findUnique({\n  where: { id },\n  select: { fullName: true }, \u002F\u002F new column\n})\n```\n\n**Phase 3: Contract** — Remove the old column once all code uses the new one.\n\n```sql\nALTER TABLE users DROP COLUMN name;\n```\n\n::callout{icon=\"i-lucide-shield\" color=\"primary\"}\nEach phase is a separate deployment. The app works correctly at every step. Zero downtime.\n::\n\n## With Prisma Migrate\n\n```bash\n# Phase 1: Add column\nprisma migrate dev --name add_full_name\n\n# After backfill and code migration:\n# Phase 3: Remove old column\nprisma migrate dev --name remove_name\n```\n\n## Common Patterns\n\n### Adding a NOT NULL column\n\n```sql\n-- 1. Add as nullable\nALTER TABLE posts ADD COLUMN reading_time INT;\n\n-- 2. Backfill\nUPDATE posts SET reading_time = CEIL(LENGTH(content) \u002F 1000);\n\n-- 3. Add constraint (after backfill completes)\nALTER TABLE posts ALTER COLUMN reading_time SET NOT NULL;\n```\n\n### Adding an index without locking\n\n```sql\n-- CONCURRENTLY avoids table locks (PostgreSQL)\nCREATE INDEX CONCURRENTLY idx_posts_published ON posts (published_at);\n```\n\n## Rollback Strategies\n\n1. **Forward-only with feature flags** — Disable the new feature, don't roll back the DB\n2. **Reverse migrations** — Write explicit down migrations\n3. **Blue-green databases** — Expensive but safest for critical changes\n\n## Rules\n\n1. Never drop a column in the same deploy that stops using it\n2. Never make a column NOT NULL in the same deploy that starts writing to it\n3. Always backfill before adding constraints\n4. Test migrations on a production-size dataset\n5. Have a rollback plan before executing\n\nSchema evolution is a solved problem — but only if you follow the pattern.\n","published","public","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1587620962725-abab7fe55159?w=1200&q=80",10,1137,"2026-06-11T06:03:37.498Z","2026-07-24T06:03:37.500Z","2026-07-28T17:12:23.063Z","Database Migrations: Strategies for Zero-Downtime Deployments | BitBlog","fddb5d93-7a2c-4d86-a06a-fa32e73a01c6",{"email":91,"bio":92,"id":89,"name":93,"avatarUrl":94},"mbeahessilfieprince@gmail.com","Fullstack Software Developer ","Mbeah Essilfie","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F93322394?v=4",[],[97,102,107],{"id":98,"name":99,"color":100,"description":101},"prisma","Prisma","#2d3748","Next-generation Node.js ORM",{"id":103,"name":104,"color":105,"description":106},"devops","DevOps","#ff6c37","Development and Operations",{"id":108,"name":109,"color":110,"description":111},"database","Database","#336791","Database design and management",[113,114],{"id":57,"name":58,"description":59},{"id":49,"name":50,"description":51},[],{"comments":117},0,[119,128,137],{"id":120,"slug":121,"title":122,"excerpt":123,"featuredImage":124,"viewCount":125,"readingTime":71,"publishedAt":126,"author":127},"2997028f-4d22-4eb7-9d86-894a54cb559a","building-type-safe-apis-nuxt3-prisma","Building Type-Safe APIs with Nuxt 3 and Prisma","Learn how to build end-to-end type-safe APIs using Nuxt 3 server routes and Prisma ORM for a seamless developer experience.","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1498050108023-c5249f4df085?w=1200&q=80",1922,"2026-07-23T06:02:46.910Z",{"id":89,"name":93,"avatarUrl":94},{"id":129,"slug":130,"title":131,"excerpt":132,"featuredImage":133,"viewCount":134,"readingTime":83,"publishedAt":135,"author":136},"da9e4553-8b0d-411e-b4ec-f9684017e163","mastering-tailwind-css-design-systems","Mastering Tailwind CSS: From Utility Classes to Design Systems","Go beyond basic utility classes and learn how to build cohesive, maintainable design systems with Tailwind CSS.","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1555066931-4365d14bab8c?w=1200&q=80",257,"2026-07-21T06:02:49.268Z",{"id":89,"name":93,"avatarUrl":94},{"id":138,"slug":139,"title":140,"excerpt":141,"featuredImage":142,"viewCount":143,"readingTime":144,"publishedAt":145,"author":146},"50add5fc-4026-4267-b917-7f71ea9e35b0","complete-guide-vue3-composables","The Complete Guide to Vue 3 Composables","Understand the power of Vue 3's Composition API through practical, real-world composable patterns that you can use today.","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1517694712202-14dd9538aa97?w=1200&q=80",990,12,"2026-07-19T06:02:51.753Z",{"id":89,"name":93,"avatarUrl":94},{"data":148,"body":150,"toc":401},{"title":149,"description":149},"",{"type":151,"children":152},"root",[153,162,168,183,188,194,205,218,228,238,248,256,267,273,283,289,296,304,310,318,324,357,363,391,396],{"type":154,"tag":155,"props":156,"children":158},"element","h2",{"id":157},"the-problem",[159],{"type":160,"value":161},"text","The Problem",{"type":154,"tag":163,"props":164,"children":165},"p",{},[166],{"type":160,"value":167},"You need to rename a column. The naive approach:",{"type":154,"tag":169,"props":170,"children":171},"ol",{},[172,178],{"type":154,"tag":173,"props":174,"children":175},"li",{},[176],{"type":160,"value":177},"Deploy migration that renames the column",{"type":154,"tag":173,"props":179,"children":180},{},[181],{"type":160,"value":182},"Deploy app code that uses the new name",{"type":154,"tag":163,"props":184,"children":185},{},[186],{"type":160,"value":187},"But between steps 1 and 2, your running app references the old column name. Errors. Downtime.",{"type":154,"tag":155,"props":189,"children":191},{"id":190},"the-expand-and-contract-pattern",[192],{"type":160,"value":193},"The Expand-and-Contract Pattern",{"type":154,"tag":163,"props":195,"children":196},{},[197,203],{"type":154,"tag":198,"props":199,"children":200},"strong",{},[201],{"type":160,"value":202},"Phase 1: Expand",{"type":160,"value":204}," — Add the new column alongside the old one.",{"type":154,"tag":206,"props":207,"children":212},"pre",{"className":208,"code":209,"language":210,"meta":149,"style":211},"language-sql","ALTER TABLE users ADD COLUMN full_name TEXT;\n-- Backfill\nUPDATE users SET full_name = name;\n-- Add trigger to keep both in sync\nCREATE TRIGGER sync_name\n  BEFORE INSERT OR UPDATE ON users\n  FOR EACH ROW EXECUTE FUNCTION sync_name_columns();\n","sql","undefined",[213],{"type":154,"tag":214,"props":215,"children":216},"code",{"__ignoreMap":149},[217],{"type":160,"value":209},{"type":154,"tag":163,"props":219,"children":220},{},[221,226],{"type":154,"tag":198,"props":222,"children":223},{},[224],{"type":160,"value":225},"Phase 2: Migrate",{"type":160,"value":227}," — Update app code to use the new column.",{"type":154,"tag":206,"props":229,"children":233},{"className":230,"code":231,"language":232,"meta":149,"style":211},"language-typescript","\u002F\u002F Before: user.name\n\u002F\u002F After: user.fullName\nconst user = await prisma.user.findUnique({\n  where: { id },\n  select: { fullName: true }, \u002F\u002F new column\n})\n","typescript",[234],{"type":154,"tag":214,"props":235,"children":236},{"__ignoreMap":149},[237],{"type":160,"value":231},{"type":154,"tag":163,"props":239,"children":240},{},[241,246],{"type":154,"tag":198,"props":242,"children":243},{},[244],{"type":160,"value":245},"Phase 3: Contract",{"type":160,"value":247}," — Remove the old column once all code uses the new one.",{"type":154,"tag":206,"props":249,"children":251},{"className":208,"code":250,"language":210,"meta":149,"style":211},"ALTER TABLE users DROP COLUMN name;\n",[252],{"type":154,"tag":214,"props":253,"children":254},{"__ignoreMap":149},[255],{"type":160,"value":250},{"type":154,"tag":257,"props":258,"children":261},"callout",{"color":259,"icon":260},"primary","i-lucide-shield",[262],{"type":154,"tag":163,"props":263,"children":264},{},[265],{"type":160,"value":266},"Each phase is a separate deployment. The app works correctly at every step. Zero downtime.",{"type":154,"tag":155,"props":268,"children":270},{"id":269},"with-prisma-migrate",[271],{"type":160,"value":272},"With Prisma Migrate",{"type":154,"tag":206,"props":274,"children":278},{"className":275,"code":276,"language":277,"meta":149,"style":211},"language-bash","# Phase 1: Add column\nprisma migrate dev --name add_full_name\n\n# After backfill and code migration:\n# Phase 3: Remove old column\nprisma migrate dev --name remove_name\n","bash",[279],{"type":154,"tag":214,"props":280,"children":281},{"__ignoreMap":149},[282],{"type":160,"value":276},{"type":154,"tag":155,"props":284,"children":286},{"id":285},"common-patterns",[287],{"type":160,"value":288},"Common Patterns",{"type":154,"tag":290,"props":291,"children":293},"h3",{"id":292},"adding-a-not-null-column",[294],{"type":160,"value":295},"Adding a NOT NULL column",{"type":154,"tag":206,"props":297,"children":299},{"className":208,"code":298,"language":210,"meta":149,"style":211},"-- 1. Add as nullable\nALTER TABLE posts ADD COLUMN reading_time INT;\n\n-- 2. Backfill\nUPDATE posts SET reading_time = CEIL(LENGTH(content) \u002F 1000);\n\n-- 3. Add constraint (after backfill completes)\nALTER TABLE posts ALTER COLUMN reading_time SET NOT NULL;\n",[300],{"type":154,"tag":214,"props":301,"children":302},{"__ignoreMap":149},[303],{"type":160,"value":298},{"type":154,"tag":290,"props":305,"children":307},{"id":306},"adding-an-index-without-locking",[308],{"type":160,"value":309},"Adding an index without locking",{"type":154,"tag":206,"props":311,"children":313},{"className":208,"code":312,"language":210,"meta":149,"style":211},"-- CONCURRENTLY avoids table locks (PostgreSQL)\nCREATE INDEX CONCURRENTLY idx_posts_published ON posts (published_at);\n",[314],{"type":154,"tag":214,"props":315,"children":316},{"__ignoreMap":149},[317],{"type":160,"value":312},{"type":154,"tag":155,"props":319,"children":321},{"id":320},"rollback-strategies",[322],{"type":160,"value":323},"Rollback Strategies",{"type":154,"tag":169,"props":325,"children":326},{},[327,337,347],{"type":154,"tag":173,"props":328,"children":329},{},[330,335],{"type":154,"tag":198,"props":331,"children":332},{},[333],{"type":160,"value":334},"Forward-only with feature flags",{"type":160,"value":336}," — Disable the new feature, don't roll back the DB",{"type":154,"tag":173,"props":338,"children":339},{},[340,345],{"type":154,"tag":198,"props":341,"children":342},{},[343],{"type":160,"value":344},"Reverse migrations",{"type":160,"value":346}," — Write explicit down migrations",{"type":154,"tag":173,"props":348,"children":349},{},[350,355],{"type":154,"tag":198,"props":351,"children":352},{},[353],{"type":160,"value":354},"Blue-green databases",{"type":160,"value":356}," — Expensive but safest for critical changes",{"type":154,"tag":155,"props":358,"children":360},{"id":359},"rules",[361],{"type":160,"value":362},"Rules",{"type":154,"tag":169,"props":364,"children":365},{},[366,371,376,381,386],{"type":154,"tag":173,"props":367,"children":368},{},[369],{"type":160,"value":370},"Never drop a column in the same deploy that stops using it",{"type":154,"tag":173,"props":372,"children":373},{},[374],{"type":160,"value":375},"Never make a column NOT NULL in the same deploy that starts writing to it",{"type":154,"tag":173,"props":377,"children":378},{},[379],{"type":160,"value":380},"Always backfill before adding constraints",{"type":154,"tag":173,"props":382,"children":383},{},[384],{"type":160,"value":385},"Test migrations on a production-size dataset",{"type":154,"tag":173,"props":387,"children":388},{},[389],{"type":160,"value":390},"Have a rollback plan before executing",{"type":154,"tag":163,"props":392,"children":393},{},[394],{"type":160,"value":395},"Schema evolution is a solved problem — but only if you follow the pattern.",{"type":154,"tag":397,"props":398,"children":399},"style",{},[400],{"type":160,"value":149},{"title":149,"searchDepth":402,"depth":402,"links":403},2,[404,405,406,407,411,412],{"id":157,"depth":402,"text":161},{"id":190,"depth":402,"text":193},{"id":269,"depth":402,"text":272},{"id":285,"depth":402,"text":288,"children":408},[409,410],{"id":292,"depth":31,"text":295},{"id":306,"depth":31,"text":309},{"id":320,"depth":402,"text":323},{"id":359,"depth":402,"text":362}]