“Measuring programming progress by lines of code is like measuring aircraft building progress by weight.” — Bill Gates

Dead Code Die Hard: A Practical Guide to Identifying Orphan Flutter Methods🧪

Saropa Contacts News
8 min read2 days ago

--

As Flutter projects evolve, they often accumulate “dead code” — methods that are defined but no longer called. This code bloat increases project size, hinders maintainability, and can potentially impact performance. Identifying these unused methods manually is tedious and error-prone, especially in large codebases.

This article presents a practical approach to automatically identifying potentially unused methods in Flutter projects. We’ll introduce a PowerShell script that analyzes Dart source code, intelligently extracts method definitions, and counts their usages across the project.

The script leverages techniques like:

  • Two-Pass Analysis: Separate method extraction and usage counting.
  • Regular Expressions: Pattern matching for definitions and calls.
  • Exclusion Dictionary: Minimize false positives with exclusion lists.
  • Comment Handling: Prevents comments from influencing the results.
  • Detailed Logging: Clear, actionable output to guide developers.

Unused Code in Flutter Projects 🧰

Unused code, especially dead methods (functions defined but never called), degrades Flutter project health. It’s not just clutter; it has tangible negative consequences:

  • Longer Builds: Compiler processes all code, even unused.
  • Larger App Size: Increased download times and storage.
  • Slower IDE: Reduced responsiveness during analysis.
  • More Complexity: Hinders understanding and development.
  • Refactoring Hesitation: Fear of breaking unused code.

Data-Driven Detection 🧮

We need an automated solution to find unused methods efficiently and accurately. This solution will handle Dart’s complexities, minimize false positives, provide actionable data, and integrate seamlessly.

We will develop a PowerShell script to analyze a Flutter project’s Dart codebase, pinpointing potentially unused methods.

Here’s how it will work:

  • Two-Pass Analysis: First, extract all method definitions, then count usages, minimizing file reads.
  • Targeted Regex: Accurately identify method definitions and calls.
  • Flutter-Aware: Exclude common keywords, reducing false positives.
  • Ignores Comments: Skip commented-out code for accurate analysis.
  • Actionable Reports: Output unused methods with location and usage count.

Optimization Strategies:

  • Generated Files: Automatically ignore g.dart files, which are not relevant to this analysis.
  • Minimum Method Name Length: To avoid false positives from very short, generic method names, the script will have a minimum length requirement (e.g., ≥ 4 characters).
  • Keyword Dictionary: A built-in dictionary of common Flutter keywords will be used to filter out potential false positives.
# Array of common keywords, types, and method names to exclude
$flutterKeywordDictionary = @(
"abstract", "addListener", "addStatusListener",
# … (rest of the dictionary)
"wait"
)
  • Underscore Methods: The script will ignore private methods starting with an underscore, which are less likely to be unused in the same way as public methods.

By identifying private methods, we can enhance the logic to identify public methods that should be private!

Output First — Imagining a Result Log 🪤

Let’s imagine the script has analyzed a project that has identified several potentially unused methods. The output might look like this:

lib/components/payment_form.dart, calculateDiscount, 0
lib/models/user_profile.dart,Method: getFullName, 0
lib/utils/string_helper.dart, toTitleCase, 0

This output tells us that the calculateDiscount method in payment_form.dart, the getFullName method in user_profile.dart, and the toTitleCase method in string_helper.dart are potentially unused, as they have a usage count of 0.

Now let’s make it more powerful! The script should generate several log files, each providing different insights into the analysis:

  • Timestamped Logs: All log files are timestamped for easy tracking (e.g., UnusedMethods-20231027103045.report.log).
  • Report Log: Summary and detailed list of potentially unused methods with location and usage count.
  • Used Methods Log: List of all used methods, grouped by file, with usage counts.
  • Local Candidates Log: Methods only used within their defining file.
  • Debug Log: Detailed information for troubleshooting, including processed files and extracted methods.

Let’s imagine the script has analyzed a project and identified several potentially unused methods. A snippet of the .report.log file might look like this:

…
Summary:
Unused methods count: 3
Used methods count: 152
Total methods analyzed: 155
Run time: 00:00:05.123
…
File: lib/components/payment_form.dart
Usages: 0 calculateDiscount
Line: 45
File: lib/models/user_profile.dart
Usages: 0 getFullName
Line: 112

File: lib/utils/string_helper.dart
Usages: 0 toTitleCase
Line: 12
…

Constructing the Script 🧩

Provide a concise overview of the progress and status in the terminal when running the script, but log analysis to files. The terminal will display :

  • Configuration: Settings used for the analysis.
  • Progress Bars: Indicate the percentage of files or methods processed
  • Status Messages: Brief updates about the current operation.
  • Timings: Start time, end time, and total run time.
  • Log Files: paths to important folders and generated logs.
# Get all Dart files in the directory and its subdirectories, always
# excluding .g.dart files using a Where-Object clause with a wildcard pattern.
# This prevents unnecessary processing of irrelevant files, improving
# efficiency.
$dartFiles = Get-ChildItem -Path $directory -Recurse -Filter *.dart | Where-Object { $_.FullName -notlike "*.g.dart" }

Pass 1: Extracting Method Definitions

  1. Read Files: Reads each relevant .dart file in a /lib/ folder.
  2. Identify Methods: Use regex to find definitions.
  3. Apply Exclusions: Skip based on length, keywords, and underscores.
  4. Data: Stores method name, file, and line number; builds debug.log.
# This regex identifies method definitions in Dart code. It handles static
# methods, various return types, method names, parameter lists, async
# keyword and different types of method bodies ({} or =>). It's designed
# to match a wide range of valid method declarations.
(?m)^\s*(?:static\s+)?(?:[\w.<> ]+\s+)?([\w]+)\s*(\([^)]*\)|)\s*(async\s*)?({\s*|\=\>\s*)

Comment Removal for Accurate Analysis

# Before analyzing method usage, the script removes both single-line and
# multi-line comments from the file content. This prevents commented-out
# code from being counted as actual method calls, ensuring more accurate
# results.
$fileContentWithoutComments = [regex]::Replace($fileContent, "//.*|/\*[\s\S]*?\*/", "")

Pass 2: Counting Method Usages

  1. Iterate Methods: Loops through each method from Pass 1.
  2. Find Usages: Use regex to find each method’s calls across all files.
  3. Count Usages: Increments a counter for each found call.
  4. Local Candidates: Marks methods used only within their defining file.
# This regex is dynamically generated for each method during Pass 2. It finds
# method calls by matching the method name ($methodName) in various contexts.
# It uses lookarounds to avoid matching the method name when used as a type
# or after a dot (.), and excludes cases where the name is followed by as or
# in.
(?<=[^.\w])\b$([regex]::Escape($methodName))\b(?!\s+(?:as|in)\b)(?=[^;]*?(;|\)))

Alphabetical Sorting

# Take advantage of Sort-Object to organize the files for processing, and
# then sorts the output by method name and file
$sortedDartFiles = $dartFiles | Sort-Object Name
…
$sortedMethods = $methodUsage.GetEnumerator() | Sort-Object { $_.Value[0] }, Key

Hash Table Storage

PowerShell hash tables will be used to efficiently store and manage information about methods, leveraging their fast key-based lookups. Arrays are used when a simple, ordered list of items is needed:

  • $methodNames: Stores each discovered method name (key) and its defining file path (value).
  • $methodUsage: Stores each method name (key) and its usage count across the project (value).
  • $debugLogFileData: Stores file paths (key) and an array of extracted methods from each file (value) for the debug.log.
  • $localCandidates (Array): Stores methods used only within their defining file (each entry is a hash table with Method and File keys).

Writing the Output Logs

  • report.log: Summary and detailed list of potentially unused methods
  • used.log: Structured list of methods with usage.
  • local.candidates.log: Methods only used locally.
  • debug.log: All processed files and extracted methods
# uses Write-Host with different foreground colors to enhance readability in
# the console. For instance, errors could be in red, warnings in yellow,
# and general information in green. The colors could be easily toggled
# with a boolean.
Write-Host "Starting analysis…" -ForegroundColor Green
Write-Host "Error: File not found!" -ForegroundColor Red

Human Evaluation and Removal Process 🔏

The PowerShell script will identify potentially unused methods, but careful judgment is crucial before removal.

Terminal output on the Saropa Contacts project

Here’s a process we follow:

  1. Investigate: Understand each flagged method’s context, usage, and potential reliance on dynamic calls or tests.
  2. Check Tests: Verify if tests cover the flagged method
  3. Consider External Usage: Remember that public API methods in libraries might be used externally.
  4. Deprecate Public APIs (Optional): If removing a public API method, @deprecate it first to warn users.
  5. Prioritize: Focus on removing the most obvious unused methods first
  6. Document: Keep a record of your decisions, including the rationale for removal or retention.
  7. Comment Out: Initially, comment out the method instead of deleting it outright.
  8. Test and Monitor: Permanently delete the commented-out code only after thorough testing and monitoring.

NOTE: Not all flagged methods can or should be immediately removed. Before removing any method, we must carefully consider its context and potential impact on the application.

Conclusion: The Importance of Code Hygiene 🔣

While seemingly a minor issue, the accumulation of dead code can have significant consequences for project maintainability, performance, and team efficiency.

Ultimately, maintaining a clean and efficient codebase is an ongoing effort. By embracing tools and practices that promote code hygiene, we can ensure that our Flutter projects remain maintainable, scalable, and enjoyable to work on.

Sample logs from analysis of the Saropa Contacts project (note: clickable links within VS Code)

The Completes PowerShell 7 Script

We deployed a completed script to our published Utility package:

Direct download here: [COMING SOON]

Limitations and Future Enhancements đź“›

Static analysis has limits, for example, it can’t find dynamic method calls. Dynamic usage detection is not possible with reflection, a linter can identify it after making a method private. Complex code can also cause log inaccuracies. Exclusion lists may need adjustments for unusual projects.

Future Enhancements

  • Multi-threading: Faster log generation for large projects.
  • Configurable: Customizable settings via parameters and a config file.
  • IDE Extension: Integration into the development environment.
  • Async: Running the script on active code introduces caching errors

Moving Forward and Recap

Best practice is to incorporate scripts like this into the CI/CD pipeline, triggering automated analysis on each code commit. The generated reports, should then be reviewed by the team in code reviews. Establish a process for prioritizing and addressing the identified issues.

This establishes a robust process for maintaining a healthy, efficient, and high-performing codebase.

  • Improved Maintainability: Easier to understand, modify, and debug.
  • Enhanced Efficiency: Reduced cognitive load and faster onboarding for new team members.
  • Improved Performance: Smaller codebase can lead to faster build times and potentially smaller application sizes.
  • Reduced Error Risk: Removing unused code eliminates potential sources of bugs.

Final Word 🪅

Your feedback is essential to us, and we genuinely value your support. When we learn of a mistake, we acknowledge it with a correction. If you spot an error, please let us know at blog@saropa.com and learn more at saropa.com.

About Saropa

Over our multi-decade journey in tech, we’ve worn many hats, from coding national bank systems, managing industry-leading dev teams, and delivering international emergency projects. We’re passionate about sparking curiosity and understanding of complex topics.

saropa.com

--

--

Saropa Contacts News
Saropa Contacts News

Written by Saropa Contacts News

Saropa Contacts is a replacement address book that is cloud-connected to real people and businesses and focused on your trusted emergency groups.

Responses (1)