The Enthusiastic Programmer

This book is about how to create software that works well for its intended users and is easy to keep developing and improving over time. It assumes you have basic programming knowledge (functions, variables, loops, if statements) but nothing more than that.

It also assumes that you are working on a team, and that you are empowered to change how your team works (or someone who trusts you is). This is a big assumption, but I think it is necessary. Melvin Conway observed that computer systems reflect the organizational structure of their creators. It follows that people working in a dysfunctional human system are likely to create equally dysfunctional computer systems. If you find yourself working in such a dysfunctional system, you will likely have to fix it before you can apply many of the techniques in this book.

Why I'm Writing This Book

I want to live in a world where software works. And by works, I mean works for the people most directly concerned—its end-users and the engineers who build it.

You might wonder how a book that is mainly about the technical side of software production could hope to achieve such lofty goals. Wouldn't more effective software simply deliver more power into the hands of megacorporations that are already using software to manipulate their users' behavior, mine attention for profit, destabilize democratic institutions, and abuse low-paid or part-time workers while disrupting the jobs of the middle class?

But this book is not merely about making software better, in some abstract, technical sense. It's about making the whole system better—the whole, human system of software production and use. It is, largely, about increasing the power of individual engineers within the economic system of software production. With increased power comes the freedom to take jobs you believe in, and to refuse to comply with unethical orders.

An at-will employee with no expertise has no leverage. They are a replaceable cog in a vast apparatus, so any perceived insubordination or attempt to wrest control away from those higher up in the organization will likely be met with dismissal. However, with expertise, the same employee has a great deal of leverage. Managers are loath to fire people who are producing results for them, and who can act as a force multiplier to the efforts of others.

This book grants power in another way, too: it will teach you how to learn about the systems, both human and mechanical, with which your work interacts. It will give you a framework for understanding the far-reaching effects of the programs you write. It will teach you to face any engineering challenge without fear—even if the problem is too big for you to solve alone.

This understanding, and this immunity to fear, are important. Evil thrives when people are ignorant and confused about the effects of their actions. Evil feeds off of fear, because people who are afraid are easy to manipulate.

By giving you expertise, this book thus grants you power. Use it for good.

About the Author

Why should you trust me?

  • I've been programming for 22 years; programming professionally for 8.
  • experience on a dozen software projects in industry
  • scores of personal projects
  • teams of 1-12 people, some isolated, some within orgs with hundreds of programmers
  • brand new codebases vs 20-year-old ones
  • databases, Arduinos, web apps, cloud platforms, programming languages, operating systems
  • scrum, kanban, extreme programming
  • functional, procedural, object oriented: C, C++, Scala, Ruby, JavaScript, Visual Basic
  • design up front, TDD, test-last, no tests

And yet, these are essentially just buzzwords. They are flimsy and vacuous arguments for my expertise. They should not be the reason you trust me.

The whole of my experience is more than the sum of these parts. I perceive these apparently different experiences not as disjoint, but as aspects of a single thing—a timeless way of programming. It is in this timeless way that the real value of this book lies.

My goal is not to present merely a collection of methods and techniques that I have learned, but to generalize from those specific techniques to find the abstractions that underlie them. Once you understand the abstractions, you can fearlessly apply specific techniques in their appropriate context.

Glossary

I use certain terms in a very precise way. Precise terms will allow me to communicate the content of this book in a reasonable amount of time and space. The terms I use are defined in this section. The first use of a term in the text will be in boldface type.

Certain terms may seem archaic or otherwise distasteful. For example, no one seriously talks about routines anymore! Now we use functions! The problem is, these words are actually used non-interchangeably in certain contexts, and to avoid confusion it is easiest to use them non-interchangeably everywhere.

  • test (v), testing (n)
  • test case
  • test double
  • mock
  • routine
  • function
  • procedure

Change Starts at Home

In order to get anything of value from this book, you're going to have to change. This is not meant as a criticism; it's simply a fact about learning. To learn is to change how you think.

  • How to learn - Kathy Sierra
  • recognize your biases

Hiring Programmers

Most software of any significant complexity is written by teams. In order to build a team, you have to find good programmers.

Unfortunately, the way most companies hire programmers these days is completely insane. Often, the candidates do not do any real programming during the interview process. Instead, they are asked to solve puzzles and write small programs on a whiteboard, without the benefit of a computer. Interview questions often focus on efficient use of computing resources like time and memory.

The problem with this hiring process is that once a new hire starts the job, they will be asked to solve much more complicated problems. They will have to improve software that other people have written. They will have to collaborate and communicate with those other people. And, as any programmer can tell you, their day-to-day efforts rarely involve detailed analysis of resource usage. Most programs these days are simply not that resource-constrained.

Interviewing is perhaps the most impactful thing you will do as a programmer. Hiring decisions can make or break a software team. The people you hire will determine the team's culture and expertise for a very long time, especially because the people you hire today will be evaluating candidates tomorrow.

Criteria for an Interview Process

What skills are important? What can be learned on the job?

important: - program design - testing - understanding existing programs - speed of learning - written communication - spoken communication - familiarity with different paradigms (to be able to learn new languages and understand others' code) - empathy - comfort setting up a project in at least one programming language - do you want to work with them? - do you trust them to interview future candidates?

Adjust expectations based on how much past experience the candidate has. A desire to learn is especially important for junior engineers, while senior engineers should demonstrate more technical expertise.

nice to have, not essential: - data structures - algorithms - computer science

learnable on the job: - specific programming languages or tools

  • Create two programming exercises that have some complexity
  • Do one halfway, in several popular programming languages
  • Give candidates a choice of which language they interview in
    • lvl language knowledge is noise--not important signal
  • Let candidates start the other project from scratch. How do they deal with a totally blank slate? Can they understand someone else's code and build on it?
  • if they don't know the language that they chose to do the interview in, that's a red flag

to avoid bias, have someone blank out names and other personal information before screening resumes. Give the candidate a short homework assignment; something that can be done in an hour or so, leaving the requirements vague enough that they can do it using any technology they choose. This gives candidates the opportunity to showcase their best work without the pressure of a face-to-face interview.

pair-program with the candidate. Let them type and ask for help when they get stuck. Ask them to verbalize what they're thinking about. Reassure them that they won't be penalized for hesitation, uncertainty, or getting stuck—these are normal parts of the problem-solving process, and, if they talk through what they're thinking, actually demonstrate that they are engaging with the problem.

Tell the candidate what you're looking for. Don't make them guess. An interview is not about how well the candidate can read your mind. Don't worry; they're not going to be able to fake it just because you told them how they're being graded. They're writing a real program; they can't fake it.

I've almost turned down several candidates who turned out to be very strong programmers because they weren't sure the interview was a good place to show off their design and testing skills—the problem seemed too simple, and they were worried they'd be penalized for over-engineering. But those design skills were exactly what I wanted to see.

See the section on pair programming for more.

The Metaprocess: Weekly Retrospectives

Retro Antipatterns

  • each person monologues about a grievance of their own, eliciting little or no response from other members of the group. When they're done speaking, everyone nods in agreement but there is no commitment to fix the problem.
  • action items are written down and assigned but no one does them.

  • team has too many problems—can't focus on just one, so nothing ever gets fixed

  • team members do not feel comfortable voicing their true feelings

Meetings

  • don't have 'em
  • especially when the people in the meeting are asked to commit to a decision, but don't have the information available in the meeting room
  • consensus is not a substitute for knowledge.

Good reasons to have meetings - broadcast communication - give a group a chance to ask an expert questions

Organizing Principles

  • move authority to information
    • requires mutual trust, which must be earned.
  • what changes together stays together
  • mutual benefit
    • programmers
    • users
    • business
  • pace
    • if you rush toward a distant goal, you'll start creating code you won't take care of later. Goals are often farther off than they appear
    • if you slacken your pace and try to make decisions very deliberately, you'll soon be lost in a maze of potential alternatives. You'll spend hours weighing tradeoffs and never get anywhere.
    • the key is to be methodical, yet aware and open-minded. Simply do one thing after the other. Follow the guidelines laid out in this book, but when different rules conflict, use your intuition. Over time, your intuition will get better.

What to do with deadlines

-

Keeping track of your work

  • the mental stack problem

Planning and Estimation

  • the purpose of estimation is to give the PM feedback on whether something is worth doing at all.
  • frequent releases are how you reduce risk—software can be sold or features used as soon as they're ready, not 3 months later.
  • PMs will often say something like well, why don't we do some work to investigate how much time this will take?
  • the cheapest way to know how long something will take is to just try to do it. Upfront analysis of possible solutions is likely to miss hidden costs. Sometimes, you'll also miss an inexpensive solution because you simply didn't see it during analysis.

A backlog is a prioritized list of work items.

Bugs, chores, features--what's the difference?

  • not much that's of any use. Scrap the distinction, or at least, don't think too hard about it.

Developers may want to put tasks like refactoring or testing in the backlog. Don't do this. Refactoring and testing are part of building a feature.

If code is already working, and you have no immediate need to change it, and the difficulty of change will not increase substantially in the future (e.g. because you forgot how it works) you should not refactor it. Same goes for tests. If it's already working, why write tests for it?

The time you should refactor or test, if you didn't do it as part of the feature, is the next time you have to change the code.

Developers often inherit a legacy codebase and want to do cleanup or testing tasks to make it nice. Don't do this. Invest in refactoring and tests only for the parts of the code you have to change.

Design: It's All About Side Effects

What is a Side Effect?

Visualizing Dependencies

Testing: Philosophy and Practice

Driving Design With Test Doubles

Code Smells and Refactoring

Commit Messages

Pair Programming

  • continuous peer review, not just of code, but of process
  • fast onboarding
  • even if you don't pair all the time, you want to work with people who will be willing to pair when the situation calls for it. That's why pair programming should be part of your interview process.

Back to the Homepage