Profile attributes have no scope, and no namespace. Profile attributes are set at the session level so changes made in one piece of code can inadvertently affect code in other areas. The fact that there are no namespaces makes the problem worse, because your chances of accidentally using a name someone else used are greater (although you could come up with some namespace scheme).
Profile attributes screw your ability to debug. Because you can't find all the places your profile attribute might be set or unset, debugging becomes much tougher.
Profile attributes screw your ability to refactor. For the same reasons that your ability to debug is screwed, so is your ability to refactor. One of the basic things you need to be able to do when refactoring is finding all the places you used variable x. Well if variable x is a profile attribute, good luck!
Getting a profile attribute from the client is costly. Not everyone understands this. Your profile attributes do not exist in the client. So when you try to read them from browser script, Siebel needs to do a round-trip to the server to get it. That's a round-trip for each profile attribute you get or set.
In short, if you think you need to use a profile attribute to accomplish something, think again! If you still think you need a profile attribute, hit yourself on the head and think again!*
* The same advice pretty much applies for shared globals as well.