You tend to end up with way too many targets that don't actually "mean anything" to a human. In one codebase I have to deal with, the Bazel build has ~10k targets whereas the previous non-Bazel build had ~400. Too many targets have an impact in various dimensions. Some examples:
* The build files are unreadable. If targets don't mean anything to a human, updates to build files become pure toil (and is when devs ask for build files to be auto-generated from source).
* IDE integrations (particularly via the IntelliJ Bazel plugin) become slower because generating metadata for those targets takes time.
* Binary debugging is slower because the C/C++ rules generate one intermediate .so file per target and GDB/LLDB take a long time to load those dependencies vs. a smaller set of deps.
* Certain Java operations can be slower. In the case of Java, the rules generate one intermediate JAR file per target, which has a direct impact on CLASSPATH length and that may matter when you do introspection. This tends to matter for tests (not so much for prod where you use a deploy JAR which collapses all intermediate JARs into just one).
My intuition was wrong, my naive understanding was that:
* Non-human intermediate targets would either be namespaced and available only in that namespace, or could be marked as hidden, and not clutter auto-completion
* IDE integrations would benefit, since they only have to deal with Bazel and not Bazel + cargo/go/Makefile/CMake/etc
* I thought C/C++ rules would generate .o files, and only the final cc_shared_library would produce an .so file
* Similar for .jar files
I guess my ideal build system has yet to be built. :(
> * Non-human intermediate targets would either be namespaced and available only in that namespace, or could be marked as hidden, and not clutter auto-completion
This is actually possible but you need the new JetBrains-owned Bazel plugin _and_ you need to leverage visibility rules. The latter are something that's unique to Bazel (none of the other language-specific package managers I've touched upon in these replies offers it) and are even harder to explain to people somehow... because these only start making sense when you pass a certain codebase size / complexity.
> * I thought C/C++ rules would generate .o files, and only the final cc_shared_library would produce an .so file
> * Similar for .jar files
These are possible too! Modern Bazel has finally pushed out all language-specific logic out of the core and into Starlark rules (and Buck2 has been doing this from the ground up). There is nothing preventing you from crafting your own build rules that behave in these specific ways.
In any case... as for dynamic libraries per target, I do not think what I described earlier is the default behavior in Bazel (we explicitly enable dynamic libraries to make remote caching more efficient), so maybe you can get what you want already by being careful with cc_shared_library and/or being careful about tagging individual cc_libraries as static/dynamic.
For Java, I've been tempted to write custom rules that do _not_ generate intermediate JARs at all. It's quite a bit of work though, so I haven't, but it could be done. BTW I'll actually be describing this problem in a BazelCon 2025 lighting talk :)
What are some of those good reasons (assuming they differ from GP's)?
I don't have much experience with Bazel aside from setting up a simple local workspace and following the tutorial.