Blog

Posts on software development, careers, and craft.

I've spent the last week in the UK

I've spent the last week in the UK. Number of pickup trucks I've seen? Nearly zero. So my fellow Americans, if you think you need a truck, ask your...

Ideally, onboarding a new developer on a JS project requires only 2...

Ideally, onboarding a new developer on a JS project requires only 2 commands: npm install npm start If more steps are required, it's often a sign t...

An epidemic problem in software development: Resume driven development

An epidemic problem in software development: Resume driven development. What it looks like: Developers suggest the tech they want to learn, instead...

One of the most wonderful benefits of public speaking: It raises the bar for...

One of the most wonderful benefits of public speaking: It raises the bar for what you consider stressful. After speaking to a crowd, most other soc...

A surprising irony about working in TypeScript: The better I get at it, the...

A surprising irony about working in TypeScript: The better I get at it, the fewer types I declare. I declare types at the “edges” of my app: ✅Endpo...

Code review habit: If a simple change is necessary, I just change it

Code review habit: If a simple change is necessary, I just change it. Examples: - Typo - Grammar issue - Naming issue If I think it’s controversial...

In code reviews, I’m far more impressed with surprising simplicity than...

In code reviews, I’m far more impressed with surprising simplicity than perplexing complexity. Surprising simplicity is hard to achieve. Perplexing...

15 reasons I enjoy @tailwindcss: 1

15 reasons I enjoy @tailwindcss: 1. I don’t have to spend time naming classes. 2. I can jump into a project that uses it immediately. 3. The final...

Problem: Tailwind styles get long sometimes

Problem: Tailwind styles get long sometimes. Solution: Install the Inline Fold extension in VS Code. It auto-collapses classes. Click the 3 dots to...

The big idea of BDD/Cucumber tests: “Write tests in plain English so...

The big idea of BDD/Cucumber tests: “Write tests in plain English so non-devs can help write the tests.” I’ve seen this attempted at many companies...

Concern: “Infinite scroll is bad because the user can’t ever access the...

Concern: “Infinite scroll is bad because the user can’t ever access the footer.” Solution: Implement the infinite scroll in a div that fills only p...

React Router 6.4 introduced loaders

React Router 6.4 introduced loaders. The big idea: Fetch everything for the route in parallel. What I've noticed: In most apps, each route has one...

⚛️ React code smell: Multiple setState calls in a row

⚛️ React code smell: Multiple setState calls in a row. This is often a sign that state should be unified, or that one value value can be derived fr...

Monorepos are great…to a point

Monorepos are great…to a point. Grouping apps that use the same tech in one repo makes a lot of sense. Why? Because they can share a lot of code an...

Employers shooting themselves in the foot: “We don’t allow using GitHub...

Employers shooting themselves in the foot: “We don’t allow using GitHub Copilot because it collects usage data.” If your competitor uses AI, (and t...

I don't write unit tests much anymore

I don't write unit tests much anymore. GitHub Copilot writes them for me. 🔥 Here's how: 1. Create a well-named function. 2. Create a test file. 3....

Pre-commit hooks are a waste of time

Pre-commit hooks are a waste of time. Why? Because they enforce standards too early. Pre-commit hooks make us wait for a quality check on every com...

Just switched a project from plain fetch to ky

Just switched a project from plain fetch to ky. I really like the result. 🔥 ✅ MUCH less code ✅ Treats non-2xx status codes as errors (don't have t...

A common mistake: Types with many optional properties

A common mistake: Types with many optional properties. Why? Because the property is often required in some cases. So, optional properties often red...

Problem: TypeScript errors are typed "unknown"

Problem: TypeScript errors are typed "unknown". Solution: Throw custom errors. They're easy to inspect. How: 1. Create an ErrorBase class. 2. Creat...

Problem: A PR is too big and contains unrelated changes

Problem: A PR is too big and contains unrelated changes. Solution: 1. Prefix the big PR's name with "WIP" so the team knows it's not ready. 2. Open...

A common software development mistake: Going too wide or too narrow

A common software development mistake: Going too wide or too narrow. Too wide = “Let’s partially implement lots of features.” Too narrow = “Let’s f...

Tip: Don't lie to TypeScript

Tip: Don't lie to TypeScript. Lying to #TypeScript reduces type safety. Examples: - Setting "id" to 0 to represent an unsaved record. - Making "id"...

Sometimes, a performance problem is actually a design problem

Sometimes, a performance problem is actually a design problem. Example: “How do we fix the performance of this page showing 1,000+ items?" Answer:...

Problem: You want to send messages between tabs, frames, iframes, or windows

Problem: You want to send messages between tabs, frames, iframes, or windows. Solution: The Broadcast channel API. More: https://developer.mozilla....

Client-side security boils down to one simple rule: Never trust the client

Client-side security boils down to one simple rule: Never trust the client. Assume the user will: - Manipulate the URL - Call endpoints directly wi...

Problem: "Our code isn't testable"

Problem: "Our code isn't testable". The path forward: 1. Write tests for new code. 2. Write end-to-end tests for the "happy path". Start 1 and 2 to...

A simple, but often overlooked benefit of using the URL for state: It avoids...

A simple, but often overlooked benefit of using the URL for state: It avoids having to pass around state. Why? The URL is global. It's easily reada...

Vercel converted the BBC site to @nextjs

Vercel converted the BBC site to @nextjs. The results: ✅ Removed 20,000+ lines of code ✅ Removed 30+ dependencies (React Router, Express, Babel...)...

Problem: You want to re-throw a JavaScript error to provide a friendlier error

Problem: You want to re-throw a JavaScript error to provide a friendlier error. But, you want to keep the original error details too. Solution: Inc...