Data Governance, Contracts, and Classification Questions
Governing data at scale: data contracts between producers and consumers, schema evolution/compatibility, data classification and sensitivity tagging, access control, and lineage/cataloging. Covers policy, ownership, and compliance-driven controls over data. The governance layer over the technical stack.
What is data lineage, and why does it matter for debugging a wrong number, building trust in a dashboard, and supporting a compliance or audit request? Describe the difference between dataset-level and column-level lineage.
Sample Answer
Data lineage is the recorded trail of where a piece of data came from and every transformation it passed through on the way to where you are looking at it now, a map of what fed into what. It matters for three distinct reasons: it lets you trace a wrong number back to its cause instead of guessing, it lets a stakeholder verify a dashboard's number is trustworthy by seeing its provenance rather than taking it on faith, and it lets you answer a compliance or audit request, "show me everything that fed into this reported figure," without manually reconstructing the pipeline's history.
Why it matters: debugging a wrong number
Without lineage, tracing a bad number backward means manually reading pipeline code and asking around. With it, you follow the recorded chain from the dashboard metric back through each transformation to the source tables, narrowing down where the value diverged from expectation directly, often in minutes instead of hours or days of archaeology.
Why it matters: building trust in a dashboard
A stakeholder who can see, even at a summary level, that a revenue number traces back through a specific, named set of transformations to a specific source table trusts it more than an opaque number with no visible provenance. When the number is questioned, "here is exactly where it comes from" is a much stronger answer than "the pipeline is supposed to be correct."
Why it matters: compliance and audit
Regulators or internal auditors asking "show me what fed into this reported figure" need a defensible, complete answer, and reconstructing that by hand for a complex pipeline is slow and error-prone. Captured lineage turns that into a query rather than an investigation, and provides an auditable record even for figures nobody has looked closely at in months.
Dataset-level versus column-level lineage
Dataset-level lineage tracks dependencies at the table or file level, "table C was built from tables A and B," without saying which specific columns of A and B fed which columns of C. It is cheaper to capture and is enough for coarse-grained impact analysis, such as "if table A changes, table C might be affected."
Column-level lineage tracks the dependency down to individual fields, "column revenue in table C is derived specifically from price and quantity in table A, via this transformation," and not from the other twenty columns table A happens to have. It is more expensive to capture, usually requiring parsing the actual transformation logic rather than just job-level metadata, but it gives a precise answer to "what actually feeds this specific number," which dataset-level lineage can only approximate.
Worked example
An executive dashboard shows quarterly revenue dropped 8% quarter over quarter, and the finance team does not believe it. Dataset-level lineage shows the revenue metric comes from a fct_revenue table, itself built from orders and refunds, which narrows the search to two tables but not further. Column-level lineage shows the dashboard's revenue figure specifically traces to orders.amount_cents minus refunds.refund_amount_cents, aggregated by quarter, and that a transformation two steps upstream started applying a status filter differently after a recent change, causing pending orders to be dropped from the current quarter's total but not the prior quarter's, a data artifact rather than a real revenue drop. Because the lineage was captured at column level, this took one query against the lineage graph instead of re-reading every transformation in the pipeline by hand. The same trace, if requested later by an auditor asking how the reported quarterly revenue figure was derived, is already sitting there as a reusable record rather than something that has to be rebuilt from memory.
Trade-offs and pitfalls
Dataset-level lineage is much cheaper to maintain and is often good enough for impact analysis, such as deciding who to notify before a schema change, so jumping straight to column-level everywhere is over-engineering for teams that mainly need the coarser view; column-level lineage earns its cost specifically for high-stakes, frequently-questioned metrics like revenue or compliance-reported figures. The common pitfall is treating lineage as something you can bolt on retroactively for an audit request; if it is not captured continuously as pipelines run, reconstructing it after the fact for a specific number someone is now questioning is exactly the manual archaeology lineage is supposed to replace.
Two or more teams keep colliding because it's unclear who owns a piece of shared transformation logic, and it causes recurring conflicts. Design an organizational fix (roles, responsibilities, automation, CI/CD process changes) that reduces the friction without freezing teams' ability to move independently, and how would you measure whether it actually worked?
Sample Answer
Direct answer. Give the shared transformation logic a single named owning team, usually the team closest to the data's primary business meaning, or a central platform team if it's genuinely cross-cutting, and require any other team's changes to go through a defined contribution process enforced by CI/CD (continuous integration and continuous delivery) automation rather than informal coordination. Measure success by whether conflicts and incidents involving that logic actually go down, not by whether a document says ownership is now clear.
Roles and responsibilities
- Designate one owning team accountable for the shared transformation logic's correctness and design direction; the natural choice is whichever team's business domain the logic most directly serves, or a central data platform team if no single domain team is the natural owner.
- Other teams needing changes become contributors, not co-owners: they can propose changes, but the owning team is accountable for approving what merges, similar to how a maintainer model works inside an open-source project.
- A named point of contact, not just "the team," on the owning side reduces the common failure where a change request sits unanswered because no individual feels responsible for responding.
Automation that reduces friction without freezing independence
- Automated tests on the shared logic, unit tests for transformation correctness plus contract tests checking the logic's output schema against what known downstream consumers expect, so a contributing team gets fast, objective feedback instead of waiting on manual review to catch a break.
- A schema or contract registry that the transformation logic's output is checked against automatically, so a breaking change to output shape is caught before merge, not discovered by a downstream team's dashboard breaking in production.
- Feature-flagged or versioned changes where practical, so a contributing team can ship a new variant behind a flag and validate it against real traffic before it becomes the default, reducing the owning team's review burden to "is this safe to flag on" rather than "is this perfect."
CI/CD process changes
- Require any pull request touching the shared logic to run the full downstream-consumer contract test suite automatically, and block merge on failure, so downstream breakage is a CI (continuous integration) failure, not a production incident.
- Require the owning team's sign-off through a required reviewer or codeowner rule enforced by the repository, not a social norm, so approval isn't optional or skippable under deadline pressure.
- Add a staged rollout step, deploying to a subset of environments or a canary period before the change reaches all consumers, giving a window to catch problems the test suite didn't anticipate.
Measuring whether it worked
- Count of cross-team escalations or "who broke this" incidents involving the shared logic, tracked before and after the change, as the direct measure of whether friction actually went down.
- Time from a contributing team opening a change request to it merging, which should stay reasonable, not balloon just because there's now a formal process, or the fix has traded one problem for another.
- Number of downstream breakages caught by CI before merge versus discovered in production after, which should shift toward "caught by CI" if the contract tests are doing their job.
Worked example (structural illustration, not a specific measured incident count). Two teams, Team A owning a "revenue" transformation and Team B needing a new revenue-adjacent field, previously edited the same shared SQL model directly and periodically broke each other's downstream dashboards. Under the new process, Team A becomes the owning team; Team B's change goes through a pull request that automatically runs contract tests against the 5 known downstream consumers of the transformation, requires Team A's codeowner sign-off, and deploys to a staging environment first. If the contract tests catch a schema mismatch, say a renamed column that 2 of the 5 downstream consumers reference, the pull request fails CI before merge instead of breaking those 2 consumers in production, exactly the class of incident the process is designed to prevent.
Trade-offs and pitfalls
- Naming an owning team without giving it enough bandwidth to review contributions promptly turns the fix into a new bottleneck; the process needs a response-time expectation for the owning team, not just for contributors.
- Over-automating, requiring heavy contract tests and staged rollouts for genuinely low-risk changes, slows down small fixes disproportionately; scale the process's weight to the blast radius of the change rather than applying the heaviest gate uniformly.
- The most common wrong turn is treating this as a purely organizational fix, just naming an owner, without the CI/CD automation; a named owner with no automated contract tests still relies on manually catching every downstream break, which doesn't scale and quietly reverts to the original problem.
Compare hashing, tokenization, and k-anonymity-style aggregation as ways to anonymize PII while still preserving enough utility for aggregation and geospatial or cohort analysis. When would you reach for a hashed surrogate key with a secure mapping table instead of one-way hashing, and what actually determines whether the result is still re-identifiable in practice?
Sample Answer
Hashing and tokenization protect an individual identifier's value while usually preserving the ability to join or count by it; k-anonymity-style aggregation protects the individual by construction, making a record indistinguishable from a group of others, at the cost of precision. Which one preserves enough utility for aggregation and geospatial or cohort analysis depends on whether the analysis needs to join back to a specific individual, or only needs group-level counts and patterns.
The three approaches
- One-way (irreversible) hashing. Apply a cryptographic hash function (for example SHA-256) to an identifier, typically with a secret salt appended before hashing, producing a fixed-length value that cannot be reversed back to the original. Two records with the same underlying identifier still hash to the same output, so joins and deduplication across datasets still work, but nobody holding only the hash can recover the original value.
- Tokenization. Replace the identifier with a randomly generated surrogate token, with the mapping between token and original value stored in a separate, tightly access-controlled vault. Unlike hashing, tokenization is reversible, but only by looking up the vault, not by any computation on the token itself.
- k-anonymity-style aggregation. Generalize or bucket quasi-identifiers (attributes that aren't identifying alone but can combine to identify someone, like ZIP code plus birth date plus gender) so that every combination of quasi-identifier values in the released dataset is shared by at least k records. A dataset satisfies k-anonymity for a given k if no row's quasi-identifier combination is unique among fewer than k rows.
When to reach for a hashed surrogate key versus a hashed-mapping-table (tokenized) approach instead of plain one-way hashing
Plain one-way hashing is the right default when the only need is consistent joinability or deduplication and the original value never needs to be recovered by anyone. Reach for tokenization (a reversible mapping held in a secure vault) instead specifically when there's a known, legitimate future need to re-identify a record, for example a fraud investigation, a customer service lookup, or a regulatory data-subject request, because a one-way hash offers no path back to the original value even for an authorized, audited purpose. The deciding question is not "is the data more or less sensitive" but "will an authorized party ever legitimately need the original value back": if yes, tokenize; if the value truly never needs to be recovered, one-way hashing is simpler, has no vault to secure or leak, and gives the same joinability.
What actually determines whether the result is still re-identifiable in practice
- Input entropy for hashing. A hash is only as hard to reverse as the input space is large. Hashing a low-entropy value like a US Social Security number's last four digits, or an email address drawn from a small guessable set, is vulnerable to a dictionary attack: an attacker just hashes every plausible candidate value and compares, which defeats the "irreversibility" of the hash entirely if there's no secret salt, or even with a salt if the salt itself leaks. A high-entropy identifier (a long random internal user ID) is much harder to attack this way.
- Salt secrecy and reuse. A shared, static salt reused across the whole dataset doesn't add entropy against an attacker who already has the salt; it primarily defends against a precomputed rainbow-table attack built before knowing the salt. Per-record or per-context salts are stronger but break the ability to join records by their hashed value, so there's a direct trade-off between hash strength and joinability.
- Auxiliary data linkage, for k-anonymity. k-anonymity's guarantee is only as good as the set of quasi-identifiers considered; a released dataset can satisfy k-anonymity on its own declared quasi-identifiers and still be re-identifiable if an attacker has an external dataset with additional overlapping fields (a classic example: combining a k-anonymized health record dataset with a public voter-registration list on ZIP code, birth date, and gender re-identifies specific individuals even though each field alone looked safely generalized). k-anonymity also says nothing about protecting the sensitive attribute itself when every member of a k-sized group happens to share the same sensitive value, a gap addressed by extensions like l-diversity, not by k-anonymity alone.
- Group size versus utility for geospatial/cohort analysis specifically. For location-based analysis, a small k or a fine-grained geographic bucket preserves useful spatial resolution but increases re-identification risk in sparsely populated areas (a rural ZIP code with very few residents can fail to genuinely protect anyone even at k=5, while the same k in a dense urban ZIP is comfortably anonymous); the safe group size is a property of the underlying population density, not a single k value that works everywhere.
Worked example
A dataset of 12 customer records has quasi-identifiers (3-digit ZIP prefix, age decade). Grouped by that pair: three records share (100, 30s), three share (100, 40s), and six share (200, 30s). Every group has at least 3 members, so this dataset satisfies k-anonymity for k=3: no combination of ZIP-prefix and age-decade is unique to fewer than 3 people. If the analysis instead needed 5-digit ZIP and exact age, the same 12 people would very likely each land in their own group (k=1), which is not anonymized at all; the generalization from 5-digit to 3-digit ZIP and from exact age to decade is precisely what raised k from 1 to 3, at the cost of losing that finer geographic and age resolution for the analysis.
Trade-offs & pitfalls
The recurring mistake is treating these as competing options to choose once, rather than layering them: a common, effective pattern is tokenizing direct identifiers for operational joinability, then additionally applying k-anonymity-style generalization to the quasi-identifiers before the dataset is used for broad cohort or geospatial analysis, since tokenization alone doesn't address the auxiliary-linkage re-identification risk that k-anonymity is specifically designed to reduce.
A team member who owned a set of important dashboards or datasets is leaving. Design a handoff process: what documentation and knowledge-transfer sessions are required, who takes temporary ownership, what access needs to change, and what checkpoints ensure nothing quietly breaks a month later when nobody remembers who to ask.
Sample Answer
Direct answer. Treat the departure as a forced deadline for something that should already be documented: assign a named temporary or permanent owner immediately rather than leaving it unassigned, run structured knowledge-transfer sessions before the departure date rather than relying on an exit interview, rotate every credential and access grant tied to that person, and schedule specific checkpoints after departure rather than assuming silence means success.
Documentation and knowledge-transfer sessions
- Have the departing owner produce, or validate if partial documentation already exists, a handoff document covering what each owned dashboard or dataset does and for whom, the data sources and any non-obvious transformation logic, known quirks or workarounds, the on-call or support pattern, and a list of scheduled jobs or automations tied to their account.
- Run at least one live walkthrough session with the incoming owner, not just a written document. A session surfaces the "oh, and one more thing" details that never make it into a doc written under deadline pressure, and lets the incoming owner ask questions while the source is still available.
- Record or take detailed notes during the session specifically for anything time-sensitive or judgment-based, for example a metric definition under active dispute with another team, since that's exactly the kind of context that's easy to lose and hard to reconstruct later.
Who takes temporary ownership. Assign a specific named person, not "the team," before the departure date, ideally someone who attended the knowledge-transfer session and has some existing context. If no one has context, temporary ownership defaults to the departing owner's manager or team lead as the accountable party, even if they delegate the day-to-day work, so there's always one name responsible.
What access needs to change
- Rotate or reassign any service-account credentials, API keys, or scheduled-job authentication running under the departing owner's personal identity, a common source of "quietly breaks a month later" incidents, since a personal account's access often silently continues working until it's deprovisioned, at which point everything tied to it fails at once.
- Grant the incoming owner the access levels actually needed to operate, not a blanket copy of the departing owner's full access, which is often broader than the role strictly requires.
- Revoke the departing owner's access on their actual last day, coordinated with IT or security offboarding, and confirm nothing was missed by checking for any resource still showing them as owner or admin.
Checkpoints ensuring nothing quietly breaks a month later
- Week 1 post-departure: confirm every scheduled job or pipeline that was tied to the departing owner's credentials ran successfully at least once under the new ownership.
- Weeks 2-4: the incoming owner handles a live incident or support question independently, or with light backup, as a practical test of whether the knowledge transfer actually took, rather than assuming it did because the sessions happened.
- 30-day checkpoint: an explicit review meeting confirming the handoff document is accurate against what was actually learned in the first month, closing out any open items from the original transfer.
- A calendar reminder, not memory, should own this cadence; the checks happen on schedule regardless of whether anything visibly went wrong, since the failure mode is exactly the quiet break nobody notices until it's overdue.
Worked example. A departing analyst owned 6 dashboards and 2 scheduled data pipelines that ran under their personal warehouse credentials. The handoff document lists all 8 assets with their data sources and known quirks; a 90-minute walkthrough session covers each; the incoming owner is assigned before the last day; the 2 pipelines are migrated from the departing analyst's personal credentials to a shared service account in the week before departure, not after, so any breakage from the migration is caught while the departing analyst can still help; access is revoked on the last day; and at the week-1 checkpoint, both pipelines are confirmed to have run successfully under the new service account.
Trade-offs and pitfalls
- The most common mistake is leaving scheduled jobs running under the departing person's personal credentials past their last day, which fails silently, running fine right up until credentials are revoked, then breaking all at once, often days or weeks after everyone's attention has moved on.
- Assigning temporary ownership to "the team" instead of one named person means the 30-day checkpoint has no one clearly accountable to run it, and it quietly doesn't happen.
- Treating the walkthrough session as optional when documentation already exists underestimates how much undocumented judgment, why a metric is defined the odd way it is, which stakeholder actually cares about which number, lives only in the departing owner's head.
Compare role-based access control (RBAC), attribute-based access control (ABAC), and row or column-level masking as approaches to controlling access to a shared analytics platform holding both financial and PII data. What are the trade-offs in complexity, auditability, and how fine-grained the control can get, and how would you migrate an organization running on ad-hoc, undocumented permissions toward one of these models over the course of a year?
Sample Answer
RBAC (role-based access control) is the simplest and fastest of the three but the coarsest; ABAC (attribute-based access control) is the most fine-grained and auditable when instrumented well but the most complex to build and test; row- or column-level masking is complementary to both rather than a substitute, since it controls what a granted user sees within a query, not whether they can query at all. A year-long migration from ad-hoc permissions typically lands on RBAC as the structural backbone with masking layered on top, escalating to ABAC only where role explosion would otherwise occur.
Trade-off comparison
| Dimension | RBAC | ABAC | Row/column masking |
|---|---|---|---|
| Complexity to implement | Low: define roles, map permissions to roles, assign users to roles | High: needs a policy engine evaluating attributes (user clearance, data sensitivity, purpose, time of day) at query time | Medium: needs masking rules per column and integration into the query layer |
| Auditability | High-level: you can show which role accessed what, but not always why a specific row was visible | Best when instrumented: policy decisions log the attribute values that drove the decision, giving a defensible "why," but only if logging is built in from day one | Strong for what was hidden, needs separate logging for what was requested but masked |
| Granularity | Coarse; a fixed role like "analyst" either can or can't see a table, leading to role explosion (a role per customer or per region) if finer control is needed | Fine-grained by design: rules like "user's region equals row's region" or "user has completed PII training" scale without creating new roles | Fine-grained at the column level, complements either RBAC or ABAC rather than replacing the access decision |
| Performance | Fastest: a role check at authorization time is a simple lookup | Slower: attribute retrieval and policy evaluation add latency per request, though caching mitigates this | Adds query-time cost (view rewriting, conditional logic per row), mitigated with materialized masked views |
Migrating from ad-hoc, undocumented permissions over a year
An organization running on ad-hoc grants (individual users given table access one-off over time, with no record of why) cannot jump straight to ABAC: there's no clean attribute model yet, and building one on top of undocumented existing access just formalizes the mess. A phased approach:
- Months 1-2: Inventory and freeze. Audit every existing grant: who has access to what, and can anyone currently explain why. Freeze new ad-hoc grants; any new access request from this point goes through a lightweight ticket, even before the target model exists, so the mess stops growing while it's being cleaned up.
- Months 3-5: Define roles from observed usage, not from an org chart. Cluster the actual query patterns (which tables does each existing user touch) into a small number of roles (analyst, engineer, finance, admin) rather than inventing roles theoretically. Migrate users into these roles, revoking their old ad-hoc grants as each migration completes.
- Months 6-7: Layer in column-level masking for known-sensitive fields. Independent of the role migration, identify PII and financial columns and apply masking by default, unmasked only for the roles that demonstrably need it. This is often the fastest risk reduction for the least migration effort, since it doesn't require the role structure to be perfect first.
- Months 8-10: Identify role-explosion pressure points. As the RBAC structure stabilizes, some access needs won't fit cleanly into roles (row-level restriction by customer or region, time-boxed elevated access). These are the specific, narrow places to introduce ABAC policies rather than a blanket ABAC rollout: a policy engine deciding "is this user's territory attribute equal to this row's territory" for the 2-3 places that actually need it.
- Months 11-12: Audit and steady-state review process. Establish a recurring (for example quarterly) access review where each role's membership and each ABAC policy's effect is re-certified by the relevant data owner, so the system doesn't quietly regress back into ad-hoc grants six months after the migration.
Trade-offs & pitfalls
The main pitfall in this migration is trying to design the "perfect" ABAC policy model up front before any RBAC baseline exists: without a clean role structure, attribute-based rules end up encoding the same undocumented exceptions they were meant to replace. The other common failure is skipping the freeze in month 1; if ad-hoc grants keep happening during the migration, the inventory from month 1 is stale by month 6 and the whole effort has to restart.
Unlock Full Question Bank
Get access to all 31 Data Governance, Contracts, and Classification interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.