Home

Notes to self from a forgetful web developer

Make it right

An LLM can write code that runs. Making it right and fast is still up to you.

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.

Interviewed by Snipcart

I was interviewed by Snipcart for their ‘Geek Talk’ series, covering my favourite superhero and some developer-type topics.

The hidden perils of Craft plugin service classes

A guide to keeping Craft plugin service classes lean and testable, by keeping code in utility classes and treating service classes as nothing more than a way of defining your plugin’s public API.

Design principles

A set of design principles discovered while sorting through old files, surprisingly applicable to both design and development work.

Introducing Craft Cookbook

Craft Cookbook is a new site providing quick solutions to common Craft problems, following the O’Reilly cookbook format.

Git push your universe

We relaunched the Experience website, moving away from ExpressionEngine to Kirby, a flat-file CMS that supports our Markdown-based writing workflow.

EllisLab bemoans paltry 900% ROI

EllisLab ended its affiliate program, claiming it wasn’t effective. This is a strange decision given the 900% ROI it provides.

Updates and oddments: August 2012

Updates and oddments for August 2012, covering add-on updates including Campaigner, Crumbly, OmniLog, SmartDown, Testee, and GitHub-only oddments.

Testee 2.2

Yesterday I released Testee 2.2, which contains a significant new feature: automated testing.

Introducing Template OmniLogger

Michael Rog has released Template OmniLogger, a plugin that makes it possible to create OmniLog entries using a template tag.

Hull Digital Live 2012

The Hull Digital Live conference is back for its third installment, on 1st November 2012.

Interviewed at Meta Q

I was interviewed by Meta Q for their ongoing series of interviews with people from the ExpressionEngine community.

Introducing SmartDown 1.2

SmartDown 1.2 has been released with easier EE code samples, more flexible configuration, and custom extension hooks.

So long to ExpressionEngine 1

Today marks the official end of life for all my ExpressionEngine 1 add-ons. They will no longer be officially supported, and BucketList is now free.

Updates and oddments

A monthly round-up of ExpressionEngine add-on updates and GitHub-only projects.

Introducing Usher

Usher is a new ExpressionEngine 2 add-on that redirects site administrators to a CP page based on their Member Group.

Speaking at EEUK 2011

I’ll be speaking at EEUK 2011 about commercial add-on development for private clients.

Introducing Campaigner 4

Campaigner 4 is now available, using a new version of the Campaign Monitor API, with a new extension hook and EE2-only support.

Speaking at EECI 2010

I’ll be speaking at the ExpressionEngine CodeIgniter Conference in Leiden this September.

Hull Digital Question Time

If you, like me, have long felt that discussions about the future of online media are sorely lacking a vicious fight to the death, salvation is at hand.

Introducing Campaigner

Campaigner 2.0 is now available, completely rewritten with new features and a revamped user interface, now a commercial product.

It speaks!

The Hull Digital podcast is now available for your listening pleasure, featuring dulcet tones on tech news from months past.

Ready to serve

Pret makes itself accountable by using the names of managers on customer comment cards instead of hiding behind faceless employees.

Introducing BucketList 1.1

BucketList 1.1 is released, introducing the ability to control which buckets are available for each field or FF Matrix cell.

The myth of company morality

A reflection on company ethos and whether large corporations can truly be judged on moral grounds, using Facebook and Get Satisfaction as examples.

Got dissatisfaction

Get Satisfaction removed features from free accounts to force upgrades, rather than adding compelling features to paid plans.

The Lack of Enterprise Show

A reflection on attending The Enterprise Show, finding it focused on tedious legal and accounting matters rather than practical advice for budding entrepreneurs.

In the club

I’ve just become a member of the ExpressionEngine Professionals Network, validating our work by our peers.

Show them what you're made of

If you’re providing a premium product or service, you need to go that extra mile and show people what they’re getting for their money. The higher the premium, the greater the need to demonstrate your worth.

Show me the money

Visual indicators of a product’s worth are important, particularly as our lives become increasingly digitised and abstract. Design isn’t the answer, but money should look like money.

Stop Planning, Start Doing

Execution, even bad execution, is immensely more valuable than detailed plans or brilliant ideas. Fail fast and fail cheap.

Marketing the Mormons

A short reflection on encountering a Mormon missionary on a rainy Wednesday night, and how organisations like the LDS church could learn from companies like Nike about marketing.

Not making the most of now

A reflection on how choosing the easy life over the bigger picture can be damaging in the long term, using Vodafone as an example.

Never forget the big picture

Your web site is only there to serve your business. If it doesn’t contribute to your business goals, then it’s a waste of money.

The USP of me

Running your own business teaches you to recognise and stick to the parts of your business that work as natural extensions of how you live your life.