---
name: breaking-change-analysis
description: "Assesses the impact of API changes before implementation to understand what code would break and plan appropriate migration paths. Use when removing methods, changing interfaces, or planning deprecations."
---

# Breaking Change Analysis Workflow

Assess the impact of API changes before implementation. Use when removing methods,
changing method signatures, altering return types/values, changing exception types,
or modifying default behavior.

## Contents

- [How to use this skill](#how-to-use-this-skill)
- [Related skills](#related-skills)
- [Step 1: Identify the Change Scope](#step-1-identify-the-change-scope)
- [Step 2: Find All Usages](#step-2-find-all-usages)
- [Step 3: Assess and Document Impact](#step-3-assess-and-document-impact)
  - [Prove the safety claim](#prove-the-safety-claim)
- [Step 4: Deprecation policy](#step-4-deprecation-policy)

## How to use this skill

Attach this file to your Copilot Chat context, then invoke it with the specific
API/method change you are considering. Use this workflow before coding to assess
impact and plan migration.

## Related skills

- [Development Workflow](../development-workflow/SKILL.md) — implement required
   changes using strict TDD

## Step 1: Identify the Change Scope

1. Determine which classes and methods are affected.
2. Check API visibility (`@api public` vs `@api private` in YARD docs).
3. Check if the change affects `Git::Repository` (the current facade layer, modules under `lib/git/repository/`).

## Step 2: Find All Usages

1. **Internal usages:**

   ```bash
   grep -rn "method_name" lib/ spec/
   ```

2. **External usage (if applicable):**

   ```bash
   gh search code "Git::Repository#method_name language:ruby"
   ```

3. **Downstream gems:** `reverse-dependencies.sql` in this directory lists every gem
   that depends on `git`, one row per gem, showing only its most recent version.

   Code search finds call sites; this finds the projects that would have to react to a
   release. Each row gives the dependent gem's name, its latest version and release
   date, the version requirement it declares against `git`, and its source or homepage
   URL. The results are ordered newest release first, so gems still under active
   maintenance — the ones a deprecation notice can actually reach — sort to the top.

   The `WHERE` clause selects the requirements that a new major would affect: `~> 4.%`
   pins, which stop receiving updates, and open-ended `>= ` requirements, which pick up
   the new major immediately whether or not the gem is ready for it. **Edit those
   patterns to match the series you are changing** — they are pinned to the v4.x
   analysis they were written for.

   Run it against a restored copy of the public RubyGems.org database dump
   (<https://rubygems.org/pages/data>); the table names are that schema's.

## Step 3: Assess and Document Impact

Produce an impact assessment:

```markdown
## Breaking Change Impact Assessment

### Change Description
[What is being changed]

### Affected API
- Class: `Git::SomeClass`
- Method: `#some_method`
- Current signature: `def some_method(arg, opts = {})`
- Proposed signature: `def some_method(arg, force: false)`

### Internal Impact
- Files affected: X
- Tests to update: Y

### External Impact
- Severity: [High/Medium/Low]
- Migration difficulty: [Easy/Medium/Hard]

### Safety Proof
[The single fact each "safe" verdict rests on, and the code that was run to prove it]

### Migration Path
[How users should update their code]
```

### Prove the safety claim

Every "this looks risky but is actually safe" verdict in the assessment rests on some
single fact — an option no caller passes, output no parser depends on, behavior
identical across the supported git range. Isolate that fact, then prove it by running
real code: a spec exercising the old and new behavior, a console session against a
fixture repository, or a query against the RubyGems dump showing no dependent gem is
affected. Record the fact and its proof in the **Safety Proof** section of the
assessment. A safety claim backed only by reasoning is an open question, not a
finding — the step is complete when every such claim names its fact and the code that
proved it. This proof applies to hard breaks and behavior changes that have no
deprecation path. A change to the class of a raised exception is such a behavior change
unless it passes the rescue-compatibility test in
[Branch & PR Strategy](../../copilot-instructions.md#branch--pr-strategy). The
deprecation policy in [Step 4](#step-4-deprecation-policy) governs removal of a
deprecated API.

## Step 4: Deprecation policy

**Removal gate.** A removal PR merges to main only when its deprecation warning and
`UPGRADING.md` entry are contained in a previous normal release. Once any removal has
merged to main, main becomes the release line for the next major version. If another
release of the previous major is needed, it is cut from a branch created for that major
(e.g. `4.x` or `5.x`). "Normal release" is semver's term for a non-pre-release version.

The gate sets the earliest major a removal may land in, not the one it must land in. A
removal may land in any major after the gate is met; which one is a roadmap decision
recorded on the API's issue.

**What a deprecation ships.** All of the following land in the same minor release:

- A runtime warning via `Git::Deprecation.warn` that names the replacement. A YARD tag
  alone does not count. Do not use ActiveSupport's `deprecate_methods`; it bakes the
  horizon into the message.
- The replacement API.
- An `UPGRADING.md` entry that agrees with the warning.
- A `@deprecated` YARD tag with migration guidance.

**Warning wording.** When the removing major is decided:

```ruby
Git::Deprecation.warn(
  'Git::Author is deprecated and will be removed in v6.0.0. Use Git::AuthorInfo instead.'
)
```

When it is not yet decided:

```ruby
Git::Deprecation.warn(
  'Git::Author is deprecated and will be removed in a future major release. ' \
  'Use Git::AuthorInfo instead.'
)
```

Deciding or changing the named major later is a documentation change that ships in a
minor. Update the warning, the YARD tag, and the `UPGRADING.md` entry together.

**When to deprecate.** Add a warning only when removal in a future major is intended.
An API that will be kept but discouraged is documented as legacy with no warning; the
hollow shells in
[ADR-0002](../../../docs/adr/0002-commit-tree-and-blob-become-hollow-shells.md) are
the precedent. Removing a warning (un-deprecating) is a non-breaking change.

[ADR-0007](../../../docs/adr/0007-removals-require-one-normal-release-of-deprecation-not-calendar-soak.md)
records the decision and its rationale.

**Commit requirements:**

- Mark removal commits with `!` and include a `BREAKING CHANGE:` footer
- DO NOT update CHANGELOG.md — it is auto-generated from commit messages
