Key takeaways
- Version the queries, scripts, and metric definitions, not generated output.
- Keep each commit to one logical change with a reason in the message.
- Use short branches and a review step for changes to published numbers.
- Never commit credentials or customer extracts; rotate any secret that leaks.
On this page
Version the logic, not the output
A report is the visible product; the queries, scripts, and metric definitions behind it are the assets that need a history. When a number changes, the first useful question is what changed in the logic and when. Git answers that with a diff, a commit message, and an author, provided the logic lives in the repository.
Keep generated files out of version control. A CSV export or a screenshot can be regenerated; committing it adds noise and may expose customer data. Store the code, the metric definitions, and a small synthetic sample instead.
Set up a repository that fits analysis work
Create a repository per reporting domain or shared analytics codebase, not one per query. A sensible layout separates SQL, Python, documentation, and tests. Add a README that explains how to connect to a development database, run the scripts, and find the definitions.
| Item | Decision |
|---|---|
| SQL models and queries behind a report | Include; this is the logic that needs history |
| Python scripts and notebook exports used in production | Include, with outputs cleared |
| Metric definitions and data dictionaries | Include |
| Small synthetic fixtures for tests | Include |
| Customer extracts and production data | Never commit |
| Large binary workbooks | Usually exclude; document the source |
Make each commit tell one story
A commit should describe one logical change: fix a join, add a validation, rename a metric. Mixing a formatting pass with a logic change makes review harder and hides the risk. Stage specific files or hunks until the commit is coherent.
Write the subject in the imperative and explain the reason in the body. 'Fix duplicate invoice totals in ar_aging' is useful; 'updates' is not. If the change alters a published number, say so and note the period affected.
git status
git diff sql/ar_aging.sql
git add sql/ar_aging.sql
git commit -m 'Exclude credit notes from invoice totals'Use short branches and a review step
Branch for a change, open a pull request, and ask someone to review the logic. The reviewer does not need to be a Git expert; they need to understand the metric. A second pair of eyes catches a missing filter, a changed join, or a hard-coded date that will break next period.
Keep branches short-lived. Long-running branches drift from main and produce painful merges. If a change is large, split it into a sequence that each leaves the repository working. Protect the main branch so changes arrive through review rather than direct commits.
Never commit secrets or production extracts
Connection strings, API keys, and passwords belong in environment variables or a secret manager. Add .env to .gitignore and commit a blank .env.example that lists the required variable names. A key committed once remains in history even after a later commit deletes it.
If a secret is committed, treat it as exposed. Rotate it first, then remove it from history with the team's agreed process. Deleting the file in a new commit is not enough. The same rule applies to customer data: use synthetic fixtures rather than extracts.
- Check the staged diff before every commit, not just the final one.
- Keep .env, credentials, and data extracts in .gitignore.
- Commit a blank .env.example that names the required variables.
- Use synthetic sample data that preserves structure, not identities.
- Rotate any secret that reaches a remote, even a private one.
Frequently asked questions
Do analysts need to learn the command line?+
Not all of it. A handful of commands covers most daily work: status, diff, add, commit, pull, push, and switch. A graphical client is fine if it makes diffs and history easier to read.
Should notebooks be committed to Git?+
Commit a cleaned notebook or, better, the script that reproduces the analysis. Clear outputs that contain data, keep execution order meaningful, and avoid noisy metadata changes that make every diff unreadable.
What is a good first repository?+
The SQL and scripts behind one recurring report. Add a README, a .gitignore, and a small synthetic fixture, then make a few focused commits. That teaches the workflow on a task you already understand.
How do I version a binary dashboard file?+
Decide explicitly. Some teams keep the binary for history; others document the model and rebuild it from source. Either way, version the definitions and source queries, because those are what can be reviewed and diffed.
Need help applying this?
Improve your data cleanup & pipelines.
Review the service scope or tell us what you are trying to improve. We will reply with a clear next step.
Free KPI definition templateRelated reading
Reading Excel files with pandas and openpyxl
How to read Excel files reliably with pandas and openpyxl: choose the engine, target the right sheet and header, protect types, and validate.
Python for finance teams: practical use cases that save time
Where Python genuinely helps finance and operations teams: file consolidation, reconciliation, validation, and recurring reports.