[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-trigger-dev-staff-engineering-skills-denormalization":3,"mdc-kzroq-key":34,"related-repo-trigger-dev-staff-engineering-skills-denormalization":2803,"related-org-trigger-dev-staff-engineering-skills-denormalization":2882},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":12,"stars":23,"repoUrl":24,"updatedAt":25,"license":26,"forks":27,"topics":28,"repo":29,"sourceUrl":32,"mdContent":33},"staff-engineering-skills-denormalization","prevent data model denormalization traps","Detect and prevent denormalization traps when designing data models. Use when writing code that copies fields between tables, embeds related data in documents, caches composed objects, or adds redundant columns to avoid joins. Activates on patterns like storing derived\u002Fcopied data, syncing fields across tables, or embedding nested objects in document stores.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},"trigger-dev","Trigger.dev","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Ftrigger-dev.jpg","triggerdotdev",[13,17,20],{"name":14,"slug":15,"type":16},"Architecture","architecture","tag",{"name":18,"slug":19,"type":16},"Database","database",{"name":21,"slug":22,"type":16},"Data Modeling","data-modeling",3,"https:\u002F\u002Fgithub.com\u002Ftriggerdotdev\u002Fstaff-engineering-skills","2026-06-17T08:40:53.835868",null,1,[],{"repoUrl":24,"stars":23,"forks":27,"topics":30,"description":31},[],"Skills that give AI   coding agents staff-engineer instincts: recognizing and avoiding production failure modes like cardinality, idempotency, and race conditions.","https:\u002F\u002Fgithub.com\u002Ftriggerdotdev\u002Fstaff-engineering-skills\u002Ftree\u002FHEAD\u002Fskills\u002Fstaff-engineering-skills-denormalization","---\nname: staff-engineering-skills-denormalization\ndescription: Detect and prevent denormalization traps when designing data models. Use when writing code that copies fields between tables, embeds related data in documents, caches composed objects, or adds redundant columns to avoid joins. Activates on patterns like storing derived\u002Fcopied data, syncing fields across tables, or embedding nested objects in document stores.\n---\n\n# Denormalization Trap\n\nEvery copy of data is a consistency obligation. Before duplicating a field to avoid a join, ask: **who updates this copy, what happens when the update fails, and how do you detect drift?**\n\n## Snapshot vs Reference\n\nThe first question when copying data: is this a **snapshot** or a **reference**?\n\n- **Snapshot:** The value at a point in time. Correct to copy. Example: the price on an order line item at time of purchase. The product price may change later, but the order price should not.\n- **Reference:** The current value of something. Dangerous to copy. Example: a user's plan name stored on their profile. When the plan changes, every copy is wrong until updated.\n\nIf you're copying a reference, you've taken on a consistency obligation. If you can't answer all four questions in the checklist below, don't denormalize.\n\n## Consistency Obligation Checklist\n\nFor **every** denormalized field, document:\n\n- [ ] **Source of truth** -- which table\u002Frecord is canonical?\n- [ ] **Update trigger** -- what code updates the copies when the source changes?\n- [ ] **Failure mode** -- what happens if the update fails halfway? How stale can it get?\n- [ ] **Repair mechanism** -- how do you detect and fix drift? Is there a reconciliation job?\n\nIf you can't fill this out, use a join instead.\n\n## Detection: When You're About to Denormalize\n\n**Stop and assess if you're about to write any of these:**\n\n1. **Copying a field from one table into another at write time** -- `order.customerName = customer.name`. What happens when the customer changes their name?\n\n2. **Embedding related objects in a document** -- `{ user: { org: { plan: { name: \"Pro\" } } } }`. Each nesting level is a copy that needs an update path.\n\n3. **Adding a column to avoid a join** -- storing `planName` on the user table so you don't have to join through organization. Who updates it when the org changes plans?\n\n4. **Building a cache blob from multiple tables** -- `buildFullProfile(userId)` that joins user + org + plan + settings into one cached object. What invalidates every field in that blob?\n\n5. **A background job that \"syncs\" data between tables** -- if this exists, you already have the trap. Is it idempotent? Does it handle partial failures?\n\n6. **`ON UPDATE CASCADE` or database triggers** -- these hide write amplification inside the database where application code can't see it.\n\n## The Default: Join at Read Time\n\n```typescript\n\u002F\u002F Prefer this. Single source of truth, always consistent.\nasync function getUserProfile(userId: string) {\n  return db.user.findUnique({\n    where: { id: userId },\n    include: {\n      organization: {\n        include: { plan: true },\n      },\n    },\n  });\n}\n```\n\nJoins are not inherently slow. A join on indexed foreign keys is fast. Measure before assuming you need to denormalize.\n\n## When Denormalization Is Justified\n\nDenormalize only when ALL of these are true:\n\n1. The join is a measured performance bottleneck (not a hypothetical one)\n2. The source data changes infrequently relative to reads\n3. You have an update path for every copy\n4. You have a repair mechanism for drift\n5. The acceptable staleness window is defined and documented\n\n## Patterns (When You Must Denormalize)\n\n### Materialized view (database-managed)\n\n```typescript\n\u002F\u002F The database owns the denormalization logic. Refresh is atomic.\nawait db.$executeRaw`\n  CREATE MATERIALIZED VIEW user_profiles AS\n  SELECT u.id, u.name, o.name as org_name, p.name as plan_name\n  FROM users u\n  JOIN organizations o ON u.org_id = o.id\n  JOIN plans p ON o.plan_id = p.id\n`;\n\u002F\u002F Refresh on schedule or after writes\nawait db.$executeRaw`REFRESH MATERIALIZED VIEW CONCURRENTLY user_profiles`;\n```\n\nStale between refreshes. But the logic is declarative, refresh is atomic, and you can't \"forget\" to update a copy.\n\n### Event-driven sync with repair job\n\n```typescript\n\u002F\u002F Update copies when the source changes\nasync function handleOrgPlanChanged(event: OrgPlanChangedEvent) {\n  const plan = await db.plan.findUnique({ where: { id: event.newPlanId } });\n  \u002F\u002F Batch update to avoid cardinality trap\n  let cursor: string | undefined;\n  do {\n    const users = await db.user.findMany({\n      where: { orgId: event.orgId }, take: 100,\n      cursor: cursor ? { id: cursor } : undefined,\n      orderBy: { id: \"asc\" },\n    });\n    for (const user of users) {\n      await db.user.update({\n        where: { id: user.id },\n        data: { cachedPlanName: plan.name },\n      });\n    }\n    cursor = users.length === 100 ? users[users.length - 1].id : undefined;\n  } while (cursor);\n}\n\n\u002F\u002F REPAIR: nightly job that detects and fixes drift\nasync function repairDenormalizedPlanNames() {\n  const drifted = await db.$queryRaw`\n    SELECT u.id, u.cached_plan_name, p.name as actual\n    FROM users u\n    JOIN organizations o ON u.org_id = o.id\n    JOIN plans p ON o.plan_id = p.id\n    WHERE u.cached_plan_name != p.name\n  `;\n  for (const row of drifted) {\n    await db.user.update({\n      where: { id: row.id },\n      data: { cachedPlanName: row.actual },\n    });\n    logger.warn(\"Repaired drifted plan name\", { userId: row.id });\n  }\n}\n```\n\nMore infrastructure. But failures are recoverable, drift is detectable, and the system self-heals.\n\n## Anti-Patterns\n\n```typescript\n\u002F\u002F Dangerous: copies org and plan data into every user record.\n\u002F\u002F Who updates orgName and planName when they change?\nreturn db.user.create({\n  data: {\n    name: data.name, email: data.email, orgId: data.orgId,\n    orgName: org.name,        \u002F\u002F copy -- consistency obligation\n    planName: plan.name,      \u002F\u002F copy -- consistency obligation\n    planFeatures: plan.features, \u002F\u002F copy -- consistency obligation\n  },\n});\n\n\u002F\u002F Dangerous: updates N tables with no failure handling.\n\u002F\u002F What if updateMany times out? What if it partially succeeds?\nasync function updateOrgName(orgId: string, newName: string) {\n  await db.organization.update({ where: { id: orgId }, data: { name: newName } });\n  await db.user.updateMany({ where: { orgId }, data: { orgName: newName } });\n  await db.invoice.updateMany({ where: { orgId }, data: { orgName: newName } });\n  await db.auditLog.updateMany({ where: { orgId }, data: { orgName: newName } });\n}\n```\n\n## Related Traps\n\n- **Cardinality** -- denormalization cost scales with the cardinality of the target collection. Denormalizing into an L4\u002FL5 set means write amplification proportional to its size.\n- **Cache Invalidation** -- caching a denormalized blob is double denormalization. The cache is a copy of a copy.\n- **Consistency Models** -- denormalized data is eventually consistent by nature. If your feature needs strong consistency, denormalization is the wrong tool.\n- **Idempotency** -- sync jobs that update denormalized copies must be idempotent, or partial failures leave data in an inconsistent state.\n",{"data":35,"body":36},{"name":4,"description":6},{"type":37,"children":38},"root",[39,48,60,67,86,111,116,122,134,204,209,215,223,324,330,602,607,613,618,646,652,659,797,802,808,1975,1980,1986,2748,2754,2797],{"type":40,"tag":41,"props":42,"children":44},"element","h1",{"id":43},"denormalization-trap",[45],{"type":46,"value":47},"text","Denormalization Trap",{"type":40,"tag":49,"props":50,"children":51},"p",{},[52,54],{"type":46,"value":53},"Every copy of data is a consistency obligation. Before duplicating a field to avoid a join, ask: ",{"type":40,"tag":55,"props":56,"children":57},"strong",{},[58],{"type":46,"value":59},"who updates this copy, what happens when the update fails, and how do you detect drift?",{"type":40,"tag":61,"props":62,"children":64},"h2",{"id":63},"snapshot-vs-reference",[65],{"type":46,"value":66},"Snapshot vs Reference",{"type":40,"tag":49,"props":68,"children":69},{},[70,72,77,79,84],{"type":46,"value":71},"The first question when copying data: is this a ",{"type":40,"tag":55,"props":73,"children":74},{},[75],{"type":46,"value":76},"snapshot",{"type":46,"value":78}," or a ",{"type":40,"tag":55,"props":80,"children":81},{},[82],{"type":46,"value":83},"reference",{"type":46,"value":85},"?",{"type":40,"tag":87,"props":88,"children":89},"ul",{},[90,101],{"type":40,"tag":91,"props":92,"children":93},"li",{},[94,99],{"type":40,"tag":55,"props":95,"children":96},{},[97],{"type":46,"value":98},"Snapshot:",{"type":46,"value":100}," The value at a point in time. Correct to copy. Example: the price on an order line item at time of purchase. The product price may change later, but the order price should not.",{"type":40,"tag":91,"props":102,"children":103},{},[104,109],{"type":40,"tag":55,"props":105,"children":106},{},[107],{"type":46,"value":108},"Reference:",{"type":46,"value":110}," The current value of something. Dangerous to copy. Example: a user's plan name stored on their profile. When the plan changes, every copy is wrong until updated.",{"type":40,"tag":49,"props":112,"children":113},{},[114],{"type":46,"value":115},"If you're copying a reference, you've taken on a consistency obligation. If you can't answer all four questions in the checklist below, don't denormalize.",{"type":40,"tag":61,"props":117,"children":119},{"id":118},"consistency-obligation-checklist",[120],{"type":46,"value":121},"Consistency Obligation Checklist",{"type":40,"tag":49,"props":123,"children":124},{},[125,127,132],{"type":46,"value":126},"For ",{"type":40,"tag":55,"props":128,"children":129},{},[130],{"type":46,"value":131},"every",{"type":46,"value":133}," denormalized field, document:",{"type":40,"tag":87,"props":135,"children":138},{"className":136},[137],"contains-task-list",[139,159,174,189],{"type":40,"tag":91,"props":140,"children":143},{"className":141},[142],"task-list-item",[144,150,152,157],{"type":40,"tag":145,"props":146,"children":149},"input",{"disabled":147,"type":148},true,"checkbox",[],{"type":46,"value":151}," ",{"type":40,"tag":55,"props":153,"children":154},{},[155],{"type":46,"value":156},"Source of truth",{"type":46,"value":158}," -- which table\u002Frecord is canonical?",{"type":40,"tag":91,"props":160,"children":162},{"className":161},[142],[163,166,167,172],{"type":40,"tag":145,"props":164,"children":165},{"disabled":147,"type":148},[],{"type":46,"value":151},{"type":40,"tag":55,"props":168,"children":169},{},[170],{"type":46,"value":171},"Update trigger",{"type":46,"value":173}," -- what code updates the copies when the source changes?",{"type":40,"tag":91,"props":175,"children":177},{"className":176},[142],[178,181,182,187],{"type":40,"tag":145,"props":179,"children":180},{"disabled":147,"type":148},[],{"type":46,"value":151},{"type":40,"tag":55,"props":183,"children":184},{},[185],{"type":46,"value":186},"Failure mode",{"type":46,"value":188}," -- what happens if the update fails halfway? How stale can it get?",{"type":40,"tag":91,"props":190,"children":192},{"className":191},[142],[193,196,197,202],{"type":40,"tag":145,"props":194,"children":195},{"disabled":147,"type":148},[],{"type":46,"value":151},{"type":40,"tag":55,"props":198,"children":199},{},[200],{"type":46,"value":201},"Repair mechanism",{"type":46,"value":203}," -- how do you detect and fix drift? Is there a reconciliation job?",{"type":40,"tag":49,"props":205,"children":206},{},[207],{"type":46,"value":208},"If you can't fill this out, use a join instead.",{"type":40,"tag":61,"props":210,"children":212},{"id":211},"detection-when-youre-about-to-denormalize",[213],{"type":46,"value":214},"Detection: When You're About to Denormalize",{"type":40,"tag":49,"props":216,"children":217},{},[218],{"type":40,"tag":55,"props":219,"children":220},{},[221],{"type":46,"value":222},"Stop and assess if you're about to write any of these:",{"type":40,"tag":224,"props":225,"children":226},"ol",{},[227,246,263,281,298,308],{"type":40,"tag":91,"props":228,"children":229},{},[230,235,237,244],{"type":40,"tag":55,"props":231,"children":232},{},[233],{"type":46,"value":234},"Copying a field from one table into another at write time",{"type":46,"value":236}," -- ",{"type":40,"tag":238,"props":239,"children":241},"code",{"className":240},[],[242],{"type":46,"value":243},"order.customerName = customer.name",{"type":46,"value":245},". What happens when the customer changes their name?",{"type":40,"tag":91,"props":247,"children":248},{},[249,254,255,261],{"type":40,"tag":55,"props":250,"children":251},{},[252],{"type":46,"value":253},"Embedding related objects in a document",{"type":46,"value":236},{"type":40,"tag":238,"props":256,"children":258},{"className":257},[],[259],{"type":46,"value":260},"{ user: { org: { plan: { name: \"Pro\" } } } }",{"type":46,"value":262},". Each nesting level is a copy that needs an update path.",{"type":40,"tag":91,"props":264,"children":265},{},[266,271,273,279],{"type":40,"tag":55,"props":267,"children":268},{},[269],{"type":46,"value":270},"Adding a column to avoid a join",{"type":46,"value":272}," -- storing ",{"type":40,"tag":238,"props":274,"children":276},{"className":275},[],[277],{"type":46,"value":278},"planName",{"type":46,"value":280}," on the user table so you don't have to join through organization. Who updates it when the org changes plans?",{"type":40,"tag":91,"props":282,"children":283},{},[284,289,290,296],{"type":40,"tag":55,"props":285,"children":286},{},[287],{"type":46,"value":288},"Building a cache blob from multiple tables",{"type":46,"value":236},{"type":40,"tag":238,"props":291,"children":293},{"className":292},[],[294],{"type":46,"value":295},"buildFullProfile(userId)",{"type":46,"value":297}," that joins user + org + plan + settings into one cached object. What invalidates every field in that blob?",{"type":40,"tag":91,"props":299,"children":300},{},[301,306],{"type":40,"tag":55,"props":302,"children":303},{},[304],{"type":46,"value":305},"A background job that \"syncs\" data between tables",{"type":46,"value":307}," -- if this exists, you already have the trap. Is it idempotent? Does it handle partial failures?",{"type":40,"tag":91,"props":309,"children":310},{},[311,322],{"type":40,"tag":55,"props":312,"children":313},{},[314,320],{"type":40,"tag":238,"props":315,"children":317},{"className":316},[],[318],{"type":46,"value":319},"ON UPDATE CASCADE",{"type":46,"value":321}," or database triggers",{"type":46,"value":323}," -- these hide write amplification inside the database where application code can't see it.",{"type":40,"tag":61,"props":325,"children":327},{"id":326},"the-default-join-at-read-time",[328],{"type":46,"value":329},"The Default: Join at Read Time",{"type":40,"tag":331,"props":332,"children":337},"pre",{"className":333,"code":334,"language":335,"meta":336,"style":336},"language-typescript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F Prefer this. Single source of truth, always consistent.\nasync function getUserProfile(userId: string) {\n  return db.user.findUnique({\n    where: { id: userId },\n    include: {\n      organization: {\n        include: { plan: true },\n      },\n    },\n  });\n}\n","typescript","",[338],{"type":40,"tag":238,"props":339,"children":340},{"__ignoreMap":336},[341,352,406,450,487,504,521,557,566,575,593],{"type":40,"tag":342,"props":343,"children":345},"span",{"class":344,"line":27},"line",[346],{"type":40,"tag":342,"props":347,"children":349},{"style":348},"--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic",[350],{"type":46,"value":351},"\u002F\u002F Prefer this. Single source of truth, always consistent.\n",{"type":40,"tag":342,"props":353,"children":355},{"class":344,"line":354},2,[356,362,367,373,379,385,390,396,401],{"type":40,"tag":342,"props":357,"children":359},{"style":358},"--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA",[360],{"type":46,"value":361},"async",{"type":40,"tag":342,"props":363,"children":364},{"style":358},[365],{"type":46,"value":366}," function",{"type":40,"tag":342,"props":368,"children":370},{"style":369},"--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF",[371],{"type":46,"value":372}," getUserProfile",{"type":40,"tag":342,"props":374,"children":376},{"style":375},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[377],{"type":46,"value":378},"(",{"type":40,"tag":342,"props":380,"children":382},{"style":381},"--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic",[383],{"type":46,"value":384},"userId",{"type":40,"tag":342,"props":386,"children":387},{"style":375},[388],{"type":46,"value":389},":",{"type":40,"tag":342,"props":391,"children":393},{"style":392},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[394],{"type":46,"value":395}," string",{"type":40,"tag":342,"props":397,"children":398},{"style":375},[399],{"type":46,"value":400},")",{"type":40,"tag":342,"props":402,"children":403},{"style":375},[404],{"type":46,"value":405}," {\n",{"type":40,"tag":342,"props":407,"children":408},{"class":344,"line":23},[409,415,421,426,431,435,440,445],{"type":40,"tag":342,"props":410,"children":412},{"style":411},"--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic",[413],{"type":46,"value":414},"  return",{"type":40,"tag":342,"props":416,"children":418},{"style":417},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[419],{"type":46,"value":420}," db",{"type":40,"tag":342,"props":422,"children":423},{"style":375},[424],{"type":46,"value":425},".",{"type":40,"tag":342,"props":427,"children":428},{"style":417},[429],{"type":46,"value":430},"user",{"type":40,"tag":342,"props":432,"children":433},{"style":375},[434],{"type":46,"value":425},{"type":40,"tag":342,"props":436,"children":437},{"style":369},[438],{"type":46,"value":439},"findUnique",{"type":40,"tag":342,"props":441,"children":443},{"style":442},"--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178",[444],{"type":46,"value":378},{"type":40,"tag":342,"props":446,"children":447},{"style":375},[448],{"type":46,"value":449},"{\n",{"type":40,"tag":342,"props":451,"children":453},{"class":344,"line":452},4,[454,459,463,468,473,477,482],{"type":40,"tag":342,"props":455,"children":456},{"style":442},[457],{"type":46,"value":458},"    where",{"type":40,"tag":342,"props":460,"children":461},{"style":375},[462],{"type":46,"value":389},{"type":40,"tag":342,"props":464,"children":465},{"style":375},[466],{"type":46,"value":467}," {",{"type":40,"tag":342,"props":469,"children":470},{"style":442},[471],{"type":46,"value":472}," id",{"type":40,"tag":342,"props":474,"children":475},{"style":375},[476],{"type":46,"value":389},{"type":40,"tag":342,"props":478,"children":479},{"style":417},[480],{"type":46,"value":481}," userId",{"type":40,"tag":342,"props":483,"children":484},{"style":375},[485],{"type":46,"value":486}," },\n",{"type":40,"tag":342,"props":488,"children":490},{"class":344,"line":489},5,[491,496,500],{"type":40,"tag":342,"props":492,"children":493},{"style":442},[494],{"type":46,"value":495},"    include",{"type":40,"tag":342,"props":497,"children":498},{"style":375},[499],{"type":46,"value":389},{"type":40,"tag":342,"props":501,"children":502},{"style":375},[503],{"type":46,"value":405},{"type":40,"tag":342,"props":505,"children":507},{"class":344,"line":506},6,[508,513,517],{"type":40,"tag":342,"props":509,"children":510},{"style":442},[511],{"type":46,"value":512},"      organization",{"type":40,"tag":342,"props":514,"children":515},{"style":375},[516],{"type":46,"value":389},{"type":40,"tag":342,"props":518,"children":519},{"style":375},[520],{"type":46,"value":405},{"type":40,"tag":342,"props":522,"children":524},{"class":344,"line":523},7,[525,530,534,538,543,547,553],{"type":40,"tag":342,"props":526,"children":527},{"style":442},[528],{"type":46,"value":529},"        include",{"type":40,"tag":342,"props":531,"children":532},{"style":375},[533],{"type":46,"value":389},{"type":40,"tag":342,"props":535,"children":536},{"style":375},[537],{"type":46,"value":467},{"type":40,"tag":342,"props":539,"children":540},{"style":442},[541],{"type":46,"value":542}," plan",{"type":40,"tag":342,"props":544,"children":545},{"style":375},[546],{"type":46,"value":389},{"type":40,"tag":342,"props":548,"children":550},{"style":549},"--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC",[551],{"type":46,"value":552}," true",{"type":40,"tag":342,"props":554,"children":555},{"style":375},[556],{"type":46,"value":486},{"type":40,"tag":342,"props":558,"children":560},{"class":344,"line":559},8,[561],{"type":40,"tag":342,"props":562,"children":563},{"style":375},[564],{"type":46,"value":565},"      },\n",{"type":40,"tag":342,"props":567,"children":569},{"class":344,"line":568},9,[570],{"type":40,"tag":342,"props":571,"children":572},{"style":375},[573],{"type":46,"value":574},"    },\n",{"type":40,"tag":342,"props":576,"children":578},{"class":344,"line":577},10,[579,584,588],{"type":40,"tag":342,"props":580,"children":581},{"style":375},[582],{"type":46,"value":583},"  }",{"type":40,"tag":342,"props":585,"children":586},{"style":442},[587],{"type":46,"value":400},{"type":40,"tag":342,"props":589,"children":590},{"style":375},[591],{"type":46,"value":592},";\n",{"type":40,"tag":342,"props":594,"children":596},{"class":344,"line":595},11,[597],{"type":40,"tag":342,"props":598,"children":599},{"style":375},[600],{"type":46,"value":601},"}\n",{"type":40,"tag":49,"props":603,"children":604},{},[605],{"type":46,"value":606},"Joins are not inherently slow. A join on indexed foreign keys is fast. Measure before assuming you need to denormalize.",{"type":40,"tag":61,"props":608,"children":610},{"id":609},"when-denormalization-is-justified",[611],{"type":46,"value":612},"When Denormalization Is Justified",{"type":40,"tag":49,"props":614,"children":615},{},[616],{"type":46,"value":617},"Denormalize only when ALL of these are true:",{"type":40,"tag":224,"props":619,"children":620},{},[621,626,631,636,641],{"type":40,"tag":91,"props":622,"children":623},{},[624],{"type":46,"value":625},"The join is a measured performance bottleneck (not a hypothetical one)",{"type":40,"tag":91,"props":627,"children":628},{},[629],{"type":46,"value":630},"The source data changes infrequently relative to reads",{"type":40,"tag":91,"props":632,"children":633},{},[634],{"type":46,"value":635},"You have an update path for every copy",{"type":40,"tag":91,"props":637,"children":638},{},[639],{"type":46,"value":640},"You have a repair mechanism for drift",{"type":40,"tag":91,"props":642,"children":643},{},[644],{"type":46,"value":645},"The acceptable staleness window is defined and documented",{"type":40,"tag":61,"props":647,"children":649},{"id":648},"patterns-when-you-must-denormalize",[650],{"type":46,"value":651},"Patterns (When You Must Denormalize)",{"type":40,"tag":653,"props":654,"children":656},"h3",{"id":655},"materialized-view-database-managed",[657],{"type":46,"value":658},"Materialized view (database-managed)",{"type":40,"tag":331,"props":660,"children":662},{"className":333,"code":661,"language":335,"meta":336,"style":336},"\u002F\u002F The database owns the denormalization logic. Refresh is atomic.\nawait db.$executeRaw`\n  CREATE MATERIALIZED VIEW user_profiles AS\n  SELECT u.id, u.name, o.name as org_name, p.name as plan_name\n  FROM users u\n  JOIN organizations o ON u.org_id = o.id\n  JOIN plans p ON o.plan_id = p.id\n`;\n\u002F\u002F Refresh on schedule or after writes\nawait db.$executeRaw`REFRESH MATERIALIZED VIEW CONCURRENTLY user_profiles`;\n",[663],{"type":40,"tag":238,"props":664,"children":665},{"__ignoreMap":336},[666,674,700,709,717,725,733,741,753,761],{"type":40,"tag":342,"props":667,"children":668},{"class":344,"line":27},[669],{"type":40,"tag":342,"props":670,"children":671},{"style":348},[672],{"type":46,"value":673},"\u002F\u002F The database owns the denormalization logic. Refresh is atomic.\n",{"type":40,"tag":342,"props":675,"children":676},{"class":344,"line":354},[677,682,686,690,695],{"type":40,"tag":342,"props":678,"children":679},{"style":411},[680],{"type":46,"value":681},"await",{"type":40,"tag":342,"props":683,"children":684},{"style":417},[685],{"type":46,"value":420},{"type":40,"tag":342,"props":687,"children":688},{"style":375},[689],{"type":46,"value":425},{"type":40,"tag":342,"props":691,"children":692},{"style":369},[693],{"type":46,"value":694},"$executeRaw",{"type":40,"tag":342,"props":696,"children":697},{"style":375},[698],{"type":46,"value":699},"`\n",{"type":40,"tag":342,"props":701,"children":702},{"class":344,"line":23},[703],{"type":40,"tag":342,"props":704,"children":706},{"style":705},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[707],{"type":46,"value":708},"  CREATE MATERIALIZED VIEW user_profiles AS\n",{"type":40,"tag":342,"props":710,"children":711},{"class":344,"line":452},[712],{"type":40,"tag":342,"props":713,"children":714},{"style":705},[715],{"type":46,"value":716},"  SELECT u.id, u.name, o.name as org_name, p.name as plan_name\n",{"type":40,"tag":342,"props":718,"children":719},{"class":344,"line":489},[720],{"type":40,"tag":342,"props":721,"children":722},{"style":705},[723],{"type":46,"value":724},"  FROM users u\n",{"type":40,"tag":342,"props":726,"children":727},{"class":344,"line":506},[728],{"type":40,"tag":342,"props":729,"children":730},{"style":705},[731],{"type":46,"value":732},"  JOIN organizations o ON u.org_id = o.id\n",{"type":40,"tag":342,"props":734,"children":735},{"class":344,"line":523},[736],{"type":40,"tag":342,"props":737,"children":738},{"style":705},[739],{"type":46,"value":740},"  JOIN plans p ON o.plan_id = p.id\n",{"type":40,"tag":342,"props":742,"children":743},{"class":344,"line":559},[744,749],{"type":40,"tag":342,"props":745,"children":746},{"style":375},[747],{"type":46,"value":748},"`",{"type":40,"tag":342,"props":750,"children":751},{"style":375},[752],{"type":46,"value":592},{"type":40,"tag":342,"props":754,"children":755},{"class":344,"line":568},[756],{"type":40,"tag":342,"props":757,"children":758},{"style":348},[759],{"type":46,"value":760},"\u002F\u002F Refresh on schedule or after writes\n",{"type":40,"tag":342,"props":762,"children":763},{"class":344,"line":577},[764,768,772,776,780,784,789,793],{"type":40,"tag":342,"props":765,"children":766},{"style":411},[767],{"type":46,"value":681},{"type":40,"tag":342,"props":769,"children":770},{"style":417},[771],{"type":46,"value":420},{"type":40,"tag":342,"props":773,"children":774},{"style":375},[775],{"type":46,"value":425},{"type":40,"tag":342,"props":777,"children":778},{"style":369},[779],{"type":46,"value":694},{"type":40,"tag":342,"props":781,"children":782},{"style":375},[783],{"type":46,"value":748},{"type":40,"tag":342,"props":785,"children":786},{"style":705},[787],{"type":46,"value":788},"REFRESH MATERIALIZED VIEW CONCURRENTLY user_profiles",{"type":40,"tag":342,"props":790,"children":791},{"style":375},[792],{"type":46,"value":748},{"type":40,"tag":342,"props":794,"children":795},{"style":375},[796],{"type":46,"value":592},{"type":40,"tag":49,"props":798,"children":799},{},[800],{"type":46,"value":801},"Stale between refreshes. But the logic is declarative, refresh is atomic, and you can't \"forget\" to update a copy.",{"type":40,"tag":653,"props":803,"children":805},{"id":804},"event-driven-sync-with-repair-job",[806],{"type":46,"value":807},"Event-driven sync with repair job",{"type":40,"tag":331,"props":809,"children":811},{"className":333,"code":810,"language":335,"meta":336,"style":336},"\u002F\u002F Update copies when the source changes\nasync function handleOrgPlanChanged(event: OrgPlanChangedEvent) {\n  const plan = await db.plan.findUnique({ where: { id: event.newPlanId } });\n  \u002F\u002F Batch update to avoid cardinality trap\n  let cursor: string | undefined;\n  do {\n    const users = await db.user.findMany({\n      where: { orgId: event.orgId }, take: 100,\n      cursor: cursor ? { id: cursor } : undefined,\n      orderBy: { id: \"asc\" },\n    });\n    for (const user of users) {\n      await db.user.update({\n        where: { id: user.id },\n        data: { cachedPlanName: plan.name },\n      });\n    }\n    cursor = users.length === 100 ? users[users.length - 1].id : undefined;\n  } while (cursor);\n}\n\n\u002F\u002F REPAIR: nightly job that detects and fixes drift\nasync function repairDenormalizedPlanNames() {\n  const drifted = await db.$queryRaw`\n    SELECT u.id, u.cached_plan_name, p.name as actual\n    FROM users u\n    JOIN organizations o ON u.org_id = o.id\n    JOIN plans p ON o.plan_id = p.id\n    WHERE u.cached_plan_name != p.name\n  `;\n  for (const row of drifted) {\n    await db.user.update({\n      where: { id: row.id },\n      data: { cachedPlanName: row.actual },\n    });\n    logger.warn(\"Repaired drifted plan name\", { userId: row.id });\n  }\n}\n",[812],{"type":40,"tag":238,"props":813,"children":814},{"__ignoreMap":336},[815,823,865,969,977,1012,1024,1074,1137,1188,1231,1247,1289,1327,1369,1412,1429,1438,1531,1561,1569,1578,1587,1613,1651,1660,1669,1678,1687,1696,1709,1747,1784,1824,1866,1882,1958,1967],{"type":40,"tag":342,"props":816,"children":817},{"class":344,"line":27},[818],{"type":40,"tag":342,"props":819,"children":820},{"style":348},[821],{"type":46,"value":822},"\u002F\u002F Update copies when the source changes\n",{"type":40,"tag":342,"props":824,"children":825},{"class":344,"line":354},[826,830,834,839,843,848,852,857,861],{"type":40,"tag":342,"props":827,"children":828},{"style":358},[829],{"type":46,"value":361},{"type":40,"tag":342,"props":831,"children":832},{"style":358},[833],{"type":46,"value":366},{"type":40,"tag":342,"props":835,"children":836},{"style":369},[837],{"type":46,"value":838}," handleOrgPlanChanged",{"type":40,"tag":342,"props":840,"children":841},{"style":375},[842],{"type":46,"value":378},{"type":40,"tag":342,"props":844,"children":845},{"style":381},[846],{"type":46,"value":847},"event",{"type":40,"tag":342,"props":849,"children":850},{"style":375},[851],{"type":46,"value":389},{"type":40,"tag":342,"props":853,"children":854},{"style":392},[855],{"type":46,"value":856}," OrgPlanChangedEvent",{"type":40,"tag":342,"props":858,"children":859},{"style":375},[860],{"type":46,"value":400},{"type":40,"tag":342,"props":862,"children":863},{"style":375},[864],{"type":46,"value":405},{"type":40,"tag":342,"props":866,"children":867},{"class":344,"line":23},[868,873,877,882,887,891,895,900,904,908,912,917,922,926,930,934,938,943,947,952,957,961,965],{"type":40,"tag":342,"props":869,"children":870},{"style":358},[871],{"type":46,"value":872},"  const",{"type":40,"tag":342,"props":874,"children":875},{"style":417},[876],{"type":46,"value":542},{"type":40,"tag":342,"props":878,"children":879},{"style":375},[880],{"type":46,"value":881}," =",{"type":40,"tag":342,"props":883,"children":884},{"style":411},[885],{"type":46,"value":886}," await",{"type":40,"tag":342,"props":888,"children":889},{"style":417},[890],{"type":46,"value":420},{"type":40,"tag":342,"props":892,"children":893},{"style":375},[894],{"type":46,"value":425},{"type":40,"tag":342,"props":896,"children":897},{"style":417},[898],{"type":46,"value":899},"plan",{"type":40,"tag":342,"props":901,"children":902},{"style":375},[903],{"type":46,"value":425},{"type":40,"tag":342,"props":905,"children":906},{"style":369},[907],{"type":46,"value":439},{"type":40,"tag":342,"props":909,"children":910},{"style":442},[911],{"type":46,"value":378},{"type":40,"tag":342,"props":913,"children":914},{"style":375},[915],{"type":46,"value":916},"{",{"type":40,"tag":342,"props":918,"children":919},{"style":442},[920],{"type":46,"value":921}," where",{"type":40,"tag":342,"props":923,"children":924},{"style":375},[925],{"type":46,"value":389},{"type":40,"tag":342,"props":927,"children":928},{"style":375},[929],{"type":46,"value":467},{"type":40,"tag":342,"props":931,"children":932},{"style":442},[933],{"type":46,"value":472},{"type":40,"tag":342,"props":935,"children":936},{"style":375},[937],{"type":46,"value":389},{"type":40,"tag":342,"props":939,"children":940},{"style":417},[941],{"type":46,"value":942}," event",{"type":40,"tag":342,"props":944,"children":945},{"style":375},[946],{"type":46,"value":425},{"type":40,"tag":342,"props":948,"children":949},{"style":417},[950],{"type":46,"value":951},"newPlanId",{"type":40,"tag":342,"props":953,"children":954},{"style":375},[955],{"type":46,"value":956}," }",{"type":40,"tag":342,"props":958,"children":959},{"style":375},[960],{"type":46,"value":956},{"type":40,"tag":342,"props":962,"children":963},{"style":442},[964],{"type":46,"value":400},{"type":40,"tag":342,"props":966,"children":967},{"style":375},[968],{"type":46,"value":592},{"type":40,"tag":342,"props":970,"children":971},{"class":344,"line":452},[972],{"type":40,"tag":342,"props":973,"children":974},{"style":348},[975],{"type":46,"value":976},"  \u002F\u002F Batch update to avoid cardinality trap\n",{"type":40,"tag":342,"props":978,"children":979},{"class":344,"line":489},[980,985,990,994,998,1003,1008],{"type":40,"tag":342,"props":981,"children":982},{"style":358},[983],{"type":46,"value":984},"  let",{"type":40,"tag":342,"props":986,"children":987},{"style":417},[988],{"type":46,"value":989}," cursor",{"type":40,"tag":342,"props":991,"children":992},{"style":375},[993],{"type":46,"value":389},{"type":40,"tag":342,"props":995,"children":996},{"style":392},[997],{"type":46,"value":395},{"type":40,"tag":342,"props":999,"children":1000},{"style":375},[1001],{"type":46,"value":1002}," |",{"type":40,"tag":342,"props":1004,"children":1005},{"style":392},[1006],{"type":46,"value":1007}," undefined",{"type":40,"tag":342,"props":1009,"children":1010},{"style":375},[1011],{"type":46,"value":592},{"type":40,"tag":342,"props":1013,"children":1014},{"class":344,"line":506},[1015,1020],{"type":40,"tag":342,"props":1016,"children":1017},{"style":411},[1018],{"type":46,"value":1019},"  do",{"type":40,"tag":342,"props":1021,"children":1022},{"style":375},[1023],{"type":46,"value":405},{"type":40,"tag":342,"props":1025,"children":1026},{"class":344,"line":523},[1027,1032,1037,1041,1045,1049,1053,1057,1061,1066,1070],{"type":40,"tag":342,"props":1028,"children":1029},{"style":358},[1030],{"type":46,"value":1031},"    const",{"type":40,"tag":342,"props":1033,"children":1034},{"style":417},[1035],{"type":46,"value":1036}," users",{"type":40,"tag":342,"props":1038,"children":1039},{"style":375},[1040],{"type":46,"value":881},{"type":40,"tag":342,"props":1042,"children":1043},{"style":411},[1044],{"type":46,"value":886},{"type":40,"tag":342,"props":1046,"children":1047},{"style":417},[1048],{"type":46,"value":420},{"type":40,"tag":342,"props":1050,"children":1051},{"style":375},[1052],{"type":46,"value":425},{"type":40,"tag":342,"props":1054,"children":1055},{"style":417},[1056],{"type":46,"value":430},{"type":40,"tag":342,"props":1058,"children":1059},{"style":375},[1060],{"type":46,"value":425},{"type":40,"tag":342,"props":1062,"children":1063},{"style":369},[1064],{"type":46,"value":1065},"findMany",{"type":40,"tag":342,"props":1067,"children":1068},{"style":442},[1069],{"type":46,"value":378},{"type":40,"tag":342,"props":1071,"children":1072},{"style":375},[1073],{"type":46,"value":449},{"type":40,"tag":342,"props":1075,"children":1076},{"class":344,"line":559},[1077,1082,1086,1090,1095,1099,1103,1107,1112,1117,1122,1126,1132],{"type":40,"tag":342,"props":1078,"children":1079},{"style":442},[1080],{"type":46,"value":1081},"      where",{"type":40,"tag":342,"props":1083,"children":1084},{"style":375},[1085],{"type":46,"value":389},{"type":40,"tag":342,"props":1087,"children":1088},{"style":375},[1089],{"type":46,"value":467},{"type":40,"tag":342,"props":1091,"children":1092},{"style":442},[1093],{"type":46,"value":1094}," orgId",{"type":40,"tag":342,"props":1096,"children":1097},{"style":375},[1098],{"type":46,"value":389},{"type":40,"tag":342,"props":1100,"children":1101},{"style":417},[1102],{"type":46,"value":942},{"type":40,"tag":342,"props":1104,"children":1105},{"style":375},[1106],{"type":46,"value":425},{"type":40,"tag":342,"props":1108,"children":1109},{"style":417},[1110],{"type":46,"value":1111},"orgId",{"type":40,"tag":342,"props":1113,"children":1114},{"style":375},[1115],{"type":46,"value":1116}," },",{"type":40,"tag":342,"props":1118,"children":1119},{"style":442},[1120],{"type":46,"value":1121}," take",{"type":40,"tag":342,"props":1123,"children":1124},{"style":375},[1125],{"type":46,"value":389},{"type":40,"tag":342,"props":1127,"children":1129},{"style":1128},"--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C",[1130],{"type":46,"value":1131}," 100",{"type":40,"tag":342,"props":1133,"children":1134},{"style":375},[1135],{"type":46,"value":1136},",\n",{"type":40,"tag":342,"props":1138,"children":1139},{"class":344,"line":568},[1140,1145,1149,1153,1158,1162,1166,1170,1174,1178,1183],{"type":40,"tag":342,"props":1141,"children":1142},{"style":442},[1143],{"type":46,"value":1144},"      cursor",{"type":40,"tag":342,"props":1146,"children":1147},{"style":375},[1148],{"type":46,"value":389},{"type":40,"tag":342,"props":1150,"children":1151},{"style":417},[1152],{"type":46,"value":989},{"type":40,"tag":342,"props":1154,"children":1155},{"style":375},[1156],{"type":46,"value":1157}," ?",{"type":40,"tag":342,"props":1159,"children":1160},{"style":375},[1161],{"type":46,"value":467},{"type":40,"tag":342,"props":1163,"children":1164},{"style":442},[1165],{"type":46,"value":472},{"type":40,"tag":342,"props":1167,"children":1168},{"style":375},[1169],{"type":46,"value":389},{"type":40,"tag":342,"props":1171,"children":1172},{"style":417},[1173],{"type":46,"value":989},{"type":40,"tag":342,"props":1175,"children":1176},{"style":375},[1177],{"type":46,"value":956},{"type":40,"tag":342,"props":1179,"children":1180},{"style":375},[1181],{"type":46,"value":1182}," :",{"type":40,"tag":342,"props":1184,"children":1185},{"style":375},[1186],{"type":46,"value":1187}," undefined,\n",{"type":40,"tag":342,"props":1189,"children":1190},{"class":344,"line":577},[1191,1196,1200,1204,1208,1212,1217,1222,1227],{"type":40,"tag":342,"props":1192,"children":1193},{"style":442},[1194],{"type":46,"value":1195},"      orderBy",{"type":40,"tag":342,"props":1197,"children":1198},{"style":375},[1199],{"type":46,"value":389},{"type":40,"tag":342,"props":1201,"children":1202},{"style":375},[1203],{"type":46,"value":467},{"type":40,"tag":342,"props":1205,"children":1206},{"style":442},[1207],{"type":46,"value":472},{"type":40,"tag":342,"props":1209,"children":1210},{"style":375},[1211],{"type":46,"value":389},{"type":40,"tag":342,"props":1213,"children":1214},{"style":375},[1215],{"type":46,"value":1216}," \"",{"type":40,"tag":342,"props":1218,"children":1219},{"style":705},[1220],{"type":46,"value":1221},"asc",{"type":40,"tag":342,"props":1223,"children":1224},{"style":375},[1225],{"type":46,"value":1226},"\"",{"type":40,"tag":342,"props":1228,"children":1229},{"style":375},[1230],{"type":46,"value":486},{"type":40,"tag":342,"props":1232,"children":1233},{"class":344,"line":595},[1234,1239,1243],{"type":40,"tag":342,"props":1235,"children":1236},{"style":375},[1237],{"type":46,"value":1238},"    }",{"type":40,"tag":342,"props":1240,"children":1241},{"style":442},[1242],{"type":46,"value":400},{"type":40,"tag":342,"props":1244,"children":1245},{"style":375},[1246],{"type":46,"value":592},{"type":40,"tag":342,"props":1248,"children":1250},{"class":344,"line":1249},12,[1251,1256,1261,1266,1271,1276,1280,1285],{"type":40,"tag":342,"props":1252,"children":1253},{"style":411},[1254],{"type":46,"value":1255},"    for",{"type":40,"tag":342,"props":1257,"children":1258},{"style":442},[1259],{"type":46,"value":1260}," (",{"type":40,"tag":342,"props":1262,"children":1263},{"style":358},[1264],{"type":46,"value":1265},"const",{"type":40,"tag":342,"props":1267,"children":1268},{"style":417},[1269],{"type":46,"value":1270}," user",{"type":40,"tag":342,"props":1272,"children":1273},{"style":375},[1274],{"type":46,"value":1275}," of",{"type":40,"tag":342,"props":1277,"children":1278},{"style":417},[1279],{"type":46,"value":1036},{"type":40,"tag":342,"props":1281,"children":1282},{"style":442},[1283],{"type":46,"value":1284},") ",{"type":40,"tag":342,"props":1286,"children":1287},{"style":375},[1288],{"type":46,"value":449},{"type":40,"tag":342,"props":1290,"children":1292},{"class":344,"line":1291},13,[1293,1298,1302,1306,1310,1314,1319,1323],{"type":40,"tag":342,"props":1294,"children":1295},{"style":411},[1296],{"type":46,"value":1297},"      await",{"type":40,"tag":342,"props":1299,"children":1300},{"style":417},[1301],{"type":46,"value":420},{"type":40,"tag":342,"props":1303,"children":1304},{"style":375},[1305],{"type":46,"value":425},{"type":40,"tag":342,"props":1307,"children":1308},{"style":417},[1309],{"type":46,"value":430},{"type":40,"tag":342,"props":1311,"children":1312},{"style":375},[1313],{"type":46,"value":425},{"type":40,"tag":342,"props":1315,"children":1316},{"style":369},[1317],{"type":46,"value":1318},"update",{"type":40,"tag":342,"props":1320,"children":1321},{"style":442},[1322],{"type":46,"value":378},{"type":40,"tag":342,"props":1324,"children":1325},{"style":375},[1326],{"type":46,"value":449},{"type":40,"tag":342,"props":1328,"children":1330},{"class":344,"line":1329},14,[1331,1336,1340,1344,1348,1352,1356,1360,1365],{"type":40,"tag":342,"props":1332,"children":1333},{"style":442},[1334],{"type":46,"value":1335},"        where",{"type":40,"tag":342,"props":1337,"children":1338},{"style":375},[1339],{"type":46,"value":389},{"type":40,"tag":342,"props":1341,"children":1342},{"style":375},[1343],{"type":46,"value":467},{"type":40,"tag":342,"props":1345,"children":1346},{"style":442},[1347],{"type":46,"value":472},{"type":40,"tag":342,"props":1349,"children":1350},{"style":375},[1351],{"type":46,"value":389},{"type":40,"tag":342,"props":1353,"children":1354},{"style":417},[1355],{"type":46,"value":1270},{"type":40,"tag":342,"props":1357,"children":1358},{"style":375},[1359],{"type":46,"value":425},{"type":40,"tag":342,"props":1361,"children":1362},{"style":417},[1363],{"type":46,"value":1364},"id",{"type":40,"tag":342,"props":1366,"children":1367},{"style":375},[1368],{"type":46,"value":486},{"type":40,"tag":342,"props":1370,"children":1372},{"class":344,"line":1371},15,[1373,1378,1382,1386,1391,1395,1399,1403,1408],{"type":40,"tag":342,"props":1374,"children":1375},{"style":442},[1376],{"type":46,"value":1377},"        data",{"type":40,"tag":342,"props":1379,"children":1380},{"style":375},[1381],{"type":46,"value":389},{"type":40,"tag":342,"props":1383,"children":1384},{"style":375},[1385],{"type":46,"value":467},{"type":40,"tag":342,"props":1387,"children":1388},{"style":442},[1389],{"type":46,"value":1390}," cachedPlanName",{"type":40,"tag":342,"props":1392,"children":1393},{"style":375},[1394],{"type":46,"value":389},{"type":40,"tag":342,"props":1396,"children":1397},{"style":417},[1398],{"type":46,"value":542},{"type":40,"tag":342,"props":1400,"children":1401},{"style":375},[1402],{"type":46,"value":425},{"type":40,"tag":342,"props":1404,"children":1405},{"style":417},[1406],{"type":46,"value":1407},"name",{"type":40,"tag":342,"props":1409,"children":1410},{"style":375},[1411],{"type":46,"value":486},{"type":40,"tag":342,"props":1413,"children":1415},{"class":344,"line":1414},16,[1416,1421,1425],{"type":40,"tag":342,"props":1417,"children":1418},{"style":375},[1419],{"type":46,"value":1420},"      }",{"type":40,"tag":342,"props":1422,"children":1423},{"style":442},[1424],{"type":46,"value":400},{"type":40,"tag":342,"props":1426,"children":1427},{"style":375},[1428],{"type":46,"value":592},{"type":40,"tag":342,"props":1430,"children":1432},{"class":344,"line":1431},17,[1433],{"type":40,"tag":342,"props":1434,"children":1435},{"style":375},[1436],{"type":46,"value":1437},"    }\n",{"type":40,"tag":342,"props":1439,"children":1441},{"class":344,"line":1440},18,[1442,1447,1451,1455,1459,1464,1469,1473,1477,1481,1486,1491,1495,1499,1504,1509,1514,1518,1522,1526],{"type":40,"tag":342,"props":1443,"children":1444},{"style":417},[1445],{"type":46,"value":1446},"    cursor",{"type":40,"tag":342,"props":1448,"children":1449},{"style":375},[1450],{"type":46,"value":881},{"type":40,"tag":342,"props":1452,"children":1453},{"style":417},[1454],{"type":46,"value":1036},{"type":40,"tag":342,"props":1456,"children":1457},{"style":375},[1458],{"type":46,"value":425},{"type":40,"tag":342,"props":1460,"children":1461},{"style":417},[1462],{"type":46,"value":1463},"length",{"type":40,"tag":342,"props":1465,"children":1466},{"style":375},[1467],{"type":46,"value":1468}," ===",{"type":40,"tag":342,"props":1470,"children":1471},{"style":1128},[1472],{"type":46,"value":1131},{"type":40,"tag":342,"props":1474,"children":1475},{"style":375},[1476],{"type":46,"value":1157},{"type":40,"tag":342,"props":1478,"children":1479},{"style":417},[1480],{"type":46,"value":1036},{"type":40,"tag":342,"props":1482,"children":1483},{"style":442},[1484],{"type":46,"value":1485},"[",{"type":40,"tag":342,"props":1487,"children":1488},{"style":417},[1489],{"type":46,"value":1490},"users",{"type":40,"tag":342,"props":1492,"children":1493},{"style":375},[1494],{"type":46,"value":425},{"type":40,"tag":342,"props":1496,"children":1497},{"style":417},[1498],{"type":46,"value":1463},{"type":40,"tag":342,"props":1500,"children":1501},{"style":375},[1502],{"type":46,"value":1503}," -",{"type":40,"tag":342,"props":1505,"children":1506},{"style":1128},[1507],{"type":46,"value":1508}," 1",{"type":40,"tag":342,"props":1510,"children":1511},{"style":442},[1512],{"type":46,"value":1513},"]",{"type":40,"tag":342,"props":1515,"children":1516},{"style":375},[1517],{"type":46,"value":425},{"type":40,"tag":342,"props":1519,"children":1520},{"style":417},[1521],{"type":46,"value":1364},{"type":40,"tag":342,"props":1523,"children":1524},{"style":375},[1525],{"type":46,"value":1182},{"type":40,"tag":342,"props":1527,"children":1528},{"style":375},[1529],{"type":46,"value":1530}," undefined;\n",{"type":40,"tag":342,"props":1532,"children":1534},{"class":344,"line":1533},19,[1535,1539,1544,1548,1553,1557],{"type":40,"tag":342,"props":1536,"children":1537},{"style":375},[1538],{"type":46,"value":583},{"type":40,"tag":342,"props":1540,"children":1541},{"style":411},[1542],{"type":46,"value":1543}," while",{"type":40,"tag":342,"props":1545,"children":1546},{"style":442},[1547],{"type":46,"value":1260},{"type":40,"tag":342,"props":1549,"children":1550},{"style":417},[1551],{"type":46,"value":1552},"cursor",{"type":40,"tag":342,"props":1554,"children":1555},{"style":442},[1556],{"type":46,"value":400},{"type":40,"tag":342,"props":1558,"children":1559},{"style":375},[1560],{"type":46,"value":592},{"type":40,"tag":342,"props":1562,"children":1564},{"class":344,"line":1563},20,[1565],{"type":40,"tag":342,"props":1566,"children":1567},{"style":375},[1568],{"type":46,"value":601},{"type":40,"tag":342,"props":1570,"children":1572},{"class":344,"line":1571},21,[1573],{"type":40,"tag":342,"props":1574,"children":1575},{"emptyLinePlaceholder":147},[1576],{"type":46,"value":1577},"\n",{"type":40,"tag":342,"props":1579,"children":1581},{"class":344,"line":1580},22,[1582],{"type":40,"tag":342,"props":1583,"children":1584},{"style":348},[1585],{"type":46,"value":1586},"\u002F\u002F REPAIR: nightly job that detects and fixes drift\n",{"type":40,"tag":342,"props":1588,"children":1590},{"class":344,"line":1589},23,[1591,1595,1599,1604,1609],{"type":40,"tag":342,"props":1592,"children":1593},{"style":358},[1594],{"type":46,"value":361},{"type":40,"tag":342,"props":1596,"children":1597},{"style":358},[1598],{"type":46,"value":366},{"type":40,"tag":342,"props":1600,"children":1601},{"style":369},[1602],{"type":46,"value":1603}," repairDenormalizedPlanNames",{"type":40,"tag":342,"props":1605,"children":1606},{"style":375},[1607],{"type":46,"value":1608},"()",{"type":40,"tag":342,"props":1610,"children":1611},{"style":375},[1612],{"type":46,"value":405},{"type":40,"tag":342,"props":1614,"children":1616},{"class":344,"line":1615},24,[1617,1621,1626,1630,1634,1638,1642,1647],{"type":40,"tag":342,"props":1618,"children":1619},{"style":358},[1620],{"type":46,"value":872},{"type":40,"tag":342,"props":1622,"children":1623},{"style":417},[1624],{"type":46,"value":1625}," drifted",{"type":40,"tag":342,"props":1627,"children":1628},{"style":375},[1629],{"type":46,"value":881},{"type":40,"tag":342,"props":1631,"children":1632},{"style":411},[1633],{"type":46,"value":886},{"type":40,"tag":342,"props":1635,"children":1636},{"style":417},[1637],{"type":46,"value":420},{"type":40,"tag":342,"props":1639,"children":1640},{"style":375},[1641],{"type":46,"value":425},{"type":40,"tag":342,"props":1643,"children":1644},{"style":369},[1645],{"type":46,"value":1646},"$queryRaw",{"type":40,"tag":342,"props":1648,"children":1649},{"style":375},[1650],{"type":46,"value":699},{"type":40,"tag":342,"props":1652,"children":1654},{"class":344,"line":1653},25,[1655],{"type":40,"tag":342,"props":1656,"children":1657},{"style":705},[1658],{"type":46,"value":1659},"    SELECT u.id, u.cached_plan_name, p.name as actual\n",{"type":40,"tag":342,"props":1661,"children":1663},{"class":344,"line":1662},26,[1664],{"type":40,"tag":342,"props":1665,"children":1666},{"style":705},[1667],{"type":46,"value":1668},"    FROM users u\n",{"type":40,"tag":342,"props":1670,"children":1672},{"class":344,"line":1671},27,[1673],{"type":40,"tag":342,"props":1674,"children":1675},{"style":705},[1676],{"type":46,"value":1677},"    JOIN organizations o ON u.org_id = o.id\n",{"type":40,"tag":342,"props":1679,"children":1681},{"class":344,"line":1680},28,[1682],{"type":40,"tag":342,"props":1683,"children":1684},{"style":705},[1685],{"type":46,"value":1686},"    JOIN plans p ON o.plan_id = p.id\n",{"type":40,"tag":342,"props":1688,"children":1690},{"class":344,"line":1689},29,[1691],{"type":40,"tag":342,"props":1692,"children":1693},{"style":705},[1694],{"type":46,"value":1695},"    WHERE u.cached_plan_name != p.name\n",{"type":40,"tag":342,"props":1697,"children":1699},{"class":344,"line":1698},30,[1700,1705],{"type":40,"tag":342,"props":1701,"children":1702},{"style":375},[1703],{"type":46,"value":1704},"  `",{"type":40,"tag":342,"props":1706,"children":1707},{"style":375},[1708],{"type":46,"value":592},{"type":40,"tag":342,"props":1710,"children":1712},{"class":344,"line":1711},31,[1713,1718,1722,1726,1731,1735,1739,1743],{"type":40,"tag":342,"props":1714,"children":1715},{"style":411},[1716],{"type":46,"value":1717},"  for",{"type":40,"tag":342,"props":1719,"children":1720},{"style":442},[1721],{"type":46,"value":1260},{"type":40,"tag":342,"props":1723,"children":1724},{"style":358},[1725],{"type":46,"value":1265},{"type":40,"tag":342,"props":1727,"children":1728},{"style":417},[1729],{"type":46,"value":1730}," row",{"type":40,"tag":342,"props":1732,"children":1733},{"style":375},[1734],{"type":46,"value":1275},{"type":40,"tag":342,"props":1736,"children":1737},{"style":417},[1738],{"type":46,"value":1625},{"type":40,"tag":342,"props":1740,"children":1741},{"style":442},[1742],{"type":46,"value":1284},{"type":40,"tag":342,"props":1744,"children":1745},{"style":375},[1746],{"type":46,"value":449},{"type":40,"tag":342,"props":1748,"children":1750},{"class":344,"line":1749},32,[1751,1756,1760,1764,1768,1772,1776,1780],{"type":40,"tag":342,"props":1752,"children":1753},{"style":411},[1754],{"type":46,"value":1755},"    await",{"type":40,"tag":342,"props":1757,"children":1758},{"style":417},[1759],{"type":46,"value":420},{"type":40,"tag":342,"props":1761,"children":1762},{"style":375},[1763],{"type":46,"value":425},{"type":40,"tag":342,"props":1765,"children":1766},{"style":417},[1767],{"type":46,"value":430},{"type":40,"tag":342,"props":1769,"children":1770},{"style":375},[1771],{"type":46,"value":425},{"type":40,"tag":342,"props":1773,"children":1774},{"style":369},[1775],{"type":46,"value":1318},{"type":40,"tag":342,"props":1777,"children":1778},{"style":442},[1779],{"type":46,"value":378},{"type":40,"tag":342,"props":1781,"children":1782},{"style":375},[1783],{"type":46,"value":449},{"type":40,"tag":342,"props":1785,"children":1787},{"class":344,"line":1786},33,[1788,1792,1796,1800,1804,1808,1812,1816,1820],{"type":40,"tag":342,"props":1789,"children":1790},{"style":442},[1791],{"type":46,"value":1081},{"type":40,"tag":342,"props":1793,"children":1794},{"style":375},[1795],{"type":46,"value":389},{"type":40,"tag":342,"props":1797,"children":1798},{"style":375},[1799],{"type":46,"value":467},{"type":40,"tag":342,"props":1801,"children":1802},{"style":442},[1803],{"type":46,"value":472},{"type":40,"tag":342,"props":1805,"children":1806},{"style":375},[1807],{"type":46,"value":389},{"type":40,"tag":342,"props":1809,"children":1810},{"style":417},[1811],{"type":46,"value":1730},{"type":40,"tag":342,"props":1813,"children":1814},{"style":375},[1815],{"type":46,"value":425},{"type":40,"tag":342,"props":1817,"children":1818},{"style":417},[1819],{"type":46,"value":1364},{"type":40,"tag":342,"props":1821,"children":1822},{"style":375},[1823],{"type":46,"value":486},{"type":40,"tag":342,"props":1825,"children":1827},{"class":344,"line":1826},34,[1828,1833,1837,1841,1845,1849,1853,1857,1862],{"type":40,"tag":342,"props":1829,"children":1830},{"style":442},[1831],{"type":46,"value":1832},"      data",{"type":40,"tag":342,"props":1834,"children":1835},{"style":375},[1836],{"type":46,"value":389},{"type":40,"tag":342,"props":1838,"children":1839},{"style":375},[1840],{"type":46,"value":467},{"type":40,"tag":342,"props":1842,"children":1843},{"style":442},[1844],{"type":46,"value":1390},{"type":40,"tag":342,"props":1846,"children":1847},{"style":375},[1848],{"type":46,"value":389},{"type":40,"tag":342,"props":1850,"children":1851},{"style":417},[1852],{"type":46,"value":1730},{"type":40,"tag":342,"props":1854,"children":1855},{"style":375},[1856],{"type":46,"value":425},{"type":40,"tag":342,"props":1858,"children":1859},{"style":417},[1860],{"type":46,"value":1861},"actual",{"type":40,"tag":342,"props":1863,"children":1864},{"style":375},[1865],{"type":46,"value":486},{"type":40,"tag":342,"props":1867,"children":1869},{"class":344,"line":1868},35,[1870,1874,1878],{"type":40,"tag":342,"props":1871,"children":1872},{"style":375},[1873],{"type":46,"value":1238},{"type":40,"tag":342,"props":1875,"children":1876},{"style":442},[1877],{"type":46,"value":400},{"type":40,"tag":342,"props":1879,"children":1880},{"style":375},[1881],{"type":46,"value":592},{"type":40,"tag":342,"props":1883,"children":1885},{"class":344,"line":1884},36,[1886,1891,1895,1900,1904,1908,1913,1917,1922,1926,1930,1934,1938,1942,1946,1950,1954],{"type":40,"tag":342,"props":1887,"children":1888},{"style":417},[1889],{"type":46,"value":1890},"    logger",{"type":40,"tag":342,"props":1892,"children":1893},{"style":375},[1894],{"type":46,"value":425},{"type":40,"tag":342,"props":1896,"children":1897},{"style":369},[1898],{"type":46,"value":1899},"warn",{"type":40,"tag":342,"props":1901,"children":1902},{"style":442},[1903],{"type":46,"value":378},{"type":40,"tag":342,"props":1905,"children":1906},{"style":375},[1907],{"type":46,"value":1226},{"type":40,"tag":342,"props":1909,"children":1910},{"style":705},[1911],{"type":46,"value":1912},"Repaired drifted plan name",{"type":40,"tag":342,"props":1914,"children":1915},{"style":375},[1916],{"type":46,"value":1226},{"type":40,"tag":342,"props":1918,"children":1919},{"style":375},[1920],{"type":46,"value":1921},",",{"type":40,"tag":342,"props":1923,"children":1924},{"style":375},[1925],{"type":46,"value":467},{"type":40,"tag":342,"props":1927,"children":1928},{"style":442},[1929],{"type":46,"value":481},{"type":40,"tag":342,"props":1931,"children":1932},{"style":375},[1933],{"type":46,"value":389},{"type":40,"tag":342,"props":1935,"children":1936},{"style":417},[1937],{"type":46,"value":1730},{"type":40,"tag":342,"props":1939,"children":1940},{"style":375},[1941],{"type":46,"value":425},{"type":40,"tag":342,"props":1943,"children":1944},{"style":417},[1945],{"type":46,"value":1364},{"type":40,"tag":342,"props":1947,"children":1948},{"style":375},[1949],{"type":46,"value":956},{"type":40,"tag":342,"props":1951,"children":1952},{"style":442},[1953],{"type":46,"value":400},{"type":40,"tag":342,"props":1955,"children":1956},{"style":375},[1957],{"type":46,"value":592},{"type":40,"tag":342,"props":1959,"children":1961},{"class":344,"line":1960},37,[1962],{"type":40,"tag":342,"props":1963,"children":1964},{"style":375},[1965],{"type":46,"value":1966},"  }\n",{"type":40,"tag":342,"props":1968,"children":1970},{"class":344,"line":1969},38,[1971],{"type":40,"tag":342,"props":1972,"children":1973},{"style":375},[1974],{"type":46,"value":601},{"type":40,"tag":49,"props":1976,"children":1977},{},[1978],{"type":46,"value":1979},"More infrastructure. But failures are recoverable, drift is detectable, and the system self-heals.",{"type":40,"tag":61,"props":1981,"children":1983},{"id":1982},"anti-patterns",[1984],{"type":46,"value":1985},"Anti-Patterns",{"type":40,"tag":331,"props":1987,"children":1989},{"className":333,"code":1988,"language":335,"meta":336,"style":336},"\u002F\u002F Dangerous: copies org and plan data into every user record.\n\u002F\u002F Who updates orgName and planName when they change?\nreturn db.user.create({\n  data: {\n    name: data.name, email: data.email, orgId: data.orgId,\n    orgName: org.name,        \u002F\u002F copy -- consistency obligation\n    planName: plan.name,      \u002F\u002F copy -- consistency obligation\n    planFeatures: plan.features, \u002F\u002F copy -- consistency obligation\n  },\n});\n\n\u002F\u002F Dangerous: updates N tables with no failure handling.\n\u002F\u002F What if updateMany times out? What if it partially succeeds?\nasync function updateOrgName(orgId: string, newName: string) {\n  await db.organization.update({ where: { id: orgId }, data: { name: newName } });\n  await db.user.updateMany({ where: { orgId }, data: { orgName: newName } });\n  await db.invoice.updateMany({ where: { orgId }, data: { orgName: newName } });\n  await db.auditLog.updateMany({ where: { orgId }, data: { orgName: newName } });\n}\n",[1990],{"type":40,"tag":238,"props":1991,"children":1992},{"__ignoreMap":336},[1993,2001,2009,2046,2062,2141,2175,2208,2242,2250,2266,2273,2281,2289,2346,2452,2549,2645,2741],{"type":40,"tag":342,"props":1994,"children":1995},{"class":344,"line":27},[1996],{"type":40,"tag":342,"props":1997,"children":1998},{"style":348},[1999],{"type":46,"value":2000},"\u002F\u002F Dangerous: copies org and plan data into every user record.\n",{"type":40,"tag":342,"props":2002,"children":2003},{"class":344,"line":354},[2004],{"type":40,"tag":342,"props":2005,"children":2006},{"style":348},[2007],{"type":46,"value":2008},"\u002F\u002F Who updates orgName and planName when they change?\n",{"type":40,"tag":342,"props":2010,"children":2011},{"class":344,"line":23},[2012,2017,2021,2025,2029,2033,2038,2042],{"type":40,"tag":342,"props":2013,"children":2014},{"style":411},[2015],{"type":46,"value":2016},"return",{"type":40,"tag":342,"props":2018,"children":2019},{"style":417},[2020],{"type":46,"value":420},{"type":40,"tag":342,"props":2022,"children":2023},{"style":375},[2024],{"type":46,"value":425},{"type":40,"tag":342,"props":2026,"children":2027},{"style":417},[2028],{"type":46,"value":430},{"type":40,"tag":342,"props":2030,"children":2031},{"style":375},[2032],{"type":46,"value":425},{"type":40,"tag":342,"props":2034,"children":2035},{"style":369},[2036],{"type":46,"value":2037},"create",{"type":40,"tag":342,"props":2039,"children":2040},{"style":417},[2041],{"type":46,"value":378},{"type":40,"tag":342,"props":2043,"children":2044},{"style":375},[2045],{"type":46,"value":449},{"type":40,"tag":342,"props":2047,"children":2048},{"class":344,"line":452},[2049,2054,2058],{"type":40,"tag":342,"props":2050,"children":2051},{"style":442},[2052],{"type":46,"value":2053},"  data",{"type":40,"tag":342,"props":2055,"children":2056},{"style":375},[2057],{"type":46,"value":389},{"type":40,"tag":342,"props":2059,"children":2060},{"style":375},[2061],{"type":46,"value":405},{"type":40,"tag":342,"props":2063,"children":2064},{"class":344,"line":489},[2065,2070,2074,2079,2083,2087,2091,2096,2100,2104,2108,2113,2117,2121,2125,2129,2133,2137],{"type":40,"tag":342,"props":2066,"children":2067},{"style":442},[2068],{"type":46,"value":2069},"    name",{"type":40,"tag":342,"props":2071,"children":2072},{"style":375},[2073],{"type":46,"value":389},{"type":40,"tag":342,"props":2075,"children":2076},{"style":417},[2077],{"type":46,"value":2078}," data",{"type":40,"tag":342,"props":2080,"children":2081},{"style":375},[2082],{"type":46,"value":425},{"type":40,"tag":342,"props":2084,"children":2085},{"style":417},[2086],{"type":46,"value":1407},{"type":40,"tag":342,"props":2088,"children":2089},{"style":375},[2090],{"type":46,"value":1921},{"type":40,"tag":342,"props":2092,"children":2093},{"style":442},[2094],{"type":46,"value":2095}," email",{"type":40,"tag":342,"props":2097,"children":2098},{"style":375},[2099],{"type":46,"value":389},{"type":40,"tag":342,"props":2101,"children":2102},{"style":417},[2103],{"type":46,"value":2078},{"type":40,"tag":342,"props":2105,"children":2106},{"style":375},[2107],{"type":46,"value":425},{"type":40,"tag":342,"props":2109,"children":2110},{"style":417},[2111],{"type":46,"value":2112},"email",{"type":40,"tag":342,"props":2114,"children":2115},{"style":375},[2116],{"type":46,"value":1921},{"type":40,"tag":342,"props":2118,"children":2119},{"style":442},[2120],{"type":46,"value":1094},{"type":40,"tag":342,"props":2122,"children":2123},{"style":375},[2124],{"type":46,"value":389},{"type":40,"tag":342,"props":2126,"children":2127},{"style":417},[2128],{"type":46,"value":2078},{"type":40,"tag":342,"props":2130,"children":2131},{"style":375},[2132],{"type":46,"value":425},{"type":40,"tag":342,"props":2134,"children":2135},{"style":417},[2136],{"type":46,"value":1111},{"type":40,"tag":342,"props":2138,"children":2139},{"style":375},[2140],{"type":46,"value":1136},{"type":40,"tag":342,"props":2142,"children":2143},{"class":344,"line":506},[2144,2149,2153,2158,2162,2166,2170],{"type":40,"tag":342,"props":2145,"children":2146},{"style":442},[2147],{"type":46,"value":2148},"    orgName",{"type":40,"tag":342,"props":2150,"children":2151},{"style":375},[2152],{"type":46,"value":389},{"type":40,"tag":342,"props":2154,"children":2155},{"style":417},[2156],{"type":46,"value":2157}," org",{"type":40,"tag":342,"props":2159,"children":2160},{"style":375},[2161],{"type":46,"value":425},{"type":40,"tag":342,"props":2163,"children":2164},{"style":417},[2165],{"type":46,"value":1407},{"type":40,"tag":342,"props":2167,"children":2168},{"style":375},[2169],{"type":46,"value":1921},{"type":40,"tag":342,"props":2171,"children":2172},{"style":348},[2173],{"type":46,"value":2174},"        \u002F\u002F copy -- consistency obligation\n",{"type":40,"tag":342,"props":2176,"children":2177},{"class":344,"line":523},[2178,2183,2187,2191,2195,2199,2203],{"type":40,"tag":342,"props":2179,"children":2180},{"style":442},[2181],{"type":46,"value":2182},"    planName",{"type":40,"tag":342,"props":2184,"children":2185},{"style":375},[2186],{"type":46,"value":389},{"type":40,"tag":342,"props":2188,"children":2189},{"style":417},[2190],{"type":46,"value":542},{"type":40,"tag":342,"props":2192,"children":2193},{"style":375},[2194],{"type":46,"value":425},{"type":40,"tag":342,"props":2196,"children":2197},{"style":417},[2198],{"type":46,"value":1407},{"type":40,"tag":342,"props":2200,"children":2201},{"style":375},[2202],{"type":46,"value":1921},{"type":40,"tag":342,"props":2204,"children":2205},{"style":348},[2206],{"type":46,"value":2207},"      \u002F\u002F copy -- consistency obligation\n",{"type":40,"tag":342,"props":2209,"children":2210},{"class":344,"line":559},[2211,2216,2220,2224,2228,2233,2237],{"type":40,"tag":342,"props":2212,"children":2213},{"style":442},[2214],{"type":46,"value":2215},"    planFeatures",{"type":40,"tag":342,"props":2217,"children":2218},{"style":375},[2219],{"type":46,"value":389},{"type":40,"tag":342,"props":2221,"children":2222},{"style":417},[2223],{"type":46,"value":542},{"type":40,"tag":342,"props":2225,"children":2226},{"style":375},[2227],{"type":46,"value":425},{"type":40,"tag":342,"props":2229,"children":2230},{"style":417},[2231],{"type":46,"value":2232},"features",{"type":40,"tag":342,"props":2234,"children":2235},{"style":375},[2236],{"type":46,"value":1921},{"type":40,"tag":342,"props":2238,"children":2239},{"style":348},[2240],{"type":46,"value":2241}," \u002F\u002F copy -- consistency obligation\n",{"type":40,"tag":342,"props":2243,"children":2244},{"class":344,"line":568},[2245],{"type":40,"tag":342,"props":2246,"children":2247},{"style":375},[2248],{"type":46,"value":2249},"  },\n",{"type":40,"tag":342,"props":2251,"children":2252},{"class":344,"line":577},[2253,2258,2262],{"type":40,"tag":342,"props":2254,"children":2255},{"style":375},[2256],{"type":46,"value":2257},"}",{"type":40,"tag":342,"props":2259,"children":2260},{"style":417},[2261],{"type":46,"value":400},{"type":40,"tag":342,"props":2263,"children":2264},{"style":375},[2265],{"type":46,"value":592},{"type":40,"tag":342,"props":2267,"children":2268},{"class":344,"line":595},[2269],{"type":40,"tag":342,"props":2270,"children":2271},{"emptyLinePlaceholder":147},[2272],{"type":46,"value":1577},{"type":40,"tag":342,"props":2274,"children":2275},{"class":344,"line":1249},[2276],{"type":40,"tag":342,"props":2277,"children":2278},{"style":348},[2279],{"type":46,"value":2280},"\u002F\u002F Dangerous: updates N tables with no failure handling.\n",{"type":40,"tag":342,"props":2282,"children":2283},{"class":344,"line":1291},[2284],{"type":40,"tag":342,"props":2285,"children":2286},{"style":348},[2287],{"type":46,"value":2288},"\u002F\u002F What if updateMany times out? What if it partially succeeds?\n",{"type":40,"tag":342,"props":2290,"children":2291},{"class":344,"line":1329},[2292,2296,2300,2305,2309,2313,2317,2321,2325,2330,2334,2338,2342],{"type":40,"tag":342,"props":2293,"children":2294},{"style":358},[2295],{"type":46,"value":361},{"type":40,"tag":342,"props":2297,"children":2298},{"style":358},[2299],{"type":46,"value":366},{"type":40,"tag":342,"props":2301,"children":2302},{"style":369},[2303],{"type":46,"value":2304}," updateOrgName",{"type":40,"tag":342,"props":2306,"children":2307},{"style":375},[2308],{"type":46,"value":378},{"type":40,"tag":342,"props":2310,"children":2311},{"style":381},[2312],{"type":46,"value":1111},{"type":40,"tag":342,"props":2314,"children":2315},{"style":375},[2316],{"type":46,"value":389},{"type":40,"tag":342,"props":2318,"children":2319},{"style":392},[2320],{"type":46,"value":395},{"type":40,"tag":342,"props":2322,"children":2323},{"style":375},[2324],{"type":46,"value":1921},{"type":40,"tag":342,"props":2326,"children":2327},{"style":381},[2328],{"type":46,"value":2329}," newName",{"type":40,"tag":342,"props":2331,"children":2332},{"style":375},[2333],{"type":46,"value":389},{"type":40,"tag":342,"props":2335,"children":2336},{"style":392},[2337],{"type":46,"value":395},{"type":40,"tag":342,"props":2339,"children":2340},{"style":375},[2341],{"type":46,"value":400},{"type":40,"tag":342,"props":2343,"children":2344},{"style":375},[2345],{"type":46,"value":405},{"type":40,"tag":342,"props":2347,"children":2348},{"class":344,"line":1371},[2349,2354,2358,2362,2367,2371,2375,2379,2383,2387,2391,2395,2399,2403,2407,2411,2415,2419,2423,2428,2432,2436,2440,2444,2448],{"type":40,"tag":342,"props":2350,"children":2351},{"style":411},[2352],{"type":46,"value":2353},"  await",{"type":40,"tag":342,"props":2355,"children":2356},{"style":417},[2357],{"type":46,"value":420},{"type":40,"tag":342,"props":2359,"children":2360},{"style":375},[2361],{"type":46,"value":425},{"type":40,"tag":342,"props":2363,"children":2364},{"style":417},[2365],{"type":46,"value":2366},"organization",{"type":40,"tag":342,"props":2368,"children":2369},{"style":375},[2370],{"type":46,"value":425},{"type":40,"tag":342,"props":2372,"children":2373},{"style":369},[2374],{"type":46,"value":1318},{"type":40,"tag":342,"props":2376,"children":2377},{"style":442},[2378],{"type":46,"value":378},{"type":40,"tag":342,"props":2380,"children":2381},{"style":375},[2382],{"type":46,"value":916},{"type":40,"tag":342,"props":2384,"children":2385},{"style":442},[2386],{"type":46,"value":921},{"type":40,"tag":342,"props":2388,"children":2389},{"style":375},[2390],{"type":46,"value":389},{"type":40,"tag":342,"props":2392,"children":2393},{"style":375},[2394],{"type":46,"value":467},{"type":40,"tag":342,"props":2396,"children":2397},{"style":442},[2398],{"type":46,"value":472},{"type":40,"tag":342,"props":2400,"children":2401},{"style":375},[2402],{"type":46,"value":389},{"type":40,"tag":342,"props":2404,"children":2405},{"style":417},[2406],{"type":46,"value":1094},{"type":40,"tag":342,"props":2408,"children":2409},{"style":375},[2410],{"type":46,"value":1116},{"type":40,"tag":342,"props":2412,"children":2413},{"style":442},[2414],{"type":46,"value":2078},{"type":40,"tag":342,"props":2416,"children":2417},{"style":375},[2418],{"type":46,"value":389},{"type":40,"tag":342,"props":2420,"children":2421},{"style":375},[2422],{"type":46,"value":467},{"type":40,"tag":342,"props":2424,"children":2425},{"style":442},[2426],{"type":46,"value":2427}," name",{"type":40,"tag":342,"props":2429,"children":2430},{"style":375},[2431],{"type":46,"value":389},{"type":40,"tag":342,"props":2433,"children":2434},{"style":417},[2435],{"type":46,"value":2329},{"type":40,"tag":342,"props":2437,"children":2438},{"style":375},[2439],{"type":46,"value":956},{"type":40,"tag":342,"props":2441,"children":2442},{"style":375},[2443],{"type":46,"value":956},{"type":40,"tag":342,"props":2445,"children":2446},{"style":442},[2447],{"type":46,"value":400},{"type":40,"tag":342,"props":2449,"children":2450},{"style":375},[2451],{"type":46,"value":592},{"type":40,"tag":342,"props":2453,"children":2454},{"class":344,"line":1414},[2455,2459,2463,2467,2471,2475,2480,2484,2488,2492,2496,2500,2504,2508,2512,2516,2520,2525,2529,2533,2537,2541,2545],{"type":40,"tag":342,"props":2456,"children":2457},{"style":411},[2458],{"type":46,"value":2353},{"type":40,"tag":342,"props":2460,"children":2461},{"style":417},[2462],{"type":46,"value":420},{"type":40,"tag":342,"props":2464,"children":2465},{"style":375},[2466],{"type":46,"value":425},{"type":40,"tag":342,"props":2468,"children":2469},{"style":417},[2470],{"type":46,"value":430},{"type":40,"tag":342,"props":2472,"children":2473},{"style":375},[2474],{"type":46,"value":425},{"type":40,"tag":342,"props":2476,"children":2477},{"style":369},[2478],{"type":46,"value":2479},"updateMany",{"type":40,"tag":342,"props":2481,"children":2482},{"style":442},[2483],{"type":46,"value":378},{"type":40,"tag":342,"props":2485,"children":2486},{"style":375},[2487],{"type":46,"value":916},{"type":40,"tag":342,"props":2489,"children":2490},{"style":442},[2491],{"type":46,"value":921},{"type":40,"tag":342,"props":2493,"children":2494},{"style":375},[2495],{"type":46,"value":389},{"type":40,"tag":342,"props":2497,"children":2498},{"style":375},[2499],{"type":46,"value":467},{"type":40,"tag":342,"props":2501,"children":2502},{"style":417},[2503],{"type":46,"value":1094},{"type":40,"tag":342,"props":2505,"children":2506},{"style":375},[2507],{"type":46,"value":1116},{"type":40,"tag":342,"props":2509,"children":2510},{"style":442},[2511],{"type":46,"value":2078},{"type":40,"tag":342,"props":2513,"children":2514},{"style":375},[2515],{"type":46,"value":389},{"type":40,"tag":342,"props":2517,"children":2518},{"style":375},[2519],{"type":46,"value":467},{"type":40,"tag":342,"props":2521,"children":2522},{"style":442},[2523],{"type":46,"value":2524}," orgName",{"type":40,"tag":342,"props":2526,"children":2527},{"style":375},[2528],{"type":46,"value":389},{"type":40,"tag":342,"props":2530,"children":2531},{"style":417},[2532],{"type":46,"value":2329},{"type":40,"tag":342,"props":2534,"children":2535},{"style":375},[2536],{"type":46,"value":956},{"type":40,"tag":342,"props":2538,"children":2539},{"style":375},[2540],{"type":46,"value":956},{"type":40,"tag":342,"props":2542,"children":2543},{"style":442},[2544],{"type":46,"value":400},{"type":40,"tag":342,"props":2546,"children":2547},{"style":375},[2548],{"type":46,"value":592},{"type":40,"tag":342,"props":2550,"children":2551},{"class":344,"line":1431},[2552,2556,2560,2564,2569,2573,2577,2581,2585,2589,2593,2597,2601,2605,2609,2613,2617,2621,2625,2629,2633,2637,2641],{"type":40,"tag":342,"props":2553,"children":2554},{"style":411},[2555],{"type":46,"value":2353},{"type":40,"tag":342,"props":2557,"children":2558},{"style":417},[2559],{"type":46,"value":420},{"type":40,"tag":342,"props":2561,"children":2562},{"style":375},[2563],{"type":46,"value":425},{"type":40,"tag":342,"props":2565,"children":2566},{"style":417},[2567],{"type":46,"value":2568},"invoice",{"type":40,"tag":342,"props":2570,"children":2571},{"style":375},[2572],{"type":46,"value":425},{"type":40,"tag":342,"props":2574,"children":2575},{"style":369},[2576],{"type":46,"value":2479},{"type":40,"tag":342,"props":2578,"children":2579},{"style":442},[2580],{"type":46,"value":378},{"type":40,"tag":342,"props":2582,"children":2583},{"style":375},[2584],{"type":46,"value":916},{"type":40,"tag":342,"props":2586,"children":2587},{"style":442},[2588],{"type":46,"value":921},{"type":40,"tag":342,"props":2590,"children":2591},{"style":375},[2592],{"type":46,"value":389},{"type":40,"tag":342,"props":2594,"children":2595},{"style":375},[2596],{"type":46,"value":467},{"type":40,"tag":342,"props":2598,"children":2599},{"style":417},[2600],{"type":46,"value":1094},{"type":40,"tag":342,"props":2602,"children":2603},{"style":375},[2604],{"type":46,"value":1116},{"type":40,"tag":342,"props":2606,"children":2607},{"style":442},[2608],{"type":46,"value":2078},{"type":40,"tag":342,"props":2610,"children":2611},{"style":375},[2612],{"type":46,"value":389},{"type":40,"tag":342,"props":2614,"children":2615},{"style":375},[2616],{"type":46,"value":467},{"type":40,"tag":342,"props":2618,"children":2619},{"style":442},[2620],{"type":46,"value":2524},{"type":40,"tag":342,"props":2622,"children":2623},{"style":375},[2624],{"type":46,"value":389},{"type":40,"tag":342,"props":2626,"children":2627},{"style":417},[2628],{"type":46,"value":2329},{"type":40,"tag":342,"props":2630,"children":2631},{"style":375},[2632],{"type":46,"value":956},{"type":40,"tag":342,"props":2634,"children":2635},{"style":375},[2636],{"type":46,"value":956},{"type":40,"tag":342,"props":2638,"children":2639},{"style":442},[2640],{"type":46,"value":400},{"type":40,"tag":342,"props":2642,"children":2643},{"style":375},[2644],{"type":46,"value":592},{"type":40,"tag":342,"props":2646,"children":2647},{"class":344,"line":1440},[2648,2652,2656,2660,2665,2669,2673,2677,2681,2685,2689,2693,2697,2701,2705,2709,2713,2717,2721,2725,2729,2733,2737],{"type":40,"tag":342,"props":2649,"children":2650},{"style":411},[2651],{"type":46,"value":2353},{"type":40,"tag":342,"props":2653,"children":2654},{"style":417},[2655],{"type":46,"value":420},{"type":40,"tag":342,"props":2657,"children":2658},{"style":375},[2659],{"type":46,"value":425},{"type":40,"tag":342,"props":2661,"children":2662},{"style":417},[2663],{"type":46,"value":2664},"auditLog",{"type":40,"tag":342,"props":2666,"children":2667},{"style":375},[2668],{"type":46,"value":425},{"type":40,"tag":342,"props":2670,"children":2671},{"style":369},[2672],{"type":46,"value":2479},{"type":40,"tag":342,"props":2674,"children":2675},{"style":442},[2676],{"type":46,"value":378},{"type":40,"tag":342,"props":2678,"children":2679},{"style":375},[2680],{"type":46,"value":916},{"type":40,"tag":342,"props":2682,"children":2683},{"style":442},[2684],{"type":46,"value":921},{"type":40,"tag":342,"props":2686,"children":2687},{"style":375},[2688],{"type":46,"value":389},{"type":40,"tag":342,"props":2690,"children":2691},{"style":375},[2692],{"type":46,"value":467},{"type":40,"tag":342,"props":2694,"children":2695},{"style":417},[2696],{"type":46,"value":1094},{"type":40,"tag":342,"props":2698,"children":2699},{"style":375},[2700],{"type":46,"value":1116},{"type":40,"tag":342,"props":2702,"children":2703},{"style":442},[2704],{"type":46,"value":2078},{"type":40,"tag":342,"props":2706,"children":2707},{"style":375},[2708],{"type":46,"value":389},{"type":40,"tag":342,"props":2710,"children":2711},{"style":375},[2712],{"type":46,"value":467},{"type":40,"tag":342,"props":2714,"children":2715},{"style":442},[2716],{"type":46,"value":2524},{"type":40,"tag":342,"props":2718,"children":2719},{"style":375},[2720],{"type":46,"value":389},{"type":40,"tag":342,"props":2722,"children":2723},{"style":417},[2724],{"type":46,"value":2329},{"type":40,"tag":342,"props":2726,"children":2727},{"style":375},[2728],{"type":46,"value":956},{"type":40,"tag":342,"props":2730,"children":2731},{"style":375},[2732],{"type":46,"value":956},{"type":40,"tag":342,"props":2734,"children":2735},{"style":442},[2736],{"type":46,"value":400},{"type":40,"tag":342,"props":2738,"children":2739},{"style":375},[2740],{"type":46,"value":592},{"type":40,"tag":342,"props":2742,"children":2743},{"class":344,"line":1533},[2744],{"type":40,"tag":342,"props":2745,"children":2746},{"style":375},[2747],{"type":46,"value":601},{"type":40,"tag":61,"props":2749,"children":2751},{"id":2750},"related-traps",[2752],{"type":46,"value":2753},"Related Traps",{"type":40,"tag":87,"props":2755,"children":2756},{},[2757,2767,2777,2787],{"type":40,"tag":91,"props":2758,"children":2759},{},[2760,2765],{"type":40,"tag":55,"props":2761,"children":2762},{},[2763],{"type":46,"value":2764},"Cardinality",{"type":46,"value":2766}," -- denormalization cost scales with the cardinality of the target collection. Denormalizing into an L4\u002FL5 set means write amplification proportional to its size.",{"type":40,"tag":91,"props":2768,"children":2769},{},[2770,2775],{"type":40,"tag":55,"props":2771,"children":2772},{},[2773],{"type":46,"value":2774},"Cache Invalidation",{"type":46,"value":2776}," -- caching a denormalized blob is double denormalization. The cache is a copy of a copy.",{"type":40,"tag":91,"props":2778,"children":2779},{},[2780,2785],{"type":40,"tag":55,"props":2781,"children":2782},{},[2783],{"type":46,"value":2784},"Consistency Models",{"type":46,"value":2786}," -- denormalized data is eventually consistent by nature. If your feature needs strong consistency, denormalization is the wrong tool.",{"type":40,"tag":91,"props":2788,"children":2789},{},[2790,2795],{"type":40,"tag":55,"props":2791,"children":2792},{},[2793],{"type":46,"value":2794},"Idempotency",{"type":46,"value":2796}," -- sync jobs that update denormalized copies must be idempotent, or partial failures leave data in an inconsistent state.",{"type":40,"tag":2798,"props":2799,"children":2800},"style",{},[2801],{"type":46,"value":2802},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":2804,"total":1414},[2805,2816,2831,2841,2853,2864,2870],{"slug":2806,"name":2806,"fn":2807,"description":2808,"org":2809,"tags":2810,"stars":23,"repoUrl":24,"updatedAt":2815},"staff-engineering-skills-backpressure","implement backpressure in streaming pipelines","Prevent unbounded resource growth when producers outpace consumers. Use when writing producer-consumer code, queue processing, batch operations, fan-out patterns, streaming pipelines, or any code where work is generated faster than it can be processed. Activates on patterns like Promise.all on dynamic-sized arrays, unbounded in-memory queues, producer loops without depth checks, fire-and-forget async calls in loops, or any buffer between a producer and consumer without a size limit.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2811,2812],{"name":14,"slug":15,"type":16},{"name":2813,"slug":2814,"type":16},"Performance","performance","2026-06-17T08:40:42.723559",{"slug":2817,"name":2817,"fn":2818,"description":2819,"org":2820,"tags":2821,"stars":23,"repoUrl":24,"updatedAt":2830},"staff-engineering-skills-cache-invalidation","implement cache invalidation strategies","Prevent stale data bugs caused by missing or incomplete cache invalidation. Use when adding caching layers (Redis, in-memory Map, CDN, HTTP cache headers), optimizing read performance, or reviewing code that caches query results, API responses, or computed values. Activates on patterns like cache.set without cache.delete on write paths, unbounded Map\u002Fobject caches, TTL-only invalidation on security-sensitive data, Cache-Control headers on mutable content, or any caching where the write path doesn't invalidate the read cache.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2822,2823,2826,2829],{"name":14,"slug":15,"type":16},{"name":2824,"slug":2825,"type":16},"Caching","caching",{"name":2827,"slug":2828,"type":16},"Engineering","engineering",{"name":2813,"slug":2814,"type":16},"2026-06-17T08:40:45.194583",{"slug":2832,"name":2832,"fn":2833,"description":2834,"org":2835,"tags":2836,"stars":23,"repoUrl":24,"updatedAt":2840},"staff-engineering-skills-cardinality","prevent cardinality traps in systems code","Detect and prevent cardinality traps in systems code. Use when writing code that iterates over collections, stores items in memory, creates per-entity resources, or fans out operations across a set of items. Activates on patterns like loops over query results, in-memory Maps\u002FSets populated from databases, or \"one X per Y\" resource allocation.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2837,2838,2839],{"name":14,"slug":15,"type":16},{"name":2827,"slug":2828,"type":16},{"name":2813,"slug":2814,"type":16},"2026-06-17T08:40:40.264608",{"slug":2842,"name":2842,"fn":2843,"description":2844,"org":2845,"tags":2846,"stars":23,"repoUrl":24,"updatedAt":2852},"staff-engineering-skills-clock-skew","prevent clock skew bugs in distributed systems","Prevent bugs caused by assuming clocks are synchronized or monotonic. Use when writing code that compares timestamps across machines, measures durations, sets lock expiry, orders distributed events, deduplicates by time window, or uses Date.now() for anything other than logging or display. Activates on patterns like Date.now() used for duration measurement, absolute timestamp expiry shared across machines, wall clock timestamps for event ordering, last-write-wins conflict resolution with timestamps, or timeout calculations using wall clock time.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2847,2848,2851],{"name":14,"slug":15,"type":16},{"name":2849,"slug":2850,"type":16},"Debugging","debugging",{"name":2827,"slug":2828,"type":16},"2026-06-17T08:40:37.803541",{"slug":2854,"name":2854,"fn":2855,"description":2856,"org":2857,"tags":2858,"stars":23,"repoUrl":24,"updatedAt":2863},"staff-engineering-skills-consistency-models","manage consistency models in distributed systems","Prevent stale read bugs caused by replica lag, cache staleness, and index delay. Use when writing code that reads data after writing it, uses read replicas, caches query results, reads from search indexes, or builds event-driven read models. Activates on patterns like create-then-redirect, update-then-read, cache-aside with TTL, search-after-create, or any write followed by a read that might hit a different data source.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2859,2860,2861,2862],{"name":14,"slug":15,"type":16},{"name":18,"slug":19,"type":16},{"name":2827,"slug":2828,"type":16},{"name":2813,"slug":2814,"type":16},"2026-06-17T08:40:46.442182",{"slug":4,"name":4,"fn":5,"description":6,"org":2865,"tags":2866,"stars":23,"repoUrl":24,"updatedAt":25},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2867,2868,2869],{"name":14,"slug":15,"type":16},{"name":21,"slug":22,"type":16},{"name":18,"slug":19,"type":16},{"slug":2871,"name":2871,"fn":2872,"description":2873,"org":2874,"tags":2875,"stars":23,"repoUrl":24,"updatedAt":2881},"staff-engineering-skills-distributed-system-fallacies","design resilient distributed systems","Prevent failures caused by treating network calls like function calls. Use when writing code that calls external services, splits a monolith into microservices, chains multiple API calls, orchestrates distributed workflows, or handles requests that depend on other services. Activates on patterns like sequential service calls without timeouts, fetch\u002Faxios calls without error handling, multi-service writes without compensation, hardcoded service URLs, or any code that assumes the network is reliable, fast, or free.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2876,2879,2880],{"name":2877,"slug":2878,"type":16},"API Development","api-development",{"name":14,"slug":15,"type":16},{"name":2827,"slug":2828,"type":16},"2026-06-17T08:40:47.666795",{"items":2883,"total":1662},[2884,2903,2916,2926,2943,2957,2971,2988,3001,3013,3024,3029],{"slug":2885,"name":2885,"fn":2886,"description":2887,"org":2888,"tags":2889,"stars":2900,"repoUrl":2901,"updatedAt":2902},"trigger-authoring-chat-agent","author durable AI chat agents with Trigger.dev","Author and run a durable AI chat agent with chat.agent from @trigger.dev\u002Fsdk\u002Fai: the per-turn run loop, why you MUST spread ...chat.toStreamTextOptions() first, returning a StreamTextResult vs calling chat.pipe(), the two server actions (chat.createStartSessionAction + auth.createPublicToken), and wiring useChat to useTriggerChatTransport. Load this when building, modifying, or debugging a chat backend (the agent task or its lifecycle hooks) or its React transport, when declaring typed tools or custom data parts, or when migrating a plain AI SDK streamText route to chat.agent.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2890,2893,2896,2897],{"name":2891,"slug":2892,"type":16},"Agents","agents",{"name":2894,"slug":2895,"type":16},"SDK","sdk",{"name":9,"slug":8,"type":16},{"name":2898,"slug":2899,"type":16},"Workflow Automation","workflow-automation",14401,"https:\u002F\u002Fgithub.com\u002Ftriggerdotdev\u002Ftrigger.dev","2026-07-02T17:12:52.307135",{"slug":2904,"name":2904,"fn":2905,"description":2906,"org":2907,"tags":2908,"stars":2900,"repoUrl":2901,"updatedAt":2915},"trigger-authoring-tasks","author backend Trigger.dev tasks","Covers writing backend Trigger.dev tasks with @trigger.dev\u002Fsdk: defining task() and schemaTask(), the run function and its ctx, retries, waits, queues and concurrency, idempotency keys, run metadata, logging, triggering other tasks (and the Result shape), scheduled\u002Fcron tasks, and the essentials of trigger.config.ts. Load this whenever you are authoring or editing code inside a \u002Ftrigger directory, defining a task, or writing backend code that triggers tasks. Realtime\u002FReact hooks and AI chat are covered by separate skills.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2909,2912,2913,2914],{"name":2910,"slug":2911,"type":16},"Backend","backend",{"name":2894,"slug":2895,"type":16},{"name":9,"slug":8,"type":16},{"name":2898,"slug":2899,"type":16},"2026-07-02T17:12:48.396964",{"slug":2917,"name":2917,"fn":2918,"description":2919,"org":2920,"tags":2921,"stars":2900,"repoUrl":2901,"updatedAt":2925},"trigger-chat-agent-advanced","manage Trigger.dev chat sessions and transports","Advanced and operational chat.agent capabilities for Trigger.dev, loaded on demand. Load this when working on the raw Sessions primitive (sessions \u002F SessionHandle), a custom chat transport or the realtime wire protocol, durable sub-agents (AgentChat, chat.stream.writer), human-in-the-loop, steering, actions, background injection (chat.defer \u002F chat.inject), fast starts (preload, Head Start via @trigger.dev\u002Fsdk\u002Fchat-server), context resilience (compaction, recovery boot, OOM, large payloads), chat.local run-scoped state, offline testing with mockChatAgent, or prerelease\u002Fversion upgrades. For the everyday chat.agent({...}) definition and the useTriggerChatTransport happy path, use the trigger-authoring-chat-agent skill instead.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2922,2923,2924],{"name":2891,"slug":2892,"type":16},{"name":9,"slug":8,"type":16},{"name":2898,"slug":2899,"type":16},"2026-07-02T17:12:51.03018",{"slug":2927,"name":2927,"fn":2928,"description":2929,"org":2930,"tags":2931,"stars":2900,"repoUrl":2901,"updatedAt":2942},"trigger-realtime-and-frontend","subscribe to Trigger.dev runs in realtime","Trigger.dev client\u002Ffrontend surface: subscribe to runs in realtime (runs.subscribeToRun and the @trigger.dev\u002Freact-hooks hook useRealtimeRun), consume metadata and AI\u002Ftext streams in React (useRealtimeStream), trigger tasks from the browser (useTaskTrigger, useRealtimeTaskTrigger), and mint scoped frontend credentials with auth.createPublicToken \u002F auth.createTriggerPublicToken. Load when wiring a frontend (React\u002FNext.js\u002FRemix) or backend-for-frontend to show live run progress, status badges, token streams, trigger buttons, or wait-token approval UIs. NOT for writing the backend task itself (streams.define \u002F metadata.set is trigger-authoring-tasks territory); this is the consumer side.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2932,2935,2938,2941],{"name":2933,"slug":2934,"type":16},"Frontend","frontend",{"name":2936,"slug":2937,"type":16},"React","react",{"name":2939,"slug":2940,"type":16},"Real-time","real-time",{"name":9,"slug":8,"type":16},"2026-07-02T17:12:49.717706",{"slug":2944,"name":2944,"fn":2945,"description":2946,"org":2947,"tags":2948,"stars":1698,"repoUrl":2955,"updatedAt":2956},"trigger-agents","orchestrate AI agents with Trigger.dev","AI agent patterns with Trigger.dev - orchestration, parallelization, routing, evaluator-optimizer, and human-in-the-loop. Use when building LLM-powered tasks that need parallel workers, approval gates, tool calling, or multi-step agent workflows.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2949,2950,2953,2954],{"name":2891,"slug":2892,"type":16},{"name":2951,"slug":2952,"type":16},"Multi-Agent","multi-agent",{"name":9,"slug":8,"type":16},{"name":2898,"slug":2899,"type":16},"https:\u002F\u002Fgithub.com\u002Ftriggerdotdev\u002Fskills","2026-04-06T18:54:46.023553",{"slug":2958,"name":2958,"fn":2959,"description":2960,"org":2961,"tags":2962,"stars":1698,"repoUrl":2955,"updatedAt":2970},"trigger-config","configure Trigger.dev project settings","Configure Trigger.dev projects with trigger.config.ts. Use when setting up build extensions for Prisma, Playwright, FFmpeg, Python, or customizing deployment settings.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2963,2966,2969],{"name":2964,"slug":2965,"type":16},"Configuration","configuration",{"name":2967,"slug":2968,"type":16},"Deployment","deployment",{"name":9,"slug":8,"type":16},"2026-04-06T18:54:44.764258",{"slug":2972,"name":2972,"fn":2973,"description":2974,"org":2975,"tags":2976,"stars":1698,"repoUrl":2955,"updatedAt":2987},"trigger-cost-savings","optimize Trigger.dev task costs","Analyze Trigger.dev tasks, schedules, and runs for cost optimization opportunities. Use when asked to reduce spend, optimize costs, audit usage, right-size machines, or review task efficiency. Requires Trigger.dev MCP tools for run analysis.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2977,2980,2983,2986],{"name":2978,"slug":2979,"type":16},"Analytics","analytics",{"name":2981,"slug":2982,"type":16},"Cost Optimization","cost-optimization",{"name":2984,"slug":2985,"type":16},"Operations","operations",{"name":9,"slug":8,"type":16},"2026-04-06T18:54:48.555552",{"slug":2989,"name":2989,"fn":2990,"description":2991,"org":2992,"tags":2993,"stars":1698,"repoUrl":2955,"updatedAt":3000},"trigger-realtime","monitor Trigger.dev tasks in real-time","Subscribe to Trigger.dev task runs in real-time from frontend and backend. Use when building progress indicators, live dashboards, streaming AI\u002FLLM responses, or React components that display task status.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2994,2995,2998,2999],{"name":2933,"slug":2934,"type":16},{"name":2996,"slug":2997,"type":16},"Observability","observability",{"name":2939,"slug":2940,"type":16},{"name":9,"slug":8,"type":16},"2026-04-06T18:54:47.293822",{"slug":3002,"name":3002,"fn":3003,"description":3004,"org":3005,"tags":3006,"stars":1698,"repoUrl":2955,"updatedAt":3012},"trigger-setup","set up Trigger.dev in projects","Set up Trigger.dev in your project. Use when adding Trigger.dev for the first time, creating trigger.config.ts, or initializing the trigger directory.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3007,3008,3011],{"name":2964,"slug":2965,"type":16},{"name":3009,"slug":3010,"type":16},"Local Development","local-development",{"name":9,"slug":8,"type":16},"2026-04-06T18:54:42.280816",{"slug":3014,"name":3014,"fn":3015,"description":3016,"org":3017,"tags":3018,"stars":1698,"repoUrl":2955,"updatedAt":3023},"trigger-tasks","build durable background tasks with Trigger.dev","Build AI agents, workflows and durable background tasks with Trigger.dev. Use when creating tasks, triggering jobs, handling retries, scheduling cron jobs, or implementing queues and concurrency control.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3019,3020,3021,3022],{"name":2891,"slug":2892,"type":16},{"name":2910,"slug":2911,"type":16},{"name":9,"slug":8,"type":16},{"name":2898,"slug":2899,"type":16},"2026-04-06T18:54:43.514369",{"slug":2806,"name":2806,"fn":2807,"description":2808,"org":3025,"tags":3026,"stars":23,"repoUrl":24,"updatedAt":2815},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3027,3028],{"name":14,"slug":15,"type":16},{"name":2813,"slug":2814,"type":16},{"slug":2817,"name":2817,"fn":2818,"description":2819,"org":3030,"tags":3031,"stars":23,"repoUrl":24,"updatedAt":2830},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3032,3033,3034,3035],{"name":14,"slug":15,"type":16},{"name":2824,"slug":2825,"type":16},{"name":2827,"slug":2828,"type":16},{"name":2813,"slug":2814,"type":16}]