Skip to the content.

CI/CD Strategy for Adaptive Tests

Table of Contents

Philosophy: Leverage Resilience, Not Dependencies

Unlike traditional test selection strategies that rely on dependency graphs, adaptive tests have a unique superpower: they don’t break when you refactor code. This fundamentally changes how we approach CI/CD.

The Two-Track Approach

🎯 Track 1: Traditional Tests (Fast Feedback)

🛡️ Track 2: Adaptive Tests (Comprehensive Safety Net)

Why This Works

Traditional test selection asks: “Which tests depend on these changed files?”

But adaptive tests don’t have fixed dependencies! They ask: “Where is the Calculator class?” and find it wherever it moves.

// This test doesn't depend on a specific file:
const Calculator = await discover({
  name: 'Calculator',
  type: 'class'
});

// It will find Calculator whether it's in:
// - src/Calculator.js
// - lib/math/Calculator.js
// - features/calc/implementation/Calculator.js
// Anywhere!

CI Workflow Jobs

Our CI runs four parallel jobs:

  1. traditional-tests: Quick feedback on changed files only
  2. adaptive-tests: Full adaptive suite (resilient to refactoring)
  3. typescript-tests: TypeScript-specific adaptive tests
  4. resilience-check: Verifies tests survive refactoring AND catch bugs

The Key Insight

You don’t need intelligent test selection for adaptive tests because:

Real-World Example

# Developer moves Calculator.js to a new location
- Traditional tests: ❌ "Cannot find module '../src/Calculator'"
- Adaptive tests: ✅ "Found Calculator in new location, all tests pass"

# Developer introduces a bug in Calculator.add()
- Traditional tests: ❌ "Expected 5 but got 3"
- Adaptive tests: ❌ "Expected 5 but got 3"

Both catch real bugs, but only adaptive tests survive refactoring!

Performance Considerations

Running all adaptive tests is practical because:

Summary

The beauty of adaptive tests in CI/CD is their simplicity:

This is simpler, faster, and more reliable than traditional test selection strategies!