Skip to main content

2025 Spring SWE Reading Summary

· 6 min read

SWE Reading

I started catching up on some reading in the first quarter of 2025, likely fueled by some New Year’s resolution motivation. Reading books—mostly on software engineering and mostly from O’Reilly—has been more enjoyable than I expected. While it does sometimes take extra effort to find quiet moments to focus and work through a few chapters, I usually feel that it’s time well spent.

Of course, not all books (or chapters) hit the mark—some are too shallow, some hard to follow, and some more intense than expected, requiring re-reading or hands-on coding to fully grasp the concepts. I finished about three books this spring, with a fourth one paused due to some work and life distractions. I’m jotting down the reviews I had after reading them here—not only to leave a short record for my future self, but also to motivate myself to finish a few more books I have in mind for the upcoming quarter.

One tangential thought about reading: audiobooks help lower the barrier to getting started—they remove some of the stress or friction of grabbing a physical book (or ebook reader) and sitting down. That said, my mind tends to drift when I consume books (especially software engineering ones) in audio form. I do enjoy audiobooks, but I’ve learned I need to be cautious with them. Case in point: I’ve listened to Clean Code by Robert C. Martin a few times while commuting, running, or biking. Unsurprisingly, the book itself warns that you need to concentrate and work through the examples to really benefit from it—and going against the advice- it’s one of the books I remember the least.

Okay, let’s get to the books I read.

Go Race Detector Observations

· 3 min read

Go has a built-in race detector, succinctly described in this Go article. I’ve found it to be a useful tool—often the first thing I reach for when debugging flaky Go tests and suspecting a race condition. I thought I’d write down some lessons I’ve learned from using this tool.

1. Go Race Detector is Your Friend

Running tests in Go with the -race flag enables race detection and can give you an immediate first-pass indication of whether the test has a race condition. Since the race detector works by "instrumenting/monitoring" the code during execution, the slower runtime compared to running the same test without the flag can actually be beneficial.

The cost of race detection varies by program, but for a typical program, memory usage may increase by 5-10x and execution time by 2-20x.

This slowdown can make elusive race conditions more reproducible. Once you learn how to interpret the race detector’s logs, you’ll have a direct way to identify which code paths need further scrutiny. An example of how to interpret race detector logs can be found in my other post.

  • In essence: find the concurrent read and write that are causing the race.

Using the race detector can give you a quick indicator of potential race conditions.

2. No Race Condition Detected != No Race Condition

The race detector only finds races that happen at runtime, so it can't find races in code paths that are not executed.

Because the race detector works at runtime, it can only detect race conditions that are actually triggered during execution. This is important to acknowledge, as some race conditions are highly timing-sensitive—they may only occur when certain operations are unusually fast or slow.

3. Race Detected != Production Code is Impacted by Race Condition

Again, since the race detector works at runtime, detecting a race condition doesn’t necessarily mean your production code is affected. Sometimes, the test code itself introduces the race.

For example, tests often try to hit edge scenarios, which can unintentionally induce race conditions. This is common in tests that use multiple goroutines to test concurrent functions, especially when results are not communicated through channels. Another pattern I’ve seen is tests that intentionally misuse the function under test—such as skipping synchronization or blocking on a done channel—to simulate concurrent processes.

One telltale sign is tweaking sleep durations across goroutines to simulate the handling of many incoming requests.

Lastly, test code is often less strict with shared variables (e.g., using a map or counter without a lock) and then making assertions across goroutines. This can introduce race conditions that the race detector rightly reports—but in such cases, it's more a sign of poorly written tests than a serious concern about the production code.

4. AI Can't Reliably Help You Detect Race Conditions

On the topic of race detection, I want to share a quick note: after experimenting with AI tools (specifically GitHub Copilot) to analyze and detect race conditions, I’ve concluded that while they can be helpful, they aren’t 100% reliable. AI can sometimes spot issues and suggest fixes, but it may also miss certain race conditions or propose solutions that aren’t suitable.

That said, this is just my observation as of April 2025—AI is improving fast, and I hope it proves me wrong soon...

A Vue Component Library Template With TypeScript and Vite

· 3 min read

I wanted to create a Vue component library, and it took me a few tries to get it right. The particular difficulty lies in how TypeScript and Vite need to be configured to generate the correct files for publishing.

This article serves as a note for me to understand how to set up a Vue component project, and it is up-to-date as of 2025. The sample component starter is available at GitHub, which can be used as a template, forked, or cloned for a quick start.

Below, I outline some critical points that I briefly walked through in the README of the starter template, which are the trickiest aspects to get right.

The Build Process

Usually, we distribute JavaScript files along with type declarations so that the component can be used in TypeScript projects seamlessly. While Vite can generate both JavaScript files from TypeScript and type declarations (via a Vite plugin), we can separate the workload between Vite and the TypeScript compiler. The split is as follows:

  • vue-tsc (a wrapper around tsc) will generate type declaration files.
  • Vite will emit JavaScript (and other) files.

vite.config.ts

In vite.config.ts, we need to configure the build settings to ensure the project is ready as a library. This includes defining the entry point and specifying the file name of the final JavaScript output.

One issue I got stuck on was the build process: Vite automatically deletes the entire dist folder, which removes the type declaration files generated by vue-tsc. This can be avoided by setting emptyOutDir: false. With this, we must configure the build script to clean the folder at the start of every build manually. Another approach is to adjust the procedure—run vite build first, then generate the type files.

package.json

Several fields are important for configuring the project:

  • exports: Defines how your component can be consumed, including specifying where the type information is located.
  • build: If we need to manually empty the output directory, we may use something like:
    "build": "rm -rf dist && vue-tsc -b && vite build"
  • files: Controls which files are uploaded to the npm registry.

tsconfig.app.json

In a project scaffolded from Vite's vue-ts template, it separated TypeScript settings into different JSON files. The most important one is tsconfig.app.json, which controls TypeScript compilation for the Vue component.

{
"noEmit": false, // This is important to generate type declaration files
"emitDeclarationOnly": true, // Prevents the generation of JS files, which is handled by Vite
"declaration": true,
"declarationMap": true,
"outDir": "dist/types",
"rootDir": "src"
}

I struggled for a long time trying to figure out why no type declarations were generated despite tweaking the configuration. It turns out that the noEmit field is set to true in the default extended config of the vue-ts template. We need to allow emitting type files by setting noEmit to false, and to avoid generating JavaScript files (which is handled by Vite), we set emitDeclarationOnly to true. Other fields above are also important.

Summary

All configurations must be set up correctly to make the package ready for publishing. The full sequence from scaffolding to publishing is included in the README.md if you want to follow along.