by Miško Hevery - 19 August 2008
So you've figured out a better way of doing things, but how do you get everyone to change the way they work and start writing code in this better way? This is something I face daily in my line of work as a best practices coach. Here are my battle tested tricks which work even after I leave the project.
What usually happens
Most change initiatives fail because people don't appreciate the reasons for making a change, or because they get demotivated by the progress of change.
Think about what usually happens. Suppose that you have recently discovered the benefits of dependency injection. The dependency injection principle requires that object creation code be kept separate from application logic. Dependencies should be found through the constructor, not by creating them or looking them up in global state. This makes code more maintainable and easier to test, which results in a higher quality codebase. Armed with all of these benefits you decide to implement this on your project. As the lead of the project you give a tech talk to the team on the benefits of dependency injection. Developers come to your tech talk, you all take a vote and everyone agrees that this is what needs to be done. Everybody goes back to their workstations full of enthusiasm. People attempt dependency injection in the first few changes they make and you are excited, but a month later everyone is back to their old routine and nothing has changed. How can we break this cycle and achieve lasting change?
Step 1: Get Buy-In
The first step is to make sure that everyone is willing to try something new. Although buy-in meetings don't achieve anything tangible, they help people understand the reasons why a particular goal is important, and it gives the entire team the opportunity to collectively decide to go after it.
But buy-in isn't enough. We must have some way of knowing that we are implementing the change. To do that, we can use a metric as a proxy for the goal. This makes progress measurable, keeps the goal front-and-center for developers, and lets us know when we're done and can declare victory. Without a metric, we risk losing direction (how do we know our steps are making things better?), losing motivation (individual steps are too small to be seen against the big goal) and losing any sense of accomplishment (because there is always something that can be improved, we don't know when we are done with this round of improvements).
Step 2: Define Your Goal in Terms of an Objective Metric
The next step is to have a measurable number that gives us an indication of where we are and where we want to be. Although it doesn't matter whether this number is an exact measure or just a proxy, it must be repeatable and calculable in an objective way at each stage of adoption.
In Java, byte-code analysis tools are a great way to get to a metric. These can measure very specific things, such as the total number of calls to deprecated APIs or classes/methods which we want to remove. They can also be used to apply hueristic rules to code, such as determining whether it breaks the Law of Demeter, contains excessively deep inheritance, or has too many lines of code in methods or classes. There are a lot of open source tools available that can measure attributes of code, including ASM to write your own metrics, JDepend / Japan for dependency enforcement, Panopticode for code-quality metrics, and Testability Explorer for identifying code that is hard to test.
There are some interesting side-effects to using metrics. One is that people may initially debate the value of making a change, but the moment there's a number - even if it is a home-grown formula - the arguments stop. Another is that management is more likely to support the change: anything that can be measured conveys a sense of visibility and tangible benefit that an unmeasurable change cannot. Again, these are side-effects, not risks: having already established team buy-in in the previous step, we're not "managing to a number" and missing the point of making the change. We're simply bringing attention to the progress we're making toward that change.
In the example above, we want to get developers to do dependency injection. I developed a metric and corresponding open-source tool called Testability Explorer which measures the smallest number of conditions which cannot be mocked out in a test. The idea is that code where branches can be isolated will be easier to test than code where the logic cannot be isolated. In the latter case, one test will exercise a lot of code and will most likely require complex setup, assertions and tear down. The best metrics are those where gaming the metric produces the desirable outcome in the code. In Testability Explorer, using dependency injection is a good way to improve the score, which is exactly what we are trying to achieve.
Checkpoint: Socially Acceptable, but Not Yet Sustainable
Buy-in reinforced with metrics make our proposed change scientific as well as socially acceptable. But this isn't enough: it must be constantly reinforced. Part II of this series will present ways to make progress toward change highly visible yet subtly encouraged with each and every commit.