Field guide · 8~8 min
Change an existing model
Your first PR added a model nothing depended on. Most work after that is different: editing a model that other models already read. The edit itself is the same — what changes is that you now need to know who is downstream before you start.
Look downstream before you edit
A new model can only be wrong in itself. An existing model can be right in itself and still break the things that read it — and those breaks happen at build time, in CI, or worst of all silently in the numbers. One command shows you what you are dealing with before any of that:
dbt ls -s my_model+The + after the name lists the model and everything downstream of it. A short list means you can read each consumer and verify the whole set yourself. A long one changes the plan: read the direct children, rely on the others' tests, and say in the PR how far you looked.
While you are there, open the YAML of the models you would affect and note their owners. If your change will alter what someone else's model produces, a message to them before the PR is cheaper than a surprise during review — they usually know a constraint you do not.
Not all changes carry the same risk
Adding a column is the gentle case. Downstream models select their columns explicitly, so a new column changes nothing for them until someone chooses to use it. Document it, test it if it has rules worth asserting, and move on.
Renaming or removing a column breaks every model that selects it, immediately and visibly — the build fails. The work is finding the usages and updating them in the same PR. The dbt extension can rename a column across the whole project by following lineage rather than matching text, which is the reliable way to catch a usage hiding in a macro or an alias.
Changing logic — a filter, a join, a derivation is the case that deserves the most respect, because nothing fails. The columns keep their names, every build stays green, and the numbers downstream quietly change. Whether that change is correct is exactly what your PR has to establish: say what moves and why, and show a before-and-after for one example a reviewer can check.
The before-and-after is easier to produce than it sounds, because while you develop, both versions exist: production still holds the output of main's logic, and your dev build holds yours. One worksheet query puts them side by side:
select 'prod' as version, count(*) as rows, count(distinct person_id) as people
from REPORTING.OLIDS.FCT_PERSON_DIABETES_REGISTER
union all
select 'dev', count(*), count(distinct person_id)
from DEV__REPORTING.OLIDS.FCT_PERSON_DIABETES_REGISTERDifferences you expected become the evidence in your PR. Differences you did not expect are the review finding itself — caught by you, in dev, instead of by a dashboard user in a month.
Changing the grain — what one row means — is a different order of change. Every consumer was written against the old grain, and their joins and counts assume it. Talk to the owners of the downstream models first, update the grain test to assert the new contract, and treat the whole thing as a coordinated piece of work rather than an edit.
Prove it in downstream models, not only in the model you changed
dbt build -s my_model+The same + that listed the consumers now rebuilds and tests them. This is the step that makes the difference: a green build of your model alone proves the SQL runs, and nothing more. The grain tests of the models downstream are what tell you whether your change fanned out someone's join or emptied someone's filter. If the selection is too wide to build in full, build the direct children at least, and say in the PR where you stopped.
Write the PR for the people downstream
The reviewer of a change to a shared model is standing in for everyone who reads it. Give them what they need: which models are affected and how, how far you built and tested, and — for a logic change — one concrete example of a number that moves, with the reason it should.