The Elite Eight Development Principles
Originally posted 2009-04-18 03:11:31
As a management team, we recently crafted and presented a list of development principles for our developers to follow. The goal was to provide vision and direction, not to dictate the outcome of every scenario. Here’s a somewhat sanitized version of what we presented.
The Elite Eight Development Principles
- Code with your teammates in mind.
- Don’t break the build.
- If it ain’t in source control, it doesn’t exist.
- If it ain’t testable, it doesn’t work.
- If it ain’t repeatable, it was never done.
- Think more, write less, and keep it simple.
- You’re better with tools.
- Share what you learn.
#1: Code with your teammates in mind
\”It’s OK to figure out murder mysteries, but you shouldn’t need to figure out code. You should be able to read it.\” — Steve McConnell
If developers truly understood and followed this principle, we could shrink this list from \”Elite Eight Development Principles\” to \”Only One Development Principle.\” You’re going to write code and move on, whether to another company, another team, another project, or simply to Disney World for a week, and somebody else will have to update, add to, refactor, or fix bugs in your code. Have compassion for the developers and analysts who come behind you. Don’t make them pick through convoluted code, straining to figure out how it works — do your best to flatten their learning curve.
Here are some thoughts on how to code with your teammates in mind:
- Use the automatic formatting set up in the IDE so the code looks consistent.
- Heed and eliminate compiler and static analyzer errors and warnings.
- Name your packages, classes, variables, and methods meaningfully.
- Use libraries appropriately.
- Avoid magic numbers.
- Comment anything that smells weird.
- Don’t copy and paste.
- Reuse common methods appropriately.
- Refactor aggressively.
- Don’t assume that everyone has operator precedence rules memorized.
- Use javadoc.
These are just some thoughts — you’ll think of more as you code with this mindset. Focus not on proving your cleverness, but rather on how to make life easier on the next coder to touch your code. If you can’t muster kind feelings for them, then at least do as the C2 Wiki says: \”Always code as if the person who ends up maintaining your code is a violent psychopath who knows where you live.\”
If javac doesn’t care about white space, why should I?
Few things ignite programmers’ ire more than discussions of tabs vs. spaces or the appropriate placement of curly braces. No formatting rules, whether curly brace placement, equals sign alignment, or the number of spaces per tab ever achieve universal preferred status. Since no formatting standard is universally correct, and people will have beefs about each possible nuance of any formatting scheme, and the compiler has no opinion and accepts correct code no matter how it’s dressed, why bother trying to format according to a standard? Isn’t function more important than form? Isn’t content what matters?
Absolutely. Content is indeed what matters, and function trumps form for everything except Donald Trump’s hair. That, however, is the point: code that differs in its format draws attention to how it looks and away from what it does. Imagine, for example, that you were reading John Grisham’s latest novel, and each page used a different typeface. Text on even pages was justified, while text on odd pages was flush left, ragged right. Some chapters were named, while others were merely numbered. These formatting oddities might not ruin the story, but they would annoy you and detract from the story. They might even annoy you more than the fact that the book ends abruptly, dangling many story lines and convincing you that the editor had called and told busy John that his time was up and that the story had to be shipped as-is to the printer, and money would roll in regardless of whether the story actually reached a literary conclusion.
In the same way, code that varies in formatting draws attention to its formatting differences and slows developers’ understanding and flow without providing any offsetting benefit. If developers instead agree to disagree on how pure code should be formatted, and set aside their formatting preferences to unite on how to arrange the curly braces, ;s, equals signs, and spaces in the shared code base, everyone can focus on the content of the code and produce more of it. Also, any energy that isn’t spent on formatting wars, in which developers race to overwrite each other’s latest check-ins with reformatted versions of the same code, can be spent on writing new code, fixing bugs, or reading John Grisham’s latest novel. Everyone wins, especially John Grisham.
A tale of copy and paste
Clipboards, which allow you to cut or copy text from one virtual place and paste it into another, represent a great advance in computing. Much of the criticism surrounding the iPhone, in fact, laments the lack of cut-and-paste (the rest of the criticism is that all of the free games on the App Store suck). Why should developers not use this powerful tool that promotes more aggressive refactoring, causes fewer typos, and aids in reusing code? Although it’s been said that \”pasting code from the Internet into production code is like chewing gum found in the street,\” developers should indeed liberally use the clipboard properly. Misusing the clipboard, however, gets developers in trouble and bloats a code base. Against this type of usage I presently lobby.
In proper usage, a developer cuts common code from a class and refactors to a base class. In improper usage, a developer copies the whole class into a new file and changes a few lines here and there. In proper usage, a developer moves code within a file to be cleaner and more readable. In improper usage, a developer copies code to multiple places within a file to perform the same steps in multiple places. In proper usage, a developer tightens the code. In improper usage, a developer loosens the code like a belt after Thanksgiving dinner.
I once worked with a developer who cut and pasted like a kindergartner on craft day, and his code sagged with duplication and smelled like Elmer’s. Though he peppered me with questions about best practices and claimed to be dedicated to continual improvement, he shunned any of my vocal entreaties for reform and continued to scissor and glue-pot his code.
Since I had spent some time programming the Windows shell in those days, and since his dedication to workstation security resembled PETA’s dedication to preserving the beauty of fur coats, I hacked some code, waited for him to go to a meeting, and installed it onto his unlocked computer. The code injected itself into his text editor and lay dormant until he tried to paste anything. Any attempts to paste triggered my code, which blocked his paste attempts. Instead, his editor would display an error and remonstrate him for coding by mucilage. This frustrated him for a time, but he learned to refactor and reuse code appropriately!
Parenthetically, he never learned to lock his workstation. He eventually had to turn down the volume on his speakers when I reprogrammed his \”Start\” button to display a different, rhyming word, and to play a vulgar sound any time it was depressed. I also created my own administrator ID on his computer so I wouldn’t be bothered by those rare instances in which he remembered to lock his workstation. Want the password?
Comments on comments
Debates rage around whether and how to comment code. Some folks insist that all code be commented, while others disdain comments as superfluous, potentially incorrect, and violations of the Don’t Repeat Yourself (DRY) principle. Blowhards from both sides, however, seem to agree on a few things:
- Method names and variables should convey their
usage and intent - Implementations of known algorithms (e.g. Quick Sort) should be commented as such, so that reviewers will know that deviations from the algorithm represent defects that should be corrected
- Comments that describe the intent of code are usually helpful
- Any code that can’t easily be read and understood by reasonably proficient developers should carry explanatory comments
Steve McConnell wrote, \”Good code is its own best documentation. As you’re about to add a comment, ask yourself, ‘How can I improve the code so that this comment isn’t needed?’ Improve the code and then document it to make it even clearer.\”
#2: Don’t break the build
\”Don’t break the build.\” — Microsoft
Broken builds break spirits, stop work, and undermine trust. You can’t compile a broken build. You can’t deploy a broken build. You can’t test anyone’s changes in a broken build. You can’t run a broken build on your workstation. You can’t develop against a broken build. If the builds break too often, people quit updating from source control, causing more merging headaches. Builds must always work. When the rare but inevitable broken build surfaces, developers should drop everything and fix the build as quickly as possible. Buildable code is the foundation for what we do and should be treated as sacrosanct.
Note that broken tests equal a broken build. Failing tests mean that the build is broken.
We have a continuous integration server that builds our code almost constantly. It sends out notifications any time the builds break or the tests fail. Heed those notices. Make it your business to keep the build alive and well.
#3: If it ain’t in source control, it doesn’t exist
\”I consider this the golden rule of source control: Check in early, check in often.\” — Jeff Atwood
OK, so the \”ain’t\” is a gratuitous attention-getter. Source control merits attention, though. Anything that’s not in source control simply does not exist with any appreciable permanence. Things outside of source control are easy to lose, easy to overwrite, and impossible to reproduce. Check in code early and often. Don’t ever lose a week’s worth of work, or even a day’s worth. Everything has to be in source control: code, deployment scripts, database schemas, SQL scripts, shell scripts, et al. Even rush changes must go through source control: retrieve the latest code from the source control repository, update it, check it back in, and THEN deploy. It doesn’t take any longer, and it ensures that someone else’s change — whether rush our routine — won’t overwrite your heroics.
Out of control
At one place I worked, another team developed a project over many months, and never checked their code in to source control. You’re probably expecting me to say that they lost all their work the day before deployment and were roundly beaten and fired, but you’re wrong. No work was lost. The project went to production and worked well . . . about a month after development and testing had completed and the customer had been notified that their project was going live. The intervening month was spent trying to determine which of all the versions of the source files on all the developers’ hard drives represented the correct versions, checking in and rechecking in files furiously, trying to get the project to build, and deploying the application over and over into the testing environment and retesting and yelling at each other that they still had the wrong version of Foo.java or Blat.properties. I sat amidst the chaos like a sanctimonious schoolmarm and tut-tutted and tsk-tsked just under audible levels while I calmly and dutifully checked in my changes, day after day, and left work on time while they continued late into the night, checking in random files like overdue library books.
Imagine the cost of that additional month, incurred because they shunned source control. And they were lucky that the ramifications weren’t worse.
#4: If it ain’t testable, it doesn’t work
\”Why don’t people like testing? Well, the traditional way of testing is tough to take. You write what seems to be perfectly sensible code, then you write a test and the test tells you that you failed. No one wants to hear that. Let’s turn it around. Write the test first and run it. Of course it fails. You haven’t written the code under test yet. Start writing code. Keep testing. Soon, the test will tell you that you’ve succeeded!\” — Michael Feathers
Another gratuitous \”ain’t,\” but I like the parallelism. We’ve really struggled as a development shop to move to test-driven development, and we’re missing the behavior-driven development wave. The excuse has been the large, legacy code base, but we’re seeing plenty of new code without automated tests. One of our teams, on the other hand, continues to cover high percentages of their code with unit tests. Some folks get it, and some don’t. We all can, and must, get on board with this, though.
We should strive to prove all our code. Testing code demonstrates that it works. Repeatedly testing code demonstrates that it still works, despites any changes applied to it. Automatically testing code demonstrates that the code works, over and over, after each change. Automatically testing code outsources the mindless part of software testing to the computer, which never tires, never complains, never forgets steps or goes on vacation during critical parts of a project. Instead of riding to Production on hope, we can ride on the knowledge that we’ve proven our code.
Detractors say that automated unit tests take longer and don’t provide enough value. Let’s examine these.
Takes longer
No definitive answer exists on this subject. Some say writing the test code does indeed take longer because you’re writing extra code. Others say it takes longer to write unit tests at first, but as you continue writing tests first, you reach levels of proficiency that make the whole coding process faster. Still others say that writing tests first is faster from the outset, that defining how the code should work through writing the unit tests first makes writing the code extremely fast, and the overall coding time shrinks.
Which of these answers is correct? Probably all of them. Just as we all wear different hat sizes and prefer different John Grisham novels, we all have different experiences with writing tests first. The right answer isn’t whether or not writing the tests first takes longer. The right answer is that writing the tests first makes better code that we can automatically and repeatedly prove to be correct before we deploy it to Production. Not writing any code is even faster than writing code without unit tests, but we don’t often argue for that approach.
Not enough value
When I write a method, say, to accept a string that represents a zip or postal code, validate that it represents a correctly-formatted zip or postal code, look up the associated region in the world in a database, and return that region’s latitude and longitude, I build that code right before my eyes. I know each step of it. I know that when I try running it and type \”123\” into the web field and click submit, that I get an error that the zip or postal code is not correctly formatted. I know that if I type \”123456789\” into the same web field and click submit, that I get an error that no region has that zip or postal code. And I know that if I type \”8675309\” into the same web field and click submit, that I get the name \”Jenny\” displayed under for the latitude (this is a test database, after all). But each time I want to test it, I have to fire up my application server, navigate to the web page, type data into the web field, and click submit. And if the database is down, I can’t verify that my zip or postal code format validation code works.
If I’m writing tests first, however, I’ll design better code. I’ll separate the zip or postal code format validation code from the region lookup code. I’ll pass
various values to the routines, including ones I can’t type into a web field (like null, or Supercalifragilisticexpialidocious, which I can never spell consistently), and it doesn’t cost me any extra time or effort to retest all those values. I can mock the database connection, so I don’t need to rely on the database being alive or containing the right values. And I can run these tests, over and over, until I prove my code correct.
What’s more, I don’t have to worry about another developer unwittingly breaking my code. If he or she breaks it, the tests will fail without any extra effort on my part. In fact, I don’t even have to know about it. The build server will build that developer’s changes, run my unit tests, and announce the broken tests for that developer to fix.
Let’s suppose, too, that many months from now, the code breaks in Production because some user named ttutone actually types in 8675309, and our zip or postal code format validation code accepts it, but the database lookup code fails because you can’t reach Jenny without an area code and we spew an ugly stack trace to the logs and an incomprehensible error to the user. The developer working Production Support will first fix the tests, and then fix the zip or postal code format validation code, so we’ll never again face this error in Production.
Unit tests provide value not only when you’re developing the code, but again and again throughout their life, so long as the build server is running.
#5: If it ain’t repeatable, it was never done
\”I am rarely happier than when spending an entire day programming my computer to perform automatically a task that it would otherwise take me a good ten seconds to do by hand.\” — Douglas Adams
Early in my technical career, I worked with someone who wrote batch files for everything he did so he could repeat them at will. I worked with someone else who kept a copy of Notepad open filled with one-liners that he could cut and paste into a command prompt, rather than retype commands. I thought them both daft, and reveled in my superior memory that I could memorize command-line switches and pipes and redirects and commands and didn’t need to resort to stupid memory tricks to do my work.
I’ve gotten smarter with age, left the coding for people more adept at it, and now I stick to emails, documents, and spreadsheets. I learned along the way, though, that computers excel at repeating things, and humans don’t. We get bored. We forget things. We rarely do things the same way each time. Computers, however, obediently do exactly what they’re told, exactly the same way, every time.
Little of what we do is done only once, whether it’s building code, deploying code, populating databases, or writing emails. Things that must be done many times with predictable outcomes (like deploying code) will quickly become undone if they’re not absolutely repeatable. If, for example, a code deployment requires me to edit a properties file after the build but before copying the distribution bundle to a server, I’ll forget from time to time to edit the properties file. Or someone else will forget. Or someone will fat-finger the edit and the information in the properties file will be wrong. Or someone will be stuck using vi for the first time and not actually save the edited file. In all these cases, the deployment will undo the work of any previous deployment. Each thing we do must be repeatable.
Speaking of repeatability . . .
My teenage son recently called me from his grandfather’s house asking about the Mac OS X built-in command \”say,\” which accepts a string as an argument and speaks aloud whatever text you’ve passed to it. He wanted to know how to make this command repeat itself infinitely, so I walked him through the shell commands to do that. Like the mysterious stranger offering the salt grinder, however, I made sure to tell him how to stop the loop by pressing Ctrl+C.
Some time later, he called me again. To a backdrop of an incessant chorus of \”Grandpa is an idiot,\” he confessed that he forgot about Ctrl+C, and instead just closed the terminal window, and now he couldn’t make the computer stop impugning his grandfather’s intelligence and was imperiling his own inheritance. I tried to teach him the finer points of \”ps\” and \”kill,\” but that stayed just out of his reach. I finally had to tell him twice to reboot (the first time he just turned off the monitor, of course) for the sound to go away. And I think that’s why hash codes in C must be salted.
#6: Think more, write less, and keep it simple
\”There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult.\” — C. A. R. Hoare
This principle was inspired by a blog post from one of our developers: Back to Basics.
The early days of Linux attracted legions of computing elitists that felt ease-of-use reflected poor robustness or scalability or something, or maybe they just wanted everyone to know how smart they were. Thankfully, those snobs have moved on to Perl, which is too hard even for them and where no readable line of code passes code review. Much of the effort in the Linux space over the past few years has been to make it easier to use. Package management allows simple installation and removal of software packages and their dependencies. GUIs, once scorned, have been instituted for many configuration tasks. Desktop environments like Gnome and KDE have blossomed to rival, and in some instances surpass, the usability and simplicity of Microsoft Windows or even Mac OS X. One of the factors in Linux’s command of the server space, and its intrusion into the desktop space, is that the tools to configure and use it have gotten simpler.
Simple minds or simple approaches, however, do not produce simple artifacts. Simplicity requires careful thought and insight. Our development process devotes an entire phase — Design — to the portion of the software development lifecycle dedicated to careful thought and planning.
Take the time to think something through before banging code into your editor. Refactor code mercilessly so that you have less code doing more things, though not at the expense of readability. Keep in mind the statement made by Martin Fowler, author of Refactoring: \”Any fool can write code that a computer can understand. Good programmers write code that humans can understand.\” Another computing guru and cofounder of Apple, Inc., Steve Wozniak, said, \”My style with projects has always been to spend a lot of time getting ready to build it.\” In fact, Wozniak was most famous for the simplicity of his designs that used fewer chips than anyone else could imagine, until he thrilled the world with his dance steps on this season’s Dancing with the Stars. Actually, one judge said he danced like a TeleTubby, so maybe he’d prefer that we remember his chip design skills.
#7: You’re better with tools
\”Man is a tool-using animal. Without tools he is nothing, with tools he is all.\” — Thomas Carlyle
Stephen Covey admonishes us to sharpen our saws. Carpenters and auto mechanics admonish us to use the right tool for the job. Monster Truck fans tote the hammer they received as a gift for dropping out of sixth grade everywhere they go, and steal duct tape by the bushel from Walmart, so they can fix cars, mend fences, and patch their overalls. They all understand that they’re better with tools.
Search for tools. Learn how to use them correctly. Spend some time in their documentation and forums to understand their breadth and power. Apply them to your work.
We support a standard toolset: Eclipse as the IDE, AccuRev for source control, Cruise Control for continuous integration, TeamTrack for ticket tracking, Java as the language. Learn those tools.
Use them. Follow the standard procedures to keep your environment up to date. This way you can mesh with other developers, get support when you have issues, and focus on delivering projects.
What if you’re more adept with other tools? We want developers to be at their best, but also to fit into the team. The standard toolset can provide that. Additionally, you can judiciously augment your environment with other tools while still accomplishing that. You must follow two important rules:
- Keep your standard environment up to date using approved procedures so you have something to fall back on and can stay in sync.
- Don’t let your deviations from the standard toolset affect the team.
What does that mean? Let’s go through some examples:
- Use IntelliJ for code editing? This can be done as long as you:
- Don’t change the file structure for Eclipse projects, package names, et al.
- Either duplicate the standard formatting rules in IntelliJ or reformat in Eclipse before checking in.
We should always be searching for better tools and bolstering our toolsets. Just keep the two rules in mind and we’ll succeed together. Don’t forget to share tools you find — see principle #8.
#8: Share what you learn
\”If you think education is expensive, try ignorance.\” — Derek Curtis Bok
When our boss began his career, only three mainframes existed in the entire world, and two of them had run out of DASD. Any technologist worth his or her punch cards could know all there was to know about computing. Nowadays, Cisco has threatened to shut down the whole Internet unless at least one new web framework appears on SourceForge daily. Developers everywhere have met that challenge head-on, and now SourceForge threatens to run out of DASD from trying to store all the new web frameworks. Technology has grown too large, and continues to grow too fast, for any one person to know all there is to know about computing.
That doesn’t mean you shouldn’t try, however. Learn all the time. Read RSS feeds. Read books. Read articles. Try new technologies. Know what people are buzzing about and why they’re buzzing. Pay attention to who the industry luminaries are and what they’re saying. Understand what tools are losing favor and why. Know what tools are gaining favor and why. Feed your head at every chance.
You still won’t keep up. Turn to your peers. If they’re doing what you’re doing, then they’re learning, too. Trade perspectives with them. Swap knowledge and lessons learned. Tell them what you’re chasing and why. A work environment can become a collective brain. We provide a wiki and a discussion forum, and encourage developers to hold lunch-and-learns on new technologies they’re exploring. We’re trying to drive a culture of learning, and need learners to push the stagnant. Pay attention, and pass it on.
Closing Thoughts
The Elite Eight Development Principles on their own won’t guarantee great code, happy developers, or a chicken in every pot. Dedicated adherence to these principles, however, will lead to better decisions, fewer mistakes, and a more cohesive environment. These principles will enable great minds to produce great code. They represent management’s vision for software development.
Some final quotes:
\”Computer science education cannot make anybody an expert programmer any more than studying brushes and pigment can make somebody an expert painter.\” — Eric Raymond
\”The programmer, like the poet, works only slightly removed from pure thought-stuff. He builds his castles in the air, from air, creating by exertion of the imagination. Few media of creation are so flexible, so easy to polish and rework, so readily capable of realizing grand conceptual structures.\” — Fred Brooks
\”My message to the serious programmer is this: spend a part of your working day examining and refining your own methods. Even though programmers are always struggling to meet some future or past deadline, methodological abstraction is a wise long-term investment.\” — Robert W. Floyd