Blog
Here are the 1,134 blog posts and public notes I have written (so far), on topics including software development, open source software, productivity, public speaking and personal updates.
June 2026
- Fixing typos in my previous command -
- Writing books and live streaming -
- How to easily get back to the root of a Git repo -
- Talking Drupal again -
May 2026
April 2026
- Configuring Git with environment variables -
- Test-Driven Drupal re-launched as an eBook -
- Using Neovim as a terminal multiplexer -
- Speaking at PHP Sussex -
March 2026
- My `create-note` script -
- Building a dynamic README.md -
- Using Process Compose with Sculpin, Browsersync and Decap CMS -
February 2026
- Reversing a YAML list -
- Using Sculpin with Decap CMS -
- Vim startup commands -
- Using .localhost URLs for local development -
- Creating recipes for Drupal Gather -
- Simplifying Drupal's default.settings.php file with Vim -
- Parsing a large XML file with xq -
- Debugging errors in Process Compose -
- Nix for PHP Developers (PHPSW) -
- treefmt - the formatter multiplexer -
- Creating a component library in Sculpin: Part 2 -
- Converting .aax files to .flac with Nix -
- Creating a component library in Sculpin: Part 1 -
- rsync with sudo -
January 2026
- Making rofi behave like dmenu -
- Nix Flake templates and Drupal Flake -
- Writing my own Nix Flake templates -
- Creating diagrams and charts with Mermaid -
- Building Drupal Gather -
- "New" Git commands for "old" people -
- Speeding up Git subtree splits -
- Combining multiple Git repositories into one monorepo -
- Using a forked version of nixpkgs to test Sculpin -
- Adding Sculpin to nixpkgs -
- DrupalCamp England returns next month -
- Published new podcast episodes -
- Rebooting the PHP South Wales website -
- I'm Building my own Component Library -
- Nix, Flakes and Monorepos -
- "Do It tomorrow" by Mark Forster -
- Speaking at PHP South West tonight -
- Adding is optional -
- I'm a Sculpin contributor -
November 2025
September 2025
August 2025
July 2025
- Changelogs with Continuous Delivery -
- A CHANGELOG isn't just a list of Git commits -
- Do you keep a changelog? -
- Don't share user accounts -
- Drupal roles are layerable -
- The permissions issue that took down a website -
- It's hard to take things away -
- Implementing the dendritic pattern -
- Exploring Drupal recipes -
- I don't like large pull requests -
- Exploring the Dendritic pattern -
- The John Carmack plan -
- Nix and the Dendritic pattern -
- Drupal and Nix similarities -
- PHP compatibility checking with phpcs -
- The downside to testing existing sites -
- Drupal Test Traits is not a replacement for traditional tests -
- Using Vim filters -
- My dotfiles repository turns 10 -
- Drupal Recipe Unpacking -
- Drupal Bundle classes -
- Counting tags -
- Easier dependency injection with autowiring -
- Breaking down tasks -
- Discussing web accessibility with Mike Gifford -
- Writing robust bash scripts with Nix -
- Asking the right question -
- What type of change are you making? -
- Dealing with icky code -
- Avoiding indentation -
- Imperative or declarative -
- What is "Infrastructure as code"? -
- PHP, Value Objects and You -
June 2025
- Managing services without NixOS -
- An example of generics in PHP -
- Discussing the Modeler API with Jürgen Haas -
- Ready to go devshells -
- Project-specific dependencies with Nix Flakes -
- Nix and older versions of PHP -
- PHP and Nix shells -
- Giving things descriptive names -
- Consistency with architectural testing -
- Consistency is key -
- My thoughts on the Action pattern -
- You have nothing to lose but your bugs -
- Exploring Drupal Test Traits -
- Drupal Bundle Classes -
- Refactoring, semantic versioning and backward compatibility -
- Is refactoring a lost art? -
- Do code reviews prevent refactoring? -
- Refactorings should be small -
- Refactoring is a rabbit hole -
- Nix for PHP Developers -
- Easily amend commits with git-instafix -
- tmux is my session manager -
- Switching to NixVim -
- Picking cherries -
- Discussing Drupal CMS Recipes with Gareth Alexander -
- Squashing commits can be OK -
- Good commit messages don't always matter -
May 2025
- Do you still need that module? -
- Do you need that module? -
- Drupal-powered podcast pages -
- Using AI for web coding with Luke McCormick (re-uploaded) -
- Why I prefer a rolling Linux distribution -
- Using AI for web coding with Luke McCormick -
- Why write your own CMS? -
- Learning lessons -
- How quickly can you get back online? -
- Don't dump. Write a test. -
- Writing your own test traits -
- Writing tests for Tome -
- My daily email archive, powered by Drupal and Tome -
- Write it down -
- I have a Peertube -
- How many TODO comments do you have? -
- Plain text TODOs -
- Speaking at Reading College -
- A reason why I like static site generators -
- Extending Sculpin with PHP -
- Don't hack core -
April 2025
- With patches, I can change anything I want -
- Automate Drupal deployments with configuration -
- Self hosting my website -
- Building static websites with Drupal -
- Upgrading incrementally -
- Building fonts with Nix -
- nix is like nvm, but for everything -
- Chaining tools for maximum benefit -
- Generating presentation slides with Nix and rst2pdf -
- Caching with decorators -
- Dealing with different API versions -
- Writing good commit messages -
- Be more selective -
- Don't commit changes with `-m` -
- Automated tests reduce debugging time -
March 2025
- First pull request submitted to nixpkgs -
- Why use Collections? -
- Dependency-free PHP Collections -
- Work in sprints, deploy continuously -
- Drupal test writer for hire -
- Don't repeat yourself -
- Building Bootstrap components with Tailwind CSS -
- Covering icky code with automated tests -
- Legacy code is anything older than... -
- Extra PHPDoc types with PHPStan -
- No-one is paying us to... -
- Archiving Drupal websites as static websites -
- An example of feature flagging -
- What's the correct way to add PHPStan to an existing codebase? -
- Building static websites with Drupal -
- Static websites are easy to backup -
- Static websites are easy to host and deploy -
- Static websites are easy to build -
- You can deploy on Fridays -
- Contrib-first doesn't mean building for every use case -
- Submit your session proposal for DrupalCon Europe -
- CSS variables everywhere -
- Rebase and reorder -
- Feature branching slows delivery -
- How much would it cost to build Drupal? -
- Solve one problem at a time -
February 2025
- Do we still need CSS preprocessors? -
- Smaller modules are more reusable -
- Don't create test.php files -
- What would a suckless version of Drupal look like? -
- To patch or not to patch -
- Tests aren't a line item -
- Speaking at PHP Thames Valley -
- More code, more problems -
- Don't branch, use feature toggles -
- Roll back or fix forward? -
- Feature branches cause merge conflicts -
- What are CI and CD? -
- Don't pre-optimise -
- Rebuilding Bootstrap with Tailwind CSS (and Sculpin) -
- Drupalisms and de-jargoning Drupal -
- Using readonly in PHP -
- Small and fast -
- Tidy then push -
- Refactoring and Test-Driven Development -
- Simpler code doesn't mean less code -
- Writing more tests than production code -
- Having less code than you started with -
- How I work around legacy code -
- Drupal and the Open Web -
- Good software is easy to change -
- Good software is easy to test -
- What's the simplest solution? -
- Don't use global dependencies -
January 2025
- Keep your PHP code up to date using Rector -
- Your CI pipeline is your gatekeeper -
- Make it work, then make it good -
- Is it the application or implementation? -
- Should you have a separate front-end for your Drupal website? -
- Why I like Drupal's Layout Builder -
- Why should you upgrade from Drupal 7? -
- What and why -
- Who's Travis? -
- Why I use nixpkgs-unstable -
- Making ddev reproducible -
- Reproducible or repeatable -
- Minimum viable development environment -
- TypeScript for PHP -
- Comments can lie. Code can't. -
- Learning by reading -
- Patching more things -
- Patching Drupal -
- Easy customisation using patches -
- Don't over rely on AI -
- Who is your BDFL? -
- Just use curl -
- Refactor, remove or replace -
- Don't make assumptions -
- It's fun to be competent -
- Actions, Commands or Services? -
- Drupal is dead. Long live Drupal! -
- Catching up with Mark Conroy -
- todo.txt is the simplest project management tool -
- Anyone can use open source software -
- Happy New Year! -
December 2024
- Why do people still build their own CMSes and frameworks? -
- Why I don't buy domain names or host websites for clients -
- Which environment should I test? -
- Import or install? -
- How quickly can you create or update an environment? -
- The more differences there are, the more likely there will be bugs -
- Everyone's localhost is an environment -
- How easily can you move changes between environments? -
- How many environments do you need? -
- Easy feature flags -
- CD or CDs -
- When would you rather fix a bug? -
- Working iteratively -
- Self hosting the Beyond Blocks podcast -
- RTFM -
- Browsing in plain text -
- Notes on Nix -
- Fixing a laptop -
- Enjoying a cup of Gitea -
- Local merging and squashing -
- Other plain texters -
- Docker and NixOS playing nicely together -
- Running Drupal on devenv -
- Using open source software to build open source software -
- Open source software all the way down -
- One more month of Drupal 7 -
- Do you even need JavaScript? -
- Sculpin from Scratch -
- Adopt a Document -
- Override Node Options and Drupal 11 -
- Homelabbing with NixOS -
November 2024
- Using Nix for local application development -
- Managing dotfiles with Nix -
- Running NixOS in the Cloud -
- Nix as an operating system -
- The Nix language -
- Nix, the package manager -
- A modest JavaScript framework for the HTML you already have -
- No more random packages -
- Building different-looking UIs with consistent class names -
- One configuration language to rule them all -
- Live coding is hard -
- Tailwind CSS v4 is so easy to set up -
- drush deploy -
- Override Node Options used by LocalGov Drupal -
- An interesting thing I spotted about the Override Node Options module -
- Git is not GitHub -
- Starting with a clean slate -
- Speaking at the Drupal London meetup -
- Why consistency and reproducibility are important -
- Could Nix and devenv replace Docker Compose? -
- Write plain text files -
- Discussing Drush and Laravel Prompts with Jess Archer -
- What will be included in Drupal CMS? -
- Should Drush be in Drupal core? -
- Excluding local files from Git -
- Drupal 11 is not Drupal 6 -
- Keep your test suite passing -
- Run your tests more often -
- Passing tests doesn't mean a working application -
October 2024
- Code reviews are about the code, not code style -
- Why "no build" is appealing -
- A deep drive into test-driven Drupal development -
- Thinking of new ideas -
- Always review your changes -
- git stash is underrated -
- Sharp blades and dull blades -
- How would you write this test name? -
- Drupal applications are modular monoliths -
- Test, then refactor -
- PHPUnit or Pest? -
- Is PHP a good first programming language? -
- 16 years on Drupal.org -
- Generative AI in PHP -
- Don't just copy and paste -
- AI as a pair-programming partner -
- AI in Drupal -
- Discussing Drupal's ECA module -
- Is post-end-of-live support an anti-pattern? -
- Make the change easy, then make the easy change -
- YAGNI -
- Technical debt isn't always bad -
- Not all legacy code is technical debt -
September 2024
- TODO: re-evaluate Storybook -
- Is testing a chore? -
- Testing personal projects -
- Static analysis with Dave Liddament -
- Don't add blank lines -
- Should I learn React? -
- Are you a real Developer? -
- What would get you fired? -
- Enforcing consistency with automation -
- Writing custom PHPStan rules for Drupal projects -
- Drupal adopts ADRs -
- Be consistent -
- The two ways of writing PHP code -
- De-jargoning Drupal -
- Next week is DrupalCon Barcelona -
- Experimenting with the Default Content module -
- Looking for alpha testers -
- What's your plan? -
- POSTing data from a JSON file -
- Drupal's Lenient Composer endpoint -
- Violinist, render arrays and feature flags -
- When did you last deploy to production? -
- Do you deploy on Fridays? -
- Setting max_allowed_packet in MariaDB -
- Error: unsupported tarball input attribute 'lastModified' -
- Beyond Blocks passes 1,000 downloads -
- Avoiding primitive obsession -
- My laptop died -
- Find bugs sooner -
- Bootcamps, Hackathons, Meetups and Drupal with George Gordon -
- find vs. get -
- Extracting a custom module with a Git subtree -
- Diagram-driven development -
- Sorting parameter arguments and array keys in Vim -
- Do your commit messages still make sense? -
- No-one sees your clean-up commits -
- You need tests to refactor safely -
August 2024
- Make it work, then make it good -
- Revisiting the Null Object pattern in Drupal -
- Merging activities in Strava -
- 600 Daily Emails -
- Single File Components in Drupal with Sam Mortenson -
- Using data attributes with Tailwind CSS -
- Sending POST requests with curl -
- Format JSON file in vim -
- Build Configs is now public and open-source -
- Named arguments add context -
- Data attributes and feature flags -
- CSS, data attributes and feature flags -
- Abbreviations are better than aliases -
- Abbreviations are better than aliases -
- Speaking at PHP Berkshire -
- git-instafix -
- Unveiling Laravel Prompts -
- The Gin admin theme -
- Software Development Graduate website -
- Scaling personal projects -
- One of my earliest Tailwind CSS projects -
- Git remotes can have more than one URL -
- Drush is using Laravel Prompts -
- sshs -
- Writing bash scripts with Nix -
- Using code snippets for effective live demos -
- Publishing a Zettelkasten -
- Ellipsis in pager template fails accessibility tests -
- Drush now uses Laravel Prompts -
- Writing bash scripts with Nix -
- Bash scripting for fun (and profit?) -
- Mermaid - markdown for charts -
- Types add context -
- What are err, req and res? -
- Docblocks or attributes? -
- Makings things frictionless -
- LEGO, robotics and open-source software -
- Always write your code as if... -
- Commits are cheap -
- To configure or not to configure -
- People read more code than they write -
- Computers don't care -
- Application code is only part of the puzzle -
- Drupal 7 security support changes -
- Drupal 11 is here! -
- Merging unrelated histories -
- Adding automated tests to Content Access by Path -
July 2024
- Why I use Linux for my operating system -
- Maintaining backward compatibility -
- Don't run code formatting in your CI pipeline -
- Using a run file in your CI pipeline -
- Only have one URL per Git remote -
- Things aren't perfect -
- There isn't just one way to do something -
- Automation, Linux training and mechanical keyboards with Jochen Lillich -
- What's the smallest number of dependencies you can have? -
- Queuing long-running tasks -
- Tailwind CSS v4, with even more CSS -
- The power of arbitrary classes -
- How I started using utility-first CSS -
- Applying all the things -
- You can do utility-first CSS with Sass -
- Back to Sass and traditional CSS -
- Running automated checks in a CI pipeline -
- Committing CI artifacts -
- CI !== CI Pipeline -
- A new version of Drupal is only a command away -
June 2024
- Bootcamps, communities and first Developer jobs -
- Countdown to Drupal 11 -
- Drupal 10.3 released -
- Aliases and abbreviations -
- Don't use aliases -
- Proof of concept -
- Today I learned -
- Re-learning Behat -
- Do you still need TypeScript? -
- Bug free guarantee -
- Is the code extensible? -
- Dead or done -
- Vetting third-party open-source software -
- Recording architectural decisions -
- Do you separate your logic? -
- Online Drupal mob contribution sessions -
- Proudly found elsewhere -
- Drupal is older than... -
- Not invented here -
- End the day with a failing test -
- Writing assertions first -
- Is the abstraction worth it? -
- Writing comments first -
- Only seven months left of Drupal 7 support -
- Don't use AI to write your automated tests -
May 2024
- Putting glue on pizza -
- Ask questions -
- Make it easy -
- Why is everyone moving to SQLite? -
- Why do you still write Sass? -
- Is it time to stop writing Sass? -
- Testing is a reusable skill -
- Don't put HTML in your body field -
- Why I use long parameter names in scripts -
- `git revert` is your friend -
- Which commit has the largest message? -
- Referencing other commits in commit messages -
- Better commit messages means better pull requests -
- Should you strictly enforce the 50/72 rule? -
- Why I don't commit with -m -
- The first test is the hardest -
- Should you include issue IDs in your commit messages? -
- Free code reviews -
- DrupalCamp Ghent -
- Merging without merge commits -
- Don't delete my commit messages -
- Optimise for revertability -
- Do you have a deadline? -
- Assertions aren't just for tests -
- Drupal 7.100.2 -
- Interactive staging -
- Making PHPStan stricter -
- Strict typing in PHP -
- Don't add boolean arguments -
- Re-evaluating old tools -
- Broken pipeline? Fix or revert it. -
April 2024
- Stepping back into debugging -
- Some kind words -
- Replicating a bug with a test -
- Can you make a test fail? -
- Don't cherry-pick features from a branch to deploy -
- If everyone branches, no-one gets the updates -
- Testing topic branches in isolation -
- Why use a static site generator -
- Building websites with PHP and Sculpin -
- Almost at 100 talks and workshops -
- Speaking at DrupalCamp Belgium -
- When should you tag 1.0? -
- I made my first commit to LocalGov Drupal Microsites -
- Regular releases encourage contribution -
- Regularly releasing open-source software -
- A note to open-source software maintainers -
- What about updating custom modules and themes? -
- Rector is not just for Drupal -
- Drupal Rector and the Project Update Bot -
- Over 100 ATDC subscribers -
- Resurrecting the Speakerdeck Field module -
- Paying it forward -
- Come for the software, stay for the community -
- Avoiding nesting -
- Drupal is a content management framework -
- One Drupal fits all -
- PHP attributes: coming soon to a Drupal version near you -
- Switching web servers using Build Configs -
- Releasing a new project one page at a time -
- I'm attending LocalGov Drupal Camp -
March 2024
- Making Git work the way you want -
- Leaving a trail of breadcrumbs -
- How I Git -
- Starting to sprinkle JavaScript with Stimulus -
- Hotfixing without branches -
- Let someone else do the work -
- Newport City Council running LocalGov Drupal -
- Why I don't use a GUI for Git -
- Write programs that do one thing and do it well -
- Watching all the things -
- Git hooks - yay or nay? -
- What is legacy code? -
- Drupal Commerce: not just for selling t-shirts and hats -
- Automated Drupal 11 compatibility fixes -
- Patches vs Merge Requests -
- Adding tests to the Content Access by Path module -
- Everything is a trade-off -
- Just say Drupal -
- 80% of PHP? -
- You should know when to remove a feature flag -
- Feature flags should be short-lived -
- Visual testing and Diffy with Yuri Gerasymov -
- Override Node Options is used on 40,624 Drupal websites -
- Conventions over readability? -
- Why write software for this? -
- Types are optional -
- Why write framework-agnostic code -
- Why you need layers in your application code -
- Centarro and Drupal Commerce with Ryan Szrama -
- Are your tests good enough? -
- Back to live streaming -
February 2024
- Experimenting with web components -
- Why I don't branch -
- Building a design system in a few hours with Symfony -
- Docker and content creation with Nick Janetakis -
- Why do people still use Git Flow? -
- When should you start writing tests? -
- Slow down to go fast -
- Diffy and visual regression testing -
- Coding defensively and considering the unhappy path -
- Which level is right for you? -
- Introducing Versa - the versatile CLI tool -
- Build something useful in one day with Mark Conroy -
- Another way to create test module configuration -
- Keep logic within tests for as long as you can -
- gray or grey, and center or centre? -
- Major version updates are just removing deprecated code -
- It takes the drama out of open-source -
- Symfony conventions making their way to Drupal -
- Twig, Symfony and SymfonyCasts with Ryan Weaver -
- Do you really need it? -
- Defining boundaries between custom Drupal modules -
- Experimenting with Architectural Testing -
- Running tests in parallel with Paratest -
- Tim Lehnen and the Drupal Association -
- .gitignore or .gitallow -
- Speaking about Sculpin at PHPSW -
- Reducing complexity makes contribution easier -
- Automated tests prevent you from adding regressions -
- Start with a failing test -
January 2024
- Automated tests mean you can make changes quicker -
- TDD doesn't mean you know everything upfront -
- Violinist and automation with Eirik Morland -
- Ignoring things globally -
- gitignore - inclusive or exclusive? -
- Write once, manage forever -
- Speaking at PHP Oxford -
- Defining Ubiquitous language -
- Why use automation tools for dependency updates -
- Tailwind CSS workshop recording -
- Where is the value in your application? -
- PHPUnit or Pest? -
- Tests can assert multiple things -
- Is zero unlimited? -
- Please don't use short variable names -
- Daily or quarterly? -
- Don't be perfect, be useful -
- Code is a liability, not an asset -
- Testing Legacy with Mike Karthauser -
- Utility classes make global scope local -
- My Drupal testing email course is live -
- Don't put business logic in templates -
- Using Tailwind CSS is a great way to learn CSS -
- Try it and see -
- Things take as long as they take -
- Sculpin - the PHP static site generator -
- PHP in Neovim -
- Reuse what you can. -
- Why you need to start upgrading from Drupal 7 now -
- Flexible Mob and Pair Programming -
- Continuous improvement -
December 2023
- Just... -
- We've always done it this way -
- Decide, automate, document -
- Don't let pride get in the way of productivity -
- Writing new code is quick, to begin with -
- Good code is not about being easy to write -
- A sneak peek of my Drupal automated testing course -
- This should never happen -
- Using a whole framework or part of it -
- Which is the best programming language, CMS or framework? -
- New year coaching -
- hover + focus = hocus -
- PHP TUIs, CLIs and open-source with Dan Leech -
- Should you run static analysis on your tests? -
- Fail fast, fix fast -
- Adding snapshot tests to Build Configs -
- Building your own in-house Drupal distribution -
- Save time and effort with Drupal distributions -
- Reviving an old PHP project -
- DrupalCon session survey results -
- Custom coding standards and conventions -
- Suffixing names -
- Rebuild or iterate -
- Don't just rewrite. Re-invent. -
- A Drupal case study from Oxfam -
- Open-source first doesn't mean you need to cover every use case -
- Open-source encourages more open-source -
- Writing contrib modules as glue between your custom code -
- The contribution-first workflow -
November 2023
- Are bugs good for users? -
- The lowest level is better than no level -
- Which PHPStan level should you use? -
- Finding the best test base -
- To docblock or not to docblock -
- Community engagement for non-technical Drupal enthusiasts -
- Are conventional commits worth it? -
- Partial mocking -
- Frequency reduces difficulty -
- Why I built "Build Configs" -
- What'll be in Drupal 11? -
- Why I've standardised on 'run' scripts -
- Writing good test names -
- Drupal's Alternate Realities -
- Avoiding over-mocking -
- Don't pre-optimise and over-customise -
- Why I prefer integration tests to unit tests -
- PHPUnit does more than unit testing -
- Drupal gives you so much out-of-the-box -
- Work in small batches -
- Retrofit with Matt Glaman -
- README-driven development -
- "Building Build Configs" at PHP South West -
- It depends -
- The first Beyond Blocks podcast episode is recorded -
- I'm starting a podcast -
- Why you should contribute to open-source software -
- Why your company should contribute to open-source software -
- Is code coverage an objective or guideline? -
- Drupal 9 is now end of life -
October 2023
- One official Drupal development environment? -
- Is Drupal a CMS or a framework? -
- I can drive my car blindfolded, but is it a good idea? -
- Can you move faster without tests? -
- Work with me and support the Drupal Association -
- Is decoupled Drupal still a thing? -
- Automated tests prevent regressions when upgrading -
- How to test code you didn't write -
- When should you run your tests? -
- Off to DrupalCon -
- Do you need to write tests for small or short-lived projects? -
- Writing tests is an investment -
- Business logic in template files? -
- Do you need that module? -
- Why use Composer to manage Drupal dependencies? -
- Spotting new things in Drupal 10.1 -
- Is test-driven development difficult? -
- A breakdown of tests from a current project -
- Software development is like going to the Dentist -
- Some solutions are good enough -
- The best solution is the one that gets the tests to pass -
- There is no perfect solution -
- Only comment what needs to be commented -
- TDD: repeat and refactor -
September 2023
- Automated testing offers repeatability -
- 92 changed files with 885 additions and 156 deletions -
- Feature flags enable continuous integration -
- Separating environments with feature flags -
- Which branch should be in which environment? -
- Should you use a staging environment? -
- Test to save your job -
- Everyone tests their code, but not everyone writes automated tests -
- Documentation and comments get stale. Tests don't. -
- Buggy software causes reputational damage -
- Why I prefer types -
- Stop writing tests -
- Increasing test coverage with regression tests -
- How much refactoring should I be doing? -
- How not to break 36,000 websites -
- Types or no types -
- Outside-in or inside-out? -
- Which type of test should I use? -
- Don't inject too many dependencies -
- Don't use "else" -
- Code is read more than it's written -
- A lack of tests discourages refactoring -
- Do you proactively refactor your code? -
- What's the simplest test to begin with? -
- Verbosity over abstraction -
- Spring clean before upgrading -
- Charging more _not_ to write tests -
- Including time for tests in estimates -
- Planning first or reviewing last? -
- Non-blocking code reviews -
August 2023
- Code review discourages small refactorings -
- TDD and "Unexpected errors" -
- CI pipelines are an automated code review -
- Pair and mob programming are continuous code review -
- Pull requests are great for open-source, but not for teams -
- Work in small chunks -
- Start with a vague test -
- Testing multiple implementations with contract tests -
- Don't use third-party services directly -
- Deployments with your CI pipeline -
- Which part of the CI pipeline has the most value? -
- PHP types and assertions -
- Asserting all the things -
- Types vs tests -
- Writing custom assertions in your tests -
- Writing tests in your own time -
- Writing test and implementation code are the same task -
- Why keep a dotfiles repository -
- Don't estimate separately for testing -
- Does not writing tests actually save time? -
- Everyone tests their code -
- Testing, fixed prices and bug-free guarantees -
- Vim is my lightsaber -
- 8 years of dotfiles -
- Tests make upgrades less risky -
- YAGNI -
- Use Drupal to own your content -
- Laravel Prompts and framework-agnostic tools -
- What problem are we trying to solve? -
- A crash course into automated testing with Drupal -
- Maintaining a module used on 35,000 Drupal websites -
July 2023
- Upgrading from Drupal 9 is easier -
- Commit often, deploy often -
- Should I wait to upgrade from Drupal 7? -
- Don't write generic commit messages -
- Stick to conventions -
- Prove the concept -
- Working backwards -
- Testing is all about confidence -
- Tomorrow is easier if today's code is simpler -
- More code, more problems -
- Comments as communication -
- Tests as communication -
- TDD is like clicker training -
- TDD as a concept is simple, but TDD is difficult -
- Too many choices? -
- When writing a failing test, you're designing your code -
- Test-driven development makes you more productive -
- Automated testing is more than just unit testing -
- Services vs Actions -
- Think smaller with TDD -
- Why write custom assertions in your tests? -
- Docker or Nix? -
- Once you start writing tests, you can't stop -
June 2023
- How did you learn automated testing? -
- Tests won't tell you if your code works -
- There's no value in a broken CI pipeline -
- A CI pipeline is like an additional team member -
- Is any code without tests legacy code? -
- Do you need permission to do Test-Driven Development? -
- Credited on 200 fixed issues on Drupal.org -
- Why keep a Changelog? -
- It's only a bad situation if you fail to learn from it -
- Deployments or releases -
- Should you deploy on a Friday? -
- Should you upgrade from Drupal 7 to Drupal 10? -
- Are you really doing CI/CD? -
- Avoid Git merge hell with trunk-based development -
- Make the change easy, then make the easy change -
- Done is better than perfect -
- What if there was no open-source software -
- Should you feature flag everything? -
- Feature flags in a multi-tenancy application -
- Failing fast -
- How do you know when to remove a feature flag? -
- Drupal is built by people -
- Write less Drupal code -
- What does the Drupal 7 EOL extension mean to you? -
- Drupal 7 end-of-life extended -
- How long should a feature flag live? -
May 2023
- Drupal 9: almost end-of-life already -
- A minor breaking change -
- Semantic versioning -
- Why is backward compatibility important? -
- What is deprecated code? -
- Mastering your tools improves productivity -
- Is the Drupal release cycle too fast? -
- Releasing small changes often is less risky -
- When You Do Things Right, People Won't Be Sure You've Done Anything at All -
- Always listen to the mob -
- Why I like pair and mob programming -
- Tailwind CSS makes change easier -
- The single responsibility principle -
- Only write enough code to get a failing test -
- You don't need to think about what to do next -
- Why it's important to see the test fail -
- Getting to green -
- Speaking at the Symfony UK meetup in London -
- Just because core is supported... -
- Does it depend on who you ask? -
- Should Drupal 7 support be extended again? -
April 2023
- Will we see Drupal 7.100? -
- Write the test backwards -
- Structure a new test by writing comments first -
- Shortening the feedback loop even more -
- CI pipelines should start locally -
- Laravel Pipelines -
- Configuration files as a service -
- Making my Drupal module template Drupal 10 compatible -
- Micro-refactorings -
- Camel-case or snake-case for Drupal code? -
- Consistency is key -
- Introducing feature flags to "build-configs" -
- Refactoring with readonly classes in PHP 8.2 -
- Automatically running commands with nodemon -
- Data transfer objects and value objects -
- Immutable read-only properties in PHP 8.1 -
- Cleaner PHP code with promoted constructor properties -
- just vs make -
- How I use Neovim for writing PHP -
- Nix, NixOS, Home Manager, and WSL2 -
- Why I use tmux -
- Speaking at PHP London -
March 2023
- Software development is about solving problems and adding value -
- With utility styles, your CSS stops growing -
- There isn't a standard "Tailwind-looking" site -
- The benefits of automation -
- Automating all the things -
- What problem are we trying to solve? -
- In what language should I write my automation? -
- Automating infrastructure with IaC -
- Mentoring for School of Code -
- Why I built a tool to generate configuration files -
- Adding a LocalGov Drupal example -
- Busy working on client projects -
- Tailwind CSS at the Norfolk Developer Conference -
February 2023
- Tailwind: why I prefer to extract HTML components -
- Clients don't care which design pattern you use -
- Creating a Drupal 10 compatible version of Override Node Options -
- Upgrading my Drupal example project to Drupal 10 -
- Tailwind CSS at the Bristol Software Development Meetup -
- Creating API endpoints with Astro -
- Fetching external API data with Astro -
- Astro as a static site generator -
January 2023
- To squash or not to squash -
- Small commits and good commit messages -
- Debugging with git bisect -
- Building Bootstrap CSS examples with Tailwind -
- Tailwind: Not just translating CSS to utility classes -
- Tailwind's classes are your classes -
- Long-term maintainability with utility classes and Tailwind CSS -
- Drupal turns 22! -
- Things to know about PHP -
- Back after PHP Stoke -
- Reducing utility class duplication -
- Utility-first or utility-last? -
- Adding Tailwind CSS to an existing project -
- Testable Tailwind CSS plugins -
- Tailwind CSS' extensibility is one of its best features -
- Don't use arbitrary values in Tailwind CSS -
- Types and static analysis saved me today -
December 2022
- Just start writing -
- Tests are living documentation -
- What is the cost of a bug? -
- Debugging gitignore rules -
- Speaking at PHP Stoke and nor(DEV):con -
- The Boy Scout rule -
- Duck typing -
- Automating all the things, including infrastructure -
- Happy Drupal 10 release day! -
- Managing databases with Neovim and Docker -
- How and why I started using PostCSS -
- The Decorator design pattern -
- Separating releases from deployments with feature flags -
- Should you comment your code? -
- Outcomes or output -
- Writing "Why first" user stories -
- What to do with TODO comments -
- Commit and push something every day -
- Writing readable code -
November 2022
- Ship, Show or Ask -
- Plan, then code -
- Doing the simplest possible thing -
- Do you need to branch if you're the only one working on a project? -
- tldr -
- Git tricks to avoid committing commented-out and other unneeded code -
- Version-controlled commented-out code -
- Are missing tests a blocker to refactoring? -
- One test a day keeps bugs away -
- Agnostic CI pipelines with run files -
- Why don't you write automated tests? -
- Writing good automated test names -
- Camel-case or snake-case for test methods? -
- How I manage multiple Drupal websites using the same codebase -
- Building a minimum viable product and managing technical debt -
- Creating a small proof-of-concept application in an afternoon -
- Your conference talk has been accepted -
- Are sprints incompatible with Continuous Deployment? -
October 2022
- Refactoring one large test into multiple smaller tests -
- The open-source-first development workflow -
- Why write framework agnostic packages? -
- Getting back into live streaming -
- Neovim as a Personalised Development Environment -
- What are Drupal distributions? -
- Looking at LocalGov Drupal -
- Automated testing and test-driven development are not the same -
- Cherry picking commits is an anti-pattern -
- run file vs task runners -
- Pair and mob programming -
- 14 years on Drupal.org and working with PHP and Drupal -
- Overcoming deployment anxiety -
- Not long until Drupal 10 -
- Contributing to open-source software, one small change at a time -
- Coding defensively, and Implicit vs explicit coding -
- First impressions of Astro -
- Refactoring to value objects -
- Minimum viable CI pipelines -
- Why do code katas? -
September 2022
- Store Wars: different state management in Vue.js -
- Mob programming at PHP South Wales -
- Mentoring with Drupal Career Online -
- Experimenting with the Nix package manager -
- Using a component library for front-end development -
- ADRs and Technical Design Documents -
- Releasing a Drupal module template -
- Being a Drupal contribution mentor -
- Why I like trunk-based development -
- Useful Git configuration -
- Thoughts on automated code formatting -
- Why I mostly write functional and integration tests -
- The simplest Drupal test -
- A month of daily emails -
- Custom styles in Tailwind CSS: `@apply`, `theme` or custom plugins -
- Automating Ansible deployments in CI -
- Refactoring a Tailwind CSS component -
- Keeping secrets with Ansible Vault -
- My Tailwind CSS origin story -
- Deploying applications with Ansible -
- Using Ansible for local environment configuration -
- Using Ansible for server configuration -
- Creating infrastructure with Ansible -
- Automating all the things with Ansible -
- Conventional commits and CHANGELOGs -
August 2022
- To monorepo, or not to monorepo? -
- Why I don't only use Drupal -
- Why I like Drupal -
- How I started programming -
- Giving back -
- Always be learning -
- Why I work in Neovim -
- How I've configured Git -
- Git: GUI or command-line? -
- Being a T-shaped Developer -
- Why I use Docker and Docker Compose for my projects -
- A return to offline meetups and conferences -
- Pair programming or code reviews? -
- 'Talking Drupal' and Tailwind CSS -
- One more "run" command, for Git worktrees -
- What are Git hooks and why are they useful? -
- Using a "run" file to simplify project tasks -
- Why I write automated tests -
- I wrote a Neovim plugin -
- Git Worktrees and Docker Compose -
July 2022
January 2022
December 2021
October 2021
- Introducing a Drupal distribution for meetup websites -
- Continuous Integration vs Continuous Integration -
July 2021
June 2021
April 2021
- Presenting from PDF slides using pdfpc (PDF Presenter Console) -
- Published my first Docker images on Docker Hub (ADR Tools, Sculpin, rst2pdf) -
- Docker resources -
February 2021
- Decorating an Entity Metadata Wrapper to add and refactor methods -
- Cleanly retrieving user profile data using an Entity Metadata Wrapper -
January 2021
- Survey results from my DrupalCon Europe session (Test-Driven Drupal) -
- Test-Driven Drupal presentation from DrupalCon Europe -
- Ignoring PHPCS sniffs for PHPUnit tests -
November 2020
September 2020
August 2020
- Interview with a Drupal Expert (with Code Enigma) -
- Creating a custom PHPUnit command for DDEV -
- Migrating to Drupal 8: Introduction -
- Coloured output with PHPUnit and GitHub Actions -
July 2020
- Streaming with Spabby (Gary Hockin) about Drupal -
- Speaking at DrupalCon Europe 2020 -
- 10 years working full time with Drupal and PHP -
- Speaking remotely during COVID-19 -
April 2020
- My first blog post published for Inviqa -
- Presenting on Tailwind CSS and Ansible at CMS Philly -
- Test-Driven Drupal on Gitstore and Leanpub -
- Using the pcss extension for PostCSS with Webpack Encore -
March 2020
February 2020
September 2019
July 2019
June 2019
May 2019
April 2019
March 2019
December 2018
- Rebuilding Bartik (Drupal's Default Theme) with Vue.js and Tailwind CSS - part 2 -
- Published my first NPM package -
November 2018
- Rebuilding Bartik (Drupal's Default Theme) with Vue.js and Tailwind CSS -
- DrupalCamp London 2019 - Tickets Available and Call for Sessions -
October 2018
September 2018
August 2018
- Examples of using Laravel Collections in Drupal -
- Experimenting with events in Drupal 8 -
- Null Users and System Users in Drupal -
- Drupal 8 Commerce: Fixing 'No Such Customer' error on checkout -
- Croeso PHP South Wales! -
July 2018
June 2018
- Drupal Bristol Testing Workshop -
- How to Use Environment Variables for your Drupal Settings with Docksal -
May 2018
April 2018
March 2018
- How to put your PHP application in a subdirectory of another site with Nginx -
- How to split a new Drupal contrib project from within another repository -
- Drupal 8.5.0 Released -
- Tweets from DrupalCamp London -
- Yay, the Mediacurrent Contrib Half Hour is Back! -
February 2018
- Building the new PHPSW Website -
- Queuing Private Messages in Drupal 8 -
- Looking forward to DrupalCamp London -
- Using Tailwind CSS in your Drupal Theme -
January 2018
November 2017
July 2017
June 2017
May 2017
- Turning Your Custom Drupal Module into a Feature -
- DrupalCamp Bristol 2017 - Early Bird Tickets, Call for Sessions, Sponsors -
- Fixing Drupal SimpleTest issues inside Docker Containers -
January 2017
- Nginx Redirects With Query String Arguments -
- Easier Sculpin Commands with Composer and NPM Scripts -
December 2016
July 2016
May 2016
February 2016
December 2015
July 2015
June 2015
April 2015
March 2015
- 2014 -
December 2014
November 2014
- Include environment-specific settings files on Pantheon -
- Using Remote Files when Developing Locally with Stage File Proxy Module -
- Include CSS Fonts by Using a SASS each Loop -
October 2014
- Updating Features and Adding Components Using Drush -
- How to fix Vagrant Loading the Wrong Virtual Machine -
May 2014
March 2014
February 2014
January 2014
December 2013
- Download Different Versions of Drupal with Drush -
- Quickly Apply Patches Using Git and curl or wget -
November 2013
September 2013
July 2013
June 2013
April 2013
March 2013
February 2013
January 2013
December 2012
November 2012
October 2012
September 2012
August 2012
July 2012
- Writing an Article for Linux Journal -
- Install and Configure the Nomensa Accessible Media Player in Drupal -
- My new Drupal modules -
May 2012
- Dividing Drupal's process and preprocess functions into separate files -
- Writing a .info file for a Drupal 7 theme -
- Prevent Apache from displaying text files within a web browser -
- How to add a date popup calendar onto a custom form -
- Forward one domain to another using mod_rewrite and .htaccess -
- Checkout a specific revision from SVN from the command line -
April 2012
- Adding Custom Theme Templates in Drupal 7 -
- Installing Nagios on CentOS -
- Create an Omega Subtheme with LESS CSS Preprocessor using Omega Tools and Drush -
February 2012
January 2012
October 2011
August 2011
May 2011
March 2011
February 2011
November 2010
October 2010
- Create a Better Photo Gallery in Drupal - Part 2.1 -
- Create a Better Photo Gallery in Drupal - Part 3 -
- How to Create and Apply Patches -
September 2010
August 2010
- Review of the Image Caption Module -
- Create a Better Photo Gallery in Drupal - Part 2 -
- Create a Better Photo Gallery in Drupal - Part 1 -
- Review of the Admin:hover Module -
July 2010
- Review of the Teleport Module -
- Add a Taxonomy Term to Multiple Nodes Using SQL -
- Create Virtual Hosts on Mac OS X Using VirtualHostX -
- Change the Content Type of Multiple Nodes Using SQL -
June 2010
- Create a Flickr Photo Gallery Using Feeds, CCK and Views -
- 10 Useful Drupal 6 Modules -
- Create a Block of Social Media Icons using CCK, Views and Nodequeue -
- Improve JPG Quality in Imagecache and ImageAPI -
May 2010
- Quickly Import Multiples Images Using the Imagefield_Import Module -
- Create a Slideshow of Multiple Images Using Fancy Slide -
- Quickly Create Zen Subthemes Using Zenophile -
- Conditional Email Addresses in a Webform -