assessing-breaking-changes
Installation
SKILL.md
Backward Compatibility in MSBuild
Backward compatibility is the default — any change that could alter existing build behavior must be explicitly justified.
This skill covers how to evaluate compatibility risk. For the mechanics of ChangeWave implementation, see the changewaves skill.
Core Philosophy
- Existing builds must not break. If a project built successfully yesterday, it must build successfully today with identical semantics.
- New warnings are breaking changes. Builds that use
-WarnAsErroror<TreatWarningsAsErrors>true</TreatWarningsAsErrors>will fail if you introduce a new warning. Always gate new warnings behind a ChangeWave or emit them asMessageimportance instead. - Output changes are behavioral changes. Even "improvements" to output formatting, file paths, or diagnostic text can break downstream consumers that parse MSBuild output.
- Removal is nearly impossible. Never remove CLI switches, public APIs, or property names. Deprecate with warnings first, then gate removal behind a ChangeWave after multiple release cycles.
Blast Radius Checklist
Before merging any behavioral change, evaluate:
| Question | If Yes |
|---|---|
| Does this change what gets built or how? | ChangeWave required |
| Does this add a new warning? | ChangeWave required (WarnAsError impact) |
| Does this change a property default? | ChangeWave required (existing .csproj files depend on defaults) |
| Does this alter target execution order? | Test with real-world solutions; likely ChangeWave |
| Does this change console or binlog output format? | Consider downstream tool impact |
| Does this affect only internal code paths with no user-visible effect? | No ChangeWave needed |
| Is this a pure bug fix restoring documented behavior? | Usually no ChangeWave; use judgment on blast radius |
When ChangeWave Is NOT Needed
- Internal refactoring with no observable behavior change
- Performance improvements that don't change semantics
- New opt-in features gated by a new property or switch
- Bug fixes that restore clearly-intended behavior with limited blast radius
For detailed ChangeWave mechanics, see ChangeWaves-Dev.md and ChangeWaves.md.
The Warnings-as-Errors Rule
This is the most commonly missed compatibility concern:
<!-- Many enterprise builds set this globally -->
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
Any new MSBxxxx warning you add will break these builds. Options:
- Gate behind ChangeWave — preferred for genuinely important warnings
- Use
MessageImportance.LoworNormal— for informational diagnostics - Add as an error from the start — if the condition is always wrong
Deprecation Protocol
- Add a deprecation warning (behind ChangeWave if broad impact)
- Document the deprecation in release notes
- Maintain the old behavior for at least two major .NET versions
- Only remove after the ChangeWave has rotated out
Compatibility Test Matrix
When testing backward compatibility, verify:
- Multi-targeting projects —
<TargetFrameworks>net472;net8.0</TargetFrameworks> - Solution builds with mixed project types — C#, F#, VB, C++/CLI
- Incremental builds — second build should be a no-op
- Design-time builds — Visual Studio calls different target contracts
- Cross-platform — path separator differences, case sensitivity on Linux
- WarnAsError builds — explicitly test with
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
Decision Framework
Is the change user-visible?
├── No → Ship it (no ChangeWave needed)
└── Yes
├── Is it a new opt-in feature? → Ship it (no ChangeWave needed)
└── Does it change existing behavior?
├── Bug fix with low blast radius? → Ship it, add regression test
└── Behavioral change or new warning?
└── Gate behind ChangeWave, test opt-out path
Document the compatibility decision in your PR description so reviewers can validate it.