CI Cost Optimization Strategy¶
Problem Statement¶
GitHub Actions was running 4 parallel jobs with complex test matrices, mutation testing, and extensive integration tests. This resulted in: - 12-15 minutes of runner time per push - High monthly GitHub Actions costs - Unnecessary complexity for simple changes
Solution: Shift-Left Testing¶
Philosophy: Run comprehensive tests locally in pre-commit hooks. Only run critical security tests in CI.
What Runs Where¶
GitHub Actions (CI) - ~2-3 minutes¶
Only critical security tests:
- tests/unit/core/test_jwt_tokens.py - JWT token security
- tests/unit/core/test_security_hashing.py - Password hashing
- tests/unit/core/test_external_http.py - External API security
- tests/unit/core/test_security_events.py - Security event logging
- tests/unit/core/test_rate_limit.py - Rate limiting
Coverage requirement: 95% on core security modules
Pre-Commit Hook (Local) - ~2-4 minutes¶
Comprehensive test suite: 1. Fast Checks (<10s) - Ruff linting with auto-fix - Import sorting
- Core Security Tests (<30s)
-
Same tests as CI (ensures local/CI parity)
-
Full Unit Tests (<60s)
- All unit tests across all modules
-
Coverage reporting
-
Integration Tests (<90s)
- API integration tests
- External service mocks
-
E2E workflows
-
Mutation Testing (optional, <120s)
- Security-critical modules only
- Can be skipped with
SKIP_MUTATION=1
Cost Savings¶
Before¶
- Jobs: 4 parallel (Lint, Unit Tests, Integration Tests, Coverage Gate, Mutation)
- Runtime: 12-15 minutes
- Monthly cost (100 pushes): ~20-25 hours of runner time
After¶
- Jobs: 1 (Core Security Tests only)
- Runtime: 2-3 minutes
- Monthly cost (100 pushes): ~3-5 hours of runner time
Savings: ~80% reduction in GitHub Actions costs
Usage¶
For Developers¶
# Normal commit - runs comprehensive tests automatically
git commit -m "feat: add new feature"
# Emergency commit - skip tests (use sparingly!)
git commit --no-verify -m "hotfix: critical production issue"
# Run tests manually
./tools/hooks/pre-commit-comprehensive.sh
# Skip mutation testing for speed
SKIP_MUTATION=1 git commit -m "feat: quick iteration"
For CI/CD¶
The simplified CI workflow runs automatically on push to dev-* branches. It:
1. Runs only core security tests
2. Fails fast on security issues
3. Provides clear feedback in <3 minutes
Migration Guide¶
Old Workflow (Deprecated)¶
# .github/workflows/ci-backend.yml (483 lines)
- Lint job
- Test matrix (unit + integration)
- Coverage gate
- Mutation testing
New Workflow (Simplified)¶
Monitoring¶
Success Metrics¶
- ✅ CI runtime: <3 minutes (target: 2-3 min)
- ✅ Pre-commit runtime: <4 minutes (target: 2-4 min)
- ✅ Core security coverage: >95%
- ✅ Developer satisfaction: Fast feedback loop
Failure Handling¶
If CI fails: - Security test failure = BLOCK MERGE - Must be fixed immediately
If pre-commit fails:
- Developer can skip with --no-verify for emergencies
- Must run manually ASAP: ./tools/hooks/pre-commit-comprehensive.sh
Best Practices¶
- Always run pre-commit tests before pushing
- Catches issues early
- Prevents CI failures
-
Saves time for everyone
-
Don't skip tests unless emergency
- Use
--no-verifyonly for hotfixes -
Run tests manually immediately after
-
Keep tests fast
- Mark slow tests with
@pytest.mark.slow - Mark load tests with
@pytest.mark.load -
These are excluded from pre-commit
-
Monitor CI costs
- Review GitHub Actions usage monthly
- Optimize if runtime creeps up
Rollback Plan¶
If the simplified CI causes issues:
# Revert to old workflow
git revert <commit-hash>
# Or temporarily use old workflow
# Edit .github/workflows/auto-merge-to-development.yml
uses: ./.github/workflows/ci-backend.yml # Old workflow