I've programmed robots for many years, and there is inevitably a case where gimbal lock will be an issue, no matter how much you prepare for them.
How are quaternions better than matrices? Both store a single absolute orientation relative to their parent.
People recommend switching from matrices to quats, or from euler angles to quats to avoid gimbal lock. This is unfortunately so wrong, because it gives a misleading impression that somehow the way the rotation is represented is what would cause the gimbal lock, but it's not.
It is the more subtle effect of having to change your logic when moving from euler angles to quats, which will help you avoid gimbal lock (to some extent, depending on what you're doing).
In the most common manner, rotations are prone to gimbal lock whenever you are operating on a sequence of three (or more) rotations R1 * R2 * R3 each constrained to happen around a fixed axis. In this logic, it can occur that the rotation performed by R2 aligns the axes of R1 and R3 so that the perform their rotations about the same axis. This causes one degree of freedom to be lost, which is the issue we all have observed.
Incidentally, Euler angles use scalar triplets a,b,c (e.g. for yaw, pitch and roll) and generate rotation matrices R_a * R _b * R_c to combine to the final rotation. This is exactly like shown above, and will be prone to gimbal lock. Switching to using a quaternion (or a matrix) instead of the euler triplets allows you to avoid gimbal lock, not due to a change in your data storage, but by forcing you to change your program logic, i.e. instead of several subsequent rotations about cardinal axes, you will only store a single full orientation, and operate on that single orientation instead.
Note that if you insisted on doing the Euler->Quat conversion poorly, and did something like
Quat yawRotation = Quat::RotateAxisAngle(WorldAxisX, yawRotation);
Quat pitchRotation = Quat::RotateAxisAngle(WorldAxisY, pitchRotation);
Quat rollRotation = Quat::RotateAxisAngle(WorldAxisZ, rollRotation);
Quat finalRotation = yawRotation * pitchRotation * rollRotation;
One might think "it's all quaternions, no matrices or euler angles, so no gimbal lock!", but that's wrong. The code is running a rotation logic exactly like shown above: a sequence of rotations constrained about fixed axes, and there are at least three of those -> prone to gimbal lock.
So, it's not the representation which itself avoids the gimbal lock, but the way your representation will let you run a different logic which avoids gimbal lock.
PS. In the field of robotics where you have a sequence of several 1DoF joints on a robot arm, you will always have to live with gimbal lock, since you can't change the physical world by changing the code from eulers/matrices to quats and switching your program logic - the joints will always be 1DoF.