Crosshire
Written concepts
Engagement20 Written concepts · 1 of 3

What makes a concept binding

A DACH client asks for these by name — Entwicklerkonzept, Berechtigungskonzept, Anonymisierungskonzept — and usually receives PDFs that were accurate on the day they were signed. Two mechanics change that: a proof that runs, and a header that dates itself.

Stand 29.07.2026

#A concept without a test is an opinion

What
A concept describes how the warehouse is supposed to behave. The warehouse behaves however the code and the grants say. Between the two there is nothing but goodwill — unless the concept names a query that fails when they diverge.
So every chapter making a checkable claim carries a file path. Three documents, three owners, three suites:
The three concepts and what proves each one
ConceptOwnerReviewersTimeboxExecutable proof
EntwicklerkonzeptLead engineerTwo engineers + platform3 days, then ½ day per quartertests/standards/ — identifier regex, layer read-direction, bundle validate
BerechtigungskonzeptData owner (business), drafted by the lead engineerSecurity/IT + DPO2 days, reviewed on every new schematests/access/ — SHOW GRANTS diffed against the concept's role matrix
AnonymisierungskonzeptDatenschutzbeauftragter (DPO)Lead engineer + legal2 days, reviewed on every new PII columntests/privacy/ — every pii-tagged column masked; no production-shaped data outside dwh_prod
Why
The failure mode is not a document that was wrong when written. It is one that was true — accurate in March, describing a warehouse that no longer exists in September. Concepts drift silently because nothing consumes them: code has tests, dashboards have users, a concept has a signature.
In a review — internal audit, a certification visit, the client's own security team — this is the difference between handing over a PDF and opening a pipeline run where the check is green. One is a claim about the warehouse; the other is the warehouse answering for itself.
How
sql
tests/access/grants_match_concept.sql — the permissions concept, executed. Zero rows means the document is still true.
-- The role matrix from chapter 3, checked into the repo as VALUES lists — one
-- per grant LEVEL, because the concept grants at three levels and each level
-- has its own information_schema view.
--
-- Symmetric EXCEPT: one direction finds grants nobody declared, the other
-- finds declarations nobody granted. One direction alone catches half.
--
-- Run this as a principal that can see every grant in the catalog.
-- information_schema is filtered to the objects the caller has privileges on,
-- so a narrowly-granted service principal reads an empty actual set, both
-- EXCEPTs collapse, and the check goes green having compared nothing.
--
-- inherited_from is the column that decides whether this check is usable at
-- all. table_privileges EXPANDS a schema- or catalog-level grant into one row
-- per table, tagged with the ancestor it came from. Chapter 3 grants gold to
-- grp_dwh_analyst as a whole, so comparing every row against a per-object list
-- reports every table in gold as undeclared on the first run — and a check that
-- is red on day one is muted by week two. The choice here is explicit: table
-- rows are filtered to DIRECT grants, and the inherited ones are declared at
-- the level they were actually issued. Databricks documents the column as
-- nullable and reports NONE for a direct grant; coalesce covers both.
--
-- Filtering to direct grants is only safe because the levels above are checked
-- too. Drop either of the other two blocks and a SELECT granted on the whole
-- catalog to grp_all_staff is inherited everywhere, direct nowhere, and
-- invisible to all six EXCEPTs.
--
-- Scoped to gold. silver and bronze get the same blocks with their own lists.
WITH concept_catalog(grantee, securable, privilege_type) AS (
  VALUES
    ('grp_dwh_bi_reader', 'dwh_prod', 'USE CATALOG'),
    ('grp_dwh_analyst',   'dwh_prod', 'USE CATALOG'),
    ('grp_dwh_engineer',  'dwh_prod', 'USE CATALOG')
),
actual_catalog AS (
  SELECT grantee, catalog_name AS securable, privilege_type
  FROM dwh_prod.information_schema.catalog_privileges
  WHERE coalesce(inherited_from, 'NONE') = 'NONE'
    AND grantee <> 'grp_dwh_owner'      -- ownership grants are implicit
),
-- Granted on the schema: this is what "gold as a whole" means, spelled out.
-- USE SCHEMA belongs here too, or the first run reports three real grants as
-- undeclared and the team learns to ignore the output.
concept_schema(grantee, securable, privilege_type) AS (
  VALUES
    ('grp_dwh_bi_reader', 'gold', 'USE SCHEMA'),
    ('grp_dwh_analyst',   'gold', 'USE SCHEMA'),
    ('grp_dwh_analyst',   'gold', 'SELECT'),
    ('grp_dwh_engineer',  'gold', 'USE SCHEMA'),
    ('grp_dwh_engineer',  'gold', 'SELECT'),
    ('grp_dwh_engineer',  'gold', 'MODIFY')
),
actual_schema AS (
  SELECT grantee, schema_name AS securable, privilege_type
  FROM dwh_prod.information_schema.schema_privileges
  WHERE schema_name = 'gold'
    AND coalesce(inherited_from, 'NONE') = 'NONE'
    AND grantee <> 'grp_dwh_owner'
),
-- Granted per object: the declared exceptions, and nothing else.
-- grp_dwh_bi_reader is one — it may reach two published objects, never the
-- schema, so it appears here rather than in the block above.
concept_table(grantee, securable, privilege_type) AS (
  VALUES
    ('grp_dwh_bi_reader', 'mv_sales',          'SELECT'),
    ('grp_dwh_bi_reader', 'agg_revenue_month', 'SELECT')
),
actual_table AS (
  SELECT grantee, table_name AS securable, privilege_type
  FROM dwh_prod.information_schema.table_privileges
  WHERE table_schema = 'gold'
    AND coalesce(inherited_from, 'NONE') = 'NONE'
    AND grantee <> 'grp_dwh_owner'
)
SELECT 'undeclared' AS problem, 'catalog' AS grant_level, *
FROM (SELECT * FROM actual_catalog  EXCEPT SELECT * FROM concept_catalog)
UNION ALL
SELECT 'missing'    AS problem, 'catalog' AS grant_level, *
FROM (SELECT * FROM concept_catalog EXCEPT SELECT * FROM actual_catalog)
UNION ALL
SELECT 'undeclared' AS problem, 'schema'  AS grant_level, *
FROM (SELECT * FROM actual_schema   EXCEPT SELECT * FROM concept_schema)
UNION ALL
SELECT 'missing'    AS problem, 'schema'  AS grant_level, *
FROM (SELECT * FROM concept_schema  EXCEPT SELECT * FROM actual_schema)
UNION ALL
SELECT 'undeclared' AS problem, 'table'   AS grant_level, *
FROM (SELECT * FROM actual_table    EXCEPT SELECT * FROM concept_table)
UNION ALL
SELECT 'missing'    AS problem, 'table'   AS grant_level, *
FROM (SELECT * FROM concept_table   EXCEPT SELECT * FROM actual_table)
The rule the three blocks encode: declare a grant at the level it was issued, and check every level you grant at. A per-object list compared against table_privileges unfiltered is not a stricter check — it is a broken one, because the view expands one schema-level grant into a row per table and the proof is red before the warehouse has done anything wrong. A per-object list compared against inherited_from = 'NONE' alone is the opposite failure: quiet, and blind to exactly the grant that hands the whole catalog to everyone.
Doing it
  • Write the proof before the chapterIf you cannot name the query that would fail, the chapter is a preference — label it as one rather than dressing it as a rule.
  • Read the grant levels out of the concept before writing the queryOne VALUES list and one information_schema view per level — catalog_privileges, schema_privileges, table_privileges — and filter each to direct grants.
  • Run the three suites in the same CI job as the unit tests on every merge, and nightly against dwh_prod
  • Link the proof by file path, not by descriptionA described test is one nobody can find.
  • Mark genuinely untestable chapters as untestable in the textEscalation paths, team topology. Untested then never means unnoticed.
Embedding it
  • Give each concept to a team member as owner, with you as reviewerA document you wrote is a document they read once.
  • In the first review ask one question per chapter: which file fails if this stops being true?Silence is the finding — write it down and move on.
  • Have them break a proof deliberately, watch CI go red, then revoke itGrant SELECT on gold.dim_customer — the table itself, so it lands as a direct grant — to a group the matrix does not name. Five minutes, and the concept stops being paper.
  • Then break it the other way, one level upGrant SELECT on the gold schema to that group and confirm the schema block, not the table block, is what turns red. That run is where the team learns which level a finding is reported at, and it is the run that stops the next proof being written against one view.
Defaults
  • Three concepts, not elevenEach extra document halves the chance any of them is read.
  • Markdown in the repository, changed by pull request, reviewed like code
  • Every testable chapter names a path; every untestable chapter says so
  • Grants declared at the level they were issued, and every level the concept grants at checkedDirect rows against the matrix, inherited rows against the ancestor that produced them.
  • Proofs run against dwh_prod on a schedule, not only against dwh_test in CIThe drift you care about is in production.
Gotchas
  • A concept as a PDF on a file shareNobody diffs a PDF, so the version the team reads and the version the auditor was given drift apart — discovered when both are on the same screen in a meeting you do not control.
  • Proofs that only run against dwh_testdwh_test matches the concept because the bundle deploys it that way; dwh_prod has two years of hand-made grants that nothing checks. The green build measures the deployment, not the warehouse.
  • A proof that passes because it could not see anythinginformation_schema returns only the objects the caller has privileges on, so the grants check run as a minimally-granted service principal compares the concept against an empty set and goes green. Assert in the same test that the actual set is non-empty, or the only thing the proof proves is that the principal is narrow.
  • A proof that is red on its first run — the mirror of the same failure, and the more common oneA per-object VALUES list compared against table_privileges without filtering inherited_from reports every table in gold as undeclared, because one schema-level grant is expanded into one row per table. Nobody debugs a proof that has never been green: it is muted in week two and the concept is unproven from then on, with a passing suite as cover.
  • Writing the concepts at the end as handover artefactsThey then document what was built instead of governing it, and every decision they record was already unchangeable when it was written down.
  • One combined 'Data Platform Concept'It gets a single owner, who is an engineer, so the data-protection chapters carry a signature from someone with no authority to give it — which is exactly the signature that gets asked for later.

#A header that dates itself

What
Every concept opens with the same machine-readable block. Not decoration: it is what makes a stale document announce itself instead of waiting to be discovered.
Why
A concept's real failure mode is not being wrong. It is being trusted while wrong: someone designs a schema against chapter 3 of a document that stopped matching production two quarters ago, and the mistake is invisible because the document looked authoritative.
The header makes that state mechanical. Past the review cycle the build fails and the page says so on its own first line. The owner field does the other half — a document owned by 'the team' is owned by nobody, and its review is nobody's Tuesday afternoon.
How
yaml
docs/concepts/berechtigungskonzept.md — the first lines
---
title:         Berechtigungskonzept - dwh_prod
owner:         A. Weber (Data Owner, Vertrieb)   # a named person, never a group
authors:       lead engineer + platform
last_reviewed: 2026-07-14
review_cycle:  90d
status:        active                      # draft | active | superseded
proof:         tests/access/grants_match_concept.sql
applies_to:    dwh_prod.gold, dwh_prod.silver
---
status: superseded earns its place: a replaced concept stays reachable with a pointer forward, because the question asked after an incident is what the rules were in March, not what they are today.
python
tests/docs/test_concept_headers.py — the whole check
import re
from datetime import date, timedelta
from pathlib import Path

import pytest
import yaml

CONCEPTS = sorted(Path("docs/concepts").glob("*.md"))


def test_concepts_are_found() -> None:
    """An empty parametrize list reports as skipped, not failed. Without this
    line the whole check passes vacuously the day the path or the CWD moves."""
    assert CONCEPTS, "docs/concepts/ is empty - nothing was checked"


@pytest.mark.parametrize("path", CONCEPTS, ids=lambda p: p.name)
def test_header_is_current(path: Path) -> None:
    front = re.match(r"^---\n(.*?)\n---", path.read_text(encoding="utf-8"), re.S)
    assert front, f"{path.name}: no header block"
    head = yaml.safe_load(front.group(1))

    if head["status"] != "active":
        return

    cycle = timedelta(days=int(head["review_cycle"].rstrip("d")))
    assert head["last_reviewed"] + cycle >= date.today(), (
        f"{path.name}: last reviewed {head['last_reviewed']}, cycle {head['review_cycle']}"
    )
    assert Path(head["proof"]).exists(), f"{path.name}: proof {head['proof']} is missing"
Doing it
  • Put the header on all three concepts in week one, while the bodies are still stubs
  • Name a person as owner and a group as reviewers — reversed, nothing happens
  • Set cycles the team will honour, and shorten a cycle rather than skip a review90 days for permissions, 180 for the developer concept.
  • Ship the check as a warning first and a hard failure from week sixIt then lands as a habit rather than an obstacle.
Embedding it
  • Ask the owner to run the review in a 30-minute slot with the quarter's schema diff openReviewing against nothing reliably produces 'still fine'.
  • When a review changes nothing, ask what changed in the warehouse that quarterSomething did — that gap is the coaching moment, not the document.
  • Let the team choose the review cycles — they defend numbers they picked
Defaults
  • One machine-readable header at the top of every concept
  • Named human owner, group reviewers, both in the header
  • Supersede, never delete
Gotchas
  • Review dates bumped without a reviewIt is one line in a diff and looks identical to real work. Countermeasure: a review lands as a commit that also touches the body or the proof, so a lone date change is one grep away from being spotted.
  • Deleting a superseded conceptThe question after an incident is what the rules were at the time, and that answer now lives only in git history nobody will reconstruct at 22:00 during an escalation.
  • review_cycle stretched to 365d so the check stops firingIt stops firing because the document is never reviewed, which was the thing being measured. It shows up as a one-line diff on a red build and is approved in seconds, so make the cycle itself reviewable: the only value the check accepts without an argument is the one the team wrote down in week one.
Platform facts on this page verified 29.07.2026 against the official documentation. Volatile claims are anchored to the currency register. This is section 1 of 3 in 20 Written concepts.