Code Complete: A Practical Handbook of Software Construction, Second Edition

Author: Steve McConnell
All Stack Overflow 109
This Year Stack Overflow 6
This Month Hacker News 1


by curiousEagle   2022-02-21
Some examples of "quality engineering practices" would be in my mind:

- Believes most business logic doesn't belong in models/controllers

- Understands the importance of naming things (classes, variables, etc.) well

- Writes tests, even if not fully TDD

- Intelligently applies SOLID engineering principles

^^ this type of thing.

I think the books "Code Complete" [0] and "Practical Object-Oriented Design: An Agile Primer Using Ruby" [1] embody a lot of what I'm looking for, if you're familiar with them.



Edit: Formatting

by maynman   2020-09-30
Code Complete
by pjmlp   2020-07-28
Here are some pointers:

"Large-Scale C++ Software Design"

Although oriented towards C++, many architecture tips apply to other languages as well.

John Lakos is in the process of writing updated versions of the book.

"Large-Scale C++ Volume I: Process and Architecture"

"Large-Scale C++ Volume II: Design and Implementation"

Then going back into the old days, you have

"Software Engineering in Modula 2: An Object Oriented Approach"

"Data Structures and Program Design in Modula-2"

"Code Complete: A Practical Handbook of Software Construction"

"AntiPatterns: Refactoring Software, Architectures, and Projects in Crisis"

"Component Software: Beyond Object-Oriented Programming"

"Use Cases Combined With Booch/Omt/Uml: Process and Products"

Just some pointers to get you started.

by au750   2019-09-12

If you want to be a generalist, you may want to learn things which are useful independently of the programming language.

Some books that would qualify in my opinion (as examples):

- Code Complete: A Practical Handbook of Software Construction, Second Edition by Steve McConnell

- Facts and Fallacies of Software Engineering by Robert L. Glass

Learning the different approches taken by multiple programming languages is certainly useful. It may not be that much relevant which language it is unless you want a job specifically in that language.

I can't speak for Google but I guess it is more relevant how familiar you are with software development practices and general knowledge about architecture, design, testing, algorithms to name a few than a specific language.

by shaggorama   2019-08-24

Probably the best book on "thinking like a software engineer" is Code Complete.

For now, here are a few general tips off the top of my head:

  • Build your toolkit as you go. Don't just be trying to construct abstractions. You should be hunting for useful abstractions. Write code that is reusable. Whatever code you wrote is solving some sub-problem of your larger solution: are there related sub-problems you could attack with a similar strategy? Can you tweak some piece of code such that it can be reused to make your life easier elesewhere?

  • Code as documentation. You are not just writing code for the purpose of accomplishing some end-goal, you are writing code so that if something breaks down the line, you will be able to figure out what needs to be fixed. This means your code should explain itself. Even if you don't anticipate anyone other than yourself will ever see your code, it might be months or years before you revisit some old code and you should anticipate that you will have forgotten basically everything about how it works. How can you make "future me"'s life easier in the process of building some solution? You should always be trying to name and structure variables/functions/objects such that it's clear what your code is doing and how information and decisions flow through your program.

  • Solve for scalability early. Premature optimization should generally be avoided, but it's often the case that there are small changes you can make very early on to make your code orders-of-magnitude more performant. Are you choosing appropriate data structures for the problem? Are you factorizing your code and dependencies in a reasonable way? Are you excising unused dependencies from your code? Are you limiting i/o? Are you moving large chunks of data around? Can your code be containerized into microservices?

by cruachanmor   2019-08-24

You've probably come across it already, but if not Code Complete is similarly old, but still well worth your time (I actually found it better than Pragmatic... first time I came across it)

by kurple   2019-08-24

Not all programming books are tutorials. There are definitely books that exist which talk about concepts and ideas, ways of thinking.

A udemy course, YouTube video medium article, or any other typical internet resource are great sets of instructions but there's more to it than that.

I was recently recommended Code Complete which i think will have a great impact for myself, despite the fact that it's moreso a textbook.

I also believe books like Eloquent JS are amazing. I would consider it the lighter side of books but is amazing for new devs.

I get where you're coming from but don't discount books entirely! There's a lot of great knowledge out there.

by TheEverHumbled   2019-07-21

If you are starting from an amateur background as a developer on C# centric stack, I'd argue that Code Complete has strong to potential to protect against your weaknesses/blind spots than just about any book about software development. It doesn't go deep, but it will introduce you to breadth of fundamentals. Feel free to skim past the ones you are already familiar with.


The particular techniques described evolve over time, but the principles endure. e.g. One poster commented of making backups. Tools in the C#/MS ecosystem have evolved considerably, but git based source control is pretty popular tech at moment. Github is popular for hosting source of open source projects - Azure Repos and Azure Devops suite has more depth for larger teams and complex processess.

by ssentrep   2019-07-21

Read "Code Complete, 2nd version". Its everything you'd learn in 10 years of experience, summarized into 1 book.

by anonymous   2019-07-21

Code complete can explain the solution to your problem better than I ever could.

by anonymous   2019-07-21

I found the following book greatly useful when I started programming:

Code Complete: A Practical Handbook of Software Construction (Steve McConnell - Microsoft Press)

by DustinBrett   2019-07-21

Another book I really liked was Code Complete.

by unpopularOpinions776   2019-07-21

Nice article. I'd like to see you do one about a topic other than Moya, though. I feel like the Moya fans already know Moya, and are specifically using a framework like Moya in order to not fully learn how something like URLSesssion works, let alone FRP.

I recently did a PR on someone's project that had implemented Moya and it was my first introduction to it. TBH I was not impressed and would never recommend someone using it again.

I couldn't believe that such a popular (from my view) networking framework has Alamofire as a dependency. The creators of Moya really need to read this book

by anonymous   2019-07-21

Null is not a 'problem'. It is an integral part of a complete modeling tool set. Software aims to model the complexity of the world and null bears its burden. Null indicates 'No data' or 'Unknown' in Java and the like. So it is appropriate to use nulls for these purposes. I don't prefer the 'Null object' pattern; I think it rise the 'who will guard the guardians' problem.
If you ask me what is the name of my girlfriend I'll tell you that I have no girlfriend. In the Java language I'll return null. An alternative would be to throw meaningful exception to indicate some problem that can't be (or don't want to be) solved right there and delegate it somewhere higher in the stack to retry or report data access error to the user.

  1. For an 'unknown question' give 'unknown answer'. (Be null-safe where this is correct from business point of view) Checking arguments for null once inside a method before usage relieves multiple callers from checking them before a call.

    public Photo getPhotoOfThePerson(Person person) {
        if (person == null)
            return null;
        // Grabbing some resources or intensive calculation
        // using person object anyhow.

    Previous leads to normal logic flow to get no photo of a non-existent girlfriend from my photo library.


    And it fits with new coming Java API (looking forward)


    While it is rather 'normal business flow' not to find photo stored into the DB for some person, I used to use pairs like below for some other cases

    public static MyEnum parseMyEnum(String value); // throws IllegalArgumentException
    public static MyEnum parseMyEnumOrNull(String value);

    And don't loathe to type <alt> + <shift> + <j> (generate javadoc in Eclipse) and write three additional words for you public API. This will be more than enough for all but those who don't read documentation.

     * @return photo or null


     * @return photo, never null
  2. This is rather theoretical case and in most cases you should prefer java null safe API (in case it will be released in another 10 years), but NullPointerException is subclass of an Exception. Thus it is a form of Throwable that indicates conditions that a reasonable application might want to catch (javadoc)! To use the first most advantage of exceptions and separate error-handling code from 'regular' code (according to creators of Java) it is appropriate, as for me, to catch NullPointerException.

    public Photo getGirlfriendPhoto() {
        try {
            return appContext.getPhotoDataSource().getPhotoByName(me.getGirlfriend().getName());
        } catch (NullPointerException e) {
            return null;

    Questions could arise:

    Q. What if getPhotoDataSource() returns null?
    A. It is up to business logic. If I fail to find a photo album I'll show you no photos. What if appContext is not initialized? This method's business logic puts up with this. If the same logic should be more strict then throwing an exception it is part of the business logic and explicit check for null should be used (case 3). The new Java Null-safe API fits better here to specify selectively what implies and what does not imply to be initialized to be fail-fast in case of programmer errors.

    Q. Redundant code could be executed and unnecessary resources could be grabbed.
    A. It could take place if getPhotoByName() would try to open a database connection, create PreparedStatement and use the person name as an SQL parameter at last. The approach for an unknown question gives an unknown answer (case 1) works here. Before grabbing resources the method should check parameters and return 'unknown' result if needed.

    Q. This approach has a performance penalty due to the try closure opening.
    A. Software should be easy to understand and modify firstly. Only after this, one could think about performance, and only if needed! and where needed! (source), and many others).

    PS. This approach will be as reasonable to use as the separate error-handling code from "regular" code principle is reasonable to use in some place. Consider the next example:

    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {
        try {
            Result1 result1 = performSomeCalculation(predicate);
            Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
            Result3 result3 = performThirdCalculation(result2.getSomeProperty());
            Result4 result4 = performLastCalculation(result3.getSomeProperty());
            return result4.getSomeProperty();
        } catch (NullPointerException e) {
            return null;
    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {
        SomeValue result = null;
        if (predicate != null) {
            Result1 result1 = performSomeCalculation(predicate);
            if (result1 != null && result1.getSomeProperty() != null) {
                Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
                if (result2 != null && result2.getSomeProperty() != null) {
                    Result3 result3 = performThirdCalculation(result2.getSomeProperty());
                    if (result3 != null && result3.getSomeProperty() != null) {
                        Result4 result4 = performLastCalculation(result3.getSomeProperty());
                        if (result4 != null) {
                            result = result4.getSomeProperty();
        return result;

    PPS. For those fast to downvote (and not so fast to read documentation) I would like to say that I've never caught a null-pointer exception (NPE) in my life. But this possibility was intentionally designed by the Java creators because NPE is a subclass of Exception. We have a precedent in Java history when ThreadDeath is an Error not because it is actually an application error, but solely because it was not intended to be caught! How much NPE fits to be an Error than ThreadDeath! But it is not.

  3. Check for 'No data' only if business logic implies it.

    public void updatePersonPhoneNumber(Long personId, String phoneNumber) {
        if (personId == null)
        DataSource dataSource = appContext.getStuffDataSource();
        Person person = dataSource.getPersonById(personId);
        if (person != null) {
        } else {
            Person = new Person(personId);


    public void updatePersonPhoneNumber(Long personId, String phoneNumber) {
        if (personId == null)
        DataSource dataSource = appContext.getStuffDataSource();
        Person person = dataSource.getPersonById(personId);
        if (person == null)
            throw new SomeReasonableUserException("What are you thinking about ???");

    If appContext or dataSource is not initialized unhandled runtime NullPointerException will kill current thread and will be processed by Thread.defaultUncaughtExceptionHandler (for you to define and use your favorite logger or other notification mechanizm). If not set, ThreadGroup#uncaughtException will print stacktrace to system err. One should monitor application error log and open Jira issue for each unhandled exception which in fact is application error. Programmer should fix bug somewhere in initialization stuff.

by anonymous   2019-07-21

I'd like to briefly address three of your points.

1. "A result of insufficient up-front design"

Common sense (and several books and bloggers) tell us we should strive for the simplest, cleanest design possible to address a given problem. While it's quite possible that some code is written without sufficient work on developing an understanding of the requirements and the problem domain, it's probably more common that "poor code" wasn't "poor" when it was written; rather, it is no longer sufficient.

Requirements change, and designs have to support additional features and capabilities. It's not unreasonable to anticipate some future changes up-front, but McConnell et al. rightly caution against high-level, overly-flexible designs when there's no clear and present need for such an approach.

3. "A dangerous activity that needlessly risks destabilising working code"

Well, yes, if done improperly. Before you seek to make any significant modification to a working system, you should put in place proper measures to ensure that you're not causing any harm - a sort of "developmental Hippocratic oath", almost.

Typically, this will be done by a mixture of documentation and testing, and more often than not, the code wins out, because it's the most up-to-date description of the actual behaviour. In practical terms, this translates into having decent coverage with a unit test suite, so that if refactoring does introduce unexpected problems, these are identified and resolved.

Obviously, when you seek to refactor, you're going to break a certain number of tests, not least because you're trying to fix some broken code contracts. It is, however, perfectly possible to refactor with impunity, provided you have that mechanism in place to spot the accidental mistakes.

4. "A waste of resources"

Others have mentioned the concept of technical debt, which is, briefly, the idea that over time, the complexity of such systems builds up, and that some of that build-up has to be reduced, by refactoring and other techniques, in order to reasonably facilitate future development. In other words, sometimes you have to bite the bullet and go ahead with that change you've been putting off, because otherwise you'll be making a bad situation appallingly worse when you come to add something new in that area.

Obviously, there's a time and a place to pay off such things; you wouldn't try and repay a loan until you had the cash to do it, and you can't afford to go around refactoring willy nilly during a critical stage in development. Nevertheless, by making the decision to address some of the problems in your code base, you save future development time, and thus money, and maybe even further into the future, avoid the cost of having to abandon or completely rewrite some component that is beyond your understanding.

by anonymous   2019-07-21

I'd HIGHLY recommend reading Code Complete 2 for some excellent insight into these kinds of questions.

by maynman   2019-01-15
Speaking from a software engineering perspective, here are a few that I found really valuable. I've seen them recommended by people I respect on many occasions.

Code Complete by Steve McConnell

The Mythical Man Month by Frederick Brooks

by anonymous   2019-01-13

From the looks of it there might be multiple things you can do to make things better, depending on your context.

But first, you need to know that management is a process, not a task, and it cannot be done only on "projects in progress". It starts way prior to that, with how you initiate a project, how you plan it, then you execute it (including analysis, design, programming, testing) and close it properly. Also, you need to monitor and control the project throughout. Missing the planning step and just focusing on already started projects will not yield positive results as you're already missing some vital steps. On the other hand, it seems to me that you might not have the proper development process, and tools/infrastructure and the programming culture within the team might be lacking.

Keep in mind there are some assumptions in my writing, so you decide what's useful, as you surely know your context better. Here are my 2 cents:

Hope that helps!

I know you probably expected a simpler answer, but the hard truth is that it would probably prove to be a long and tough journey. Good results always require persistence and hard work and there are rarely shortcuts.

by anonymous   2019-01-13

As an alternative to @Sweeper's answer, consider using the Arrays.copyOf() method, as suggested in this answer. Note that the copyOf() method is type-safe whereas the clone() method isn't.

double[] a, b, c;
c = Arrays.copyOf((b = Arrays.copyOf((a = new double[10]), a.length)), b.length);

But again, I'll reiterate as @Sweeper does, that this code really smells and you should consider doing it in multiple lines. As Steve McConnell says in Code Complete 2nd Ed., the Primary Technical Imperative of software is to manage complexity (i.e. make your code simple). This doesn't necessarily mean reducing the lines of code, but more to do with enabling people who read your code to understand what it does at a glance.

by anonymous   2019-01-13


1. GIT and other version controls systems treat white-space differently

Based on my experience, we faced on our projects: GIT and other version controls systems treat invisible spaces + TABS differently, and it leads to changes in lines, which actually haven't been affected. It's easy not to notice, when there will accidentally added one space + TAB = indent looks the same in IDE, but GIT will make the difference when merging. It damages your ability to effectively compare revisions in source control, which is really scary. It never going to happen when you are having spaces only.

2. Neutralize difference in collaborator's environment (Editor, OS, preferences, etc.)

The tab width (in spaces) depends on your environment (text editor, OS, preferences, etc.), but the space width is the same everywhere. IDEs are smart enough to treat white spaces up to you personal taste, but the output generated for collaboration should be up to standards.

3. Developers who use spaces make more money than those who use tabs

Using spaces instead of tabs is associated with an 8.6% higher salary. Using spaces instead of tabs is associated with as high a salary difference as an extra 2.4 years of experience. (source: Stack Overflow 2017 Developer Survey).

4. Numerous studies on coding style importance

If every collaborator on your project would keep the same standards on coding - it will be good in long run, collaboration is more efficient and professional, the same indent when you refactor or develop. Studies regarding that:

  1. For example, Ben Shneiderman confirmed this in Exploratory experiments in programmer behavior:

    when program statements were arranged in a sensible order, experts were able to remember them better than novices. When statements were shuffled, the experts' superiority was reduced.

  2. An old 1984 study by Soloway and Ehrlich cited in Code Complete, and supported studies from The Elements of Programming Style:

    Our empirical results put teeth into these rules: It is not merely a matter of aesthetics that programs should be written in a particular style. Rather there is a psychological basis for writing programs in a conventional manner: programmers have strong expectations that other programmers will follow these discourse rules. If the rules are violated, then the utility afforded by the expectations that programmers have built up over time is effectively nullified.