Most developers have at least heard this axiom before: “Programs must be written for people to read, and only incidentally for machines to execute."
The idea is simple. If someone writes code that is perfectly executable, but no other engineer can read it, then it’s bad code. In a magical software utopia this would be rare, but in real life things get messy. Languages and conventions go in and out of style. Developers go on vacation or switch jobs, and a dragon-like legacy code is left behind for shiny new developers to grapple with.
I talked to developers at Shopify about how to deal with spaghetti code you didn't write. (Not only that, but we got their best tips in writing clean, bulletproof code that won’t make future developers hate you.)
Above all, discovering the other developer’s motivation is the most important part of wading through unfamiliar code, says James MacAulay, Toronto Engineering Lead at Shopify. Let's assume you're starting with a working program, but one that's far from perfect, and you've been asked to take over. How do you do it?
Wading into legacy code can feel like walking blindly into a minefield. Some developers are too afraid to touch it, and then you’re stuck unable to implement new features altogether. Fear is your worst enemy in facing legacy code. But you shouldn’t cannonball into it, either.
At Shopify, new hires start with a few small bug fixes. The same method works when you face ugly code, says Peter McCracken, software developer at Shopify. Before you make any changes, start out by firing small bugs that were recorded, and go from there, he says. “By touching a few small files of code, it gives you a little tickle, and eventually you get to see the bigger purpose.”
“It always seems like a disaster at first,” McCracken says. It’s human nature to confuse unfamiliar with worse, but be patient and don’t make rash judgments, he says.
“It’s always tempting to think: ‘This code is the worst.’ It’s faster to rewrite it from scratch than try to understand what other developers did. But your first glance is often an overly simplified take, and over time you learn more about the problem and realize their code actually did make sense, even if there are more functions tacked on.”
In other words: If it ain’t broke, don’t fix it. Try to avoid making big changes to the code, at least in the beginning before you’ve added unit tests. “I try not to be harsh to other developers because they were doing the best job they could at the time,” says McCracken.
Two other developers must sign off on any Shopify code before it can be published. Not all companies are this diligent, and McCracken admits Shopify is the first company he’s worked at where code reviews are totally standard.
Consider him a convert: He says he’d always want to do peer reviews going forward. “It’s a sanity check. Sometimes you need someone to tell you: 'This makes no sense.'”
Try to get feedback from other developers, even if it feels cumbersome, MacAulay says. “The costs associated with people trying to understand ugly code are much bigger than the costs associated with taking the time to make it readable.”
The most basic, common problem in reading someone else’s code is the variable names (or lack thereof). Both MacAulay and McCracken echoed the importance of choosing descriptive names for variables. Be as explicit as possible about your motivations, they say. Meaningful names make working with old code infinitely easier.
Most languages have their own set of conventions, and code that follows the conventions consistently is always easier to work with. Shopify uses Ruby on Rails, so there is a directory structure and documentation framework to keep all code bases uniform.
“If you’re working with a language that has good idioms and everyone always does things the same way, then it’s much easier,” says MacAulay. Another pro tip: Keep functions short--a few small gulps is better than a long, convoluted function.
Ideally, all developers would follow rules three and four. But what if you encounter ancient code full of obscure acronyms and completely foreign variable names?
First step: Check the documentation. If there’s little documentation to explain the functions, the next best strategy is testing. Developers often disagree on how much testing is necessary, but MacAulay swears by it.
By running functional tests every time you add new code, you can slowly implement new features and small unit tests, and eventually draw out what the code intends to accomplish, he says. “Automated testing is the best kind of documentation. It really helps.”
This might be abundantly obvious, but the more languages you know, the easier it is to understand whatever code you come across, says MacAulay. It comes in handy to be flexible.
Even if a developer has followed all the above rules with a draconian sense of discipline, there is still an element of art to it. MacAulay and McCracken agree that it’s easier to work with the code of someone they know personally.
“It’s similar to being a writer or artist. You can usually identify someone’s style of writing, even if it’s embedded in a well-defined structure,” MacAulay says.
Reading old code can be a glimpse into another developer’s personality, or even another era. “People cluster around certain languages and these frameworks develop over time so it’s sometimes obvious that code was written four years ago, just from seeing a pattern of conventions,” says MacAulay.
At Shopify, a consistent framework of conventions is used throughout the company. But the conventions have changed over time, both formally and organically, as things come in and out of style, he says.
Working with other people’s code can be unpleasant at best, and a disaster at worst, even for seasoned developers at the most successful tech giants.
Want proof? They’re asking for help.
A flock of companies have popped up to offer software development consulting. One of the most well-known of this species is Pivotal, who boasts Twitter, Groupon, Square, and GE among its clients.
“Many of these huge companies have ancient code bases that are very fragile,” says David Goudreau, Pivotal Labs’ VP of Engineering, West Coast. “We regularly have people come to us saying: It takes us too long to implement a feature now, because the code base is so complicated, everyone who originally wrote it has left, and everyone is afraid to touch anything.”
The San Francisco-based consultancy is a hodgepodge of different platforms, all strewn together with a broad mandate: Teach companies how to build web and mobile software for rapid deployment on a large scale--which is what Google, Amazon, and others have mastered over the past decade.
At the heart of Pivotal is Pivotal Labs, who works with startups to beef up software development through “agile” strategies. Programmers here work exclusively in pairs. Two keyboards, two mice, and one computer assemble at each workstation, where two developers build the same piece of software. It’s meant to be a focused, seamless conversation during which two brains work as one. There is no email, texting, or web surfing, and distractions are discouraged.
Pair programming may seem borderline fascist to an outsider, and has sparked inevitable backlash. See: An April Fool’s day video titled “Spooning”, a spoof on pairing, or the rabbit hole of threads on Reddit about pairing nightmares.
It forces developers to sit in close quarters for hours, a far cry from the warm blanket of solitude many coders are accustomed to. Goudreau admits that some client developers can be “apprehensive” about pairing.
In practice, pairing is rare outside of “agile development” and extreme programming camps. Even so, Pivotal’s agile strategy been credited for shaping Twitter’s development culture. Jack Dorsey once announced that the cultural contributions from Pivotal to Twitter have been “quite meaningful”,and that much of Square’s development process is actually based on Pivotal’s process.
It’s a wildly useful concept, especially when it comes to working with old code bases or introducing new hires to an existing code base, says Goudreau.
With pairing, you are never dependent on just one engineer for a project, because all code will be touched by at least two developers, says Goudreau. This helps prevent “information siloing”, a common occurrence when each developer in a project has a specific task, such as front end or DVA. They won’t see the other sections of code, and if someone goes on vacation or leaves, the others are stuck.
“We always focus on the ‘bus count.’ How many people would have to get hit by a bus before the team would be in trouble? Ideally the bus count is as high as possible,” he says.
[Image: Flickr user Nat Welch]