Day 1 Homework and Wrap-Up - Advanced Testing, Macros & Mesh¶
This pulls together everything from 1.2-advanced-testing.md, 1.3-jinja-macros-and-custom-schema-logic.md, and 1.4-dbt-mesh.md into one closing checklist, plus two new requirements: a documentation/test-coverage health check, and a final PR.
Work on your dpg/<your_name>/day1 branch. Keep committing as you go - don't save it all for one giant commit at the end; a real commit history makes the PR review (and your own reasoning trail) much easier to follow, especially for the bug you're about to find and fix in section 1.
1. Finish the outstanding exercise steps¶
From 1.2-advanced-testing.md¶
- Use dbt Catalog's Recommendations (Testing category) against
mediapulse_baseto get a prioritized coverage-gap list - start with the streaming domain's staging layer, which doesn't have amodels.ymlyet - Write the tests to fill the identified gaps yourself (don't add a
uniquetest on a column you haven't confirmed is actually unique) - Run
dbt buildand confirm the gaps you picked are actually closed - Decide the right fix for the incoming
content_typevalues (shorts,interactive) - since these are known upcoming values rather than an unknown future change, that points toward padding theaccepted_valueslist rather than looseningseverity; apply that fix everywherecontent_typeis tested in the project, and note in your commit message why this case called for that tactic over the alternative - Add a
not_nulltest onrelease_dateinfct_streaming_events, scoped withconfig: where:to only the rows wherectnt_typedid resolve - a plainnot_nullwould fail on every legitimately-unmatched event, which isn't the bug you're after - Add a
dbt_utils.not_empty_stringtest onplan_typein the staging model, not just relying on theaccepted_valuestest that runs later in the mart after lowercasing - Add a
dbt_utils.fewer_rows_thantest comparingint_dedupe_subscribersagainst the staging model it's built from - Add a
uniquetest onuser_idtodim_subscriptions(alongside the existingnot_null) - confirm it fails, and write down why (stg_streaming__subscriptions_lifecycle_recis one row per subscription event, not per user) - Fix
dim_subscriptionsby pointing it atint_dedupe_subscribersinstead of the staging model directly; confirm theuniquetest now passes - Add a
uniquetest onevent_idtofct_streaming_events- confirm it fails, and write down why (the join onuser_idalone, with no date condition, fans every watch event out once per subscription event that user has ever had) - Implement both candidate fixes for the fan-out - (a) joining against
int_dedupe_subscribersfor a clean one-row-per-user join, and (b) keeping the full subscription history but adding the missing date-range condition so each event matches only the subscription that was actually active atwatched_at- and decide which one you're shipping - Write down the real trade-off between the two: option (a) is simpler and always correct on row count, but stamps every event with the user's single most recent subscription regardless of which one was active when the event happened; option (b) preserves point-in-time accuracy but is more SQL to get right. State which one this use case actually needs and why
From 1.3-jinja-macros-and-custom-schema-logic.md¶
- Find the safe-division
caseexpression already inline infct_ad_impressions.sql(click_through_rate) - Write a
safe_divide(numerator, denominator, precision=4)macro that returns0on a zero denominator and otherwise the rounded division, with a default value forprecision - Replace the inline
caseinfct_ad_impressions.sqlwith a call tosafe_divide; confirm the model builds and the column's values are unchanged - Add a
+schemaconfig for thestreamingandadsmodel folders indbt_project.yml; run the affected models and check where they actually landed - is it the schema name you configured, or something else? - Open
mediapulse_base/macros/generate_schema_name.sqland compare it against dbt's defaultgenerate_schema_namemacro - as it stands they are identical, so the override changes nothing until you add the dev/prod branch below - Extend the existing macro (don't write a new one) with a condition on the current target's name, so dev builds land in one flat schema (e.g.
dbt_yourname) while prod gets the fulltarget-schema_custom-schemanaming (e.g.prod_streaming) - Answer the extension prompt: should
mediapulse_analyticsduplicate this schema-naming macro, or share it? Given that a project dependency (per1.4-dbt-mesh.md, step 1) only exposesmediapulse_base's public models as an API and never compiles or runs its actual code, decide whether sharing this macro across the mesh boundary is even on the table, and write down what that implies for keeping schema-naming behavior consistent between the two projects
From 1.4-dbt-mesh.md¶
- Read
mediapulse_analytics/dependencies.ymland write down what's different about a project dependency vs. thedbt_utilspackage dependency inpackages.yml - For each of the seven listed models (
stg_news__articles,stg_ads__campaigns,int_campaign_content_spend_allocation,dim_campaigns,dim_content_catalog,dim_subscriptions,fct_streaming_events), determine - frommediapulse_base/dbt_project.yml's project-level defaults and any folder-level overrides - whethermediapulse_analyticscan currentlyref()it - Read the team lead's request and write down explicitly: which models is this message asking you to make a real, ongoing commitment to (
dim_content_catalog,dim_subscriptions,fct_streaming_events), and which is it explicitly telling you not to worry about yet (theads/newsstaging models) - Add a folder-level default on
streaming/indbt_project.ymlto make the requested models public - Note (don't just assume) that this config change alone doesn't unblock the analytics team - cross-project
ref()resolves againstmediapulse_base's last successful production run, so this needs to be merged and built in prod before theirdbt buildwill see it - Add
contract: {enforced: true}underconfigfordim_content_catalog,dim_subscriptions, andfct_streaming_events; fill in every column'sdata_type, verified against actual warehouse output, not guessed - Confirm all three are materialized as
tableorview, then rundbt run --select dim_content_catalog dim_subscriptions fct_streaming_eventsand resolve anything the contract check flags - Decide whether the three staging models (
stg_streaming__content_ctlg,stg_streaming__subscriptions_lifecycle_rec,stg_streaming__usr_watch_events_log) - alsoaccess: publictoday - should get a contract too, and write down your reasoning against what the team lead's request actually asked for vs. what a staging contract would cost you on the next upstream source change - Extension: add the
v1/v2versioning block tofct_streaming_events, withv1keepingmonthly_fee_centsandv2replacing it withmonthly_fee_dollars(the same/ 100.0conversiondim_subscriptionsalready does) - Set
latest_version: 1explicitly, createfct_streaming_events_v2.sqlwith the real conversion logic, and leave the existing file asv1 - Set a realistic
deprecation_dateonv1(roughly a quarter out) - Confirm
dbt run --select fct_streaming_eventsbuilds both versions, anddbt run --select fct_streaming_events,version:latestbuilds onlyv1
2. Project health check - documentation & test coverage¶
Don't aim for 100% coverage on either front - that's a vanity metric, not a quality bar. Instead, confirm the following are all true before moving on:
Documentation:
- Every model you touched today (across testing, macros, and mesh) has a top-level
description -
dim_content_catalog,dim_subscriptions, andfct_streaming_eventseach document their contract in plain language somewhere visible - a future maintainer shouldn't have to reverse-engineer why a column can't just be renamed - The
safe_dividemacro has a description of what it does and whatprecisioncontrols - macros without docs are exactly the kind of thing a new team member re-implements by accident six months from now - Any column whose meaning isn't obvious from its name has a
description- this especially applies tomonthly_fee_centsvs.monthly_fee_dollarsnow that both are in play across versions -
dbt docs generateruns clean with no missing-doc warnings on anything you touched today
Test coverage:
- Every primary key has
not_null+unique- including the two you just fixed (user_idondim_subscriptions,event_idonfct_streaming_events) - The
content_typefix from section 1 is applied consistently everywhere the column is tested, not just in one model - The scoped
release_datetest and the staging-layerplan_typetest are both in place and passing - The
fewer_rows_thandedup test onint_dedupe_subscribersis in place and passing -
dbt buildruns clean - zero failing tests - across everything you touched today, including bothfct_streaming_eventsversions
If a fresh look at Catalog's coverage report still shows gaps after this, use judgment: a gap on a business-critical column (foreign keys, anything feeding the contract) is worth fixing; a gap on a free-text or clearly cosmetic column usually isn't. Write one line in your PR description on any gap you deliberately chose to leave open and why.
3. Open a PR¶
Open a PR from your dpg/<your_name>/day1 branch. The description should cover, at minimum:
- Summary - one or two sentences on what today's work accomplished overall
- Testing coverage - what gaps existed, what you closed, and what (if anything) you deliberately left open and why
content_typetest parameters - why padding the accepted list was the right call here, specifically because the two new values were already known, not a hypothetical future change- The fan-out bug - this is the centerpiece of today's work, so don't undersell it: explain what
dim_subscriptionsandfct_streaming_eventswere actually doing wrong, how theuniquetests proved it, which of the two fixes you shipped for thefct_streaming_eventsjoin, and why (point-in-time accuracy vs. simplicity) - Macros - a short note on
safe_divideand thegenerate_schema_namedev/prod branch, plus your answer to the sharing-vs-duplicating extension question - dbt Mesh (access + contracts) - confirm which models are now public, why the staging layer was or wasn't also contracted, and a reminder that this needs a production run before the analytics team can actually see it
- Versioning - flag the breaking change explicitly:
monthly_fee_centsis being phased out,v2is available now withmonthly_fee_dollars, and state the deprecation date so a reviewer standing in for the analytics team can't miss it
Tag the PR for review as if the mediapulse_analytics team lead from 1.4-dbt-mesh.md were genuinely about to depend on this - the description should give them everything they'd need without having to ask you directly.