The Developer View
This document addresses the way Relevance works from the perspective of a dev team. A developer might be someone who writes code, or it may be an interaction designer, a graphic designer, or a tester.
In essence, this document describes the things that happen in between the daily standup meetings. Put another way, the developer view addresses those activities that don’t usually require direct involvement by customers and other product owners.
That means that developers must have a clear understanding of the larger context in which these activities exist: the product owner view. At Relevance, developers and customers collaborate closely. We follow the process described in that document; this one is mostly about the practices and standards that developers adhere to.
Tools and Practices
Tools don't solve your problems; people do. Tools just help. It's crucial to choose the right practices to go with your tools—the right ways to use your tools so that they do the most for you.
In retrospectives, and day-to-day as required, we discuss how our tools are helping or hurting us, and how they can work better for us. When things aren't working quite right, we'll first try to look at the way we're using the tool; changing our practices is less costly and disruptive than changing a tool. But we occasionally change the tool as well.
This may sound like a lot of talking and navel-gazing, but our other practices help to keep it lightweight and efficient. Pairs work out a good way of using a new tool together, and then propagate that knowledge to the rest of the team as they regroup to pair with others, during daily standups and retrospectives. There are also plenty of opportunities to communicate the practice to other project teams within Relevance.
Pair programming involves two programmers at a single computer (each with a monitor and keyboard of their own) working together on a single task. As a unit, they maintain a high level of quality as well as ensuring a broad perspective on problem solving. For many complex problems and architectural design exercises, pair programming can drastically reduce defects and helps ensure that code requires a minimum amount of rewriting. (See the studies referenced at the Wikipedia entry on Pair Programming.)
When used effectively, pair programming reduces the overall cost of development and maintenance.
At Relevance, we look at each coding and design task to determine if pairing is appropriate, and at the discretion of the project team, many coding tasks are performed solo. When this happens, the code must pass code review before moving on to QA.
Test- and Behavior-Driven Development
At Relevance, we write most of our code in a "test-driven" style. Test-driven development (TDD) means that, before writing a bit of production code that's required, we'll first write an automated test that verifies that the code is working correctly. (Which, of course, it isn't at the time we write the test, so the test fails.) Then we'll go write the code that's necessary to make the test pass. Lather, rinse, repeat. That whole cycle often takes a minute or less.
TDD is incredibly valuable, for numerous reasons:
- As one of our employees has pointed out, it's possible to fail even if your test suite seems thorough. TDD is the best way we know to ensure that our tests actually cover all the requirements, not just all the code.
- TDD helps us focus on what the code needs to do, so we avoid the trap of building "nice to have" features that the customer doesn't value.
- TDD radically shortens the feedback loop between making a coding error and discovering that error. Debugging time is slashed by a huge amount. (Even if all of the other benefits did not exist, this alone would pay for the time spent writing tests.)
- The tests—especially if they're written in a very expressive style (see below)—serve as technical documentation about how the code should work. This documentation is very useful to future developers who might take up maintenance or further development on the project. And continuous integration ensures that these tests always reflect the actual state of the system, which makes them far superior to a prose design document. (We've never seen one of those that wasn't very outdated.)
- Code that's really well designed is also very testable. In fact, one of the best ways to ensure that your code is well designed is to require that it be testable. (And the best way to do that is to build it through testing!) TDD helps us deliver code that has high cohesion and loose coupling, and a lot of other qualities that may seem like just so much programmery jargon, but which do make the systems less costly to maintain and enhance over time.
"Behavior-Driven Development" (BDD) is a style of test-driven development that focuses on a different way of expressing the tests, using different terminology that emphasizes the test's role as specification. For example, in BDD, we try to refrain from calling them "tests," even though testing remains a big part of the purpose. Instead, they are called "specs" or "examples," to remind us of the many ways they support the project beyond just "testing."
BDD doesn't add anything new technically beyond TDD; rather, it's a shift in focus, seeing the specs not merely as error-detection tools but as communication tools, helping us to better understand requirements (and to know that we’ve understood them) and to record them in a useful way. BDD also emphasizes specs that focus on externally visible behavior of the system or component, rather than focusing on internal state. That shift highlights the role of BDD as a design tool.
The tools we use for BDD mean that, in many cases, specs and examples can be read and understood by customer representatives, including domain experts (perhaps with a little help from us). That's really valuable, especially when the requirements are tricky, because it helps us to be clear and precise, and to know that we're really building what our customers want.
Although many tasks call for pair programming, it is simply not practical or efficient to pair on every task. When tasks are tackled by a solo developer, their work must always be reviewed by another developer on the team.
Our task management system is configured to give a task two paths out of development. One path is "Completed by Pair, Ready for QA." Tasks handled by a solo developer follow an alternate path: "Completed Solo, Ready for Code Review." That allows us to ensure that every piece of code that gets delivered to our customers has been seen by at least two developers. Of course, that doesn't mean there aren't any problems, but it does shorten an important feedback loop, catching most problems early, when fixing them is cheapest.
Continuous Integration (CI) ensures that an application is healthy by making sure that automated tests pass on a neutral machine (not just a specific developer's machine). Whenever anybody commits code to the central code repository, a CI server downloads the code and runs the build to ensure that the tests are still passing. If any of the automated tests fail, the build fails and the entire team is notified.
We believe that the build should always be passing, and if somebody breaks the build, it needs to be fixed immediately before further development can take place.
Code coverage has many definitions, but it normally represents the percentage of lines (or branches) in a software project that are covered by automated tests. A low score probably means bad code, but a high score doesn't necessarily mean good code!
At the start of a project, the project team can choose what code coverage standard to enforce. There are many subtleties here: see our series of essays and conference talks for a more in-depth discussion of using code coverage on projects.
Distributed Source Control
Relevance uses a distributed source control system called Git (and Github) to store and track source code and other development-related artifacts.
This is more important than it may first appear. Traditional, centralized source control systems inhibit agile teams by making branch and merge operations expensive. This makes parallel, distributed development more costly. So much so, in fact, that source control can become a bottleneck on overall productivity of the team.
As mentioned elsewhere, technology can't solve social problems. But that doesn't mean they can't help. Git's open, distributed nature has numerous benefits for the way we work:
- It eliminates many barriers to productivity
- It allows us to work with all of our usual tools even when we have to be off the network (such as when traveling)
- It makes it safe to experiment, allowing us to simply try interesting ideas rather than wasting time in analysis paralysis
This excellent talk from Google's I/O conference in 2009, The Myth of the Genius Programmer , is a great introduction to the ways tools (and the way they're configured) can encourage or discourage certain types of behavior within and between software teams.
Fridays (20% Time)
On Fridays at Relevance, we don't do billable client work. We spend our time on things that improve our lives as developers, our own pet projects or passions, and for giving back to the rich sea of open-source software (especially development tools) in which our business swims and thrives.