I don't use masks. GM8 has much more flexibility in sprite collisions. I set bounding boxes for each sprite and use the dimensions of the bounding boxes for setting the coordinates in collision_line and collision_rectangle. Sure, there is the problem of bbox_left and bbox_right changing when image_xscale=-1, but that's easily compensated for (although I'm still tweaking the universal code). However, you cannot change bounding boxes mid-game or have multiple bounding boxes in a sprite, like you can with masks, so you do need to spend more resources on additional sprites in order to share their bounding boxes. However, you're already spending resources on the masks if you use masks, so I think the trade-off is fair.
Keep also in mind that the sprite's origin affects position when image_xscale or image_yscale is negated. A sprite with origin (0,0) will be rotated around its very left edge, whereas a 16x16 sprite with its origin at (8,8) will be rotated around its middle. What this means is a sprite with its origin centered will stay in place when flipped, but a sprite with its origin at (0,0) will appear to jump when flipped.
Beware place_meeting() and collision_rectangle() check the entire box, so if there is even a single pixel collision, it passes as true (or returns an ID, which makes it "true" basically). The thing to be aware of especially with collision_rectangle() is that the check collisions IN AND AROUND the parameters. So collision_rectangle(bbox_left,bbox_top,bbox_left,bbox_bottom,obj_wall) will check a 16x16 sprite with a full bounding box to see if any obj_wall bounding box is present between (-1,-1) and (16,16). Keep in mind that a 16x16 sprite has a bbox_bottom of 15, not 16. This means the very bottom row of pixels will not collide.