Templates in C++ are quite different because of overload candidate resolution (and so SFINAE), which makes debugging templates a pain. Zig doesn't have overloads at all, plus, the error reporting mechanism (which C++ doesn't have) is available at compile time, as it is at runtime. So Zig both has better error reporting (or the potential for that) and doesn't have a big source for scary template errors in C++ even before adding concepts.
Having said that, I actually would like to see "concepts" in Zig, probably as a simple boolean predicate as the type annotation in function parameters, for one reason, and one reason only, which isn't mentioned in the article: documentation.
BTW, there's a general trend to compare Zig to some other language and extrapolate from it, but while I understand why people think that's helpful, it ends up just being confusing, because Zig is a very radical language that's not really similar to anything (except superficially). So it's very hard to compare features in isolation, because most problems are caused by an intersection of some features, and pretty much all the interesting features in Zig are different from what people know -- some are "stronger" and some are "weaker."
> The compiler gives you more flexibility, but OTOH if you want library users to have a good developer experience when they make mistakes, you need to do a bunch of extra work.
Yes, but I'd say the difference here, while similar in argument to typed vs. untyped languages, is qualitatively different. The value of finding errors sooner is not as high as the difference between finding errors before a program runs on the user's machine or after. Even if a generic library isn't well-tested (although, any library might not be well-tested) to the point that it has simple type errors, those errors will not find their way into the executable.
Having said that, I obviously agree that Zig's type system is not for all languages, but I'm surprised the article doesn't mention one of the most obvious reasons. Most application programming these days is done in languages that are JIT compiled, and so they don't need partial evaluation (i.e. compile-time evaluation) as much as low-level programming languages do. While some low-level languages have generics/templates and macros and constexprs -- and require distribution of libraries as source code anyway -- many if not most of the popular application languages can do with just generics. Zig's system is definitely much simpler (and personally I find it more elegant) than generics+macros+constexprs, but it isn't simpler than just generics. What Zig discovered was that since low-level programming languages do require partial evaluation, it can be made general and pleasant enough to also do polymorphism (so generics aren't required) and powerful enough to not require macros (which are usually good to avoid, and often require another "language within a language"). So I'd say that if your language does require sophisticated partial evaluation, consider Zig's approach -- it's revolutionary and refreshing; if not, you're probably better off with generics.