# Square Anvil for Kotlin Dagger dependency graphs

> Anvil is a Kotlin compiler plugin and Gradle plugin that merges contributed Dagger modules and component interfaces, while also generating bindings and multibindings. It fits established K1 projects, but its deprecation and K2 boundary make it unsuitable for builds that require Kotlin K2.

**square/anvil** — A Kotlin compiler plugin to make dependency injection with Dagger 2 easier.

- Repository: https://github.com/square/anvil
- Stars: 1,401 · Forks: 88
- Language: Kotlin
- License: Apache-2.0
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/square-anvil

## What Anvil changes in a Dagger component

Anvil targets Kotlin projects that use Dagger 2 and want a component to discover dependency graph pieces without listing every module and parent interface by hand. It is built for application or library modules that contribute injectable types, Dagger modules, or component interfaces. The project combines a Gradle plugin with a Kotlin compiler plugin. The Gradle plugin configures the compiler integration and annotation dependencies, while the compiler plugin performs the merge during compilation.

The normal Dagger pattern requires the real component to declare its modules and parent interfaces. Anvil changes that declaration into a scope-based declaration. For example, an application component can be annotated with `@MergeComponent(AppScope::class)`. An annotated module or interface is then associated with `AppScope` through `@ContributesTo`. The generated interface seen by Dagger is equivalent to a component annotated with `@Component`, with the contributed module included and the contributed interface extended.

This mechanism is useful when a dependency graph is assembled from several libraries. Each library can contribute parts of the graph on its own compile classpath, and the final application component can collect those parts without rewriting a central module list. The benefit is not general Dagger compilation by itself. Anvil specializes in graph composition and binding generation, leaving Dagger responsible for processing the generated component after Anvil has assembled it.

## Anvil scopes and classpath discovery

An Anvil scope is a marker class used to connect a merged component with matching contributions. The README explicitly separates these marker scopes from Dagger scopes. Defining `AppScope` does not make the component singleton. A Dagger scope such as `@Singleton` still has to be applied to the merged component when that component should use Dagger singleton semantics.

Contributions are discovered from classes on the compile classpath. That boundary matters for multi-module builds. A type can affect a graph only when the relevant module and its Anvil annotations are visible where the graph is compiled. Applying the Gradle plugin only in the final component module is therefore insufficient if contributing modules are expected to supply graph elements. The README says the plugin must be applied in every module that either contributes classes to the dependency graph or merges them.

Anvil also generates binding declarations. `@ContributesBinding(AppScope::class)` on `RealAuthenticator` can replace a hand-written Dagger module whose only purpose is to bind that implementation to `Authenticator`. Qualifiers are retained, so a qualifier such as `@Named("Prod")` appears on the generated binding. `@ContributesMultibinding` generates a set multibinding by default. If the implementation also carries a map key annotation, Anvil generates a map multibinding with that key instead.

The scope marker does not create bindings or impose Dagger scoping. It routes existing declarations, generated bindings, and exclusions into a specific graph assembly. This separation gives the plugin a clear internal contract, although it also requires developers to understand two kinds of scope in the same codebase.

## Installing Anvil across a Kotlin Gradle build

The README documents application through the `com.squareup.anvil` Gradle plugin. Apply it in each module that contributes graph elements and in each module that merges them. The documented plugin block is:

```groovy
plugins {
  id 'com.squareup.anvil' version "${latest_version}"
}
```

The README uses the symbolic `${latest_version}` expression rather than embedding a fixed release number. Before using the block, replace that expression with the version selected for the build. The Gradle plugin adds the Kotlin compiler plugin and the required annotation dependencies.

A legacy application route is also documented through `buildscript`, `mavenCentral()`, and the `gradle-plugin` classpath. New code should avoid selecting that route without a project-specific reason.

After application, the first meaningful use is to mark a module or component interface with `@ContributesTo` and create the real component with `@MergeComponent`. The next compilation step should let Anvil find the contribution on the compile classpath and generate the Dagger annotation on the merged component. The repository includes `sample/`, with separate `sample/app/`, `sample/library/`, and `sample/scopes/` modules. The README does not give a command for running that sample, so no additional task name should be inferred.

## Generated bindings and multibindings

The quickest way to understand Anvil is through a binding that would otherwise occupy a dedicated module. Suppose an application exposes an `Authenticator` interface and injects `RealAuthenticator` as its implementation. The README first presents an `AuthenticatorModule` with a Dagger `@Binds` method, then shows the same relationship through `@ContributesBinding`:

```kotlin
interface Authenticator

@ContributesBinding(AppScope::class)
class RealAuthenticator @Inject constructor() : Authenticator
```

Anvil can generate the binding method and add it to `AppScope`. This removes a module whose sole responsibility is that binding declaration. It does not replace `@Inject` on the constructor or decide that Dagger singleton behavior is required.

Qualifiers survive this transformation. When `RealAuthenticator` has `@Named("Prod")`, Anvil preserves it on the generated binding. Multibindings follow a similar path. An annotated class normally produces an `@IntoSet` binding:

```kotlin
@ContributesMultibinding(AppScope::class)
@Named("Prod")
class MainListener @Inject constructor() : Listener
```

The README states that a class carrying a map key annotation produces an `@IntoMap` multibinding instead. The map key value identifies the generated entry, while the qualifier remains available for distinguishing bindings.

These annotations are appropriate when each implementation needs to enter a shared graph without a central module. They are less suitable when a binding requires custom factory logic, additional parameters that Anvil does not infer, or graph-specific assembly behavior beyond the documented qualifier and multibinding cases.

## Replacing modules and excluding graph contributions

Anvil has two exclusion mechanisms for different graph needs. The first is replacement through `@ContributesTo`. An annotated replacement module names the classes it supersedes, and Anvil omits the replaced class from participating graphs. The README presents instrumentation modules as the main use case: a test module can provide a different endpoint selector without creating duplicate bindings against the normal application module.

Replacement is global for applications that include the relevant classes on their compile classpaths. It is not confined to one component declaration. That makes it convenient for variants such as development or test builds, but it also means a replacement declaration can affect multiple merged components whenever both modules are visible. This is a design constraint rather than a local editing feature.

The second mechanism is component-level exclusion through the `exclude` argument on `@MergeComponent`. The README shows `DaggerModule::class` in that list and states that the exclusion affects the individual application without changing other applications. It describes this as an escape for imperfect build graphs involving legacy modules, incorrect imports, or deeply nested dependencies.

The distinction is important during review. Replacement expresses precedence between graph contributions, while `exclude` removes a contribution for one merged component. The README does not document rollback for a mistaken declaration, so graph decisions still require compilation checks and inspection.

## Anvil's K1 boundary and Dagger 2 trade-off

The README carries a direct deprecation warning: Anvil is deprecated, will only be supported for K1 going forward, and will stop development when K2 becomes a requirement. This is more significant than the repository's archive status or release cadence. The repository is not archived, and the last push was on 2026-09-29, but the project's stated support policy excludes K2 from the forward path.

A Dagger project that relies on Anvil must therefore account for eventual compiler migration or replacement of the merge layer. The README does not describe a K2 release, migration procedure, or compatibility flag. It links to the maintenance-mode issue for context, but the maintenance policy itself is enough to affect adoption planning.

Compared with conventional Dagger component declaration, Anvil removes repeated module lists and interface inheritance, but introduces a Kotlin-specific compiler plugin, marker scopes, classpath discovery rules, and generated annotations. Conventional Dagger requires more explicit wiring while keeping the component model visible in ordinary Kotlin annotations. That explicit model may be easier to diagnose for a small graph or a project that does not want compiler-plugin coupling.

Anvil is therefore a reasonable fit for an established K1 Dagger codebase whose graph is distributed across libraries. It is the wrong choice when K2 support is an immediate requirement, when plugin integration cannot be accepted, or when avoiding generated component declarations is a primary design constraint.

## Licensing, releases, and upgrade cost

Anvil is distributed under the Apache-2.0 license. The repository places that identifier in `LICENSE.txt`. Consumers should review that file and the applicable dependency notices for the specific build, but this article does not provide legal advice or infer obligations beyond the stated license.

The repository is organized into separate Gradle plugin, compiler plugin, annotations, utility, and integration-test components. Adopting it as build infrastructure spreads changes across several modules. Applying Anvil in every contributing module broadens the affected build surface, so upgrading it requires compatibility checks for the Kotlin, Dagger, annotation, and compiler-plugin versions used by the project. The README does not publish an upgrade matrix, so compatibility must be established from the selected release and the actual build.

The latest listed release is `v2.7.0` from 2025-10-03, following `v2.6.1` from 2025-07-18 and `v2.6.0` from 2025-07-11. The repository's last push was on 2026-09-29. Neither fact overrides the documented K1-only support policy. For a new deployment, the maintenance cost includes pinning a compatible Anvil version, keeping K1 in place, reviewing scope and replacement declarations, and budgeting for the eventual move away from Anvil.

## Conclusion

Teams maintaining a K1-based Dagger 2 application can use Anvil when classpath-based graph discovery removes meaningful boilerplate, but they should not choose it for a new K2-oriented build. Before adoption, verify that the build uses K1, inspect every scope and replacement declaration, and account for the migration work required when Anvil development ends with K2. For Anvil, K1 compatibility is the decisive adoption boundary.

## FAQ

### What does Square Anvil do for Dagger 2?

Anvil automatically merges Dagger modules and component interfaces contributed to the same scope. It can also generate binding and multibinding methods for annotated classes.

### Does Square Anvil support Kotlin K2?

No. The README says Anvil is deprecated, will only be supported for K1 going forward, and will stop development when K2 becomes required.

### Where must the Square Anvil Gradle plugin be applied?

The README requires it in every module that contributes classes to the dependency graph or merges them. It adds the Kotlin compiler plugin and annotation dependencies.

### Are Anvil scopes the same as Dagger scopes?

No. Anvil scope classes are marker classes used for graph assembly. A Dagger scope such as `@Singleton` must still be applied when singleton behavior is required.

### How does Anvil handle test module replacement?

A contributed module can name another module in `replaces`. Anvil then omits the replaced class from graphs containing both classes, which has a global effect across applications that include them.

## Sources

- [Issues](https://github.com/square/anvil/issues)
- [License: Apache-2.0](https://github.com/square/anvil/blob/main/LICENSE)
- [README](https://github.com/square/anvil/blob/main/README.md)
- [Releases](https://github.com/square/anvil/releases)
- [square/anvil on GitHub](https://github.com/square/anvil)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/square-anvil
