These are some of my personal opinions from using, and violently advocating for, bazel:

> Build graphs are trees. Build graphs are acyclic.

Bazel uses some tricks/magic to hide outputs of build rules from other build rules. Bazel also prevents cyclic dependencies (A depends on B which depends on A).

> Compilers will always modify the timestamps on every file they are expected to output.

Source files a target depends on will be hashed. The operations applied to the files will also be hashed.

> It's possible to determine the dependencies of a target without building it.

Bazel forces people to explicitly define all deps that the source code uses. It uses sandboxing to hide unspecified deps from the source code. All of the definition of these rules is defined statically-ish (if you ignore WORKSPACE which technically applies to Blaze).

> Targets do not depend on the rules used to build them.

Bazel hashes the operations the rules will perform.

> Targets depend on every rule in the whole build system.

Bazel understands the full DAG and knows what each target depends on and what depends on each target.

> Detecting changes via file hashes is always the right thing. Detecting changes via file hashes is never the right thing.

It's a tradeoff that someone has to decide. Neither is right or wrong. Using a file content+metadata hash allows you gauntness that the build is always correct.

> It's possible to narrow down the set of possibly-updated files to a small hand-enumerated set.

As long as you use file hashing and are explicit about your inputs and outputs of every build rule, you can.

> Nobody will ever want to rebuild a subset of the available dirty targets.

You can build a specific target and it's deps at any time. Unrelated dependency trees are ignored.

> stat is slow on modern filesystems.

Anything is slow given a large enough scale. Any syscall is slow once you have millions of source files.

> Non-experts can reliably write portable shell script.

In bazel people interact with higher level rules. You don't need to write shell scripts unless you're implementing something that requires shell scripts. If you'd like you can also use an output of a target as a tool to do further compiling. For example: build protoc then use protoc to compile protos into libraries.

Every build step updates at most one file. Every build step updates at least one file.
It's possible to tell the compiler which file to write its output to.
It's possible to tell the compiler which directory to write its output to.
It's possible to predict in advance which files the compiler will update.

> Your build tool is a great opportunity to invent a whole new language. Said language does not need to be a full-featured programming language. In particular, said language does not need a module system more sophisticated than #include. Said language should be based on textual expansion. Adding an Nth layer of textual expansion will fix the problems of the preceding N-1 layers.

It is a great opportunity to implement a domain specific language as "building software" is a domain that we would like to define programs for. Bazel uses skylark, it's a "fully featured" programming language in that there's loops, conditionals, functions (called "macros"), and a module-ish system ("load()"). It does not operate on textual expansion.

> It's totally OK to spend over four hours calculating how much of a 25-minute build you should do.

Bazel's target analysis takes a while but for my scale of code base it doesn't take hours, it takes seconds. If you have a huge codebase then these "wasted" seconds would be earned back by it's effective caching and remote build execution.

> All the code you will ever need to compile is written in precisely one language.

Bazel is multilingual.

> Everything lives in a single repository.

What is a "repository"? It's really a file system. What is a file system? A fancy kv store. Could you build a KV store that could return the values for every key that is a file in your source tree? Probably.

Bazel is built around a monorepo design but you could just as easily clone libraries into shared folders and executre bazel from above your project. You could also use remote repository rules. IMO you're better off just vendoring any source code you're writing in a folder in your repo so you can guarantee old versions of things are around when you need them and you never have a leftpad incident.

> Files only ever get updated with timestamps by a single machine. Version control systems will always update the timestamp on a file. Version control systems will never update the timestamp on a file. Version control systems will never change the time to one earlier than the previous timestamp.

File contents are check so this is not as much of an issue.

> Programmers don't want a system for writing build scripts; they want a system for writing systems that write build scripts.

I think people don't want systems like this because they've been burned. I've used a lot of makefile generators and they're all a really poor caching system. You essentially make cache invalidation part of your dev flow. AS a developer I don't want to think about that. I want to ask something to build me something and have it know when it needs to reload build definitions or code. I also don't want to care about what specific "gcc" or "javac" commands are being run. I need to be able to go down to that level but 99% of the time I just need to say "Turn a.c, b.c, and c.c into foobar.bin".

As for what bazel is really bad at, these all still apply:

> People will only want to build software on Linux. People will only want to build software on a Unix derivative. Nobody will want to build software on Windows. People will only want to build software on Windows.

Bazel support in windows is shaky. Bazel itself works but many tools (rules_docker) do not work. One place bazel would be really amazing for, in my opinion, is the game development industry. You have massive source trees, very complex packaging steps, you often have tools that are used as a compiler further in the compilation process (asset packets), you can benefit from shared build clusters, etc.

> Single-character magic variables are a good idea in a language that most programmers will rarely use.

Not the same but make-like variable substitution exists and it's a bit of a pain: https://docs.bazel.build/versions/master/be/make-variables.h...

> System libraries and globally-installed tools never change. Version numbers of system libraries and globally-installed tools only ever increase.

Bazel is mostly hermetic except for extremely strange things. Build toolchains that are "expected" to be in the host bleed in and effect a lot of things. It's a really gross side effect of certain things and can cause some nasty bugs.

And for some new rules that bazel sins with:

1. People are ok with running builds on hosts of separate architectures to build their software for different architectures

Cross compiling in bazel is extremely painful.

2. IDE integration doesn't matter.

A lot of people who use bazel don't really care about how well, or how poorly, it integrates with an IDE. As such a lot of the open source world around bazel's IDE integrations are lack luster. Getting things like gRPC working in bazel with auto-completions for multiple languages is nearly impossible.

3. Everyone wants 100% reproducible builds.

Many people do not want reproducible builds. That may sound shocking to some but a lot of other tools depend on non-reproducible elements of a build chain. For instance: when using bazel to build a docker container it sets the container's creation timestamp to the unix epoch. Because of this I cannot use Gitlab's registry cleanup script as it'll wipe all images since they're all over N days old.

4. All of the compilers I want to use during my build are installed on my host system.

Docker was probably the most amazing improvement in build/tooling workflow in recent years. If I want to build something with docker all I need is docker. If bazel to make this workflow the same it could take over the world. If there was a single place to configure something like this:

toolchains(
arm: {
c: "gcc:10.1.0"
}
x86: {
c: "icc:....."
}
)

And if the default was that for any language not specified, it would either not be available or would be easily buildable from source as long as you have a C compiler.

I don't know if this is the best way to represent this concept but I have no idea why I have to even be concerned what version my host OS ships of gcc. I also don't know why I have to care if my host system is x86 but I want to build a binary for arm. Either use cross compiling features of the compiler or spin up a VM and run the build using native tooling in there. I shouldn't have to manually do that.