Yeah. Michael Feathers' Working Effectively with Legacy Code book is good.

Interestingly, Feathers defines Legacy Code as code without automated tests.

the rough process goes like this:

0. you need to make a change to some code that you dont understand and that has no automated tests and no reliable documentation of what its behaviour is

1. you need to refactor the code or change some logic in order to fix the required bug / add the new feature, but you cannot do this safely without having a way to measure the impact upon the existing code's unknown behaviour.

2. a controlled way of making such changes is to first wrap the code in automated regression / characterisation tests. you need to get these automated tests in place without changing the behaviour of the code. this may be challenging. there may be a bit of a chicken and egg problem where you dont want to refactor without having regression tests, yet you need to refactor to wedge the tests in.

3. after you have some decent regression tests in place, you can try to focus in on isolating and understanding the region(s) that you need to modify to achieve your intended change.

4. it may be necessary to refactor the existing code to achieve this, or to add unit tests to key components. your safety net of regression tests gives you some confidence to do this, provided you are careful in how you refactor to get "seams" in place in the code structure to do fine-detail unit / characterisation tests.

Most of the book is tactics of roughly how to do this. from memory, some of the tips in the book are geared towards typed compiled languages (java, C++) -- e.g. you can use the compiler's type checker as a source of information to learn about how the code is structured (by changing things and trying to compile and seeing how many pages of errors you get...)

Feathers also has a blog which may give you some actionable ideas:

* https://michaelfeathers.silvrback.com/revealing-interfaces

* https://michaelfeathers.silvrback.com/to-kill-code

* https://michaelfeathers.silvrback.com/characterization-testi...

* https://michaelfeathers.silvrback.com/knowing-what-is-there

edit: another good rule of thumb:

http://wiki.c2.com/?RefactorLowHangingFruit

the most ideal scenario for getting legacy code under control is the following:

* the code itself is purely functional: no side effects, and no external dependencies on irritating things like databases, rpc calls to external services, random crap that gets shell-executed. the code also happens to be completely deterministic. the code is written in a language that is statically typed and has clever compilers, linters, automated test frameworks, editors that implement automated guaranteed-safe refactoring moves, etc.

in reality you are almost certainly going to have external state. figuring out how to isolate/mock/reproduce that in regression tests might be one of the first major challenges.

it would be very helpful if you have good support from "the business" for this work, in particular that they understand getting this legacy code under control may be a very slow and costly operation.

if you are particularly unlucky then no one left in the org will understand the "theory of the [legacy] code". in order to make safe and useful changes to the existing codebase you will need to gradually reverse engineer the theory of the code / re-learn what the requirements and major constraints on the system are. there's a great essay by Naur (of e.g. bNf fame) about this: http://pages.cs.wisc.edu/~remzi/Naur.pdf (previous HN discussion: https://news.ycombinator.com/item?id=10833278 )

there are also other strategies for dealing with this kind of situation.

probably trying to rewrite the entire thing is a poor idea, various people have written about the risks and failure modes of that approach.

it might be possible to identify one responsibility of the legacy system that needs to be changed, and can be rewritten as a separate service. see e.g. https://www.martinfowler.com/bliki/StranglerApplication.html ; http://cdn.pols.co.uk/papers/agile-approach-to-legacy-system...