How does Blender calculate path normals?
In the Shape panel for Curve objects there are 3 twisting options:


Z-Up: uses Z axis to determine the twist.
Tangent: uses Frenet / Darboux frame tangent.
Minimum: step along the curve points, for each point computing a normal that is orthogonal to the curve direction and rotated as little as possible compared to the normal of the previous point.


Minimum is the default, and it behaves well in most cases. The downside is that it's non-local, any control point can influence the twist in the rest of the curve, but that's unavoidable, there is no other way to get minimal twisting.

The implementation in the Blender source code is in make_bevel_list_3D_minimum_twist.