A Beginner’s Guide on How to Improve Your Programming Skills
The basics above help you get started, but you won’t be a very good programmer yet with those. Below, I’ll explain some ways to go from noob to good in a short while. This section is the most complex one, so if you can’t follow along currently, don’t forget to bookmark to revisit it later when your mind is sharpest!
5.1 Write clean code like a champ
Let’s say you’re working on a 2D platformer game in Unity. You want to add simple range detection to decide when an enemy should attack the player. A regular programmer may write code that looks like this (if not using colliders):
void Update() {if (Mathf.Abs(transform.position.x * 100.0 - player.transform.position.x * 100.0) <= 10.0) {isAttacking = true;animation.SetAnimation(0, "attack", false);}}
If that terrifies you, it should. Writing clean code is about making it so easy to understand that you’re simply reading English. The above example is extremely simple, yet we can do much better. Here’s one way a more advanced programmer may write it:
//// Core logicvoid Update() {if (IsCloseToPlayer())Attack();}//// Implementation detailsbool IsCloseToPlayer() {return (PositionDifference(transform, player.transform) <= ATTACK_DETECTION_RANGE);}float PositionDifference(Transform t1, Transform t2) {return Mathf.Abs(GetHorizontalPosition(t1) - GetHorizontalPosition(t2));}float GetHorizontalPosition(Transform t) {return t.position.x * PIXELS_PER_UNIT;}void Attack() {if (isAttacking) return; // No need to repeat if already attacking isAttacking = true;int animationTrack = 0;bool loop = false;
animation.SetAnimation(animationTrack, "attack", loop); // Will reset isAttacking after AnimationComplete}
const float PIXELS_PER_UNIT = 100.0; // Number of pixels in a single Unity world view unitconst float ATTACK_DETECTION_RANGE = 10.0;
What’s one of the first thing you’re noticing?
“There’s a lot more code!!”, right?
Did it take me more time to write? You bet! But here’s why it’s better than the first block of code (more explanation also in section 5.2):
Abstraction and encapsulation
Hide implementation details.
If you look at the Update() function of the second code block again:
void Update() {if (IsCloseToPlayer())Attack();}
It’s simple to read, right? Especially next to the first version you previously saw:
void Update() {if (Mathf.Abs(transform.position.x * 100.0 - player.transform.position.x * 100.0) <= 10.0) {isAttacking = true;animation.SetAnimation(0, "attack", false);}}
When someone else (or future-you) reads your code, they want to understand what’s going first before jumping into the details. Sometimes, the details are not that important.
If you read the second code block, it will take you a lot more time to understand that it means: “if I’m close to the player, I should start attacking”. Creating nicely named bite-size methods makes your code a lot more readable.
Name your boolean conditions.
IsCloseToPlayer() is a lot easier to read than Mathf.Abs(transform.position.x * 100.0 — player.transform.position.x * 100.0) <= 10.0
As much as possible, name all your boolean expressions to something clear. A nice way to do it is by prepending is or has in front of the action name.
isCloseToPlayer, hasEnoughStorage, isAlive, etc.
Name your actions.
Attack() is a lot easier to read than:
isAttacking = true;animation.SetAnimation(0, "attack", false);
Can you encapsulate a set of instructions into a single named function? If so, do it!
Most good code doesn’t need comments to explain the logic because variables and functions are all named properly. If you need a comment to explain a block of code, chances are you can create a well-named method instead.
The rule of 7
How many lines of code should a function/method have? Here’s a simple answer: 7 ± 2. The reason for this is that our brain only can work on about that many instructions at the same time. I cringe whenever I see methods that have more than 9 lines of code.
Here’s an example from a game I’m working on:
public void SetUp() {this.SoulReaper.SetUp(GridAllies.Team); LoadPrefabs ();InitLists ();grids.ForEach (grid => InitGrid (grid));InitTimeline (true);InitAllyRowBonuses (teams [0]);InitOpponentRowBonuses (teams [1]);}
If you read the above code, you know all the steps required to setup this class (this sets up the “Combat” scene). It’s a little technical, but it’s still readable by normal people. Most beginners would put all the initialization logic in here.
I’ll spare you the long example, but combined, it’s about 250 lines of code. You’d never be able to read the logic in one go. If you dig into some of these methods, you’ll see they’re also less than 7 lines of code:
void InitLists () {monsters = new List
We did the same with our methods in this same section at the top:
bool IsCloseToPlayer() {return (PositionDifference(transform, player.transform) <= ATTACK_DETECTION_RANGE);}float PositionDifference(Transform t1, Transform t2) {return Mathf.Abs(GetHorizontalPosition(t1) - GetHorizontalPosition(t2));}float GetHorizontalPosition(Transform t) {return t.position.x * PIXELS_PER_UNIT;} Maintainability
Abstraction and encapsulation make it easier to maintain the code down the road because you understand what everything means without having to search through 100+ lines of code.
In the example above from my game, if there’s an issue with the timeline not getting initiated properly, I can simply search my code for “Init Timeline” and it’s very likely the problem lies in that method.
This all makes the code much easier to debug.
5.2 Write classes like a champ
Now that we know why the second code block in section 5.1 is better, let’s add more to it.
Let’s say now you have a different enemy you want to code. The problem is, it attacks vertically this time, and its detection range is further.
A junior programmer may copy + paste the code above and replace position.x with position.y and change the ATTACK_DETECTION_RANGE constant.
We can do better with the use of classes to minimize code duplication. Good programmers don’t rewrite code they don’t need too. That’s why you’ll often hear to good programmers are “lazy”.
Here’s what the “parent” class could look like:
public class Enemy : MonoBehaviour { public float AttackDetectionRange = 10.0f;public string AttackAnimationName = "attack"; protected const float PIXELS_PER_UNIT = 100.0; // Number of pixels in a single Unity world view unit //// Core logic void Update() {if (IsCloseToPlayer())Attack();} //// Implementation details protected abstract bool IsCloseToPlayer(); protected virtual void Attack() {if (isAttacking) return; // No need to repeat if already attacking isAttacking = true;int animationTrack = 0;bool loop = false;
animation.SetAnimation(animationTrack, AttackAnimationName, loop); // Will reset isAttacking after AnimationComplete}
protected float PositionDifference(Transform t1, Transform t2) {return GetWorldPosition(t1) - GetWorldPosition(t2);} float GetWorldPosition(Transform t) {return t.position * PIXELS_PER_UNIT;}}
So far, we’ve only made a nice abstracted class out of our previous enemy script. You’ll notice a few differences:
A variable or method that is public is one that can be called by (using the . notation in most languages) any instance of the class (an Object):
var instance = new Enemy();print instance.AttackAnimationName;
This would print “attack”. If the variable wasn’t public, it would give you an error, saying the variable is not accessible.
A variable or method that is protected is one that can be called by parent and child classes. We’ll dive deeper into the concept shortly.
A virtual method (in C#) means that it can be overridden by a child class by using the override keyword. abstract methods also need to use the override keyword.
Now, you won’t be able to instantiate Enemy because of the abstract method. Everyabstract method need to be implemented in child classes. If we want to instantiate it, we need to create a child class. Let’s make the horizontal attacker first:
public class EnemyHorizontal : Enemy { protected override bool IsCloseToPlayer() {return (PositionDifference(transform, player.transform).x <= AttackDetectionRange);}}
That’s it!
Inheritance
The above code block touches on a very important concept of classes. Likely the most important, so pay attention! :)
Inheritance is a hierarchy of classes deriving their logic from other classes.
In the code above, EnemyHorizontal inherits all the public and protected variables and methods from Enemy. Enemy is the parent class. EnemyHorizontal is the child class.
In the Enemy class, IsCloseToPlayer was abstract. We created a “definition” for it in the child class called EnemyHorizontal. EnemyHorizontal will do the same things as Enemy except for its detection of the player in range (IsCloseToPlayer()).
This line: public class EnemyHorizontal : Enemy can be read as EnemyHorizontal is a type of Enemy. It’s an Enemy, but with additional logic.
The above code block is 100% equivalent to the following version that doesn’t use inheritance:
public class EnemyHorizontal : MonoBehaviour { public float AttackDetectionRange = 10.0f;public string AttackAnimationName = "attack"; protected const float PIXELS_PER_UNIT = 100.0; // Number of pixels in a single Unity world view unit //// Core logic void Update() {if (IsCloseToPlayer())Attack();} //// Implementation details protected bool IsCloseToPlayer() {return (PositionDifference(transform, player.transform).x <= AttackDetectionRange);} protected virtual void Attack() {if (isAttacking) return; // No need to repeat if already attackingisAttacking = true;int animationTrack = 0;bool loop = false;
animation.SetAnimation(animationTrack, AttackAnimationName, loop); // Will reset isAttacking after AnimationComplete}
protected float PositionDifference(Transform t1, Transform t2) {return GetWorldPosition(t1) - GetWorldPosition(t2);} float GetWorldPosition(Transform t) {return t.position * PIXELS_PER_UNIT;}}
That’s a little longer, right?
Now, let’s create the vertical attacker:
public class EnemyVertical : Enemy { protected override bool IsCloseToPlayer() {return (PositionDifference(transform, player.transform).y <= AttackDetectionRange);}}
That’s it!
In both cases, because AttackDetectionRange and AttackAnimationName are public variables, we can set any value for them. We can therefore create an infinite amount of enemies that can attack either horizontally or vertically. For example:
# Goomba : EnemyHorizontal# AttackDetectionRange = 5.0;# Koopa : EnemyHorizontal# AttackAnimationName = "hide_in_shell"# HammerBro : EnemyVertical# AttackDetectionRange = 15.0;# AttackAnimationName = "throw_hammer";
That’s all good, but what if the attack pattern is different? What if I want a monster that is constantly attacking when in range?
Can you figure it out on your own?
If not, here’s one way to implement it:
public class EnemyBrawler : HorizontalEnemy { void Update() {this.isAttacking = IsCloseToPlayer();Attack();} protected override void Attack() {if (!isAttacking || animation.animationName == AttackAnimationName) return; // Don't continue if already attacking
int animationTrack = 0;bool loop = true;
animation.SetAnimation(animationTrack, AttackAnimationName, loop);}
Here’s what the hierarchy of classes would look like at this point:
Enemy is the parent class of Horizontal and Vertical. Horizontal is the parent class of Brawler. Horizontal and Vertical are child classes of Enemy. And Brawler is a child class of Horizontal.
So, while in the second code block of section 5.1, it may have looked like we added so much code uselessly, we shortened the code in the long run and prevented any duplication of code.
This leads us to the concept of Cohesion:
Cohesion
Cohesion refers to the degree to which the elements inside a module belong together.
In our simple example above, every enemy only has one action to perform: Attack. If we had to add more actions, what would they be?
The simple question to ask yourself where deciding which methods a class should have is: “does it make sense for this object to do that action?” If not, the action should be added to a different class.
Here’s an example:
public class Animal {void Move() {// TODO: Implement this}}public class Dog : Animal {void Bark() {// TODO: Implement this}}public class Cat : Animal {void Meow() {// TODO: Implement this} void Bark() {// TODO: ???}}
This example is intentionally ridiculous to prove a point. It is extremely common for programmers to add a method to a class it doesn’t belong to because it also doesn’t belong to other existing classes. The Cat class here is an example of a class with low cohesion.
5.3 Use Git like a champ
Take this very seriously, please! There’s nothing senior programmers hate doing more than resolving merge conflicts and when they can be avoided!
The very basics
At its most basic, Git is a “framework” for collaborating on a codebase. Git handles versioning and merging of files from one commit into another. Git code is stored in a repository, living on a Git server.
As a user, you’ll checkout a branch of the code, usually develop or master. This means it’s now stored on your computer. Any changes you make remain on your local computer until you push your commit(s) to a remote branch. However, you can’t push until you’ve committed your changes into your local version of the branch.
Phew!
While that sounds complicated, here’s the most basic process when working alone:
git commit -am "something"git push origin develop
The first line creates a snapshot of your code (a commit) with the annotation: “something”.
The second line sends the committed code to the remote server, to the branch called develop.
That’s simple enough so far since you’re working alone, but let’s say Jane joins the project. To keep things simple, Jane is also working on the develop branch.
The remote branch develop is the “source of truth”. It’s where the most final version of the code that can be run by everyone on the project is stored. Now, both you and Jane have a local branch of the same name (develop).
Let’s say both of you want to work on the same file at the same time: EnemyHorizontal. You want to define the IsCloseToPlayer() method (specified by the override keyword), and she wants to add a new method called UpdateRange().
Once you’re done implementing your code, you’ll do that:
git commit -am "implemented IsCloseToPlayer"git push origin develop
Shortly after, Jane finishes her part and does similar:
git commit -am "added UpdateRange method to EnemyHorizontal"git push origin develop
Git won’t allow the second line to go through.
Why do you think that is?
At the moment Jane pushes her code, the remote develop has a new commit from you— it is further ahead than her local branch in terms of code.
Before she can push her commit, she’ll have to pull the latest changes on the remote develop. It’s done this way:
git pull origin develop
This merges the latest commits from the remote develop branch into her local develop branch. So now Jane has the implemented IsCloseToPlayer method in her local branch as well, in addition to her code. Now she can safely push (provided no one else pushed before her!).
git push origin develop
To stay on the safe side, you might always want to pull before pushing.
git commit -am "something"git pull origin developgit push origin develop Conflicts
You’ll dread the word. And it will happen to you. Let’s say you and Jane didn’t communicate properly and you both worked on the IsCloseToPlayer method for the HorizontalEnemy class. Jane commits and pushes her changes first, followed by you:
git commit -am "implemented IsCloseToPlayer on EnemyHorizontal"git pull origin develop
BAM! Big fat warning from Git: “You have merge conflicts!”
Well, this blows. What happened?
Git can’t decide if your version of the code is “better” than Jane’s. Both started from the same base commit and changed the same part of the code. There’s no way for it to tell which version of the new code is better. It’s then up to you to decide. Git will show you the differences, and you have to resolve the conflict. You decide if your version is better or hers.
You could do one of two things here: (1) revert your changes and accept hers or (2) resolve the conflict using your version of the code, then push again.
GitFlow workflow
I know all this sounds complicated for now, yet it gets a lot more complicated…
The above process works fine for very small teams working on small projects, but it starts falling short very quickly. You want to minimize conflicts and losses of code as much as you can.
A common “framework” is the GitFlow workflow. I use it all the time on my projects. Going into the details of the workflow would be beyond the scope of this article, but it would end up looking something like that:
You can find more information on it here.
5.4 Become insanely resourceful
This is the number one skill of any programmer. Which is great because this is also one of the most useful life skill you can ever master.
As you keep learning Programming, you’ll frequently come across stackoverflow.com or similar websites. When you Google how to do something in programming, chances are someone else had the same issue as you did and someone else gave a clear answer and how to fix it. That question/answer is likely found on StackOverflow.
So, as a junior developer, spend a good amount of time Googling how to solve things in the language and framework you are using. There’s no shame in not knowing every answer. There’s simply too much to Programming to know everything.
If I’m working in a language I’m not familiar with, I frequently Google the syntax. If I’m working in a framework I’ve never used before, I Google “How do I do in framework”. Everyone does that.
In the programming world, you’re always expected to be resourceful and figure out answers by yourself first — which is a great way to learn anything. If you can’t figure it out after trying, then is the time to seek out help from others, either in person or by asking on StackOverflow.
Programming is the kind of skill that never stops evolving. If you can’t adapt, you’ll be left out.
When I hire programmers, I like to give them assignments in languages they’ve never worked with. Stressful? Sure, but it proves how adaptable they are. And that, to me, is more valuable than someone who knows 95% of a language.
Make a free weekly coding lesson your New Year’s resolution.
Code Year is a free computer coding course.
Photograph by iStockphoto/Thinkstock.
If you’re looking for a New Year’s resolution, let me suggest an idea that you might not have considered: You should learn computer programming. Specifically, you should sign up for Code Year, a new project that aims to teach neophytes the basics of programming over the course of 2012. Code Year was put together by Codecademy,* a startup that designs clever, interactive online tutorials. Codecademy’s founders, Zach Sims and Ryan Bubinski, argue that everyone should know how to program—that learning to code is becoming as important as knowing how to read and write. I concur. So if you don’t know how to program, why not get started this week? Come on, it’ll be fun!
Code Year’s minimum commitment is one new lesson every week. The company says that it will take a person of average technical skill about five hours to complete a lesson, so you’re looking at about an hour of training every weekday. That’s not so bad, considering that the lessons are free, and the reward could be huge: If you’re looking to make yourself more employable (or more immune from getting sacked), if you’d like to become more creative at work and in the rest of your life, and if you can’t resist a good intellectual challenge, there are few endeavors that will pay off as handsomely as learning to code.
But this isn’t only about you. Let’s talk about how all of us—our entire tech-addled society—could benefit from a renewed interest in coding. Over the past 20 years, and especially in the last five, computers invaded every corner of our lives. Most of us accepted their ascendancy with grudging tolerance; even if they’re a pain to use and don’t ever work as well as they should, these machines often make our jobs easier and our lives more enjoyable. Part of the reason we’ve all benefitted from computers is that we don’t have to think about how they work. In their early days, the only way to use a computer was to program it. Now computers require no technical wizardry whatsoever—babies and even members of Congress can use the iPad. This is obviously a salutary trend. I’ve long argued that computers, like cars, shouldn’t require technical skill to operate, and the easier that computers are to use, the more valuable they’ll be to all of us.
Yet the fact that any moron can use a computer has lulled us into complacency about the digital revolution. You can see this in the debates over SOPA, the disastrous Internet piracy bill that has been embraced by politicians because many of them simply don’t understand its technical implications. Or, as Thomas Friedman points out, consider the absence of any substantive topic relating to technology from the Republican presidential debates.
I noticed something similar in the summer, when I published my series about the robots that are poised to steal high-skilled workers’ jobs. I was surprised, during my research, to find that many people who are vulnerable to replacement by machines had no idea how quickly they could become irrelevant. Lots of people I spoke to insisted that their jobs required too much schooling, or relied on several “fundamentally human” skills, and would likely remain forever dominated by humans. (That’s what travel agents thought, too.) There’s bliss in this kind of ignorance, but it’s dangerous. You don’t need to know how a computer works in order to use it—but if you learn how computers work, you may avoid one day working for them.
There’s no better way to learn how computers work than to start programming. “Learning to code demystifies tech in a way that empowers and enlightens,” Gina Trapani, the app developer and former editor of Lifehacker, wrote when I emailed to ask how coding had changed her life. “When you start coding you realize that every digital tool you have ever used involved lines of code just like the ones you’re writing, and that if you want to make an existing app better, you can do just that with the same foreach and if-then statements every coder has ever used.”
Programming can come in handy even if you work in a nontechnical field. Say you were a travel agent in the 1990s: If you didn’t know how to code, you wouldn’t have been able to see the coming demise of your profession. But if you’d dabbled in programming, not only would you have had the skills to appreciate how the Internet might hurt your profession—you might also have been able to play a part in the online travel bonanza, either by building your own travel site or going to work as an expert for one of the new Web travel firms.
Sims, the co-founder of Codecademy, says there are countless such opportunities in the workplace—places where a little bit of code can make you much better at your job. “When we were raising money, we met one investor who created a script for Angel List”—an online directory of startups looking for cash. “When a company would hit a certain number of followers on Angel List, his script would automatically send out an e-mail to the company looking for a meeting. That’s an example of how knowing a tiny bit of coding helped him get better deals without hiring an associate,” Sims says.
But knowing how to code will improve your life even if you don’t ever program anything useful. I learned the Basic programming language when I was a kid, and then I sharpened my programming skills in high school and college. I am by no means an expert programmer, and I rarely take up coding in my job, but I learned enough to alter the way I approach problems. And that’s the most interesting thing about taking up programming: It teaches you to think algorithmically. When faced with a new challenge—whether at work or around the house—I find myself breaking down the problem into smaller, discrete tasks that can be accomplished by simple, repeatable processes.
Want to train your baby to sleep? Try this algorithm, which my wife and I found in a book called The Sleepeasy Solution when our then 1-year-old was driving us crazy every night. At bedtime, put your baby in the crib. Leave him to cry for five minutes, and then go in to comfort him for 30 seconds. Next, leave him to cry for 10 minutes before you comfort him, and after that go in to comfort him every 15 minutes. Do it until he sleeps, which should happen in an hour’s time.
After a few days of this, our baby was sleeping through the night for the first time in his life. I can’t say for sure that this method worked because it was a well-defined algorithm, but I suspect that was the case. Babies, like computers, seem to like predictable, rigid routines. Maybe we’re all born with a capacity to think in code—and perhaps it’s time you took advantage of that awesome power.
Correction, Jan. 4, 2012: This piece originally and incorrectly referred to Codecademy as Code Academy. (Return to the corrected sentence.)
Pair Programming: Benefits, Tips & Advice for Making it Work
Pair Programming — a pair that’s greater than the sum of its parts. You may have heard about pair programming and wondered whether it was worth trying in your workplace. On the surface it sounds simple, but two developers sitting together are not all that it takes to achieve productive pairing.
Logistical and personal hurdles such as scheduling, tool choices, and distractions can stop you from getting the most out of pairing. But the potential advantages can make it worth the trouble of recognizing and surmounting these challenges.
Why Pair?
How could it be more productive to take two programmers who were previously working on separate projects and have them work together on a single project? Won’t everything take twice as long? To an outsider the idea of pairing may sound counterproductive at first, but the advantages become apparent when you start to think about why we code and what we’re trying to accomplish.
Programming is not about churning out the most lines of code in the shortest amount of time, or even delivering the most features within increasingly tight deadlines. You can have engineers working around the clock pushing new features into production, but how productive are they really if those features are cranked out by individuals working in isolation according to their own unique understanding of the overall architecture? The resulting code is likely to be riddled with technical debt such as hidden bugs, performance issues, idiosyncratic syntax, and inefficient designs that may not use resources efficiently and may make it that much more difficult and time consuming to modify the code when one of those flaws surfaces.
You need your code to be meaningful and well written so that it works together seamlessly and can be modified easily. You need it to encapsulate the desired functionality so that your end product behaves properly and performs as expected. You need it to be resilient so it can withstand organizational changes that are a natural part of working together, as well as environmental changes and new customer expectations that may make today’s workable solution obsolete without much warning.
In order to make that possible, developers need to be able to agree about fundamental requirements clearly, get up to speed quickly with whatever new or established technologies may be required, and focus without interruption to test out creative solutions and develop a product that’s worth putting in front of the customer.
These are the real-world challenges that pair programming helps to address. When two developers work together in a pair, the quality of the code they produce improves along with their shared understanding of how it works. This makes it easier for the next person who reads the code to pick it up and modify it when necessary, and it reduces the danger that the only person on the team who knows how part of the code works may win the lottery and leave the team, taking that precious knowledge with them.
The time cost in mythical work hours is nowhere near the 50% that may seem intuitive if you tried to to equate the intricate art of coding with repetitive assembly line work. Some empirical studies have concluded that pair programming might result in about a 15% increase in the time it takes two programmers to accomplish the same tasks had they been working alone, but the resulting code will also be of much higher quality, with about 15% fewer observable defects to fix. Combine this with the shared ownership, deeper engagement, and faster problem solving that comes from having more than one mind engaged in solving a problem, and it’s clear why pair programming is a popular approach.
What Exactly is Pairing?
So what does it take for two developers working together to achieve the productivity and quality improvements that come from pairing? It’s mostly a matter of learning how to work collaboratively, which is not necessarily the way most of us learned to code.
By definition, pair programming doesn’t start until you have two people working together on one computer. But how does that work in practice?
Two People …
The fundamental element of pair programming is working together with your pair. When a task is accepted, it needs to be shared between both of the people working on it, and they both need to be fully engaged in the task while they’re pairing on it. That means that they both need to understand the requirements the same way, and work together to come to a shared understanding of how they want to go about meeting them.
Pairing helps people get better at verbalizing their ideas and expectations. The implicit understanding you have in your head when you’re working alone needs to be communicated so both you and your pair know you’re on the same page. Getting as explicit as possible about the work and the approach up front will help make the pairing experience much more agreeable. Pairing involves a lot of talking, as that’s the best way to keep two minds actively engaged in the problem at the same time.
For this reason, pairing is often associated with agile story writing, in which requirements for a feature are defined in consistent, plain language that can be understood equally well by Product and Engineering people with little room for ambiguity. Often pairs will ask for stories to be spelled out in Gherkin, which is a way of using common, non-technical phrases that are easy to translate into automated tests, so the pair can verify and demonstrate that each feature works as expected.
Writing in Gherkin means taking a feature and breaking it down into a simple story about a customer who wants something that this feature will deliver:
As I want
Then all the acceptance criteria are written out in a consistent syntax, defining the anticipated permutations and scenarios associated with that story:
Given When
Of course, it’s not mandatory to use this exact phrasing, but if the requirements of a feature can’t be expressed in this minimalist way, it’s possible that the expectations are ambiguous. That’s a potential red flag that’s easier for a pair of programmers to spot when they start to discuss what’s needed.
As soon as a pair accepts a story to work on, they should be able to define how they will know they are done and how they’re going to prove it. From there, they can start to figure out together how best to approach the job.
In fact, the pair working on a feature should know enough up front that they could start by writing an automated test based on the first acceptance criterion before writing any code, making sure the new test fails, and then writing just enough code to make that test pass before refactoring and then starting on the next acceptance criterion. This approach is known as behavior-driven development, and while it’s not part of the definition of pair programming, it harmonizes beautifully, along with test-driven development.
Working Together …
When you have two or more people trying to work together, the first thing they need to do is agree on a work schedule. It’s not really pairing if two developers aren’t working together at the same time. Because of this, it’s essential that the developers who plan to work together coordinate their schedules and make sure they both agree to a time and place where they will work.
One mistake I’ve seen pairs make is trying to maximize the time they work together as a pair by scheduling a full eight hours together, and sometimes trying to work together beyond that. Pairing is intensive work that requires a heightened level of focus and participation. It’s very taxing to try to pair for more than five or six hours in a day, and even that might be stretching it for even the most resilient of us. When setting up a pairing schedule, try to agree on a fixed and limited time that will fit within a typical eight-hour work day, leaving time for lunch, email, personal tasks, etc. Those personal tasks are essential to our work day, but they’re also distractions that shouldn’t be attempted during a pairing session.
It can also be socially awkward to remind your pair when the agreed pairing time has come to an end. For that reason, it can be a good idea to set an alarm that will break the news to both of you without putting the burden on one or the other.
Skill level is another area where pairs can have trouble. It’s a fair assumption that, no matter what you’re working on, the person you’re working with has a different background, experience, and comfort with the topic. Recognizing that up front is important, so neither of you will feel the need to try to hide that fact. One of the benefits of pairing is that working together naturally brings up the skills of anyone learning something new, whether that something is a programming language or a communication style.
Be gracious if you feel you’re more skilled than your pair. Treat them the way you’d want to be treated as you learned something new. And remember that the point is not only to get the work done, but to make sure that both of you have sufficient knowledge and ownership of the end result.
To that end, it’s vital that each programmer have the opportunity to sit at the keyboard and drive while the other observes and navigates through the code. The concept of taking turns may be new to anyone whose exclusive experience as a programmer has been solo work, so there will be a learning curve as you adapt to verbalizing your intentions when navigating, or carrying out someone else’s ideas when driving.
A programmer new to pairing but comfortable with the task at hand can easily get into a pattern of holding onto the driver role for as long as possible. Similarly, if you’re not driving at the keyboard and you’re not all that familiar with the code, it’s easy to find your mind wandering back to your phone, your email, and your other tasks. When that happens, you end up with one person coding alone and the other person sitting in the same room scrolling through social media.
One of the clues that a pair might be having trouble taking turns is silence. Pairing is a noisy process, involving a lot of questions, feedback, discussion, and collaboration. When a pair finds themselves going for more than a minute or two without saying a word, it’s likely the pairing has stopped.
One useful technique that can keep pairs from falling into this antipattern is to use a Pomodoro timer. These timers will keep a running countdown of the seconds as you work in 25-minute increments, and then tell you to take a break for five minutes. By switching roles between the driver and the navigator during these breaks, a pair can avoid lapsing into extended sessions with just one driver.
On One Computer …
Pairing only works when two people dedicate their full attention to a single computer. It’s better to avoid the distraction of having two (or more) active screens going during a pairing session. Even if one person just wants to look up some relevant code examples or check on the status of a background process, it’s better to do that on the one shared computer. If it’s a search, both developers can see how the search is constructed and what potential results come up. If it’s a status update, neither developer needs to be left behind.
So in any pair, both developers must be able to see the screen they are working on together clearly. One of the essential tools for pairing is a monitor big enough that both developers can see what’s being written clearly. Depending on the circumstances, this can be accomplished with a shared laptop if you don’t mind huddling together and you use a large enough font with adequate contrast. A better solution is a larger desktop or wall-mounted monitor where the code can be displayed and viewed together more comfortably.
Remote pairing is a very viable option these days. There are a number of great solutions ranging from Slack or Zoom tools to full-blown integrated development environments and text processors that support screen sharing, conference calling, or both, allowing two people on opposite sides of the desk or opposite sides of the world to work together. Even if you’re at the next seat in an open office, screen sharing can make it easier for both of you to see and interact with the code on the screen more comfortably. (I just encourage you to stay focused and resist the urge to bring up your email in a separate window while you’re pairing remotely. It’s disrespectful, it will be obvious to the person you’re pairing with, and your distraction will make you miss out on opportunities to learn together and produce quality results.)
If a colocated pair is sharing a single machine, they’ll need to come to some agreements about the configuration of their shared computers, keyboards, mouses, etc. That ergonomic keyboard with all the custom hardware macros might not be the best choice for sharing. But if you don’t mind using the same laptop or keyboard and mouse, swapping roles from navigator to driver in a pair might be as simple as switching places.
A popular and very robust solution is to make active use of a version control system like Git. Commit your code frequently to a shared repository so each developer can pull the latest version and work on their own device when they switch roles. Using version control to handle swapping in pairs has the added advantage of creating a more detailed history of code changes for future logging and potential rollbacks. If the Git logs get too cluttered, it’s always possible to go back and squash those extra commits into a single, more meaningful one before doing a pull request.
At a higher level, once you start pairing with another developer, you’re going to notice some differences in the ways you each approach your tasks. One of you might know some fancy keyboard shortcuts, have some special aliases for common shell commands, or prefer to use a specific IDE because of its unique features. In terms of the code itself, you may also each have a different implicit understanding about how variables are named, how to structure a commit message, when to use comments, etc.
Pairing is an opportunity to make these unconscious variations in technique visible so everyone can benefit from the hidden wealth of experience and knowledge about how we code more effectively.
How to Start Pairing
There’s often a period of adjustment while building the muscle memory and learning to express ideas out loud that were once just thoughts in the back of your head. It’s also necessary to establish workable logistics to allow two people to work together, which might mean making adjustments in terms of schedules, locations, and equipment. And when you’ve got that working, you might try expanding the process to the whole team with enhancements such as mob programing for groups, or promiscuous pairing to give everyone on the team a chance to pair on all of the team’s stories.
Putting in the effort to get through the learning stages usually pays off in significant improvements, both for the quality of the code and for the happiness and sustainability of the people who create it. And if your entire team or organization adopts pairing, the learning curve will become even easier for new people, improving the onboarding process and helping everyone be more productive.
I've worked as a Web Engineer, Writer, Communications Manager, and Marketing Director at companies such as Apple, Salon.com, StumbleUpon, and Moovweb. My research into the Social Science of Telecommunications at UC Berkeley, and while earning MBA in Organizational Behavior, showed me that the human instinct to network is vital enough to thrive in any medium that allows one person to connect to another.
Comments
Post a Comment