snippetjavascriptTip
Using Test-Driven Development to refactor a JavaScript project
Viewed 0 times
javascriptprojectrefactortestdrivendevelopmentusing
Problem
In the past three articles, I've covered kickstarting a project with TDD, designing a user-centric API, and implementing the library with Vite. In this article, I'll refactor the library to make it more maintainable and reduce its bundle size, down from 6.04 kB (1.63 kB gzipped). Let's dive right in!
The main goals of this refactoring are threefold: reduce complexity, make the code less error-prone, and make the code more maintainable. As a health metric, we'll be aiming to keep readability at about the same level. We'll also be leaning quite heavily on the test suite to ensure that we don't break anything.
As I was originally looking to reduce the bundle size, I went with an approach akin to <dnf title="Code golf is a type of recreational computer programming competition in which participants strive to achieve the shortest possible source code that solves a certain problem.">code golf</dnf>, but not quite. I first took a long hard look at what may be redundant or could be simplified, and then I tried to simplify and shorten pieces of logic that seemed like they were doing too much.
_Why code golf?_ Because it's a really fun thing to do. It's been quite a long time since I last did it, and I felt like making something this small even tinier was a fun place to dust off those skills.
Like I said, I started by taking a long hard look at the code. Where could one spot complexity that wasn't really necessary? What patterns could be extracted into abstractions?
The main goals of this refactoring are threefold: reduce complexity, make the code less error-prone, and make the code more maintainable. As a health metric, we'll be aiming to keep readability at about the same level. We'll also be leaning quite heavily on the test suite to ensure that we don't break anything.
As I was originally looking to reduce the bundle size, I went with an approach akin to <dnf title="Code golf is a type of recreational computer programming competition in which participants strive to achieve the shortest possible source code that solves a certain problem.">code golf</dnf>, but not quite. I first took a long hard look at what may be redundant or could be simplified, and then I tried to simplify and shorten pieces of logic that seemed like they were doing too much.
_Why code golf?_ Because it's a really fun thing to do. It's been quite a long time since I last did it, and I felt like making something this small even tinier was a fun place to dust off those skills.
Like I said, I started by taking a long hard look at the code. Where could one spot complexity that wasn't really necessary? What patterns could be extracted into abstractions?
Solution
The `captrue` and `name` options are only ever used in the `toGroup` function to produce the group prefix. And, in all three use cases, we can **calculate the resulting value ahead of time** before calling `toGroup`. Let's refactor it to remove the options object:As I was originally looking to reduce the bundle size, I went with an approach akin to <dnf title="Code golf is a type of recreational computer programming competition in which participants strive to achieve the shortest possible source code that solves a certain problem.">code golf</dnf>, but not quite. I first took a long hard look at what may be redundant or could be simplified, and then I tried to simplify and shorten pieces of logic that seemed like they were doing too much.
_Why code golf?_ Because it's a really fun thing to do. It's been quite a long time since I last did it, and I felt like making something this small even tinier was a fun place to dust off those skills.
Like I said, I started by taking a long hard look at the code. Where could one spot complexity that wasn't really necessary? What patterns could be extracted into abstractions?
> [!NOTE]
>
> Some details of the refactoring process have been omitted for brevity. If you're interested in the full process, you can check out the GitHub repository.
Code Snippets
The `captrue` and `name` options are only ever used in the `toGroup` function to produce the group prefix. And, in all three use cases, we can **calculate the resulting value ahead of time** before calling `toGroup`. Let's refactor it to remove the options object:The same logic can be applied for quantifiers and lookarounds, making all three of the intermediate creation functions pretty simple and similar.Notice how we've replaced the use of `concat` with `nonCaptureGroup` in lookarounds. As the former is basically an alias of the latter, we can make these two functions **look more similar**, which will come in handy later.
Notice also how how we're spreading the raw expressions in both lookarounds and quantifiers. Upon closer inspection of the `toGroup` method, it's clear that the conversion to `Segment`s and the joining is done there, so **no reason to redo it** elsewhere. This will also facilitate the next step.
### Merging functions
At this point, `joinSegments` is only ever called in tandem with `toSegments`. It stands to reason that these two functions, simple as they are, could be **merged into a single function**. Let's do that:Context
From 30-seconds-of-code: tdd-codebase-refactoring
Revisions (0)
No revisions yet.