I start by mapping I/O. Unless it's a really small program, I generally avoid addressing real-world I/O in the actual program. Even if you don't really need to, there are so many situations where it's preferable that I just end up doing it as a matter of course. If it's a really complicated process, I might do a flow chart of how it's going to work before writing the code. But generally I can just write it as I go along and it usually turns out okay. In RSLogix 500 I try to take advantage of multiple Data Tables as much as I can. I don't like shoving all of my booleans into B3. I might make several depending on what category they are, so for instance I'll make a B file for Alarm bits, one for real world inputs, one for real world outputs, one for HMI inputs, one for HMI outputs, etc. and use the default B3 file for my internal program logic. This really helps keeps things straight so I can look at, say, B10:0/0 and know that this is an internal bit mapped to a real-world output, or B20:1/1 and know that this is an alarm flag immediately.
I also like to make a String file for alarm messages, configure the HMI to have a set number of way more than I'll ever use, configure each message to look at a different string address of the corresponding number (so, alarm 1 would be ST29:1, alarm 2 ST29:2, and so on), and then each alarm flag to a pre-determined boolean address. That way, I can add alarms when I'm online with the PLC without having to touch the HMI program.