# MSBuild Chain Extension Pattern: Why You Should Never Overwrite DependsOn Properties

> Learn why you must append to MSBuild DependsOn properties instead of overwriting. Discover how overwriting breaks build chains and silently removes SDK targets.

- Repository: [.NET Platform/skills](https://github.com/dotnet/skills)
- Tags: best-practices
- Published: 2026-07-11

---

**You must append to `$(…DependsOn)` properties using the syntax `$(CompileDependsOn);YourTarget` rather than overwriting them, because overwriting silently removes SDK-provided targets and breaks the build chain.**

The `dotnet/skills` repository documents a critical pattern for extending MSBuild projects without corrupting the default build pipeline. MSBuild organizes compilation through a three-level target chain where **property-based dependency lists** like `BuildDependsOn`, `CompileDependsOn`, and `CoreBuildDependsOn` control execution order. Understanding how to safely extend these chains prevents subtle bugs that cause missing compile steps, broken incremental builds, and lost SDK functionality.

## The Three-Level Target Chain and Property-Based Dependencies

MSBuild executes the build process as a hierarchical chain: **Build** → **CoreBuild** → **Clean**. Each level relies on semicolon-separated properties—such as `BuildDependsOn`, `CompileDependsOn`, and `CoreBuildDependsOn`—that contain ordered lists of target names. These properties act as extensible hooks defined in [`plugins/dotnet-msbuild/skills/target-authoring/SKILL.md`](https://github.com/dotnet/skills/blob/main/plugins/dotnet-msbuild/skills/target-authoring/SKILL.md), allowing the SDK to declare default steps while permitting custom targets to join the sequence.

When MSBuild evaluates a target like `Compile`, it expands the `CompileDependsOn` property to determine which targets must execute first. This design creates a **chain extension pattern** where multiple imports can contribute steps to the same logical phase without knowing about each other.

## The Correct MSBuild Chain Extension Pattern

### Appending to DependsOn Properties (Correct Approach)

Always preserve the existing property value when adding custom targets. According to the guidance in [`plugins/dotnet-msbuild/skills/target-authoring/SKILL.md`](https://github.com/dotnet/skills/blob/main/plugins/dotnet-msbuild/skills/target-authoring/SKILL.md), the canonical pattern uses property reference syntax:

```xml
<PropertyGroup>
  <!-- ✅ Correct – preserves SDK compile steps and adds custom logic -->
  <CompileDependsOn>$(CompileDependsOn);MyCodeGenTarget</CompileDependsOn>
</PropertyGroup>

```

This approach maintains the full SDK chain—including `CoreCompile`, `GenerateAssemblyInfo`, and incremental-build tracking—while inserting your target at the end of the sequence.

### Overwriting DependsOn Properties (The Anti-Pattern)

Overwriting the property destroys the chain:

```xml
<PropertyGroup>
  <!-- ❌ Wrong – drops all SDK compile steps, causing build failures -->
  <CompileDependsOn>MyCodeGenTarget</CompileDependsOn>
</PropertyGroup>

```

When you overwrite `CompileDependsOn`, MSBuild no longer executes built-in targets like `CoreCompile` or registers file writes for incremental builds. The project may fail to compile entirely, or silently skip essential tasks such as assembly info generation and error handling. This pattern is documented as unsafe in [`plugins/dotnet-msbuild/skills/property-patterns/SKILL.md`](https://github.com/dotnet/skills/blob/main/plugins/dotnet-msbuild/skills/property-patterns/SKILL.md).

## When to Use DependsOnTargets, BeforeTargets, and AfterTargets

The MSBuild chain extension pattern differs from other dependency mechanisms. Choose the right approach based on ownership and injection point:

- **DependsOnTargets**: Define this attribute on the target that *requires* the dependency. Use it when the target explicitly cannot run without another target completing first.
- **BeforeTargets**: Define this on the *injecting* target to execute custom logic immediately before a specific target you do not own.
- **AfterTargets**: Define this on the *injecting* target to execute custom logic immediately after a specific target you do not own.

Appending to `$(…DependsOn)` is preferred when you own the `.props` or `.targets` file and want your target to participate in the same logical step as the SDK. Use `BeforeTargets` or `AfterTargets` when you cannot modify the original property chain—for example, when consuming third-party SDKs where you do not control the import order.

## Complete Example: Extending the Compile Pipeline

The following implementation from `dotnet/skills` demonstrates safe extension of the compile chain with code generation:

```xml
<!-- MyProject.targets -->
<Project>
  <PropertyGroup>
    <!-- Extend the compile chain – keep SDK logic intact -->
    <CompileDependsOn>$(CompileDependsOn);MyCodeGenTarget</CompileDependsOn>
  </PropertyGroup>

  <Target Name="MyCodeGenTarget"
          DependsOnTargets="_ValidateMyFeatureInputs"
          Inputs="@(MyFeatureInput)"
          Outputs="$(IntermediateOutputPath)MyFeature.generated.cs">
    <Exec Command="my-tool.exe -o $(IntermediateOutputPath)MyFeature.generated.cs" />
    <ItemGroup>
      <Compile Include="$(IntermediateOutputPath)MyFeature.generated.cs" />
      <FileWrites Include="$(IntermediateOutputPath)MyFeature.generated.cs" />
    </ItemGroup>
  </Target>

  <Target Name="_ValidateMyFeatureInputs">
    <Error Text="MyFeatureInput items are required."
           Condition="'@(MyFeatureInput)' == ''" />
  </Target>
</Project>

```

If you cannot modify `CompileDependsOn` directly, inject a pre-compile step using `BeforeTargets`:

```xml
<Target Name="MyPreCompile" BeforeTargets="CoreCompile">
  <!-- Custom pre-compile work executes before CoreCompile -->
</Target>

```

## Summary

- **Always append** to `$(…DependsOn)` properties using the syntax `$(PropertyName);YourTarget` to preserve SDK functionality.
- **Never overwrite** `BuildDependsOn`, `CompileDependsOn`, or `CoreBuildDependsOn`, as this removes critical targets and breaks incremental builds.
- Use **`BeforeTargets`** and **`AfterTargets`** when you cannot modify the property chain directly.
- Reference [`plugins/dotnet-msbuild/skills/target-authoring/SKILL.md`](https://github.com/dotnet/skills/blob/main/plugins/dotnet-msbuild/skills/target-authoring/SKILL.md) and [`plugins/dotnet-msbuild/skills/property-patterns/SKILL.md`](https://github.com/dotnet/skills/blob/main/plugins/dotnet-msbuild/skills/property-patterns/SKILL.md) for authoritative guidance on target authoring.

## Frequently Asked Questions

### What happens if I overwrite CompileDependsOn instead of appending to it?

Overwriting `CompileDependsOn` replaces the entire semicolon-separated list with your single target, causing MSBuild to skip all SDK-provided compile steps such as `CoreCompile` and `GenerateAssemblyInfo`. This results in failed builds, missing assembly attributes, and broken incremental build tracking because the compiler never executes.

### When should I use BeforeTargets instead of appending to a DependsOn property?

Use `BeforeTargets` when you need to inject logic before a specific target but cannot modify the property chain—such as when consuming third-party SDKs or when your custom targets file is imported after the SDK sets the `DependsOn` properties. `BeforeTargets` requires no knowledge of the existing chain structure.

### Can I append multiple targets to a DependsOn property?

Yes. You can append multiple targets by extending the semicolon-separated list: `$(CompileDependsOn);Target1;Target2;Target3`. Each target executes in the order listed, after all SDK targets complete.

### Where is the official documentation for this pattern?

The official pattern is documented in the `dotnet/skills` repository at [`plugins/dotnet-msbuild/skills/target-authoring/SKILL.md`](https://github.com/dotnet/skills/blob/main/plugins/dotnet-msbuild/skills/target-authoring/SKILL.md) and [`plugins/dotnet-msbuild/skills/property-patterns/SKILL.md`](https://github.com/dotnet/skills/blob/main/plugins/dotnet-msbuild/skills/property-patterns/SKILL.md), which describe the three-level chain, safe property extension, and the distinction between `DependsOnTargets`, `BeforeTargets`, and `AfterTargets`.