Whiteboards at the "what makes a good programmer?" talk, barcamp philly, 2017
October 15th, 2017

What makes a good programmer?

Yesterday at Barcamp Philly, I led a conversation around a simple question: what makes a good programmer?

I started by rejecting the idea that the technology is the hardest part. Getting good at anything is a thousand-hour problem, if not more. So I dumped the terms "hard skills" and "soft skills" in favor of "computer skills" and "people skills."

But of course lots of things are in the middle. And in the end, they are all in the middle: we use computers for a reason. And that reason is people.

Computer skills, people skills and in between

"What's with the ghost and the clock?" I was listed in the official program as "Thomas Boubell." I explained that Thomas Boubell is a ghost who lives in a clock tower.

Everyone has an opinion about what makes a good programmer. And I made clear that all of those opinions are welcome. Because if you work in design, UX, management or business development you know a lot about the process and what good outcomes look like.

Computer skills

The first hand raised for the "computer skills" program was for SQL. OK, yes, absolutely. It's pretty likely you'll need to talk to a SQL database at some point. But after that, things were much more meta. Much more universal.

Our computer skills list:

  • SQL
  • Version control, and git
  • Frameworks, and the MVC (Model-View-Controller) pattern
  • Testing
  • DevOps: deploying projects to production in a reliable way
  • Googling: making good use of Stack Overflow and other resources
  • Debugging, in general
  • IDE (Integrated Development Environment) mastery: making good use of tools designed to help you
  • Curiosity
  • Focus

I personally added "focus" as a counterpoint to "curiosity." Today there's a new JavaScript framework almost every hour. A curiosity about new tools is valuable; everyone should learn new programming languages and libraries. But focus is also valuable. It can take years to truly master something and bring a major project to fruition.

People skills

Then we dove into people skills. A program that doesn't solve the right problem is the buggiest problem ever. How do we get to the right solution without burning people out? How do we work together in a sustainable way?

Our people skills list quickly grew:

  • Requirements gathering
  • UI (User Interface) design
  • Code and API design
  • Empathy and listening
  • Flexibility
  • Writing well
  • Speaking well
  • Speaking publicly
  • Constructive criticism (giving and receiving)
  • Mentorship of others
  • Approachability
  • Curiosity
  • UX (user experience)

UX is an entire discipline in itself, but the basics are valuable for any developer to know. UX is rooted in humility ("we need to find out what the customer really wants and needs") and empathy ("we need to keep improving based on real-world experience"),  two skills that go a long way for programmers who want to avoid wasted weeks shaving the wrong yak.

In the Middle

The skills in the middle are the most interesting to me:

Writing readable code

Writing documentation

Taking ownership

Naming things


Know what you don't know (humility)

Writing user stories

"Productive laziness"

All of these take time and practice to learn but greatly improve your value to the team, the project and the organization. Even, yes, productive laziness, which I would define as a willingness to see when the fastest code is actually the slowest: choosing the solution you can implement correctly in the time available that will work just fine for the problem at hand.  And also the DRY principle: Don't Repeat Yourself; write reusable code and reuse it.

Co-programming, with Tom Boutell

Co-programming: playing awesomely with others

The emphasis on people skills led us to dive more deeply into what helps developers work effectively with one another and with adjacent professions, like UX:

  • Pair programming; sharing skills and facts
  • Retroactive humility ("remember how little you knew")
  • Present humility ("remember how little you know")
  • Code review: explaining your code to others
  • Diagramming
  • "What have you tried?" Doing your homework before raising your hand for help
  • "Productive struggling:" do raise your hand for help once you've banged your head against the same wall twice
  • One question at a time: focus your questions
  • Building a clear shared model of the problem
  • Self-documenting code
  • Delegation: don't try to do it all, don't hero it out
  • Prototyping

As a senior developer, I'm especially familiar with both the failure to delegate and the desire to "hero it out" and paper over big gaps in time and resources by throwing my own body... and evenings... and weekends... on the tracks. But ultimately this is bad for the company, not just for you. And it's really, really bad for you.

I don't intend this to mean that you should never pursue something you're passionate about in your personal time, just because it's work-related. Different things engage different people. But if you feel like you must work on weekends, that's a bad sign.

Version control and pair programming

Version control: how not to step on my foot

As a dancer, I know my first job is not to step on my partner's feet. Followed by not colliding with everybody else on the dance floor. Followed by making my partner feel great about the dance. And... oh yeah, having fun myself.

The same priorities apply to programming. Version control, the process of managing the contributions of many developers to the same code, is a big part of the answer. And nearly everyone at Barcamp had opinions about it and git, the (almost) universal version control system of modern developers.

Our list included:

  • Clear commit messages ("this is what I did")
  • Frequent commits (don't code a novel at a time)
  • Meaningful chunks of work in commits
  • Branching: do it!

Branching is a huge win of modern version control systems like git: it's very, very easy to say "hey, I'm going to code in my own little space for a while, and merge my changes back to the master branch later." And you should do it. Every time.

But people forget. So git is designed so that you can even branch at the last minute, after you've already made lots of changes but before you've committed them:

git checkout -b cool-new-feature

Easy, no?

Then we got into the specifics of branching. There should be feature branches — a branch for each new feature added! Everything should arrive in master via a git "pull request!" There should be release branches, and nothing should land in master until it's been deployed! There should be "dev," "staging" and "production" branches, to run the code through testing by the developers, then by the users, and finally make it live!

There's more than one answer here. The right path depends on the complexity of the project, the number of stakeholders, and the risks involved in deploying a mistake to production.

But git makes branching so easy that at an absolute bare minimum, you should always create feature branches, and have a second person review them before they are merged to master, even on the smallest team project. We were all firmly in agreement on that.

Folks also suggested specific techniques to make code review nicer:

  • "Squash on merge," and/or git rebasing
  • Merging master back into to your feature branch frequently

The latter is easy and I'd agree you should always do it, especially if your branch is around for a long time. That way you don't face a monumental struggle to merge the code at the end. Squashing and rebasing on the other hand, is a nice touch that makes it easier to review the history of a project later. But since github does such a nice job displaying the total set of changes to your project based on all of the commits in a pull request, squashing and rebasing aren't essential for every project.

Pair programming: getting to know you...

When it comes to coding together, version control isn't the end of the story. There was great enthusiasm for pair programming at Barcamp. Pair programming is a technique in which developers sit together at the same computer... and only one of them has control of the keyboard at any given time.

This might seem like it cuts productivity in half, but it often does the opposite. Programmers, even the most experienced, make mistakes constantly. A second human being, working together in a constructive environment, often spots these right away. And pairing also leads to ideas and approaches that would never have been considered by a single person. Without pair programming, our Apostrophe CMS wouldn't have cursors.

The benefits of pairing as a mentorship technique are also considerable.

More than 50% of the room had tried pair programming at some point. Just a handful worked that way at least 50% of the time. But quite a few more wanted to. Having built most of Apostrophe in pair-programming sprints, I strongly agree.

But what makes a good pair programmer? Here's our list from Barcamp:

  • Let people know you're only human: make mistakes in front of them
  • Try not to "keep score"
  • Be friends — have a beer with your pair programming partner
  • Check your opinions — listen and consider ideas
  • Language matters. If you are the more experienced developer, you should never say "it's easy" or "anybody could do it."
  • The least experienced developer should "drive" (sit at the keyboard) most of the time, which ensures...
  • Patience.
  • Asking lots of questions.
  • Thinking out loud.
  • Continuous improvement. Refactor (restructure and rewrite) code together. "It just barely works now" is not the end of the process.


Testing Code

"Let's talk about testing real quick."

"That's what ALWAYS happens."

We couldn't get to everything in a 45-minute conversation. But with a few minutes left on the clock, I suggested we have a quick conversation about testing.

And the response came back right away: "that's always when testing finally comes up!"

So that's mistake number one: don't put off thinking about testing. Write unit tests (automated tests of each part of your program) early. Ideally, engage in test driven development (TDD): write the unit tests first, and the code second, so that as you write the code your tests begin to pass.

This works better for some types of code than others, but if you're writing back-end code like a REST API it's silly not to use TDD. Plus, it's enormously relaxing to know that your code already passes a complete test suite when you first hand it to another programmer to use.

Of course, automated "unit testing" isn't the only relevant type of testing. "Regression testing" in which a human being steps through the functions of the program before each new deployment to production... even if everybody swears up and down they changed "only one thing..." is enormously helpful in finding unexpected bugs and interactions.

In the middle stands automated interface testing, in which a program clicks simulated buttons to walk through your interface. This is valuable too. But while it catches bugs, it's easier to avoid bugs in the first place with unit testing. And no matter how good your automated testing is, there will always be situations where a button "works..." but the button is hidden under another button. People are a necessary part of the process. People are the point of the process.

Here's our list of thoughts on testing from Barcamp:

  1. Make time! Write tests!
  2. Keep tests simple, each with a clearly understandable purpose.
  3. Don't make them too simple. Don't test trivialities.
  4. Don't just test the "happy path" in which users do the right thing.
  5. Respect the QA (Quality Assurance) team. Be glad they found the bug, rather than the customer
  6. Have a QA team. For some projects, it isn't easy to afford a QA team. Try to find a way
  7. Create automated interface tests.
  8. Repeat yourself! When it comes to testing, "Don't Repeat Yourself" doesn't apply. Try  the same operations from a slightly different angle, in a slightly different order.
  9. "Mock" less stuff. "Mocks" are greatlysimplified simulations of other components of the program, so you can test just one. In practice, you'll often find bugs sooner if you go ahead and use the real components. Of course, those components might not exist yet. Or they might spend real dollars. Mocks have their place.
  10. Err on the side of performing more tests. There's good advice here about what tests to emphasize, but tests are almost always good.
  11. Test user stories. A great way to relate your tests to the requirements you gathered at the start of the project.
  12. Help the UX team make sure error states are well-designed. Sometimes developers are aware of possible user errors that don't come up in the design process. We tend to wing it. Work with your UX team to do a better job.
  13. Factor your tests so they are not broken trivially by CSS changes and other aesthetics that don't matter to the functionality of the program. But...
  14. Don't forget the human regression testing. Busted styles can make a "working" interface unusable. White on white text, anyone?

Spoiler alert: they are all people skills

In the end, it all comes down to people. Get to know your team, get to know your customer, get to know the human purpose of your project. Those things lead directly to a sense of ownership, an emphasis on quality and a better result for everyone. Which leads to healthier, happier programmers with the skills to take on new challenges.

Opportunities to do that are easy to find. Make time to participate in the early UX conversations with the client, so that you grasp what is important to them and why it should matter to you too.

Institute regular "best practices" conversations with other developers in your workplace; share what you know and learn what you don't.

Schedule regular conversations with your client... don't just wait for the phone to ring.

Mentor less experienced developers in your workplace,  through pair programming, listening and advising, not just monologuing.

Attend an unconference

Pick up the phone and call your customer. It helps.

And don't save the quality-assurance conversation for the last minute. Because nothing says "I care about the outcome of this project" quite like taking the time to make sure things work.