Files

11 KiB

source, confidence, namespace, last_synced, alwaysApply
source confidence namespace last_synced alwaysApply
~/DuckDuckGo/apple-browsers.git/main/.cursor/rules/anti-patterns.mdc 0.9 work 2026-04-28 true

Anti-patterns and Common Mistakes to Avoid

Singleton Anti-patterns

NEVER: Static Shared Instances Without Dependency Injection (.shared instance pattern)

Example: See singleton-antipattern.swift

NEVER: Global State Access

// ❌ AVOID - Global state access
var globalSettings: [String: Any] = [:]

func someFunction() {
    globalSettings["key"] = "value" // Global state is hard to test and debug
}

// ✅ CORRECT - Injected dependencies
final class SomeService {
    private let settings: AppSettings
    
    init(settings: AppSettings) {
        self.settings = settings
    }
    
    func someFunction() {
        settings.setValue("value", for: "key")
    }
}

Async/Await Anti-patterns

NEVER: UI Updates Without @MainActor

Example: See async-ui-updates.swift

NEVER: Unhandled Async Errors

// ❌ AVOID - Swallowing async errors
func fetchData() async {
    let data = try? await networkService.getData() // Silently ignoring errors
    // Process data...
}

// ✅ CORRECT - Proper error handling
func fetchData() async throws {
    let data = try await networkService.getData()
    // Process data...
}

// Or handle errors appropriately:
func fetchData() async {
    do {
        let data = try await networkService.getData()
        // Process data...
    } catch {
        // Log error and show user-friendly message
        logger.error("Failed to fetch data: \(error)")
        await showError(error)
    }
}

NEVER: Blocking Main Thread with Sync Operations

// ❌ AVOID - Blocking main thread
@MainActor
func loadData() {
    let data = NetworkService.fetchDataSynchronously() // Blocks UI
    updateUI(with: data)
}

// ✅ CORRECT - Use async operations
@MainActor
func loadData() async {
    let data = try await NetworkService.fetchData() // Non-blocking
    updateUI(with: data)
}

Memory Management Anti-patterns

NEVER: Strong Reference Cycles in Closures

Example: See memory-leak-closure.swift

NEVER: Retaining View Controllers in Cache

// ❌ AVOID - Caching view controllers without cleanup
class NavigationManager {
    private var cachedViewControllers: [String: UIViewController] = [:]
    
    func getViewController(for identifier: String) -> UIViewController {
        if let cached = cachedViewControllers[identifier] {
            return cached // May contain stale data and strong references
        }
        let vc = createViewController(for: identifier)
        cachedViewControllers[identifier] = vc
        return vc
    }
}

// ✅ CORRECT - Cache view models, not view controllers
class NavigationManager {
    private var cachedViewModels: [String: ViewModel] = [:]
    
    func getViewController(for identifier: String) -> UIViewController {
        let viewModel = getOrCreateViewModel(for: identifier)
        return createViewController(with: viewModel)
    }
    
    private func getOrCreateViewModel(for identifier: String) -> ViewModel {
        if let cached = cachedViewModels[identifier] {
            return cached
        }
        let viewModel = createViewModel(for: identifier)
        cachedViewModels[identifier] = viewModel
        return viewModel
    }
}

Error Handling Anti-patterns

NEVER: Force Unwrapping Without Justification

Example: See force-unwrapping.swift

NEVER: Generic Error Messages

// ❌ AVOID - Generic error handling
func handleError(_ error: Error) {
    print("Something went wrong") // Not helpful for debugging
    showAlert("Error occurred")   // Not helpful for users
}

// ✅ CORRECT - Specific error handling
enum NetworkError: LocalizedError {
    case noConnection
    case timeout
    case unauthorized
    case serverError(Int)
    
    var errorDescription: String? {
        switch self {
        case .noConnection:
            return "No internet connection. Please check your network settings."
        case .timeout:
            return "Request timed out. Please try again."
        case .unauthorized:
            return "You are not authorized to access this resource."
        case .serverError(let code):
            return "Server error (\(code)). Please try again later."
        }
    }
}

func handleNetworkError(_ error: NetworkError) {
    logger.error("Network error: \(error)")
    showAlert(error.localizedDescription)
}

SwiftUI Anti-patterns

NEVER: Heavy Computation in View Body

// ❌ AVOID - Expensive operations in body
struct ContentView: View {
    let items: [Item]
    
    var body: some View {
        List {
            ForEach(items) { item in
                Text(expensiveProcessing(item)) // Computed every view update
            }
        }
    }
    
    private func expensiveProcessing(_ item: Item) -> String {
        // Heavy computation
        return item.data.complexProcessing()
    }
}

// ✅ CORRECT - Pre-compute or use lazy loading
struct ContentView: View {
    @StateObject private var viewModel: ContentViewModel
    
    var body: some View {
        List {
            ForEach(viewModel.processedItems) { item in
                Text(item.displayText)
            }
        }
        .onAppear {
            viewModel.processItems()
        }
    }
}

NEVER: Direct State Mutation from View

// ❌ AVOID - Direct state mutation in view
struct ContentView: View {
    @State private var items: [Item] = []
    
    var body: some View {
        List {
            ForEach(items) { item in
                ItemRow(item: item) { updatedItem in
                    // Don't mutate state directly in view
                    if let index = items.firstIndex(where: { $0.id == updatedItem.id }) {
                        items[index] = updatedItem
                    }
                }
            }
        }
    }
}

// ✅ CORRECT - Use ViewModel for state management
struct ContentView: View {
    @StateObject private var viewModel: ContentViewModel
    
    var body: some View {
        List {
            ForEach(viewModel.items) { item in
                ItemRow(item: item) { updatedItem in
                    viewModel.updateItem(updatedItem)
                }
            }
        }
    }
}

Design System Anti-patterns

NEVER: Hardcoded Colors or Icons

Example: See design-system-violation.swift

Network and API Anti-patterns

NEVER: Hardcoded URLs or API Keys

// ❌ AVOID - Hardcoded values
func fetchData() async throws -> Data {
    let url = URL(string: "https://api.example.com/data")! // Hardcoded URL
    let apiKey = "abc123xyz"                               // Hardcoded API key
    
    var request = URLRequest(url: url)
    request.addValue(apiKey, forHTTPHeaderField: "Authorization")
    
    let (data, _) = try await URLSession.shared.data(for: request)
    return data
}

// ✅ CORRECT - Configuration-based approach
struct APIConfiguration {
    let baseURL: URL
    let apiKey: String
    
    static let production = APIConfiguration(
        baseURL: URL(string: "https://api.duckduckgo.com")!,
        apiKey: Bundle.main.object(forInfoDictionaryKey: "API_KEY") as! String
    )
}

func fetchData() async throws -> Data {
    let config = APIConfiguration.production
    let url = config.baseURL.appendingPathComponent("data")
    
    var request = URLRequest(url: url)
    request.addValue(config.apiKey, forHTTPHeaderField: "Authorization")
    
    let (data, _) = try await URLSession.shared.data(for: request)
    return data
}

Testing Anti-patterns

NEVER: Testing Implementation Details

// ❌ AVOID - Testing private implementation
class ViewModelTests: XCTestCase {
    func testPrivateMethod() {
        let viewModel = ViewModel()
        
        // Don't test private methods directly
        let result = viewModel.privateHelperMethod()
        XCTAssertEqual(result, expected)
    }
}

// ✅ CORRECT - Test public behavior
class ViewModelTests: XCTestCase {
    func testLoadDataUpdatesState() async {
        let mockService = MockDataService()
        let viewModel = ViewModel(service: mockService)
        
        await viewModel.loadData()
        
        // Test the observable behavior, not implementation
        XCTAssertFalse(viewModel.isLoading)
        XCTAssertNotNil(viewModel.data)
        XCTAssertNil(viewModel.error)
    }
}

NEVER: Tests That Don't Test Anything

// ❌ AVOID - Tests without assertions
func testInitialization() {
    let viewModel = ViewModel()
    // Test does nothing
}

// ❌ AVOID - Tests that can't fail
func testAlwaysTrue() {
    XCTAssertTrue(true) // This test is meaningless
}

// ✅ CORRECT - Meaningful tests with specific assertions
func testInitializationSetsDefaultState() {
    let viewModel = ViewModel()
    
    XCTAssertEqual(viewModel.state, .idle)
    XCTAssertTrue(viewModel.items.isEmpty)
    XCTAssertFalse(viewModel.isLoading)
}

Performance Anti-patterns

NEVER: Synchronous Operations on Main Thread

// ❌ AVOID - Blocking main thread
@MainActor
func processLargeDataSet() {
    let result = heavyComputation() // Blocks UI
    updateUI(with: result)
}

// ✅ CORRECT - Background processing
@MainActor
func processLargeDataSet() async {
    let result = await Task.detached(priority: .userInitiated) {
        return heavyComputation()
    }.value
    
    updateUI(with: result)
}

Communication Anti-patterns

NEVER: Celebrate Partial Results or Progress

// ❌ AVOID - Celebrating when work is incomplete
"✅ MISSION ACCOMPLISHED!" (when tests still failing)
"🎯 Outstanding Achievement:" (when task isn't finished)
"📊 FINAL RESULTS:" (when results aren't final)
"✅ Successfully achieved X" (when Y tests still failing)

// ✅ CORRECT - Focus on what's left to do
"7 tests still failing. Continuing to fix remaining issues."
"Progress made but task incomplete. Working on remaining failures."
"X tests now passing, Y still need work."

Never celebrate or summarize achievements when:

  • Tests are still failing
  • Tasks are incomplete
  • User's request hasn't been fully satisfied
  • Work is in progress

Only summarize results when:

  • ALL tests pass (100% success rate)
  • Task is completely finished
  • User's request is fully satisfied
  • No work remaining

These anti-patterns should be actively avoided to maintain code quality, testability, and performance in the DuckDuckGo browser codebase.