Ask Slashdot: How To Start Reading Other's Code?
BorgeStrand writes "I'm reviving an open source project and need to read up on a lot of existing code written by others. What are your tricks for quickly getting to grips with code written by others? The project is written in C++ using several APIs which are unknown to me. I know embedded C pretty well, so both the syntax, the APIs and the general functionality are things I wish to explore before I can contribute to the project."
If there's a lot of documentation, interpret it like your favorite religious text. Try to hit up some of the old developers from the VCS. Also, I'd like to help :)
Knowing the data structures gives you the ground work for understanding what the code is doing. The data structures are a more direct description of the design decisions.
What are your tricks for quickly getting to grips with code written by others?
For me, it comes down to a lot of mountain dew, techno music, and hours of guru meditation. As you dissect each function, sketch out its relationship to other major functions. I take a two pass approach .. first, just look at the function call outs and the return values and make a rough sketch of the 'scaffolding' of a program. On the second pass, any function that you can't see the obvious application of, or appears obfusciated or complicated, dissect into functional units and sketch out what it does in your notes. I do this by actually physically drawing the relationships using something called a mind map.
Until you get used to it, actually writing it down, even if it's just a bunch of messy arrows to blobs of circled text... it will help job your memory and help things sink in until you have the necessary 'ah ha!' moment.
YMMV.
#fuckbeta #iamslashdot #dicemustdie
Look straight at the code for a few hours without moving an inch. After that its details should be printed into your brain.
If possible, I would try writing unit tests for the existing code. This tests your understanding of what you are reading and will come in handy later if you change the code. If unit tests already exist then I suggest that you read them since they will tell you the intention of each function.
Everything else will seem simple after that...
Any insufficiently advanced magic is indistinguishable from technology.
Even without Doxygen's specific format for comments, you can use it to graph object relationships, call-trees, etc.
You can generate docs limited to a few files or classes if you just want to focus on them.
www.doxygen.org
First, figure out how the code gets loaded and runs. Find the equivalent of the 'main' function. Then start tracing it, seeing what functions get called, how things are loaded, etc. What really helps here is an editor you can CTRL+click on a function on to go to its definition. When you hit a function that doesn't call any other unknown functions, then you can start understanding what it does without having to step into it. These are the basic functionality units. Then when you know enough of those, you can start going a level up, etc. Eventually a picture forms in your mind of just how things work. You can optionally skip over functions for preference of looking at them later if it seems pretty clear what they do based on the name & how they're called, but you might find important stuff in there later. This is how I go about it, anyway. It can be very frustrating and very confusing at first, but eventually the picture starts making sense, then things click in a most satisfying manner. That being said, the above is also the reason I can dislike complicated frameworks. There's so much indirection that it can take quite a while indeed until you hit something concrete. The mark of a good framework is, either it doesn't do that, or it does but soon enough you figure out its parts and then you can treat it intuitively.
I find that going through some key functions (assuming you can find them) and reformatting them to your own liking can be helpful, commenting code along the way. Then if you want to get more aggressive, start cleaning up some code in minor ways that still stay true to the function's meaning. After you've done a bit of that, you should probably have at least a vague idea what's going on.
People who say "sheeple" have about as much sophistication as an AOL user, and in fact are probably actually AOL users.
It won't help you understand the code but you'll stop worrying about it so much.
I recommend starting by working on the bug list. It gives you something to work on constructively and it also makes you look through all the code to track the problem.
The trouble with university education, is that most people who teach there are computer scientists, not software engineers with years of experience in the trenches.
If this were actually the case, there would be a recognition that reading code is far harder than writing it. And far more emphasis would be on coming to grips, understanding, and working on large code bases. There'd be more stuff on things like unit testing, breaking dependencies, troubleshooting, and refactoring at least.
Find out what drugs the original coder was using when writing, and take the same.
Find a function. Refactor it until you grok it. Discard the results.
Keep in mind that it will be VERY tempting to commit your changes, but you must throw away the work and chalk it up as a learning experience if you ever want to be taken seriously by the others who work on the project. Junior developers (and even some senior developers) often think they're doing everyone a favor by doing drive-by refactors, but they're not; they're just slowing down the entire team and coming across as that a**hole who keeps f***ing up the diffs and destroying the useful output of tools like git blame.
If you found any bugs in the previous step, make a patch with the absolute minimal change to fix each individual bug. IMPORTANT: Before committing the patch, first be sure that you can reproduce it in the old code, and that the test case is fixed by your new code.
Repeat the process until you understand the entire system.
With any luck, you will finish with a solid understanding of how the code actually works, and you will most likely also fix a few dozen bugs (if you didn't find at least one bug per kLOC, then "you're doing it wrong" or the code was written by an inspired genius with OCD). At that point, you will be the team's expert on how things work, and you will be in a position where you can start proposing simple refactorings that will improve the code quality.
You mentioned you have embedded C experience and the code of interest is written in C++. You didn't mention if you had any C++ or other object-oriented programming experience. I assume the C++ code uses the OO features of C++ that distinguish it from C -- but this assumption is not necessarily true.
So, if you lack OO experience and the code is truly OO C++ code, you might want to do a little reading up on the basics of OOP in order to spend less time spinning your wheels.
I am not a crackpot.
Here is how I work on legacy code:
1) I don't look at the whole picture because there are too much details, so I prefer to attack little by little.
2) I quickly check what I can rewrite in order to optimize the code. If I have no idea, I run a profiler, and take a look at the routines that take the most time.
3) once I understood or rewrote the most consuming parts (sometimes it's heavily optimized, but most of the time, I can make a real improvement), I decide what most important functionality I would like to add, and I just focus on that.
4) if I really need to have robust code, I write tests for the routines before optimizing them, so that I can validate if there are regressions
5) whenever possible, I use "assert" and put some bound-checking tests, in order to validate the ranges of certain values or conditions.
The important thing is to start by taking ownership of a small part of the code, then a bigger part, etc... ... ?
Take one slice at a time, not the whole pie.
And one last point: knowing every little detail is useless, concentrate on what is important for you: performance, functionalities,
2) Just because the code is awful doesn't mean it has no value -- No matter how bad it is and how difficult it is to read, if it works at all it has probably got years (maybe even decades) of bug fixes and feature requests. Until you have a handle on it, any little change could cause a catastrophic cascade of side-effects.
3) No, we don't need to rewrite it. See 2. A working program now is worth more than all the pie in the sky you can promise a year from now.
4) It takes 6 months to have a reasonably good grasp of any moderately complex in-house application. It could be a year before you get to the point where someone can describe a problem and you immediately have a good idea of where in the code the problem is occurring and what functions to check.
Maintenance programming is as much about detective work as anything else. The only clues you have about the previous programmer are his source files. Once you've read them for a while you can start to tell what he was thinking, when he was confused, when he was in a hurry. Most of the atrocious in-house applications have changed hands several times and each programmer adds their own layer of crap. You can redesign these applications a chunk at a time until nothing remains of the original code if it's really bad, but it's best to save really ambitious projects until you understand the code better. I heartily encourage the wholesale replacement of "system()" calls with better code immediately, though. In several languages I've run across these calls to remove files, when they could have simply called a language library call (Typically "unlink".) If the original programmer used system("rm...") you can pretty much assume that they were a bad programmer and you're in for a lot of "fun" maintaining their code.
I'm trying to teach myself to set people on fire with my mind... Is it hot in here?
Read this: http://stackoverflow.com/questions/3586073/reading-others-code
Also: http://www.codinghorror.com/blog/2012/04/learn-to-read-the-source-luke.html
Since this is an OSS project, can you suggest any tools similar to Understand that don't cost $995?
The only thing I could find was source navigator NG, but I have zero experience with it.
echo "i am here";
or print or console.log or printf or ...
The problem with slashdot is that most of its users were bullied and stuffed into lockers as kids!
Reading other people's code is a punishment that one must master if you hope to grow as a programmer.
So what is the reward for reading comments which are unnecessarily set in monospace type?
"You're right," Fisheye says. "I should have set it on 'whip' or 'chop.'"
You can use doxygen to create a dependency graph and visualize it using CCVisu.
http://www.stack.nl/~dimitri/doxygen/
http://ccvisu.sosy-lab.org/
http://ccvisu.sosy-lab.org/manual/main.html#sec:input-dox
It's just how your brain works. It's a lot easier to examine a piece of mechanical machinery when it's in motion. You notice more. Do the same with the code. Run it. Run components independently. Put plenty of log statements or if it's feasible, watch under a debugger. But don't try to look at stale code just sitting there. You'll notice more as it moves.
Any guest worker system is indistinguishable from indentured servitude.
That's assuming the original programmers followed the MVC pattern. Very often they didn't. That's when you start reading tea leaves.
This calls to mind the story of the king who was in a class for learning algebra, and after realizing it was hard, he took the teacher aside and said 'I'm the king - show me the easier route'.
There's isn't one. Sit down and read it. Then re-read it. Then think. Then read it again. Think again. Repeat.
"The greatest lesson in life is to know that even fools are right sometimes" - Winston Churchill
print statements are the greatest debugging tool ever invented.
it will work on any piece of code, any language, any type of situation. you can trace anything.
You will never fully understand the code just by reading it. My approach is to ignore all of it until something needs to be changed. When you need to change something, add a feature, etc... find where in the code the functionality is and tweak it a bit. See what happens. Tweak it some more. See what breaks. You will start to get a deep understanding of a focused section of the code and not have to worry (yet) about other unrelated areas. Start with small changes first. Larger changes may require a deeper understanding of the architecture and how pieces interact. This will come in time. After a few iterations of this and you will eventually become intimately familiar with all the pieces of the code.
Ascalante: Your bride is over 3,000 years old.
Kull: She told me she was 19!
Spend an afternoon or three skimming around the code pulling threads and following them. Jump around kind of randomly, if things start making sense in one module, go somewhere else for awhile. Take frequent breaks. Take notes about what you think things are doing, or perhaps ideas about how to improve the code - but don't start improving things now, you just want to figure out how much you're in for.
After awhile doing that, you should have a few ideas about good accomplishable problems, now pick one and go deep for a limited time (hour, afternoon, week, depends on the scope of the code and your commitment to it). Again, keep notes, and then throw all your work away (or check it in somewhere - but don't focus on shipping, that detracts from learning). Again, go somewhere else in the code, fix something, take notes, throw it away. Alternate back and forth between research and application, trying not to bias towards one or the other (which can be a form of procrastination).
Now throw away all your notes. They were written by someone who had no idea what was going on. By now you're pretty sure you know what's going on (you don't) and how to make things better (you have no idea), so circle around for another pass. Stop when you start finding that your notes seem to be recognizing actual immediately-actionable problems and solutions, rather than hypothesis and speculation. Or just stop because you're now so busy fixing things that you don't have time for exploration.
Find the person who wrote the code. Make sure that educating you or your colleagues is part of their paid responsibilities, and make sure that you respect their work when reviewing it: this helps them share the ir work and take it well if you need to revise things. My colleagues and I often bring new features or help stabilize old projects, and a working relationship with the original author is invaluable.
And ff the author says "just read the code, I don't do documentation because documentation can lie", or if they say "don't bother checking the data for correctness, just don't make mistakes", be ready to throw out _everything_ they ever wrote. It may work at the moment, but it's likely to be as broken and unsustainable as their attitudes.
I always find this greatly depends on the quality of the code, which varies greatly from well written and documented and just involves some boring reading and tracing through of execution paths too the absolutely appalling where you can sometimes only understand why the fuck they did something when you change the code and see how it breaks. The former I usually rely on a quiet room and lots of caffeine, the later requires swearing, loads of code changes to trace what is actually happening and cursing the original author, their relations and the goat you are sure they must have been molesting while writing the code.
A person is a genius if he knows every nook and cranny of C++. But no one is expected to. Even just classes and objects are a fantastic addition over C, so there is really no reason to shun C++.
The problem is that taking C and just adding classes and objects would have been nice, but the changes in C++ go so far beyond that they can reach a perl-like level of syntax confusion.
William of Ockham had no beard. The most likely explanation is that it was chewed off by squirrels every morning.