Home

Notes to self from a forgetful web developer

Move Slowly and Make Things

The current “vibe code an MVP in a day” trend is toxic bullshit. Good software takes time, but it’s worth the effort.

PhoenixTest foot guns

Notes to self about the unexpected quirks of the otherwise excellent PhoenixTest library.

Weeknotes: Claude

The post-covid slump is real. On the plus side I became a thought leader, and shipped the best thing I will ever work on.

The Complexity Halo

After working on a complex problem, I tend to over-complicate the next one. I call this “The Complexity Halo”.

Weeknotes: Covid, mostly

After three years of cheating death, I finally caught Covid. That said, the week hasn’t been a complete write-off.

Organising Ecto schemas

Placing schemas inside contexts makes it difficult to distinguish between data and actions. Let’s fix that.

Naming Phoenix context functions

In a Phoenix context we frequently need to create and update resources, using changesets. Each operation requires a function to get the changeset, and a function to perform the operation.

If the operation in question is “create”, the standard approach is to name the changeset function create_changeset, but that’s just confusing; am I getting a changeset for the “create” operation, or am I creating a generic changeset?

I prefer the following conventions.

How to bypass the Git pre-push hook

Git’s pre-push hooks are a great way to enforce code quality. They’re also a pain when you just want to push some in-progress work to a branch.

Building Happy Stack: naming things

Going into this project, I assumed the biggest problem would be how to retrieve the status of each product. As ever, the real problem was naming things.

Building Happy Stack: notifications

The plan has always been to launch Happy Stack with email notifications; that’s it. I have lots of other notification channels planned of course, but email is still the most universal.

Simplify imports with path mapping

Your TypeScript application uses relative imports. Every time you move a file, the imports break. You’ve configured path mapping, and now things break in an entirely new way.

Check a date with Chai

You need to check a date using Chai. You also need to account for the test execution time.

Verify a checksum

You need to validate the checksum of a file. Rather than Googling it for the hundredth time, you decide to write a note to self, in the hope it will stick this time.

Using TypeScript with Gatsby

Gatsby supports TypeScript out-the-box. Unfortunately, the official solution has several shortcomings which limit its usefulness. There is a better way.

Nested optional chaining in JavaScript

You can use JavaScript’s optional chaining operator multiple times within a statement. That is useful if you need to access nested properties which may not exist.

Custom Decap CMS previews in Gatsby

You’re using Decap CMS to manage the content of your Gatsby-powered website. The default Decap CMS preview displays every field, including metadata. That probably isn’t what you want.

Undocumented Node Migrate options

Migrate is a very useful migration framework for Node. Unfortunately, its documentation is lacking in places. Here are a couple of handy options not covered by the README.

Custom TypeScript type guards

Type guards let you provide TypeScript with more information about a variable’s type. Type guards are especially useful when working with a variable which may be undefined.

Chai assert error

Chai lets you assert that some code throws an error. The key is to pass the test subject to expect, not the test result.

Selectively disable ESLint

You can disable ESLint for a single line, a block of code, or an entire file. Wherever possible, specify the rule or rules you wish to disable.

Visual Studio Code "actions" shortcut

VS Code has the concept of code actions. When a code action is available, VS Code displays a small lightbulb icon nearby 💡. Clicking on the lightbulb displays a list of available actions.

Transform objects in a Node stream

By default, a Node.js stream expects to operate on a Buffer or a Uint8Array. We can override this by telling the stream to use “object mode”.

Sort the output of git status

When working on a codebase with a lot of unstaged or untracked changes, the default git status output is unhelpful.

Clear the terminal in macOS

Typing clear at a command prompt clears the screen. Simple enough. Unfortunately, it’s no help if you’re inside a REPL, or a long-running process.

Avoid Array.prototype.push

Avoid Array.prototype.push. It modifies the array in place, which is asking for trouble. It also returns the array length, not the modified array, which is plain confusing.

Sane URL validation in JSON Schema

JSON Schema is a very useful tool for validating API requests. Sadly it’s not immune to the quagmire of pedantry that is URL validation.

Generate an array of random data

Faker is a useful JavaScript library for generating dummy data. For example, faker.random.words(3) generates a string containing three random words. But what if you want to generate an array of random words?

Split a JavaScript array into chunks

There are countless blog posts detailing how to split a JavaScript array into chunks. However, many of these solutions fail with arrays containing hundreds of thousands of items.

Generate a pseudo-random boolean

You can generate a pseudo-random boolean in JavaScript or PHP using a single line of code. No, you do not need to install yet another idiotic JavaScript package.

Node -r

The Node CLI supports a “require” flag, which allows you to preload a module when running Node.

Node type definitions

Node isn’t managed by npm. As such, there’s no guarantee that running npm install -D @types/node will install the correct type definitions for your version of Node.

TypeScript "Generics"

Use ‘generics’ when writing a TypeScript function which works with a variety of types.

GitLab CI and YAML booleans

GitLab’s opinion of what constitutes valid YAML differs from the official spec. Booleans, in particular, are problematic.

Randomise your tests

A quick and easy way to ensure your tests don’t have any unwanted side-effects is to randomise the order in which they run. PHPUnit makes this easy.

Laravel route collection binding

Route model binding is a very useful Laravel feature. But what happens if you have an endpoint which needs to support multiple, comma-delimited IDs? Route collection binding to the rescue.

Laravel's mysteriously macroable paginators

I recently found myself with the need to add a custom method to the LengthAwarePaginator class. Not a problem, I thought, I’ll write a quick macro. A fine solution, but for the fact the LengthAwarePaginator isn’t “macroable”. Or so it would seem at first glance.

Eager loading Eloquent properties

A simple, performant solution for eager-loading Eloquent relationships, when you’re only interested in one or two specific properties on the related model.

Cleaner code with Eloquent events

Eloquent makes it easy to respond to certain key points in the lifecycle of a model instance, by exposing events such as creating, updating, and deleted.

Eloquent attributes and database defaults

There’s an important gotcha to remember when working with Eloquent and default database values: if you create a new model instance, without overriding a default value, the attribute will not be set.