Readplace

Why You Should Forget the Tooling and Refactor Code Manually

fagnerbrack.com 5 min read
View original
  • current
Summary (TL;DR)
Manual code refactoring builds deeper system understanding and knowledge retention through deliberate practice, unlike over-reliance on automated tools. While tools help with speed, they reduce engagement and memory formation. The approach pays off long-term in complex codebases, making code itself the documentation. Caveats include diminishing returns for well-designed systems and upfront time investment.

In the world of software development, tools are everywhere. There are automated tools for testing, deployment, and refactoring. But what if I told you that relying too much on tools can be counterproductive? What if I told you that manually refactoring your code can lead to a better understanding of the system without endless documentation? In this post, we explore why you should forget the tooling and refactor code manually.

First, let’s define what we mean by “tooling.” In this context, we refer to automated tools that assist developers in refactoring code. These tools are useful for identifying code smells, finding duplicate code, and making small changes to improve code quality.

Examples are:

  • Github Copilot as a refactoring assistant
  • Changing automatic imports
  • Automatic applying patterns like Extract Method, Extract Class, etc.

Relying too heavily on these tools can lead to a lack of understanding of the system as a whole.

When developers manually refactor code, they become more familiar with the codebase and feel the pain of change. This creates incentives to design systems that are good enough, not too tightly coupled or too loosely coupled. Manual refactoring also forces developers to move code around in small steps. Otherwise, it’s very hard to keep control of the code. This helps developers create a mental model of how the system works.

Psychological science supports this approach to learning (see references). When we manually perform a task, we engage in deliberate practice, repeating small, specific steps to improve performance. By manually refactoring code, developers also engage in deliberate practice with their code, leading to a deeper understanding of the system.

There's also the role of dopamine in learning and motivation (see references), specifically its role in reward-based learning. Dopamine modulates synaptic plasticity, which has an impact on memory formation. When we refactor code manually, we achieve small goals that reinforce memory.

The role of manual refactoring improves codebase knowledge retention

However, relying on manual refactoring can also be counterproductive if you apply it blindly. It's important to understand the goal; for example, a huge and complex codebase that is not very well designed may take a long time to give a return on investment. If you want short-term gains, this is a practice that wouldn't work. However, if you're looking for sustainable development and long-term gains in a complex codebase, relying on manual refactoring creates the right incentives without a significant impact on new developers coming to the team.

Remember: Software Development is cognitive work, not menial typing work. So typing faster or writing code faster won't create better outcomes.

Another example is if the architecture is already well thought out, small services which have high cohesion and low coupling, then it becomes less necessary to go through the typing experience as you reach diminishing returns when new devs coming to the teams can understand it well enough and most of the work is in infrastructure. However, my experience with dozens of companies, from small to huge, shows that 99.99% of the software does not live in an ideal state despite all the "Engineering @ BigOrg" blog posts stating otherwise.

The reason why teams have developers with a high Bus Factor is due to the lack of practices that allow developers to retain knowledge deeper and faster in the things that matter. This is one part of it, and of course, it's an investment like anything else with a ramp up curve.

Relying on manual refactoring can also be counterproductive

Q: Do you mean copy/pasting instead and pasting the wrong or unnecessary things?

No, see also Don’t copy-paste code. Type it out. Besides, if you copy/paste the wrong path of a file import, you should have tests (or static analysis) that can spot that before it goes to production.

Q: Have you seen this work somewhere before?

Yes, while I was at MYOB, a multi-billion dollar company with more than 2000 employees, one team became the best team from the company in my tenure, also using this practice with my oversight.

Q: I don’t see the point; what if you can type without purpose too?

The act of reading, in this case, using a tool to tell you what's gonna happen, has lower retention than purposeful experience. Just typing doesn’t mean anything by itself, obviously; that's why it has to be tied to a goal. Otherwise, there’s no dopamine generated for the task. You can do the same reading and generate no dopamine if your reading doesn't have a goal; having a purposeful goal is necessary for retention regardless of what you're doing, and that is outside the scope of this post.

For example, say you use a boilerplate tool like NodeJS npm --init to create a default package.json without understanding what each property of a generatedpackage.json means. That creates the same effect as using refactoring tools. Even though you have a purpose (to initialise an application), it has lower retention of knowledge than creating your own package.json at least a few times until you don't have to do it anymore. Using this technique, I learned to always start package.json as { "private": true } for non Open Source and non-publishable projects, then evolve from there.

If everyone on a team engages in this approach to refactoring, everyone will eventually have a similarly deep understanding of the system's design. This eliminates the need for endless documentation that never quite matches the code. Instead, the code itself becomes the documentation.

While automated refactoring tools can be helpful, developers should not rely on them too heavily. Manual refactoring leads to a deeper understanding of the system and helps to create a mental model of how the code works. Developers can improve their skills and create better code by engaging in deliberate practice.

This is one of those things that are counter-intuitive. I do that all the time, but nobody else does it or believes it to have any benefits, and the results can be insane!

Forget the tooling; in order to be truly productive long-term, it's time to start manually refactoring your code (or preferably somebody else's) until you don't have to.

See also Don’t copy-paste code. Type it out.

References

Murphy-Hill, E., & Parnin, C. (2011). How We Refactor, and How We Know It. IEEE Transactions on Software Engineering, 38(1), 5–18. doi: 10.1109/TSE.2011.2

Kataoka, Y., Ernst, M. D., & Griswold, W. G. (2001). Automated Support for Program Refactoring Using Invariants. IEEE International Conference on Software Maintenance (ICSM), 736–743. doi: 10.1109/ICSM.2001.972780

Ericsson, K. A., Krampe, R. T., & Tesch-Römer, C. (1993). The Role of Deliberate Practice in the Acquisition of Expert Performance. Psychological Review, 100(3), 363–406. doi: 10.1037/0033–295X.100.3.363

Forward, A., & Lethbridge, T. C. (2002). The relevance of software documentation, tools and technologies: a survey. Proceedings of the 2002 ACM Symposium on Document Engineering, 26–33. doi: 10.1145/585058.585067

Lisman, J. E., & Grace, A. A. (2005). The Hippocampal-VTA Loop: Controlling the Entry of Information into Long-Term Memory. Neuron, 46(5), 703–713. doi: 10.1016/j.neuron.2005.05.002

Shohamy, D., & Adcock, R. A. (2010). Dopamine and adaptive memory. Trends in Cognitive Sciences, 14(10), 464–472. doi: 10.1016/j.tics.2010.08.002

Kempadoo, K. A., Mosharov, E. V., Choi, S. J., Sulzer, D., & Kandel, E. R. (2016). Dopamine release from the locus coeruleus to the dorsal hippocampus promotes spatial learning and memory. Proceedings of the National Academy of Sciences, 113(51), 14835–14840. doi: 10.1073/pnas.1616515114