Categories
Geek / Technical Personal Development

You Are NOT a Code Monkey, So Stop Acting Like One

I lead a C++ lunch-and-learn group at my day job. Its audience is either non-developers interested in learning some basic programming, or developers who have experience with a different language and are still getting to grips with C++’s basic syntax.

One of the things I try to stress is the need to be conscious when coding. Software development is an intellectual activity. The most important aspect is not coding. It’s thinking.

So I often stop my group and ask them to justify a code change. Too often I see someone copy and paste code in the hopes that it will “work”. It then becomes trial-and-error in getting the magic incantation of semi-colons, curly braces, and keywords in the right order in an attempt to appease the compiler so that the developer can continue.

It’s what I call Hope-and-Pray-based Development, and while a million code monkeys eventually might be able to hack out the equivalent of Shakespeare’s works using this method, it’s not what a software engineer should be doing when trying to create customer value through code.

Now, it’s one thing to leverage the compiler. If you type out some code, and you’re not sure if it is right, the compiler will happily tell you why faster than you can formulate a guess. But you’re not just typing and pasting code in different ways in the hopes that the compiler will shut up.

The key is that you are testing a hypothesis and getting feedback and, most importantly, learning.

But sometimes the lesson is subtle.

Recently we’ve been working on the Prime Factors Kata by Uncle Bob Martin. It’s a short kata in Java that we’ve translated to C++ as we’ve gone through it, but because of my insistence on being conscious developers, it has taken a few sessions to get through the first few tests.

The first unit test says that you get an empty collection when you ask for the prime factors of the number 1. The second unit test asks you to return the number 2 when you ask for the prime factors of 2. So after the second test passes, the code looks something like:

std::vector<int> primeFactors(int number)
{
  std::vector<int> factors;
  if (number == 2)
  {
     factors.push_back(2);
  }
  return factors;
}

The third test is about the prime factors of the number 3. Once it passes, the code looks like:

std::vector<int> primeFactors(int number)
{
  std::vector<int> factors;
  if (number == 2)
  {
     factors.push_back(2);
  }
  if (number == 3)
  {
     factors.push_back(3);
  }
  return factors;
}

The third part of the test-driven development cycle is refactoring, and my group wanted to refactor this code to look like:

std::vector<int> primeFactors(int number)
{
  std::vector<int> factors;
  if (number == 2 || number == 3)
  {
     factors.push_back(number);
  }
  return factors;
}

The tests still passed, but I asked the group to justify their refactoring. I got responses about how it is more readable this way, or that there are fewer lines of code.

I said that those reasons are all well and good, but I fear they’ve lost something in terms of intent. I said that the variable number is the input. We’re asking for the prime factors of number, and giving back number as output happens to work but isn’t really what we’re doing when we’re providing the prime factors of a number. I argued that this change is hiding the intent of the code and that explicitly returning 2 for 2 and 3 for 3, while more lengthy, didn’t assume anything about the input as output.

This discussion/debate went on for the last 10 minutes of the session. Afterwards, they read ahead in the kata’s slides and saw that the refactoring in the slides does, in fact, do the refactoring as they did.

So was I wrong?

The prime factors of a number are directly related to the number, so it makes sense that the code can be written to return values that are defined in terms of that inputted number. So, ultimately, yes, the code will reflect this fact.

But I wasn’t focused on the code’s correctness. I was focused on the correctness of the thinking behind the code.

The developers in my group didn’t argue that the refactoring’s validity is due to the number being its own prime factor. They were focused on removing what they saw as duplication, on making the code shorter.

Effectively, they saw a change they could make and made it without concern about how it will be read later or how it will impact changes later. It was a change made for its own sake, mimicking changes they’ve seen elsewhere. It “worked” in this other context, so it must “work” here.

In other words, I recognized that this otherwise fine code change was happening for the wrong reasons.

Another example is when we passed our second unit test and decided to refactor the tests. We created a fixture, and the code originally looked like:

class PrimeFactorsFixture : Test
{
};

The compiler didn’t like it: error: β€˜static void testing::Test::SetUpTestCase()’ is inaccessible
static void SetUpTestCase() {}

After the developers eventually read the error message and tried to figure out why this function was even relevant to their code, someone suggested that sometimes the keyword public is put in front of the base class.

So they tried it, and it “worked”. The compiler built the code and we were able to run the unit tests.

Rather than continue on with the refactoring, I spent the next few minutes (re)explaining how inheritance works in C++, and that there is a difference between private and public inheritance, and that what they saw was a compiler error as our unit test was expecting PrimeFactorsFixture to have functions that Test provides but because we were inheriting from it privately, those functions were not available.

I spent that time because it is important that they understand WHY it worked. I want them to get out of the habit of throwing spaghetti at a wall and seeing if it sticks. I don’t want them to be satisfied with waving a dead chicken to get past a compiler error. I want them to think beyond “I saw this other code that looked like it might work here” and get to “we need this code because of this good reason.”

Trial and error is fine if you are actively learning from your errors, but if you don’t stop to find out why the errors happen, you won’t learn how to avoid them in the future. You’ll just leave behind a trail of horrible half-functioning code that appears to “work” at the time you wrote it without you being able to explain why.

If your development process could be replaced by a script that generates copy and pasted code or shifts values around at random until the compiler works, you aren’t acting like a software developer. You are acting like a code monkey.

Code monkeys bang on the keyboard until things seem to work, even if the code doesn’t in fact work as they think it does. They focus on getting past compiler errors without worrying about why the errors happened or if the code changes they made to “make it work” are even sensible in terms of what they are trying to accomplish.

Software developers think through what code is needed, and once they implement it, they know with some level of confidence that their code actually does in fact work. They learn from their mistakes. It’s not about getting it working. It’s about getting it working correctly.

And since I want my group to be made up of software developers, I often stop my lunch and learn group and force them to justify what they were thinking when they made a change. I won’t tolerate random code changes to appease the compiler. Instead, I want them to think the code through. I want them to be able to predict accurately how the compiler will react to their code when they write it.

I want them to put down the dead chickens and think.

One reply on “You Are NOT a Code Monkey, So Stop Acting Like One”

what with intuition and art, no only intellect? Software development is an art… I do it this way a long period of time but tired to see my surrounding.
Try to insert this point too, may be. Because you will never try to add excessive rows of code if you treat this as your artistic masterpiece.
And when you modify it as would be reproducing or fixing artistic ancient masterpiece, you will be careful like archaeologist, kind of – never adding ugly patches to Athene Statue being goddess of wisdom…. πŸ™‚ You could compare C++ with nectar of Gods in Olympus that now is replaced with coca-cola like java and C# and objective-c… πŸ™‚
Nice article…
πŸ™‚

Comments are closed.