jpskill.com
🛠️ 開発・MCP コミュニティ

crap-analysis

コードの変更リスクと複雑さを分析し、テストされていない箇所を特定することで、品質改善が必要なリスクの高いコード領域を効率的に見つけ出すSkill。

📜 元の英語説明(参考)

Analyze code coverage and CRAP (Change Risk Anti-Patterns) scores to identify high-risk code. Use OpenCover format with ReportGenerator for Risk Hotspots showing cyclomatic complexity and untested code paths.

🇯🇵 日本人クリエイター向け解説

一言でいうと

コードの変更リスクと複雑さを分析し、テストされていない箇所を特定することで、品質改善が必要なリスクの高いコード領域を効率的に見つけ出すSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o crap-analysis.zip https://jpskill.com/download/8703.zip && unzip -o crap-analysis.zip && rm crap-analysis.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/8703.zip -OutFile "$d\crap-analysis.zip"; Expand-Archive "$d\crap-analysis.zip" -DestinationPath $d -Force; ri "$d\crap-analysis.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して crap-analysis.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → crap-analysis フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

CRAPスコア分析

このSkillを使用するタイミング

このSkillは、以下の場合に使用します。

  • 変更前にコード品質とテストカバレッジを評価する
  • リファクタリングまたはテストが必要な高リスクのコードを特定する
  • .NETプロジェクトのカバレッジ収集を設定する
  • リスクに基づいてテストするコードの優先順位付けを行う
  • CI/CDパイプラインのカバレッジ閾値を設定する

CRAPとは?

CRAPスコア = 複雑度 x (1 - カバレッジ)^2

CRAP(Change Risk Anti-Patterns)スコアは、循環的複雑度とテストカバレッジを組み合わせて、リスクの高いコードを特定します。

CRAPスコア リスクレベル 必要なアクション
< 5 十分にテストされ、保守可能なコード
5-30 許容範囲だが、複雑さに注意
> 30 テストまたはリファクタリングが必要

CRAPが重要な理由

  • 高い複雑度 + 低いカバレッジ = 危険: 理解が難しく、テストされていないコードは変更するリスクが高い
  • 複雑さだけでは不十分: 100%のカバレッジを持つ複雑なメソッドは、0%のカバレッジを持つ単純なメソッドよりも安全
  • 労力の集中: 単純なgetter/setterではなく、複雑なコードのテストを優先する

CRAPスコアの例

メソッド 複雑度 カバレッジ 計算 CRAP
GetUserId() 1 0% 1 x (1 - 0)^2 1
ParseToken() 54 52% 54 x (1 - 0.52)^2 12.4
ValidateForm() 20 0% 20 x (1 - 0)^2 20
ProcessOrder() 45 20% 45 x (1 - 0.20)^2 28.8
ImportData() 80 10% 80 x (1 - 0.10)^2 64.8

カバレッジ収集の設定

coverage.runsettings

リポジトリのルートに coverage.runsettings ファイルを作成します。CRAPスコアの計算には、OpenCover形式が必要です。これは、循環的複雑度のメトリクスが含まれているためです。

<?xml version="1.0" encoding="utf-8" ?>
<RunSettings>
  <DataCollectionRunSettings>
    <DataCollectors>
      <DataCollector friendlyName="XPlat code coverage">
        <Configuration>
          <!-- OpenCover format includes cyclomatic complexity for CRAP scores -->
          <Format>cobertura,opencover</Format>

          <!-- Exclude test and benchmark assemblies -->
          <Exclude>[*.Tests]*,[*.Benchmark]*,[*.Migrations]*</Exclude>

          <!-- Exclude generated code, obsolete members, and explicit exclusions -->
          <ExcludeByAttribute>Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute,ExcludeFromCodeCoverageAttribute</ExcludeByAttribute>

          <!-- Exclude source-generated files, Blazor generated code, and migrations -->
          <ExcludeByFile>**/obj/**/*,**/*.g.cs,**/*.designer.cs,**/*.razor.g.cs,**/*.razor.css.g.cs,**/Migrations/**/*</ExcludeByFile>

          <!-- Exclude test projects -->
          <IncludeTestAssembly>false</IncludeTestAssembly>

          <!-- Optimization flags -->
          <SingleHit>false</SingleHit>
          <UseSourceLink>true</UseSourceLink>
          <SkipAutoProps>true</SkipAutoProps>
        </Configuration>
      </DataCollector>
    </DataCollectors>
  </DataCollectionRunSettings>
</RunSettings>

主要な構成オプション

オプション 目的
Format 複雑度メトリクスには opencover を含める必要があります
Exclude パターンでテスト/ベンチマークアセンブリを除外します
ExcludeByAttribute 生成されたコード、廃止されたコード、および明示的に除外されたコードをスキップします(ExcludeFromCodeCoverageAttribute を含む)
ExcludeByFile ソース生成されたファイル、Blazorコンポーネント、および移行をスキップします
SkipAutoProps 自動プロパティを分岐としてカウントしません

ReportGeneratorのインストール

リスクホットスポットを含むHTMLレポートを生成するために、ReportGeneratorをローカルツールとしてインストールします。

.config/dotnet-tools.jsonに追加

{
  "version": 1,
  "isRoot": true,
  "tools": {
    "dotnet-reportgenerator-globaltool": {
      "version": "5.4.5",
      "commands": ["reportgenerator"],
      "rollForward": false
    }
  }
}

次に、復元します。

dotnet tool restore

またはグローバルにインストール

dotnet tool install --global dotnet-reportgenerator-globaltool

カバレッジの収集

カバレッジ収集でテストを実行

# Clean previous results
rm -rf coverage/ TestResults/

# Run unit tests with coverage
dotnet test tests/MyApp.Tests.Unit \
  --settings coverage.runsettings \
  --collect:"XPlat Code Coverage" \
  --results-directory ./TestResults

# Run integration tests (optional, adds to coverage)
dotnet test tests/MyApp.Tests.Integration \
  --settings coverage.runsettings \
  --collect:"XPlat Code Coverage" \
  --results-directory ./TestResults

HTMLレポートの生成

dotnet reportgenerator \
  -reports:"TestResults/**/coverage.opencover.xml" \
  -targetdir:"coverage" \
  -reporttypes:"Html;TextSummary;MarkdownSummaryGithub"

レポートの種類

タイプ 説明 出力
Html 完全なインタラクティブレポート coverage/index.html
TextSummary プレーンテキストの概要 coverage/Summary.txt
MarkdownSummaryGithub GitHub互換のMarkdown coverage/SummaryGithub.md
Badges README用のSVGバッジ coverage/badge_*.svg
Cobertura マージされたCobertura XML coverage/Cobertura.xml

レポートの読み方

リスクホットスポットセクション

HTMLレポートには、複雑さでソートされたメソッドを示すリスクホットスポットセクションが含まれています。

  • 循環的複雑度: コードを通る独立したパスの数(if/else、switch case、ループ)
  • NPath複雑度: 非循環的な実行パスの数(ネストによる指数関数的な増加)
  • Crap Score: 複雑さとカバレッジから計算

結果の解釈

Risk Hotspots
─────────────
Method                          Complexity  Coverage  Crap Score
──────────────────────────────────────────────────────────────────
DataImporter.ParseRecord()      54          52%       12.4
AuthService.ValidateToken()     32          0%        32.0   ← HIGH RISK
OrderProcessor.Calculate()      28          85%       1.3

(原文がここで切り詰められています)
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

CRAP Score Analysis

When to Use This Skill

Use this skill when:

  • Evaluating code quality and test coverage before changes
  • Identifying high-risk code that needs refactoring or testing
  • Setting up coverage collection for a .NET project
  • Prioritizing which code to test based on risk
  • Establishing coverage thresholds for CI/CD pipelines

What is CRAP?

CRAP Score = Complexity x (1 - Coverage)^2

The CRAP (Change Risk Anti-Patterns) score combines cyclomatic complexity with test coverage to identify risky code.

CRAP Score Risk Level Action Required
< 5 Low Well-tested, maintainable code
5-30 Medium Acceptable but watch complexity
> 30 High Needs tests or refactoring

Why CRAP Matters

  • High complexity + low coverage = danger: Code that's hard to understand AND untested is risky to modify
  • Complexity alone isn't enough: A complex method with 100% coverage is safer than a simple method with 0%
  • Focuses effort: Prioritize testing on complex code, not simple getters/setters

CRAP Score Examples

Method Complexity Coverage Calculation CRAP
GetUserId() 1 0% 1 x (1 - 0)^2 1
ParseToken() 54 52% 54 x (1 - 0.52)^2 12.4
ValidateForm() 20 0% 20 x (1 - 0)^2 20
ProcessOrder() 45 20% 45 x (1 - 0.20)^2 28.8
ImportData() 80 10% 80 x (1 - 0.10)^2 64.8

Coverage Collection Setup

coverage.runsettings

Create a coverage.runsettings file in your repository root. The OpenCover format is required for CRAP score calculation because it includes cyclomatic complexity metrics.

<?xml version="1.0" encoding="utf-8" ?>
<RunSettings>
  <DataCollectionRunSettings>
    <DataCollectors>
      <DataCollector friendlyName="XPlat code coverage">
        <Configuration>
          <!-- OpenCover format includes cyclomatic complexity for CRAP scores -->
          <Format>cobertura,opencover</Format>

          <!-- Exclude test and benchmark assemblies -->
          <Exclude>[*.Tests]*,[*.Benchmark]*,[*.Migrations]*</Exclude>

          <!-- Exclude generated code, obsolete members, and explicit exclusions -->
          <ExcludeByAttribute>Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute,ExcludeFromCodeCoverageAttribute</ExcludeByAttribute>

          <!-- Exclude source-generated files, Blazor generated code, and migrations -->
          <ExcludeByFile>**/obj/**/*,**/*.g.cs,**/*.designer.cs,**/*.razor.g.cs,**/*.razor.css.g.cs,**/Migrations/**/*</ExcludeByFile>

          <!-- Exclude test projects -->
          <IncludeTestAssembly>false</IncludeTestAssembly>

          <!-- Optimization flags -->
          <SingleHit>false</SingleHit>
          <UseSourceLink>true</UseSourceLink>
          <SkipAutoProps>true</SkipAutoProps>
        </Configuration>
      </DataCollector>
    </DataCollectors>
  </DataCollectionRunSettings>
</RunSettings>

Key Configuration Options

Option Purpose
Format Must include opencover for complexity metrics
Exclude Exclude test/benchmark assemblies by pattern
ExcludeByAttribute Skip generated, obsolete, and explicitly excluded code (includes ExcludeFromCodeCoverageAttribute)
ExcludeByFile Skip source-generated files, Blazor components, and migrations
SkipAutoProps Don't count auto-properties as branches

ReportGenerator Installation

Install ReportGenerator as a local tool for generating HTML reports with Risk Hotspots.

Add to .config/dotnet-tools.json

{
  "version": 1,
  "isRoot": true,
  "tools": {
    "dotnet-reportgenerator-globaltool": {
      "version": "5.4.5",
      "commands": ["reportgenerator"],
      "rollForward": false
    }
  }
}

Then restore:

dotnet tool restore

Or Install Globally

dotnet tool install --global dotnet-reportgenerator-globaltool

Collecting Coverage

Run Tests with Coverage Collection

# Clean previous results
rm -rf coverage/ TestResults/

# Run unit tests with coverage
dotnet test tests/MyApp.Tests.Unit \
  --settings coverage.runsettings \
  --collect:"XPlat Code Coverage" \
  --results-directory ./TestResults

# Run integration tests (optional, adds to coverage)
dotnet test tests/MyApp.Tests.Integration \
  --settings coverage.runsettings \
  --collect:"XPlat Code Coverage" \
  --results-directory ./TestResults

Generate HTML Report

dotnet reportgenerator \
  -reports:"TestResults/**/coverage.opencover.xml" \
  -targetdir:"coverage" \
  -reporttypes:"Html;TextSummary;MarkdownSummaryGithub"

Report Types

Type Description Output
Html Full interactive report coverage/index.html
TextSummary Plain text summary coverage/Summary.txt
MarkdownSummaryGithub GitHub-compatible markdown coverage/SummaryGithub.md
Badges SVG badges for README coverage/badge_*.svg
Cobertura Merged Cobertura XML coverage/Cobertura.xml

Reading the Report

Risk Hotspots Section

The HTML report includes a Risk Hotspots section showing methods sorted by complexity:

  • Cyclomatic Complexity: Number of independent paths through code (if/else, switch cases, loops)
  • NPath Complexity: Number of acyclic execution paths (exponential growth with nesting)
  • Crap Score: Calculated from complexity and coverage

Interpreting Results

Risk Hotspots
─────────────
Method                          Complexity  Coverage  Crap Score
──────────────────────────────────────────────────────────────────
DataImporter.ParseRecord()      54          52%       12.4
AuthService.ValidateToken()     32          0%        32.0   ← HIGH RISK
OrderProcessor.Calculate()      28          85%       1.3
UserService.CreateUser()        15          100%      0.0

Action items:

  • ValidateToken() has CRAP > 30 with 0% coverage - test immediately or refactor
  • ParseRecord() is complex but has decent coverage - acceptable
  • CreateUser() and Calculate() are well-tested - safe to modify

Coverage Thresholds

Recommended Standards

Coverage Type Target Action
Line Coverage > 80% Good for most projects
Branch Coverage > 60% Catches conditional logic
CRAP Score < 30 Maximum for new code

Configuring Thresholds

Create coverage.props in your repository:

<Project>
  <PropertyGroup>
    <!-- Coverage thresholds for CI enforcement -->
    <CoverageThresholdLine>80</CoverageThresholdLine>
    <CoverageThresholdBranch>60</CoverageThresholdBranch>
  </PropertyGroup>
</Project>

CI/CD Integration

GitHub Actions

name: Coverage

on:
  pull_request:
    branches: [main, dev]

jobs:
  coverage:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '9.0.x'

      - name: Restore tools
        run: dotnet tool restore

      - name: Run tests with coverage
        run: |
          dotnet test \
            --settings coverage.runsettings \
            --collect:"XPlat Code Coverage" \
            --results-directory ./TestResults

      - name: Generate report
        run: |
          dotnet reportgenerator \
            -reports:"TestResults/**/coverage.opencover.xml" \
            -targetdir:"coverage" \
            -reporttypes:"Html;MarkdownSummaryGithub;Cobertura"

      - name: Upload coverage report
        uses: actions/upload-artifact@v4
        with:
          name: coverage-report
          path: coverage/

      - name: Add coverage to PR
        uses: marocchino/sticky-pull-request-comment@v2
        with:
          path: coverage/SummaryGithub.md

Azure Pipelines

- task: DotNetCoreCLI@2
  displayName: 'Run tests with coverage'
  inputs:
    command: 'test'
    arguments: '--settings coverage.runsettings --collect:"XPlat Code Coverage" --results-directory $(Build.SourcesDirectory)/TestResults'

- task: DotNetCoreCLI@2
  displayName: 'Generate coverage report'
  inputs:
    command: 'custom'
    custom: 'reportgenerator'
    arguments: '-reports:"$(Build.SourcesDirectory)/TestResults/**/coverage.opencover.xml" -targetdir:"$(Build.SourcesDirectory)/coverage" -reporttypes:"HtmlInline_AzurePipelines;Cobertura"'

- task: PublishCodeCoverageResults@2
  displayName: 'Publish coverage'
  inputs:
    codeCoverageTool: 'Cobertura'
    summaryFileLocation: '$(Build.SourcesDirectory)/coverage/Cobertura.xml'

Quick Reference

One-Liner Commands

# Full analysis workflow
rm -rf coverage/ TestResults/ && \
dotnet test --settings coverage.runsettings \
  --collect:"XPlat Code Coverage" \
  --results-directory ./TestResults && \
dotnet reportgenerator \
  -reports:"TestResults/**/coverage.opencover.xml" \
  -targetdir:"coverage" \
  -reporttypes:"Html;TextSummary"

# View summary
cat coverage/Summary.txt

# Open HTML report (Linux)
xdg-open coverage/index.html

# Open HTML report (macOS)
open coverage/index.html

# Open HTML report (Windows)
start coverage/index.html

Project Standards

Metric New Code Legacy Code
Line Coverage 80%+ 60%+ (improve gradually)
Branch Coverage 60%+ 40%+ (improve gradually)
Maximum CRAP 30 Document exceptions
High-risk methods Must have tests Add tests before modifying

What Gets Excluded

The recommended coverage.runsettings excludes:

Pattern Reason
[*.Tests]* Test assemblies aren't production code
[*.Benchmark]* Benchmark projects
[*.Migrations]* Database migrations (generated)
GeneratedCodeAttribute Source generators
CompilerGeneratedAttribute Compiler-generated code
ExcludeFromCodeCoverageAttribute Explicit developer opt-out
*.g.cs, *.designer.cs Generated files
*.razor.g.cs Blazor component generated code
*.razor.css.g.cs Blazor CSS isolation generated code
**/Migrations/**/* EF Core migrations (auto-generated)
SkipAutoProps Auto-properties (trivial branches)

When to Update Thresholds

Lower thresholds temporarily for:

  • Legacy codebases being modernized (document in README)
  • Generated code that can't be modified
  • Third-party wrapper code

Never lower thresholds for:

  • "It's too hard to test" - refactor instead
  • "We'll add tests later" - add them now
  • New features - should meet standards from the start

Additional Resources