By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.
(Scrum Guide 2020 Edition)
You’re a Scrum Team Developer. Your Product Owner just dropped a "small" feature request—"Just tweak the login flow to support SSO, but keep the old one working for legacy users." You glance at the codebase and see:
AuthService
login_v1()
login_v2()
login_v3()
TODO
This is technical debt—the invisible tax on your future work. Ignore it, and: - Your velocity slows to a crawl (every "small" change takes 3x longer). - Bugs multiply (because untested, duplicated code hides landmines). - Morale plummets (no one wants to touch the "legacy" parts).
Refactoring is how you pay down that debt. Continuous Integration (CI) is your early-warning system—it tells you before debt becomes a crisis.
Why this matters in production: - Without CI: You merge broken code, and QA (or worse, customers) find it. Downtime = lost revenue. - Without refactoring: Your codebase becomes a Jenga tower—one wrong pull request collapses it. - Without tracking debt: Your Sprint Planning turns into a guessing game. "Why did this take 3 days instead of 1?"
Real-world scenario: You’re on a team maintaining a 5-year-old e-commerce platform. The checkout flow is a 2,000-line PHP file with 15 global variables. The CTO wants to add Apple Pay. Do you: A) Hack in Apple Pay and pray? B) Refactor the checkout flow first, then add Apple Pay?
This guide gives you the tools to choose B—and justify it to your Product Owner.
calculateTax()
TaxCalculator
processOrder()
validateOrder()
calculateTotals()
UserService
UserAuthService
UserProfileService
if (status == 3) { ... }
const STATUS_ACTIVE = 3
discountRate
chargeCustomer()
int x
int userAge
if-else
if (userType == "ADMIN")
AdminUser
calculateTotal(price, tax, discount, shipping)
calculateTotal(Order order)
sendEmail()
User
EmailService
pipeline { agent any; stages { stage('Test') { sh 'npm test' } } }
.github/workflows/test.yml
.gitlab-ci.yml
main
trunk
if (featureFlags.isEnabled("newCheckout")) { ... }
Set up a CI pipeline that:1. Runs tests on every push.2. Blocks merges if tests fail.3. Reports code coverage.
Create .github/workflows/test.yml:
name: CI Pipeline on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Node.js uses: actions/setup-node@v4 with: node-version: 20 - name: Install dependencies run: npm install - name: Run tests run: npm test - name: Run linter run: npm run lint - name: Check code coverage run: npm run coverage env: CI: true
package.json
{ "scripts": { "test": "jest --coverage", "lint": "eslint .", "coverage": "jest --coverage --watchAll=false" } }
Create tests/auth.test.js:
tests/auth.test.js
test("login should return 401 for invalid credentials", () => { const response = login("wrong", "password"); expect(response.status).toBe(401); // This will fail if `login()` doesn't handle errors });
git add . git commit -m "Add failing test to simulate debt" git push
Update src/auth.js:
src/auth.js
function login(username, password) { if (!username || !password) { return { status: 400, error: "Missing credentials" }; // Fix: Handle missing inputs } if (username !== "admin" || password !== "secret") { return { status: 401, error: "Invalid credentials" }; // Fix: Return 401 for invalid creds } return { status: 200, token: "abc123" }; }
git add . git commit -m "Fix auth logic to handle invalid credentials" git push
Update jest.config.js:
jest.config.js
module.exports = { collectCoverage: true, coverageThreshold: { global: { branches: 80, functions: 80, lines: 80, statements: 80 } } };
yaml env: DB_PASSWORD: ${{ secrets.DB_PASSWORD }} #-Good DB_PASSWORD: "hunter2" #-Bad
npm audit
snyk test
admin
bash docker build -t myapp:${{ github.sha }} .
javascript if (featureFlags.isEnabled("newAuthFlow")) { return newAuthService.login(username, password); } else { return legacyAuthService.login(username, password); }
jest --detectOpenHandles
Trap: "The Developers should fix debt in their spare time."
"What’s the purpose of refactoring?"
Trap: "To add new features faster."
"When should a team refactor?"
Trap: "Only during a dedicated 'refactor sprint'."
"What’s the main benefit of CI?"
"Your team is falling behind on Sprint Goals because of technical debt. What should you do?" --Correct: 1. Add debt items to the Product Backlog. 2. Work with the Product Owner to prioritize them. 3. Refactor in small increments during Sprints. --Trap: "Stop working on features and spend a Sprint just refactoring."
You have a legacy Python function that calculates discounts. It’s 50 lines long, has no tests, and uses magic numbers. Refactor it to:1. Be under 10 lines.2. Have 100% test coverage.3. Use constants instead of magic numbers.
Original Code (discount.py):
discount.py
def calculate_discount(price, customer_type): if customer_type == 1: return price * 0.9 elif customer_type == 2: return price * 0.85 elif customer_type == 3: if price > 1000: return price * 0.8 else: return price * 0.9 else: return price
# discount.py STANDARD_DISCOUNT = 0.9 PREMIUM_DISCOUNT = 0.85 VIP_DISCOUNT_HIGH = 0.8 VIP_DISCOUNT_LOW = 0.9 VIP_THRESHOLD = 1000 def calculate_discount(price, customer_type): if customer_type == 1: return price * STANDARD_DISCOUNT elif customer_type == 2: return price * PREMIUM_DISCOUNT elif customer_type == 3: return price * (VIP_DISCOUNT_HIGH if price > VIP_THRESHOLD else VIP_DISCOUNT_LOW) return price
Tests (test_discount.py):
test_discount.py
import pytest from discount import calculate_discount, STANDARD_DISCOUNT, PREMIUM_DISCOUNT, VIP_DISCOUNT_HIGH, VIP_DISCOUNT_LOW def test_standard_customer(): assert calculate_discount(100, 1) == 100 * STANDARD_DISCOUNT def test_premium_customer(): assert calculate_discount(100, 2) == 100 * PREMIUM_DISCOUNT def test_vip_customer_high_price(): assert calculate_discount(1500, 3) == 1500 * VIP_DISCOUNT_HIGH def test_vip_customer_low_price(): assert calculate_discount(500, 3) == 500 * VIP_DISCOUNT_LOW def test_no_discount(): assert calculate_discount(100, 4) == 100
Why this works: - Small steps: Extracted magic numbers first, then simplified logic. - Tests first: Wrote tests before refactoring to ensure behavior didn’t change. - Readable: Constants make the code self-documenting.
x
userAge
yaml on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm test
Join 4M+ learners. Unlock unlimited quizzes, wrong-answer tracking, flashcards + reminders, study guides, and 1-on-1 challenges.