Make it right
An LLM can write code that runs. Making it right and fast is still up to you.
An LLM can write code that runs. Making it right and fast is still up to you.
The code is not the point, but what if it’s the reason?
The current “vibe code an MVP in a day” trend is toxic bullshit. Good software takes time, but it’s worth the effort.
Selecting random records from grouped data isn’t as simple as it seems. Here’s how to do it in Laravel, using Eloquent subqueries.
Notes to self about the unexpected quirks of the otherwise excellent PhoenixTest library.
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.
After working on a complex problem, I tend to over-complicate the next one. I call this the “complexity halo”.
After three years of cheating death, I finally caught Covid. That said, the week hasn’t been a complete write-off.
This week has been a balance of working on Hap, and making a start on finding a new Elixir contract.
Placing schemas inside contexts makes it difficult to distinguish between data and actions. Let’s fix that.
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.
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.
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.
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.
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.
You need to check a date using Chai. You also need to account for the test execution time.
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.
Every ObjectId includes an embedded timestamp. No need for a separate createdAt column.
Tailwind 1.3 added the ability to specify a line-height for each font-size in your config file.
Gatsby supports TypeScript out-the-box. Unfortunately, the official solution has several shortcomings which limit its usefulness. There is a better way.
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.
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.
JSON Schema includes the multipleOf keyword. This is very useful for checking that a number is rounded to a specific number of decimal places.
You can apply a highlighter effect to text using background gradients.
Your site uses Gatsby and Netlify CMS. Tailwind and PostCSS take care of the styling. You run gatsby develop, and encounter a cryptic error message.
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.
sudo -u <user> <command> doesn’t inherit the user’s environment. Here’s how to fix that.
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 lets you assert that some code throws an error. The key is to pass the test subject to expect, not the test result.
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.
Don’t use deep equals when comparing arrays with Chai; it depends on element order. Examine the array members instead.
How to find documents according to the size of an array field.
TypeScript’s strict type checking can cause problems when working with numeric environment variables. Here’s a solution.
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.
Assume we have two JavaScript arrays, alpha and bravo. We need to determine which items appear in alpha, but not in bravo.
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.
When working on a codebase with a lot of unstaged or untracked changes, the default git status output is unhelpful.
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.
If you’re accustomed to PHP’s logical operators, JavaScript’s implementation can be confusing.
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.
Using the Vim extension for Visual Studio Code? Here’s how to quickly find files and folders in the Explorer.
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.
The default recurse depth of console.dir is 2. This is rarely what I want.
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?
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.
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.
Statecharts are a very useful tool for modelling complex workflows in your application.
TypeScript doesn’t always play nicely with object bracket notation.
The Node CLI supports a --require flag, which allows you to preload a module when running Node.
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.
Use generics when writing a TypeScript function which works with a variety of types.
Many PHP module names include the PHP version. This makes it difficult to write portable bash scripts. Here’s a fix.
Nova uses Laravel policies to manage resource authorisation. This can be problematic when it comes to resource actions.
The Composer documentation provides instructions for using a private GitHub or BitBucket repository as a dependency. This note covers GitLab.
GitLab’s opinion of what constitutes valid YAML differs from the official spec. Booleans, in particular, are problematic.
How to conditionally apply Laravel validation rules, even when you don’t have access to a validator instance.
When you define a resource, Laravel Nova makes an educated guess regarding its URL slug. You can override the default slug by implementing the uriKey method. Here’s how.
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 makes heavy use of the Macroable trait throughout its codebase, but the official docs only mention it in passing. Let’s dig in.
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.
The standard method for removing a global scope from an Eloquent model is a little clunky. We can do better.
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.
A simple, performant solution for eager-loading Eloquent relationships, when you’re only interested in one or two specific properties on the related model.
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.
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.
I was interviewed by Snipcart for their ‘Geek Talk’ series, covering my favourite superhero and some developer-type topics.
Thinking of your plugin as a client of its own API forces you to concentrate on core functionality and changes your perspective on what a plugin actually is.
Composer dependency conflicts between Craft plugins are a thorny problem. Here are some approaches to handle them gracefully.
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.
A guide to implementing PSR-4 autoloading in Craft plugins for loading non-standard classes that don’t fit into standard plugin sub-directories.
Announcing the release of our first Craft ebook, providing a complete guide to validation in Craft 2.
A set of design principles discovered while sorting through old files, surprisingly applicable to both design and development work.
A custom Laravel validation rule for U.S. ZIP codes, validating 5-digit codes and ZIP+4 format.
Introducing Glossary, a new plugin for Craft CMS.
Craft makes it trivially easy to add CTRL-S support to your plugin forms. Just add data-saveshortcut attribute to the form tag.
Part 5 of a definitive guide to validation in Craft, covering how to implement custom validators as callback functions or separate validator classes.
Part 4 of the definitive guide to validation in Craft, covering hidden validator settings that aren’t accessible via usual Craft attribute rules.
Part 3 of a definitive guide to validation in Craft, covering attribute rules, their effect on validation, and which rules are available.
Part 2 of the definitive guide to validation in Craft, covering AttributeTypes and how they affect model validation.
A complete guide to validation in Craft, starting with foundational concepts including model attributes and validation methods.
How to build a fluent repository API in Laravel that allows chaining filters while maintaining decoupling from the data storage layer.
This guide describes what makes for a good Craft Cookbook recipe, providing guidance on structure, tone, and language to promote consistency across recipes.
Using a site-specific plugin to encapsulate site-specific variables and functionality, making templates cleaner and more maintainable.
Craft Cookbook is a new site providing quick solutions to common Craft problems, following the O’Reilly cookbook format.
A quick tip on finding users by username in Laravel using a custom findByUsernameOrFail method.
A simple, elegant solution to common Django ModelForm annoyances: colons after labels, ‘_id’ suffixes, and placeholder attributes.
How to use Craft’s Matrix field with Markdown to create structured, content-author-friendly articles with complete control over the generated HTML.
SmartDown for Craft is a Twig Filter which brings Markdown Extra and Smartypants to Craft CMS.
A reflection on the PyCon incident and the growing belief that people have a right not to be offended.
We relaunched the Experience website, moving away from ExpressionEngine to Kirby, a flat-file CMS that supports our Markdown-based writing workflow.
EllisLab ended its affiliate program, claiming it wasn’t effective. This is a strange decision given the 900% ROI it provides.
What are ExpressionEngine ACTion IDs, and why are they so important?
Updates and oddments for August 2012, covering add-on updates including Campaigner, Crumbly, OmniLog, SmartDown, Testee, and GitHub-only oddments.
Yesterday I released Testee 2.2, which contains a significant new feature: automated testing.
Michael Rog has released Template OmniLogger, a plugin that makes it possible to create OmniLog entries using a template tag.
An improved solution to the ExpressionEngine nested no_results tags bug, courtesy of Low, which properly handles nested conditionals.
This week in EEceania has been a joyous celebration of increased productivity and doubleplusgood social harmony.
Bug fixing in ExpressionEngine should not be the responsibility of the community. We’re paying for a commercial product.
An annoying bug in the ExpressionEngine template parser where nested template tags fail to output no_results blocks, with workarounds.
The Hull Digital Live conference is back for its third installment, on 1st November 2012.
Each of my add-ons now has its own changelog RSS feed at /software/feeds/{addon_name}/.
A reflection on the lack of a standardised solution to multi-language ExpressionEngine sites and the problems it causes for developers.
A guide to implementing custom extension hooks in ExpressionEngine add-ons, following on from the previous post about abdicating responsibility.
The ability to add custom extension hooks to your add-on is one of the most powerful features of ExpressionEngine. It is also woefully under-used by most add-on developers.
Marc Miller has created Developer Info for ExpressionEngine 2, building on the original SL Developer Info concept.
I was interviewed by Meta Q for their ongoing series of interviews with people from the ExpressionEngine community.
SmartDown 1.2 has been released with easier EE code samples, more flexible configuration, and custom extension hooks.
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.
A monthly round-up of ExpressionEngine add-on updates and GitHub-only projects.
Usher is a new ExpressionEngine 2 add-on that redirects site administrators to a CP page based on their Member Group.
The final post in a series on integrating ExpressionEngine with third-party APIs, covering testing strategies and requirements.
Strategies for getting up and running with an API quickly, including understanding your library, reading documentation, and testing methods.
OmniLog is a system-wide message logging add-on for ExpressionEngine, designed for use by ExpressionEngine add-on developers.
Covering responsibilities, using libraries, planning for the future, logging, and notification strategies when integrating with third-party APIs.
I’ll be speaking at EEUK 2011 about commercial add-on development for private clients.
Introduction to a series on integrating ExpressionEngine with third-party APIs, covering trust issues and strategies for robust add-on development.
Crumbly is an ExpressionEngine 2 add-on that automatically generates page breadcrumbs based on the current URL, handling standard EE URLs and custom structures.
DropDate is now fully compatible with ExpressionEngine 2, with credit for the EE2 version going to Lodewijk Schutte.
An update on the ExpressionEngine add-on development series, which stalled after 10 months due to time constraints and perfectionism.
Testee is an EE1 and EE2 module that makes it possible to unit test your ExpressionEngine add-ons.
Campaigner 4 is now available, using a new version of the Campaign Monitor API, with a new extension hook and EE2-only support.
Campaigner for ExpressionEngine 2 is now available, completely rewritten and tested, making it easy to subscribe members to Campaign Monitor mailing lists.
Designing the user interface for the Tweedee Twitter module for ExpressionEngine, covering tools, approaches, and the design process.
MailChimp Subscribe 2.0.0 is out, bringing ExpressionEngine 2 compatibility along with a new UI for the EE 1 version.
Covering basic principles of good UI design for ExpressionEngine add-ons, including simplicity, consistency, differentiation, and conventions.
Introducing Tweedee, a Twitter module for ExpressionEngine 2 that allows building custom searches and controlling which tweets are displayed.
Results from a survey on ExpressionEngine add-on development topics, with insights into reader experience and preferences.
I’ll be speaking at the ExpressionEngine CodeIgniter Conference in Leiden this September.
Clear your schedule, nerds, the Hull Digital Live conference has returned.
Introducing a series of blog posts covering ExpressionEngine add-on development, from initial idea through to support.
Plans for converting our ExpressionEngine add-ons to EE 2, including status updates and notes for each add-on.
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.
Campaigner 2.0 is now available, completely rewritten with new features and a revamped user interface, now a commercial product.
SmartDown uses Markdown Extra and SmartyPants to make your quotes smarter and your hyphens more dashing in ExpressionEngine.
The Hull Digital podcast is now available for your listening pleasure, featuring dulcet tones on tech news from months past.
DropDate is a custom fieldtype for ExpressionEngine, enabling easy date selection using day, month, and year drop-downs.
Pret makes itself accountable by using the names of managers on customer comment cards instead of hiding behind faceless employees.
BucketList 1.1 is released, introducing the ability to control which buckets are available for each field or FF Matrix cell.
A reflection on company ethos and whether large corporations can truly be judged on moral grounds, using Facebook and Get Satisfaction as examples.
SL Developer Info is a new module that collates all the Weblog, Template, and File Upload Preference information you need when developing an ExpressionEngine site.
Get Satisfaction removed features from free accounts to force upgrades, rather than adding compelling features to paid plans.
ExpressionEngine 2.0 went from being a full commercial release to a public beta, creating uncertainty for add-on developers.
Speakeasy is a new ExpressionEngine extension and plugin to help in the ever-tedious fight against spambots.
The final installment covers Git branches, workflow, and day-to-day usage for managing an ExpressionEngine website.
Part 3 of a series on using Git to manage ExpressionEngine websites, covering local repositories, gitignore files, remote repositories, live repositories, and everyday practicalities.
Hot on the heels of a Best in Class award, the Iconic Interiors site has been shortlisted for the “Top Sites of 2008” Interactive Media Award.
Part 2 of a series on using Git to manage ExpressionEngine websites, covering how to set up Git repositories, push, and pull code changes.
Part 1 of 4 on using Git to manage ExpressionEngine websites, covering installation, setup, and basic workflow.
Labgruppen.com has received an “Outstanding Achievement” IMA, a testament to the ExpressionEngine CMS and the content authors.
A reflection on attending The Enterprise Show, finding it focused on tedious legal and accounting matters rather than practical advice for budding entrepreneurs.
Atebits takes a refreshingly common-sense approach to software licensing, treating customers like reasonable human beings rather than potential thieves.
I stopped reading business books and started doing what I enjoy instead. This led to more work and better opportunities.
I’ve just become a member of the ExpressionEngine Professionals Network, validating our work by our peers.
A guide to writing a well-considered and informative quotation request that will help you get better quotes from web agencies and save you money.
A guide to choosing between HTML and XHTML DOCTYPEs, explaining why HTML 5 is the best choice for most websites.
A quote from No Country For Old Men about picking your fights in business and life.
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.
During the economic downturn, I’ve started buying on price, concerned with whether companies are charging enough to survive as a business.
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.
We’ve just been awarded a prestigious Best in Class Interactive Media Award for IconicInteriors.
Email Standards have the potential to completely change the quality and economic viability of HTML email.
A reflection on building web applications compared to medieval cathedrals, drawing parallels between bay-by-bay construction and agile development.
Finding clients who share your values, and why saying ‘no’ can sometimes be the right thing
Execution, even bad execution, is immensely more valuable than detailed plans or brilliant ideas. Fail fast and fail cheap.
Something often overlooked in the debate about plain-text vs. HTML emails is that the latter take much longer to produce.
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.
A reflection on how choosing the easy life over the bigger picture can be damaging in the long term, using Vodafone as an example.
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.
Trying to fool your customers is a very short-sighted business strategy. Opt-out check boxes are a common example of such trickery.
A clearly defined and documented process makes the quality of your end-product more consistent, makes your company more reliable, and serves as a great sales tool.
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.
The constant quest for the perfect productivity solution is actually the biggest productivity killer of all.
A reminder to keep JavaScript form validation simple. Too much validation impairs user experience and can annoy genuine customers.