In favour of Nix.
In Nix, the sequence of events that lead to your build are figured out on the language level. A build is this sequence of events.
In Starlark, you have to do things in a specific order - and that can get messy. It has a bunch of built-in phases that mean WORKSPACE, .bzl and BUILD files all have different levels of ability. A workspace can define a dependency (e.g. a http_archive), load a file into bazel, then call a bazel function from it. If you want to do that outside of a huge unmaintainable file, you have to break the steps down into multiple bzl files - one that does the download, then another that loads the dependency and calls it. And then you can call the latter from your workspace. BUILD files can do neither of the above and can only really define builds. So you end up with awkward file arrangements to load the right dependencies.
In Nix, anything you evaluate is a Nix variable. A function is a variable, a derivation (target) is a variable. You can refer to it natively and do what you want with it.
In Bazel, everything is referred to in a global register via labels, which are just strings. If you want two versions of the same thing that are the result of a function call, you have to give them different labels, so you can end up screwing around with string manipulation to generate unique identifiers.
This also feeds into a deeper issue. Bazel loves its global registry. Names from your dependencies are also your dependencies - so if the author of a project decides it hates the users and wants to enforce a “com_github_owner_repository” as its workspace name (because who doesn’t love Java-style conventions), you’re stuck using “@com_github_owner_repository//:library” throughout your codebase, anywhere you rely on that dependency. And to load in that dependency’s own dependency, you have to either load a special file and run special functions that the author provides, or you have to copy a bunch of code into your WORKSPACE file, making sure you give things the naming conventions used within the dependency.
In Nix, you name things whatever you want. If something you depend on has dependencies, it manages them itself - you can override them if you want.
Bazel doesn’t have much of a registry of third party repos. The onus is on you to scour the web, find an archive you want, grab the hash, and sometimes write your own build file to explain how it gets put together.
Nix has a truly vast repository of dependencies, which can be library dependencies or programs (in the latter, it’s the largest repository of applications out there - bigger than even debian and arch provide).
Bazel is maintained by Google, and maintainers make it very clear that they are spread thinly and don’t enjoy doing it. Some projects are completely stalled due to lack of maintenance. When bugs are found in the internals, they don’t get fixed for a long time. It’s based on Blaze, just like VSCode is based on Visual Studio, and there’s a feeling that it is being held back when things that could benefit users would be incompatible with their own system. There’s always the looming apprehension that Google are going to abandon it as soon as the next shiny thing comes along, or as soon as the people who work on it get promoted. Want to submit a PR? Sign this agreement first, then we might look at your PR, then we might merge it, might say no, but (note: speculation) an eerily familiar yet unattributed push might arrive a few months later from a googler.
Nix is full FOSS, with the standard open process you’d expect.
In favour of Bazel.
In Bazel, because you’re expected to follow a certain flow, errors are friendlier and easier to figure out. Bazel knows what you’re supposed to be doing and can help. When it builds things and fails, the intermediate files are saved in an easy-to-find place within your local directory (as a symlink) so it’s easy to explore and figure out the issue.
In nix, a derivation (target) truly resolves down to the tiny operations that put it together. It doesn’t know what you’re trying to do with it, so a typo at the topmost definition of a derivation can end up giving you horrific errors about low level list operations. If an error occurs in the code you’re building, intermediates can be found by copying a huge nix store path from the terminal and navigating to it, but it is a laborious process.
Starlark is based on Python, and it’s familiar to a lot of people. The overhead of learning the language is just a case of “oh it’s Python but without X, Y, Z, and… without fstrings!?”.
Nix is an entire language, with its own conventions, its own library, and its own syntax. It’s easy enough to learn if you’re used to picking up new languages (and honestly is one of my favourite languages now), but that’s an overhead. Most people think imperatively and eagerly, and nix is functional and lazy, so it can be hard to figure out how to accomplish something.
Bazel has an actual build system. It has native support for a bunch of languages. Nix is very much DIY - if you’re using make or CMake then the mkDerivation can do a lot of things for you, but for a developer using the two technologies together is unpleasant. We went as far as making our own build system in Nix that invoked e.g. gcc directly rather than try to work with cmake.