It's often good to try things the hard way and then go back to doing it the easy way once you've mastered that. The easy way seems so much easier once you've done otherwise.
The "ugly strings" suggestion is bizarre, though. If he means "avoid hardcoded strings so you can localize your program", then you still need keys into your message catalog, which are usually...strings! And if he means "define constants for strings", you still need to define their values, which are also...strings!
Putting strings in a separate "message" file is a really annoying way to localize code. It's both hard to read, and hard to write. See GNU gettext for a more reasonable approach: http://en.wikipedia.org/wiki/Gettext
I agree that doing things the hard way can be an interesting exercise though, which is why coding blindfolded is just as good of a suggestion as these others.
The reason for "ugly strings" is that the value for named constants can be changed at a single location.
If a given constant appears in multiple locations, you're likely to screw up when you need to change its value. (Nope - global search and replace doesn't help - it will change semantically unrelated constants that happen to have the exact same value. Consider 0.)
1.) The string in question has an observable effect on the application's behavior, eg. a filename or text that appears in the UI.
2.) The string in question is used as a programmatic key; its actual value matters to you for readability's sake, but the user doesn't know or care what its value is.
For #1, you should be using some sort of localizable message catalog (eg. gettext or Java property bundles) or config file and not hardcoding it as a constant. Almost all of these use strings as the message key; in gettext's case, they just happen to be fairly human-readable strings. Converting them to a constant merely goes from one suboptimal solution to another.
For #2, the particular string used is part of the internal API of the program. Coding it as a constant buys you a little in that you can get the compiler to check it for you and make sure you didn't make any typos. The difference is really the old static vs. dynamic language debate. In any case, changing the string value here should be considered a program refactoring and done the same way you would do any other refactoring.
The bits have to be somewhere. The question is whether they appear one place per "meaning" or they're at every use.
Normalization is not just for databases.
> In any case, changing the string value here should be considered a program refactoring and done the same way you would do any other refactoring.
Which refactoring tools can distinguish "obj" (used as a prefix for object file temp names) from "obj" (used as a prefix for orange bear jumpsuit part numbers), let alone 0 from 0?
The "ugly strings" suggestion is bizarre, though. If he means "avoid hardcoded strings so you can localize your program", then you still need keys into your message catalog, which are usually...strings! And if he means "define constants for strings", you still need to define their values, which are also...strings!