Logically, a step event collision is the most fallible. You can collide with inanimate, stationary objects whenever you want; when both parties are moving on their own, then you should handle collisions in the End Step event (or immediately prior, just like Game Maker does).
Scenario 1) ObjectA has an hspd of ±2. The terrain ObjectB doesn't move at all. The collision with terrain can be handled anywhere. If handled before speeds are assigned or after all movement code, ObjectA checks if there is currently a collision with ObjectB and moves out of it if there is (classic method). If the collision is handled after the speed is set and before the movement code is run, then you check if there will be a collision and reduce speed until there isn't (popular method).
Scenario 2) ObjectA has an hspd of ±2. The terrain ObjectB has a steady speed of -1. Now ObjectB must be run before ObjectA, otherwise you need to factor in ObjectB's speed into the collision calculations. For example, instead of place_meeting(x+hspd, y, ObjectB), it would have to be place_meeting(x+hspd-ObjectB.hspd, y, ObjectB).
Scenario 3) ObjectA has an hspd of ±2. The terrain ObjectB has a variable speed of ±1 and there are two or more instances in the room. This scenario could be handled in Step Event, but it'd need to be handled by ObjectB this time. The speed of each ObjectB would need to be taken into consideration. This is one reason the End Step is actually better. Additionally, unless ObjectA is guaranteed to always run before ObjectB, then ObjectB has no foreknowledge of ObjectA's speed, so any calculations requiring ObjectA's speed and ObjectB's speed are quite fallible since one instance could be calculating collisions with the other instance based on previous speeds, then when the next instance gets to run its code it might change its speed and throw that collision calculation off. This is easily avoided by using the End Step.