Keeps secrets, data files, notebook checkpoints and Python junk out of Git. Copy it to .gitignore in a repository.
# .gitignore for a Python + Jupyter + data project (Acme Analytics template)
# One pattern per line. "folder/" means a whole folder, "*" matches any text,
# and a line starting with "!" brings a file back. Check a rule with:
# git check-ignore -v <path>
# --- Secrets: passwords, tokens and keys never go into Git -------------------
.env
.env.*
!.env.example
*.pem
*.key
secrets/
# --- Data: raw and generated data lives in the warehouse, not in Git ---------
data/*.csv
data/*.xlsx
*.parquet
*.feather
*.pkl
# --- Jupyter: autosave copies (clear notebook outputs before committing) -----
.ipynb_checkpoints/
# --- Python leftovers ----------------------------------------------------------
__pycache__/
*.py[cod]
.venv/
venv/
*.egg-info/
build/
dist/
.pytest_cache/
.coverage
htmlcov/
# --- Editor settings (delete this line if your team shares .vscode/) ---------
.vscode/
.idea/
# --- Operating-system junk ------------------------------------------------------
Thumbs.db
desktop.ini
.DS_Store
gitattributes.txt
Stops Windows/Mac line-ending noise; marks binary files. Copy it to .gitattributes in a repository.
# Sample .gitattributes for a Python + data team.
# To use it, copy this file into the root of a repository and name it .gitattributes
# (it is stored here as gitattributes.txt so it doesn't affect this folder).
#
# Why: Windows uses CRLF line endings, macOS/Linux use LF. Without rules, the same
# file can look "completely changed" just because someone on another OS saved it.
# Let Git normalise line endings for every text file:
# stored as LF in the repository, converted for your OS on checkout.
* text=auto
# Scripts that must keep specific endings on every OS.
*.sh text eol=lf
*.ps1 text eol=crlf
*.bat text eol=crlf
*.cmd text eol=crlf
# Source and config files (explicitly text).
*.py text diff=python
*.sql text
*.md text
*.yml text
*.yaml text
*.json text
*.csv text
# Notebooks are JSON text. If you install nbstripout, it adds a filter line for *.ipynb here.
*.ipynb text
# Binary files: never convert, never try to merge line by line.
*.png binary
*.jpg binary
*.pdf binary
*.xlsx binary
*.parquet binary
# Large files tracked with Git LFS (only after running: git lfs install / git lfs track).
# *.parquet filter=lfs diff=lfs merge=lfs -text
dot-github/pull_request_template.md
Pre-fills every new PR description. Copy it to .github/pull_request_template.md in a repository.
## What does this change?
<!-- One or two sentences. What will be different after this is merged? -->
## Why?
<!-- The problem it solves. Link the issue: "Closes #12" closes it automatically when this PR is merged. -->
Closes #
## How did you test it?
<!-- The commands you ran and what you checked by hand. -->
- [ ] `python -m unittest` passes locally
- [ ] Tried it on a sample file:
## Anything reviewers should look at closely?
<!-- Risky parts, open questions, or decisions you'd like a second opinion on. -->
## Checklist
- [ ] The branch is up to date with `main`
- [ ] No secrets, passwords, or real data files in the diff
- [ ] Notebook outputs cleared (if you changed a notebook)
dot-github/ISSUE_TEMPLATE/bug_report.md
Bug report and feature request forms. Copy it to .github/ISSUE_TEMPLATE/ in a repository.
---
name: Bug report
about: Something is wrong or broken
title: "Bug: "
labels: bug
---
## What happened?
<!-- What you saw. Paste the exact error message if there is one. -->
## What did you expect?
## How to reproduce it
1.
2.
3.
## Where
- Branch or version (for example `main` or `v1.2.0`):
- File or step in the pipeline:
## Anything else?
<!-- Screenshots, sample input (never real customer data or passwords). -->
dot-github/ISSUE_TEMPLATE/feature_request.md
Bug report and feature request forms. Copy it to .github/ISSUE_TEMPLATE/ in a repository.
---
name: Feature request
about: Suggest an improvement
title: "Feature: "
labels: enhancement
---
## The problem
<!-- What is hard or slow today? Who is affected? -->
## What you'd like
<!-- The outcome you want, not necessarily how to build it. -->
## How we'll know it's done
- [ ]
- [ ]
## Alternatives you considered
dot-github/workflows/python-ci.yml
Runs the tests on every push and pull request (GitHub Actions). Copy it to .github/workflows/ci.yml in a repository.
# Continuous integration for a small Python project.
# Copy this file to .github/workflows/ci.yml in a repository (the folder name matters).
# GitHub runs it on every push to main and on every pull request, and shows the
# result as a check (green tick or red cross) on the pull request.
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
# 1. Get a copy of the repository onto the runner (a fresh Linux machine).
- uses: actions/checkout@v6
# 2. Install the same Python version you use on your laptop.
- uses: actions/setup-python@v5
with:
python-version: "3.10"
# 3. Fail fast if any file has a syntax error.
- name: Check syntax
run: python -m compileall -q .
# 4. Run the unit tests. A failing test turns the check red.
- name: Run tests
run: python -m unittest discover -s tests -t . -v
dot-github/CODEOWNERS
Requests reviews from the right people automatically. Copy it to .github/CODEOWNERS in a repository.
# CODEOWNERS: who must review changes to which files.
# Copy to .github/CODEOWNERS. When a pull request touches a matching file,
# GitHub automatically requests a review from the listed people or teams.
# With branch protection's "Require review from Code Owners" turned on,
# their approval is required before merging.
#
# Format: <file pattern> <@user or @org/team> ...
# The LAST matching line wins, so put general rules first and specific ones after.
# Everything: the team lead reviews by default.
* @sam-chen
# Pipeline logic: the data engineers.
transform.py @acme/data-engineering
extract.py @acme/data-engineering
# CI and repository settings: the platform team.
/.github/ @acme/platform
# Notebooks: anyone on the analytics team can review.
/notebooks/ @acme/analytics
commit-messages.md
How to write messages your team will thank you for.
# Commit messages that help your team
A commit message is a note to the person who reads the history later. Often that person is you, six months from now, trying to find out why something changed.
## The shape
```
Cap discounts at 100 percent <- subject: what the commit does, max ~50 characters
Discounts above 100% produced negative <- body (optional): why, in plain sentences.
invoice totals for Fabrikam Medical. <- Wrap at ~72 characters.
The cap matches the rule in the
billing policy.
Closes #14 <- links (optional): issues, tickets
```
- **Subject in the imperative**, as if giving an order: "Add", "Fix", "Remove", "Rename". A good test: *"If applied, this commit will ___"*.
- **Start with a capital letter, no full stop at the end.**
- **Explain why in the body** when it isn't obvious. The diff already shows *what* changed.
- **One idea per commit.** A fix and a reformat are two commits.
## Good vs. not so good
| Not so good | Better |
|---|---|
| `fix` | `Fix rounding of tax on invoice totals` |
| `changes` | `Add supplier filter to the monthly report` |
| `WIP` | `Add CSV export for supplier totals (tests pending)` |
| `updated transform.py` | `Cap discounts at 100 percent` |
| `Fixed the bug Sam found yesterday` | `Handle empty invoice files in read_invoices` |
| `asdfgh` | *(anything that says what changed)* |
## Conventional Commits (if your team uses them)
Some teams add a type prefix so tools can build changelogs automatically:
```
feat: add supplier filter to the report
fix: cap discounts at 100 percent
docs: explain how to run the tests
refactor: move tax logic into its own function
test: cover negative quantities in line_total
chore: update CI to Python 3.12
```
Check your team's `CONTRIBUTING.md`, or look at `git log --oneline` on `main`, and copy the style you see.
## Writing longer messages from PowerShell
`git commit -m "subject"` is fine for one line. For a subject **and** a body, run `git commit` with no `-m`. Git opens your editor (VS Code, once you set `core.editor` on Day 1). Write the message, save, and close the tab.
Or pass `-m` twice. Each `-m` becomes its own paragraph:
```powershell
git commit -m "Cap discounts at 100 percent" -m "Discounts above 100% produced negative totals."
```
pr-description.md
Pull request descriptions that get fast reviews.
# Pull request descriptions reviewers love
A pull request (PR) asks your team to accept a change. A good description makes review fast, because reviewers know what to look for and how you checked it.
## A good example
> **Title:** Cap discounts at 100 percent
>
> **What does this change?**
> `apply_discount` now caps the percentage at 100, so an invoice total can never go below zero.
>
> **Why?**
> Fabrikam Medical's September file had a 150% discount line (a data-entry error upstream). It produced a negative total, which broke the monthly report. Closes #14.
>
> **How did you test it?**
> - Added `test_discount_is_capped` (fails before the fix, passes after)
> - `python -m unittest` passes locally
> - Re-ran the report on the September sample: totals are all ≥ 0
>
> **Anything to look at closely?**
> Should we also *log* discounts over 100%, so the upstream team hears about bad data? Happy to do that in a follow-up PR.
## A not-so-good example
> **Title:** fixes
>
> fixed the discount thing
The reviewer has to reverse-engineer everything from the diff, and nobody can link it to the issue later.
## Tips
- **Keep PRs small.** Under ~300 changed lines gets a faster, better review. Split big work into a series of PRs.
- **Title = what it does**, like a commit subject.
- **Link the issue** with a closing keyword: `Closes #14`, `Fixes #14` or `Resolves #14`. The issue closes automatically when the PR merges into the default branch.
- **Open a Draft PR** early if you'd like feedback before it's finished.
- **Review your own diff first** on the *Files changed* tab. You'll catch leftover debug prints, and anything you didn't mean to include.
- **Say how you tested it.** "Tests pass" is good. "Tests pass, and I ran it on last month's file" is better.
code-review-comments.md
Giving and receiving review kindly and clearly.
# Code review comments: giving and receiving
Code review is how a team shares knowledge and catches mistakes. It's about the code, never the person.
## Label your comments so the author knows what's required
| Prefix | Meaning | Example |
|---|---|---|
| **blocking:** | Must change before merge | *blocking: this reads `.env` from the repo root. Can we use an environment variable instead, so the password never sits in the repo?* |
| **suggestion:** | Worth considering, the author decides | *suggestion: `sum(line_total(q, p) for q, p in lines)` might read more clearly than the loop.* |
| **question:** | You want to understand | *question: why do we round before applying tax rather than after?* |
| **nit:** | Tiny style point, optional | *nit: typo in the docstring, "percantage".* |
| **praise:** | Something done well | *praise: nice test name, it reads like a sentence.* |
## Kind and specific beats short and sharp
| Instead of | Try |
|---|---|
| "This is wrong." | "I think this returns a negative total when `percent` > 100. Could we cap it, or raise an error?" |
| "Why would you do it this way?" | "What made you choose a loop here? I wondered if `sum()` would be simpler." |
| "Fix the naming." | "nit: `calc2` → `apply_discount` would say what it does." |
## GitHub's "suggested change"
On the **Files changed** tab, add a comment on a line and insert a suggestion. The author can apply it with one click (**Commit suggestion**), and it becomes a real commit on the PR branch. Great for typos and small fixes.
~~~markdown
```suggestion
"""Take a percentage discount off an amount (capped at 100%)."""
```
~~~
## When you finish a review, choose one
- **Comment**: general feedback, no decision yet.
- **Approve**: good to merge (after any small fixes you mentioned).
- **Request changes**: at least one blocking issue.
## Receiving review
- Say thanks, and assume good intent.
- Reply to every comment: "Done in a1b2c3d", or explain why not.
- Push fixes as **new commits** on the same branch. The PR updates itself.
- Don't take it personally. Even very senior people get change requests.
- If a thread goes back and forth more than twice, have a quick call instead.
branch-naming.md
Naming conventions and when to branch.
# Branch naming
A branch name tells your teammates what the work is and why it exists, before they open anything.
## A common convention
```
<type>/<issue-number>-<short-description>
```
| Type | Use for | Example |
|---|---|---|
| `feature/` | New functionality | `feature/7-supplier-filter` |
| `fix/` or `bugfix/` | A bug that isn't urgent | `fix/14-discount-cap` |
| `hotfix/` | An urgent fix for something already released | `hotfix/1.2.1` |
| `docs/` | Documentation only | `docs/contributing-tips` |
| `refactor/` | Restructuring without changing behaviour | `refactor/split-transform-module` |
| `chore/` | Tooling, CI, dependencies | `chore/ci-python-3-12` |
| `release/` | Preparing a release (Git Flow teams) | `release/1.3.0` |
| `<name>/` | Personal or experimental work (some teams) | `alex/try-polars` |
## Rules of thumb
- **lowercase-with-hyphens**: no spaces, no capitals (they cause trouble on case-insensitive Windows).
- **Short but specific**: `feature/7-supplier-filter`, not `feature/stuff` or `feature/the-new-supplier-filter-that-sam-asked-for-in-standup`.
- **Include the issue or ticket number** if your team uses them (`INV-231`, `#7`). Tools can then link the branch to the ticket.
- **One branch per piece of work.** Delete it after it's merged (GitHub offers a **Delete branch** button right after merging).
- **Never work directly on `main`.** Branch first, even for one-line fixes.
## Creating one
```powershell
git switch main
git pull
git switch -c feature/7-supplier-filter
```
Always branch from an **up-to-date** `main`, so you start from the latest code.