Practical vim macro Codemods (draft)

I tend to edit text with vim. In this post I’ll share some of the more advanced capabilities and demonstrate automation of large and boring modifications to codebases.

To illusrate the part of how I work I will demonstrate the following codegen/codemods: - Retroactive lint rule introduction. - Scripting initial test coverage of a React component tree.

Retroactive Linting

A concrete example of needing to make a large and boring code modification is when you introduce linting into an existing codebase. Whether or not this is a good idea depends on several factors and it’s not without downsides.

Protip!!: be very cautious about using your personal git authorship. I learned this the hard way.There were many times at a previous engagement that someone would ask me about a piece of code that I didn’t author but I helped conform to new lint rules.

Tools like revgrep allow you to do so in a more gradual manner but this approach is not without pitfalls. For one you end up with your set of code practices in inconsistent state. Secondly, revgrep’s detection mechanism is fairly naive and moving code around even a bit can break the squelching of existing lint violations.

Generating Tests

Nothing (at least not yet) can replace the quality of a human operator clearly expressing the intent of a piece of software with a well-crafted test.

In some early stage organizations there can be a valid and pragmatic calculus applied to the way systems are built with regard to the amount of automated verification. Once you have something sticking to the wall the true costs of the software start manifesting and the return on investing in testing starts going up.

At a previous company we found ourselves in the situation of having a large, aging, and creaking React codebase that was literally devoid of tests.

Getting a team to adopt better testing practices involves an interesting set of concerns and only some of them are purely technical in nature. For the purposes of this post I only discuss a subset of these concerns.

Development on teams rightfully involves propagating existing patterns. (The alternative of unboundded pattern propagation is an interesting group misbehavior that deserves it’s own discussion). One way to encourage writing of tests is to provide a clear pattern to do so.

Generating a test skeleton for each of our components provided us some very basic assurances of correctness and provided the team with a pattern to propagate further.

These test skeletons accomplished a few specifics for each component: - Ensured the component could render without throwing an error. - Added jest snapshot coverage. - Ensured no console.error calls occurred.

Perhaps more importantly they demonstrated that Foo.js should have a Foo.test.js and be an easy basis for reference when a new component was being born.