DEV Community

ThankGod Chibugwum Obobo
ThankGod Chibugwum Obobo

Posted on • Originally published at actocodes.hashnode.dev

Refactoring for Readability: Reducing Complexity Scores in TypeScript and Dart

This topic is the practical "how-to" that follows the theory of complexity metrics. It focuses on the specific surgical techniques you can use to dismantle "spaghetti code" and turn high-complexity functions into clean, readable ones. see previous article here

Knowing that your code is complex is only half the battle. The real skill lies in the refactor, the ability to take a function with a high cognitive complexity score and simplify it without changing its behavior.

In TypeScript and Dart, we have unique language features that make this process easier. Here are four essential strategies to lower your complexity scores today.

1. The Power of the Guard Clause

The "Arrow Anti-pattern" (nested if statements) is the biggest contributor to high cognitive complexity. Guard clauses allow you to "fail fast" and keep the main logic at the lowest nesting level.

Before (Deep Nesting):

function getDiscount(user: User) {
  if (user.isMember) {
    if (user.active) {
      if (user.points > 100) {
        return 0.2;
      }
    }
  }
  return 0;
}
Enter fullscreen mode Exit fullscreen mode

After (Linear Flow):

function getDiscount(user: User) {
  if (!user.isMember || !user.active) return 0;
  if (user.points <= 100) return 0;

  return 0.2;
}
Enter fullscreen mode Exit fullscreen mode

Impact: Reduced Cognitive Complexity from 5 to 2.

2. Extracting to "Composed Functions"

If a function is doing multiple things, it naturally accumulates a high score. Break it down into small, private helper functions that do one thing well.

Strategy: If you see a comment like // Step 2: Calculate Taxes, that block of code belongs in its own function.

In Dart:

// Before
void processOrder(Order order) {
  // Validate
  if (order.id.isEmpty) throw Exception();
  // Calculate
  double total = order.items.fold(0, (sum, item) => sum + item.price);
  // Save
  repository.save(order);
}

// After
void processOrder(Order order) {
  _validateOrder(order);
  final total = _calculateTotal(order);
  repository.save(order, total);
}
Enter fullscreen mode Exit fullscreen mode

3. Replace Complex Switch/If with Lookups

In TypeScript, you can often replace a bulky switch statement with a Record or an Object literal. This removes the "branching" complexity entirely.

Before:

function getColor(status: Status) {
  switch (status) {
    case 'success': return 'green';
    case 'error': return 'red';
    case 'pending': return 'yellow';
    default: return 'gray';
  }
}
Enter fullscreen mode Exit fullscreen mode

After (The Configuration Map):

const STATUS_COLORS: Record<Status, string> = {
  success: 'green',
  error: 'red',
  pending: 'yellow',
};

function getColor(status: Status) {
  return STATUS_COLORS[status] ?? 'gray';
}
Enter fullscreen mode Exit fullscreen mode

4. Modern Language Features (Dart & TS)

Use modern syntax to handle nulls and collections. This removes the need for explicit if checks.

Optional Chaining (TS/Dart): Use user?.profile?.address instead of nested if (user && user.profile).

Nullish Coalescing: Use const name = inputName ?? 'Guest' instead of ternary operators.

Collection If/For (Dart): Dart allows you to use logic directly inside list literals, which is much cleaner than building lists imperatively.

// Dart Collection If
final navBar = [
  HomeButton(),
  if (isAdmin) AdminSettings(),
  ProfileButton(),
];
Enter fullscreen mode Exit fullscreen mode

Summary: The Refactoring Checklist

When you see a complexity score above 10, ask these three questions:

  • Can I return early? (Flatten the nesting).
  • Can I name this block? (Extract to a new function).
  • Is this logic data or code? (Replace if/else with a Map/Record).

Conclusion

Refactoring for readability isn't about making the code shorter, it's about making it predictable. When a developer can read a function from top to bottom without jumping back and forth between nested braces, the "mental tax" of maintaining that code drops to near zero.

p.s: The examples here may be too simplified, but the idea is to establish the underlying patterns.

Top comments (0)