1. Purpose

Canon’s 4-layer architecture provides powerful flexibility, but this can be overwhelming. This guide helps you choose the right configuration for your use case through decision trees, use case scenarios, and practical recommendations.

2. Quick Decision Tree

graph TD
    Start[What are you comparing?] --> Similar{Similar<br/>structure?}
    Similar -->|Yes| Fast{Need<br/>speed?}
    Similar -->|No| Semantic[Use Semantic Algorithm]

    Fast -->|Yes| DOM[Use DOM Algorithm]
    Fast -->|No| Semantic

    DOM --> Format{Care about<br/>formatting?}
    Semantic --> Format

    Format -->|Yes| Strict[strict profile]
    Format -->|No| SpecFriendly[spec_friendly profile]

    Strict --> Output1[by_line mode]
    SpecFriendly --> Output2{Want<br/>operations?}

    Output2 -->|Yes| ByObject[by_object mode]
    Output2 -->|No| ByLine[by_line mode]

    style DOM fill:#fff4e1
    style Semantic fill:#e1f5ff
    style Strict fill:#ffe1f5
    style SpecFriendly fill:#ffe1f5
    style ByObject fill:#e1ffe1
    style ByLine fill:#e1ffe1

3. Layer-by-Layer Decision Guide

3.1. Layer 1: Preprocessing

Question: How should documents be normalized before comparison?

Choose When Example

none

Documents already in comparable form, no normalization needed

Comparing canonicalized XML files

c14n

Testing XML canonicalization implementations

Validating C14N output

normalize

Whitespace differences are irrelevant

Comparing generated vs handwritten XML

format

Want to compare structure, ignore all formatting

Comparing minified vs formatted JSON

Default: none (no preprocessing)

Ruby API:

Canon::Comparison.equivalent?(doc1, doc2,
  preprocessing: :normalize  # or :c14n, :format
)

CLI:

canon diff file1.xml file2.xml --preprocessing normalize

3.2. Layer 2: Algorithm Selection

Question: What comparison strategy fits your documents?

Choose When Characteristics

dom

• Similar document structure
• Traditional diff workflow
• Speed is important
• Production use (stable)

• Fast
• Position-based
• No move detection
• Well-tested

semantic

• Restructured documents
• Need move detection
• Operation analysis needed
• Experimental OK

• Slower
• Signature-based
• Detects moves
• Experimental

Default: dom (stable algorithm)

Decision Matrix:

Scenario DOM Semantic

Documents have same structure

Documents are reordered

Need fast comparison

Need move detection

Production use

Large documents (> 100KB)

Ruby API:

Canon::Comparison.equivalent?(doc1, doc2,
  diff_algorithm: :dom  # or :semantic
)

CLI:

canon diff file1.xml file2.xml --diff-algorithm semantic

3.3. Layer 3: Match Options

Question: How strict should comparison be?

Profile Use When

strict

Exact matching required. Everything must match exactly including whitespace, attribute order, comments.

rendered

Comparing rendered output. Simulates browser/CSS rendering - ignores formatting but keeps content strict.

spec_friendly

Writing tests. Ignores formatting differences, focuses on content and structure.

content_only

Content comparison only. Ignores all structural and formatting differences.

Default: strict (exact matching)

Ruby API:

Canon::Comparison.equivalent?(doc1, doc2,
  match_profile: :spec_friendly  # or :strict, :rendered, :content_only
)

CLI:

canon diff file1.xml file2.xml --match-profile spec_friendly

3.3.2. Custom Match Dimensions

For fine-grained control, configure individual dimensions:

Canon::Comparison.equivalent?(doc1, doc2,
  match: {
    text_content: :normalize,           # normalize, strict, ignore
    structural_whitespace: :ignore,     # ignore, normalize, strict
    attribute_order: :ignore,           # ignore, strict (XML/HTML)
    attribute_values: :normalize,       # normalize, strict, ignore
    comments: :ignore,                  # ignore, normalize, strict
    whitespace_type: :strict            # strict (default), normalize
  }
)

Remember: Match options behave differently with each algorithm! See Algorithm-Specific Behavior.

3.3.3. Whitespace Type Sensitivity

By default, Canon distinguishes between different Unicode whitespace types (e.g. regular space U+0020 vs non-breaking space U+00A0 vs ideographic space U+3000). This catches accidental insertion of wrong whitespace characters.

Use whitespace_type: :normalize when all Unicode whitespace variants should be treated as equivalent (e.g. when output from different tools may use different whitespace types for the same visual result).

3.4. Layer 4: Diff Formatting

Question: How should differences be displayed?

3.4.1. Choosing Diff Mode

Mode Best For Output Type

by_line

• Traditional diffs
• Code review
• Quick scanning
• DOM algorithm

Line-based diff similar to git diff

by_object

• Tree structure view
• Operation analysis
• Semantic algorithm

Tree-based with operations (INSERT, DELETE, UPDATE, MOVE)

Default: by_line for DOM, by_object for Semantic

Natural Fits: * DOM + by_line = Traditional positional diff * Semantic + by_object = Operation-based tree diff

Ruby API:

Canon::Comparison.equivalent?(doc1, doc2,
  diff_mode: :by_object,  # or :by_line
  verbose: true           # Enable diff output
)

CLI:

canon diff file1.xml file2.xml --diff-mode by-object --verbose

3.4.2. Visual Formatting Options

Canon::Comparison.equivalent?(doc1, doc2,
  verbose: true,
  use_color: true,              # Enable colors
  context_lines: 3,             # Lines of context
  diff_grouping_lines: 5,       # Group nearby changes
  show_legend: true             # Display symbol legend
)

4. Use Case Scenarios

4.1. Scenario 1: Unit Testing XML Generation

Requirement: Test that code generates correct XML, ignoring formatting

Configuration:

expect(actual_xml).to be_equivalent_to(expected_xml).with_options(
  preprocessing: :normalize,        # Ignore formatting differences
  diff_algorithm: :dom,             # Fast, stable
  match_profile: :spec_friendly,    # Test-friendly
  verbose: true                     # Show diffs on failure
)

Why: * normalize handles inconsistent whitespace * dom is fast and stable for tests * spec_friendly focuses on content, not formatting * verbose helps debug failures

4.2. Scenario 2: Comparing API Responses

Requirement: Compare JSON responses, key order doesn’t matter

Configuration:

Canon::Comparison.equivalent?(response1, response2,
  diff_algorithm: :dom,
  match: {
    key_order: :ignore,             # JSON key order irrelevant
    text_content: :normalize        # Normalize string values
  },
  verbose: true,
  diff_mode: :by_object             # Tree view of differences
)

Why: * key_order: :ignore handles JSON object key reordering * by_object shows structured diff * dom is sufficient for API responses

4.3. Scenario 3: Detecting Document Restructuring

Requirement: Find what changed when document is reorganized

Configuration:

result = Canon::Comparison.equivalent?(old_doc, new_doc,
  diff_algorithm: :semantic,        # Detect moves
  match_profile: :spec_friendly,    # Ignore formatting
  verbose: true,
  diff_mode: :by_object             # See operations
)

# Analyze operations
puts "Moves: #{result.statistics.moves}"
puts "Updates: #{result.statistics.updates}"

Why: * semantic algorithm detects moves and restructuring * by_object shows operation-level changes * Statistics provide quantitative analysis

4.4. Scenario 4: Code Review Diff

Requirement: Traditional diff for reviewing changes

Configuration:

canon diff old.xml new.xml \
  --diff-algorithm dom \
  --match-profile spec_friendly \
  --diff-mode by_line \
  --verbose \
  --use-color \
  --context-lines 3

Why: * dom + by_line gives traditional diff * context_lines provides context * Colors improve readability

4.5. Scenario 5: Canonicalization Testing

Requirement: Test C14N implementation

Configuration:

Canon::Comparison.equivalent?(doc, canonical_doc,
  preprocessing: :c14n,             # Apply canonicalization
  diff_algorithm: :dom,
  match_profile: :strict,           # Exact match required
  verbose: true
)

Why: * c14n preprocessing applies canonicalization * strict profile ensures exact match * Tests that canonicalization produces correct output

4.6. Scenario 6: Content-Only Comparison

Requirement: Compare only text content, ignore all structure

Configuration:

Canon::Comparison.equivalent?(doc1, doc2,
  preprocessing: :format,           # Normalize structure first
  diff_algorithm: :semantic,        # Better for structure-independent
  match_profile: :content_only,     # Ignore all structure
  verbose: true,
  diff_mode: :by_object
)

Why: * content_only profile ignores structure * semantic algorithm better at structure-independent comparison * format preprocessing normalizes before comparison

5. Layer Interaction Matrix

This table shows recommended configurations for common scenarios:

Use Case Layer 1 Layer 2 Layer 3 Layer 4 Notes

Unit tests (similar structure)

normalize

dom

spec_friendly

by_line

Fast, test-friendly

Unit tests (any structure)

normalize

semantic

spec_friendly

by_object

Handles restructuring

API response comparison

none

dom

custom

by_object

Configure key_order

Document evolution tracking

none

semantic

rendered

by_object

Detect operations

Code review

none

dom

strict

by_line

Traditional diff

C14N testing

c14n

dom

strict

by_line

Exact match

Content extraction testing

format

semantic

content_only

by_object

Structure-independent

Regression testing

normalize

dom

spec_friendly

by_line

Stable, fast

6. Common Configuration Patterns

6.1. Pattern 1: Fast Test Assertion

# Minimal configuration for speed
Canon::Comparison.equivalent?(expected, actual,
  match_profile: :spec_friendly
)
# Uses defaults: no preprocessing, dom algorithm, by_line output

6.2. Pattern 2: Comprehensive Analysis

# Full analysis with all features
result = Canon::Comparison.equivalent?(doc1, doc2,
  preprocessing: :normalize,
  diff_algorithm: :semantic,
  match_profile: :spec_friendly,
  verbose: true,
  diff_mode: :by_object,
  use_color: true,
  context_lines: 5,
  show_legend: true
)

# Access rich data
puts result.operations
puts result.statistics

6.3. Pattern 3: Strict Validation

# Exact match required
Canon::Comparison.equivalent?(doc1, doc2,
  preprocessing: :c14n,       # Canonicalize first
  match_profile: :strict,     # Exact matching
  verbose: true               # Show any differences
)

6.4. Pattern 4: Flexible Content Comparison

# Focus on content, ignore structure
Canon::Comparison.equivalent?(doc1, doc2,
  preprocessing: :normalize,
  diff_algorithm: :semantic,
  match_profile: :content_only,
  verbose: true
)

7. Anti-Patterns to Avoid

7.1. Anti-Pattern 1: Over-Configuration

# DON'T: Conflicting settings
Canon::Comparison.equivalent?(doc1, doc2,
  preprocessing: :c14n,              # Canonicalizes
  match: {
    structural_whitespace: :strict   # Conflicts with c14n
  }
)

# DO: Choose one approach
Canon::Comparison.equivalent?(doc1, doc2,
  preprocessing: :c14n               # Handles normalization
)

7.2. Anti-Pattern 2: Wrong Algorithm/Mode Combination

# SUBOPTIMAL: Loses semantic information
Canon::Comparison.equivalent?(doc1, doc2,
  diff_algorithm: :semantic,
  diff_mode: :by_line              # Doesn't show operations well
)

# BETTER: Use natural fit
Canon::Comparison.equivalent?(doc1, doc2,
  diff_algorithm: :semantic,
  diff_mode: :by_object            # Shows operations clearly
)

7.3. Anti-Pattern 3: Unnecessary Semantic Algorithm

# SLOW: Semantic not needed for similar documents
Canon::Comparison.equivalent?(doc1, doc2,
  diff_algorithm: :semantic         # Overkill if no restructuring
)

# FASTER: Use DOM for similar structures
Canon::Comparison.equivalent?(doc1, doc2,
  diff_algorithm: :dom              # Fast for similar docs
)

7.4. Anti-Pattern 4: Missing Verbose Flag

# DON'T: Can't see what's different
result = Canon::Comparison.equivalent?(doc1, doc2)
# result is just true/false

# DO: Enable verbose for debugging
result = Canon::Comparison.equivalent?(doc1, doc2,
  verbose: true
)
# result.diff shows actual differences

8. Performance Considerations

8.1. Performance Impact by Layer

Layer Low Impact Medium Impact High Impact

Layer 1

none

normalize, format

c14n (complex documents)

Layer 2

dom

semantic

Layer 3

Any profile

Complex custom dimensions

Layer 4

by_line

by_object (small docs)

by_object (large docs)

8.2. Optimization Guidelines

For Speed:

Canon::Comparison.equivalent?(doc1, doc2,
  preprocessing: :none,       # Skip preprocessing
  diff_algorithm: :dom,       # Fast algorithm
  match_profile: :strict,     # Simple matching
  diff_mode: :by_line        # Fast output
)

For Intelligence (accepting slower performance):

Canon::Comparison.equivalent?(doc1, doc2,
  preprocessing: :normalize,  # Normalize first
  diff_algorithm: :semantic,  # Intelligent algorithm
  diff_mode: :by_object      # Rich output
)

9. Migration Checklist

When changing configuration:

9.1. Changing Algorithm (DOM → Semantic)

  • Update diff_algorithm option

  • Consider changing diff_mode to by_object

  • Remove or update attribute_order expectations

  • Update test assertions for operation-based output

  • Accept slower performance

  • Review move detection impact

9.2. Changing Algorithm (Semantic → DOM)

  • Update diff_algorithm option

  • Consider changing diff_mode to by_line

  • Add attribute_order: :ignore if needed

  • Update test assertions for line-based output

  • Expect faster performance

  • Accept no move detection

9.3. Changing Match Profile

  • Review impact on existing tests

  • Understand what each dimension does

  • Test with sample documents

  • Update documentation

10. Using Configuration Profiles

If you use the same configuration across multiple gems or projects, consider using a configuration profile instead of repeating settings. A single line replaces dozens of configuration calls:

# Instead of 60+ lines of per-format settings:
Canon::Config.instance.profile = :metanorma

# Or load a custom profile from a local YAML file:
Canon::Config.instance.profile = "config/my_canon_profile.yml"

Profiles bundle all layers (preprocessing, match profile, diff settings, whitespace element lists) into a named preset defined in YAML. Custom file profiles can inherit from built-in profiles.

See Configuration Profiles for full documentation.

11. See Also