1"use strict";(globalThis.webpackChunk_talkingschemaai_blog_site=globalThis.webpackChunk_talkingschemaai_blog_site||[]).push([[1376],{6980(e,t,n){n.r(t),n.d(t,{assets:()=>h,contentTitle:()=>l,default:()=>m,frontMatter:()=>r,metadata:()=>a,toc:()=>c});var a=n(559),s=n(4848),i=n(3023),o=n(6363);const r={slug:"database-normalization-practical-guide",title:"Database Normalization Is Not Enough: Context-Aware OLTP Schema Design",image:"/img/talkingschema-git-bot-flywheel.png",description:"Your OLTP schema is normalized, constrained, and technically correct \u2014 and yet analysts, ML engineers, and executives downstream still reverse-engineer what columns mean. This guide covers context-aware OLTP design: capturing column intent with the TalkingSchema Git bot, using schema context to prevent AI-generated data models from silently lying, and building a modeling inventory that surfaces every analytical model your data could power.",authors:["karan"],tags:["database normalization","oltp schema design","context-aware data modeling","data catalog alternative","schema documentation","schema business context capture","git bot schema review","data modeling inventory","ai data modeling","schema intent documentation","coding agent database migration"],date:new Date("2026-04-28T00:00:00.000Z"),last_modified:new Date("2026-05-25T00:00:00.000Z")},l=void 0,h={authorsImageUrls:[void 0]},c=[{value:"The schema serves more than just the app",id:"the-schema-serves-more-than-just-the-app",level:2},{value:"Schema work is no longer a one-time event",id:"schema-work-is-no-longer-a-one-time-event",level:2},{value:"Where TalkingSchema starts",id:"where-talkingschema-starts",level:2},{value:"The Git bot: making coding agents accountable to your schema",id:"the-git-bot-making-coding-agents-accountable-to-your-schema",level:2},{value:"See the difference: context-empty vs. context-rich",id:"see-the-difference-context-empty-vs-context-rich",level:2},{value:"Let's ask both schemas the very same question",id:"lets-ask-both-schemas-the-very-same-question",level:3},{value:"The modeling inventory: from captured context to pre-built data models",id:"the-modeling-inventory-from-captured-context-to-pre-built-data-models",level:2},{value:"Advanced Modeling: every possibility, not just the obvious ones",id:"advanced-modeling-every-possibility-not-just-the-obvious-ones",level:2}];function d(e){const t={a:"a",blockquote:"blockquote",code:"code",em:"em",h2:"h2",h3:"h3",hr:"hr",img:"img",li:"li",p:"p",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,i.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(t.p,{children:"The data is right there. The meaning is not."}),"\n",(0,s.jsxs)(t.p,{children:["That gap \u2014 between what your OLTP schema ",(0,s.jsx)(t.em,{children:"stores"})," and what it ",(0,s.jsx)(t.em,{children:"means"})," \u2014 has quietly become the most expensive problem in modern data work. The textbook half is solved. Tables normalize to 3NF, foreign keys check out, migrations ship without locking, and most senior engineers can stamp out a clean transactional schema in their sleep."]}),"\n",(0,s.jsx)(t.p,{children:"The unsolved half is everything the schema doesn't say. The intent behind a column. The grain of a row. The business definition of an enum value. The mutability of a foreign key target. None of it lives in the schema, the catalog, or the data dictionary \u2014 and the cost of that silence falls almost entirely on the teams downstream: analysts reverse-engineering column meanings from old Slack threads, ML engineers guessing at grain, executives squinting at dashboards quietly compromised by an undocumented default."}),"\n",(0,s.jsx)(t.p,{children:"We're not alone in calling this out. Scroll through what the rest of the industry has been writing about it:"}),"\n",(0,s.jsx)(o.L,{label:"Worth a read",links:[{source:"BCG X",title:"Solving the Data Context Problem for Development Teams",href:"https://www.bcg.com/x/the-multiplier/solving-data-context-problem-todays-development-teams"},{source:"Atlan",title:"Context Engineering for AI Analysts: Why It's Essential",href:"https://www.atlan.com/know/context-engineering-for-ai-analyst/"}
1,{source:"Colrows",title:"Knowledge Drift and Semantic Decay: The New Technical Debt",href:"https://blog.colrows.com/knowledge-drift-and-semantic-decay-the-new-technical-debt-11b25224d408"},{source:"Databytego",title:"The Death of the Static Data Catalog",href:"https://www.databytego.com/p/the-death-of-the-static-data-catalog"},{source:"ER/Studio",title:"Why Data Models Are the Only Documentation That Lasts",href:"https://erstudio.com/blog/data-models-are-the-only-documentation-that-survives-turnover/"},{source:"kargin-utkin",title:"How Meta Built 50 AI Agents to Map Tribal Knowledge Across Its Data Pipelines",href:"https://www.kargin-utkin.com/meta-tribal-knowledge-ai-agents-data-pipelines"},{source:"Promethium",title:"AI's Achilles' Heel: Why Data Context Is the Key to Trustworthy Insights",href:"https://promethium.ai/ais-achilles-heel-why-data-context-is-the-key-to-trustworthy-insights/"},{source:"DataHub",title:"AI Documentation & Context Propagation",href:"https://datahub.com/blog/benefits-of-ai-generation-documentation-and-context-propagation/"},{source:"tianpan.co",title:"Your RAG Knows the Docs. It Doesn't Know What Your Engineers Know.",href:"https://tianpan.co/blog/2026-04-19-tacit-knowledge-capture-rag-enterprise"}]}),"\n",(0,s.jsx)(t.hr,{}),"\n",(0,s.jsx)(t.h2,{id:"the-schema-serves-more-than-just-the-app",children:"The schema serves more than just the app"}),"\n",(0,s.jsx)(t.p,{children:"The original purpose of an OLTP schema was modest: support fast, correct, concurrent transactions. Insert an order. Update a balance. Roll back if anything goes wrong. The relational model and normalization theory were designed for that workload, and forty years of production experience have refined the playbook to the point where most senior engineers can write a clean transactional schema in their sleep."}),"\n",(0,s.jsxs)(t.p,{children:["But OLTP schemas no longer serve only the application sitting on top of them. The same ",(0,s.jsx)(t.code,{children:"customers.email"})," column now has to cooperate with:"]}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsxs)(t.li,{children:["An ",(0,s.jsx)(t.strong,{children:"ARR / revenue dashboard"})," that joins customers to subscriptions to invoices, and needs to know which ",(0,s.jsx)(t.code,{children:"email"})," field \u2014 ",(0,s.jsx)(t.code,{children:"customers.email"}),", ",(0,s.jsx)(t.code,{children:"subscriptions.contact_email"}),", or ",(0,s.jsx)(t.code,{children:"invoices.billing_email"})," \u2014 is the source of truth when the three disagree."]}),"\n",(0,s.jsxs)(t.li,{children:["A ",(0,s.jsx)(t.strong,{children:"churn model"}),' in the data science team that wants the email domain, the signup vintage, and a stable definition of "active" that the product team keeps quietly changing.']}),"\n",(0,s.jsxs)(t.li,{children:["A ",(0,s.jsx)(t.strong,{children:"growth team's cohort analysis"})," that needs to know whether trial users, freemium users, and paid users all live in the same column or not."]}),"\n",(0,s.jsxs)(t.li,{children:["A ",(0,s.jsx)(t.strong,{children:"sales ops view"})," that wants to attribute revenue back to acquisition channel, source campaign, and account owner \u2014 none of which is in the OLTP schema by those names."]}),"\n",(0,s.jsxs)(t.li,{children:["A ",(0,s.jsx)(t.strong,{children:"compliance pipeline"})," that needs to know the field is PII so it can be redacted on request, encrypted at rest, and excluded from logs."]}),"\n",(0,s.jsxs)(t.li,{children:["An ",(0,s.jsx)(t.strong,{children:"AI agent"})," answering questions like ",(0,s.jsx)(t.em,{children:'"is this account a churn risk?"'})," \u2014 using a column whose actual business meaning lives in a Slack thread from 2023."]}),"\n"]}),"\n",(0,s.jsx)(t.p,{children:"Every one of those downstream consumers has to reverse-engineer the column's intent from its name. The schema gives them no help. The application code gives them outdated help. The wiki gives them stale help. The engineer who first added the column is at a different company."}),"\n",(0,s.jsxs)(t.blockquote,{children:["\n",(0,s.jsx)(t.p,{children:"The cost is not in the transaction. The cost is in everything downstream that has to guess what the transaction meant."}),"\n"]}),"\n",(0,s.jsxs)(t.p,{children:["This is the gap modern OLTP design has to close, and it's why the textbook framing of schema design \u2014 ",(0,s.jsx)(t.em,{children:"normalize for integrity, index for performance"})," \u2014 has stopped being enough on its own."]}),"\n",(0,s.jsx)(t.hr,{}),"\n",(0,s.jsx)(t.h2,{id:"schema-work-is-no-longer-a-one-time-event",children:"Schema work is no longer a one-time event"}),"\n",(0,s.jsxs)(t.p,{children:["The old mental model is that schema design is something you do ",(0,s.jsx)(t.em,{children:"at the start"})," of a system. You sit down, draw an ERD, hand it to the team, and then engineers write CRUD against it forever. Migrations are an unfortunate exception, not a discipline."]}),"\n",(0,s.jsx)(t.p,{children:"That model has been quietly broken for a
1while, and almost no tooling has caught up to it."}),"\n",(0,s.jsx)(t.p,{children:"The reality of every production OLTP system we've seen:"}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsxs)(t.li,{children:["Schemas evolve ",(0,s.jsx)(t.strong,{children:"weekly"}),". A column gets added for a new feature. A table gets split because a domain matured. A constraint gets relaxed because a partner integration needed it."]}),"\n",(0,s.jsx)(t.li,{children:"Most of those changes are made by engineers who weren't there when the original model was designed."}),"\n",(0,s.jsx)(t.li,{children:"Most of those changes are reviewed in PRs by people who don't know the historical context behind the affected tables."}),"\n",(0,s.jsx)(t.li,{children:"Most of those changes ship without anyone updating the documentation, the data dictionary, the catalog, or the downstream consumers."}),"\n"]}),"\n",(0,s.jsx)(t.p,{children:'In a fast-moving codebase, "we know what this column means" has a shelf life of months, not years. By the time you onboard your third backend engineer, half the schema is already running on tribal knowledge. The analytics team, the ML team, and the BI team pay interest on that debt every time they build on top of it.'}),"\n",(0,s.jsxs)(t.p,{children:["OLTP schema design has quietly become a ",(0,s.jsx)(t.strong,{children:"continuous practice"})," \u2014 closer to how teams treat application code than how they treat infrastructure. And like application code, it needs the equivalent of code review, tests, linters, and a way for new contributors to understand the ",(0,s.jsx)(t.em,{children:"why"})," behind what they're touching."]}),"\n",(0,s.jsx)(t.hr,{}),"\n",(0,s.jsx)(t.h2,{id:"where-talkingschema-starts",children:"Where TalkingSchema starts"}),"\n",(0,s.jsxs)(t.p,{children:[(0,s.jsx)(t.a,{href:"https://talkingschema.ai",children:"TalkingSchema"})," is built around exactly this thesis: schema work as a continuous practice, with intent captured at design time and surfaced wherever it matters later \u2014 to the new engineer six months in, the analyst writing a cohort query, or the coding agent building features."]}),"\n",(0,s.jsx)(t.p,{children:"The most useful place to start that practice is the one bottleneck every schema change already passes through \u2014 the pull request."}),"\n",(0,s.jsx)(t.hr,{}),"\n",(0,s.jsx)(t.h2,{id:"the-git-bot-making-coding-agents-accountable-to-your-schema",children:"The Git bot: making coding agents accountable to your schema"}),"\n",(0,s.jsx)(t.p,{children:"In 2026, most database changes don't originate in a modeling tool. They originate in a feature branch, written by an engineer working alongside a coding agent \u2014 Claude, Cursor, Codex, or whatever comes next. The engineer describes a feature, the agent generates the migration, the PR opens, the migration ships. The schema mutates. None of the feature conversation that produced the change makes it into the database, the catalog, or anywhere a future reader can find it."}),"\n",(0,s.jsx)(t.p,{children:"Every PR is an intent-capture opportunity that almost every team is currently throwing away."}),"\n",(0,s.jsx)(t.p,{children:"We're prototyping a Git bot that closes this loop."}),"\n",(0,s.jsx)(t.p,{children:"When a PR touches anything schema-related, the bot blocks the merge with a required check and asks the coding agent to push the relevant context up to TalkingSchema first. The agent already has the feature conversation it had with the engineer \u2014 it just needs to surface it."}),"\n",(0,s.jsx)(t.p,{children:"For anything the agent can't answer, it asks the engineer directly in the PR. If the engineer doesn't have the answer either, they can defer to a domain owner \u2014 and the question stays logged as a pending context request on TalkingSchema until someone closes it."}),"\n",(0,s.jsx)(t.p,{children:(0,s.jsx)(t.img,{alt:"A four-stage flow diagram showing how the TalkingSchema Git bot turns every database-related pull request into an intent-capture opportunity: an engineer and an AI coding agent ship a feature with a database change, the Git bot spots the schema diff in the pull request, the coding agent pushes the feature conversation context into TalkingSchema and asks the engineer for anything it can't infer, and the schema merges with durable AI-readable intent attached to every changed element.",src:n(8959).A+"",width:"1536",height:"1024"})}),"\n",(0,s.jsxs)(t.p,{children:["The point isn't to slow down database changes. It's to make sure every database change leaves the schema ",(0,s.jsx)(t.em,{children:"smarter"}
1)," than it found it."]}),"\n",(0,s.jsxs)(t.blockquote,{children:["\n",(0,s.jsx)(t.p,{children:"Every PR is an intent-capture opportunity. The Git bot makes sure none of them are wasted."}),"\n"]}),"\n",(0,s.jsx)(t.hr,{}),"\n",(0,s.jsx)(t.h2,{id:"see-the-difference-context-empty-vs-context-rich",children:"See the difference: context-empty vs. context-rich"}),"\n",(0,s.jsx)(t.p,{children:"So what does a schema look like after six months of the Git bot running on every PR? Two structurally identical schemas \u2014 same tables, same columns, same indexes \u2014 make the difference visible."}),"\n",(0,s.jsxs)(t.p,{children:[(0,s.jsx)(t.strong,{children:"Schema A \u2014 the typical OLTP database, no Git bot."})," Structurally correct. Normalized to 3NF. Hover over any element and you'll find brief technical descriptions: ",(0,s.jsx)(t.em,{children:'"Order status", "Product price in cents", "Seller tier."'})," Enough to write a migration. Not enough to build a revenue model, a churn predictor, or a cohort analysis without going to hunt for more."]}),"\n",(0,s.jsx)(o.Ig,{threadId:"cdfa37be-2f93-4a99-ba0e-eeadca105017",title:"Schema A: Marketplace Schema \u2014 Without Context"}),"\n",(0,s.jsxs)(t.p,{children:[(0,s.jsx)(t.strong,{children:"Schema B \u2014 the same database, six months in."})," Same shape. Every table note now tells you the grain, the owner, the downstream models that consume it, the KPIs it feeds, and the business rules that govern it. Hover over the elements and read through the notes."]}),"\n",(0,s.jsx)(o.Ig,{threadId:"4858b86c-270b-494d-9ac0-ee63e05c8838",title:"Schema B: Marketplace Schema \u2014 With TalkingSchema Context"}),"\n",(0,s.jsx)(t.p,{children:"The structural diff is zero. The semantic diff is everything a downstream team needs \u2014 and everything they're currently reverse-engineering from Slack threads and departed engineers."}),"\n",(0,s.jsx)(t.h3,{id:"lets-ask-both-schemas-the-very-same-question",children:"Let's ask both schemas the very same question"}),"\n",(0,s.jsx)(t.p,{children:"The real test is what an agent does with the context. So take a question any marketplace exec would ask:"}),"\n",(0,s.jsxs)(t.blockquote,{children:["\n",(0,s.jsx)(t.p,{children:(0,s.jsx)(t.em,{children:'"Build me a model for GMV by buyer geography \u2014 total revenue per state, per month."'})}),"\n"]}),"\n",(0,s.jsxs)(t.p,{children:["The agent's proposed model is baked into each canvas above as a table group called ",(0,s.jsx)(t.code,{children:"gmv_by_buyer_geography"}),". Open both. The diff is one foreign key:"]}),"\n",(0,s.jsxs)(t.table,{children:[(0,s.jsx)(t.thead,{children:(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.th,{}),(0,s.jsx)(t.th,{children:"Schema A (no context)"}),(0,s.jsx)(t.th,{children:"Schema B (with context)"})]})}),(0,s.jsxs)(t.tbody,{children:[(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{children:"Fact table"}),(0,s.jsx)(t.td,{children:(0,s.jsx)(t.code,{children:"gmv_by_state_month"})}),(0,s.jsx)(t.td,{children:(0,s.jsx)(t.code,{children:"gmv_by_state_month"})})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{children:"Grain"}),(0,s.jsx)(t.td,{children:"(state, country, month)"}),(0,s.jsx)(t.td,{children:"(state, country, month)"})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{children:(0,s.jsx)(t.strong,{children:"How orders get geography"})}),(0,s.jsxs)(t.td,{children:[(0,s.jsx)(t.code,{children:"buyer_geo_dim"})," \u2014 keyed by ",(0,s.jsx)(t.strong,{children:(0,s.jsx)(t.code,{children:"buyer_id"})})," (current address)"]}),(0,s.jsxs)(t.td,{children:[(0,s.jsx)(t.code,{children:"buyer_order_geo_snapshots"})," \u2014 keyed by ",(0,s.jsx)(t.strong,{children:(0,s.jsx)(t.code,{children:"order_id"})})," (snapshot at checkout)"]})]})]})]}),"\n",(0,s.jsx)(t.p,{children:"That single column decides whether next year's regional revenue report tells the truth."}),"\n",(0,s.jsxs)(t.p,{children:["If you have worked in data warehousing, this is the ",(0,s.jsx)(t.a,{href:"/blog/slowly-changing-dimensions-schema-not-etl",children:"slowly-changing-dimension"})," problem in plain clothes. Buyer geography is not fixed forever; people move, edit addresses, delete addresses, and change defaults. A context-empty model treats geography like a current profile attribute. A context-rich model knows historical reporting needs the geography as it was when the order happened."]}),"\n",(0,s.jsx)(t.p,{children:(0,s.jsx)(t.strong,{children:"Schema A \u2014 the agent has to guess."})}),"\n",(0,s.jsx)(t.p,{children:"Nothing in the schema warns the agent that buyer geography changes over time. So the agent reaches for the easiest source available \u2014 the buyer's current shipping address:"}),"\n",(0,s.jsx)(o.Lg,{variant:"error",children:(0,s.jsx)(t.p,{children:"\"We will derive buyer geography from the buyer's default shipping address \u2014\nfor most marketplaces, the default shipping address is the best simple proxy\nfor 'where this buyer lives/operates.'\""})}),"\n",(0,s.jsxs)(t.p,{children:["As a result, ",(0,s.jsx)(t.code,{children:"buyer_geo_dim"})," reflects whatever address each buyer has on file ",(0,s.jsx)(t.em,{children:"right now"})," \u2014 not the address they had when each order was placed. So when a California buyer moves to New York in 2026 and updates their default address, ",(0,s.jsx)(t.strong,{children:"every one of their historical 2024 California orders retroactively appears as a New York order"}),"."]}),"\n",(0,s.jsx)(t.p,{children:"The model compiles. The job runs. The dashboard looks exactly like the CFO asked for. A year of regional reporting silently rewrites itself, week by week, as buyers edit their profiles. There is no obvious error. No validation fires. Nobody catches it."}),"\n",(0,s.jsx)(t.p,{children:(0,s.jsx)(t.strong,{children:"Schema B \u2014 the agent reads the note."})}),"\n",(0,s.jsx)(t.p,{children:"Run the same prompt against the context-rich schema. Before proposing a model, the agent flags a problem:"}),"\n",(0,s.jsx)(o.Lg,{variant:"success",children:(0,s.jsxs)(t.p,{children:['"Given the current schema and the ',(0,s.jsx)(t.strong,{children:"'buyer geography is mutable' caveat"}),", I'd\nmodel this as its own analytic group that uses ",(0,s.jsx)(t.strong,{children:"snapshotted geography, not\nlive addresses"}),'."']})}),"\n",(0,s.jsxs)(t.p,{children:["That \"caveat\" isn't something the agent guessed. It's a single ",(0,s.jsx)(t.code,{children:"CRITICAL:"})," note on the ",(0,s.jsx)(t.code,{children:"buyer_addresses"})," table \u2014 captured automatically by the Git bot the day an engineer added it \u2014 that warns anyone using the table for historical analysis to snapshot the address at order time instead."]}),"\n",(0,s.jsxs)(t.p,{children:["That one note is the entire reason the agent reached for a ",(0,s.jsx)(t.strong,{children:"snapshot"})," table \u2014 an immutable point-in-time capture of geography at order time. ",(0,s.jsx)(t.code,{children:"buyer_order_geo_snapshots"})," writes one row per order, captures the shipping state at the moment of checkout, and never updates it. The California buyer who moves to New York stays correctly attributed to California for every order they placed there."]}),"\n",(0,s.jsxs)(t.p,{children:["Nothing in the columns, types, or foreign keys signals ",(0,s.jsx)(t.em,{children:'"this address is mutable in a way that breaks historical analysis."'})," The shape of the data does not contain that fact. The captured intent does. ",(0,s.jsx)(t.strong,{children:"The schema told the agent what the columns could not."})]}),"\n",(0,s.jsx)(t.p,{children:"Same schema. Same buyer. Same orders. Two completely different models \u2014 one that will ship a corrupt geography chart to the board, and one that won't. The difference is whether the schema remembered to tell you."}),"\n",(0,s.jsx)(t.hr,{}),"\n",(0,s.jsx)(t.h2,{id:"the-modeling-inventory-from-captured-context-to-
1pre-built-data-models",children:"The modeling inventory: from captured context to pre-built data models"}),"\n",(0,s.jsx)(t.p,{children:"Captured context compounds. A note written once silently shapes every model that touches the schema later \u2014 every cohort, every ML feature, every report, every coding agent that comes through."}),"\n",(0,s.jsx)(t.p,{children:"But the compounding rarely happens. Most analytics teams are sitting on a deep backlog of models that this kind of context should be quietly powering \u2014 and every one of them still has to be designed from scratch."}),"\n",(0,s.jsxs)(t.p,{children:[(0,s.jsx)(t.strong,{children:"The modeling inventory"})," is that backlog, drafted in advance."]}),"\n",(0,s.jsxs)(t.p,{children:["Once context about your transactional schema is reliable, TalkingSchema no longer has to wait for anyone to ask. It already understands the shape of the business: what tables exist, what each column means, where the grain lives, who owns each domain, and which KPIs matter. With that context, it can draft the downstream models a team is likely to need ",(0,s.jsx)(t.em,{children:"before"})," they ask \u2014 as ready-to-review work."]}),"\n",(0,s.jsx)(t.p,{children:(0,s.jsx)(t.img,{alt:"Three-panel illustration of the modeling inventory in action: the first panel shows a pre-built "Seller Revenue Retention" model card with grain, KPIs, stakeholders, and source OLTP tables linked to the transformed model ERD; the second panel shows a BI analyst, data scientist, and ML engineer iterating on the model together in TalkingSchema's ERD canvas; the third panel shows one click generating a complete pipeline spec with stack options (Snowflake, dbt, Databricks), handed off to a data engineer or coding agent.",src:n(9251).A+"",width:"1672",height:"941"})}),"\n",(0,s.jsxs)(t.p,{children:[(0,s.jsx)(t.strong,{children:"What gets pre-built."})," Each model lands as a ",(0,s.jsx)(t.strong,{children:"data model file"})," \u2014 an opinionated technical doc that explains the model in both business and technical terms (grain, primary keys, KPI definitions, compliance summary, lineage to OLTP source tables, sample queries, edge cases) and links directly into an ERD canvas where the model is already rendered. Not a wireframe. Not a placeholder. An actual proposed model that the team can read end-to-end on day one."]}),"\n",(0,s.jsxs)(t.p,{children:[(0,s.jsx)(t.strong,{children:"How models are organized."})," Pre-built models are grouped along two axes data teams already think in:"]}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsxs)(t.li,{children:[(0,s.jsx)(t.strong,{children:"By business domain"})," \u2014 Revenue, Customer, Product, Marketing, Operations, Finance. Each domain surfaces every model that touches that subject area. Opening ",(0,s.jsx)(t.em,{children:"Revenue"})," shows you cohort tables, subscription facts, and payout marts."]}),"\n",(0,s.jsxs)(t.li,{children:[(0,s.jsx)(t.strong,{children:"By model type"})," \u2014 ",(0,s.jsx)(t.strong,{children:"Marts"})," (aggregated, BI-ready rollups for dashboards), ",(0,s.jsx)(t.strong,{children:"Feature stores"})," (entity-grain, point-in-time-correct for ML training and inference), and ",(0,s.jsx)(t.strong,{children:"Dimensional models"})," (star/snowflake schemas for ad-hoc analytical querying). Every analytics engineer and data scientist immediately knows which shelf to reach for."]}),"\n"]}),"\n",(0,s.jsxs)(t.p,{children:[(0,s.jsx)(t.strong,{children:"Iterating on a proposed model."})," No pre-built model ships perfect."]}),"\n",(0,s.jsxs)(t.p,{children:["Say the ML team opens the ",(0,s.jsx)(t.em,{children:"Customer Churn Feature Set"})," and realizes it's missing a feature: days since last login, in both a 7-day and 30-day rolling version. They don't file a ticket. They open the model in TalkingSchema's ERD canvas, describe the change, and it's done. The model updates, lineage stays intact, and everyone using it sees the revision immediately."]}),"\n",(0,s.jsxs)(t.p,{children:[(0,s.jsx)(t.strong,{children:"Shipping the model."})," Once the team is satisfied, one click turns the model into a ",(0,s.jsx)(t.strong,{children:"pipeline spec"})," for their stack \u2014 Snowflake, Databricks, or BigQuery with dbt or Airflow on top \u2014 covering source tables, transformations, grain, tests, and SLAs."]}),"\n",(0,s.jsx)(t.p,{children:"From there, refine it further with TalkingSchema, or hand it off to an engineer or coding agent to build."}),"\n",(0,s.jsx)(o.Pq,{children:(0,s.jsx)(t.p,{children:"A downstream model stops being a six-week project that starts with a Slack\nping. It becomes a piece of work that's already 80% drafted by the time the\nteam opens TalkingSchema, and gets shipped through the same conversation that\nrefined it."})}),"\n",(0,s.jsx)(t.p,{children:"That's the bet. The modeling inventory is a shelf of ready-to-ship downstream models, grouped for the people who actually need them, and one click away from a pipeline spec their stack can build."}),"\n",(0,s.jsx)(t.hr,{}),"\n",(0,s.jsx)(t.h2,{id:"advanced-modeling-every-possibility-not-just-the-obvious-ones",children:"Advanced Modeling: every possibility, not just the obvious ones"}),"\n",(0,s.jsx)(t.p,{children:"The real promise of Advanced Modeling is that it expands what your business can think to ask \u2014 beyond what your team already knows it needs."}),"\n",(0,s.jsx)(t.p,{children:"Today, someone \u2014 usually a senior analytics engineer or data lead \u2014 drives the modeling agenda. They build the models they can think of. The ones they can't think of never exist. This isn't a failure of talent. It's a failure of surface area: no single person can enumerate every anal
1ytically meaningful slice of a complex transactional schema. Not the senior engineer. Not the data lead. Not a team of four."}),"\n",(0,s.jsx)(t.p,{children:"There's a clean way to state the asymmetry:"}),"\n",(0,s.jsxs)(t.blockquote,{children:["\n",(0,s.jsx)(t.p,{children:(0,s.jsxs)(t.strong,{children:["|M",(0,s.jsx)("sub",{children:"possible"}),"| \u226b |M",(0,s.jsx)("sub",{children:"built"}),"|"]})}),"\n",(0,s.jsxs)(t.p,{children:["The set of analytical models a schema ",(0,s.jsx)(t.em,{children:"could"})," power grows combinatorially with tables, grains, KPIs, and segmentations. The set a team has actually ",(0,s.jsx)(t.em,{children:"built"})," grows linearly with headcount and the quarters available. The gap widens every time the schema does."]}),"\n"]}),"\n",(0,s.jsx)(t.p,{children:(0,s.jsx)(t.img,{alt:"An antique cartographer's map of an archipelago \u2014 a small cluster of islands carefully charted in ink, with the rest of the sea fading into faintly-sketched uncharted territory marked Terra Incognita \u2014 a metaphor for the small subset of analytical models any team has actually mapped versus the vast possibility space the data could support",src:n(7188).A+"",width:"1536",height:"1024"})}),"\n",(0,s.jsx)(t.p,{children:(0,s.jsx)(t.em,{children:"A few islands carefully explored. The rest of the archipelago is already there \u2014 just waiting to be discovered."})}),"\n",(0,s.jsx)(t.p,{children:(0,s.jsx)(t.strong,{children:"Advanced Modeling changes the calculus."})}),"\n",(0,s.jsx)(t.p,{children:"When TalkingSchema has full context on your OLTP schema, it can enumerate the models itself. Every analytically coherent grain. Every fact-to-dimension relationship. Every KPI that can be derived from the data that already exists. The modeling inventory becomes the complete set of business questions your current data can answer."}),"\n",(0,s.jsx)(t.p,{children:"For most organizations, that inventory is startlingly large. And startlingly underexploited."}),"\n",(0,s.jsx)(t.p,{children:"The most consequential reader of it is the executive \u2014 because every executive operates from a mental model of the business that was assembled, often years ago, by whichever team lead had two weeks and a vague brief. The quality of every strategic decision they make is bounded by the quality of that assembled model. What they rarely have is a complete picture of what their data can tell them."}),"\n",(0,s.jsx)(t.p,{children:"TalkingSchema gives leaders a common analytical surface across the business \u2014 Revenue, Customer, Product, Marketing, Operations, Finance. The executive stops being dependent on whichever subject matter expert happened to propose the questions worth asking."}),"\n",(0,s.jsx)(t.p,{children:"The shape of the problem is familiar to anyone who's looked at a power law:"}),"\n",(0,s.jsxs)(t.blockquote,{children:["\n",(0,s.jsxs)(t.p,{children:["Attention spent on analytical models follows a ",(0,s.jsx)(t.strong,{children:"long-tail distribution"}),". A handful of obvious dashboards \u2014 revenue, churn, retention \u2014 absorb almost all the attention. The long tail of strategy-changing models almost never gets touched, even though that's where the leverage usually lives."]}),"\n"]}),"\n",(0,s.jsx)(t.p,{children:(0,s.jsx)(t.img,{alt:"The long tail of analytical opportunities: the head gets all the attention, but the high-leverage strategy-changing models live in the long tail",src:n(7899).A+"",width:"1536",height:"1024"})}),"\n",(0,s.jsx)(t.p,{children:"That is not a small thing. The models that never get built are the ones that contain the insights that change strategy \u2014 and they don't get built not because they're hard, but because no one had the context to know they were possible."}),"\n",(0,s.jsx)(t.p,{children:"The inventory makes them visible. The questions nobody knew to ask, whose answers were already sitting in your database, finally get asked."}),"\n",(0,s.jsx)(t.hr,{}),"\n",(0,s.jsxs)(t.p,{children:[(0,s.jsx)(t.a,{href:"https://talkingschema.ai/app",children:"Start with your existing schema"}),". Import it, bring in the context you already have, and see the modeling inventory it generates. The questions your data can answer have always been there. Now there's a way to see all of them at once."]}),"\n",(0,s.jsx)("script",{type:"application/ld+json",children:JSON.stringify({"@context":"https://schema.org","@type":"FAQPage",mainEntity:[{"@type":"Question",name:"What is the difference between normalized and context-aware schema design?",acceptedA
1nswer:{"@type":"Answer",text:"A normalized schema eliminates data redundancy and enforces referential integrity \u2014 columns contain the right values and foreign keys check out. A context-aware schema additionally captures why columns exist, what business decisions shaped each table, what the grain of a row is, which KPIs a table feeds, and which downstream models can be built from it. Normalization solves the structural problem. Context-awareness solves the semantic problem \u2014 preventing AI agents, analysts, and new engineers from reverse-engineering meaning from names alone."}},{"@type":"Question",name:"Why do AI coding agents generate wrong data models from correctly normalized schemas?",acceptedAnswer:{"@type":"Answer",text:"Because normalized schemas describe structure but not semantics. An agent given a 3NF schema can see that buyer_addresses has a foreign key to users \u2014 but it cannot see that buyer geography is mutable (people move), which means historical revenue analysis needs a snapshot of geography at order time, not today's address. Without that context, the agent correctly uses the available foreign key and builds a model that silently corrupts historical reporting. The fix is capturing business intent as column-level annotations so the schema itself communicates the constraint."}},{"@type":"Question",name:"What is a data modeling inventory?",acceptedAnswer:{"@type":"Answer",text:"A modeling inventory is a pre-built catalog of downstream analytical models that your OLTP schema could power \u2014 organized by business domain (Revenue, Customer, Product) and model type (Marts, Feature Stores, Dimensional Models). Instead of waiting for analysts to ask for models, a modeling inventory drafts them in advance from the accumulated context about the schema. Each model lands as a data model file with grain, KPI definitions, compliance summary, OLTP source lineage, and a rendered ERD canvas \u2014 ready to review rather than starting from scratch."}},{"@type":"Question",name:"What is the TalkingSchema Git bot and how does it capture schema context?",acceptedAnswer:{"@type":"Answer",text:"The TalkingSchema Git bot is a PR-level automation that treats every database migration pull request as a context-capture opportunity. When a PR touches schema-related files, the bot requests that the coding agent push the relevant feature conversation context \u2014 the reasoning behind the change, field intent, business rules, constraints \u2014 to TalkingSchema. For anything the agent can't answer, it asks the engineer directly in the PR. The captured context is permanently linked to the affected schema elements as AI-readable annotations."}},{"@type":"Question",name:"How does schema context propagate to downstream analytics?",acceptedAnswer:{"@type":"Answer",text:"Once captured, schema context shapes every model built from that schema. A CRITICAL: note on buyer_addresses that says 'buyer geography is mutable \u2014 snapshot at order time for historical analysis' will cause any AI agent querying that schema to prefer an address snapshot table over a current-address join. The context doesn't need to be repeated for each new model request \u2014 it's permanently attached to the schema element and automatically included in any AI context window that touches it."}}]})})]})}function m(e={}){const{wrapper:t}={...(0,i.R)(),...e.components};return t?(0,s.jsx)(t,{...e,children:(0,s.jsx)(d,{...e})}):d(e)}},6363(e,t,n){n.d(t,{Lg:()=>u,Pq:()=>c,pO:()=>v,C1:()=>p,L:()=>j,HV:()=>k,w:()=>T,Ig:()=>h});var a=n(6540),s=n(4848);const i="allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox";function o(){return(0,s.jsx)("svg",{width:"14",height:"14",viewBox:"0 0 16 16",fill:"none",stroke:"currentColor",strokeWidth:"1.75",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:(0,s.jsx)("path",{d:"M10 2h4v4M6 14H2v-4M14 10v4h-4M2 6V2h4"})})}function r(){return(0,s.jsxs)("svg",{width:"13",height:"13",viewBox:"0 0 16 16",fill:"none",stroke:"currentColor",strokeWidth:"1.75",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:[(0,s.jsx)("path",{d:"M6 3H3a1 1 0 0 0-1 1v9a1 1 0 0 0 1 1h9a1 1 0 0 0 1-1v-3"}),(0,s.jsx)("path",{d:"M10 2h4v4m0-4L8 8"})]})}function l(){return(0,s.jsx)("svg",{width:"13",height:"13",viewBox:"0 0 16 16",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round","aria-hidden":"true",children:(0,s.jsx)("path",{d:"M2 2l12 12M14 2L2 14"})})}function h({threadId:e,title:t,height:n=640,baseUrl:h="https://talkingschema.ai"}){const[c,d]=(0,a.useState)(!1),m=`${h}/public/${e}`;return(0,a.useEffect)(()=>
1{if(!c)return;const e=e=>{"Escape"===e.key&&d(!1)};return window.addEventListener("keydown",e),()=>window.removeEventListener("keydown",e)},[c]),(0,a.useEffect)(()=>(document.body.style.overflow=c?"hidden":"",()=>{document.body.style.overflow=""}),[c]),(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)("div",{className:"ts-thread-embed",children:[(0,s.jsxs)("div",{className:"ts-thread-embed-toolbar",children:[(0,s.jsx)("span",{className:"ts-thread-embed-toolbar-title",children:t}),(0,s.jsx)("div",{className:"ts-thread-embed-toolbar-actions",children:(0,s.jsxs)("button",{className:"ts-embed-btn",onClick:()=>d(!0),title:"Expand schema",children:[(0,s.jsx)(o,{}),"Expand"]})})]}),(0,s.jsx)("iframe",{src:m,title:t,style:{height:`${n}px`},sandbox:i,allowFullScreen:!0,loading:"lazy"}),(0,s.jsx)("div",{className:"ts-thread-embed-label",children:(0,s.jsx)("a",{href:m,target:"_blank",rel:"noopener noreferrer",children:"Open full screen \u2197"})})]}),c&&(0,s.jsx)("div",{className:"ts-thread-modal-backdrop",onClick:()=>d(!1),role:"dialog","aria-modal":"true","aria-label":t,children:(0,s.jsxs)("div",{className:"ts-thread-modal",onClick:e=>e.stopPropagation(),children:[(0,s.jsxs)("div",{className:"ts-thread-modal-toolbar",children:[(0,s.jsx)("span",{className:"ts-thread-modal-toolbar-title",children:t}),(0,s.jsxs)("div",{className:"ts-thread-modal-toolbar-actions",children:[(0,s.jsxs)("a",{href:m,target:"_blank",rel:"noopener noreferrer",className:"ts-embed-btn",title:"Open in new tab",children:[(0,s.jsx)(r,{}),"Open"]}),(0,s.jsxs)("button",{className:"ts-embed-btn ts-embed-btn--close",onClick:()=>d(!1),title:"Close (Esc)",children:[(0,s.jsx)(l,{}),"Close"]})]})]}),(0,s.jsx)("iframe",{src:m,title:t,sandbox:i,allowFullScreen:!0,style:{flex:1,width:"100%",border:"none",display:"block"}})]})})]})}function c({children:e}){return(0,s.jsx)("div",{className:"ts-callout",children:e})}var d=n(883);const m={error:"Bug",success:"Correct",talkingschema:"TalkingSchema"};function u({variant:e,label:t,children:n}){const i=t??m[e],o=(0,d.Ay)("/img/logo-icon.svg"),r="talkingschema"===e,{collapseRef:l,expanded:h,hasOverflow:c,toggleExpanded:u}=function({enabled:e,contentDep:t}){const n=(0,a.useRef)(null),[s,i]=(0,a.useState)(!1),[o,r]=(0,a.useState)(!1),l=(0,a.useCallback)(()=>{i(e=>!e)},[]);return(0,a.useEffect)(()=>{if(!e)return i(!1),void r(!1);const t=()=>{const e=n.current;e&&!s&&r(e.scrollHeight>e.clientHeight+2)};let a=null,o=null;const l=()=>{null!==a&&cancelAnimationFrame(a),a=requestAnimationFrame(t)};return l(),"undefined"!=typeof document&&document.fonts?.ready&&(document.fonts.ready.then(l),document.fonts.addEventListener?.("loadingdone",l)),"undefined"!=typeof ResizeObserver&&n.current&&(o=new ResizeObserver(l),o.observe(n.current)),"undefined"!=typeof window&&window.addEventListener("resize",l),()=>{null!==a&&cancelAnimationFrame(a),o?.disconnect(),"undefined"!=typeof window&&window.removeEventListener("resize",l),"undefined"!=typeof document&&document.fonts&&document.fonts.removeEventListener?.("loadingdone",l)}},[t,e,s]),{collapseRef:n,expanded:s,hasOverflow:o,toggleExpanded:l}}({enabled:r,contentDep:n});return(0,s.jsxs)("blockquote",{className:`ts-alert-quote ts-alert-quote--${e}`,children:["talkingschema"===e?(0,s.jsx)("img",{src:o,alt:"","aria-hidden":"true",className:"ts-alert-quote__icon"}):(0,s.jsx)("span",{className:"ts-alert-quote__badge",children:i}),(0,s.jsx)("div",{className:"ts-alert-quote__body",children:r?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)("div",{ref:l,className:"ts-alert-quote__collapse-body"+(h?" ts-alert-quote__collapse-body--expanded":""),children:n}),c&&(0,s.jsx)("button",{className:"ts-alert-quote__toggle",onClick:u,"aria-expanded":h,children:h?"Show less \u2191":"Show more \u2193"})]}):n})]})}n(3587);const g=a.createContext(null);function p({tabIndex:e,anchorId:t,children:n}){const i=(0,a.useContext)(g);return(0,a.useEffect)(()=>{i?.registerAnchor(t,e)},[t,i,e]),i?(0,s.jsx)("a",{href:`#${t}`,onClick:e=>{e.preventDefault(),i.navigateToAnchor(t),window.history.replaceState(null,"",`#${t}`)},children:n}):(0,s.jsx)("a",{href:`#${t}`,children:n})}function b({rotated:e}){return(0,s.jsx)("svg",{width:"11",height:"11",viewBox:"0 0 16 16",fill:"none",stroke:"currentColor",strokeWidth:"2.25",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",style:{transform:e?"rotate(180deg)":"rotate(0deg)",transition:"transform 0.2s ease",flexShrink:0},children:(0,s.jsx)("path",{d:"M4 6l4 4 4-4"})})}function y(){return(0,s.jsx)("svg",{width:"14",height:"14",viewBox:"0 0 16 16",fill:"none",stroke:"currentColor",strokeWidth:"2.25",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",style:{flexShrink:0,transition:"transform 0.15s ease"},className:"ts-dtabs-continue-arrow",children:(0,s.jsx)("path",{d:"M3 8h10M9 4l4 4-4 4"})})}function x(){return(0,s.jsx)("svg",{width:"12",height:"12",viewBox:"0 0 16 16",fill:"none",stroke:"currentColor",strokeWidth:"2.25",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",style:{flexShrink:0,transition:"transform 0.15s ease"},className:"ts-dtabs-prev-arrow",children:(0,s.jsx)("path",{d:"M13 8H3M7 4L3 8l4 4"})})}const f={"first-star":0,"conformed-bus":0,"edw-core":1,"dv-raw-vault-section":2,"dv-raw-vault":2,"dv-business-vault-section":2,"dv-business-vault":2};function w({children:e,heading:t="Architecture Deep Dives"}){const n=a.Children.toArray(e),[i,o]=(0,a.useState)(null),[r,l]=(0,a.useState)(new Set),h=(0,a.useRef)(null),c=(0,a.useRef)(new Map),d=(0,a.useCallback)((e,t)=>{c.current.set(e,t)},[]),m=(0,a.useCallback)(e=>{const t=c.current.get(e)??f[e];void 0!==t&&(o(t),l(e=>{if(e.has(t))return e;const n=new Set(e);return n.add(t),n})),window.requestAnimationFrame(()=>{window.setTimeout(()=>{document.getElementById(e)?.scrollIntoView({behavior:"smooth",block:"start"})},void 0!==t?120:0)})},[]);function u(e){const t=h.current;if(!t)return;t.scrollIntoView({behavior:"smooth",block:"start"});let n=!1;function a(){n||(n=!0,o(e),l(t=>
1{if(t.has(e))return t;const n=new Set(t);return n.add(e),n}))}const s=new IntersectionObserver(e=>{e[0].isIntersecting&&(s.disconnect(),clearTimeout(i),setTimeout(a,1500))},{threshold:.5});s.observe(t);const i=setTimeout(()=>{s.disconnect(),a()},1850)}return(0,a.useEffect)(()=>{function e(){const e=window.location.hash.slice(1);e&&(c.current.has(e)||void 0!==f[e])&&m(e)}return e(),window.addEventListener("hashchange",e),()=>window.removeEventListener("hashchange",e)},[m]),(0,s.jsx)(g.Provider,{value:{registerAnchor:d,navigateToAnchor:m},children:(0,s.jsxs)("div",{className:"ts-dtabs",ref:h,children:[(0,s.jsxs)("div",{className:"ts-dtabs-section-header",children:[(0,s.jsx)("span",{className:"ts-dtabs-section-label",children:"Continue reading"}),(0,s.jsx)("p",{className:"ts-dtabs-section-title",children:t})]}),(0,s.jsx)("div",{className:"ts-dtabs-triggers",children:n.map((e,t)=>{const{tag:n,title:a,description:r,accentColor:h}=e.props,c=h??"var(--ifm-color-primary)",d=h&&h.startsWith("#")?`${h}12`:void 0,m=i===t;return(0,s.jsxs)("button",{className:"ts-dtabs-trigger-card"+(m?" ts-dtabs-trigger-card--active":""),style:{"--ts-card-accent":c,...m&&d?{background:d}:{}},onClick:()=>function(e){i===e?o(null):(o(e),l(t=>{if(t.has(e))return t;const n=new Set(t);return n.add(e),n}))}(t),"aria-expanded":m,children:[(0,s.jsx)("span",{className:"ts-dtabs-trigger-tag",children:n}),a&&(0,s.jsx)("p",{className:"ts-dtabs-trigger-title",children:a}),r&&(0,s.jsx)("p",{className:"ts-dtabs-trigger-desc",children:r}),(0,s.jsxs)("span",{className:"ts-dtabs-trigger-cta",children:[m?"Collapse":"Read deep dive",(0,s.jsx)(b,{rotated:m})]})]},t)})}),n.map((e,t)=>{const a=t<n.length-1?n[t+1]:null,o=a?.props.accentColor??"var(--ifm-color-primary)",l=t>0?n[t-1]:null,h=l?.props.accentColor??"var(--ifm-color-primary)",c=e.props.accentColor??"var(--ifm-color-primary)";return(0,s.jsxs)("div",{className:"ts-dtabs-panel",style:{display:i===t?"block":"none","--ts-panel-accent":c},"aria-hidden":i!==t,children:[e.props.children,r.has(t)&&(l||a)&&(0,s.jsxs)("div",{className:"ts-dtabs-continue",children:[l&&(0,s.jsxs)("button",{className:"ts-dtabs-prev-btn",style:{"--ts-prev-accent":h},onClick:()=>u(t-1),children:[(0,s.jsx)(x,{}),(0,s.jsx)("span",{className:"ts-dtabs-prev-label",children:"Previous Page"}),(0,s.jsxs)("span",{className:"ts-dtabs-prev-next",children:[(0,s.jsx)("span",{className:"ts-dtabs-prev-tag",style:{color:h},children:l.props.tag}),l.props.title&&(0,s.jsx)("span",{className:"ts-dtabs-prev-title",children:l.props.title})]})]}),a&&(0,s.jsxs)("button",{className:"ts-dtabs-continue-btn",style:{"--ts-continue-accent":o},onClick:()=>u(t+1),children:[(0,s.jsx)("span",{className:"ts-dtabs-continue-label",children:"Next Page"}),(0,s.jsxs)("span",{className:"ts-dtabs-continue-next",children:[(0,s.jsx)("span",{className:"ts-dtabs-continue-tag",style:{color:o},children:a.props.tag}),a.props.title&&(0,s.jsx)("span",{className:"ts-dtabs-continue-title",children:a.props.title})]}),(0,s.jsx)(y,{})]})]})]},t)})]})})}w.Tab=function(e){return null};const v=w;function j({links:e,label:t="Further reading"}){const n=[...e,...e];return(0,s.jsxs)("div",{className:"ts-ticker-wrap",children:[(0,s.jsx)("span",{className:"ts-ticker-label",children:t}),(0,s.jsx)("div",{className:"ts-ticker-viewport",children:(0,s.jsx)("div",{className:"ts-ticker-track",children:n.map((e,t)=>(0,s.jsxs)("a",{href:e.href,target:"_blank",rel:"noopener noreferrer",className:"ts-ticker-item",children:[(0,s.jsx)("span",{className:"ts-ticker-source",children:e.source}),(0,s.jsx)("span",{className:"ts-ticker-title",children:e.title})]},t))})})]})}function k({children:e}){return(0,s.jsxs)("div",{className:"ts-prompt-bubble",role:"note","aria-label":"User prompt",children:[(0,s.jsxs)("svg",{className:"ts-prompt-bubble__icon",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:1.6,strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",focusable:"false",children:[(0,s.jsx)("circle",{cx:"12",cy:"8.5",r:"3.5"}),(0,s.jsx)("path",{d:"M4.5 20c0-3.6 3.4-6.2 7.5-6.2s7.5 2.6 7.5 6.2"})]}),(0,s.jsx)("div",{className:"ts-prompt-bubble__body",children:e})]})}function T({children:e,id:t}){return(0,s.jsx)("h4",{className:"ts-section-heading",id:t,children:e})}},7188(e,t,n){n.d(t,{A:()=>a});
1const a=n.p+"assets/images/archipelago-charted-vs-uncharted-fe35135f0348c3ac41f3d2d7b6058c31.png"},7899(e,t,n){n.d(t,{A:()=>a});const a=n.p+"assets/images/long-tail-power-law-distribution-db5eb9f3460aa3436f1ede4bbb8868ea.png"},9251(e,t,n){n.d(t,{A:()=>a});const a=n.p+"assets/images/talkingschema-advanced-modeling-artifact-5173e6d1df2182f52b314a38d916689a.png"},8959(e,t,n){n.d(t,{A:()=>a});const a=n.p+"assets/images/talkingschema-git-bot-flywheel-1cd1b6af21e5f5488f019ab50c9e376c.png"},3023(e,t,n){n.d(t,{R:()=>o,x:()=>r});var a=n(6540);const s={},i=a.createContext(s);function o(e){const t=a.useContext(i);return a.useMemo(function(){return"function"==typeof e?e(t):{...t,...e}},[t,e])}function r(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:o(e.components),a.createElement(i.Provider,{value:t},e.children)}},559(e){e.exports=JSON.parse('{"permalink":"/blog/database-normalization-practical-guide","source":"@site/blog/2026-04-28-normalized-but-meaningless-oltp-design.mdx","title":"Database Normalization Is Not Enough: Context-Aware OLTP Schema Design","description":"Your OLTP schema is normalized, constrained, and technically correct \u2014 and yet analysts, ML engineers, and executives downstream still reverse-engineer what columns mean. This guide covers context-aware OLTP design: capturing column intent with the TalkingSchema Git bot, using schema context to prevent AI-generated data models from silently lying, and building a modeling inventory that surfaces every analytical model your data could power.","date":"2026-04-28T00:00:00.000Z","tags":[{"inline":true,"label":"database normalization","permalink":"/blog/tags/database-normalization"},{"inline":true,"label":"oltp schema design","permalink":"/blog/tags/oltp-schema-design"},{"inline":true,"label":"context-aware data modeling","permalink":"/blog/tags/context-aware-data-modeling"},{"inline":true,"label":"data catalog alternative","permalink":"/blog/tags/data-catalog-alternative"},{"inline":true,"label":"schema documentation","permalink":"/blog/tags/schema-documentation"},{"inline":true,"label":"schema business context capture","permalink":"/blog/tags/schema-business-context-capture"},{"inline":true,"label":"git bot schema review","permalink":"/blog/tags/git-bot-schema-review"},{"inline":true,"label":"data modeling inventory","permalink":"/blog/tags/data-modeling-inventory"},{"inline":true,"label":"ai data modeling","permalink":"/blog/tags/ai-data-modeling"},{"inline":true,"label":"schema intent documentation","permalink":"/blog/tags/schema-intent-documentation"},{"inline":true,"label":"coding agent database migration","permalink":"/blog/tags/coding-agent-database-migration"}],"readingTime":13.86,"hasTruncateMarker":true,"authors":[{"name":"Karan Patel","title":"Founder, CEO","url":"https://talkingschema.ai/docs/authors/karan-patel","socials":{"linkedin":"https://www.linkedin.com/in/karansql/","github":"https://github.com/KaranDrivesEvents"},"imageURL":"https://avatars.githubusercontent.com/u/201362562?v=4&s=400","key":"karan","page":null}],"frontMatter":{"slug":"database-normalization-practical-guide","title":"Database Normalization Is Not Enough: Context-Aware OLTP Schema Design","image":"/img/talkingschema-git-bot-flywheel.png","description":"Your OLTP schema is normalized, constrained, and technically correct \u2014 and yet analysts, ML engineers, and executives downstream still reverse-engineer what columns mean. This guide covers context-aware OLTP design: capturing column intent with the TalkingSchema Git bot, using schema context to prevent AI-generated data models from silently lying, and building a modeling inventory that surfaces every analytical model your data could power.","authors":["karan"],"tags":["database normalization","oltp schema design","context-aware data modeling","data catalog alternative","schema documentation","schema business context capture","git bot schema review","data modeling inventory","ai data modeling","schema intent documentation","coding agent database migration"],"date":"2026-04-28T00:00:00.000Z","last_modified":"2026-05-25T00:00:00.000Z","sidebar_label":"Database Normalization Is Not Enough: Context-Aware OLTP Schema Design__TS_BLOG_META__14 min read"},"unlisted":false,"prevItem":{"title":"The Missing Layer in ERD Tools: Why Database Schemas Need Business Context","permalink":"/blog/missing-layer-in-erd-tools"},"nextItem":{"title":"Star Schema vs Snowflake Schema: When to Use Each, with dbt and BI Tool Guidance","permalink":"/blog/star-schema-vs-snowflake-schema"}}')}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.