Building Better Software › because for some weird reason, users want their software to work
/**/
Building Better Software
because for some weird reason, users want their software to work
Skip to content
About Andrew StellmanAbout Jennifer GreeneAbout this siteContact UsGet training from Stellman & Greene
How to hold a more effective code review
Saturday, September 20, 2008 - by Andrew Stellman
A lot of programmers feel like being asked to go to a code review is like being told by mom to eat our veggies. We’ll complain about it, and even if we do eventually swallow them we’re determined not to enjoy them.It’s something I’ve seen over and over again: programmers groaning about having to go to a code review, usually because someone gets some big idea about making things better, and decides this is how you do it. There’s sometimes a little nervous joking at the beginning of the meeting about how nobody really wants to be there. And after it’s done, a lot of us get the distinct feeling that it was a waste of time.The thing is, code reviews can be a really good thing. And not only that, they don’t have to be a chore. If you do them right, people on the team can start to appreciate them and even - heaven forbid - enjoy them.So how do we make code reviews more palatable? We need to think about what motivates us as programmers. Programmers love to code. We love building things, and we love solving problems. But we hate anything that seems bureaucratic or tedious, and we definitely hate meetings. But most of all, we hate being in uncomfortable social situations that require us to confront people face-to-face. We’re not alone in that; most people hate situations like that.I think that’s a pretty big part of why programmers intuitively dislike code reviews. It’s not that we’re afraid of putting our work out there for our peers to see. That’s actually something we look forward to: we love to show off code that we’ve worked so hard on, and we definitely appreciate the input from the people around us. But it’s almost never the person whose code is being reviewed who groans about it. No, it’s usually the people who are asked to attend the review. And I think I know why: it’s because we don’t like being asked to criticize the work of others, openly and without hesitation, for the good of the team. I think we naturally feel uncomfortable putting someone else in the position of having to openly confront their errors, because it’s so easy to imagine ourselves in that same position.And that may be the secret to holding code reviews that people actually look forward to attending: make it about helping make the code better, not criticizing and finding its flaws. If you can come up with a way to avoid bitter arguments about tiny details and stubbornly-held opinions, and instead concentrate on helping someone improve his or her code, then people will stop thinking about code reviews as an uncomfortable meeting and start thing about them as a way to build better software.That sounds like a tall order. How do you do it?Well, you start out by thinking about one of the best things that can happen in a code review, something that I’ve seen many times. It usually happens about two thirds of the way through the review, about the time when the first signs of meeting fatigue are starting to set in. Someone points out another problem with the code, and a conversation starts up about an aspect of it that nobody really thought of. Uh-oh — it’s a bug, a particularly nasty one that. Then everyone kind of looks at each other with a weird mixture of relief and disgust, because we found a bug that a) definitely would have made it into production, and b) would have taken hours or days to track down and fix.I’ve said this many times before: programmers are very practical people, especially when it comes to our own time. If something will save us time down the road, we’ll definitely do it. If you can show a programmer that a tool or technique (like a code review) saves more time than it costs, that’s a great way to get him or her to start thinking positively about it.That doesn’t change the fact that a lot of people get nervous about code reviews, even people who have done them a bunch of times. So I spent a little time thinking of advice I’ve given about code reviews over the years. Some of this is pretty standard code review stuff, but I think it’s worth repeating because people have so many different views on how to do code reviews effectively. And I think that if you think about them, and get other people on your team thinking the same way, it could definitely help you hold effective code reviews.So here’s some advice about holding better code reviews — you can think of them as code review tools (or even code review best practices, although I’m not a huge fan of that term) that can make your software better:First things first: there are a lot of different ways to do a code review. Some people like to follow a very strict process. Personally, I like to keep them informal; the more it seems like an everyday conversation, the more work we actually get done.It’s important to keep the meeting to under two hours — any more than that and meeting fatigue sets in. Most code review teams can handle between 200 and 400 lines of code in a two-hour review meeting. (Your mileage may vary.)Don’t forget to distribute the code before the review, and make sure you give everyone enough time to actually look through it. Send around a PDF of the code (a2ps is a good way to make it readable, and it’s got stylesheets for almost every language). Also make sure that everyone also has access to the source, and that they know how to build and run it. Sometimes it’s a lot easier to prepare for a code review if you can actually debug your way through the code.Make sure that everyone knows you appreciate their time. It’s easy to forget that, but it helps the team see the review as a useful tool and not just another boring meeting. Remember, you’re pulling half a dozen or more people into a room for two hours, plus preparation time — that’s the equivalent of taking a top developer off of all projects for two days. That’s also why it’s very important to choose a good block of code to review. Choose one that’s inherently risky: a difficult algorithm, code from a library that many other people depend on, an interface a lot of people will use, a particularly nasty bit of spaghetti code.Code reviews and refactoring work really well together. Look for opportunities to extract methods, rename variables, replace literals with constants, etc.Pay attention to OOP principles, especially encapsulation. Improving encapsulation is an easy and effective way to prevent bugs.The code review isn’t just about bringing up issues — it should also be about giving some indication of how to resolve those issues. Ideally, the programmer whose code is being reviewed should be able to read through the results of the review and very quickly implement the fixes, because in the meeting we wrote down exactly what needs to be fixed and how to fix it. (We don’t actually have to write down lines of code, of course — just enough information so it’s clear what to do.) A good way to do this is to make the goal of the meeting to be to produce a log, or a list of fixes that need to be made to the code.Instead of storing the log in a spreadsheet, Word document, or wiki page (I’ve done all three), try having the moderator put the results of the review directly into the code as comments (which includes an easily searchable unique string like ‘// %%TODO: CODE REVIEW 8/28/08%%’, so the programmer can find them all). The results of the review meeting can be e-mailed out a link to a diff report in the source repository. When the programmer goes back to update the code, he or she can alter the comments to make sure they make sense in context — but they can stay in the code because they’ll make more sense, and it’ll be clear why the code is the way it is if someone is maintaining it in the future.A good way to focus the discussion is to guide the conversation away from arguments about coding in general, and towards what to write down in the log to resolve the current issue. Make a good effort to figure out how to resolve the issue: most can be resolved in the meeting. Any issue that can’t be resolved in a reasonable amount of time gets added to the log as an open issue.I’ve always gotten a lot of mileage out of using a moderator. The moderator’s job is to keep track of the discussion, and keep the discussion on track. If people are getting off onto a tangent that won’t benefit the code, or if they’ve gotten into a disagreement where there are merits on both sides of the issue and it clearly won’t be resolved, the moderator should suggest that we log it as an open issue and move on. You can always follow up later and resolve the issue.Some people get very strict about making sure that the moderator stays at arm’s length, and doesn’t get involved with the review. Personally, I think code reviews are hard enough without imposing arbitrary rules like that (because we’re laying someone’s code bare and dissecting it, which can be difficult for anyone who’s not used to it). We’re all adults here, and we can trust any of us not to “abuse” a moderator role. If the moderator has something to say, he or she should say it. If it’s easier, replace “moderator” with “note-taker” or something like that.Don’t be pedantic, and try to avoid theoretical discussions. It’s really easy to get bogged down with a discussion that doesn’t go anywhere about whether this variable declaration should be here or there, whether this type of structure or that is slightly more efficient, if we could do something better in theory if we scrapped a large amount of code and rewrote it. If a discussion isn’t going to directly lead to a change, even if it’s an interesting topic, note it in the log as an open issue and move on. And definitely don’t point out spelling errors. A lot of grate programmers are lousy spelers.Make sure variable names make sense, but don’t worry about naming conventions. Some people love underscores in front of parameters, some people hate them. I’m sure you can come up with at least three different “official” conventions for any programming language. There are few things less useful during a code review than an argument over whether to use PascalCase or camelCase.One way people like to do reviews is to have people “read” the code - interpret it out loud. I’ve had some success with going around the table and having people take turns “reading” each chunk of code. If there’s a chunk that is difficult to “read”, it’s not clear enough and is a good candidate for refactoring.Before you distribute the code to the team, run it through a static code analyzer (like FindBugs or FxCop) and fix issues that are found. There’s no need to waste discussion time on problems that a tool can catch and log for us.I’ve had a lot of success with a kind of review called a “high impact inspection” (that’s a term that was developed at HP about fifteen years ago). Basically, it boils down to having everyone do the code review independently and e-mailing their issues to a moderator. The moderator puts the issues into one big list, sends them back out to everyone, and then the review meeting itself is focused on just going through those issues. Jenny and I developed a code review process similar to high impact inspections to let us hold inspections in teams outsourced to India (where time zone differences make it very difficult to regularly schedule two-hour meetings). We ran it a bunch of times, and it worked really well.When Jenny and I were writing the section on code review techniques in our first book, Applied Software Project Management, we came up with a checklist of things that you should look for during a code review. That should give you a good starting point for coming up with a good review.Good luck with your code reviews! If you end up with a good code review success (or failure!) story, I’d love to hear about it.Did you like this post? You can help us out by sharing it! These icons link to social bookmarking sites where readers can share and discover new web pages.
Filed in Code, Quality
|
Comments (0)
Beautiful Teams coming into the home stretch
Sunday, September 7, 2008 - by Andrew Stellman
We’ve gotten more than a few e-mails from readers wondering where we went. First of all, don’t worry - Jenny and I are still around! We’ve just been working overtime on our latest book, Beautiful Teams (here’s the Amazon page for it). It’s coming out well, and we’re really excited about it.Beautiful Teams is a new kind of book for us, because it’s the first time we’ve done more editing than writing. We brought in a great team of guest writers and thinkers, people we really respect and whose work we’ve been big fans of over the years: Scott Berkun, Karl Wiegers, Karl Fogel, Johanna Rothman, Barry Boehm, Steve McConnell, Grady Booch, James Grenning, Cory Doctorow and a whole lot more. A good portion of the book will be stories from their careers, which was a big departure for us: it was as much about creative writing (helping our authors make the stories fun to read) as it was about software engineering. And in addition to the stories, Jenny and I have been doing interviews with some of these great thinkers, and I’m extremely happy about how they’re coming out. We’ve put a lot of effort into finding people who are thoughtful and knowledgeable, but also really good at talking about what they do and how they think. All in all, it should be a very unique and fun to read book.Oh, there’s one more thing I want to tell you about the project - actually, my favorite part about it. We’re donating almost all of the royalties to PlayPumps International, a truly wonderful charity that digs wells to deliver clean drinking water to rural schools and villages in sub-Saharan Africa. If you haven’t heard of them, take five minutes and watch this piece that Frontline did on them.I’m sure we’ll give you plenty more details about it as time goes on. In the meantime, hopefully we’ll be able to post a little more here for you!Did you like this post? You can help us out by sharing it! These icons link to social bookmarking sites where readers can share and discover new web pages.
Filed in Books
|
Comments (1)
Unit testing and the narrowly averted Citicorp Center disaster
Saturday, May 24, 2008 - by Andrew Stellman
I was working on a project earlier today. Now, typically I always do test-driven development, where I’ll build unit tests that verify each class first and then build the code for the class after the tests are done. But once in a while, I’ll do a small, quick and dirty project, and I’ll think to myself, “Do I really need to write unit tests?” And then, as I start building it, it’s obvious: yes, I do. It always comes at a point where I’ve added one or two classes, and I realize that I have no idea if those classes actually work. I’ll realize that I’ve written a whole bunch of code, and I haven’t tested any of it. And that starts making me nervous. So I turn around and start writing unit tests for the classes I’ve written so far… and I always find bugs. This time was no exception.This time, for some reason, that exercise reminded me of the story of the nearly disastrous Citicorp Center building.Citicorp Center was one of the last skyscrapers built in the New York City skyscraper and housing boom in the 1960s and 1970s. A lot of New Yorkers today probably don’t realize that it was actually one of the more interesting feats of structural engineering at the time. The building was built on a site occupied by St. Peter’s, a turn-of-the-century Lutheran church that would have to be demolished to make way for the skyscraper. The church agreed to let Citigroup demolish it, on one condition: that it be rebuilt on the same site.The engineer, Bill LeMessurier, came up with an ingenious plan: put the base of the building up on columns, and cantilever the edge of the building over the church. Take a look at it on Google Maps’ Street View — you can pan up, navigate around, and see just how much of a structural challenge this was.The building was completed in 1977. A year later, LeMessurier got a call from an engineering student studying the Citicorp building. Joe Morgenstern’s excellent 1995 New Yorker article about the building describes it like this:The student wondered about the columns–there are four–that held the building up. According to his professor, LeMessurier had put them in the wrong place.“I was very nice to this young man,” LeMessurier recalls. “But I said, ‘Listen, I want you to tell your teacher that he doesn’t know what the hell he’s talking about, because he doesn’t know the problem that had to be solved.’ I promised to call back after my meeting and explain the whole thing.”Unfortunately, LeMessurier was mistaken, and in the article he describes the problem in all its gory detail. It’s a fascinating story, and I definitely recommend reading it — it’s a great example of how engineering projects can go wrong. It’ll probably seem eerily familiar to most experienced developers: after a project is done, someone uncovers something that seems to be a tiny snag, which turns out to be disastrous and requires a huge amount of rework.Rework in a building isn’t pretty. In this case, it required a team to go through and weld steel plates over hundreds of bolted joints throughout the building, all over the weekends so nobody would find out and panic.But what I found especially interesting about the story had to do with testing the building:On Tuesday morning, August 8th, the public-affairs department of Citibank, Citicorp’s chief subsidiary, put out the long delayed press release. In language as bland as a loan officer’s wardrobe, the three-paragraph document said unnamed “engineers who designed the building” had recommended that “certain of the connections in Citicorp Center’s wind bracing system be strengthened through additional welding.” The engineers, the press release added, “have assured us that there is no danger.” When DeFord expanded on the handout in interviews, he portrayed the bank as a corporate citizen of exemplary caution–”We wear both belts and suspenders here,” he told a reporter for the News–that had decided on the welds as soon as it learned of new data based on dynamic-wind tests conducted at the University of Western Ontario.There was some truth in all this. During LeMessurier’s recent trip to Canada, one of Alan Davenport’s assistants had mentioned to him that probable wind velocities might be slightly higher, on a statistical basis, than predicted in 1973, during the original tests for Citicorp Center. At the time, LeMessurier viewed this piece of information as one more nail in the coffin of his career, but later, recognizing it as a blessing in disguise, he passed it on to Citicorp as the possible basis of a cover story for the press and for tenants in the building.Tests were at the center of this whole situation. It turned out that insufficient testing was done at the beginning of the project. Now, more tests were used to figure out how to handle the situation. Tests got them into the situation, and tests got them out.So what does this have to do with software?I have a hunch that anyone who’s done a lot of test-driven development will see the relevance pretty quickly. The quality of your software — whether it does its job or fails dramatically — depends on the quality of your tests. It’s easy to think that you’ve done enough testing, but once in a while your tests uncover a serious problem that would be painful — even disastrous — to repair. And as LeMessurier found, it’s easy to run tests that give a false sense of security because they’re based on faulty assumptions.I’ve had arguments many times over my career with various people about how much testing to do. I can’t say that I’ve always handled them perfectly, but I have found a tactic that works. I point to the software and ask which of the features doesn’t have to work properly. But it’s good to remind myself how easy it is to question the importance of tests. It’s so easy, in fact, that I did it myself earlier today. And that’s why it’s important to have examples like Citicorp Center to remind us of how important testing can be.Did you like this post? You can help us out by sharing it! These icons link to social bookmarking sites where readers can share and discover new web pages.
Filed in Quality, Test-driven development
|
Comments (0)
Writing about software
Friday, May 9, 2008 - by Andrew Stellman
Jenny and I are at the tail end of our fourth book, Beautiful Teams, and it’s really coming out well. We’ve put together a team of authors who’ve got some really fascinating stories about building software. Each of them has written a really compelling story from his or her past career. We’ve got people writing about all sorts of industries, and running into (and, in most cases, overcoming) a lot of different kinds of problems. All together, the collection is starting to paint a picture of what it’s like working in software.One thing that I thought would be hard about this project was the coaching. These are software people, after all, not creative writing people. We have a few veteran authors on the team, but we also have a lot of people who hadn’t really done a lot of writing. And the authors themselves, in a lot of cases, were just as apprehensive as we were. It seemed like most of the people we worked with knew they had a good story to tell, but were worried about whether or not they could really tell it well enough.It turned out that we didn’t have anything to worry about. I was very impressed with how well everyone “got it”. We put a lot of effort into selecting people who had a good story to tell, and it only took a small amount of guidance to help the authors tell them. Even people who hadn’t really done a lot of writing before really took the time to flesh out the characters, give us all the conflict, and really draw out the stories to make them fun to read.Another thing that Jenny and I really wanted to do with Beautiful Teams was to put storytelling ahead of teaching. There are plenty of books that will teach you about building software. And we knew that there would be a lot of good lessons about software in the book. But our goal wasn’t a book that you could pick up and suddenly do your job better. The goal was to put you in the shoes of someone who’d been there before. Or, even better, we wanted to put you in the shoes of someone who was in a really interesting situation. Or a really bad, or even unimaginable, situation. We wanted to show you teams that were great to work on, and teams that, despite being awful, managed to muddle through. Or didn’t.We’d never done anything like this project before, and we when we started it, we weren’t sure if we’d end up with engaging stories, or if we’d just get people writing about their boring projects from work. We were lucky. It turns out that on most memorable software projects, there’s some interesting drama: a bad boss, a deceitful or weird coworker, a serious and last-minute crisis that needs to be dealt with, a loud and unreasonable client. And it’s those very things which made the projects interesting that make the stories interesting to read.At the beginning of the project, I was worried that people who wrote good software might not necessarily be good authors. But they really took to telling their stories. A lot of the authors told us that it was actually cathartic to get their stories on paper, and most of them really seemed to enjoy doing the project.To me, the best part of the project is the fact we’re having royalties from the book donated to PlayPumps International. It’s a great charity that came up with a novel way to provide clean drinking water to rural villages and schools in South Africa. If you haven’t heard of them, I highly recommend watching this Frontline segment about them.I’ll post again once the project is done with details about the stories. (We’re still getting the last ones in, so it’ll have to be a surprise for now!)Did you like this post? You can help us out by sharing it! These icons link to social bookmarking sites where readers can share and discover new web pages.
Filed in Books
|
Comments (0)
It’ll take about three weeks…
Thursday, March 20, 2008 - by Andrew Stellman
If you’ve been reading our posts here, you probably noticed that we like to give our “Why Projects Fail” talk. (If you’re curious, here’s a link to the slides [PDF].) One reason we really like it is that it seems to go over well with a lot of different audiences, from hard-core architects and programmers to grey-haired project managers. It’s a pretty informal talk — we interrupt each other and go off on the occasional tangent, which keeps the mood pretty light. And that’s always a good thing, especially when you’re doing a talk to people at a PMI meeting or a user group who just spent the day at work and don’t need to sit through yet another boring slide presentation.I was thinking about that presentation yesterday, after getting off the phone with a manager at a company that wants to hire us to do software estimation training for their programmers. One problem that they’re having is a pretty common one. Their programmers, testers, and even project managers seem reluctant to give estimates. That reminded me of this slide from the talk: When we get to this slide in the talk, I usually say something like this:There’s something we call the “about three weeks” problem. Have you ever noticed that when you ask a programmer how long it’ll take to do something, it’s always going to take “about three weeks”? I’ve done it myself many times over the last fifteen years or so. How long to build a simple website to do a few calculations? About three weeks. What about a utility that will automate some file operations and generate a few reports? About three weeks. A new feature for our core product? Sure, it’ll take about three weeks. See, the problem is that three weeks seems like a long enough time to get something significant done. And if you think for thirty seconds about pretty much any programming project, you can do enough hand-waving and ignore enough details so that it’ll seem to fit into about three weeks.What does this have to do with why programmers are so reluctant to give estimates?There are many reasons for this, more than I’ll go into here. But a big one might just be because we’ve all quoted “about three weeks” for a programming project that ends up taking a whole lot longer than that, and we never want to be stuck in that situation again. So after we’ve been burned enough, we just stop giving estimates. I was at a job a few years ago, sitting at a full conference table with a dozen developers. The CTO — an abrasive guy who clearly went home every evening to lift weights, and spent most of his day yelling at the people who reported to him — growled an “order” at the team, demanding an estimate. Everyone at the table knew that they’d be yelled at individually, threatened with dismissal, and generally made miserable if they didn’t come up with an estimate. Yet nobody looked up and volunteered anything. Eventually a junior guy in the back cleared his throat, and in almost a whisper he said, “I’m not sure about the rest of it, but I think my piece will take about three weeks.”And there it is. Nobody wants to go on the record and say how long they think it’ll take to do a job. We all know it’ll take as long as it takes. If the estimate is right, then there’s no great reward or recognition. But if the estimate is wrong, then we’re on the hook for it, and we get to look forward to status meetings where we get to take the blame for whatever terrible consequence happened because the project was late.So what do we do about it?Jenny and I put a lot of thought into this problem when we were working on our first book, Applied Software Project Management. It turns out that there’s a really effective way to get a good idea of how long the software will take without putting any one person on the hook, and it’s our favorite way of generating estimates. It’s called Wideband Delphi, and we talk a lot about it in the Estimation chapter in the book (which you can download for free [PDF]). It’s a straightforward technique — it just takes two two-hour meetings to nail down estimates for even a large project. It’s very iterative and highly interactive, which helps the team all come to a consensus and agree on the final result. It’s collaborative, so that no one person is solely responsible — usually, everyone ends up buying into the final numbers. And best of all, it doesn’t require any special expertise beyond what you need to actually do the project.My favorite part about Wideband Delphi is that it’s really focused on assumptions. That’s another thing I like to talk about in the “Why Projects Fail” talk. If you think that building a particular program is going to take nine weeks, but I think it’s going to take four weeks, we usually aren’t disagreeing on how long it’ll take to do the task. Usually, we’re disagreeing on some basic assumption about the task. For example, you might think that we’ll be building a big GUI with printing support, while I might think that it’s just going to be a console application. That means that we can assume for the sake of the estimate that we’ll either build a GUI or build a console application, and we’ll write down that assumption. That way, if it turns out to be incorrect, we’ll know exactly why the estimate was wrong… and if someone in a meeting later wants to blame us, we can point to that assumption and give a good reason for the delay. (That’s why Delphi has two meetings: the first meeting is mostly taken up with a discussion of those assumptions.)One nice thing about Delphi is that it’s not some esoteric, theoretical thing. Both Jenny and I have done this in the real world, many times, with all sorts of software teams. Delphi really does work, and it actually does a good job of helping team come up with numbers. And those numbers are pretty accurate, at least on the projects I’ve worked on. If you’re having trouble convincing your reluctant team to come up with an estimate, I definitely recommend giving Delphi a shot.Did you like this post? You can help us out by sharing it! These icons link to social bookmarking sites where readers can share and discover new web pages.
Filed in Estimation, Project Management
|
Comments (1)
‹ Older posts
Search
RSS Feeds
All posts
Andrew's posts
Jenny's posts
ContentsAbout Andrew StellmanAbout Jennifer GreeneAbout this siteContact UsGet training from Stellman & Greene
Are you looking for training?
The best developers and project managers are always looking to improve their skills. We offer training courses in programming and project management. Check out our training page for details.
We write books for
Our current project
We're currently working on Beautiful Teams. It's going to be similar to Beautiful Code: a collection of stories from developers, project managers and industry leaders about their experiences leading or wokring with software teams.
Other books by Andrew and Jenny
Some of our favorite past articles and posts
Articles
ONLamp.com: What Corporate Projects Should Learn from Open Source
Dr. Dobb's Journal: Quick-Kill Project Management
Dr. Dobb's Journal: Better, Stronger, Faster Integration Testing
Posts
Don't document your process!
Building Video Games
Kitchen of the Future
What about Agile?
Quality Really IS Free
Open source projects we maintain
Publication Harvester
SC/Gen and Social Networking Report
Scientific Distance Report
CSVReader (a simple open source C# CSV file reader)
MGRS to UTM coordinate converter
Categories
Agile Articles Books C# Code Estimation Gold Plating News PM Clinic PMI PMP Presentations Project Management Project Management Professional exam Q&A Quality Requirements Engineering Software Development Software Engineering Software Process Test-driven development Uncategorized Users
Archives
September 2008 May 2008 March 2008 February 2008 January 2008 December 2007 November 2007 October 2007 September 2007 August 2007 June 2007 May 2007 April 2007 June 2006 May 2006 April 2006 March 2006 February 2006 January 2006
Blogroll Applied Software Project ManagementChris WintersDave MurdockDr. Dobb’s JournalJavaRanchO’Reilly MediaONLampScott Berkun
Meta
Register Log in
© 2008 Andrew Stellman and Jennifer Greene - some portions © O'Reilly Media
¶
Woof, woof.
st_go({blog:'1072570',v:'ext',post:'0'});
var load_cmc = function(){linktracker_init(1072570,0,2);};
if ( typeof addLoadEvent != 'undefined' ) addLoadEvent(load_cmc);
else load_cmc();
_uacct = "UA-1739249-1";
urchinTracker();
|
|