Full-Stack Machine Learning Project Repository Structure Guide: From Chaos to Convention

A comprehensive guide to structuring full-stack ML project repositories from experimentation to production.
This article explores how to organize full-stack machine learning projects into clean, maintainable repositories. It covers the unique complexity of ML projects, recommends a directory structure based on the Cookiecutter Data Science template, and explains key principles like data immutability, code-data separation, and configuration externalization. It also addresses advanced topics including dependency management, containerization, experiment tracking, large file handling, and the value of open-source community feedback.
From Zero to One: Repository Structure for Full-Stack Machine Learning Projects
For many developers just entering the machine learning field, training a model may not be the hardest part. The real challenge often lies in integrating experimental code, data processing, model training, and frontend/backend services into a well-structured, maintainable complete project. Recently, a developer shared their first full-stack ML project on Reddit, sincerely seeking community feedback on their repository structure. This topic sparked considerable discussion and highlighted a commonly overlooked yet crucial aspect of ML engineering—project organization and engineering standards.

From the original post, the author had already completed a full loop from data to model to application interface. This "full-stack" approach is commendable: it means the model is no longer an experimental artifact sitting in a Jupyter Notebook, but a product that can actually serve users. However, as the author themselves recognized, "it works" doesn't mean "it's well-organized"—a chaotic repository structure will create enormous maintenance costs as the project scales.
Why Repository Structure Matters So Much for ML Projects
In software engineering, the rationality of code organization directly impacts team collaboration efficiency and long-term project maintainability. This is especially true for machine learning projects, as ML projects typically contain more complex components than traditional software.
The Unique Complexity of ML Projects
Compared to traditional software development, machine learning projects introduce an entirely new dimension—data dependency. Traditional software behavior is determined by code logic, while ML system behavior depends on the combination of code, data, and the training process. Google pointed out in their classic paper Hidden Technical Debt in Machine Learning Systems that the actual model code in ML systems often accounts for only a tiny fraction, while the surrounding "glue code" for data collection, feature extraction, configuration management, and monitoring constitutes the bulk of the system. This means an ML project's complexity doesn't grow linearly—it expands exponentially as data pipelines, model versions, and deployment environments multiply.
A typical full-stack ML project needs to handle several categories of content simultaneously:
- Data layer: raw data, cleaned data, feature engineering artifacts;
- Model layer: training scripts, model weights, evaluation metrics;
- Service layer: wrapping the model as an API (e.g., FastAPI, Flask);
- Application layer: frontend interface or calling logic;
- Configuration & experiments: hyperparameters, experiment logs, environment dependencies.
If these are all mixed together, the project quickly becomes impossible to track as iterations increase. Which model corresponds to which experiment? Which dataset was used to train which version? These questions require a clear directory structure to answer. In industry, the accumulation of such issues is called "technical debt," and ML projects, due to their experimental nature, are inherently more prone to accumulating this debt than traditional software.
Recommended Directory Structure for Full-Stack ML Projects
For beginners with these needs, the community has accumulated some mature best practices. Although the original post didn't provide a specific directory tree, combining common conventions, an ideal full-stack ML project structure looks roughly like this:
project/
├── data/
│ ├── raw/ # Raw data, read-only
│ ├── processed/ # Processed data
│ └── external/ # External data sources
├── notebooks/ # Exploratory analysis
├── src/
│ ├── data/ # Data processing scripts
│ ├── features/ # Feature engineering
│ ├── models/ # Training and prediction
│ └── api/ # Backend services
├── models/ # Saved model files
├── frontend/ # Frontend code
├── tests/ # Tests
├── configs/ # Configuration files
├── requirements.txt
├── Dockerfile
└── README.md
This structure draws from the widely adopted Cookiecutter Data Science template philosophy. Cookiecutter Data Science is an open-source project template generator released by the DrivenData team in 2015, inspired by the "convention over configuration" principle from software engineering. The template gained widespread recognition in the data science and ML community because it solves a core pain point: when every data scientist organizes projects according to their own habits, the friction cost of team collaboration becomes extremely high. By providing a standardized set of directory conventions, new team members can understand the project layout with zero learning cost. The template has now iterated to v2, adding native support for modern toolchains (such as DVC for data version control). Its core principle is: separation of concerns. Data, code, models, and services each have their own place, allowing anyone picking up the project to quickly locate what they need.
Three Key Principles for Repository Organization
First, raw data is immutable. The data/raw directory should be treated as read-only, and all processing should generate new artifacts saved in processed. This ensures experiment reproducibility. In data science, the "reproducibility crisis" is a widely discussed topic—a 2016 Nature survey showed that over 70% of researchers had tried and failed to reproduce others' experiments. In ML projects, keeping raw data immutable is the foundational guarantee that anyone can rebuild the complete data pipeline from scratch at any point in time.
Second, separate code from data. Extract reusable logic from Notebooks into src modules, with Notebooks used only for exploration and presentation. This is the critical step from "experimental code" to "production code." Jupyter Notebooks are powerful for exploratory analysis, but their non-linear execution, difficulty in code review, and inconvenience for unit testing make them unsuitable as production code carriers. The industry consensus is to treat Notebooks as "scratch paper"—used for quickly validating hypotheses, then refactoring validated logic into modular Python packages.
Third, externalize configuration. Extract hyperparameters, paths, environment variables, etc., into configuration files to avoid hardcoding and facilitate switching between different environments. Common configuration management approaches include YAML/TOML files, environment variables (with python-dotenv), and more specialized configuration frameworks like Hydra (open-sourced by Meta). Hydra is particularly well-suited for ML projects because it supports configuration composition and overriding, making it extremely convenient to switch between different experiment settings via command-line arguments.
From ML Beginner to Engineer: Advanced Considerations
The fact that this developer proactively sought structural feedback is itself a sign of maturity. Many people stop after completing their first working project, yet engineering capability is precisely what separates "someone who can tune models" from "someone who can deliver products."
Engineering Elements Worth Adding
Beyond directory structure, a truly robust full-stack ML project should also consider the following:
-
Dependency management: Use
requirements.txtor more modern tools likepoetryoruvto lock environment versions. Python dependency management has evolved through several stages: the earliestpip freeze > requirements.txtapproach was simple and direct but lacked dependency resolution capabilities; Poetry, which emerged in 2018, introducedpyproject.tomlto unify project metadata and dependency declarations, ensuring precise environment reproduction through lock files (poetry.lock); and uv, which burst onto the scene in 2024 (developed by the Astral team, also the creators of Ruff), rewrote the package manager core in Rust, achieving installation speeds 10-100x faster than pip, and is rapidly becoming the darling of the Python community. For ML projects, precise version locking is especially important since dependency chains typically involve numerous scientific computing libraries (NumPy, PyTorch, etc.). -
Containerized deployment: Use a Dockerfile to ensure deployment environment consistency, avoiding the "it works on my machine" predicament. Containerization is far more important in ML than in traditional web development because ML model execution often depends on specific versions of CUDA drivers, cuDNN libraries, and various system-level dependencies. Docker fundamentally eliminates environment inconsistency by packaging the entire runtime environment into an image. Going further, in production-level deployments, teams typically combine Kubernetes for automatic model service scaling, or use NVIDIA's GPU container runtime for model inference acceleration. This consistency guarantee from development to deployment is one of the core pillars of MLOps (Machine Learning Operations). MLOps borrows from DevOps thinking, emphasizing automation, monitoring, and continuous delivery throughout the ML system lifecycle.
-
Experiment tracking: Introduce MLflow or Weights & Biases to record parameters and results for each experiment. Experiment tracking is one of the most essential practices in ML engineering. MLflow is a platform open-sourced by Databricks in 2018, offering experiment logging, model registry, and model deployment capabilities. Its advantage lies in being fully open-source and self-hostable, making it suitable for enterprises sensitive to data security. Weights & Biases (W&B) is a SaaS platform known for its excellent visualization capabilities and collaboration features, widely popular in academia and startups. Both share the same core value: making every model training run a traceable record, including what data was used, what hyperparameters were set, and what metrics were produced. An ML project without experiment tracking is like a software project without version control—as iterations increase, you'll be completely unable to answer basic questions like "what configuration did that better-performing model from last week use?"
-
Documentation and README: Clearly explain the project's purpose, installation steps, and usage instructions—this is often the first impression when evaluating an open-source repository;
-
Test coverage: Even in ML projects, data processing and API logic need unit test coverage. Many people mistakenly believe ML projects are "hard to test" because model outputs have randomness. In reality, a large number of components in ML projects are fully testable: data preprocessing functions should guarantee input-output consistency, feature engineering pipelines should validate data types and dimensions, and API endpoints should test request-response formats. For models themselves, you can use "smoke tests" to verify that a model can complete one full train-inference cycle on a small dataset, or "regression tests" to ensure new code doesn't cause unexpected drops in model accuracy.
Model Weights and Large File Management
In full-stack ML projects, a common question is: should model weight files (typically ranging from tens of MB to several GB) be included in Git version control? The answer is almost always no. Git was designed to manage incremental changes in text files and handles binary large files extremely inefficiently—each modification saves a complete copy in the .git directory, causing repository size to balloon rapidly.
Mainstream industry solutions include: Git LFS (Large File Storage), which stores the actual content of large files on a remote server while keeping only pointer files in the Git repository, thus keeping the repository lightweight. DVC (Data Version Control) goes further, designed specifically for data science scenarios—it not only manages large file versions but can also define data pipeline dependencies, enabling end-to-end reproducibility. DVC can store data and models in cloud storage like S3 or GCS while using Git-like semantics for version management. For large models (such as LLM weight files), Hugging Face Hub also provides dedicated model hosting and version management services. Which solution to choose depends on team size and project needs, but the core principle remains consistent: use Git for code, use specialized tools for large files.
The Value of Open Source Community Feedback
Making a project public and soliciting feedback is an efficient way to grow. The value of open source communities lies not only in code sharing but also in the collision of experiences. Through others' code reviews, developers can quickly discover blind spots in their own understanding—such as whether model weights should be included in version control, or how to design clearer module boundaries.
In Reddit communities like r/MachineLearning and r/learnmachinelearning, project structure discussion posts often receive extremely high-quality replies. Senior engineers draw from actual production experience to point out issues that beginners easily overlook: whether a .gitignore was added to exclude data files and model weights, whether the README explains how to obtain training data, whether API keys or other sensitive information were accidentally committed, etc. This low-cost peer review mechanism is especially valuable for independent developers who lack enterprise mentors.
Conclusion
From this seemingly simple sharing, we can distill a truth applicable to all ML learners: Model capability is certainly important, but engineering literacy determines how far your project can go. A well-structured repository is not only easier to maintain yourself but is also the best calling card for demonstrating professionalism to potential employers or collaborators.
For developers building their first full-stack ML project, consider starting by emulating mature templates and gradually understanding the design philosophy behind each directory. As project complexity increases, these seemingly tedious conventions will gradually reveal their value. And the courage to make your work public and proactively seek feedback is itself the most valuable quality on the path to becoming an excellent engineer.
Related articles

Should You Open Source Your Project? A Layered Open Source Strategy Using Project Replay as a Case Study
Should indie developers open source their projects? Using the game custom achievement tool Project Replay as a case study, this article analyzes the open source decision and offers a practical layered strategy.

130+ Open-Source Interactive Security Awareness Training: Reshaping Habit Formation Through 3D Office Scenarios
A project with 130+ free open-source interactive security awareness exercises using immersive 3D office scenarios to simulate phishing, vishing, MFA fatigue attacks and more, building employee security habits.

From Musk to Jefferson: Beware the Cognitive Trap of Cross-Domain Experts
Why do geniuses in one field often become overconfident in others? From Musk's controversial interview to Jefferson's blind spots, an exploration of cross-domain cognitive arrogance.