Best Practices
Code Complete: A Practical Handbook of Software Construction
Discover the timeless techniques and strategies that help you:
952 pages, Paperback
First published January 1, 1993
Ratings & Reviews
Friends & Following
Community Reviews
"Conquer Complexity". High quality code manages complexity. No one can think of all of the levels of abstraction needed to fully understand a program at once; just admit it and try to make your code less complex. Complexity can be managed at every level of the development process from having a well thought out high level design to choosing good variable names.
"Pick Your Process". Having a process is important. It does not matter exactly what the process is; in fact, the process should be tailored to the problem at hand. The purpose of a process is to allow for coordination between people. When a project is small (1, maybe 2 people), then it is the talent of the individual that matters most. When a project is large, it is managing communication that matters most.
"Write Programs for People First, Computers Second". Computers do not care about programs being readable, but people do, and people are going to read your programs many time. Readable code has a plethora of advantages including lower error rates, being easier to debug and modify, and having shorter development time. Make code readable first, and only optimize when you can make measurable improvements to measurable performance bottlenecks.
"Program into Your Language, Not in It". Do not limit your programming by the features your language supports. Write libraries that will support the programming features you want for the problem at hand. One example McConnell gives writing an assertion library if your language does not support assertions.
"Focus Your Attention with the Help of Conventions". Conventions, like processes, do not matter in their particulars. Some conventions are better than others, but for the most part, conventions tend to be arbitrary. However, having conventions makes code easier to read and modify because a convention can communicate a lot without using much space or requiring much thinking.
"Program in Terms of the Problem Domain". This is a particular method of managing complexity. Higher level code should be supported by lower level code that hides implementation specific details from the higher level code. When done well, this makes the code easier to read and easier to modify. Even at the construction level, this can be done by choosing good class names and abstractions, factoring code into methods to maintain a common level of abstraction, and choosing good variable names.
"Watch for Falling Rocks". Look out for warning signs, such as classes with an abnormally high number of defects. These warning signs do not necessarily mean that something is wrong with that part of the program, but they are a good indicator that you should be a little bit suspicious. These warning signs could show up after construction (error rate) or during construction (compiler warning, indications from your self or other that your program is hard to understand).
"Iterate, Repeatedly, Again and Again". In addition to being my favorite section heading in the book, this principle emphasizes that iteration is appropriate at all points of the software development process. Requirements are rarely fixed in stone, bugs are always present, and developers can always find a better way to rewrite code. Iteration gives all of these improvements a chance to actually make it into the product under development.
"Thou Shalt Rend Software and Religion Asunder". No one convention, process, or tool set is the be all and end all of software development. Developers should be wary of absolutes and try to avoid blind faith in the processes they use. Solutions should be adapted to the problem at hand, not vice versa. The key to keeping an open mind and becoming effective and flexible is experimentation. Be willing to try new things, measure the effectiveness of those experiments, and be willing to change based on the results.
Those are the high level principles. These principles occur over and over again through the seven parts of this book. The first part, titled "Laying the Foundation" discusses the general process of software development and the role of construction (a.k.a. programming) in that process. Construction is important, according to McConnell, because it is the only part of the software development process that absolutely must happen to produce a working software project. Construction is also an area that, traditionally, has not has as much attention to it as other areas (such as high level design or testing). However, McConnell stresses that all parts of the development process are important in creating a successful project and gives pointers throughout the text to resources that discuss other parts of the software development process in more depth. He notes that pre-construction planning is particularly important since no amount of good construction and through testing can save a bad design.
Section two is "Creating High-Quality Code". This section introduces a point emphasized again and again throughout the book. Software's "Primary Technical Imperative" is managing complexity. High quality code exposes people reading it to consistent levels of abstraction separated by clear boundaries. Complexity is managed by minimizing the essential complexity one has to deal with at any given time and trying to keep accidental complexity from spreading throughout the code base. High quality classes and routines provide consistent abstractions, document their assumptions, and check their invariants defensively; they fail sooner rather than later. Even a simple class or routine is worthwhile if it decreases the complexity of reading the code where it is used.
One of the most practically useful facts I got out of Code Complete was learning about the "Pseudocode Programming Process". This process is a way of developing code by starting with detailed pseudocode. When constructing a program, a developer should (iteratively) write pseudocode that is high level enough to be in the domain of the problem but low level enough for translation to real code to be nearly mechanical. Developing pseudocode ensures that the developer understands the problem at a low enough level for implementation, encourages the programmer to think about error checking before implementing the nominal path through the code, may indicate what when to factor code into separate routines (and suggest names for those routines). Those parts of the high level pseudocode that the developer decides to leave in provide automatic, high level commenting of code.
The third section is entitled "Variables" and discusses the effective use of variables. The chapters in this section discuss data initialization (do it close as close to the declaration as possible), variable scope (keep it as small as possible), limiting variables to a single purpose, effective variable names (keep them specific, use a naming conventions), and tips for using fundamental and more complex data types.
Statements are covered in section four called, not surprisingly, "Statements". This section discusses methods for effectively organizing and using straight line code, conditionals, and loops as well as more exotic control structures such as exceptions, gotos, and various table driven control structures. This section discusses how deep nesting of control structures tends to make code complex. If possible, it should be avoided by restructuring the code or factoring the nested code into its own routine. The more paths there are through a code fragment, the more complex it is; the number of paths a developer must consider at a single time should be minimized.
Section five, "Code Improvements" discusses a mishmash of techniques for improving code. It discusses software quality, collaboration, developer testing, debugging, refactoring, and code tuning. One key point of this section is that the goals of a certain construction project should be clear. Some goals are bound to go against each other, and if developers do not know which are most important, they will do a bad job of trying to optimize all of them at once. The most obvious example of this tendency is that aggressive optimization may make code less readable and prevent beneficial refactorings. This section also points out that code reviews, testing, debugging, refactoring, and code tuning all have the potential to improve code quality, but it is when they are used thoughtfully in unison that their potential is maximized.
Section six, "System Considerations" discusses some higher level issues in constructing a system. As project size increases, project quality and development speed tend to go down in a faster than linear manner. This is because as the project increases, more and more overhead gets taken up by managing communication and more details tend to get lost in the cracks. It is for this reason that having a process and conventions becomes more important on large projects; the more that is automatic, the less that quality and and development time will suffer. This section also discusses how to manage programmers and essential tools that every developer should know about and use. This section also discusses several integration processes and emphasizes that which process is right depends on the project being developed.
The final section of Code Complete is "Software Craftsmanship". This section talks about good practices in actually structuring code and how to write good, effective comments and code that documents itself as much as possible. This section also describes the importance of personal character in becoming an excellent developer. McConnell posits that intelligence is less important than other personal characteristics such as humility, curiosity, intellectual honesty, communication and cooperation, creativity and discipline, effective laziness, and good habits. The point emphasized throughout the discussion on personal character is that a good developer needs to be happy and willing to learn from other developers and be willing to admit when their are right and wrong if they want to earn the trust and respect of others.
A useful part of the final section was McConnell's summary of where to find more information. In particular, he presents "A Software Developer's Reading Plan" reproduced below for my future reference. Note that the plan should be supplemented to the needs and interests of particular developers.
Adams, James L. Conceptual Blockbusting: A Guide to Better Ideas, 4th ed. Cambridge, MA: Perseus Publishing, 2001.
Bentley, Jon. Programming Pearls, 2d ed. Reading, MA: Addison-Wesley, 2000.
Glass, Robert L. Facts and Fallacies of Software Engineering. Boston, MA: Addison-Wesley, 2003.
McConnell, Steve. Software Project Survival Guide. Redmond, WA: Microsoft Press, 1998.
McConnell, Steve. Code Complete, 2d ed. Redmond, WA: Microsoft Press, 2004.
Berczuk, Stephen P. and Brad Appleton. Software Configuration Management Patterns: Effective Teamwork, Practical Integration. Boston, MA: Addison-Wesley, 2003.
Fowler, Martin. UML Distilled: A Brief Guide to the Standard Object Modeling Language, 3d ed. Boston, MA: Addison-Wesley, 2003.
Glass, Robert L. Software Creativity. Reading, MA: Addison-Wesley, 1995.
Kaner, Cem, Jack Falk, Hung Q. Nguyen. Testing Computer Software, 2ed. New Yor, NY: John Wiley & Sons, 1999.
Larman, Craig. Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and the Unified Process, 2d ed. Englewood Cliffs, NJ: Prentice Hall, 2001.
McConnell, Steve. Rapid Development. Redmond, WA: Microsoft Press, 1996.
Wiegers, Karl. Software Requirements, 2d ed. Redmond, WA: Microsoft Press, 2003.
"Manager's Handbook for Software Development," NASA Goddard Space Flight Center.
Bass, Len, Paul Clements, and Rick Kazman. Software Architecture in Practice, 2d ed. Boston, MA: Addison-Wesley, 2003.
Fowler, Martin. Refactoring: Improving the Design of Existing Code. Reading, MA: Addison-Wesley, 1999.
Gamma, Erich, et al. Design Patterns. Reading, MA: Addison-Wesley, 1995.
Gilb, Tom. Principles of Software Engineering Management. Workingham, England: Addison-Wesley, 1988.
Maguire, Steve. Writing Solid Code. Redmond, WA: Microsoft Press, 1993.
Meyer, Bertrand. Object-Oriented Software Construction, 2d ed. New York, NY: Prentice Hall PTR, 1997.
"Software Measurement Guidebook," NASA Goddard Space Flight Center.
For more details and up to date reading lists see www.construx.com/professionaldev (requires an account, free with purchase of code complete).
Baecker, Ronald M., and Aaron Marcus. 1990. Human Factors and Typography for More Readable Programs. Reading, MA: Addison-Wesley.
Various works by Kent Beck
Various works by Barry Boehm
Dijkstra, Edsger. 1965. "Programming Considered as a Human Activity." Proceedings of the 1965 IFIP Congress. Amsterdam: North-Holland, 213-17. Reprinted in Yourdon 1982 (Writings of the Revolution).
Disjkstra, Edsger. 1972. "The Humble Programmer." Communications of the ACM 15, no. 10 (October): 859-66.
Knuth's Literate Programming.
Paul Oman and Curtis Cook. "The Book Paradigm for Improved Maintenance" and "Typographic Style is More than Cosmetic".
Schneiderman, Ben. 1980. Software Psychology: Human Factors in Computer and Information Systems. Cambridge, MA: Winthrop.
Spinellis, Diomidis. Code Reading: An Open Source Perspective.
Various by Gerald Weinberg.
This second edition is from 2004, and although obviously some of its content may seem a little dated, most of it still holds up well in 2015. Given that context, I find it difficult to find fault with most of the book. Much of the advice given is good advice, and as a programmer, you should adopt and internalise most of it.
On these grounds, the book deserves 4 or 5 stars, so why do I rate it as I do?
There are two reasons for this.
The first is that, having programmed for some 15-20 years already, I learned little. Although I think this tome could be indispensable for a so-called junior programmer, I only gained little from it.
The second reason is probably an effect of the first, but I was bored to tears most of the way through. Still, I kept reading, a chapter per month or so, since there was an occasional gem here and there.
The rating I've given therefore reflects my experience reading the book, more than the quality of it, which is indisputable.
If you're a seasoned programmer, you may consider skipping it. If you have only a few years of professional experience, you should consider reading it.
One-liner: Read it. 3.5 stars
I came into this with super high expectations. Things I'd heard people say: "I make everyone on my team read this." or "Every developer should start with this book". So, I thought, "sweet, a great 'back to basics' book... I can't wait!". I read through this book with a host of colleagues all with quite different experience levels and in different areas of expertise.
It was the best of times. It was the worst of times.
Well... maybe not the worst, but not my favorite ;)
Needless to say the book didn't live up to my expectations... however, the book is still a gem. I think the book is best approached (especially for more experienced devs) with a 'back to basics' mindset or an 'I'm starting fresh here... forgetting everything I know' type of attitude. It can be incredibly easy to completely discount large portions of this book without that type of attitude.
The Best
The absolute best part of the experience was having a group of peers to bounce ideas off of. Many pieces of information in the book invoked questions in my head that needed some consideration. Having a discussion forum like a book club was an excellent way to solidify my understanding of things I thought I already knew, and to break down some misunderstandings with opinions and feedback from the group.
Some specifics...
Simplicity is a common theme throughout the entire book... and I think rightly so. Software has such a knack at becoming complex, and without always striving for simplicity, the complexity will overwhelm anyone.
In particular, I appreciated the performance chapters where he talks about how to improve performance (loaded term...) in your system. The gist of the story is that you have to measure. If you don't measure how do you know you got better? Developers almost always assume the bottleneck is in the wrong place (odd isn't it?). And this gem of a quote "if it isn't worth profiling [/measuring:] it isn't worth improving". PLEASE keep that in mind the next time you're performance hacking! Oh yeah... just in case that bit didn't do it for you... MEASURE BEFORE AND AFTER you enhance performance! It's so simple, really.
He talks about standardizing in many areas of the book. Standardizing on naming, style, layout, design concepts, standards themselves, etc., etc. Standardizing is great, and keeps things simple... until someone puts out an eye. It's easy after reading this thing to want to go CRAZY on standards. "Spaces here, not there; Braces here, not there; Capital letter here, and there, and there, but not over there!". "A good style applies to most cases" (emphasis mine). I think this holds true for most standards. When a standard is defined in absolutes, I think, it becomes less about simplicity and more about rigidity (BTW, simplicity != rigidity).
Steve gives some amazing stats that are often quite surprising and inspiring, and are usually well backed (although, I'm guessing he could've found stats to back any statement he wanted to make). The wealth of information and completeness of the book in the areas that it touches are impeccable and spot on.
The Worst
I don't want to hate on the book too much (or at all really), but I do have some nits to pick.
This thing is freakin' huge! I had to use a hand-truck to haul this thing around! I needed a social contract to get myself through this book. I had been trying to read it for years. A group of us set out to read this together, meeting once a week as a book club, so that gave me the social commitment I needed to get through this monster.
I felt a general 'anti-Agile' undertone, though he never really stated it explicitly. I'm no Agile Zealot, but I do like to be lean and nimble.
Many of the things I felt like I already knew, or already had opinions on. Some of his suggestions (PPP in particular) just seemed weird, and not generally (or even specifically) applicable to how I work. Maybe I was just looking to be shocked and awed by the experience and wasn't. I think had I approached some of the book with a more open mindset I might've been able to glean more out of those 'basics'.
The book is filled with nuggets of wisdom. Some of my favorite quotes, some from McConnell, some from other writers that he includes in the book:
Managing complexity is the most important technical topic in software development. In my view, it's so important that Software's Primary Technical Imperative has to be managing complexity.
A "wicked" problem is one that can be clearly defined only by solving it.
It's OK to figure out murder mysteries, but you shouldn't need to figure out code. You should be able to read it.
Eighty percent of the errors are found in 20 percent of a project's classes or routines.
Don't document bad code - rewrite it.
Jackson's Rules of Optimization: Rule 1. Don't do it. Rule 2 (for experts only). Don't do it yet - that is, not until you have a perfectly clear and unoptimized solution.
No programmer has ever been able to predict or analyze where performance bottlenecks are without data. No matter where you think it's going, you will be surprised to discover that it is going somewhere else.
The Fundamental Theorem of Formatting says that good visual layout shows the logical structure of a program. Making the code look pretty is worth something, but it's worth less than showing the code's structure.
Build one to throw away; you will, anyhow.
One downside to the book is that it seems to largely focus on OO languages (C++, Java) and even older imperative ones (C, Ada, etc). There is virtually no consideration given to functional programming. This is a shame, as immutable variables, pure functions, and lack of side effects inherently solve or mitigate MANY of the code complexity and readability problems he discusses in the book. I chuckled at a line in the book where he says "Recursion isn't useful often...", which is true in the languages he used, which don't support tail call optimization.
The book also pre-dates the open source explosion, github, cheap/free access to amazing tools and cloud services, and the growth of continuous integration/deployment. These have had some pretty profound impact on software development that are not taken into account in the book.
После повторного прочтения данной книги окончательно убеждаюсь в том, что данная книга должны быть прочитана, как минимум раз, каждым развивающимся разработчиком, менеджером связанным с разработкой, и тем, кто так или иначе связан с областью разработки.
Мое мнение может быть предвзятым, так как на момент написания - это единственная книга по разработке, которую я прочитал, но могу сказать, что автору удалось пролить свет на разработку как таковую, и мне, как начинающему разработчику она очень помогла, не смотря на то, что некоторые главы пока просто не понятны. Также, хотелось бы отметить, что в книге есть главы, которые традиционно являются поводом для многих "религиозных" войн, но автору удалось преподнести предмет спора, в нейтральной манере, благодаря чему, например, выработать хороший стиль кодирования и форматирования, после прочтения книги, не такая уж и большая проблема.
В заключении хочу сказать, что эту книгу я буду стараться перечиывать как можно чаще, по мере получения опыта, так как с каждым чтением, эффект после прочтения все больше и больше.
I mean, it doesn’t even feel like a proper programming book - it’s written in some “Easy way to quit smoking for dummies” style. Every idea is explained verbosely, then illustrated with some numeric stats, then with a 3D chart, then with some real-life anecdote, then with a reference to a 1973 paper, and finally reiterated in a checklist in the end of the chapter.
But still, this book makes a lot of good points, it’s easy to read, there’s not much prerequisites (“If you’re rusty on polynomials, they’re the the things that look like Ax² + Bx + C”) – maybe it makes sense to read it in high school.
Я бы выделил эту книгу в TOP10 книг которые стоит прочитать чтобы вырасти до Lead Developer.
If there's one problem with this book, it's that it is too long, at 900+ pages. But, I wouldn't hold that against it. This book has tons of good information. I should have read this years ago. A lot of what I learnt over the years could have been done so earlier. Also, there were a few points that I didn't completely agree with, but I guess that's ok. Like it is said here, creating software is a craft.
The top 3 biggest takeaways from this book are
* Design is a heuristic process
* Software's primary technical imperative is managing complexity
* Code for humans first, and computers second
This is a fantastic book!
I read the first edition. If you can get your hands on the second edition, I'd recommend that but even the first edition is worth reading.
Even though it's 30 years old, I'd say about 80% of the first edition is still relevant. Stuff like the nitty gritty of naming routines, how to comment, etc are evergreen topics. And McConnell did an extensive survey of all the literature, so a lot of the content is backed up with hard data about what works and what doesn't.
Highly recommended.
Don't get me wrong, it's hard to find fault with the content itself - all of it is excellent advice, focusing mostly on the construction phase of software development. Unfortunately, I cannot imagine a software engineering with any amount of real-world experience who'd learn much new information reading it. Most of the chapters feel like common sense mixed with fluff and it's hard to avoid thinking this shouldn't have been a 900+ pages book.
I'd probably recommend the book to university students or to self-taught developers just starting their careers, but others would likely be wasting their time.
So, I finished the other book (Mythical Man Month) and took Code Complete with me on trip. I was enthralled with the style of writing and ideas presented as much as Mythical Man Month. In fact, on the way home from the trip I read almost 400 pages. McConnell really has a knack for presenting his material in a thought provoking and entertaining manner. I kept underlining and scribbling notes. I fully intend re-reading every 18 months or so to check up on myself and evaluate the suggested best-practices.
Further, a lot of people recommend this book to beginners which is not worthless but, I would dare to say, dangerous. Many ideas are very controversial, and to yet unformed minds they may be really wrecking.
The idea which I believe can replace the whole book is here: write the code for people to understand. Nothing is beyond that simple truth.
This idea and many other things were an eyeopener to me. For example, I was not able to explain why I don't feel good about "hacky code" in feature implementation or why sometimes I read code and struggle to understand what is going on. Now I see that both of those things add a lot of complexity.
I think every developer with 3-5 years of experience should read this book. Sure it can be dated at some places and not always applicable to small companies development capabilities but it definitely conveys ideas that all developers should understand and embrace.
Random examples of ideas that resonated strongly with me:
* Ch5: Design is "wicked", and you're best off iterating with both top-down and bottom-up approaches and using loose heuristics rather than rigid algorithms.
* Ch9: "One of the biggest differences between hobbyists and professional programmers is the difference that grows out of moving from superstition into understanding." Amen. Similarly, in the debugging chapter, I strongly agreed with the emphasis on truly understanding the root cause of an issue and all of the observed behavior before fixing the problem.
* Ch10: All of the comments on scoping and the reasons to minimize scopes.
* Ch23: Totally agree with the general attitude towards debugging, and the notion that programmers learn a lot from debugging. I've probably learned 2/3 of what I know from my experiences debugging, and those experiences inform every line of code that I write. That said, the notion that one should seriously consider giving up on a bug after less than an hour of debugging is ridiculous.
* Ch24: Parts of this chapter on refactoring and "design-ahead code" are remarkably concise and forceful. I can't tell you how many times I've written code for a feature I was sure we were going to want, but didn't quite flesh out because we weren't sure exactly how it should work, and that code turned out to be poorly exercised, buggy, and ultimately ripped out -- not because of the poor quality, but because we simply didn't need the feature, or the feature needed to work very differently than we initially anticipated. Of course, like all things, there's a balance, and one has to consider each case individually.
* Ch32: Bad idea to refer to bugids and other historical artifacts in code. The source should represent the current state of the system, and the bug tracker and source control document what changes were made when, by whom, and why.
By contrast, there's lots I took issue with:
* Ch5: Many of the examples, particularly in the design section and with respect to OOP, are highly simplistic.
* Ch6: Encapsulation as typically lauded in OOP texts is often a sham, in my experience. Realistic problems rarely lend themselves to simple, clean abstractions. Even traditionally successful abstractions, like Unix file descriptors and POSIX open/read/write/close, reveal a lot about the underlying implementation, and one cannot use them successfully in complex programs without understanding details about the implementation.
* Ch7: The Bjarne Stroustrup quote about macros (that each macro demonstrate a flaw in the programming language) is exactly the kind of dogmatic lunacy that gave us C++[11].
* Ch8: The discussion of error handling misses what I consider the most important question in determining what a component should do in handling an error, which is: can the current layer be *sure* it knows what the caller wants to do? If not, it should either propagate the error to the caller so that it can decide what to do with it, or else provide options so that the caller can indicate ahead of time what it wants to do (e.g., ignore, retry N times, or fail). If there's more than one consumer, err on the side of providing rich information to the caller so it can decide what it wants to do. There's nothing more infuriating than library functions that retry operatons after errors that you know aren't transient or which try to deal with a fatal error by papering over it. It's much worse than having to retry something the library should have known should be retried.
* Ch9: The pseudocode-writing technique suggests thinking both implementation-agnostic and low-level enough that code generation is automatic. These two are always competing, and (in my experience) usually exclusive.
* Ch13: There's an over-emphasis on a superficial notion of "safety", and the suggestion that things like void pointers in C lead to serious runtime bugs. Despite the arguments for "proper" generics in higher-level languages, in my experience, improper use of void*'s in data structures is not even remotely an important category of runtime bugs. One of the "C" examples in this chapter isn't even valid C.
* Ch16 is full of small errors and some braindead code (e.g., "if (...) { assert(false); }").
The book is divided into several sections:
* The first few chapters on metaphors and the notion of construction are thoughtful and unusually self-aware for this kind of "advice" book.
* The next several chapters on requirements and design are a microcosm of much of the rest of the book, containing a smattering of useful ideas in a sea of more dubious, often bureaucratic advice.
* The middle chapters on actual writing and structuring of code are probably the best.
* Some of the later chapters on debugging, refactoring, style, and so on, are pretty thoughtful. Some of the others (like collaborative construction and tools) feel like useless catalogs of techniques and software packages: they're not immediately useful to the reader, nor are they a particularly good reference, but they seem to be there to flesh out an exhaustive description of software construction.
One nice thing about the book is that the text itself is organized with useful headings and bolded main ideas, and each major section ends with main idea bullet points and a complete checklist, making it relatively easy to skim. This could help an experienced engineer pick out good parts that are worth reading more about. Unfortunately, the kinds of things that are most useful to learn are typically the things you wouldn't necessarily realize are important from a bullet point.
As I mentioned, the book is very thoughtful, and the issues raised are all important and relevant for practicing engineers. I found about 30% of the advice strongly resonated with me, at least 50% was highly suspect, and the rest was pretty thought-provoking. For less experienced engineers who are good at picking things up from books like this, it might be a useful text. For experienced engineers, or the majority of us who learn subtle lessons much better by making the mistakes (or at least seeing real-life, personally meaningful examples), you may be better off learning by doing.
