Behavioral Difference Patterns
Overview
This document catalogs common patterns of behavioral differences found when comparing original and migrated repositories, along with diagnostic approaches and remediation strategies.
Logic Errors
Pattern: Off-by-One Error
Symptoms:
- Array index out of bounds
- Loop iterations differ by one
- Boundary conditions fail
Example:
# Original
for i in range(len(items)):
process(items[i])
# Migrated (incorrect)
for i in range(len(items) - 1): # Missing last item!
process(items[i])
Detection:
- Test with edge cases (empty, single item, full)
- Compare loop iteration counts
- Check boundary value tests
Remediation:
- Review loop conditions
- Add boundary tests
- Use inclusive/exclusive range correctly
Pattern: Operator Inversion
Symptoms:
- Opposite boolean results
- Inverted comparisons
- Wrong conditional branches
Example:
# Original
if x > threshold:
return "high"
# Migrated (incorrect)
if x < threshold: # Inverted!
return "high"
Detection:
- Compare boolean return values
- Test boundary conditions
- Check conditional logic
Remediation:
- Review all comparison operators
- Add tests for both branches
- Use truth tables to verify logic
Pattern: Missing Null Check
Symptoms:
- NullPointerException / AttributeError
- Crashes on edge cases
- Tests pass in original, fail in migrated
Example:
# Original
if user is not None:
return user.name
# Migrated (incorrect)
return user.name # Missing null check!
Detection:
- Test with null/None inputs
- Check error handling
- Review defensive programming
Remediation:
- Add null checks
- Use optional types
- Add tests for null cases
Missing Functionality
Pattern: Unimplemented Method
Symptoms:
- NotImplementedError
- AttributeError
- Method not found
Example:
# Original
class Calculator:
def add(self, a, b):
return a + b
def multiply(self, a, b):
return a * b
# Migrated (incomplete)
class Calculator:
def add(self, a, b):
return a + b
# multiply() missing!
Detection:
- Compare class interfaces
- Check method signatures
- Run comprehensive test suite
Remediation:
- List all missing methods
- Implement missing functionality
- Add interface tests
Pattern: Incomplete Migration
Symptoms:
- Some features work, others don't
- Partial functionality
- TODO comments in code
Example:
# Migrated
def process_data(data):
# TODO: Implement validation
return transform(data)
Detection:
- Search for TODO/FIXME comments
- Compare feature lists
- Run full test suite
Remediation:
- Complete all TODOs
- Implement missing features
- Remove placeholder code
API Contract Violations
Pattern: Changed Response Structure
Symptoms:
- JSON structure differs
- Missing fields
- Different data types
Example:
// Original
{
"user": {
"id": 123,
"name": "Alice"
}
}
// Migrated (incorrect)
{
"userId": 123,
"userName": "Alice"
}
Detection:
- Compare JSON schemas
- Validate against OpenAPI spec
- Test API clients
Remediation:
- Document API contract
- Match original structure
- Add schema validation tests
Pattern: Changed Status Codes
Symptoms:
- Different HTTP status codes
- Error handling breaks
- Client expectations violated
Example:
# Original
return Response(data, status=200)
# Migrated (incorrect)
return Response(data, status=201) # Changed from 200 to 201
Detection:
- Compare status codes
- Test error scenarios
- Check API documentation
Remediation:
- Match original status codes
- Document intentional changes
- Add status code tests
Performance Regressions
Pattern: Algorithmic Complexity Change
Symptoms:
- Significantly slower execution
- Timeout on large inputs
- Memory exhaustion
Example:
# Original: O(n)
def find_item(items, target):
return target in items # Uses hash lookup
# Migrated: O(n²) - incorrect!
def find_item(items, target):
for item in items:
if item == target:
return True
return False
Detection:
- Benchmark with varying input sizes
- Profile execution time
- Analyze algorithm complexity
Remediation:
- Identify bottleneck
- Restore efficient algorithm
- Add performance tests
Pattern: Missing Optimization
Symptoms:
- Slower than original
- Repeated computations
- No caching
Example:
# Original (optimized)
@lru_cache(maxsize=128)
def expensive_computation(x):
return complex_calculation(x)
# Migrated (missing cache)
def expensive_computation(x):
return complex_calculation(x)
Detection:
- Compare execution times
- Profile function calls
- Check for repeated work
Remediation:
- Add caching
- Optimize hot paths
- Benchmark improvements
State Management Issues
Pattern: Shared Mutable State
Symptoms:
- Tests fail when run together
- Order-dependent failures
- Intermittent bugs
Example:
# Migrated (incorrect - shared state)
cache = {} # Global mutable state
def process(key, value):
cache[key] = value # Modifies shared state
return cache
Detection:
- Run tests in random order
- Check for global variables
- Test isolation
Remediation:
- Remove global state
- Use dependency injection
- Add proper setup/teardown
Pattern: Missing State Reset
Symptoms:
- Second run behaves differently
- Accumulated state
- Memory leaks
Example:
# Migrated (incorrect)
class Processor:
def __init__(self):
self.results = []
def process(self, data):
self.results.append(data) # Never cleared!
return self.results
Detection:
- Run operations multiple times
- Check memory usage
- Test state isolation
Remediation:
- Reset state between operations
- Use immutable data structures
- Add cleanup methods
Data Type Mismatches
Pattern: Type Coercion Difference
Symptoms:
- Unexpected type conversions
- Precision loss
- String/number confusion
Example:
# Original
def calculate(x, y):
return x / y # Returns float
# Migrated (incorrect)
def calculate(x, y):
return x // y # Returns int - different!
Detection:
- Check return types
- Test with various inputs
- Use type checking tools
Remediation:
- Match original types
- Add type annotations
- Test type conversions
Pattern: Encoding Issues
Symptoms:
- Character corruption
- Unicode errors
- Different string representations
Example:
# Original
text = "Hello" # UTF-8
# Migrated (incorrect)
text = b"Hello" # Bytes instead of string
Detection:
- Test with non-ASCII characters
- Check encoding declarations
- Validate string operations
Remediation:
- Use consistent encoding
- Handle Unicode properly
- Add encoding tests
Error Handling Differences
Pattern: Swallowed Exceptions
Symptoms:
- Silent failures
- Missing error messages
- Different error behavior
Example:
# Original
def process(data):
if not validate(data):
raise ValueError("Invalid data")
return transform(data)
# Migrated (incorrect)
def process(data):
try:
if not validate(data):
raise ValueError("Invalid data")
return transform(data)
except:
pass # Swallows all exceptions!
Detection:
- Test error scenarios
- Check exception types
- Verify error messages
Remediation:
- Preserve error handling
- Don't catch all exceptions
- Add error tests
Pattern: Changed Exception Types
Symptoms:
- Different exception types
- Broken error handling
- Client code breaks
Example:
# Original
raise ValueError("Invalid input")
# Migrated (incorrect)
raise Exception("Invalid input") # Too generic!
Detection:
- Compare exception types
- Test error handling
- Check exception hierarchy
Remediation:
- Use same exception types
- Preserve exception hierarchy
- Test exception handling
Dependency Differences
Pattern: Library Version Mismatch
Symptoms:
- Different behavior
- API changes
- Deprecated features
Example:
# Original (pandas 1.0)
df.append(row) # Works
# Migrated (pandas 2.0)
df.append(row) # Deprecated!
Detection:
- Compare dependency versions
- Check deprecation warnings
- Test with same versions
Remediation:
- Match library versions
- Update deprecated APIs
- Pin dependencies
Pattern: Missing Dependency
Symptoms:
- Import errors
- Module not found
- Feature unavailable
Example:
# Original
import numpy as np
# Migrated (missing dependency)
# numpy not installed!
Detection:
- Check requirements files
- Test imports
- Verify dependencies
Remediation:
- Install missing dependencies
- Update requirements.txt
- Add dependency tests
Timing and Concurrency Issues
Pattern: Race Condition
Symptoms:
- Intermittent failures
- Non-deterministic behavior
- Concurrency bugs
Example:
# Migrated (incorrect - race condition)
def increment():
global counter
temp = counter
# Another thread might modify counter here!
counter = temp + 1
Detection:
- Run tests with threading
- Use race detection tools
- Test under load
Remediation:
- Add proper locking
- Use thread-safe structures
- Test concurrency
Pattern: Timeout Difference
Symptoms:
- Operations timeout
- Different timing behavior
- Flaky tests
Example:
# Original
response = requests.get(url, timeout=30)
# Migrated (incorrect)
response = requests.get(url) # No timeout!
Detection:
- Test with slow operations
- Check timeout settings
- Monitor execution time
Remediation:
- Set appropriate timeouts
- Handle timeout errors
- Add timeout tests
Diagnostic Workflow
For any behavioral difference:
- Isolate: Identify the minimal test case that reproduces the difference
- Compare: Examine the relevant code in both versions side-by-side
- Trace: Use execution tracing to see where behavior diverges
- Test: Create a specific test for the difference
- Fix: Implement the correction
- Verify: Confirm the fix resolves the difference
- Prevent: Add tests to prevent regression
Summary
Common patterns by frequency:
- Logic errors (40%)
- Missing functionality (25%)
- API contract violations (15%)
- Performance regressions (10%)
- State management issues (10%)
Most critical patterns:
- Logic errors affecting core functionality
- API contract violations breaking clients
- Missing functionality causing failures
- Performance regressions causing timeouts
- Data corruption from state issues