Module 13 β ProductionΒΆ
π§ β DeepTab Β· π Course home Β· CI/CD & Deployment βΆ
Goal: Take the code youβve been writing in notebooks and graduate it into a packaged, tested, scheduled, observable Python project that another engineer can pick up.
Estimated time: 5β7 hours.
Prerequisites: Module 1 (functions). Helpful: NB 12 (HTTP), NB 31 (the toolkit weβll package).
βββββββββββββββββββββββββββββββββββββββ
β src/ layout + pyproject.toml + β
β pytest + CLI entry point + β
β mypy / ruff / virtualenv β
β (NB 45) β
βββββββββββββββββββ¬ββββββββββββββββββββ
β
βΌ
NB 46
Scheduling
cron / systemd /
GitHub Actions /
Prefect
Notebooks at a glanceΒΆ
# |
Notebook |
β± Time |
Difficulty |
What youβll build |
|---|---|---|---|---|
45 |
|
~50β70 min |
Intermediate |
|
46 |
|
~45β60 min |
Intermediate |
A weekly support-ops report that runs itself β task function, scheduler loop, idempotency, retries, and alerts, plus ready-to-ship cron / systemd / GitHub Actions / Prefect snippets |
Notebook guidesΒΆ
45 Β· From Notebook to Project β 45_from_notebook_to_project.ipynbΒΆ
A notebook is a great place to figure things out and a terrible place to deploy from. This lesson follows one concrete story the whole way through: the loose helpers youβve been copy-pasting between notebooks (call_cost, safe_get, parse_number) get assembled β in front of you, on the real filesystem under /tmp β into a shippable package called costkit, a cost toolkit for LLM usage. The mental model is βfrom a messy workbench to a labelled toolboxβ: every function gets a drawer (cost.py, parsing.py), the lid (__init__.py) lists the public API, and anyone can carry the whole box to CI, a server, or a teammateβs laptop.
By the last cell the project has a standard folder layout, a pyproject.toml with declared dependencies, real pytest tests (run in-process from the notebook), a costkit total ... CLI a scheduler can invoke, a README and .gitignore, and mypy/ruff configuration β plus a detour into what import actually does (modules, packages, sys.path) and the reproducible virtualenv workflow.
π― The rule of thumb. Once two notebooks copy-paste the same function, move that function into a project. Once one notebook needs to be re-run on a schedule, build the project around it.
Learning objectives:
Lay out a Python project with the standard
src/layout β and decide what__init__.pyexposes vs. keeps privateWrite a
pyproject.tomlthat declares your package and its dependenciesCreate a virtualenv and
pip installyour project in editable modeWrite pytest tests with fixtures and parametrize
Build a CLI with
argparse(ortyper)Run type checks with mypy and linting with ruff
Sections:
Why graduate from a notebook?
The standard layout β
src/vs flatBuild the project β for real, in
/tmpWrite the package modules (what
importactually does; module vs package)The project metadata β
pyproject.tomlThe CLI
The tests
README +
.gitignoreInstall the package and run the tests
Type hints and linting β mypy & ruff
The virtualenv workflow
Where to go next β the rest of βproduction Pythonβ
Practice: 3 β quick exercises Β· 4 π§ͺ practice exercises (ββββ, incl. a βDebug me πβ) Β· 4 π§ stretch exercises (βββ) Β· π bonus mini-project: a monthly_report entry point.
Tools covered: src/ layout Β· pyproject.toml (setuptools) Β· pytest (approx, raises, parametrize) Β· argparse (with typer/click as the step-up) Β· mypy Β· ruff Β· venv + editable installs.
46 Β· Scheduling and Automation Orchestration β 46_scheduling_orchestration.ipynbΒΆ
You have a package (NB 45); the last mile of automation is making it run without a human present. One concrete job carries the whole notebook: a weekly support-ops report that every Monday at 6 a.m. fetches last weekβs ticket numbers, computes the share handled automatically, writes a summary file, and pings the team in Slack β the same scenario Capstone A (NB 47) picks up later. The mental model is βhire a night-shift robotβ: it needs a job description (the task function), a shift schedule (cron / systemd / Prefect), the habit of writing down what it did (logging), and the sense to phone you when something breaks (alerts) β without re-doing work it already finished (idempotency).
Starting from a bare task function, the notebook layers on each production essential in turn β an in-process scheduling loop you can watch tick, idempotency, retries with backoff, failure notifications β then compares the four hosting options with copy-paste snippets, dissects how cron reads 0 9 * * 1-5 (proved offline with a tiny matches(cron_expr, dt) function), and assembles everything into a production-shape skeleton with a closing tour of common pitfalls. Picking a host boils down to the notebookβs decision tree:
βββ on your laptop, low-frequency βββ cron
β
Where does the task βΌββ on a Linux server, low-frequency βββ systemd timer
actually run? β
βββ on GitHub already, weekly / on-PR βββ GitHub Actions
β
βββ multiple tasks, dependencies between them βββ Prefect / Airflow / Dagster
Learning objectives:
Write a task function thatβs safe to call repeatedly
Use the
schedulelibrary for in-process scheduling (no extra infrastructure)Add structured logging so a failed 3 a.m. run leaves a clear trace
Make tasks idempotent and add retries with backoff at the task level
Wire up email / Slack / webhook notifications for failures
Pick between cron, systemd, GitHub Actions, and Prefect/Airflow
Sections:
What does βautomationβ actually mean? (task function / scheduler / observability)
The task function β a runnable unit of work
In-process scheduling with
scheduleIdempotency β safe to re-run
Retries with backoff β survive the flaky internet
Notifications β fail loud
Picking a host β cron, systemd, GitHub Actions, Prefect (incl. how cron parses
0 9 * * 1-5, proved offline)Logging β what to record
Putting it together β the production-shape skeleton
Common pitfalls
Practice: 4 β quick exercises Β· 4 π§ͺ practice exercises (ββββ, incl. a lock-file guard and a βDebug me πβ) Β· 4 π§ stretch exercises (βββ: circuit breaker, chained tasks with file-based handoff, exponential backoff, cron pretty-printer) Β· π bonus mini-project: a daily metrics digest.
Tools covered: the schedule library (re-implemented in ~20 lines for offline runnability) Β· logging Β· a retry-with-backoff wrapper Β· Slack incoming-webhook alerts Β· cron Β· systemd .service/.timer units Β· GitHub Actions workflow YAML Β· Prefect flows (Airflow/Dagster compared).
How these notebooks workΒΆ
Both lessons run 100% offline, like every notebook in this course: NB 45 creates its package on the local filesystem and runs pytest in-process, while NB 46 stands in a 20-line TinyScheduler for the schedule library and proves cron expressions with pure Python β no server, network, or scheduler daemon required. Each notebook opens with a Colab badge and a time/difficulty line, drops ββ Quick exercise (~2 min)β checkpoints with collapsible solutions as you go, and closes with β-rated π§ͺ practice exercises (including a βDebug me πβ), π§ stretch exercises, a π bonus mini-project, key takeaways, and a self-assessment checklist.
Where nextΒΆ
β Module 14 β CI/CD & Deployment (../14_cicd/README.md) β you learned packaging and scheduling here; Module 14 takes the same app through Docker, pipelines, and a live server. Then the Capstones (../15_capstones/47_capstone_analytics.ipynb).
π Finished this module? Test yourself with the Module 13 quiz β five questions, ~10 minutes.