Skip to content

Latest commit

 

History

History
684 lines (547 loc) · 18.2 KB

File metadata and controls

684 lines (547 loc) · 18.2 KB
layout title parent nav_order
default
Chapter 3: Refactoring & Optimization
Continue Tutorial
3

Chapter 3: Refactoring & Optimization

Welcome to the art of code improvement! While Continue excels at generating new code, its true power shines when refactoring and optimizing existing code. This chapter explores how Continue can help you modernize legacy code, improve performance, and maintain code quality.

What Problem Does This Solve?

Most teams struggle here because the hard part is not writing more code, but deciding clear boundaries for order, items, user so behavior stays predictable as complexity grows.

In practical terms, this chapter helps you avoid three common failures:

  • coupling core logic too tightly to one implementation path
  • missing the handoff boundaries between setup, execution, and validation
  • shipping changes without clear rollback or observability strategy

After working through this chapter, you should be able to reason about Chapter 3: Refactoring & Optimization as an operating subsystem inside Continue Tutorial: Open-Source AI Coding Agents for IDE and CLI, with explicit contracts for inputs, state transitions, and outputs.

Use the implementation notes around total, Continue, item as your checklist when adapting these patterns to your own repository.

Intelligent Code Analysis

Identifying Refactoring Opportunities

Continue can analyze your code and suggest improvements:

// Original problematic code
function processUsers(users) {
  var result = [];
  for (var i = 0; i < users.length; i++) {
    if (users[i].active) {
      result.push({
        name: users[i].name,
        email: users[i].email
      });
    }
  }
  return result;
}

// Continue suggests: "Refactor to use modern JavaScript features"
// Suggested improvement:
function processUsers(users) {
  return users
    .filter(user => user.active)
    .map(user => ({
      name: user.name,
      email: user.email
    }));
}

Performance Optimization Suggestions

# Continue identifies: "Inefficient list concatenation in loop"
# Original:
result = []
for item in items:
    result = result + [item * 2]  # Inefficient!

# Suggested optimization:
result = [item * 2 for item in items]

Refactoring Patterns

Function Decomposition

// Original: Large, complex function
function processOrder(order) {
  // Validate order
  if (!order.items || order.items.length === 0) {
    throw new Error('Order must have items');
  }

  // Calculate total
  let total = 0;
  for (let item of order.items) {
    total += item.price * item.quantity;
  }

  // Apply discount
  if (order.discountCode) {
    total = total * 0.9; // 10% discount
  }

  // Save to database
  database.save(order);

  return { orderId: order.id, total };
}

// Continue suggests decomposition:
function validateOrder(order) {
  if (!order.items || order.items.length === 0) {
    throw new Error('Order must have items');
  }
}

function calculateTotal(items) {
  return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}

function applyDiscount(total, discountCode) {
  return discountCode ? total * 0.9 : total;
}

function processOrder(order) {
  validateOrder(order);
  let total = calculateTotal(order.items);
  total = applyDiscount(total, order.discountCode);
  database.save(order);
  return { orderId: order.id, total };
}

Async/Await Modernization

// Original: Promise chains
function fetchUserData(userId) {
  return api.getUser(userId)
    .then(user => {
      return api.getUserPosts(user.id)
        .then(posts => {
          return api.getUserComments(user.id)
            .then(comments => {
              return { user, posts, comments };
            });
        });
    })
    .catch(error => {
      console.error('Error fetching user data:', error);
      throw error;
    });
}

// Continue suggests: "Modernize to async/await"
async function fetchUserData(userId) {
  try {
    const user = await api.getUser(userId);
    const [posts, comments] = await Promise.all([
      api.getUserPosts(user.id),
      api.getUserComments(user.id)
    ]);
    return { user, posts, comments };
  } catch (error) {
    console.error('Error fetching user data:', error);
    throw error;
  }
}

Error Handling Improvements

# Continue suggests: "Add comprehensive error handling"
# Original:
def divide_numbers(a, b):
    return a / b

# Enhanced version:
def divide_numbers(a, b):
    try:
        if b == 0:
            raise ValueError("Cannot divide by zero")
        if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):
            raise TypeError("Both arguments must be numbers")
        return a / b
    except ZeroDivisionError:
        raise ValueError("Cannot divide by zero")
    except TypeError as e:
        raise TypeError(f"Invalid input types: {e}")
    except Exception as e:
        raise RuntimeError(f"Unexpected error during division: {e}")

Performance Optimization

Algorithm Improvements

# Continue identifies: "O(n²) complexity can be optimized"
# Original inefficient code:
def has_duplicates(items):
    for i in range(len(items)):
        for j in range(i + 1, len(items)):
            if items[i] == items[j]:
                return True
    return False

# Suggested optimization:
def has_duplicates(items):
    return len(items) != len(set(items))

Memory Optimization

// Continue suggests: "Optimize memory usage with generators"
function processLargeFile(filePath) {
  const data = fs.readFileSync(filePath, 'utf8'); // Loads entire file into memory
  const lines = data.split('\n');
  return lines.map(line => processLine(line));
}

// Optimized version:
function* processLargeFile(filePath) {
  const fileStream = fs.createReadStream(filePath, 'utf8');

  let buffer = '';
  for await (const chunk of fileStream) {
    buffer += chunk;
    const lines = buffer.split('\n');
    buffer = lines.pop(); // Keep incomplete line

    for (const line of lines) {
      yield processLine(line);
    }
  }

  // Process remaining buffer
  if (buffer) {
    yield processLine(buffer);
  }
}

Database Query Optimization

# Continue suggests: "Optimize database queries with select_related"
# Original with N+1 queries:
def get_posts_with_authors():
    posts = Post.objects.all()
    result = []
    for post in posts:
        result.append({
            'title': post.title,
            'author_name': post.author.name,  # Additional query per post!
            'content': post.content
        })
    return result

# Optimized version:
def get_posts_with_authors():
    posts = Post.objects.select_related('author').all()
    return [{
        'title': post.title,
        'author_name': post.author.name,  # No additional query!
        'content': post.content
    } for post in posts]

Code Quality Improvements

Naming Convention Fixes

// Continue suggests: "Improve variable naming for clarity"
const x = 10;  // Unclear
const y = 20;  // Unclear

// Better names:
const width = 10;
const height = 20;

Magic Number Elimination

# Continue identifies: "Replace magic numbers with constants"
def calculate_price(quantity, item_type):
    if item_type == 1:  # What does 1 mean?
        return quantity * 10.99
    elif item_type == 2:  # What does 2 mean?
        return quantity * 15.50

# Improved version:
STANDARD_ITEM_PRICE = 10.99
PREMIUM_ITEM_PRICE = 15.50

ITEM_TYPE_STANDARD = 1
ITEM_TYPE_PREMIUM = 2

def calculate_price(quantity, item_type):
    if item_type == ITEM_TYPE_STANDARD:
        return quantity * STANDARD_ITEM_PRICE
    elif item_type == ITEM_TYPE_PREMIUM:
        return quantity * PREMIUM_ITEM_PRICE

Documentation Enhancement

# Continue suggests: "Add comprehensive docstring"
def process_payment(amount, card_number, expiry_date):
    # Missing documentation!
    pass

# Enhanced version:
def process_payment(amount, card_number, expiry_date):
    """
    Process a credit card payment.

    Args:
        amount (float): Payment amount in dollars
        card_number (str): Credit card number (16 digits)
        expiry_date (str): Expiry date in MM/YY format

    Returns:
        dict: Payment result with transaction ID and status

    Raises:
        ValueError: If payment details are invalid
        PaymentError: If payment processing fails

    Example:
        >>> result = process_payment(99.99, "4111111111111111", "12/25")
        >>> result['status']
        'approved'
    """
    pass

Architecture Refactoring

Separation of Concerns

// Continue suggests: "Separate business logic from presentation"
class UserManager {
  constructor() {
    this.users = [];
    this.ui = new UserInterface();
  }

  addUser(userData) {
    // Business logic mixed with UI
    const user = this.validateUser(userData);
    this.users.push(user);
    this.ui.showSuccessMessage('User added successfully!');
    this.ui.refreshUserList(this.users);
  }
}

// Refactored version:
class UserService {
  constructor(userRepository) {
    this.userRepository = userRepository;
  }

  addUser(userData) {
    const user = this.validateUser(userData);
    return this.userRepository.save(user);
  }

  validateUser(userData) {
    // Business logic only
    if (!userData.name || !userData.email) {
      throw new ValidationError('Name and email are required');
    }
    return { ...userData, id: generateId() };
  }
}

class UserController {
  constructor(userService, userView) {
    this.userService = userService;
    this.userView = userView;
  }

  async addUser(userData) {
    try {
      const user = await this.userService.addUser(userData);
      this.userView.showSuccessMessage('User added successfully!');
      this.userView.refreshUserList();
    } catch (error) {
      this.userView.showErrorMessage(error.message);
    }
  }
}

Dependency Injection

# Continue suggests: "Implement dependency injection for testability"
class EmailService:
    def __init__(self):
        self.smtp_server = "smtp.gmail.com"  # Hardcoded dependency

    def send_email(self, to, subject, body):
        # Implementation...

# Refactored version:
class EmailService:
    def __init__(self, smtp_client=None):
        self.smtp_client = smtp_client or SMTPLibClient("smtp.gmail.com")

    def send_email(self, to, subject, body):
        return self.smtp_client.send(to, subject, body)

# Usage:
email_service = EmailService(SMTPLibClient("smtp.company.com"))
# Now easily testable with mock SMTP client

Testing Improvements

Test Coverage Enhancement

# Continue suggests: "Add missing test cases"
def is_valid_email(email):
    return '@' in email and '.' in email

# Suggested comprehensive tests:
def test_is_valid_email():
    # Valid emails
    assert is_valid_email("user@example.com")
    assert is_valid_email("test.email+tag@domain.co.uk")

    # Invalid emails
    assert not is_valid_email("invalid-email")
    assert not is_valid_email("@domain.com")
    assert not is_valid_email("user@")
    assert not is_valid_email("")
    assert not is_valid_email(None)

Mock Integration

// Continue suggests: "Add mocks for external dependencies"
const axios = require('axios');

class APIService {
  async fetchData(endpoint) {
    const response = await axios.get(endpoint);
    return response.data;
  }
}

// Suggested test with mocks:
const MockAdapter = require('axios-mock-adapter');

describe('APIService', () => {
  let mock;
  let apiService;

  beforeEach(() => {
    mock = new MockAdapter(axios);
    apiService = new APIService();
  });

  afterEach(() => {
    mock.restore();
  });

  it('should fetch data successfully', async () => {
    const mockData = { id: 1, name: 'Test' };
    mock.onGet('/api/data').reply(200, mockData);

    const result = await apiService.fetchData('/api/data');
    expect(result).toEqual(mockData);
  });
});

Automated Refactoring Commands

Using Continue's Refactoring Features

# In Continue chat, use refactoring commands:
/refactor - Suggest general refactoring improvements
/improve - Optimize performance
/modernize - Update to modern language features
/simplify - Reduce code complexity
/extract - Extract methods or variables
/rename - Suggest better naming
/document - Add documentation

Batch Refactoring

// Continue can refactor entire files or directories
// Select multiple files and use:
/refactor file - Apply refactoring to specific file
/refactor project - Analyze entire project for improvements
/refactor pattern - Refactor code following specific patterns

Best Practices for Refactoring

1. Incremental Changes

  • Make small, focused changes
  • Test after each refactoring
  • Commit frequently
  • Use feature flags for risky changes

2. Preserve Behavior

  • Ensure refactored code maintains same functionality
  • Keep comprehensive test suite
  • Use automated tests to verify changes

3. Code Review Integration

  • Generate before/after comparisons
  • Document refactoring rationale
  • Get team approval for architectural changes

4. Performance Monitoring

  • Benchmark before and after refactoring
  • Monitor for regressions
  • Set performance budgets

5. Team Consistency

  • Follow established coding standards
  • Maintain consistent patterns across codebase
  • Update team documentation

Common Refactoring Patterns

Extract Method

# Before: Long method with multiple responsibilities
def process_order(order):
    # Validate order
    if not order.items: raise ValueError("No items")

    # Calculate total
    total = sum(item.price * item.quantity for item in order.items)

    # Apply tax
    tax = total * 0.08
    total += tax

    # Save order
    db.save(order)

# After: Extracted methods
def validate_order(order):
    if not order.items:
        raise ValueError("No items")

def calculate_total(order):
    return sum(item.price * item.quantity for item in order.items)

def apply_tax(total, tax_rate=0.08):
    return total * (1 + tax_rate)

def process_order(order):
    validate_order(order)
    total = calculate_total(order)
    total = apply_tax(total)
    db.save(order)

Replace Conditional with Polymorphism

// Before: Complex conditional logic
class PaymentProcessor {
  process(payment) {
    if (payment.type === 'credit_card') {
      // Credit card processing logic
    } else if (payment.type === 'paypal') {
      // PayPal processing logic
    } else if (payment.type === 'bank_transfer') {
      // Bank transfer processing logic
    }
  }
}

// After: Polymorphic approach
class PaymentProcessor {
  process(payment) {
    const processor = this.getProcessor(payment.type);
    return processor.process(payment);
  }

  getProcessor(type) {
    const processors = {
      credit_card: new CreditCardProcessor(),
      paypal: new PayPalProcessor(),
      bank_transfer: new BankTransferProcessor()
    };
    return processors[type];
  }
}

Measuring Refactoring Success

Code Quality Metrics

# Continue can suggest monitoring these metrics:
def calculate_code_metrics(code):
    return {
        'complexity': calculate_cyclomatic_complexity(code),
        'maintainability': calculate_maintainability_index(code),
        'duplication': calculate_code_duplication(code),
        'test_coverage': calculate_test_coverage(code)
    }

Performance Benchmarks

// Before/after performance comparison
const Benchmark = require('benchmark');

const suite = new Benchmark.Suite();

suite
  .add('Original Implementation', () => {
    // Original code
  })
  .add('Refactored Implementation', () => {
    // Refactored code
  })
  .on('complete', function() {
    console.log('Fastest is ' + this.filter('fastest').map('name'));
  })
  .run();

What's Next?

Excellent! You've learned how Continue can transform your code through intelligent refactoring and optimization. The ability to analyze existing code and suggest improvements is incredibly powerful for maintaining high-quality codebases.

In Chapter 4: Documentation & Comments, we'll explore how Continue can help you create comprehensive documentation and improve code readability through intelligent commenting.

Ready to document your code like a pro? Let's continue to Chapter 4: Documentation & Comments!


Generated by AI Codebase Knowledge Builder

How it Works Under the Hood

Under the hood, Chapter 3: Refactoring & Optimization usually follows a repeatable control path:

  1. Context bootstrap: initialize runtime config and prerequisites for order.
  2. Input normalization: shape incoming data so items receives stable contracts.
  3. Core execution: run the main logic branch and propagate intermediate state through user.
  4. Policy and safety checks: enforce limits, auth scopes, and failure boundaries.
  5. Output composition: return canonical result payloads for downstream consumers.
  6. Operational telemetry: emit logs/metrics needed for debugging and performance tuning.

When debugging, walk this sequence in order and confirm each stage has explicit success/failure conditions.

Source Walkthrough

Use the following upstream sources to verify implementation details while reading this chapter:

  • Continue Repository Why it matters: authoritative reference on Continue Repository (github.com).
  • Continue Docs Why it matters: authoritative reference on Continue Docs (docs.continue.dev).
  • Continue Releases Why it matters: authoritative reference on Continue Releases (github.com).

Suggested trace strategy:

  • search upstream code for order and items to map concrete implementation paths
  • compare docs claims against actual runtime/config code before reusing patterns in production

Chapter Connections