To expand on that, on Windows it's best to use kernel32 (or WindowsApp) unless you really need the cross-platform convenience of the C lib. The exception being architecture dependent functions like memcmp, memcpy and memset which will most likely have efficient assembly implementations.
ntdll is lower level and technically unstable but core functions have been pretty stable for a long time. They could of course have breaking changes but it risks breaking things like cygwin. Microsoft tends to take compatibility seriously, although perhaps not as much as they used to.
Direct system calls are completely unstable. They can and do change a lot between releases. You'd need to use a lookup table for every build of Windows.
Basically yes. ntdll is an implementation detail that shouldn’t be relied upon. kernel32/user32 and friends are considered the “proper” interface to the system and have been stable for decades.
There are some ntdll calls that are officially documented and ok to use. Of course there are also a lot of calls you shouldn't use.
When necessary, it's fine to use even undocumented ones to support Windows 7 and older. It's not like those are going to change anymore.
> it’s very rare to call ntdll or to make system calls directly
Until you need to do something that's not possible through kernel32.dll. Sometimes I've called ntdll.dll directly to support older Windows versions.
I'm guessing the performance benefits are negligible anyway?
C library => kernel32.dll => ntdll.dll => system calls
You don’t have to go via the C library - calling kernel32 directly is fine (I believe this is what Go does). However, it’s very rare to call ntdll or to make system calls directly.