The Best Programmer I Know • Daniel Terhorst-North • GOTO 2024
Daniel Terhorst-North explores the learnable traits of great programmers, challenging the notion of innate talent. He outlines a three-part framework focusing on effective execution, selecting appropriate tools, and fostering a psychologically safe team environment, emphasizing that greatness is achieved through deliberate practice and a focus on product and people over code.
Daniel Terhorst-North challenges the gatekeeping notion that a formal computer science degree is a prerequisite for being a great programmer. He argues that the best programmers are not born but are made through deliberate, learnable behaviors. The talk is structured around the characteristics of “the best programmer I know,” a real person who exemplifies these learnable traits.
Part 1: Getting the Job Done
The first part focuses on the mindset and practices required to deliver value effectively.
- Just Start: Overcome the procrastination that stems from ego and the fear of writing imperfect code. The key is to start anywhere, accepting that the initial code is a learning step and will likely be replaced. This requires humility and giving yourself “license to be rubbish” while you learn and iterate. The first draft of code is temporary; its purpose is to teach you what the next version should be.
- Build a Product, Not Just Software: The focus should be on building a product that solves a real-world problem, not just on writing software. The code is merely a means to an end. The most effective solution might even involve writing no code at all by making the problem disappear.
- Study the Domain: A great programmer deeply understands the business domain they are working in—not just the technical terms, but the people, their goals, and their frustrations.
- Code in the Language of the Domain: The code should be readable as a solution in the domain’s language, not just as abstract programming constructs.
- Observe Users: Watch people use your software and listen for the “sigh”—the unconscious sound of frustration. These moments reveal pain points that users won’t report because they don’t know a better way is possible.
- Solve for Now, Not for Next Time: Resist the urge to over-generalize and build complex frameworks. Solve the immediate, real problem with the simplest possible solution. Terhorst-North gives an example of a team that needed to serialize seven data structures to JSON. Instead of importing a large, generic JSON library, they wrote a simple
toJsonmethod for each of the seven types, creating a zero-dependency, highly testable solution that perfectly fit their needs. This approach is encapsulated in the idea of “embarrassment-driven refactoring”: keep simplifying until you are no longer embarrassed to hand the code over.
Part 2: Choosing the Right Tool
This section reframes how teams should think about technology choices.
- Choose the Tool for the Situation, Not the Team: Teams should not define themselves by a single language (e.g., “a Java team”). A team of good programmers can learn new languages. The right tool is the one that is simplest for the problem at hand, not the one the team is most familiar with.
- Distinguish Simple from Easy: Referencing Rich Hickey’s talk “Simple Made Easy,” Terhorst-North explains:
- Easy is about familiarity and what is closest to your current mindset.
- Simple is about having a direct, objective connection between the problem and the solution. The example of Microsoft Office’s “File -> Print” command illustrates this: it’s familiar (“easy”) but not logical (“simple”), as printing is an action on a document, not a file system operation.
- Make Change Easy: Quoting Kent Beck, the mantra is to “make the change easy, then make the easy change.” This involves designing systems with a minimal blast radius. Architectures based on small, replaceable components (like microservices, CSP, or the Actor Model) allow for safe experimentation and evolution because a failure in one component is contained.
- Spike and Stabilize: Treat all new code as a speculative “spike.” Once it proves its value in production, invest the time to “stabilize” it with tests and refactoring. This ensures that engineering effort is only spent on code that delivers real value.
- Be a Polyglot: Explore different languages, tools, and paradigms (functional, Lisp-family, etc.) to expand your thinking, even if you don’t use them in your daily work. This extends to being “full stack” in the broadest sense—from hardware and infrastructure to team processes and organizational structure.
Part 3: Caring About the Team
The final section emphasizes the human element of software development.
- Foster a Supportive Environment: Find joy in helping and teaching others. A rested team is an effective team, so send people home on time. As Terhorst-North notes, “the best debugger is a good night’s sleep.” If a team member is struggling, the problem is likely the environment, not the person.
- Cultivate Psychological Safety: Coined by Amy Edmondson, psychological safety is a team-level phenomenon where members feel safe to take interpersonal risks. A team is psychologically safe if its members’ responses to the following are positive:
- “I don’t know” (vulnerability to learn).
- “I need help” (desire to collaborate).
- “I disagree” (willingness to challenge the status quo for the better). This is about being safe, not necessarily comfortable, and it allows for the healthy, constructive conflict that high-performing teams need. High-performing teams don’t make fewer mistakes; they report more of them, which enables learning and improvement.
- Care for Yourself: The increasing discussion around mental health and burnout in tech is a positive sign that the industry is becoming more open. To be effective, you must care for yourself. Have interests outside of programming, find a sport or hobby, and model healthy behavior by going home on time.