What You Built in This Module
You can now hand another engineer a package that imports without side effects, runs as a module, lives under version control, rejects malformed input at its boundary, and reinstalls the same environment anywhere. None of those properties showed up in the notebook you began with. You earned each one by reproducing the specific failure first and then fixing it.
What you completed
The first three lessons built the package: the shape, the conversion, the git workflow. The last three were hardening passes, and each one broke the working package to show you what was still missing.
- You set up the package shape before any of your own code went into it. The
src/layout,python -mwith a__main__guard, and the editable install aren’t ceremony. They’re the reasonimport mypkgresolves from anywhere instead of only from the folder you launched in, and every later lesson leaned on that blueprint. - You converted the notebook. Same logistic regression, same test AUC, but lifted out of one kernel’s run order into importable functions that an installed package produces. That conversion is what made the rest of the module possible, because you can’t harden a notebook. You can only harden a package.
- You came away understanding git instead of memorizing it. The point was never the four commands. It was knowing which place each one moves your work to, and why a branch keeps
mainworking while you change things underneath it. Every module and project after this one assumes you already run that workflow. - You saw the two import failures that only show up once the package moves. It ran because you launched it one specific way, and it broke the instant a scheduler or a test runner launched it from somewhere else. Watching that happen, and seeing why a module that does real work at import time is a trap, is what made the editable install feel load-bearing instead of cosmetic.
- You built a boundary that rejects nonsense. A bare dict of features will happily score a typo’d key or a stringified number and say nothing. The pydantic contract at the edge is what turns a silently wrong answer into a malformed input that gets refused, and now the training and serving paths share that one contract.
- You made reproducibility a claim you can actually defend. A locked dependency graph plus a deliberate
.gitignoreline is what lets you say the only thing that changed is the thing I changed, and every later debugging session depends on that sentence being true.
Check your understanding
Try these without scrolling back up. Each one is a fresh project, not the loan-scoring package the lessons used, because the point is to apply the judgment rather than recall the case. See how far you get before you open the hint.
-
A colleague hands you a single script,
etamodel.py, that trains and serves a ride-share arrival-time predictor and gets run withpython etamodel.pyfrom inside its own folder. You need to import itspredictfunction from a scheduled job that lives somewhere else. Describe the project shape you’d move it into, and explain why a folder of loose scripts can’t be imported reliably from another directory.Hint
Think about what makes `import x` resolve from any working directory instead of only the folder you launched in. Walk through the three pieces: the package directory layout, running as a module, and the install that puts the package on the path. -
The ride-share model lives in a notebook where cell 4 defines the feature transform and cell 9 trains, and the AUC is only correct if the cells run top to bottom. A teammate asks why you can’t just schedule this to run nightly as-is. Explain what the notebook hides that a converted package would expose, and say specifically what you’d lift out first.
Hint
The problem is run-order dependence: the result is a property of one kernel's execution sequence, not of the code. Think about what an importable function gives you that a cell can't, and which piece of the notebook has to become callable before anything else can. -
You’re mid-experiment on the ride-share model, the version on
mainis what a teammate is demoing this afternoon, and you want to try a different feature set without putting that demo at risk. Describe the version-control move that lets you experiment whilemainstays runnable, and name what each step actually moves your work between.Hint
The point isn't the commands. It's knowing which place each one moves your work to. Think about why a branch isolates change, and what the working tree, the staging area, and the commit history each hold. -
The ride-share package imports and runs fine when you launch it from its own directory, but the nightly scheduler invokes it from
/var/jobsand it fails with an import error the moment it runs there. Diagnose the two distinct ways an import can break once the launch location changes, and explain why a module that does real work at import time is a trap.Hint
Separate "the package isn't on the path from this directory" from "import time runs code that needs an environment that isn't there yet." Think about why the editable install fixes the first, and why side effects at import time hide the second until something else launches you. -
The ride-share endpoint accepts a feature dict and one day receives
{"distance_km": "12.4", "rider_count": 3}: a number arrived as a string and an expected key got misspelled upstream. The model returns a confident ETA anyway. Describe where you’d put the check that refuses this input, and what that boundary buys both the training and the serving path.Hint
A bare dict validates nothing, but a typed contract at the edge does. Think about the difference between a wrong answer returned silently and a malformed input refused at the door, and why both paths should share the one contract. -
Two engineers run the ride-share training on what they believe is the same setup and get different AUCs, and a third can’t tell which result to trust. Explain what makes the sentence “the only thing that changed is the thing I changed” defensible, and name the two artifacts that have to be in place before that sentence can be true.
Hint
Reproducibility is a claim you have to be able to defend, not a hope. Think about what a locked dependency graph removes from the set of things that could have moved, and what a deliberate ignore rule keeps out of the comparison.
What it adds up to
Making the notebook run was the start of the job. Making it safe for someone else to build on was the rest of it. What you can hand off now isn’t a notebook that works in one kernel. It’s a package with real properties: it imports cleanly, runs as a module, lives under version control, turns away malformed input at its boundary, and installs the same environment anywhere. Violate any one of those and you get a specific failure, which is exactly what this module did to you: it showed you the failure on something already working, then had you fix it. That’s the gap between code that runs on your machine and a foundation another engineer can ship on.
What comes next
The package is the container, and its feature step is the naive first pass you converted straight from the notebook, mean-fill and all, which quietly leaks test statistics into training. That naive transform is where the next module lives. You’ll replace it with a real one that turns the raw Lending Club table into the numeric matrix a model consumes. The hard part isn’t getting features to flow. It’s making the same transform produce identical output at train time and at serve time, with the data-quality repairs, label alignment, and frozen learned state that a one-off transform never has to worry about. The leak you carried forward rides through that module too, named again at its imputation step, and it finally gets closed in M3, Numerical & Statistical Foundations, where the fill is learned on the training split alone. The typed contract you built here grows into a whole-frame schema check, and the train/serve skew this module pointed at is the very problem that module builds first and then defeats.