VirtusLab
Level 0

The Anatomy

Workspace, packages, labels, and Starlark

Read a Bazel repository with confidence: find the root, identify packages, understand labels, and scan BUILD files as declarations instead of scripts.

The Anatomy cover

Level 0 teaches you how to enter a Bazel repository and understand what you are looking at. Before commands are useful, the checkout needs coordinates: where the repository starts, which package owns a file, how targets are named, and why a BUILD file is not an ordinary script.

Build The Repository Coordinates

0.1 Filesystem Hierarchy gives the physical map. Bazelisk can read .bazelversion to select the requested Bazel version. Repository markers define the root, BUILD files divide the tree into packages, and the output tree keeps generated artifacts outside the source directories you edit.

0.2 Language of Labels turns that map into addresses. The same notation family shows up in dependency lists, command-line target patterns, visibility rules, package groups, and later query output. Once //pkg:target, :target, and //... become coordinates instead of punctuation, many errors get easier to read.

0.3 Starlark Syntax Basics teaches the BUILD-file reading habit. The syntax may look Python-shaped, but the useful first pass is structural: what was loaded, which package defaults apply, which targets exist, and what their attributes mean.

Follow The Coordinates Into A BUILD File

Use one final repository fragment to combine the level's separate ideas:

demo/
├── MODULE.bazel
├── app/
│   ├── BUILD.bazel
│   ├── Main.java
│   └── settings.json
└── lib/
    ├── BUILD.bazel
    └── Message.java
# //app/BUILD.bazel
load("@rules_java//java:defs.bzl", "java_binary")

java_binary(
    name = "app",
    srcs = ["Main.java"],
    deps = ["//lib:message"],
    data = ["settings.json"],
)

# //lib/BUILD.bazel
load("@rules_java//java:defs.bzl", "java_library")

java_library(
    name = "message",
    srcs = ["Message.java"],
    visibility = ["//app:__pkg__"],
)

Before revealing the answer, identify the repository root, both packages, the full label of each rule target, the dependency direction, and the distinct roles of srcs, deps, and data. Then ask what would change if app/config/BUILD.bazel were added around a file previously owned by //app.

Check the anatomy

MODULE.bazel marks the root. //app and //lib are packages because each has a BUILD.bazel. Their rule targets are //app:app and //lib:message, and the first may be shortened to //app. The dependency points from the application to the library. Main.java and Message.java are direct build inputs in srcs. //lib:message is a target dependency in deps. settings.json is a runtime file in data. The library permits clients from exactly the //app package. Adding a nested BUILD.bazel creates a new package: files beneath it stop belonging to //app, so their labels and any parent globs or relative references must follow the new boundary.

key takeaway

Finding the repository root and package boundary, expanding a canonical label, distinguishing files from rule targets, and reading a BUILD declaration structurally reveal the graph that later Bazel commands address.

Next: use those coordinates to build, test, run, and inspect targets in 1.1 CLI Survival Kit.