1. When to use this skill
Use this skill when analyzing Dart and Flutter codebase maintainability, evaluating function readability, or remediating high-complexity warning findings. Unlike traditional Cyclomatic Complexity (which linearly counts branching paths and punishes clean declarative table switches), Cognitive Complexity directly measures the comprehension friction required for a human or reviewing LLM to read and simulate control flow.
Specifically, target methods and classes matching these indicators:
- Deeply Nested Control Flow: Functions exhibiting multiple layers of
enclosing conditionals (
if,for,while), where indentation obscures logic. - Convoluted Conditional Trees: Functions employing verbose
if-elseorelse ifchains instead of modern Dart 3 exhaustive pattern matching or table-driven switches. - Monolithic Method Bodies: Functions that breach operational complexity thresholds during evaluation or static analysis audits.
- God Classes: Logic classes exceeding structural line-count ceilings
(excluding declarative Flutter
buildmethods).
Discovery Commands (Targeting Hot-Spots)
Run read-only discovery commands to identify nesting hot-spots before calculating scores:
# Locate functions with deeply nested conditionals (4+ levels of indentation on flow-breaking statements)
grep -rnE '^\s{8,}(if|for|while|switch)\s*\(' lib/
# Locate verbose if-else ladders ripe for Dart 3 switch expression refactoring
grep -rnE '^\s*\} else if \(' lib/
2. Algorithmic Scoring Rules for Dart
Calculate Cognitive Complexity algorithmically using three deterministic scoring rules:
Rule 1: Benign Shorthand & Switches (+0 Penalty)
No base points or nesting multipliers are added for idiomatic Dart syntax that consolidates operations into scannable expressions:
- Null-Aware Operators & Cascades:
??,?.,??=, null-aware spreads (...?), null-aware collection elements (?item), and cascade notation (..) cost +0 points. - Dart 3 Switch Statements & Expressions: An entire exhaustive
switchblock costs exactly +1 base point, regardless of whether it evaluates 3 or 50caseor pattern arms. Case labels receive +0 points.
Rule 2: Flow Interruption (+1 Base)
Add +1 base point whenever execution diverges from linear top-to-bottom reading flow:
- Conditionals & Ternaries:
if,else if,else, and conditional expressions (cond ? x : y). - Iterators:
for,while,do-while, and collectionfor/ifclauses inside list literals. - Exception Catching:
catch/onblocks (tryandfinallycost +0). - Logical Operator Switches: Consecutive strings of identical operators
(
a && b && c) count as +1 total. Alternating operator families (a && b || c) adds +1 per alternation.
Rule 3: The Nesting Multiplier (+D Depth)
Whenever a flow-breaking structure (from Rule 2) is nested inside another structural block, add its structural nesting depth (D) to the base cost:
- Top-level
if(D=0): +1 base = +1 point. forloop nested inside thatif(D=1): +1 base + 1 depth = +2 points.- Inner
ifinside thefor(D=2): +1 base + 2 depth = +3 points. - Flat Multiplicity Exceptions: Unlike conditional openers and loops,
else,else if, andcatch/onstructures receive only a flat +1 base penalty without incurring depth nesting multipliers.
3. Execution & Delegation Strategy
Line-by-line complexity arithmetic consumes high token bandwidth. Apply adaptive delegation to preserve primary context:
- Subagent by Default (Files, Libraries, PRs): When evaluating entire
files, directories, or multi-class architectures, invoke a
selforResearcherread-only subagent assigned the roleComplexity Auditorto handle file reads and scoring calculations. Instruct the subagent to return only an aggregated scoreboard listing methods that breach complexity thresholds, along with their exact line ranges and failing structures. - Inline Computation (Micro-Scope): Calculate complexity scores directly in the primary context window solely when inspecting tiny code snippets (< 50 lines) pasted directly in chat or during immediate post-refactoring verification of an individual modified method block.
- Immutability Guardrail: Subagent complexity evaluators must operate strictly in read-only mode to prevent unintended workspace mutations during audit sweeps.
4. Actionable Thresholds & Calibration
- Production Logic Functions: Target score <= 15. Functions exceeding 15 points mandate architectural refactoring.
- Test Methods (
_test.dart): Target score <= 40. Test harnesses tolerate higher structural setup sequences before decomposition is required. - Class Size Ceiling: Logic classes (services, domain objects, controllers) should remain <= 150 non-comment lines.
- Flutter UI Calibration: Do not enforce the 150 LOC class ceiling on
declarative Flutter
buildmethods, as widget wrappers consume vertical space without increasing cognitive logic load. Instead, enforce a Widget Tree Nesting Ceiling of maximum 5 horizontal indentation levels before extracting discrete helper widget classes.
5. Refactoring Strategies & Dart Patterns
When remediating functions that breach complexity ceilings, apply these Dart-specific architectural refactorings:
Pattern A: Replace Nested If-Else with Dart 3 Switch Expression
Before: Deeply Nested Conditionals (Score: 12)
int resolveTimeout(String protocol, bool isSecure, int retryCount) {
if (protocol == 'http') { // D=0 -> +1 (if)
if (isSecure) { // D=1 -> +2 (if + depth 1)
if (retryCount > 3) { // D=2 -> +3 (if + depth 2)
return 5000;
} else { // D=2 -> +1 (else flat)
return 3000;
}
} else { // D=1 -> +1 (else flat)
return 1000;
}
} else if (protocol == 'ftp') { // D=0 -> +1 (else if flat) +3 (nested...)
return isSecure ? 10000 : 2000;
}
return 0;
}
After: Table-Driven Switch Expression (Score: 1)
int resolveTimeout(String protocol, bool isSecure, int retryCount) =>
switch ((protocol, isSecure, retryCount)) { // D=0 -> +1 (switch expression)
('http', true, > 3) => 5000, // Case arms cost +0
('http', true, _) => 3000,
('http', false, _) => 1000,
('ftp', true, _) => 10000,
('ftp', false, _) => 2000,
_ => 0,
}; // Total complexity reduction: 12 -> 1
Pattern B: Guard Clause Inversion (Flattening Nesting Depth)
Invert conditional checks into early guard return statements
(if (!condition) return;). Every early exit strips away a layer of nesting
multiplication from subsequent downstream logic.
Before: Pyramid of Nesting (Score: 11)
Future<void> syncPayload(User? user, Payload? data) async {
if (user != null) { // D=0 -> +1
if (user.hasPermission) { // D=1 -> +2 (if + depth 1)
if (data != null && data.isValid) { // D=2 -> +3 (if + depth 2) +1 (&&)
for (final item in data.items) { // D=3 -> +1 (for) + 3 depth = +4
await repository.save(item);
}
}
}
}
}
After: Early Exit Guard Clauses (Score: 5)
Future<void> syncPayload(User? user, Payload? data) async {
if (user == null || !user.hasPermission) return; // D=0 -> +1 (if) +1 (||) = +2
if (data == null || !data.isValid) return; // D=0 -> +1 (if) +1 (||) = +2
for (final item in data.items) { // D=0 -> +1 (for at zero depth)
await repository.save(item);
}
}
Pattern C: Encapsulated Method Object Extraction
When a monolithic function contains dense closures capturing heavy local
variable state that prevents simple function extraction, migrate the function
body into a dedicated private runner class using the
encapsulated-method-object skill. Read the encapsulated-method-object
skill instructions via view_file before applying this refactoring. Promoting
local variables to class instance fields collapses closure nesting penalties
and unlocks focused helper method decomposition.
6. Verification Guardrails
Run these verification commands before submitting refactored code:
- Code Presentation: Run
dart format .to maintain uniform syntactic styling. - Static Analysis: Run
dart analyzeto ensure zero static warnings, lint violations, or un-awaited asynchronous gaps. - Test Fidelity: Run
dart test(orflutter test) to verify zero behavioral drift across existing test suites.