agentsclimarketplace

Bazel monorepo expert

Skill kinhluan/rules-quarkus-skills/.agent-skills/bazel-monorepo-expert

πŸ€– Complete AI expert ecosystem for Modern Java, Quarkus & Bazel development β˜•οΈβš‘οΈ Coverage for Vert.x, GraalVM, Maven/Gradle migration, and more πŸš€

Install
npx -y skills add kinhluan/rules-quarkus-skills --skill bazel-monorepo-expert

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Expert knowledge for managing large-scale Bazel monorepos with multiple services, shared libraries, and cross-cutting concerns. Use for workspace structure, visibility, and dependency management.

SKILL.md

13.2 KB, as published. Nobody here has run it

bazel-monorepo-expert

Keyword: monorepo | Platforms: gemini,claude,codex

Bazel Monorepo Expert Skill - Structuring, managing, and optimizing large-scale Bazel workspaces with multiple services, shared libraries, and complex dependency graphs.

Core Mandates

  • Single Source of Truth: One workspace for all services and shared libraries.
  • Strict Visibility: Default private, explicitly expose only public APIs.
  • Dependency Hygiene: No circular dependencies, no diamond dependency conflicts.
  • Consistent Tooling: Same Java version, same linter, same test framework across all packages.
  • Incremental Builds: Granular targets for fast incremental builds and cache hits.

Workspace Structure

Recommended Layout

my-monorepo/
β”œβ”€β”€ WORKSPACE.bazel (or MODULE.bazel)
β”œβ”€β”€ .bazelrc
β”œβ”€β”€ .bazelignore
β”œβ”€β”€ .bazelversion
β”œβ”€β”€ BUILD.bazel (root)
β”œβ”€β”€ platforms/
β”‚   └── BUILD.bazel
β”œβ”€β”€ toolchains/
β”‚   └── BUILD.bazel
β”œβ”€β”€ third_party/
β”‚   └── BUILD.bazel
β”œβ”€β”€ common/
β”‚   β”œβ”€β”€ utils/
β”‚   β”‚   β”œβ”€β”€ src/main/java/...
β”‚   β”‚   β”œβ”€β”€ src/test/java/...
β”‚   β”‚   └── BUILD.bazel
β”‚   β”œβ”€β”€ models/
β”‚   β”‚   └── BUILD.bazel
β”‚   └── proto/
β”‚       └── BUILD.bazel
β”œβ”€β”€ services/
β”‚   β”œβ”€β”€ user/
β”‚   β”‚   β”œβ”€β”€ src/main/java/...
β”‚   β”‚   β”œβ”€β”€ src/test/java/...
β”‚   β”‚   β”œβ”€β”€ src/main/resources/
β”‚   β”‚   β”œβ”€β”€ k8s/
β”‚   β”‚   β”œβ”€β”€ helm/
β”‚   β”‚   └── BUILD.bazel
β”‚   β”œβ”€β”€ order/
β”‚   β”‚   └── BUILD.bazel
β”‚   └── payment/
β”‚       └── BUILD.bazel
β”œβ”€β”€ libs/
β”‚   β”œβ”€β”€ auth/
β”‚   β”‚   └── BUILD.bazel
β”‚   β”œβ”€β”€ messaging/
β”‚   β”‚   └── BUILD.bazel
β”‚   └── observability/
β”‚       └── BUILD.bazel
└── tools/
    β”œβ”€β”€ buildifier/
    β”œβ”€β”€ linters/
    └── scripts/

Root BUILD.bazel

# BUILD.bazel (root)
exports_files([".bazelrc", "MODULE.bazel"])

# Test suite for all tests in the repo
test_suite(
    name = "all_tests",
    tests = [
        "//common/utils:all_tests",
        "//common/models:all_tests",
        "//services/user:all_tests",
        "//services/order:all_tests",
        "//services/payment:all_tests",
        "//libs/auth:all_tests",
        "//libs/messaging:all_tests",
    ],
)

# Build all services
filegroup(
    name = "all_services",
    srcs = [
        "//services/user:user-service_image",
        "//services/order:order-service_image",
        "//services/payment:payment-service_image",
    ],
)

Visibility Boundaries

Package-Level Visibility

# common/utils/BUILD.bazel
java_library(
    name = "utils",
    srcs = glob(["src/main/java/**/*.java"]),
    visibility = [
        "//services/...",  # All services
        "//libs/...",      # All libraries
        "//common/...",    # Other common packages
    ],
)

# Internal-only utility
java_library(
    name = "internal-utils",
    srcs = ["src/main/java/com/example/utils/InternalHelper.java"],
    visibility = ["//visibility:private"],  # Only this package
)

API vs Implementation

# libs/auth/BUILD.bazel
# Public API
java_library(
    name = "auth-api",
    srcs = ["src/main/java/com/example/auth/api/*.java"],
    visibility = ["//visibility:public"],
    deps = ["@maven//:jakarta_inject"],
)

# Implementation (not exposed)
java_library(
    name = "auth-impl",
    srcs = glob(["src/main/java/com/example/auth/impl/*.java"]),
    visibility = ["//libs/auth:__subpackages__"],  # Only within auth
    deps = [
        ":auth-api",
        "@maven//:io_quarkus_quarkus_security",
    ],
)

# Public facade
java_library(
    name = "auth",
    srcs = ["src/main/java/com/example/auth/AuthFacade.java"],
    visibility = ["//visibility:public"],
    exports = [":auth-api"],  # Consumers see auth-api types
    runtime_deps = [":auth-impl"],
)

Visibility Package Groups

# BUILD.bazel (root or common/)
package_group(
    name = "services",
    packages = ["//services/..."],
)

package_group(
    name = "libraries",
    packages = ["//libs/..."],
)

package_group(
    name = "internal",
    packages = [
        "//common/...",
        "//libs/...",
    ],
)
# common/utils/BUILD.bazel
java_library(
    name = "utils",
    srcs = glob(["src/main/java/**/*.java"]),
    visibility = ["//:internal"],  # Use package group
)

Dependency Management

Dependency Graph Analysis

# Visualize dependency graph
bazel query "deps(//services/user:user-service)" --output=graph > graph.dot
dot -Tpng graph.dot -o graph.png

# Find unused dependencies
bazel query "deps(//services/user:lib) except deps(//services/user:lib, 1)"

# Find reverse dependencies
bazel query "rdeps(//..., //common/utils:utils)"

# Check for circular dependencies
bazel query "somepath(//services/user:lib, //services/order:lib)"
bazel query "somepath(//services/order:lib, //services/user:lib)"
# If both return results β†’ circular dependency!

# Find diamond dependencies (same dep via different paths)
bazel query "allpaths(//services/user:lib, @maven//:com_google_guava_guava)"

Dependency Hygiene Rules

# tools/dependency_check.bzl
def _dependency_check_impl(ctx):
    # Check for banned dependencies
    banned = ["@maven//:commons_logging", "@maven//:log4j"]
    for dep in ctx.attr.deps:
        if str(dep.label) in banned:
            fail("Banned dependency: " + str(dep.label))

dependency_check = rule(
    implementation = _dependency_check_impl,
    attrs = {"deps": attr.label_list()},
)

Version Pinning

# MODULE.bazel
bazel_dep(name = "rules_jvm_external", version = "6.6")

maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
maven.install(
    name = "maven",
    artifacts = [
        "io.quarkus:quarkus-arc:3.20.1",
        "io.quarkus:quarkus-rest:3.20.1",
        "com.google.guava:guava:33.4.0-jre",
    ],
    lock_file = "//:maven_install.json",  # Pin all versions
)
use_repo(maven, "maven")

Cross-Cutting Concerns

Shared Build Macros

# tools/build_defs.bzl
def quarkus_service(name, srcs, deps, main_class = None, **kwargs):
    """Macro for standard Quarkus service setup."""
    java_library(
        name = name + "-lib",
        srcs = srcs,
        deps = deps + [
            "@maven//:io_quarkus_quarkus_core",
            "@maven//:jakarta_inject",
        ],
        visibility = ["//services/..."],
        **kwargs
    )

    java_binary(
        name = name,
        main_class = main_class or "io.quarkus.runner.GeneratedMain",
        runtime_deps = [":" + name + "-lib"],
        visibility = ["//visibility:public"],
    )

    java_test(
        name = name + "-test",
        srcs = native.glob(["src/test/java/**/*.java"]),
        test_class = "com.example." + name.replace("-", ".") + ".TestSuite",
        deps = [
            ":" + name + "-lib",
            "@maven//:io_quarkus_quarkus_junit5",
        ],
    )

Using the Macro

# services/user/BUILD.bazel
load("//tools:build_defs.bzl", "quarkus_service")

quarkus_service(
    name = "user-service",
    srcs = glob(["src/main/java/**/*.java"]),
    deps = [
        "//common/utils:utils",
        "//common/models:models",
        "//libs/auth:auth",
        "@maven//:io_quarkus_quarkus_hibernate_orm_panache",
    ],
)

Shared Linting

# tools/lint.bzl
def java_lint(name, srcs):
    native.genrule(
        name = name + "_lint",
        srcs = srcs,
        outs = [name + "_lint_report.txt"],
        cmd = "java -jar $(location //tools:spotbugs) $(SRCS) > $@",
        tools = ["//tools:spotbugs"],
    )

# In BUILD files
load("//tools:lint.bzl", "java_lint")

java_lint(
    name = "user-service",
    srcs = glob(["src/main/java/**/*.java"]),
)

CI/CD Integration

GitHub Actions for Monorepo

# .github/workflows/ci.yml
name: Monorepo CI

on:
  push:
    branches: [main]
  pull_request:

jobs:
  detect-changes:
    runs-on: ubuntu-latest
    outputs:
      services: ${{ steps.changes.outputs.services }}
      common: ${{ steps.changes.outputs.common }}
    steps:
      - uses: actions/checkout@v4
      - uses: dorny/paths-filter@v3
        id: changes
        with:
          filters: |
            services:
              - 'services/**'
            common:
              - 'common/**'
              - 'libs/**'

  build-affected:
    needs: detect-changes
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: bazelbuild/setup-bazelisk@v3

      - name: Mount Bazel Cache
        uses: actions/cache@v4
        with:
          path: |
            ~/.cache/bazel
            ~/.cache/bazel-disk-cache
          key: bazel-${{ runner.os }}-${{ hashFiles('**/MODULE.bazel') }}

      - name: Build Affected Targets
        run: |
          if [ "${{ needs.detect-changes.outputs.common }}" == "true" ]; then
            bazel build //...
          else
            bazel build $(bazel query "rdeps(//..., set(${{ steps.changes.outputs.changed_files }}))")
          fi

      - name: Test Affected Targets
        run: bazel test //...

Bazel CI Best Practices

# .bazelrc - CI-specific settings
build:ci --disk_cache=~/.cache/bazel-disk-cache
build:ci --repository_cache=~/.cache/bazel-repo-cache
build:ci --remote_cache=grpc://cache.internal:9092
build:ci --jobs=4
build:ci --local_cpu_resources=4
build:ci --local_ram_resources=HOST_RAM*0.8

# Test settings
test:ci --test_output=errors
test:ci --test_summary=detailed
test:ci --flaky_test_attempts=3

Performance Optimization

Target Granularity

# BAD - monolithic target
java_library(
    name = "user-service",
    srcs = glob(["src/main/java/**/*.java"]),  # 100+ files
)

# GOOD - granular targets
java_library(
    name = "user-api",
    srcs = glob(["src/main/java/com/example/user/api/*.java"]),
    visibility = ["//services/..."],
)

java_library(
    name = "user-service",
    srcs = glob(["src/main/java/com/example/user/service/*.java"]),
    deps = [":user-api"],
)

java_library(
    name = "user-repository",
    srcs = glob(["src/main/java/com/example/user/repository/*.java"]),
    deps = [":user-api"],
)

Build Graph Analysis

# Find critical path (longest build chain)
bazel analyze-profile profile.json | grep "Critical path"

# Generate build profile
bazel build //... --profile=profile.json
bazel analyze-profile profile.json

# Check cache hit rate
bazel build //... --execution_log_json_file=log.json
cat log.json | jq '. | select(.cacheHit == true)' | wc -l
cat log.json | jq '. | select(.cacheHit == false)' | wc -l

Troubleshooting

"Too many open files"

# Increase file descriptor limit
ulimit -n 4096

# Or in .bazelrc
startup --host_jvm_args=-XX:-MaxFDLimit

Slow analysis phase

# Reduce glob scope
# BAD: glob(["src/**/*.java"])
# GOOD: glob(["src/main/java/**/*.java"])

# Use --nolegacy_external_runfiles
build --nolegacy_external_runfiles

# Disable strict Java deps for faster analysis
build --java_runtime_version=remotejdk_21

Memory issues

# Increase Bazel server memory
startup --host_jvm_args=-Xmx8g

# Reduce parallelism
build --local_ram_resources=HOST_RAM*0.5

Decision Trees

Adding a New Service

New service needed?
  β”œβ”€β”€ Does it share domain logic?
  β”‚     β”œβ”€β”€ YES β†’ Create in //services/
  β”‚     └── NO β†’ Could it be a library?
  β”‚           β”œβ”€β”€ YES β†’ Create in //libs/
  β”‚           └── NO β†’ Create in //services/
  β”œβ”€β”€ Does it need its own DB?
  β”‚     β”œβ”€β”€ YES β†’ Add to service k8s manifests
  β”‚     └── NO β†’ Use shared DB or in-memory
  └── Does it need its own image?
        β”œβ”€β”€ YES β†’ Add oci_image target
        └── NO β†’ Use as library dependency

Extracting a Library

Code duplicated across 3+ services?
  β”œβ”€β”€ YES β†’ Extract to //libs/<name>
  β”œβ”€β”€ Is it domain-specific?
  β”‚     β”œβ”€β”€ YES β†’ //libs/<domain>/
  β”‚     └── NO β†’ //common/<category>/
  └── Does it have external deps?
        β”œβ”€β”€ YES β†’ Document in MODULE.bazel
        └── NO β†’ Keep minimal

References

Skill Interoperability

The bazel-monorepo-expert πŸ“¦ skill manages workspaces using:

  • bazel-expert πŸ—: Core Bazel build system.
  • bazel-oci-expert 🐳: Container images for services.
  • bazel-k8s-expert ☸️: Kubernetes deployment for services.
  • rules-quarkus πŸ”§: Quarkus service builds.
  • java-expert β˜•: Java code organization.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.