What do you do to understand some code that you didn't write? Or code that you wrote long time ago and don't remember what it does anymore.

Do you have some technique that you go about? Do you analyze the structures first, or the public methods, or do you draw flow charts, etc.? Or do you fire up the debugger and just step through it? Or do you just ad-hoc your way through until you understand it?

There are either too many possible answers, or good answers would be too long for this format. Please add details to narrow the answer set or to isolate an issue that can be answered in a few paragraphs.
If this question can be reworded to fit the rules in the help center, please edit the question.

+1 for "saving discoveries in written form". I follow a similar process and having data flow diagrams etc makes it much easier to get inside the author's thought processes.
–
Gary RoweDec 21 '10 at 9:44

1

"Saving discoveries in written form": Since finding no documentation and no meaningful variable or function names becomes the norm in my job, i start with adding comments and making names easier to understand when i finally "get" a code fragment.
–
LennyProgrammersDec 21 '10 at 14:55

2

I wish asking the author was a reasonable choice more often.
–
Logan CapaldoDec 22 '10 at 3:14

+1 for mentioning writing tests. It usually helps because you go through the whole method and can compare your expectations about how it should work against how it actually works. And you actually created something useful for the future.
–
Anne SchuesslerDec 21 '10 at 14:19

Usually, I analyse a simple part first, e.g. the module used to maintain a small table. This teaches me the style the other programmer is using. If I have problems understanding even that, it's either very badly written or my knowledge of the language, framework etc. is insufficient. Once I grasp the simple part, it's time to move to the more complex parts of the program.

I go through the usecases. Each use-case starts at some point and finishes at another. Start looking at the begining and follow the flow. When you've examined three or four usecases you know the structure of the code.

Preferably you should be writing tests when following the code, since it will help you keep a more active role in examining the code than reading it line by line.

The debugger is a great tool to follow the flow, you could make some quick tests that doesn't realy assert anything but start the code at the point you want to debug from, to get a quick starting point for the debugger.

Though depending on how comfortable you are with tests, tests might be faster to check expected results.

this is coming with the experience. when you're a newbie, or you jump into other programming language is a little bit difficult.
when you have several years working with a language, then is easier to understand.

but, as a general rule, I'm firing up the debugger, and start to understand what's happening in it. also it is VERY IMPORTANT to comment your code, and to work on documented code.

Use a white board to write out and diagram interactions between classes or methods. This can help you to see the flow of the program. Once you have the 100ft view, then start digging, tracing and debugging to find the nuances of the system.

Well, most I don't go about it at all. I only try to understand it if it doesn't work, and I'm trying to figure out if I did something wrong or "the other" did. And the tools I use for that is reading the code and using the debugger.

Different people have different learning styles, so you have to choose the method that works best for you.

The first thing I do (after building the project) is read the entire code base through at least once. That gives me a general idea of where everything is. Then I choose a section to examine in more detail. Data structures would be a good place to start. Once I have a general idea of what's going on, I do the same with another portion of the code that interacts with the first. After enough iterations, I have a good sense of how the code works.

Interesting question, I don't think I've ever documented the process before but here are a few things I like to do.

assessing

maybe I'm just extending one aspect of the author's code. If I can correctly limit my task to a class or group of dependencies this means I can use find / replace within a file, or dir search in my OS to check what folders and files contain a target string (i.e. class, var name etc.) and limit my code reading to the most essential parts and help isolate their dependencies.

doing grep -l [text to find] [files to look in] is sweet for this kind of thing in Mac or Linux. No idea how this would be accomplished in Windows.

There might be API or dev docs even if it's a small project. If there are score. If not, it's time to really search out useful comments in the code.

researching

Taking the example that it's an internal project I've inherited--maybe from a long departed colleague who I've never met:

After reading code and documentation. I realize it's super annoying to do this, but now is possibly a good time to look at the tickets, issue tracking, bug reports or whatever project management tool / system you're company was using at the time the author's code was created. Maybe the issue you've been asked to fix is a long standing issue with a documented history thread.

manual labour

The previous steps were all designed to prep you for the intense amount of manual labour that is inherently involved in reverse engineering someone else's code. Up to now, most of the work has been focused on reading.

As others have suggested, moving stuff around and breaking it in a controlled way (i.e. ctrl + z is your friend in failed compiler tests) , stepping through with the debugger. Extending classes, plugins, etc. All great strategies you can use to limit the amount of re-writing and re-factoring that needs to be done.

large scale refactoring should be almost always be avoided. That said, refactoring small sections of code with well documented comments maybe even stating you are not the original developer can be quite helpful.

Substantial refactoring should only be conducted if the state of the code system actually requires it (i.e. it's running really slow, it's become a "big ball of mud", etc.) What you want to avoid is simply trading in one bug for a new one. In my experience, refactoring a project just to wrap your mind around it has unforseen consequences, and these consequences may only become evident at a much later date. Even with automated unit tests, in a large system it can be difficult to re-test every single aspect of a development cycle.

When you're reading through a class, look at the attributes and behaviors that it has (methods + fields). For methods, take mental note of the method name, it's input parameters, and it's return type. If you don't know the method name (because it's obfuscated), then sometimes you can get this information by looking at the input, output, and implementation.

It accepts three doubles, returns a double, but the telling part is the mathematical equation: square-root of a-squared plus b-squared plus c-squared: That's distance!

Now, you'll want to refactor f() to distance(). Decypher the easy ones first. Once you have more and more of the puzzle solved, it'll give hints into the harder to decypher areas.

My favorite trick is control+F. That's the search function. And you can use to search entire projects too (not just individual files). How do you even know what to search for, you ask ? Well, by using the application or library in some way... it'll give you hints as to what to search for. First, you have a general idea of what particular feature you wanna look for. Then, if you've used the project even just a little bit, you can get more specific clues to narrow your search.

For example, I'm working on a project now that I've inherited from an inactive author (an open-source Minecraft plugin with 418 files and 46k lines of code). And we had a user open a ticket: "What is the permission node to use join signs ?" Heck, I have no idea. And the documentation didn't mention it either. No sweat, control+F to the rescue. (My weapon of choice).

One of the things you can't always 100% decypher is the question of their design: "Why did they design it like this ?" One can only speculate.