9

Possible Duplicate:
I’ve inherited 200K lines of spaghetti code — what now?

I have been recently handled a giant multithreaded program with no comments and have been asked to understand what it does, and then to improve it (if possible).

Are there some techniques which should be followed when we need to understand someone else's code? OR do we straightaway start from the first function call and go on tracking next function calls?

C++ (with multi-threading) on Linux

6

9 Answers 9

7

This will depend on the language, but (and I know it's a terribly old fashioned tool) IMHO you cannot beat a simple flow chart for code, with associated data-flow diagram.

Pretty much the way one would have designed code from requirements, in days of yore.

Start from the whole program, and decompose...

Obviously, you'll be doing a separate chart for each (non trivial) function... the detail of each chart depends on the functional complexity.

In the case of classes, treat each class as a standalone, self-contained block - from a system viewpoint, each class should (if designed properly) be able to be considered as a "black box"

Decompose the whole into classes, then classes into data structures and functions, etc

Modern-fangled tools such as the UML have their place, but for reverse engineering, stick to the basic tools.

6
  • 1
    I just want to add that some functional tests and poking around changing little bits and checking the output / result can help a lot. Nov 6, 2012 at 8:49
  • I have added the language. So, you mean that I should be drawing flow charts for every function to better understand them? Nov 6, 2012 at 8:53
  • should we start by understanding the classes, then the functions or vice versa? Nov 6, 2012 at 9:16
  • 1
    Always start with the biggest block and decompose downwards... so split the program into classes, then decompose the classes into data/functions then decompose each data structure/function etc
    – Andrew
    Nov 6, 2012 at 9:19
  • 2
    My problem with this approach is that "spaghetti code" is going to produce "spaghetti flowcharts"... Nov 6, 2012 at 14:34
3

It would be most helpful if there are some unit tests around the code - unit tests can often have a secondary function as documentation.

However, legacy code often comes without tests. Maybe writing some would be a good way of figuring out what is going on?

Then, you can refactor the spaghetti code into easy to read, well written code.

3

Fundamentally, you want to be following the scientific method. i.e. Observe, hypothesise, test. There are various techniques which might be appropriate for each stage.

Observe:

  1. Run the code: Hopefully, this is obvious!
  2. Run the code through a profiler: This can be a good indicator of where to focus your efforts. If you have a decent profiler, it can also be great for picking up on unexpected side-effects of operations.
  3. Step through the code in a debugger: Pretty crucial if you're trying to understand the details.

Hypothesise:

  1. Flowcharts might come in useful in building your hypotheses: They can be very useful for clarifying what a function/etc is doing. Just bear in mind that if the code you have to represent is spaghetti-like, then inevitably your flowcharts will be too!
  2. Change the code: Comment out some code, force the value of a variable, insert extra method calls, etc., etc.
  3. Write unit tests: These should be a bit less ad-hoc than quick code changes. i.e. use them when you're more confident of your hypotheses. They have the added benefit, as Paul T Davies mentions, of being extremely useful when it comes to any future refactoring.

Test:

Run your changed code/unit tests and observe again! If you were right, document it (comments, etc.) and move on to the next thing (which may be tidying up/refactoring the code). If not, make some new hypotheses based on your new data.

1
  • I like the idea of running through the debugger. Thannks. Nov 6, 2012 at 15:32
2

You might also find some help in the answers to this StackOverflow question: https://stackoverflow.com/questions/3150900/is-there-any-free-c-c-code-to-flowchart-generator-tool-available-on-the-net

2

What have worked for me is:

  • Make yourself a copy of the source and a test environment ( for example a copy of the database ) so you can mess around without causing any damage.

  • Refactor, extracting to a function/method/script every block of repetitive (or non-repetitive) code that you understand well enough and safely enough. That way you will be taking out of sight everything that you already understand, uncluttering what you don't.

  • You can print certain pieces of long code in order to study them on paper. This helps, I don't know why, maybe because you can jot arrows and circles around things.

  • What you will have at the end is a shorter, uncluttered, refactored version of the code. I'm not telling you that you replace the production code with the refactored one ( that's up to you ). But the process of refactoring in a safe environment will have helped you understand the still messy production version.

2

Not an easy task, but... may be a documentation tool like doxygen, even if applied to an undocumented code, ca do a good inventory of namespaces, classes, function, the way they interact each other, how they reference each other end so on.

It does not explain what everything does, but at least helps to start to figure out how the code is composed. At least it keeps spaghetti sorted!

1
  • That's the first thing that I do with any inherited code.
    – Mawg
    Aug 26, 2015 at 13:00
1

IMO one of the quickest ways to get to the bottom of unfamiliar code is to track down the original developer(s) (e.g. on Linked-In) and make contact, asking a few high level questions (you may need to send some code snippets to jog his / her memory). Obviously you'll need to gloss over your actual feelings toward the quality of the code.

Most developers I know are usually reasonably proud of their work (whether justified or not), and are only too happy to divulge knowledge on their previous systems (also, the rationale is that the culprit probably still earning a living as a developer in the same state / city, so is likely to want to keep his / her reputation intact).

If the previous dev can convey some of the conceptual patterns (or anti patterns) they have used, it may help you quickly help you crack open much of the code base.

And even if you can't track down the dev, or get no response, by taking time to phrase a question ('Why did you do X here?') it might jog your own thinking process toward a solution.

0

I'd start with system testing just to see what the program does and then link these to brief flowcharts to show how it does what it does. System testing alone only lets you understand what the system does not how it does it and flow charts alone tends to give you a rather abstract look of the system and it can take a while to be able to say what button x actually does so it's best to use them together IMO.

0

The quickest and most instructive way to learn a new base of code, whether it's elegant or spaghetti, and when you don't have resources like documentation or knowledgeable people to ask, is to change something. This might as well be a change in the direction of the needs of the business. But keep it a small change to start with.

Refactoring something small is a good way to start. E.g. a class that appears to have some tacked-on behaviour (e.g. a class that handles managing some data, but also writes that data to a file format), and factor this out into a sub module (that writes the given data to the needed format). Removing code is good fun: Try to eliminate a global variable, or eliminate a member variable that can in fact be local. Try to eliminate an entire class that appears to do very little for the lines of code it takes up.

If the code is uncommented, this is not necessarily a total loss. It may be that the variable names have been chosen well. If not then you will have extra work to do. Rename variables following your own naming convention that makes sense and don't be afraid to use long variable names. There are lots of opinions on variable naming, but I always think "capture sense, not type". Renaming badly named variables will force you to understand what each variable is used for (which is something the original author was too lazy to do).

As a task in itself, drawing diagrams of what you think is going on is a waste of time and is just a diversion from what you need to do which is, and there is no way of getting away from this, to read through the code. It can be better to paste small extracts of code from different classes and modules in a text document and describe what is going on that way. You may need to do diagrams later to clarify things at a high level or to help explain code to someone else. Explaining the code to someone else is a good way of increasing your own understanding.

These suggestions have been made already: Writing unit tests and contacting the previous author are definitely a part of attacking an inherited codebase.

Not the answer you're looking for? Browse other questions tagged or ask your own question.