learn-analytics-engineering-with-phoebe / Leader session 6 of 6
Learn Analytics Engineering with Phoebe · Leader track · Session 6 of 6

Documentation that stays true

Every leader reading this has funded a data wiki, and every one of them has watched it rot. That is not a discipline failure and another initiative will not fix it. Documentation kept away from the code has no forcing function, so it decays in weeks and the decay is invisible, which is the worst combination available. This session is about the structural alternative: descriptions that live beside the columns they describe, and a lineage graph derived from the code rather than drawn by a person. It closes the leader track, so it also closes with what to do next.

🟡 Leader track Heads of data · analytics leads · stakeholders No code to write 45 min
0-3 · Frame the session 3-15 · Why the wiki fails, and what changes 15-24 · Insist on generated · the honest limits 24-45 · Readiness check and next 30 days
Part 0

The only structural session in this track

The previous five sessions were mostly about judgement. a1 explained why the role exists, a2 made the case for models as code, a3 reframed a test suite as a trust budget, a4 was the spend decision, a5 was org design. All of those depend on people choosing well and continuing to choose well.

This one is different, and it is the reason it comes last. Generated documentation does not require anybody to keep choosing. It works because of where the text physically lives, and that makes it the one improvement on this list that does not degrade when attention moves elsewhere. If you take one thing from six sessions into your next planning cycle, take this one, because it is the only one that holds without you.

Live - presented in session Self-study - read after class ◆ Discussion - run it with your team Sources covered
★ What you walk out with today A structural explanation of wiki decay you can repeat to a sceptical executive, the three artifacts to insist are generated rather than written, an honest account of what generated documentation cannot do and where the rest belongs, and a 30-day plan that closes the leader track.
Part 1 · covers the dbt documentation on docs, and software-engineering practice applied to SQL

Why the wiki fails, and what changes 12 min live

The interesting question is not why documentation goes stale. It is why nobody notices. A wiki page that is wrong looks exactly like a wiki page that is right, and it keeps looking that way for years, being quoted in meetings.

Documentation kept away from the code a separate page, a separate edit, a separate habit nothing fails when it is skipped, so it is skipped a column rename does not touch the page wrong pages look identical to right pages decays in weeks, and the decay is invisible Documentation kept in the code the description sits beside the column it describes a rename and its documentation are one change the reviewer sees both, or neither, together lineage is derived from the code, not drawn stale text becomes visible in a diff The structural difference, in one sentence A wiki asks somebody to remember. Documentation in the code asks nobody, because the change and its description are the same commit and the same review. That is not better discipline, it is a different structure - and structure is the only kind of documentation fix that survives a busy quarter.
🔍 Click to zoom - the difference is where the text lives, not who is diligent
LiveSay the sentence to your sceptical executive4 min▶

You will be asked why this attempt at documentation will work when the last three did not. Here is the answer, and it does not require anybody to be more committed than they were last time.

"The last three attempts asked people to update a second place. This one has no second place." When the description of a column sits beside the definition of that column, renaming the column and updating its description are the same edit, reviewed by the same person, in the same change. There is no separate task to deprioritise, because there is no separate task.

Then add the part that closes it: and the dependency map is not drawn at all. It is read out of the project's own build output. This course's project has a lineage graph of 32 nodes and 47 edges, and nobody maintains it. It cannot be out of date, because if the dependencies it describes were wrong, the build that produced it would not have worked.

The reframe that lands Stop presenting documentation as a deliverable and present it as a by-product. Deliverables get deprioritised in busy quarters. By-products do not, because the thing that produces them is the work itself.
LiveDerived lineage versus the diagram from the offsite4 min▶

Almost every organisation has a data-flow diagram somebody made once. It is usually beautiful, usually on a wall or in a shared drive, and usually wrong in at least two places that matter. It is wrong because it was a snapshot of an estate that kept moving, and nothing about the estate moving updates the picture.

A derived graph is a categorically different object. Because every model names its inputs in the code rather than pointing at a table directly, the tool can read the whole dependency structure out of the code itself. That has three consequences a leader should care about:

  • It is accurate by construction. Not maintained accurately: incapable of being inaccurate about the dependencies that exist in the code. Session b4 of the builder track explores the real graph node by node.
  • It answers "what breaks if we change this" before the change. Which converts a rename from a three-week discovery process into a question with an answer, and is why teams with derived lineage still rename things.
  • It costs nothing per model. A drawn diagram gets more expensive as the estate grows, which is precisely why it stops being updated at the size where it would start being useful.

This is the single strongest line in a business case, and it is the one leaders most often leave out because it sounds like a technical detail. It is not. It is the difference between an estate you can change and one you can only add to.

Self-studyWhy invisible decay is worse than no documentation3 min read▶

A missing document is a known gap. Somebody asks a question, nobody has an answer, and the cost is a conversation. That is a cheap failure and it is self-announcing.

A wrong document is worse in a specific way: it gets quoted. An analyst reads that a column excludes cancelled orders, builds a report on that basis, and the report is wrong in a manner nobody can trace, because the reasoning was sound and the input was false. Six months later two teams present different numbers and the reconciliation takes a week, and the root cause is a page that was accurate when it was written.

This is why "we should just write better documentation" is not a plan. Better writing produces the same decay on a slightly slower schedule, and the failure mode is unchanged. The only fix that changes the failure mode is removing the second place where the text lives, so that being wrong requires the code to be wrong too.

Part 2 · covers the dbt documentation on generated docs and lineage

Three things to insist are generated 5 min live

You do not need to specify how any of this works. You need to insist on three outputs, refuse the hand-written versions of them, and be able to say why. Everything else about documentation is negotiable.

Insist on the output. Do not specify the method. 1 · Lineage derived, never drawn Read out of the project's own build output, not out of anybody's memory. This project: 32 nodes, 47 edges, unmaintained by design. If your diagram was drawn in a workshop, treat it as a historical document rather than a map. 2 · Column descriptions beside the column itself The meaning of a field lives where the field is defined, so both change together. The test: is the description visible in the same place as the definition? If it sits in a different system, you have two truths and no way to tell which one is current. 3 · The test list per model, published What must remain true about this table, listed, where a consumer can read it. This project: 100 tests over 23 models, 95 generic and 5 written by hand. Session a3 called that a trust budget. Published, it becomes a promise a consumer can rely on. Why these three and not a longer list Each one is currently maintained by hand somewhere in your organisation, each one is wrong today, and each one costs nothing once the models are in the tool. Three items you can actually enforce.
🔍 Click to zoom - three outputs, no method specified
Generated, never writtenWritten by a person, and ownedWhat each is good for
Lineage. What depends on what, read from the code. 32 nodes and 47 edges in this project's case. Why the design is what it is. Why this grain, why this dimension conformed, why revenue excludes what it excludes. Generated lineage answers "what breaks if we change this" mechanically. Written rationale answers "should we change this", which no tool can derive because the reasons were never in the code.
Column descriptions. The meaning of each field, stored beside its definition so both move in one change. Known compromises. The shortcut taken under deadline, the field that is approximate, the source you do not fully trust yet. Generated descriptions stop analysts guessing what a field means. A written compromise list stops the next person rediscovering a decision at the worst moment, and gives it an owner.
The test list per model. What must stay true about this table, published where consumers can read it. Consumers outside the tool. Dashboards, exports and syncs, declared deliberately, because the graph cannot see them. A published test list turns a3's trust budget into a promise. A declared consumer list turns an invisible dependency into one the impact analysis includes.
LiveWhat to say when your team proposes a catalogue project instead4 min▶

This proposal will arrive, and it will be well intentioned. A separate catalogue tool, populated by the team, describing all the tables. It sounds like more than generated documentation because it covers more surface area.

The question to ask is not whether the catalogue is good. It is: what makes an entry in it wrong loudly rather than quietly? If the answer is a person remembering to update it, you have rebuilt the wiki with a nicer interface and the same failure mode, and you now have three places where a column's meaning lives instead of two.

There is a legitimate version of this. A catalogue that reads its descriptions and lineage from the transformation project rather than storing its own is genuinely useful, because it adds discovery and search across systems on top of a source of truth that stays true. The distinction is direction: does the catalogue pull from the code, or does a human push into the catalogue? Pull is an amplifier. Push is a wiki.

One question, asked once "Where does the text physically live, and what breaks if it is wrong?" Every documentation proposal you will ever be shown can be sorted with that question in under a minute.
Self-studyThe freshness signal you get for free3 min read▶

Here is a benefit that rarely makes the business case and is worth knowing about. When descriptions live in the code, they inherit the code's history. You can see when a description was last touched, by whom, and in the same change as what.

That gives you something no wiki gives you: a way to find documentation that is probably wrong. If a model's logic was edited four times this year and its description has not been touched in eighteen months, that is a specific, findable, prioritisable piece of staleness. On a wiki, every page looks equally current, which is why nobody can triage them and why the annual documentation clean-up never happens twice.

It is a small mechanism and it changes documentation from a monolith you cannot maintain into a list you can work through. Ask your team whether they can produce that list. If the answer is yes, they have already built the thing this session is asking for.

Part 3 · covers the dbt documentation on exposures

The honest limits 4 min live

Generated documentation has exactly two blind spots and both of them matter. If you present it as complete, you will be caught out by one of them within a quarter, and the credibility cost will be larger than the thing you were overselling.

What generated docs tell you: what IS every model, column and type that exists what each column means, if somebody wrote it what depends on what, derived from the code what is tested, and what the tests assert when the description was last touched, and by whom What they cannot tell you: WHY why this grain and not a finer one why revenue excludes what it excludes which compromises were deliberate what was tried before and abandoned who to ask, and who decided The second blind spot: the graph stops at the edge of the tool Dashboards, scheduled exports and syncs into other systems are all genuinely downstream of your marts and none of them appear in a derived graph. So a change can be safe according to the lineage and break a board report. Declaring those consumers deliberately is exactly what exposures are for.
🔍 Click to zoom - two blind spots, both worth naming out loud
LiveWhere the WHY belongs, and who owns it4 min▶

Generated documentation describes the estate as it is. It has no access to the reasoning that produced it, because the reasoning was never written into the code and mostly could not be.

That material is real and it is what people actually need in the hard moments: why the fact table is at order-line grain rather than order grain, why the revenue definition excludes what it excludes, which compromise was accepted under deadline and is still outstanding, and what was tried in the first attempt and abandoned. None of it is derivable. All of it needs prose that somebody writes and somebody owns.

The sibling course, learn-data-modeling, keeps exactly this in a contract file alongside the model: the decisions, the grain justification, the known compromises, and the name against each. That is where it belongs. Not in the wiki, because it has the same decay problem, and not in generated docs, because it cannot be generated. Beside the model, written by hand, with an owner and a date.

The division to hold Generate everything that describes what is. Write, by hand and with a named owner, everything that explains why. Confusing the two is how you end up hand-maintaining things that should be free and leaving unwritten the things only a person can say.
Self-studyExposures, in leader terms3 min read▶

The derived graph knows about every dependency expressed inside the transformation project. It knows nothing about anything outside it, and the things outside it are the ones your stakeholders see.

The practical effect: a change can be entirely safe according to the lineage and still break the report the executive committee reads on Monday, because that report queries a mart directly and the graph has never heard of it. Nobody is at fault. The information genuinely was not there.

The mechanism for closing that gap is a declared consumer, which dbt calls an exposure: a dashboard, an export or a sync recorded deliberately in the project so it appears in the graph and in the impact analysis alongside everything else. It is a small amount of work with an unusual property for documentation, in that it makes something previously invisible show up in the blast radius automatically from then on.

As a leader you do not need the mechanism. You need to ask one question and then insist on the answer being written down somewhere the graph can see: which dashboards, exports and syncs read directly from our models? The builder track wires this up; your job is to make sure the list exists.

Discussion · 16 min · everyone participates

Readiness check ★ no code, no laptops

Three exercises. The third one takes ten minutes in your next team meeting, needs nothing but a whiteboard, and reliably produces a list nobody knew was missing.

Audit what you have. Five most-viewed documentation pages, last edit date, one factual claim each checked against reality.

Sort your artifacts. For each thing your team maintains by hand: could this be generated, and if so why is it not?

Name the invisible consumers. Every dashboard, export and sync that reads your top three tables directly.

Then write the 30-day plan. Three actions, three owners, three dates. Not a strategy.

LiveExercise 1 · the freshness audit6 min▶

The prompt. "Open our data documentation. Take the five most-viewed pages. For each one, read out the last edit date, and then pick one factual claim on the page and check it against the warehouse right now."

What a good answer sounds like. Discomfort, quickly followed by specifics: "this one says the table is refreshed hourly and it has been daily since March." That is the session's argument demonstrated on your own material in under six minutes, and it is far more persuasive than anything on these slides because your team found it themselves.

The failure mode to listen for. "The dates are recent, so we are fine." Recent edit dates measure attention, not accuracy, and pages get touched for formatting. Insist on the factual check, on the highest-traffic page, in the room. The most-quoted page being wrong is the finding that changes the next planning cycle.

LiveExercise 2 · generated or written6 min▶

The prompt. "List everything we currently maintain by hand that describes our data: the lineage diagram, the column glossary, the metric definitions document, the dashboard inventory, all of it. Now sort each one into three piles. Could be generated today. Cannot be generated, and needs a named owner. Should not exist at all."

What a good answer sounds like. The generated pile is larger than anyone expected, and the third pile is not empty. Something gets moved to "should not exist" because it duplicates another document, and somebody says so out loud. Ideal output is two or three items you stop maintaining this month, and one item that moves from nobody to a named owner.

The failure mode to listen for. Everything landing in "cannot be generated" with justifications about how the organisation is special. Some of that is real, most of it is attachment to work already done. Test each claim against the three artifacts in Part 2: lineage, column descriptions, test lists. If a team insists those three cannot be generated for them, the reason is usually that the models are not in a transformation tool yet, which is session a4's question and not a documentation question at all.

LiveExercise 3 · name the invisible consumers10 min · run this in your next team meeting▶

The prompt. "Pick our three most-used tables. Now list, on the board, every single thing that reads them directly and is not one of our own models: dashboards, scheduled exports, the sync into the CRM, that spreadsheet finance refreshes, the API somebody built. Then tell me where that list is currently written down."

What a good answer sounds like. A longer list than anyone predicted, with two or three items that surprise the room, and an honest "nowhere" to the second half of the question. That combination is exactly what exposures exist for, and you have now produced the input list in ten minutes without any tooling.

The failure mode to listen for. A short, confident list of the official dashboards only. Ask specifically about the unofficial ones: the export somebody set up for a board pack, the sync into a sales tool, the direct connection from a spreadsheet. Those are precisely the consumers that break loudly and are absent from every diagram, and the person who knows about them is usually not in the room, which is itself worth noticing.

Real world

The strongest version of this argument is one you can measure on your own estate in an afternoon. Take the most-quoted number in your company. Ask three questions: where is its definition written, when was that text last changed, and when was the logic behind it last changed. In most organisations the definition lives in a slide or a wiki page, the text was last edited more than a year ago, and the logic was edited last quarter. Nothing about that is negligence. It is the predictable output of keeping the description in a different place from the thing it describes, and no amount of commitment fixes it, because the commitment was never the constraint. Move the description next to the definition and the same three questions have boring answers forever.

Take this back to your team

Questions to ask your data team use them this week

Seven questions. Ask the second one first if you only have time for one, because the answer reframes everything else.

AskWhy it works
"When was our most-viewed documentation page last edited, and is it still true?" Two facts, one minute, and they are almost never consistent with each other. It converts an abstract argument about decay into a specific finding on your own material.
"Is our lineage diagram drawn or derived?" The single highest-leverage question in this session. Drawn means it is a historical document. Derived means it cannot be wrong about the dependencies in the code, and that difference decides whether you can safely change anything.
"Can you show me a column's description in the same place the column is defined?" Tests the structure rather than the intent. If the description lives in another system, you have two truths and no way to know which is current, whatever the policy says.
"Which dashboards, exports and syncs read directly from our models, and where is that list?" Surfaces the consumers the graph cannot see. "Nowhere" is the usual answer and it is exactly the gap exposures close, so the question doubles as the work item.
"Where do we record why a model is shaped this way, not just what it contains?" Separates generated from written. If there is no answer, the design reasoning exists only in one person's head, which is a5's single-point-of-failure list with a different label.
"What did we knowingly compromise on, and who owns that decision?" Known compromises are the most valuable undocumented asset in any data team. Written down with a name and a date, they stop being rediscovered at the worst possible moment.
"If I rename a column tomorrow, what tells me who I break?" The whole session in one question. A good answer is a command or a graph. A bad answer is a person's name, which means your impact analysis is somebody's memory and it does not take annual leave into account.
Homework · the next 30 days

Five things to do, in this order ◐ the leader track's closing brief

★ The leader track is complete Six sessions: why the role exists (a1), models as code (a2), tests as the trust budget (a3), buy versus build (a4), the team shape (a5), and documentation that stays true. If you now want to see all of it actually built rather than argued, the builder track starts at b1 · From hand-written SQL to a project, where the same marketplace warehouse is rebuilt as 23 models with 100 tests and reproduces the hand-built numbers to the cent. And when you are ready to run it on a schedule and monitor it, that is learn-dataops, because dbt does not schedule itself and nothing in these six sessions changes that.
Source material

Sources covered

This page covers:

✓The dbt documentation - generated docs, column descriptions, derived lineageParts 1-2 · the graph quoted here is this project's own, 32 nodes and 47 edges, read from its build output
✓The dbt documentation - exposures, for the consumers a derived graph cannot seePart 3 · treated as the leader's list-making job. Wired up in the builder track
✓Software-engineering practice applied to SQL - documentation in the same change as the codePart 1 · the structural argument, plus the freshness signal you inherit from the code's history
◐The dbt documentation - model contracts as a published promise to consumersPart 2 · named where the test list becomes a promise. Taught in the builder track
◐Kimball & Ross - the grain and conformance decisions that need written rationalePart 3 · the written half belongs in the contract file of learn-data-modeling
Check yourself

Three questions before you go 🎯 ◐ 90 seconds

1 · Your team proposes a new catalogue tool, populated by hand, to finally fix documentation. What is the question that decides whether to fund it?

Direction is the whole distinction. A catalogue that pulls descriptions and lineage from the transformation project adds discovery on top of something that stays true. A catalogue humans push into adds a third place where a column's meaning lives, with the same invisible decay as the first two.

2 · A change passes every test and the lineage shows nothing downstream affected. On Monday the executive committee's report is broken. What happened?

A derived graph is accurate about every dependency expressed inside the project and blind to everything else: dashboards, scheduled exports, syncs into other systems. That is the second blind spot, and declaring those consumers deliberately, which dbt calls exposures, is what closes it.

3 · Which of these should NOT be generated, and needs a named human owner instead?

Generated documentation describes what IS. It has no access to reasoning that was never in the code, and the design rationale plus the known compromises are exactly that. Keep them beside the models, written by hand, with an owner and a date, the way the modeling course's contract file does. Generate everything else.

The whole leader track on one card · pin this

a1 · Why the role existsA gap opened between landing data and asking questions of it. Somebody owns the middle now.
a2 · Models as codeVersion control, review, environments and rollback applied to SQL. Review is the load-bearing part.
a3 · Tests as trust100 tests on 23 models is a governance artifact. Decide what blocks a build; noise costs trust.
a4 · Buy vs buildCount models and editors first. Cloud sells operations and onboarding, never capability.
a5 · Team shapeThree roles, three layers, explicit ownership. Central owns the shared core, domains own their marts.
a6 · Docs that stay trueGenerate lineage, column descriptions and test lists. Write the WHY by hand, with an owner.
The measured claimSame warehouse twice. 111,906.48 both ways, to the cent. 32 nodes and 47 edges, derived.
What is nextSee it built: the builder track, b1. Schedule and monitor it: learn-dataops. dbt does not schedule itself.