This is probably something everyone has to face during the development sooner or later.

You have an existing code written by someone else, and you have to extend it to work under new requirements.

Sometimes it's simple, but sometimes the modules have medium to high coupling and medium to low cohesion, so the moment you start touching anything, everything breaks. And you don't feel that it's fixed correctly when you get the new and old scenarios working again.

One approach would be to write tests, but in reality, in all cases I've seen, that was pretty much impossible (reliance on GUI, missing specifications, threading, complex dependencies and hierarchies, deadlines, etc).

So everything sort of falls back to good ol' cowboy coding approach. But I refuse to believe there is no other systematic way that would make everything easier.

Does anyone know a better approach, or the name of the methodology that should be used in such cases?

First off, it gets a little wearing that everyone on this site thinks anything written by anyone else is rubbish.

Understanding code is difficult, admittedly some poor programming practices make it more difficult, but, for any reasonably complex system understanding the internal structure and idioms used is going to be hard, even if its well written code.

Systems routinely run for more than twenty years. Programming methodologies, best practices, design philosophies and fashions change every couple of years, and, programmers pick up the improved styles at different rates. So what would have been considered a state of the art and excellent example of code in 2007, looks old-fashioned and quirky today. As an exercise I suggest you dig out some code you wrote three years ago, I can almost guarantee you will cringe.

So first of you need to suppress the initial WTF response. Tell yourself that the system has worked well enough and long enough for it to become your problem so there must be something good about it.

Try to get a hang of the original coders style, the idioms used, study the weirder bits of code and see if they fall into a pattern.

If the required changes are small then follow the original coding style, that way someone picking up the code after you only needs to get used to one set idiosyncrasies. 

If the required changes are large and the changes are concentrated in a few functions or modules, then, take the opportunity to refactor these modules and clean up the code.

Above all do not re-factor working code which has nothing to do with the immediate change request. It takes too much time, it introduces bugs, and, you may inadvertently stamp on a business rule that has taken years to perfect. Your boss will hate you for being so slow to deliver small changes, and, your users will hate you for crashing a system that ran for years without problems.
