Hello! Welcome to the fourth lesson in our foundational module.
In the last three lessons, we explored the "what" of your three potential project directions: the nature of flaky tests, the concept of technical debt, and the mechanics of ML-based test generation. Now, we'll connect these ideas to the "how" by framing them in the language of machine learning.
This step is crucial. Translating a real-world challenge into a well-defined machine learning problem is the first and most important part of building any data-driven tool.
By the end of this lesson, you will be able to:
- Formulate flake detection as a classification problem, debt prioritization as a ranking problem, and test creation as a generation problem.
This will provide a clear, technical lens through which you can compare the three project paths in our next lesson.
A Quick Tour of ML Problem Types
At a high level, most supervised machine learning tasks fall into one of three families:
- Classification: Assigning a predefined category or label to an input. Is this email
spamornot spam? Is this testflakyornot flaky? - Regression/Ranking: Predicting a continuous numerical value or an ordered list. What will the price of this stock be tomorrow? Which technical debt items are most important to fix?
- Generation: Creating new, structured content that mimics data it was trained on. Generate an image of an astronaut on a horse. Generate a unit test for this Python function.
Let's now map each of your project ideas to one of these paradigms.
1. Flaky Test Detection as a Classification Problem
Imagine you have a test that sometimes passes and sometimes fails in your CI/CD pipeline. The core question you want to answer is simple: "Is this test flaky?" This is a classic binary classification problem.
Your goal is to build a model that takes a test case as input and outputs one of two labels: flaky or not_flaky.

To make a prediction, the model needs data, or features, about the test case. These could include:
- The source code of the test itself.
- Historical execution data (e.g., pass/fail history, run duration).
- Code metrics of the application code being tested.
- Information from the version control system (e.g., how often the test file is changed).
The model learns patterns from this data to distinguish between flaky and reliable tests.
To see how this is done in practice, let's look at a research paper that built a framework called FlaKat for exactly this purpose.
A Machine Learning-Based Categorization Framework for Flaky Tests
This document, 'A Machine Learning-Based Categorization Framework for Flaky Tests', provides a detailed look at formulating flakiness as an ML problem. We'll focus on how they structure the task.
First, read the 'Abstract' on page ii to understand the project's goal. Then, read Section 3.1, 'FlaKat: Workflow' (pages 22-24). Focus on the four phases, especially how they transform raw test code into something a machine learning classifier can use (Phase II and III) before making a prediction (Phase IV).
As the paper describes, the process involves taking unstructured data (raw Java code) and systematically converting it into a structured format (vector embeddings) that classifiers like Random Forest or SVMs can process. This pipeline of parsing -> vectorizing -> classifying is fundamental to applying ML to code.
How Do You Measure Success?
For classification problems, you might think of using accuracy. However, flaky tests are often rare. If only 1% of your tests are flaky, a model that always predicts not_flaky would be 99% accurate but completely useless! This is known as the class imbalance problem.
Therefore, we use more informative metrics. The following video gives a great overview of the most important ones.
How to evaluate ML models | Evaluation metrics for machine learning
The video 'How to evaluate ML models' from AssemblyAI provides a clear, concise explanation of the key metrics used in classification.
Watch the segment on classification metrics (00:40 - 04:12). Pay close attention to the definitions of Precision, Recall, and F1-Score. Try to think about what each one would mean in the context of flake detection.
To put it in context:
- Precision: Of all the tests your model labeled as flaky, how many actually were? High precision means you don't waste developers' time investigating non-flaky tests (low false positives).
- Recall: Of all the tests that truly are flaky, how many did your model find? High recall means your model is good at catching flaky tests and they don't slip through into the codebase (low false negatives).
- F1-Score: The harmonic mean of precision and recall. It provides a single number that balances the two concerns, making it a very common metric for imbalanced problems like flake detection.
2. Technical Debt Prioritization as a Ranking Problem
Now, let's consider technical debt. Static analysis tools can easily find thousands of "debt" items in a large codebase. The problem isn't just finding them; it's deciding which ones to fix first.
A simple "yes/no" classification isn't enough. You need to produce an ordered list, from most critical to least critical. This is a ranking problem.
The goal is to build a model that takes a list of technical debt items and sorts them based on their "urgency" or "impact."

So, what determines this urgency? A simple metric like code complexity isn't enough.
Prioritizing Technical Debt as If Time & Money Matters • Adam Tornhill • GOTO 2022
In this GOTO talk, 'Prioritizing Technical Debt as If Time & Money Matters', Adam Tornhill makes a powerful case for a data-driven approach to prioritization.
Watch the section on 'hotspot analysis' (08:10 - 10:26), where he combines complexity with change frequency. Then, watch his summary (25:48 - 26:58), where he outlines a three-factor prioritization strategy. These factors are precisely the kinds of features a ranking model would use.
Tornhill's key insight is that the most critical technical debt lies at the intersection of:
- Code Complexity: The code is hard to understand.
- Development Hotspot: Developers are frequently changing this code.
- Knowledge Loss: The developers who know the code best are leaving.
An ML model can learn to weigh these factors (and many others) to produce a priority score for each debt item, which can then be used to rank them.
It's Not Just Ranking
While ranking is the most nuanced approach, the problem can also be simplified into a classification task. A PhD thesis on this exact topic explored several ways to frame it.
Technical Debt Prioritization: Methods, Techniques, and a Large ...
This thesis, 'Technical Debt Prioritization: Methods, Techniques, and a Large ...', is a deep dive into applying ML to this problem. We'll focus on how the authors formulated the problem for their models.
First, read Section 3.2.4, 'What results do technical debt prioritization methods provide?' (page 33). This shows the different kinds of outputs a model can produce. Then, review Table 6.2 and the accompanying text in Section 6.1.2 'Data Preparation' (page 68) to see how developer survey answers were converted into labels for different ML tasks.
As the thesis shows, you can frame the problem in three ways:
- Ranking (Ordered List): The most detailed output, producing a specific payment order (Item 42 -> Item 36 -> ...). This is often tackled with "learning-to-rank" models.
- Multi-class Classification (Categories): A simpler approach where you assign each debt item to a priority bucket, such as
High,Medium, orLow. - Binary Classification (Boolean): The simplest approach, where the model makes a
PayorDon't Paydecision for each item.
Your project could implement any of these, from a simple binary classifier to a more sophisticated ranking model.
3. Test Case Generation as a Generation Problem
Finally, we have test case generation. Unlike the previous two problems, the goal here isn't to predict a label or a score. It's to create a new artifact: a piece of executable source code. This is a generation problem.
As we covered in the last lesson, this task is often handled by Large Language Models (LLMs), a specific type of generative model.
The formulation is straightforward:
- Input: Context about the code you want to test. This could be the function's source code, its signature and docstring, or a natural language description of its behavior (e.g., from a user story).
- Output: A complete, syntactically correct test function that verifies the behavior of the input code.
For example:
Input:
def calculate_shipping_cost(weight, distance, is_express):
"""Calculates shipping cost.
Args:
weight: Weight of the package in kg.
distance: Distance in km.
is_express: Boolean for express shipping.
Returns:
The total cost.
"""
# ... implementation ...
Output (generated by the model):
import pytest
def test_calculate_shipping_cost_basic():
assert calculate_shipping_cost(5, 100, False) == 15.0 # example value
def test_calculate_shipping_cost_express():
assert calculate_shipping_cost(5, 100, True) == 25.0 # example value
def test_with_zero_weight():
with pytest.raises(ValueError):
calculate_shipping_cost(0, 100, False)
How Do You Measure Success?
Evaluating generative models is more complex. We can't use simple metrics like F1-score. Instead, we have to assess the quality of the generated output:
- Execution: Does the generated test code run without syntax errors?
- Validity: Does the test pass when the application code is correct and fail when it's incorrect? (This can be tested with mutation testing).
- Coverage: How many lines of the application code does the generated test execute?
We will dive much deeper into prompt engineering and evaluating LLMs for code in Module 6. For now, the key is to recognize this as a fundamentally different type of ML problem.
Conclusion
You've now seen how each of your three project ideas maps onto a standard machine learning paradigm. This formalization is the bridge from a high-level goal to a concrete technical plan.
Key Takeaways:
| Project Idea | ML Problem Type | Example Input | Example Output |
|---|---|---|---|
| Flake Detection | Classification | Test execution history, source code features | A label: flaky or not_flaky |
| Debt Prioritization | Ranking / Classification | Code complexity, change frequency, file metrics | An ordered list of items, or a priority label |
| Test Generation | Generation | A function's source code and docstring | A new, complete pytest function |
Preview of the Next Lesson:
With this framework in mind, you're ready for the final lesson of this module. We will directly compare the three project directions across several practical criteria: data requirements, implementation complexity, and potential impact on a software team. This will equip you to make a final, informed decision on which project you want to build.