Interesting. I don't find the Nix language itself especially complex, however, the whole system built with it is not that easy. For instance, packaging process is very well documented, making a derivation looked simple and reasonable until I decided package some code for myself. At this point things stopped being smooth. According to the documentation I needed to explicitly import my nix file from the top-level list of all packages which was not what I wanted, I'm unsure anybody except me would want my package (at least at the early stage of development). Intuitively, I was sure it wasn't necessary, there definitely must be a simpler way to toy with packaging but skimming through the documentation didn't reveal any answer. Looking at the source code left the impression that I needed a lot more time to understand how things worked behind the hood.
On the other hand replacing pyenv with Nix was very pleasant experience. Overriding an existing package with my fork on github was almost a one-liner.
The Nixpkgs packaging guide describes how to use the Nixpkgs packaging tools in the context of contributing to Nixpkgs, where to populate the package set with your custom packages, you add them on a top-level attribute in pkgs/top-level/default.nix.
But a lot of users want to leverage Nixpkgs while instead (or simply first) adding their custom packages to an overlay or a company repo of Nix packages.
> 3. Add the package to the file pkgs/top-level/all-packages.nix. The Nix expression written in the first step is a function; it requires other packages in order to build it. In this step you put it all together, i.e., you call the function with the right arguments to build the actual package.