The interviews are done. Someone sat with the domain expert and committed `ontology.md` next to the code. Then the quarter got busy. Six months later a report counts the same `Customer` three times, and the model built to prevent exactly that has not changed since launch. An ontology earns its keep only when a team uses it. A [software ontology][what-ontology] is a shared model of a domain: its entities, their attributes, and their relationships, written so people and code agree on what each term means. This guide assumes the model already exists. The goal is to put it to work: in workflows, design sessions, database schemas, and on every screen where a teammate looks something up. Pick the section that matches the task in front of you. The command examples assume PostgreSQL and `psql`, but the practice maps to any engine that supports comments and constraints. The examples use the Subscription Billing context from the [creation guide][create-ontology], so the terms line up across both articles. ## Goal Operate an established ontology daily to maintain it as the single source of truth for language, guide the database schema, reflect current reality, and ensure easy access. ## Prerequisites **Required knowledge:** * Comfort reading a domain model: entities, value objects, aggregates, and their relationships. * Familiarity with the ontology’s DDD terms: ubiquitous language, bounded context, aggregate. * Working knowledge of the schema or codebase the ontology describes. **Required tools:** * The ontology in version control beside the code (e.g., `ontology.md` and `ontology.yaml`). * A diagram tool that renders relationships (Mermaid, PlantUML, or the native ontology editor). * Access to the schema migration workflow for the target database. * Command-line access to the database (e.g., `psql`) for drift check in Step 4. **Required access:** * Write access to the repository that holds the ontology. * Permission to open schema migration pull requests. * A place to publish the ontology where the whole team can read it. ## Step 1: Ground daily workflows in the ontology Treat the ontology as the dictionary every workflow checks against. It stops teams re-litigating what words mean in tickets, code review, and naming. Wire it into the moments where vocabulary drifts: * Use exact entity names in ticket writing to avoid ambiguity. For example, specifying “let a `Customer` keep one `Subscription` after cancellation” raises the question of whether `Customer` means the paying account or a signed-up user, which the ontology clarifies. * In code review, flag new classes, tables, or API fields without matching ontology. For example, a new `Account` table with no `Account` entity indicates a name issue or missing model concept. Both are important to catch. * In pull request templates, add a checkbox: "New domain terms reconciled with the ontology." It costs one line and prevents silent vocabulary forks. Success is simple: engineers resolve ambiguous `Customer` references by consulting the ontology rather than relying on memory. ## Step 2: Run design sessions against the ontology Design sessions introduce new concepts and influence the ontology's growth, intentionally or unintentionally. Share the screen before starting. Use it three ways during a session: * Open the bounded context to begin from agreed terms, not a blank slate. * As a referee, check if a proposed entity exists under another name. If the model has `Subscription`, a new `Membership` is likely the same concept. Renaming is better than creating duplicates. * When the session invents something new, record the term, its definition, and relationships in the ontology before the meeting ends, as notes promising to "add it later" rarely survive the week. A practical approach for event-storming or domain-modeling is to assign one person as the ontology scribe, responsible for reconciling sticky notes with the model in real time. The scribe asks, "Does this already exist, and if not, what is it related to?" to prevent vocabulary sprawl. ## Step 3: Transfer the ontology to the database schema The ontology and schema describe the same domain at different abstraction levels. The ontology defines concepts and relations; the schema shows storage. They won't match shape for shape: an aggregate can span several tables, and one table can serve several concepts. What stays consistent is the vocabulary. Anyone who knows the ontology should recognize the names. Carry the names and definitions with consistent rules: ```mermaid flowchart TB A[Entity] --> B[Table named for the entity] C[Entity attribute] --> D[Column named for the attribute] E[One-to-many relationship] --> F[Foreign key on the many side] G[Many-to-many relationship] --> H[Join table] I[Concept definition] --> J[Column or table comment] ``` Apply the mapping when creating or changing a schema. * Name tables and columns after the ubiquitous language, not abbreviations. If it’s `LineItem`, use `line_item`, not `li`. * Carry definitions into the database by pasting the ontology's definition on tables and columns, supported by most engines, so the meaning stays with the schema. * Encode relationships as constraints: "Many Invoices bill a Subscription" becomes a foreign key from `invoice` to `subscription`. A many-to-many becomes a join table. The database's cardinality should match the model. This migration carries an `Invoice` definition directly from the ontology into the schema: ```sql CREATE TABLE invoice ( invoice_id BIGINT PRIMARY KEY, subscription_id BIGINT NOT NULL REFERENCES subscription (subscription_id), billing_period_start DATE NOT NULL, billing_period_end DATE NOT NULL, total_cents BIGINT NOT NULL ); COMMENT ON TABLE invoice IS 'A demand for payment covering one billing period. Ontology: Invoice, bills exactly one Subscription.'; ``` The `subscription_id` foreign key indicates "many Invoices bill a Subscription." The comment repeats the glossary definition verbatim for clarity. Renaming a column to match the ontology breaks every query and view that referenced the old name. Replace the risky rename with three safe steps: add the new column, backfill it, migrate readers, then drop the old column once nothing uses it. Adding a `COMMENT` is reversible, but `DROP` or `RENAME` are not, so guard them with tested migrations and rollback plans. The success check: a reader with ontology knowledge can open the schema and identify every table without a translation guide. ## Step 4: Keep the ontology fresh An ontology that stops matching reality becomes a liability. People learn to ignore it, and then it documents a domain that no longer exists. Freshness comes from a cadence and a change process, not good intentions. Set up three habits: * Make ontology changes part of the work, not a follow-up. When a feature introduces a new concept, the same pull request that ships the code updates `ontology.md`. Reviewers block the merge if the term is missing. * Schedule a quarterly review to compare the ontology with the live schema, flagging nonexistent entities, changed relationships, and new concepts in the code but missing from the model. * Detect drift automatically. Compare live table names against ontology entity names and surface anything the schema has but the model lacks. Run it in continuous integration so drift shows up in a pull request, not six months later. This drift check reads entity names from `ontology.yaml`, the queryable form from the [worked example][ontology-repo], and lists tables present in the database but missing from the model. ```bash # Tables in the database with no matching ontology entity. # Tune the grep pattern to your ontology.yaml structure. comm -23 \ <(psql -Atqc "SELECT table_name FROM information_schema.tables \ WHERE table_schema = 'public' ORDER BY table_name") \ <(grep -oiP 'name:\s*\K\w+' ontology.yaml | tr 'A-Z' 'a-z' | sort -u) ``` **Expected output:** empty if schema and ontology match, or list of unmapped tables to reconcile. Fail CI job if list is non-empty. The signal this is working: the latest ontology update is days old, not quarters. ## Step 5: Make the ontology accessible to the team An ontology nobody can find goes unused. Ship accessibility as a feature, not a side effect. Lower the cost of looking something up: * Publish a browsable version; busy teammates skip raw files. Create a searchable web page or wiki for easy access. * Make it searchable by term so that typing `Subscription` leads directly to its definition, attributes, and relationships, avoiding scrolling through a 200-node diagram. * Link to the ontology from where work happens. Add the link in README, onboarding, and pull request template. Fewer clicks between a question and answer encourage consulting the ontology instead of guessing. * Use it in onboarding by giving new hires the ontology as a domain map. It helps convey vocabulary faster than reading code and establishes shared language expectations. Success is behavioral: teammates cite and link to the ontology in conversation and reviews unprompted. ## Step 6: Put AI to work with the ontology A large language model (LLM) has read the public internet, so it knows the average meaning of `Customer`, `Invoice`, and `Subscription`. It has not read your domain. Hand it the ontology and its output grounds in your billing context instead of a statistical average. The ontology supplies the missing context, turning a generic code generator into one that speaks your ubiquitous language. The [explanation article][what-ontology] covers why this works; this step covers how to wire it into the daily workflow. Feed the model the artifact, not a paraphrase. Paste `ontology.md` or `ontology.yaml` from the [worked example][ontology-repo] into the context window, or point an AI agent at the file in the repository. Then layer it onto the workflows from the earlier steps. * In ticket drafting (Step 1), ask the model to rewrite a vague ticket using exact entity names. "Let users pause billing" becomes "Suspend a `Subscription` without deleting its `Invoice` history," with the ambiguity surfaced for a human to settle. * In schema work (Step 3), give the model the ontology plus a target entity and ask for the migration. It names the table for the entity, copies the glossary definition into a `COMMENT`, and encodes the relationship as a foreign key, because the model is right there in the prompt. * In code review (Step 1), ask the model to flag any new class, table, or API field with no match in the ontology. It catches a stray `Account` against a model that only knows `Customer`. * In design sessions (Step 2), use the model as a quick referee. Paste a concept and ask if the ontology already names it. A proposed `Membership` matches the existing `Subscription` before duplicating. Here is a prompt that grounds a migration request in the model: ```text Here is our domain ontology (Subscription Billing context): Write a PostgreSQL migration for the LineItem entity. Name the table and columns after the ubiquitous language. Copy each definition into a COMMENT. Encode "an Invoice contains many LineItems" as a foreign key. Flag any term you cannot find in the ontology instead of inventing one. ``` Beware confident nonsense: invented relationships, a `Tier` table where the ontology says `Plan`, a definition that drifts from the glossary. The model proposes; you dispose. Run the same checks a human review would, and trust the ontology when the two conflict. The drift check from Step 4 is the backstop: if the model invents a table, continuous integration catches the term that never made it into the ontology. **Success check:** the model-generated code uses your entity names and definitions verbatim, and a reviewer familiar with the ontology spots nothing foreign. ## Verification Confirm the ontology is genuinely in use, not just present: * Open a recent pull request that introduced a domain concept. The ontology changed in the same request. * Pick a random table in the production schema. Its name and definition trace to an ontology entity. * Ask a teammate to find a domain term's definition. They find it in under a minute by search, without asking anyone. * Check the ontology's change history; the recent edit is linked to a real feature. * Hand the ontology to an AI agent and ask for a migration. The generated names and definitions match the glossary. If all five hold, the ontology is operational. ## Troubleshooting ### Problem: The schema and ontology have drifted apart **Symptoms:** Tables have unmatched entities or describe concepts not stored in the database. **Solution:** Run a one-time reconciliation by listing all tables, columns, ontology entities, and attributes, then compare the sets. For mismatches, determine if the schema or ontology is ahead and update accordingly. **If that doesn't work:** The drift is too large to fix at once. First, use the continuous-integration drift check from Step 4 to freeze new divergence, then close the gap gradually over several sprints. ### Problem: Nobody updates the ontology **Symptoms:** The last edit is months old, yet the domain has clearly changed. **Solution:** Integrate the update into the definition of done; a domain-touching feature isn’t complete until its pull request updates the ontology and reviewers enforce it. Process takes precedence over reminders. **If that doesn't work:** High friction; investigate causes. Formats that require specialized tools or unique files become outdated. Use a format everyone on the team can edit. ### Problem: Two words mean the same concept **Symptoms:** The ontology has `Plan`, but tickets, code, and sales decks all say `Tier` for the same thing. **Solution:** Resolve in a design session (Step 2). If the two are genuinely distinct, sharpen both definitions for clarity; if they denote a single concept, adopt the ontology's term (`Plan`) and update all references. **If that doesn't work:** The domain experts disagree, and the ambiguity is real. Escalate to the domain language owner and record the decision in the ontology as the tiebreaker. ### Problem: The ontology is too big to navigate **Symptoms:** A diagram with hundreds of entities that nobody opens. **Solution:** Split by bounded context and rely on search for lookups. Most want one definition, not the whole map. Provide a search box and a relevant slice. ### Problem: The AI invents terms the ontology never defined **Symptoms:** Generated code adds a `Tier` table when the ontology says `Plan`, or wires a relationship the model never specified. **Solution:** Tell the model to flag unknown terms instead of inventing them, and paste the ontology into the prompt rather than describing it. The drift check from Step 4 catches anything that slips through, since an invented table has no matching entity. **If that doesn't work:** The context window is too small for the whole model, so the AI fills gaps with guesses. Feed only the relevant bounded context, not the entire ontology, so the slice it needs fits with room to spare. ## Related Content * [What Is a Software Ontology?][what-ontology], for the concept this guide operates: ubiquitous language, bounded contexts, and aggregates. * [How Do I Create a Software Ontology?][create-ontology], for building the model before you put it to work. * [Fundamentals of Graph Databases][graph-databases], for storing a relationship-heavy ontology so it stays queryable. ## References * [How Do I Create a Software Ontology?][create-ontology], for the step-by-step process that produces the model this guide operates. * [What Is a Software Ontology?][what-ontology], for the concept and its Domain-Driven Design vocabulary. * [Domain-Driven Design][ddd-book] by Eric Evans, the source of the ubiquitous language, bounded context, and aggregate concepts used throughout. * [Diátaxis][diataxis], the documentation framework this guide follows. [what-ontology]: https://jeffbailey.us/blog/2026/05/28/what-is-a-software-ontology/ [create-ontology]: https://jeffbailey.us/blog/2026/05/29/how-do-i-create-a-software-ontology/ [graph-databases]: https://jeffbailey.us/blog/2026/02/14/fundamentals-of-graph-databases/ [ontology-repo]: https://github.com/jeffabailey/learn/tree/main/software/ontology [ddd-book]: https://www.domainlanguage.com/ddd/ [diataxis]: https://diataxis.fr/