There is an inherent difficulty to maintaining velocity. We always want things more streamlined, more efficient, more lean - sky is the limit, really, if the technical realm of a product is not micromanaged by the higher echelons of the company management, but is upgraded by grassroots effort. A good team of engineers worth their salt will, as wel know, improve and clean the product of must of technical debt - all a good manager has to do, really, is not to impede that process.

There is an inherent difficulty to maintaining velocity. We always want things more streamlined, more efficient, more lean - sky is the limit, really, if the technical realm of a product is not micromanaged by the higher echelons of the company management, but is upgraded by grassroots effort. A good team of engineers worth their salt will, as wel know, improve and clean the product of must of technical debt - all a good manager has to do, really, is not to impede that process.

There is an inherent concern however, which becomes especially important when the teams are small. Sometimes, velocity and efficiency need to be sacrificed a little if the team wants to preserve equilibrium in human relationships.

Joe is an extremely talented developer, and he wants to completely rewrite module Foo that Jane has set up a year ago. He knows for a fact that Foo is easily the worst part of the product at this moment, and is up for a complete overhaul. He was not on the team when Jane wrote it, however. Jane, in turn, is just wrapping up a tedious feature involving lots of underspecced UI with subtle, unspecified behaviours. She vaguely remembers that Foo was a bit of a pain in the butt to build initially, but now it is firmly out of her system because she literally left it behind two years ago. Now Joe has a few choices:

  • He can bring it up with the team that Foo is no longer up to snuff, and spin up a refactoring effort.
  • He can just go in and rewrite Foo on his own when he has a minute between his usual responsibilities, without consulting with anyone.
  • He can walk up to Jane, pull her out of her flow, and ask her as to why Foo has every code smell known to mankind and about 5x less tests than it is supposed to, given it’s importance in the product’ architecture.
  • He can go and rewrite Foo, and put it up for review for Jane, so that she can confirm his findings about possible improvements on Foo were accurate.

Looking at this from a practical perspective, none of these are ideal - and it’s actually funny to examine why.

Take 1, and Joe increases the bandwidth for the entire group, because he puts up a component for reconsideration that has been long laying dormant. Everyone on the team has something else to do at this point, and engineering bandwidth is limited. Now he needs to convince the group that his reimplementation of Foo will be better, or – at the very least – force the team to think with him whether the proposed improvements are worth the time to be expended.

Take 2, and Joe might end up being that jackass who commits a 2K-lines formatting diff just to mark the territory – for some, that is. And what if Jane has her reservations about the very design of Foo that Joe worked so hard to phase out? What if Peter actually contributed decisions during the design of Foo and those decisions are not in code and were not paper-trailed (and Peter was the sole person in ops at the time, so his opinion mattered a lot)? Also, what if the design of Foo was somehow coupled to an external system that is impossible to alter, and the very hairiness of Foo has to do with this external system’s idiosyncrasies?

Take 3, and Jane is pulled out of her flow with the under-specced UI feature she was working on. She now has to go through a massive total recall process to remember what it was that made Foo’s design so horrible in the first place. Was it because she was not familiar with the language? Or Peter mandated an especially cumbersome configuration setup which increased the amount of code twofold? Or was it Ron, the then-CTO, who basically convinced her to do a line-by-line port from JavaScript that was in the previous version of the system? This can easily consume half a day of Jane’s life, that she was planning to spend on more interesting tasks. Or on that UI screen where only 2 buttons have documented behaviour, and the rest is left to the implementer because the designer on the feature is high on fonts and Photoshop but is slacking UX.

Take 4, and Joe picks an approach to redesigning Foo that Jane might not necessarily agree with. Also, he does not know about Peter’s concerns from the day back when, and his new design does not take them into account. He will basically overburden Jane in the same way as taking option 3, but this time his overburdening will be backed by an impelementation he already spent time on. So Jane will not only have to examine his input and ideas and compare them to her previous approach, but she will also have to take into account the simple fact that Joe already spent time on the implementation. Diss that, and his work goes out the window. Do that a notch too much, and Joe gets punished for showing initiative - his half a day of work spent on the foo-refactor is sent back to the drawing board, for “because reasons” he had no way of knowing when he started. And what if Joe uses ReduxFluxyServerSideEventedReactive.js, which will take Jane a week to understand on it’s own, in his redesign of Foo?

This is a very delicate question, which has many sides to it - and it becomes especially sensitive in smaller teams where losing or upsetting even one team member can have dramatic effects on the product or on the team’s morale. It requires everyone in play - Peter, Joe, Jane and their now-CTO - to be aligned on how they approach this dilemma, and only if they all are is there any chance of consensus. Basically, engineers are territorial beasts - and tremendously so. Give a bit too much system to a person for too long, and saddle him with all the possible responsibility that comes with that system - and he is likely to resist any external alterations to it. Do that with multiple systems - and you have a walled garden of stale corners of the product, where any alterations are likely either to upset Jane or disrupt her flow (since she knows the system was not optimal when concieved, but the mental overhead of altering it now is just too much).

If we look at these forks in the road - and in the life of a product there will be a few, certainly - from a purely Rayndian, or “meritocratic”, perspective, the answer is crystal-clear. Joe should just go in and throw the damn thing away. He should replace it with whatever he considers better, back it with good evidence that passes muster when compared to the previous effort, and the team should then move on. However, we can see that doing so can have subtle negative effects on Jane and on Peter. Now they will both feel WWIC, and with every step like this taken by the codebase, this feeling will grow. At a certain point, they will be assuming anything they contribute is up for removal, replacement or forcible deprecation any minute, and their contributions are likely to be less robust and thought out. Also, this will disadvantage them in comparison to new team members - having new, fresh ideas, looking at various Foos from a new perspective (without the burden of legacy requirements that might not be present anymore) always opens doors to radical innovation. But how should Jane cope? Yes, when she wrote Foo 2 years ago her command of FancyLang 0.x was not really her forte, because 3 years of her life just before that job were spent writing GeneratorFactoryBeanAdapterShims in EnterpriseLang.vFOREVER. But should she be overruled this way just because Joe, having worked with FancyLang 3.+ for a few years, sneers at her way of formatting function parameters? Or because he favours inheritance over composition?

So yes, you can take the path of “radical improvement”, but be prepared that some team members (and good ones at that) are going to be pissed off. More over, be prepared that there will be situations where more experienced team members will be at a disadvantage compared to the fresh blood. And be prepared that you are sacrificing their happiness for the sake of “objective efficiency”, whereas in fact, if you look at the product from the 30,000 ft of altitude, the improvement provided by the Foo refactor is not that great at all.

If you go from the path of the grand Dutch “gezelligheid”, or of the general continental-European “common accord”, you are very likely to just postpone the Foo refactor indefinitely. Do that often enough, and there will be a tradition where alterations to existing systems or modules in your product will be actually discouraged - what if you piss on someone’s territory while invading them? What if those interventions will result in Peter having a clinical depression because of feeling undervalued, or overburden Jane to a degree of her going on sabbatical thanks to a burn-out you gave her? So err on that side, and your product will start to stagnate - but the team members are very likely to be happier than they otherwise would be, because their contributions are valued and long-lived, even though they might be suboptimal at the time of conception.

The answer is, of course, somewhere in the middle. Managing humans is a complex endeavour, and the path to hell is always paved with good intentions as we know. There is, however, an approach that has to do with cultivating behaviour as opposed to solving the conflict. That is, a good manager will actually predict that with the age of the product these situations are about to develop eventually, and will try to prepare his team for dealing with it in the best way that befits that particular team.

I do, of course, have my ideas about the actual solutions – but I will reserve them for the subsequent posts. Curious if my readers have their ideas on this as well, share them in the comments.