Learning Platform
Глоссарий Troubleshooting
Урок 05.03 · 22 мин
Продвинутый
manifestschemaversioningmigration

Schema versions: эволюция manifest от v6 до v12, обратная совместимость

dbt-core релизит major version каждые ~6 месяцев, и каждая major version имеет potential для schema migration. manifest.json schema эволюционировал от v1 (dbt 0.x) до v12 (1.10/1.11), и v13 запланирован для 1.12. Для senior critical: знать, какие fields appeared/disappeared в какой версии, что бы поддерживать tools backwards-compatible или планировать миграцию.

В этом уроке — full timeline schema versions, key changes между versions, migration patterns, и как использовать schemas.getdbt.com для validation.


Model versions: v1/v2, latest_version, deprecation_date (dbt II)

schemas.getdbt.com

dbt Labs публикует formal JSON Schema для каждой artifact версии:

https://schemas.getdbt.com/dbt/manifest/v12.json
https://schemas.getdbt.com/dbt/run-results/v6.json
https://schemas.getdbt.com/dbt/catalog/v1.json
https://schemas.getdbt.com/dbt/sources/v3.json
https://schemas.getdbt.com/dbt/semantic_manifest/v1.json

В metadata.dbt_schema_version каждого артефакта указан URL:

{
  "metadata": {
    "dbt_schema_version": "https://schemas.getdbt.com/dbt/manifest/v12.json",
    "dbt_version": "1.11.0"
  }
}

Use cases:

  1. Validation: tool читает schema URL, fetches JSON Schema, валидирует manifest.
  2. Version detection: parse URL -> extract v12 -> branched logic.
  3. Documentation: schema file сам по себе документация — fields, types, required/optional, descriptions.
  4. Code generation: pydantic/typescript types из schema.
import requests
import json
from jsonschema import validate

manifest = json.load(open('target/manifest.json'))
schema_url = manifest['metadata']['dbt_schema_version']
schema = requests.get(schema_url).json()

validate(instance=manifest, schema=schema)
print("Manifest is valid for", schema_url)
TIP

Кэшируйте schema files локально. `schemas.getdbt.com` rate-limits, а сами файлы static. Для CI/CD — pre-fetch и commit в repo.


Timeline schema versions

manifest schema | dbt-core version | release date  | major changes
----------------|------------------|---------------|----------------
v1              | 0.17.x           | 2020-07       | initial
v2              | 0.18.x           | 2020-09       | refinements
v3              | 0.19.x           | 2020-12       | resource_type enum
v4              | 1.0.x            | 2021-12       | 1.0 GA, generic tests
v5              | 1.1.x            | 2022-05       | small additions
v6              | 1.2.x            | 2022-08       | groups, materialized_view
v7              | 1.3.x            | 2022-10       | Python models, semantic metrics v1
v8              | 1.4.x            | 2023-01       | snapshots cleanup
v9              | 1.5.x            | 2023-04       | versioned models, model contracts
v10             | 1.6.x            | 2023-07       | semantic_models, saved_queries (MetricFlow)
v11             | 1.7.x            | 2023-11       | unit_tests in nodes, constraints expansion
v12             | 1.8.x - 1.11.x   | 2024-05+      | unit_tests top-level, microbatch
v13             | 1.12.x           | 2026-04       | semantic layer v2 YAML, planned

В каждой версии backward compatibility partial — некоторые fields добавляются safely, другие меняют семантику.


v6 -> v7 (dbt 1.2 -> 1.3)

Major addition: Python models (language: 'python') и semantic metrics v1.

Added в v7

{
  "language": "python",
  "raw_code": "def model(dbt, session): ...",
  "config": {
    "packages": ["pandas", "numpy"]
  }
}

Новое поле language: 'python' различает Python vs SQL models. Tools должны check:

if node['language'] == 'python':
    # Python model logic
elif node['language'] == 'sql':
    # SQL model logic

В v6 у nodes не было language поля — все были SQL.

Semantic metrics v1

{
  "metrics": {
    "metric.jaffle_shop.revenue": {
      "name": "revenue",
      "model": "ref('fct_orders')",
      "type": "sum",
      "sql": "amount",
      "timestamp": "order_date",
      "time_grains": ["day", "week", "month"]
    }
  }
}

Первая версия dbt semantic layer (predecessor MetricFlow). v7 ввела это поле, v10+ depreciated в пользу semantic_models + saved_queries.


v8 -> v9 (dbt 1.4 -> 1.5)

Major addition: Versioned models + model contracts.

Versioned models

В v9 refs стали dicts (вместо strings):

// v8
"refs": [["fct_orders"], ["dim_customers"]]

// v9
"refs": [
  {"name": "fct_orders", "package": null, "version": null},
  {"name": "dim_customers", "package": null, "version": 2}
]

unique_id тоже изменился для versioned:

model.jaffle_shop.fct_orders       -> unversioned
model.jaffle_shop.fct_orders.v1    -> version 1
model.jaffle_shop.fct_orders.v2    -> version 2

Model contracts

{
  "config": {
    "contract": {
      "enforced": true
    }
  },
  "columns": {
    "order_id": {
      "data_type": "BIGINT",
      "constraints": [
        {"type": "not_null"},
        {"type": "primary_key"}
      ]
    }
  }
}

contract.enforced: true означает dbt валидирует column types и constraints в warehouse при materialization.

Access modifiers

{
  "config": {
    "access": "protected",
    "group": "finance"
  }
}

access: public / private / protected. Используется для cross-project ref governance.

WARNING

v8 -> v9 — самый breaking change. Tools, написанные для v8 и не handling dict-refs, ломаются на v9. До 1.7 dbt allowed refs как arrays of arrays (very legacy). Tools должны handle three forms.


v9 -> v10 (dbt 1.5 -> 1.6)

Major addition: MetricFlow integration.

semantic_models и saved_queries

{
  "semantic_models": {
    "semantic_model.jaffle_shop.orders": {
      "name": "orders",
      "node_relation": {
        "alias": "fct_orders",
        "schema_name": "marts",
        "database": "jaffle_shop"
      },
      "entities": [...],
      "dimensions": [...],
      "measures": [...]
    }
  },
  "saved_queries": {
    "saved_query.jaffle_shop.weekly_revenue": {
      "name": "weekly_revenue",
      "query_params": {
        "metrics": ["revenue"],
        "group_by": ["TimeDimension('order__order_date', 'WEEK')"]
      }
    }
  }
}

metrics legacy field остался для backward compat (deprecated с 1.6, completely removed в 1.12).

deprecation_date

{
  "config": {
    "deprecation_date": "2026-12-31"
  }
}

Для models — warning, что они скоро устареют (часто related к versioned models).


v10 -> v11 (dbt 1.6 -> 1.7)

Major addition: Unit tests (в nodes с resource_type=‘unit_test’).

Unit tests в nodes

{
  "nodes": {
    "unit_test.jaffle_shop.test_revenue_logic": {
      "resource_type": "unit_test",
      "name": "test_revenue_logic",
      "model": "fct_revenue",
      "given": [
        {"input": "ref('stg_orders')", "rows": [...]}
      ],
      "expect": {"rows": [...]}
    }
  }
}

В v11 они шли в основной nodes dictionary с resource_type='unit_test'. Это значило: tools filtering by resource_type должны были handle новый type.

Constraints expansion

В v11 column constraints расширились — больше типов (foreign_key, check с expression).

{
  "columns": {
    "customer_id": {
      "constraints": [
        {"type": "foreign_key", "expression": "REFERENCES dim_customers(id)"}
      ]
    }
  }
}

Group governance

Groups стали более detailed:

{
  "groups": {
    "group.jaffle_shop.finance": {
      "owner": {
        "name": "Finance Team",
        "email": "[email protected]",
        "slack": "@finance"
      }
    }
  }
}

v11 -> v12 (dbt 1.7 -> 1.8+)

Major addition: Unit tests вынесены в top-level, microbatch materialization.

Unit tests top-level

{
  "nodes": {
    // unit_tests removed from here
  },
  "unit_tests": {
    "unit_test.jaffle_shop.test_revenue_logic": {
      "resource_type": "unit_test",
      ...
    }
  }
}

Tool которая filtering nodes by resource_type=‘unit_test’ в v12 получит пустой результат. Migration:

def get_unit_tests(manifest):
    schema_version = manifest['metadata']['dbt_schema_version']
    
    if 'v11' in schema_version:
        return [
            n for n in manifest['nodes'].values()
            if n.get('resource_type') == 'unit_test'
        ]
    elif 'v12' in schema_version:
        return list(manifest.get('unit_tests', {}).values())
    else:
        raise ValueError(f"Unsupported schema: {schema_version}")

Microbatch materialization

{
  "config": {
    "materialized": "microbatch",
    "event_time": "order_date",
    "batch_size": "day",
    "lookback": 3,
    "begin": "2024-01-01"
  }
}

Новый materialization type для time-partitioned incremental processing.

Saved queries — расширение

{
  "saved_queries": {
    "saved_query.jaffle_shop.weekly_revenue": {
      "exports": [
        {
          "name": "weekly_revenue_export",
          "config": {
            "export_as": "table",
            "schema": "exports"
          }
        }
      ]
    }
  }
}

exports — declare что и куда экспортировать.

legacy metrics removed

metrics field остаётся в manifest для backward compat, но в production не используется. v13 (1.12) полностью удалит.


v12 -> v13 (dbt 1.11 -> 1.12, planned)

Planned addition: Semantic Layer v2 YAML, dbt Cloud-native integrations.

Semantic Layer v2

Новый YAML spec (1.12):

# models/_semantic/_metrics.yml (v2)
metrics:
  - name: revenue
    label: "Revenue"
    type: simple
    measure: amount
    semantic_model: orders
    description: "Total revenue from orders"
    filter: "{{ Dimension('order__status') }} = 'completed'"
    fill_nulls_with: 0

В manifest это будет reflect новые поля в semantic_models. Legacy v1 YAML still supported, but deprecated.

Other planned changes

  • Improved error tracking (each node может иметь parsing_error поле)
  • Cross-project versioning enhancements
  • dbt MCP server metadata в manifest
NOTE

v13 запланирован для dbt 1.12 (Q2 2026). Точные изменения могут меняться. Production tools должны subscribe to dbt-core changelog и тестировать compat early.


Backward compatibility approach

dbt-core официальная политика:

  1. Forward-compat: new dbt versions read old manifests (for state:modified comparisons across versions).
  2. Backward-compat: old tools на old schema versions могут failing on new manifest.
  3. Schema version bump only on breaking change: добавление optional field не бампит version. Removing/renaming field — бампит.

В реальности:

  • Tools должны explicitly check dbt_schema_version.
  • dbt CLI пишет manifest в current schema. Если dbt 1.6 reads manifest v12 from 1.11 — может work если read только compatible fields, но full deserialization упадёт.
  • state:modified сравнивает manifests с possibly different schemas — dbt internally handles migration.

Migration patterns для tools

Pattern 1: branched logic per major version

def parse_manifest(path):
    manifest = json.load(open(path))
    schema_url = manifest['metadata']['dbt_schema_version']
    
    # Extract version: v10, v11, v12, ...
    import re
    match = re.search(r'/v(\d+)\.json$', schema_url)
    if not match:
        raise ValueError(f"Can't parse version from {schema_url}")
    
    version = int(match.group(1))
    
    if version <= 9:
        raise ValueError(f"Schema v{version} too old; minimum supported is v10")
    elif version == 10:
        return _parse_v10(manifest)
    elif version == 11:
        return _parse_v11(manifest)
    elif version == 12:
        return _parse_v12(manifest)
    elif version >= 13:
        # Forward compat — attempt v12 logic
        import warnings
        warnings.warn(f"Schema v{version} newer than tool supports; using v12 logic")
        return _parse_v12(manifest)

Pattern 2: feature detection

Вместо schema version, check fields:

def has_unit_tests_top_level(manifest):
    return 'unit_tests' in manifest

def get_unit_tests(manifest):
    if has_unit_tests_top_level(manifest):
        return list(manifest['unit_tests'].values())
    else:
        # Old schema — look в nodes
        return [
            n for n in manifest['nodes'].values()
            if n.get('resource_type') == 'unit_test'
        ]

Feature detection robust против minor changes. Schema version check explicit но fragile при minor releases.

Pattern 3: pydantic models с migration

from pydantic import BaseModel, validator
from typing import Optional, List, Dict, Any

class Ref(BaseModel):
    name: str
    package: Optional[str] = None
    version: Optional[str] = None
    
    @classmethod
    def parse_from_manifest(cls, raw):
        # Handle three forms
        if isinstance(raw, str):
            return cls(name=raw)
        elif isinstance(raw, list):
            return cls(name=raw[0])
        elif isinstance(raw, dict):
            return cls(**raw)
        else:
            raise ValueError(f"Unknown ref format: {raw}")

# Использование
for raw_ref in node['refs']:
    ref = Ref.parse_from_manifest(raw_ref)
    print(ref.name, ref.version)

Pydantic provides defaults и type safety, migration logic в parse_from_manifest.

Pattern 4: dbt-core API (when stable)

from dbt.contracts.graph.manifest import Manifest

manifest_dict = json.load(open('target/manifest.json'))
manifest = Manifest.from_dict(manifest_dict)

# dbt-core handles schema migration internally
for node in manifest.nodes.values():
    print(node.unique_id, node.config.materialized)

Hidden behind API — но API changes между dbt versions.

WARNING

dbt-core Python API не считается stable для external tools (только для plugins). API может measure между major versions. Если используете — pin dbt-core version и test caarefully при upgrade.


Validation workflow

Production-grade validation:

import json
import requests
from jsonschema import Draft7Validator, RefResolver
from functools import lru_cache

@lru_cache(maxsize=10)
def fetch_schema(url):
    """Cache schema fetches."""
    return requests.get(url).json()

def validate_manifest(manifest_path):
    manifest = json.load(open(manifest_path))
    schema_url = manifest['metadata']['dbt_schema_version']
    schema = fetch_schema(schema_url)
    
    validator = Draft7Validator(schema)
    errors = list(validator.iter_errors(manifest))
    
    if not errors:
        return {"valid": True, "schema": schema_url}
    
    return {
        "valid": False,
        "schema": schema_url,
        "errors": [
            {
                "path": list(e.absolute_path),
                "message": e.message
            }
            for e in errors[:10]  # first 10
        ]
    }

result = validate_manifest('target/manifest.json')
if not result['valid']:
    for err in result['errors']:
        print(f"  {' -> '.join(map(str, err['path']))}: {err['message']}")

В CI:

- name: Validate manifest
  run: |
    dbt parse
    python scripts/validate_manifest.py target/manifest.json

Tool compatibility matrix

Топ-tools и поддержка schema versions:

Tool                  | min dbt | max dbt | notes
----------------------|---------|---------|---------------------------
dbt-osmosis           | 1.5     | 1.10    | handles v9-v12
dbt-coverage          | 1.4     | 1.10    | feature detection
Elementary            | 1.5     | 1.12    | regular updates
re_data               | 1.3     | 1.8     | sometimes lags
dbt-checkpoint        | 1.5     | 1.10    | pre-commit hooks
dbt-meshify           | 1.5     | 1.11    | mesh-specific tooling
Datafold              | 1.5     | 1.12    | enterprise
Recce                 | 1.5     | 1.11    | PR review

Используйте feature detection и graceful degradation, чтобы tool не ломался при minor dbt updates.


Real-world migration story

В 2024 году dbt 1.8 release принес v12 schema с unit_tests top-level. Tools которые filtered nodes by resource_type='unit_test' стали пропускать unit tests.

# v11-era code (broken on v12)
unit_tests_count = sum(
    1 for n in manifest['nodes'].values()
    if n.get('resource_type') == 'unit_test'
)
# v12: возвращает 0, потому что unit_tests переехали

Fix:

# Handle both v11 и v12
def get_unit_tests(manifest):
    if 'unit_tests' in manifest:  # v12+
        return manifest['unit_tests']
    # v11 and earlier
    return {
        uid: node
        for uid, node in manifest.get('nodes', {}).items()
        if node.get('resource_type') == 'unit_test'
    }

В уроке 04-parsing-manifest-python.mdx мы рассмотрим больше patterns для robust parsing.


Когда schema version важна

  1. CI/CD: pin dbt version, не surprise upgrades.
  2. Tooling integrations: feature detection + validation.
  3. State comparison: state:modified across major versions может break.
  4. Custom adapters: adapter API tied к dbt-core version.
  5. Multi-project mesh: все projects желательно на same major dbt version.

Ключевые выводы

  1. dbt-core schema versioning — formal через schemas.getdbt.com/dbt/manifest/v{N}.json.
  2. Timeline: v6 (1.2, 2022-08) -> v12 (1.8+, 2024+). v13 запланирован 1.12.
  3. Breaking changes — v8->v9 (refs стали dicts, versioned models, contracts), v11->v12 (unit_tests top-level).
  4. Additive changes обычно — Python models, semantic_models, microbatch, constraints expansion.
  5. Forward-compat: dbt-core новых versions reads старые manifests; backward — нет.
  6. Tools должны handle multiple versions: branched logic per major, feature detection, или pydantic с migration.
  7. Validation: jsonschema package + cached schema fetches от schemas.getdbt.com.
  8. dbt-core Python API не stable: meet external tool risks.
  9. state:modified across versions — internal mechanism dbt-core, не для external tools.
  10. Production tools должны pin dbt versions, test against multiple, log schema version в outputs.
Проверка знанийKnowledge check
Команда upgrades dbt 1.7 -> 1.10. Custom observability tool написан для schema v11. После upgrade ничего не сломалось, но в Slack alerts перестали приходить о failed unit tests. Что произошло, как fix?
ОтветAnswer
**Что произошло**: schema migration v11 -> v12 переместила unit_tests из `nodes` в top-level `unit_tests` field.\n\n**Tool logic** (v11-era):\n\n```python\ndef get_failed_unit_tests(manifest, run_results):\n # Найти unit tests\n unit_tests = {\n uid: node\n for uid, node in manifest['nodes'].items()\n if node.get('resource_type') == 'unit_test'\n }\n # В v12 — пустой dict! Unit tests не в nodes больше.\n \n failed = []\n for result in run_results['results']:\n if result['unique_id'] in unit_tests and result['status'] == 'fail':\n failed.append({\n 'name': unit_tests[result['unique_id']]['name'],\n 'message': result['message']\n })\n return failed\n```\n\nИ результат: `failed = []` всегда. Tool не alerts, потому что не находит unit tests.\n\n**Diagnostics**:\n\n```python\nimport json\nmanifest = json.load(open('target/manifest.json'))\n\nprint('Schema:', manifest['metadata']['dbt_schema_version'])\nprint('In nodes (v11 location):', sum(\n 1 for n in manifest['nodes'].values()\n if n.get('resource_type') == 'unit_test'\n))\nprint('In top-level (v12 location):', len(manifest.get('unit_tests', {})))\n```\n\nOutput:\n\n```\nSchema: https://schemas.getdbt.com/dbt/manifest/v12.json\nIn nodes (v11 location): 0\nIn top-level (v12 location): 47\n```\n\nConfirmed: 47 unit tests live в `manifest['unit_tests']`.\n\n**Fix**: feature detection + branched logic.\n\n```python\ndef get_unit_tests(manifest):\n # Try v12 location first (newer)\n if 'unit_tests' in manifest:\n return manifest['unit_tests']\n \n # Fall back к v11 (in nodes)\n return {\n uid: node\n for uid, node in manifest.get('nodes', {}).items()\n if node.get('resource_type') == 'unit_test'\n }\n\ndef get_failed_unit_tests(manifest, run_results):\n unit_tests = get_unit_tests(manifest)\n \n failed = []\n for result in run_results['results']:\n if result['unique_id'] in unit_tests and result['status'] == 'fail':\n failed.append({\n 'name': unit_tests[result['unique_id']]['name'],\n 'message': result.get('message', 'No message')\n })\n return failed\n```\n\n**Тест coverage**:\n\n```python\n# tests/test_unit_tests_extraction.py\nimport json\n\ndef test_get_unit_tests_v11():\n # Mock v11 manifest\n manifest = {\n 'metadata': {'dbt_schema_version': 'https://schemas.getdbt.com/dbt/manifest/v11.json'},\n 'nodes': {\n 'unit_test.proj.test1': {'resource_type': 'unit_test', 'name': 'test1'},\n 'model.proj.foo': {'resource_type': 'model'}\n }\n }\n result = get_unit_tests(manifest)\n assert len(result) == 1\n assert 'unit_test.proj.test1' in result\n\ndef test_get_unit_tests_v12():\n # Mock v12 manifest\n manifest = {\n 'metadata': {'dbt_schema_version': 'https://schemas.getdbt.com/dbt/manifest/v12.json'},\n 'nodes': {\n 'model.proj.foo': {'resource_type': 'model'}\n },\n 'unit_tests': {\n 'unit_test.proj.test1': {'name': 'test1'}\n }\n }\n result = get_unit_tests(manifest)\n assert len(result) == 1\n\ndef test_get_unit_tests_mixed():\n # Theoretical mixed (defensive)\n manifest = {\n 'nodes': {\n 'unit_test.proj.in_nodes': {'resource_type': 'unit_test', 'name': 'in_nodes'}\n },\n 'unit_tests': {\n 'unit_test.proj.top_level': {'name': 'top_level'}\n }\n }\n # v12 location takes priority — return top-level\n result = get_unit_tests(manifest)\n assert 'unit_test.proj.top_level' in result\n```\n\n**Schema version logging**:\n\n```python\ndef parse_manifest(path):\n manifest = json.load(open(path))\n schema = manifest['metadata']['dbt_schema_version']\n \n # Log schema version при каждом parse\n logger.info(f'Parsing manifest with schema: {schema}')\n \n # Detect schema features\n has_unit_tests_top = 'unit_tests' in manifest\n has_semantic_models = 'semantic_models' in manifest\n logger.info(f' unit_tests at top-level: {has_unit_tests_top}')\n logger.info(f' semantic_models present: {has_semantic_models}')\n \n return manifest\n```\n\n**CI safety net** — validate schema:\n\n```yaml\n- name: Validate manifest schema\n run: |\n dbt parse\n python -c "\n import json\n m = json.load(open('target/manifest.json'))\n schema = m['metadata']['dbt_schema_version']\n print(f'Schema: {schema}')\n # Fail if schema unknown to tool\n supported = ['v10', 'v11', 'v12']\n if not any(v in schema for v in supported):\n raise SystemExit(f'Unsupported schema: {schema}')\n "\n```\n\n**Alerting на schema changes**:\n\n```python\n# При first run after dbt upgrade\ndef detect_schema_change(manifest, last_known_schema):\n current_schema = manifest['metadata']['dbt_schema_version']\n if current_schema != last_known_schema:\n send_slack_alert(\n f'Schema version changed: {last_known_schema} -> {current_schema}. '\n f'Verify tool compatibility.'\n )\n return current_schema\n```\n\n**Production-grade fixes**:\n\n1. **Feature detection** (not version-based) — more robust\n2. **Logging schema version** — visibility\n3. **Unit tests на оба formats** — regression prevention\n4. **CI schema validation** — catch unsupported versions\n5. **Alerting on schema changes** — incident response\n\n**Lessons**:\n\n- dbt schema migrations не trigger errors — silent breakage.\n- Tools must explicit handle schema versions.\n- Feature detection > version detection (more resilient к minor changes).\n- Documentation о supported dbt versions critical для tool reliability.\n- Schema migration story в release notes должен alert tool maintainers.
Проверка знанийKnowledge check
Tool создан для dbt 1.10 (schema v12). Production project uses dbt 1.12 (schema v13). Tool не валидирует manifest, просто читает поля. Какие risks, как mitigate?
ОтветAnswer
**Risks reading newer schema без validation**:\n\n**1. Field removal**:\n\nЕсли dbt 1.12 удалил какое-то поле (например, deprecation metrics), tool пытающийся читать его получает `KeyError`.\n\n```python\nfor metric in manifest['metrics'].values(): # v13: empty или removed\n print(metric['name']) # IndexError или KeyError\n```\n\n**2. Semantic changes**:\n\nПоле существует, но значит другое. Например, новая semantic layer v2 YAML может change `semantic_models` structure внутри.\n\n```python\nfor sm in manifest['semantic_models'].values():\n measures = sm['measures'] # in v12 — array of {name, expr, agg}\n # in v13 — может быть array of {name, type, ...}\n```\n\n**3. New required fields**:\n\nНовая dbt version может requires новые fields, которые tool не sets при writing tools-generated manifests (rare, но possible).\n\n**4. New top-level keys**:\n\nЕсли tool iterates все top-level keys для какой-то analytics — пропустит новые. Например, если v13 добавит `audit_logs` top-level.\n\n**5. Unique_id format change**:\n\nIf v13 changes formula (e.g., adds environment segment), все tools regex parsing unique_id break.\n\n**Mitigation strategies**:\n\n**1. Explicit version check at start**:\n\n```python\ndef parse_manifest(path):\n manifest = json.load(open(path))\n schema = manifest['metadata']['dbt_schema_version']\n \n SUPPORTED = ['v10', 'v11', 'v12']\n if not any(v in schema for v in SUPPORTED):\n raise UnsupportedManifestError(\n f'Tool supports schemas {SUPPORTED}, got {schema}. '\n f'Update tool or use older dbt version.'\n )\n \n return manifest\n```\n\nFail loud at parse — better than silent breakage.\n\n**2. Defensive field access**:\n\n```python\n# BAD\nmaterialized = node['config']['materialized']\n\n# GOOD\nmaterialized = node.get('config', {}).get('materialized', 'view')\n```\n\nDefaults для все critical fields.\n\n**3. Try-except per node**:\n\n```python\ndef analyze_node(uid, node):\n try:\n return {\n 'uid': uid,\n 'materialized': node['config']['materialized'],\n 'refs': [r['name'] for r in node.get('refs', [])]\n }\n except (KeyError, TypeError) as e:\n logger.warning(f'Failed parsing {uid}: {e}')\n return None\n\nresults = [analyze_node(u, n) for u, n in manifest['nodes'].items()]\nresults = [r for r in results if r is not None]\n```\n\nPartial degradation > total failure.\n\n**4. Schema validation в CI**:\n\n```yaml\n- name: Validate manifest schema\n run: |\n dbt parse\n python -c "\n import json, requests\n from jsonschema import validate\n \n m = json.load(open('target/manifest.json'))\n schema_url = m['metadata']['dbt_schema_version']\n schema = requests.get(schema_url).json()\n \n try:\n validate(instance=m, schema=schema)\n print('Manifest valid')\n except Exception as e:\n print(f'Validation failed: {e}')\n raise SystemExit(1)\n "\n```\n\nValidates structure correctness против official schema.\n\n**5. Feature detection patterns**:\n\n```python\ndef has_microbatch_support(manifest):\n return any(\n n.get('config', {}).get('materialized') == 'microbatch'\n for n in manifest.get('nodes', {}).values()\n )\n\ndef has_semantic_models(manifest):\n return 'semantic_models' in manifest and len(manifest['semantic_models']) > 0\n\ndef has_unit_tests_top_level(manifest):\n return 'unit_tests' in manifest\n\n# Use в tool\nif has_unit_tests_top_level(manifest):\n process_unit_tests(manifest['unit_tests'])\nelse:\n process_unit_tests_legacy(manifest['nodes']) # старая location\n```\n\n**6. Pin dbt-core version в tool's dev dependencies**:\n\n```toml\n# pyproject.toml\n[tool.poetry.group.dev.dependencies]\ndbt-core = "~=1.10.0" # dev/test pin\n# But production can use newer (с risks)\n```\n\n**7. CI matrix testing**:\n\n```yaml\nstrategy:\n matrix:\n dbt-version: ['1.8.0', '1.9.0', '1.10.0', '1.11.0']\nsteps:\n - run: pip install dbt-core==${{ matrix.dbt-version }}\n - run: pytest\n```\n\nTool tested против all supported.\n\n**8. Telemetry / monitoring**:\n\n```python\n# В tool production deploy\nlogger.info(f'Manifest schema: {schema_url}')\nlogger.info(f'dbt version that generated: {manifest["metadata"]["dbt_version"]}')\n\n# Send metric\nmetrics.increment('tool.parse', tags={\n 'schema': schema_url.split('/')[-1].replace('.json', ''),\n 'dbt_version': manifest['metadata']['dbt_version']\n})\n```\n\nIf usage shows v13 — proactive update.\n\n**9. Documentation**:\n\n```markdown\n# Tool README\n\n## Supported dbt versions\n- dbt-core 1.8 (schema v12) — full support\n- dbt-core 1.9 (schema v12) — full support\n- dbt-core 1.10 (schema v12) — full support\n- dbt-core 1.11 (schema v12) — full support\n- dbt-core 1.12 (schema v13) — NOT yet supported. Track issue #42.\n\nIf using dbt 1.12+, pin dbt-core to 1.11.x until tool updates.\n```\n\n**10. Plugin philosophy**:\n\nFor critical features — use dbt-core Python API instead of raw JSON. dbt internal serialization handles schema migration:\n\n```python\nfrom dbt.contracts.graph.manifest import Manifest\nmanifest = Manifest.from_dict(json.load(open('target/manifest.json')))\n# dbt-core handles schema internally\n```\n\nDownside: API stability concerns. Pinning dbt-core sometimes safer.\n\n**Risk priority**:\n\n1. **Silent data corruption** (high): tool processes manifest, generates incorrect output, downstream consumers get wrong data.\n2. **Runtime errors** (medium): tool crashes when reading newer schema — at least visible.\n3. **Stale features** (low): newer dbt features (microbatch, etc.) not reflected in tool output.\n\n**Production-grade tool should**:\n\n1. Explicit version check + fail loud\n2. Defensive .get() для optional fields\n3. Schema validation в CI\n4. Feature detection in addition to version check\n5. Test matrix across supported dbt versions\n6. Telemetry для observed dbt versions in production\n7. Documentation о support matrix\n8. Clear migration path when adding new dbt version support

Проверьте понимание

Результат: 0 из 0
Аналитический
Вопрос 1 из 5. Tool написан для schema v12 (dbt 1.8). Команда upgrades к dbt 1.12 (v13). Tool ничего не валидирует. Какие production risks?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5