Egor Howell Production-Grade ML Deploy Framework

Transform any ML idea into a fully automated, end-to-end production system with CI/CD, a live database, and a deployed front-end — built to a standard that impresses technical recruiters.

// TL;DR

The Egor Howell Production-Grade ML Deploy Framework is a step-by-step method for turning any ML idea into a fully automated, end-to-end deployed system — with CI/CD, a live database, a scheduled retraining pipeline, and a hosted front-end. Use it when you want to move a model beyond a Jupyter notebook into a real product, especially for portfolio projects that impress technical recruiters. It combines a two-domain architecture (forecast + optimise), single-responsibility code files, tests that outnumber source lines, Poetry, CircleCI, GitHub Actions, Supabase, Streamlit, and a VPS. Ship a working baseline first, then refine — and apply for jobs while you iterate.

// When should you use the Production-Grade ML Deploy Framework?

Use this skill when a user has an ML model or idea they want to move beyond a notebook into a real, deployed, automated product. Especially relevant when preparing portfolio projects for job applications or when learning how professional ML engineers structure production systems.

// What do you need before starting a production-grade ML project?

  • Project domain / personal interestrequired
    The real-world topic the ML project will address (e.g. finance, health, sports). Must be something the builder is genuinely interested in.
  • ML task / algorithm choicerequired
    The core prediction or optimisation task and the model or method to be used (e.g. time-series forecasting with Prophet, classification with XGBoost).
  • Data sourcerequired
    Where the raw input data will come from (e.g. a public API like yfinance, a CSV, a web scrape).
  • Output / actionrequired
    What the model's predictions should drive — a portfolio allocation, a recommendation, an alert, etc.
  • Target deployment environment
    Where the app will live — VPS, cloud provider, etc. A Hostinger KVM2 VPS is the reference setup in this framework.

// What principles guide the Production-Grade ML Deploy Framework?

Do it first, then do it right, then do it better

Ship a working end-to-end system before adding sophistication. A simple, complete pipeline beats a sophisticated, incomplete one every time. Iterate on a live baseline rather than waiting for perfection before deploying.

Personal Interest → Project Idea Formula

List five things you are interested in outside of work. For each, write five questions — giving 25 questions total. Then ask how ML or a tech-based approach could answer each one. Use this process to find project ideas that are personal, differentiated, and sustainable to build.

Sufficient Quality, Scalable, Something Others Don't Have

A portfolio project must meet three bars: it must be of sufficient quality (production code, tests, CI/CD), it must be scalable in principle, and it must be something differentiated — i.e. most candidates will not have it.

Tests Should Outnumber Source Code

Every function in source code should have at least two or three tests associated with it. If your test directory has fewer lines of code than your source directory, you are under-testing. More test lines than source lines is the target.

Group Code by Action

Code associated with a specific action (extraction, processing, modelling, optimisation, database interaction) belongs in its own dedicated file. Each file exposes one primary callable. This is how production ML engineering teams actually organise codebases.

Don't Over-Complicate the Stack

AWS and complex cloud infrastructure are often overkill for a solo portfolio project. Simpler tools — Poetry, Pyenv, CircleCI, GitHub Actions, Supabase, Streamlit, a VPS — are what professionals actually use, are faster to ship, and are just as impressive when combined correctly.

Apply for Jobs Whilst You Build

You do not need to finish the project before applying. Start applying while iterating. At interview, describe what you have built and what you are actively working to improve — this signals continuous learning, which is exactly what employers want to see.

// How do you build and deploy a production-grade ML project step by step?

  1. 1

    Generate your project idea using the Personal Interest → 25 Questions Formula

    List 5 personal interests. Write 5 questions per interest. Identify which questions can be answered with ML or data. Pick the one that is most personally motivating AND least likely to appear in other candidates' portfolios. Do not ask someone else what project to build — use this formula yourself.

  2. 2

    Define the two-domain architecture: a forecasting/ML component AND an optimisation/action component

    Production ML projects impress more when predictions feed into a downstream action or decision system. In the reference project: Prophet forecasting feeds Markowitz Portfolio Optimisation. Map your own equivalent: what does your model predict, and what decision or allocation does that prediction drive?

  3. 3

    Set up Python version and dependency management with Pyenv and Poetry

    Install Homebrew → install Pyenv → set local Python version to your target (e.g. 3.12) → pip install poetry for that version → run `poetry install` to create the virtual environment from pyproject.toml. Never mix Python versions across repos. Document all dependencies in pyproject.toml; Poetry.lock handles transitive dependencies automatically.

  4. 4

    Structure the repository with the canonical production layout

    Required structure: `src/` (source code), `tests/` (unit tests), `Makefile` (convenience commands), `pyproject.toml` (dependencies), `README.md`. Additional: `scripts/` (shell scripts for deployment), `.circleci/config.yml`, `.github/workflows/`. Every production repo at a professional ML company follows this structure or a close variant.

  5. 5

    Write the source code split across single-responsibility files

    Mandatory files: `main.py` (entry point and orchestration only), `extractor.py` (data ingestion), `processor.py` (preprocessing and feature engineering), `model.py` (training and inference), `optimizer.py` (downstream decision logic), `database.py` (Supabase integration), `streamlit_app.py` (front-end), `settings.py` (all constants — tickers, date ranges, risk parameters, model hyperparameters). `main.py` calls these modules in sequence; it does not contain logic itself.

  6. 6

    Create a Makefile with standard commands

    At minimum implement: `make install` (runs `poetry install`), `make lint` (runs Ruff + Black), `make type-check` (runs mypy), `make test` (runs pytest), `make check` (runs format → lint → type-check → test in sequence), `make dashboard` (launches Streamlit locally). The Makefile is not optional — it is a signal of professional practice.

  7. 7

    Write unit tests targeting every source function, targeting more test lines than source lines

    Use pytest. Prefix every test function with `test_` so pytest autodiscovers them. For each source function, write tests covering: the happy path, at least one edge case, and one failure mode. Assert on output columns, types, shapes, and values — not just that the function runs without error.

  8. 8

    Set up Supabase as the Backend-as-a-Service database

    Create a Supabase account and project. Define a table whose column names exactly match the dictionary keys your code will write (e.g. `as_of_date`, `ticker`, `predicted_price`, `predicted_return`, `weight`). Store the Supabase URL and service-role secret key as environment variables locally (in `.zshrc` or `.env`). Never hardcode credentials. Mirror these as GitHub repository secrets for CI use.

  9. 9

    Configure CircleCI for continuous integration

    Create `.circleci/config.yml`. Define three jobs: `lint`, `type-check`, `test` — each running on Python 3.12, installing the project via Poetry, then executing the relevant make command. Add a workflow that runs all three jobs on every push. In GitHub, protect the `main` branch: require a pull request and require all CircleCI checks to pass before merging. This prevents anyone from accidentally breaking production.

  10. 10

    Configure GitHub Actions for daily automated model execution

    Create `.github/workflows/daily_run.yml`. Set a cron schedule (e.g. `0 9 * * *` for 9 AM UTC). Add `workflow_dispatch` so it can also be triggered manually from the GitHub UI. Steps: checkout code, setup Python 3.12, install Poetry, install dependencies, run `poetry run python src/main.py`. Inject Supabase credentials from GitHub repository secrets as environment variables — do not rely on local secrets.

  11. 11

    Configure GitHub Actions for automated deployment on merge to main

    Create `.github/workflows/deploy.yml`. Trigger: `on push to main`. This job SSHes into the VPS using a private key stored as a GitHub secret (paired with a public key on the VPS) and executes `scripts/deploy.sh`. The deploy script: navigates to the repo on the VPS, runs `git pull`, checks for Poetry dependency changes, and restarts the Streamlit service via systemd (`systemctl restart <service_name>`).

  12. 12

    Provision and configure a VPS to host the Streamlit front-end

    Purchase a VPS (reference: Hostinger KVM2 — 8GB RAM, 2 cores, Ubuntu). SSH in. Install: Python, Pyenv, Poetry, Nginx. Clone the repo. Add Supabase credentials to the repo's `.env` file (not to the system environment). Configure a systemd service for Streamlit with `Restart=always`. Set up Nginx as a reverse proxy. Install Certbot for HTTPS/SSL. The Streamlit service polls Supabase every ~300 seconds for new predictions and re-renders automatically.

  13. 13

    Build the Streamlit front-end to display predictions and allocations

    The front-end should API-call Supabase on load and display: portfolio allocation weights (as percentages), per-asset predicted price and predicted return for the next trading day, a time-series chart of actual vs. predicted prices for the last month, and prediction accuracy over time. Keep it simple — Streamlit is appropriate here. For production complexity, replace with a JavaScript framework, but ship Streamlit first.

  14. 14

    Validate the full end-to-end pipeline before presenting the project

    Run `main.py` locally. Confirm predictions appear in Supabase with the correct timestamp. Open the Streamlit app locally and confirm data renders. Merge a dummy change to main and confirm the GitHub Actions deploy workflow fires and the live VPS reflects the change. Run `make check` and confirm all linting, type-checking, and tests pass. Only then is the project in a state to show a recruiter.

// What are real examples of projects built with this framework?

A user interested in sport wants to build a project predicting player injury risk and recommending squad selection percentages.

Apply the Personal Interest → 25 Questions Formula with 'sport' as one of the five interests. Identify 'which players are most likely to be injured next match week?' as the ML question. Build two domains: a forecasting model (e.g. a classification model predicting injury probability per player) and an optimisation layer (e.g. a linear programme maximising expected squad points subject to injury-risk constraints). Structure code into extractor.py (pulls player stats from a sports API), processor.py, model.py, optimizer.py, database.py (writes selections to Supabase), and streamlit_app.py (displays recommended squad and confidence scores). Set up CircleCI for CI, a daily GitHub Action to retrain on new match data, a deploy action on merge to main, and host the dashboard on a VPS.

A user interested in e-commerce wants to forecast product demand and optimise restocking orders.

Use the 25 Questions Formula with 'retail / e-commerce' as an interest. Identify 'how many units of each SKU will sell next week?' as the ML question. Two domains: Prophet (or similar) forecasting demand per SKU, and an inventory optimisation layer minimising holding cost plus stockout cost subject to budget constraints. settings.py holds SKU list, lead times, cost parameters. The daily GitHub Action pulls the latest sales data, reruns forecasts, pushes restocking recommendations to Supabase. Streamlit displays recommended order quantities per SKU. CircleCI enforces code quality gates. VPS hosts the live dashboard accessible to the team.

// What mistakes should you avoid when building a production-grade ML project?

  • Waiting until the project is 'finished' before applying for jobs — the framework is designed to be built iteratively while you apply.
  • Using AWS or complex cloud infrastructure for a solo portfolio project — it is overkill, slows you down, and does not add proportionate signal. Start with the simpler stack (VPS + GitHub Actions + Supabase).
  • Having more source code lines than test lines — this is a direct red flag to reviewers. Every source function needs at least two tests.
  • Hardcoding API keys or credentials anywhere in the codebase — all secrets must live in environment variables locally and in GitHub repository secrets for CI/CD.
  • Force-pushing directly to main — always protect main, require pull requests, and require CircleCI checks to pass before merging.
  • Choosing a project topic because someone else said it was a good idea — only projects grounded in genuine personal interest are sustainable to build and compelling to discuss in interviews.
  • Skipping the Makefile — without it the repo looks like a notebook project, not production code. It is a low-effort, high-signal addition.
  • Choosing an ML algorithm purely for sophistication — plug-and-play models like Prophet are deliberately chosen here because they let you focus on the engineering and deployment, which is the harder and more differentiating skill.
  • Neglecting the downstream action layer — a model that only predicts without driving a decision or allocation is less impressive than a two-domain system (forecast + optimise).
  • Not adding type annotations and running mypy — type checking is part of `make check` and is standard practice in professional ML engineering.

// What key terms should you know for production-grade ML deployment?

End-to-End ML Project
A project that covers the full pipeline from data ingestion through model training, prediction, storage, and a live deployed front-end — not just a notebook or a trained model in isolation.
Personal Interest → 25 Questions Formula
Egor's project ideation method: list 5 personal interests, write 5 questions per interest (25 total), then identify which questions ML can answer. Produces differentiated, personally motivated project ideas.
Sufficient Quality
Egor's bar for portfolio projects: the project must have production code standards, unit tests, CI/CD, and deployment — not just a working notebook.
Production-Grade Code
Code structured with single-responsibility files, type annotations, docstrings, unit tests outnumbering source lines, a Makefile, and dependency management via Poetry — matching how ML engineers write code at large tech companies.
Makefile
A Unix utility file in the repo root that exposes shorthand commands (`make check`, `make lint`, `make test`, `make dashboard`) to standardise and speed up common development operations.
Pyenv
A Python version manager used to install and switch between multiple Python versions per repository, preventing version conflicts across projects.
Poetry
Egor's preferred Python dependency manager. Manages package versions via `pyproject.toml` and resolves transitive dependencies into `poetry.lock`. Used in production at multiple top tech companies according to Egor.
CircleCI
The CI/CD platform used to automatically run linting, type-checking, and unit tests on every push and pull request, preventing broken code from reaching the main branch.
GitHub Actions
Workflow automation tool used for two purposes in this framework: (1) running the ML pipeline on a daily cron schedule, and (2) triggering automated deployment to the VPS on every merge to main.
Supabase
A 'backend as a service' platform providing a real-time Postgres database and authentication. Used to store model predictions so the Streamlit front-end can API-call them without a custom backend.
Backend-as-a-Service
A hosted backend platform (Supabase in this framework) that provides database, authentication, and API infrastructure without requiring the user to build or manage a custom server backend.
Streamlit
A Python-native front-end framework used to build the portfolio dashboard. Chosen for simplicity and speed of development; Egor recommends replacing with a JavaScript framework if production complexity demands it.
VPS (Virtual Private Server)
A dedicated virtual machine (Hostinger KVM2 in the reference project) that hosts the Streamlit service 24/7 and serves the live web application.
systemd service
A Linux background process configuration that keeps the Streamlit app running continuously on the VPS, with `Restart=always` ensuring it recovers from failures automatically.
Deploy Script (deploy.sh)
A shell script stored in `scripts/` that is executed on the VPS by GitHub Actions on every merge to main. It pulls the latest code, checks for dependency changes, and restarts the Streamlit service.
Protect Main
A GitHub branch protection rule that prevents direct pushes to main, requiring all changes to go through a pull request that passes CircleCI checks — preventing accidental production breakage.
Do it first, then do it right, then do it better
Egor's core development philosophy: ship a working end-to-end baseline first, then refactor for quality, then layer in complexity. Never wait for perfection before shipping.
settings.py
A dedicated configuration file containing all project constants — tickers, date ranges, model hyperparameters, risk parameters — that should be editable without touching business logic.
make check
The master quality-gate command in the Makefile that runs code formatting, linting, type-checking, and pytest in sequence. Must pass locally before pushing any branch.

// FREQUENTLY ASKED QUESTIONS

What is the Egor Howell Production-Grade ML Deploy Framework?

It's a complete method for taking an ML model from notebook to a live, automated production system. It covers project ideation, single-responsibility code structure, unit testing, CI/CD with CircleCI, daily retraining and deployment via GitHub Actions, a Supabase database, and a Streamlit front-end hosted on a VPS — the way professional ML engineers actually build and ship systems.

What is a production-grade ML project?

A production-grade ML project covers the full pipeline — data ingestion, training, prediction, storage, and a live deployed front-end — not just a trained model in a notebook. It meets three bars: sufficient quality (production code, tests, CI/CD), scalability in principle, and differentiation (something most candidates won't have). It runs automatically and drives a downstream decision, not just a prediction.

How do I come up with an ML portfolio project idea?

Use the Personal Interest → 25 Questions Formula. List five things you're genuinely interested in outside work, write five questions for each (25 total), then ask which questions ML or data could answer. Pick the one that is most personally motivating AND least likely to appear in other candidates' portfolios. Personal interest makes the project sustainable to build and compelling to discuss in interviews.

How do I deploy an ML model without using AWS?

Use a simpler, professional stack: Poetry for dependencies, CircleCI for CI, GitHub Actions for scheduled retraining and deployment, Supabase as a backend-as-a-service database, Streamlit for the front-end, and a VPS (like Hostinger KVM2) running the app via a systemd service behind Nginx with Certbot SSL. This ships faster than AWS and is just as impressive when combined correctly.

How does this framework compare to a typical Jupyter notebook ML project?

A notebook project stops at a trained model; this framework produces a live, automated system. The difference is production signals: single-responsibility files, a Makefile, tests outnumbering source lines, protected main branches, CI/CD gates, scheduled retraining, a real database, and a deployed dashboard. Recruiters can distinguish the two instantly — the framework demonstrates engineering maturity, not just modelling ability.

When should I use this framework?

Use it when you have an ML model or idea you want to move beyond a notebook into a real, deployed, automated product. It's especially valuable when building portfolio projects for job applications or when learning how professional ML engineers structure production systems. It's overkill only if you never intend to deploy or share the project.

Do I need to finish the project before applying for jobs?

No — start applying while you iterate. The framework is designed to be built on a live baseline. At interview, describe what you've shipped and what you're actively improving. This signals continuous learning, which is exactly what employers want. Waiting for a 'finished' project is one of the framework's named pitfalls.

What results can I expect from building a project this way?

A live, automated ML product that retrains daily, stores predictions in a real database, and serves them through a hosted dashboard — plus a codebase that passes linting, type-checking, and tests via a single command. This gives you a differentiated portfolio piece that signals production engineering competence and gives you concrete, credible talking points in technical interviews.

How much testing does this framework require?

Every source function should have at least two or three tests covering the happy path, an edge case, and a failure mode. The target is more lines of test code than source code — if your tests directory is smaller than your source directory, you're under-testing. Assert on output columns, types, shapes, and values, not just that functions run.

Which ML algorithm should I use for a production portfolio project?

Choose a plug-and-play model like Prophet or XGBoost rather than the most sophisticated option available. The engineering and deployment are the harder, more differentiating skills, so a simple model lets you focus there. Choosing an algorithm purely for sophistication is a named pitfall — the model is a means to demonstrate production practice.

What is the two-domain architecture in this framework?

It's pairing a forecasting/ML component with an optimisation/action component so predictions drive a decision. In the reference project, Prophet forecasts feed Markowitz portfolio optimisation. A model that only predicts is less impressive than a system that predicts AND acts — like injury forecasting feeding squad selection, or demand forecasting feeding restocking orders.

// GET THIS SKILL — FREE

Use this skill in your AI

Every skill on SkillForge is free. Drop your email and copy this skill straight into Claude, ChatGPT, or any LLM.

We'll email you when new skills drop. Unsubscribe anytime.