Membawa kode ML keluar dari Jupyter menuju Python production: struktur project src-layout, type hints & dataclass untuk skema data, unit testing dengan pytest, dan packaging dengan pyproject.toml agar model code siap dipakai pipeline.

Setelah di episode 2 kita membangun kerangka siklus hidup ML, sekarang kita mulai praktik di fondasi paling bawah: kode Python itu sendiri. Jupyter Notebook adalah alat yang luar biasa untuk eksplorasi — tetapi ia bukan tempat yang tepat untuk model yang akan dideploy, diuji, dan dirawat selama bertahun-tahun.
Mengapa episode ini penting? Karena hampir semua masalah di dunia MLOps berakar dari kode ML yang tidak terstruktur: fungsi di notebook yang tidak bisa di-import, skema data yang tidak didefinisikan, dan tidak ada tes yang menangkap kesalahan sebelum pipeline jalan. Episode ini mengubah cara kalian menulis kode ML — bukan cara melatih model.
Notebook menjadi script yang terstruktur lewat pola src-layout. Struktur project ML yang sehat kira-kira seperti ini:
churn-predictor/
├── src/
│ └── churn/
│ ├── __init__.py
│ ├── config.py
│ ├── data/
│ │ ├── load.py
│ │ └── validate.py
│ ├── features.py
│ ├── model.py
│ └── evaluate.py
├── tests/
│ ├── test_features.py
│ └── test_model.py
├── pyproject.toml
├── README.md
└── .gitignoreKunci polanya: kode produktif tinggal di src/, tes di tests/, dan config terpisah di config.py. Setiap modul punya satu tanggung jawab: memuat data, membuat fitur, melatih model, mengevaluasi. Struktur ini bukan soal estetika — ia menentukan apakah fungsi bisa di-import, diuji, dan dikemas dengan benar.
Model ML hidup dari data tabular dengan skema tertentu. Alih-alih mengoper dict atau list acak yang gampang salah, definisikan skema sebagai dataclass:
from dataclasses import dataclass
@dataclass
class CustomerFeatures:
age: int
monthly_charges: float
tenure_months: int
contract_type: str
def risk_score(self) -> float:
return self.monthly_charges / max(self.tenure_months, 1)Dengan dataclass, IDE dan type checker menangkap kesalahan tipe sebelum runtime. Di production nanti (episode 9), skema ini akan menjadi validasi request API — memastikan model tidak menerima input yang bentuknya salah.
Untuk data yang lebih kompleks atau ingin validasi runtime yang ketat, kalian bisa memakai Pydantic. Ia umum dipakai bersama FastAPI di episode 9.
Notebook jarang diuji; kode production harus diuji. Untuk ML, unit test bukan hanya menguji logika, tapi juga skema data dan bentuk output model — kesalahan yang paling sering muncul di pipeline:
from churn.features import build_features
from churn.model import predict_proba
import numpy as np
def test_features_shape():
df = build_features(sample_rows=10)
assert df.shape[0] == 10
assert "tenure_ratio" in df.columns
def test_predict_proba_output():
probs = predict_proba(np.zeros((1, 8)))
assert probs.shape == (1, 1)
assert 0.0 <= probs[0][0] <= 1.0Tes-tes ini terlihat sepele, tetapi ia menjaga pipeline dari kegagalan kelas "kolom hilang setelah refactor" dan "output berubah dimensi". Jalankan dengan:
pip install pytest
pytest -qDi episode 8, tes-tes ini akan dijalankan otomatis di CI — dan menjadi gate yang menahan pipeline jika model tidak sehat.
Warning
Jangan menaruh data besar atau download dataset di dalam unit test — test akan lambat, flaky, dan menggantung CI. Mock data kecil (10-50 baris) sudah cukup untuk menguji logika dan skema. Uji besar dilakukan di episode 7 (pipeline training).
Agar kode bisa dipakai oleh pipeline lain (mlflow run, CI, serving), kemas sebagai package Python. Format modernnya adalah pyproject.toml:
[project]
name = "churn-predictor"
version = "0.1.0"
description = "Churn prediction model"
requires-python = ">=3.12"
dependencies = [
"numpy>=2.0",
"pandas>=2.2",
"scikit-learn>=1.6",
"mlflow>=2.20",
]
[project.optional-dependencies]
dev = ["pytest>=8.0", "ruff>=0.9"]
[build-system]
requires = ["setuptools>=75"]
build-backend = "setuptools.build_meta"
[tool.setuptools.packages.find]
where = ["src"]Dengan file ini, siapa pun bisa memasang package dan semua dependency-nya secara reproducible:
pip install -e ".[dev]"
python -c "from churn.features import build_features; print('OK')"Kalian bisa mengganti pip dengan uv untuk kecepatan jauh lebih tinggi, atau Poetry jika ingin lockfile dan publishing terintegrasi. Yang penting konsisten — episode 4 akan memperketat pinning dependency.
sys.path → gunakan struktur src + package yang benar.ruff dan pyright/mypy sejak awal.Pada episode 3 ini, kalian telah membawa kode ML keluar dari Jupyter menuju Python production.
Inti yang harus dibawa pulang:
src/, tes di tests/, config terpisah.Di episode 4 selanjutnya kita akan membahas reproducibility & environment — dependency pinning, lockfile, dan container Docker untuk ML — agar hasil training kalian bisa direproduksi di mesin mana pun, dan "di laptop saya jalan" tidak lagi jadi alasan. Sampai jumpa di episode 4!