Have you ever wanted to change the behaviour of some code that you work with?
I’d like to share a few techniques with you which have helped me to change the behaviour of code I’ve worked with, with little conflict, great success and positivity all round.
Imagine a legacy system who’s been in the job for five years and can’t see the problem with the way he’s behaving.
Imagine a class, who feels like he needs to be in control of everything, and can’t delegate or trust other classes effectively.
Imagine a service who’s overloaded, doing the job of three systems that no longer perform, but who just heroically accepts all the work and never gives any signal that he’s about to collapse, again.
We’ve successfully used these techniques to help change our codebases, our systems, our services and our applications, as well as our people. I’d like to share these simple guidelines with you, based on the sandwich model of feedback. They are:
- Start with the right mindset
- Anchor the things you value
- Avoid labelling
- Use examples
- Work towards a bright future
Start with the right mindset
There are lots of reasons we might want to change our code. I’ve seen people work on code
- because it doesn’t work the way they expect
- because it’s annoying
- because it refuses to do something which isn’t really its responsibility anyway
- because it’s fun and powerful to change code for the sake of it.
I believe there are only two good reasons to change code:
- to reinforce valuable behaviour
- to increase effectiveness.
If you’re not approaching the code with one of these mindsets, stop. How much do you actually respect the code you’re about to change? All code wants to do a job effectively. No code is perfect. Remember that time when you first wrote code? You were learning, too. If you can approach the code with an appreciation for the things it does right, and build on those, you’re more likely to meet with success.
As an example, I had one piece of code which controlled the registration of pets. I really wanted this controller to broadcast a pet’s registration, but instead of communicating asynchronously, the controller was insisting that anyone who requested the registration of a pet hung around, waiting.
Unfortunately, one of the people who requested pets was the GUI – an extremely important class with a lot of other jobs to do too!
The code was frustrating me so much that I almost got rid of him and replaced him with someone else… but then I thought about it, and remembered that apart from the fractious relationship with the GUI, the controller had been doing his job very well. This respect allowed me to change the code’s behaviour instead of replacing him.
Anchor the things you value
Once you have an appreciation of the value of the code, make sure that the code knows. Reinforce this. You can call out specific examples of times when the code has done something well.
I told my controller, “I appreciate the way in which you register pets, and the effective way you delegate to the repository. Here’s an example. I registered a new rabbit, Snowy. I gave you his date of birth, his breeder and the price I wanted to sell him for, and when I verified it with the repository later, I found that you’d passed the information on very well. This is important to me.”
I tend to use a unit testing framework and perhaps a mocking framework – Moq or mockito – to translate these conversations into the language of the code.
If it’s a whole team whose behaviour needs to change – perhaps an entire application – then I’ll get everyone together and talk about the things that the entire team does well. For this, I use scenarios, either with a DSL or perhaps a BDD tool like Cucumber or JBehave. Automation tools like Selenium or WiPFlash let me translate my feedback into something they understand.
By anchoring the things that I value about the code, I can change his behaviour without fear that he’ll change in ways I didn’t want.
There are two types of labelling that I avoid: negative labelling, and pattern labelling.
Once I was judging a competition in which a coder announced, “This code is shite!” I marked him down. “If any other coder hears you say that,” I said, “they’ll either be wary of trying things out or they’ll think it’s OK to start devaluing other people’s code.” Labelling code negatively this way can be damaging for everyone involved in the project, even if you wrote the code.
Labelling code by calling out the patterns of behaviour can also be unhelpful. I remember one piece of code that got labelled as a “Visitor”. From then on, everyone expected it to behave like a Visitor. No matter what its responsibilities were, coders expected it to behave like a Visitor and encouraged the other classes in the system to do the same. Eventually the code broke down so badly that we had to replace it altogether, just because we had labelled it badly.
We already looked at how to use examples to anchor positive behaviour instead. We can also use examples to show the difference between the behaviour we want, and the behaviour we’ve actually got.
“Instead of watching the repository,” I told the controller, “I’d like you to listen to him instead, and trust him more. Sometimes the repository might give you a message. I’d like you to respond to that message.”
“How long do you want me to wait after saving to get the message?” the controller asked.
“Don’t wait,” I told him. “Instead of telling me it’s done, I’d like you to tell me that it’s in progress instead. Let’s say that the repository takes five seconds. You should be responding in less than one.” We used some threading to illustrate how the repository might go off and do its job, and talked through the example in the controller’s language until I knew he understood.
Work towards a bright future
By focusing on the positive goals we want to achieve, we don’t really have to spend much time on the negative aspects of the code’s behaviour. After the examples were written, the controller was able to verify that his behaviour had changed appropriately, with as little fuss as possible. What’s more, he had those tests available to him going forward, so his behaviour stayed fixed. Eventually he became one of the most important classes in the system, responsible for the interactions of ten other imports – but he never got overwhelmed by the job.
We can do the same thing with scenarios and applications, or performance tests and whole systems. The ability to define the behaviour we want is one of the most important aspects of changing the behaviour which exists.