How Intuit Manages 10 Million Lines of Code
drdobbs.com
drdobbs.com
It's always interesting how the larger Fortune 500 software companies adopt practices associated with teeny-tiny little software firms, and vice versa.
Examples I'm intimately familiar with include A/b testing, using usage metrics to drive decisions, customer development, lifecycle email marketing, etc etc. Or for more dev focused stuff, unit tests, Selenium ("the best technology that I'll never use"), code reviews as a routine practice, reproducible server setup/deploys, etc.
That sounds like the Joel test for 2012.
Yes, but I've been pleasantly surprised at the clueyness of of more than a few big-name clients recently.
> Or for more dev focused stuff, unit tests, Selenium ("the best technology that I'll never use"), code reviews as a routine practice, reproducible server setup/deploys, etc.
For deployments at least I've been blown away by the range of configurations I've seen. Everything from "git push heroku master" to "ssh into these three machines, run svn up, restart apache, merge dev couchdb to live, copy paste these 80 sql patches out of a file into the terminal, sacrifice a hamster to Amon Ra and cross your fingers."
Basically it comes down to quantifying these quality works over developing new features.
I had the pleasure to work in a very small tight-knit team where you have to have the bulldog to bark at people when they're slowing down their defense (i.e.: writing unit-tests, making sure build script is up to date).
Results: on-time, within-budget, 1 Saturday, 1 Statutory holiday, 2 days OT until 10.30PM (all re-imburse afterwards) for a rather ambitious project to be shipped in 6 months from zero to live in 2 months afterwards.
Oh, and we have 1 performance bug after the release (easy one to solve) and 1 requirement bug 3 months after release. The rest (been live for about a year) was as smooth as the baby skin. We rotate the BB phone (on-call) but we never had it buzzed.
Our saving graces were:
0) Management Support
1) Continuous Integration
2) Unit-Testing
We use these unit-tests as REPL mostly, so we don't have to re-compile and re-deploy the _whole_ app to GlassFish.
The management almost never questioned us except one or two situations.
But if I may pick a nit about something I think you were describing as a potential process improvement, per-team branches have been tried at scale many times before, both with and without Git. Many smart people I know who've had to clean up subsequent messes believe, counterintuitively, that the larger and more complex you get, the more branches hurt you, because they encourage you to delay integration, and integration is painful. Git merge capability, while excellent, can't save you if two teams independently decide to rename a method that both rely on; that's still a conflict. I've heard of several other large shops relying successfully on toggles with a single mainline.
Fowler describes this pain in terms of feature branches rather than team branches, but it's the same idea: http://martinfowler.com/bliki/FeatureBranch.html
Branching is a poor substitute for genuine modularity.
I've been at workplaces where we have multiple epic branches that diverge for three or four iterations at a time. In this scenario, it doesn't matter what VCS you happen to be using, integration is going to be painful and error-prone.
More recently the branch and pull-request into master model has been working really nicely on a per-story basis, as long as people are considerate and mergeback from master before initiating the request.
Overall though, I think the only branch that gets deployed anywhere should be master (or trunk if you prefer). Like you say, it's a terrible substitute for a system built of decoupled components.
At the time they had this weird home brew mix of PSP and "agile" as their 'methodology' and while it generated a lot of meetings and paperwork, it didn't improve the code one bit. I am glad to know that they are moving towards better practices, but Intuit is the most technically inept organization I've seen, so I wouldn't be too hopeful of the end results. OTOH they do understand their customers (and marketing to them) really really well. I learned a lot from Intuit about these aspects of product dev. Which goes to prove you can make billions with totally screwed up engineering.
No Way.
Because in order to do that, you need to stop writing new code effectively and just heads down and improve things. The thing is, if the codebase is tightly coupled, you've got tons of work to do as typically you can't improve one thing without changing the other. In the absence of unit-testing, nobody has the guts to refactor the code without high-risk of breaking the product.
I haven't even mentioned the cultural (human) issues that need to be fixed/changed as well.
So people truck forward, like in any software development shops :).
Speaking of which, thanks for sharing your insight working for Intuit. The article never mentioned the actual quality of code, but instead focusing on things that he improved :) (not a small feat, but probably don't improve the quality of the code).
It's why I'm a big fan of code ownership as a negative reinforcement tool. Despite it being a really bad idea, it does tend to rein in the cowboys - because they're too busy fixing the bugs in their code to add new ones.
Something that might mean more is growth in code complexity over time. For instance, if measured every month for a year without changing the compiler version or platform, how much bigger or smaller did the compiled binaries become? How much more or less memory and runtime were consumed? How many "severe" bugs were found? When overlaid with information on the features that were added or removed during that time, it should become a lot clearer how large the code base really is and how maintainable it will be.
LoC isn't a perfect metric but it is very easy to relate to. Given some extra context like the type of application, the size of the company or the age of the codebase one can mentally account for some of the weaknesses of using LoC as a metric.
If this were a study using LoC as a sole metric with no other context I'd agree with you but LoC seems perfectly adequate in this case.
It's probably the best metric to use, I just wish there were something better.
Software metrics is a major research area, and dates back to the 60s. The paper "Software Metrics: A Roadmap" has a good summary of the state-of-the-art as of 2000.
After controlling for lines of code, most complexity metrics become worthless.
If not, your alternatives don't serve the required purpose. Everyone gets that a project with 10 million lines of code is big. Whether it's bigger in any useful sense than another project of 8 million lines isn't really the point, and any alternative that doesn't have an immediate intuition for people reading the discussion isn't helping much.
Let's compare with a movie, one that is constantly updated and worked on by many people, if such a thing exists. Is length of such a movie an exact measure of the complexity of that undertaking? No, but managing such a process for a 10 hour movie will probably be harder than that for a 2-minute one, even if it's just because there are probably many more people working on the 10 hour one.
- doesn't have the luxury of having code that's not backwards compatible,
- code that can't be thrown out every 3 years
- code that has been around for more than 5 years and isn't going away
Similarly, we use Perforce. Similarly we have a CI system that builds our code on a farm of VMs, though ours does fairly extensive unit testing on every build. We also have a significant number of version permutations we test on a fairly regular basis as well.
We regularly, automatically, run Valgrind and it's on my to-do list to hook up the Microsoft static analyzer and investigate code coverage tools.
One saving grace is that the code base is very modular and has decently quick iteration times in general. Sometimes the legacy and the degree of cross-platform support can be frustrating but for the most part the infrastructure works so well now that it's no big deal.
If there was ever an application that would benefit greatly from unit testing, this is it.
Code bases where fundamental aspects (models, database interaction, custom UI libraries, etc.) were developed and solidified far before unit testing was the norm or expected are a tough nut to crack, but we are making improvements every day. Within the last three months I've solved compilation and linker hurdles and we're now integrated with googletest and googlemock, which are fantastic libraries!
Also, while our list of C++ unit tests is small but growing now, the use of NUnit, etc. was championed from Day One on C# projects stretching back to our usage of .Net 1.1.
It's interesting how much working with Ruby the last 7 years has affected my C++ development. I'm dual-wielding Avid Grimm's Objects-on-Rails and Michael Feather's Working Effectively with Legacy Code with great results.
That codebase was a beast and there was code from 1979 floating around in some of the core Fortran routines. The scary part was the app ostensibly had backward compatibility to files created in the 80s though it was well designed enough to have anticipated most of cases.
I'm sort of surprised their build takes so long without accelerators but then again I'm not as without using precompiled headers I think our build was several hours longer. If they could not structure their projects to take advantage of pch files and if the source is very #include heavy I can see it. How external code references are handled is another reason interpreted languages rock and Java/.NET are superior to C and C++ build systems in my experience.
Also, instruction for payroll deductions are pretty simple[1]. Usually everything you need is on one page. Plus a very short list of exemptions (401(k), HSA, FSA, cafeteria). Nowhere near 11,000 pages of tax code.
"Payroll deductions are pretty simple" is not as true when you require nationwide support. The Yonkers residency tax, the Indiana counties payroll tax, etc. are not as simple as getting the employees' states and running calcs. Add in non-tax deductions (401(k), wage garnishments, worker's comp) and you've now found yourself in an interesting world.
"You wouldn't guess it," Burt says, "but on each platform, all those products come out of a single codebase. The Windows version is about 80,000 source files, 10+ million lines of C++ code plus a little C# for the .NET parts.
Wow! And they've apparently spent a tremendous amount of time and resources optimizing the build process.
It's been 10 years since I worked on a project where a build took more than a few minutes. Back then, a project I worked on took over 3 hours to compile on a 4-processor Solaris box. The PC technology improved so drastically that in a few years we were able to get it cross-compiling to Solaris from a Linux box in under 20 minutes.
And it had to support all sorts of weird things such as "which compiler do you use to build the compiler; the compiler you just built, the compiler you last used, or the compiler we last shipped?" for a very large configuration of compilers, runtime platforms, etc.
Hearing this makes me want to learn Go. Maybe changing a language to improve compilation speed is a worthwhile feature.
POORLY