If you try to predict the movement of objects you get the problems you worry about. It’s fairly easy to assume that objects will continue at the same speed, but this causes big problems when there are sudden changes. If you have a player standing still and then suddenly start moving, the message that s/he started moving will be delayed meaning that when the other players receive this message he’ll suddenly snap to his predicted position. The same problem happens when the player stops, at which point the extrapolated player will suddenly teleport back to where he stopped.
Valve don’t extrapolate positions. You will be displaying a delayed version of the world if you use interpolation, which is the standard nowadays. In this case you simply try to make sure that the world (other players, physical objects, etc) look smooth by interpolating between the state you get to reduce stuttering. All objects will be delayed by the ping, but at least they’re 100% smooth. I believe I read somewhere that the Battlefield games use interpolation for twitchy character movement, but extrapolation when dealing with vehicles because they simply can’t change their speed quickly and mostly just continue in the same direction they’re going.
However, each client is a bit flexible when it comes to their own players. When you move in the source engine the game sends a movement command to the server, but the game moves your character immediately without waiting for a response. As long as the player isn’t too far away from the last confirmed position from the server, the client is allowed to move things before getting confirmation. So the client doesn’t ignore the server when it says where the player is, but it doesn’t enforce it completely to reduce perceived lag. If you’ve ever heard of “rubberbanding”, it happens when the client and the server disagrees and the player is suddenly snapped or slowly pulled to his correct position. Most games are simple enough that this never happens unless your ping is fluctuating a lot.