# Finding unused targets with bazel

DevFeed: [Finding unused targets with bazel](<https://devfeed.tech/articles/finding-unused-targets-with-bazel-25411.md>)

Original publisher: [Read original article](<https://smileykeith.com/2025/03/24/unused-bazel-targets/>)

Author: Keith Smiley

Published: 2025-03-24T18:00:00Z

Content type: tutorial

Language: en

Sources: [Keith Smiley](<https://devfeed.tech/sources/keith-smiley.md>)

Topics: [Graphs](<https://devfeed.tech/topics/graphs.md>), [coding](<https://devfeed.tech/topics/coding.md>), [Code](<https://devfeed.tech/topics/code.md>), [toolchains](<https://devfeed.tech/topics/toolchains.md>)

Tags: [bazel](<https://devfeed.tech/tags/bazel.md>), [build](<https://devfeed.tech/tags/build.md>), [code](<https://devfeed.tech/tags/code.md>), [coding](<https://devfeed.tech/tags/coding.md>), [dependencies](<https://devfeed.tech/tags/dependencies.md>), [graph](<https://devfeed.tech/tags/graph.md>), [toolchains](<https://devfeed.tech/tags/toolchains.md>), [use-cases](<https://devfeed.tech/tags/use-cases.md>)

## AI overview

This article explains how to use bazel query to inspect a build graph and identify unused targets. It presents a query that subtracts dependencies of top-level targets from all first-party targets, then discusses handling edge cases with kind filters and special tags such as allow-unused. It also describes tagging toolchains and other targets whose dependencies should be treated as used.

## Source excerpt

Once you've fully migrated a codebase to bazel, one of the many advantages is that you can easily inspect your build graph using bazel query. One of the many things you can do with queries is write scripts to enforce coding standards, or in today's example, find unused targets that can lead to discovering unused code. The simplest version of this query starts like this: let all_targets = //... in let top_level_targets = tests($all_targets) union kind("(.*_binary) rule", $all_targets) in $all_targets - deps($top_level_targets) This initial version discovers all your first party targets, and then subtracts the dependencies of all "top level" targets, so that any remaining targets are considered unused. The idea of "top level" targets is better defined as: anything that you consider to be important enough that its dependencies are used. Depending on your codebase you might want to exclude test targets from this so that targets only in the dependencies of test targets are diagnosed as unused (this won't work if you have intentional testonly dependencies, although you could special case those as shown below). Once you have this initial query, you can start iterating in order to handle in edge cases. For example it's likely that you have some targets that aren't binaries or tests, but are considered used. There are 2 approaches I would recommend to handle this. First you can continue to build out the kind filter: kind("(.*_binary|platform|test_suite) rule", $all_targets) The downside with this approach is it can get unwieldy quickly. I like adding rules here that have many uses, but for other one off cases another approach you can use is to expand the query to look for special tags: let all_targets = //... in let top_level_targets = tests($all_targets) union kind("(.*_binary|platform|test_suite) rule", $all_targets) in let allowed_unused = attr(tags, allow-unused, $all_targets) in $all_targets - deps($top_level_targets) - $allowed_unused Then you can add tags = ["allow-un