diff --git a/.gitignore b/.gitignore index bb459b8ea..dcba387fa 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ /recipes /vendor/ /web +!/files diff --git a/files/public/.htaccess b/files/public/.htaccess new file mode 100644 index 000000000..b0dc5406e --- /dev/null +++ b/files/public/.htaccess @@ -0,0 +1,24 @@ +# Deny all requests from Apache 2.4+. + + Require all denied + + +# Deny all requests from Apache 2.0-2.2. + + Deny from all + + +# Turn off all options we don't need. +Options -Indexes -ExecCGI -Includes -MultiViews + +# Set the catch-all handler to prevent scripts from being executed. +SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006 + + # Override the handler again if we're run later in the evaluation list. + SetHandler Drupal_Security_Do_Not_Remove_See_SA_2013_003 + + +# If we know how to do it safely, disable the PHP engine entirely. + + php_flag engine off + \ No newline at end of file diff --git a/files/public/2025-05/1517685979984.jpg b/files/public/2025-05/1517685979984.jpg new file mode 100644 index 000000000..64cb41652 Binary files /dev/null and b/files/public/2025-05/1517685979984.jpg differ diff --git a/files/public/2025-05/duncan.jpeg b/files/public/2025-05/duncan.jpeg new file mode 100644 index 000000000..717d3cd78 Binary files /dev/null and b/files/public/2025-05/duncan.jpeg differ diff --git a/files/public/2025-05/joe-howell.jpg b/files/public/2025-05/joe-howell.jpg new file mode 100644 index 000000000..628585ef5 Binary files /dev/null and b/files/public/2025-05/joe-howell.jpg differ diff --git a/files/public/2025-05/jon-hallett.jpeg b/files/public/2025-05/jon-hallett.jpeg new file mode 100644 index 000000000..2732bce87 Binary files /dev/null and b/files/public/2025-05/jon-hallett.jpeg differ diff --git a/files/public/2025-05/luke2.mp3 b/files/public/2025-05/luke2.mp3 new file mode 100644 index 000000000..468e7ea88 Binary files /dev/null and b/files/public/2025-05/luke2.mp3 differ diff --git a/files/public/2025-05/michael-itkoff.jpg b/files/public/2025-05/michael-itkoff.jpg new file mode 100644 index 000000000..9791ff676 Binary files /dev/null and b/files/public/2025-05/michael-itkoff.jpg differ diff --git a/files/public/2025-05/mick-felton.jpg b/files/public/2025-05/mick-felton.jpg new file mode 100644 index 000000000..ebdc95c82 Binary files /dev/null and b/files/public/2025-05/mick-felton.jpg differ diff --git a/files/public/feeds/daily2_1.xml b/files/public/feeds/daily2_1.xml new file mode 100644 index 000000000..985887096 --- /dev/null +++ b/files/public/feeds/daily2_1.xml @@ -0,0 +1,22168 @@ + + + + Oliver's daily email list + A daily email newsletter about software development, DevOps, community, and open-source. + http://localhost:8000/daily + + Building fonts with Nix + /daily/2025/04/16/fonts + http://localhost:8000/daily/2025/04/16/fonts + +
<p>I recently started <a href="http://localhost:8000/daily/2025/04/07/nix-rst2pdf">using Nix to build my PDF presentation slides</a> that <a href="http://localhost:8000/presentations/building-presenting-slide-decks-rst2pdf">I create with rst2pdf</a>.</p> + +<p>I removed the custom build script that was generating the PDF files and moved that code into a Nix derivation.</p> + +<p>Now I can run <code>nix build .#test-driven-drupal</code> and it will generate the slides for that talk.</p> + +<p>As well as the files specific to each presentation, I also have a derivation for shared assets that apply to all talks - i.e. the stylesheets and fonts that are embedded within the PDF.</p> + +<p>The font files were stored in the repository but I wanted to remove them and use the font files available in nixpkgs.</p> + +<p>After some small changes, <a href="https://code.oliverdavies.uk/opdavies/talks/commit/e24d2df83f04e492151b1a1f4901490ce76ffd45">I was able to do it</a> and remove the font files from my repository.</p> + +<p>The Inter font is available in nixpkgs, but it downloads an <code>InterVariable.ttf</code> font that rst2pdf didn't know how to change the font weight for, so I made my own derivation of the static Inter font based on the releases from GitHub.</p> + +<p>I'm happy that I was able to achieve this, as my repository is leaner and I'm continuing to find new and interesting uses for Nix in my workflows.</p> +
+
+ Wed, 16 Apr 2025 00:00:00 GMT +
+ + nix is like nvm, but for everything + /daily/2025/04/15/nix-nvm + http://localhost:8000/daily/2025/04/15/nix-nvm + +
<p>I was recently explaining and demonstrating Nix and direnv to a colleague and showing how, when I moved into a directory, new packages or different versions of packages became available.</p> + +<p>If I left the directory, I was reverted back to my global packages and versions.</p> + +<p>In this demonstration, I was showing how I can have different versions of PHP and node for a particular project - replacing a lot of what I'd previously used tools like Vagrant and Docker for.</p> + +<p>I came up with a comparison between Nix and nvm - the node version manager - a tool that allows you to install multiple versions of nodejs and switch between them.</p> + +<p>Using Nix and direnv is more seamless, but it works for everything.</p> + +<p>I'm able to switch versions of PHP, MySQL, MariaDB, PostgreSQL or anything else I need with Nix.</p> + +<p>Not just node, and without needing containers.</p> +
+
+ Tue, 15 Apr 2025 00:00:00 GMT +
+ + Chaining tools for maximum benefit + /daily/2025/04/08/chaining + http://localhost:8000/daily/2025/04/08/chaining + +
<p>Yesterday I showed <a href="http://localhost:8000/daily/2025/04/07/nix-rst2pdf">how I'm using Nix to build my presentation slide decks</a> with rst2pdf.</p> + +<p>This allows me to run a simple command like <code>nix build .#test-driven-drupal</code> to build the slides for the given presentation.</p> + +<p>But I can use other tools to make this even easier.</p> + +<p>What if I wanted to have a list of the available presentations to select from, and selecting one would build it?</p> + +<p>Following the UNIX philosophy, I can use multiple tools together to achieve this.</p> + +<p>Firstly, I can run <code>nix flake show --json</code> to show the output from my flake.nix file, which looks something like this:</p> + +<pre><code class="json">{ + "devShells": { ... }, + "formatter": { ... }, + "packages": { + "x86_64-linux": { + "build-configs": { ... }, + "sculpin": { ... }, + "shared": { ... } + } + } +} +</code></pre> + +<p>The package names - a.k.a. the presentation names - are what I want to select from.</p> + +<p>I can parse the JSON object with <code>jq</code>, remove any unwanted options with <code>grep -v</code> and use <code>fzf</code> to give me a list I can fuzzy search in.</p> + +<p>In a Bash script, I can assign this to a variable:</p> + +<pre><code class="bash">selected=$(nix flake show --json | jq --raw-output '.packages["x86_64-linux"] | keys[]' | grep -v shared | fzf) +</code></pre> + +<p>Once I have selected a name, I can call <code>nix build</code> on it.</p> + +<pre><code class="bash">nix build .#"$selected" +</code></pre> + +<p>This is a simple example, but it shows how programs can be used together and output can be passed through each program to get the result you want.</p> +
+
+ Tue, 08 Apr 2025 00:00:00 GMT +
+ + Generating presentation slides with Nix and rst2pdf + /daily/2025/04/07/nix-rst2pdf + http://localhost:8000/daily/2025/04/07/nix-rst2pdf + +
<p>Since switching to Nix and NixOS, I've been looking for opportunities to use Nix more in addition to managing my laptop and server configurations and creating development shells for projects.</p> + +<p>Nix is a build tool, so I've started to use it to build my slide decks which I create using rst2pdf.</p> + +<p>I write the rst (reStructuredText) file and compile it to a PDF file.</p> + +<p>I had a flake.nix file to add rst2pdf, pdfpc and other tools to my shell, but the compilation to a PDF file was done in a bash script which I've since removed.</p> + +<p>Here's how my flake.nix file looks now:</p> + +<pre><code class="nix">{ + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + outputs = { nixpkgs, ... }: + let + system = "x86_64-linux"; + pkgs = import nixpkgs { inherit system; }; + + inherit (nixpkgs.lib) makeOverridable; + inherit (pkgs.stdenvNoCC) mkDerivation; + + shared = mkDerivation { + name = "talks-shared"; + src = ./src; + + installPhase = '' + runHook preInstall + + mkdir $out + cp -r fonts styles $out + + runHook postInstall + ''; + }; + + commonBuildInputs = with pkgs; [ + (python310.withPackages (p: with p; [ rst2pdf ])) + ]; + + mkTalk = makeOverridable ({ src }: mkDerivation { + inherit shared src; + + name = builtins.head (builtins.attrNames talks); + + buildInputs = commonBuildInputs; + + buildPhase = '' + runHook preBuild + + mkdir $out + + rst2pdf slides.rst \ + --break-level 1 \ + --fit-background-mode scale \ + --font-path "${toString shared}/fonts" \ + --output "$out/slides.pdf" \ + --stylesheets bw,"${toString shared}/styles/opdavies-light" + + runHook postBuild + ''; + }); + + talks = { + build-configs = mkTalk { src = ./src/building-build-configs; }; + sculpin = mkTalk { src = ./src/building-static-websites-sculpin; }; + tailwind-css = mkTalk { src = ./src/taking-flight-with-tailwind-css; }; + test-driven-drupal = mkTalk { src = ./src/test-driven-drupal; }; + }; + in + { + devShells.${system}.default = with pkgs; mkShell { + packages = with pkgs; commonBuildInputs ++ [ + ghostscript + just + pdfpc + texliveMedium # includes pdfjam + ]; + }; + + packages.${system} = { + inherit shared; + } // talks; + }; +} +</code></pre> + +<p>Each talk is its own derivation, so I can run <code>nix run .#test-driven-drupal</code> and it will generate the appropriate PDF file for me to present or share.</p> + +<p>The source code is available at <a href="https://code.oliverdavies.uk/opdavies/talks">https://code.oliverdavies.uk/opdavies/talks</a> if you want to see how I use rst2pdf to create my presentations and I've even <a href="http://localhost:8000/presentations/building-presenting-slide-decks-rst2pdf">given a presentation about how I create presentations</a>.</p> +
+
+ Mon, 07 Apr 2025 00:00:00 GMT +
+ + Caching with decorators + /daily/2025/04/06/caching + http://localhost:8000/daily/2025/04/06/caching + +
<p>As well as <a href="http://localhost:8000/daily/2025/04/05/strategies">working with different versions of an API</a>, I was able to use the same technique I wrote about yesterday to easily add a cacheable version of the API client.</p> + +<p>As they all implement the same <code>ApiClientInterface</code>, I can inject and decorate a client with another client, making one solely responsible for caching the result from the API whilst keeping the API interaction logic separate (aka <a href="http://localhost:8000/daily/2022/12/08/the-decorator-design-pattern">the Decorator design pattern</a>).</p> + +<p>Here's an example based on the code I wrote:</p> + +<pre><code class="php">final class CacheableApiClient implements ApiClientInterface { + + /** + * The cache duration in seconds. + */ + private const CACHE_DURATION = 3600; + + public function __construct( + private readonly ApiClientInterface $client, + private readonly TimeInterface $time, + private readonly CacheBackendInterface $cache, + ) { + } + + public function getResults(): Collection { + $key = $this-&gt;getCacheKey(); + + $cache = $this-&gt;cache-&gt;get($key); + + if ($cache !== FALSE) { + return $cache-&gt;data; + } + + $result = $this-&gt;client-&gt;getResults(); + + $this-&gt;cache-&gt;set( + cid: $key, + data: $result, + expire: $this-&gt;time-&gt;getRequestTime() + self::CACHE_DURATION, + ); + + return $result; + } + +} +</code></pre> + +<p>Nothing in this instance is specific to either version of the API.</p> + +<p>This client is only concerned with retrieving and saving cache data, and delegating any other logic to the original version.</p> + +<p>With this approach, I can switch between <code>V1ApiClient</code>, <code>V2ApiClient</code> or any other version with the same methods without having to reimplement caching as that's handled within the <code>CacheableApiClient</code>.</p> + +<p>But what if I don't want to interact with the API at all?</p> + +<p>For local development, I have a <code>FakeApiClient</code> that returns a static response that I can work with.</p> + +<p>The possibilities are endless.</p> +
+
+ Sun, 06 Apr 2025 00:00:00 GMT +
+ + Dealing with different API versions + /daily/2025/04/05/strategies + http://localhost:8000/daily/2025/04/05/strategies + +
<p>I've recently developed a new custom module for a Drupal project.</p> + +<p>It pulls in an initial set of data from an API, builds a block that includes a form, makes another API request based on the form selections and displays the result to the user.</p> + +<p>Part of the API base URL is <code>v1</code> so I assume that, at some point, there could be breaking changes in the API response and the endpoint will change to be <code>v2</code>.</p> + +<p>If this happens, I don't to have to change the existing code to make it work with a new version of the API.</p> + +<p>I want to be able to write new code to work with the new version.</p> + +<p>For the version 1 of the API, I've written a <code>V1ApiClient</code> that makes and processes the response from the v1 endpoint.</p> + +<p>If we move to version v2, I'll write a <code>V2ApiClient</code> that will work with the version 2 response.</p> + +<p>Each implementation will have its own logic but implement a <code>ClientInterface</code> to ensure both have the same required methods to make them interchangeable.</p> + +<p>Then I can switch between implementations as needed and not have to change the existing code.</p> + +<p>Similar to feature toggles, this allows me to deploy new code without making it immediately active.</p> + +<p>I don't need to hold back merging changes until I'm ready to change the API version.</p> + +<p>The same approach works in other situations, like payment gateways.</p> + +<p>What if you need to switch to a different payment gateway or map service?</p> + +<p>Instead of tightly coupling to one implementation, creating different implementations and strategies makes the code more flexible and easier if you need to change implementations or support multiple implementations at once.</p> +
+
+ Sat, 05 Apr 2025 00:00:00 GMT +
+ + Writing good commit messages + /daily/2025/04/04/good + http://localhost:8000/daily/2025/04/04/good + +
<p>There are many good resources and interesting articles online about how to write good messages when committing changes to a Git repository.</p> + +<p>The post I often refer to is <a href="https://cbea.ms/git-commit">How to Write a Git Commit Message</a> by Chris Beams.</p> + +<p>In his post, he explains why good commit messages matter and gives these seven rules:</p> + +<blockquote> + <ul> + <li>Separate the subject from body with a blank line.</li> + <li>Limit the subject line to 50 characters.</li> + <li>Capitalize the subject line.</li> + <li>Do not end the subject line with a period.</li> + <li>Use the imperative mood in the subject line.</li> + <li>Wrap the body at 72 characters.</li> + <li>Use the body to explain what and why vs. how.</li> + </ul> +</blockquote> + +<p>I'd recommend reading the article to get the full context.</p> + +<p>Rules two and six suggest lengths for the subject line and body which is another reason <a href="http://localhost:8000/daily/2025/04/02/commit">why I rarely use <code>-m</code></a> when committing changes.</p> + +<p>Whilst you can create multi-line commit messages on the command line, by opening it in my preferred editor (Neovim for me), I can see where the lines should end and be warned if I exceed them.</p> + +<p>I can even include Chris' rules in my commit message template so I see them whenever I'm about to commit something.</p> + +<p>This additional feedback helps me create my commit messages how I intend.</p> +
+
+ Fri, 04 Apr 2025 00:00:00 GMT +
+ + Be more selective + /daily/2025/04/03/selective + http://localhost:8000/daily/2025/04/03/selective + +
<p>Another common Git issue I see is people using <code>git add .</code> to commit every change in every file they have locally.</p> + +<p>Similar to <a href="http://localhost:8000/daily/2025/04/02/commit">committing with <code>-m</code></a>, this seems to be a common in Git tutorials, but can have consequences due to unexpected changes being staged and committed.</p> + +<p>Maybe there are unrelated changes in the same file or other files have been changed that you don't want to commit yet.</p> + +<p>What if something was committed and pushed that caused the CI pipeline to fail or break production?</p> + +<p>At the least, it's going to add time and delay getting the intended changes live as someone will need to revert and fix the commits or address the changes in a code review.</p> + +<p>I'm very selective about what I include in each commit to keep my code stable and the commits easy to review and, if needed, revert.</p> + +<p>I always use <code>git add -p</code> to interactively stage changes from the command line or use keybindings in my Neovim configuration to add particular lines.</p> + +<p>I'll also review my staged changes before committing and the commit once it's been made using <code>git log --stat</code> to see what's included.</p> + +<p>Only once I'm sure my commits include only what I intended will I push them or submit them for review.</p> +
+
+ Thu, 03 Apr 2025 00:00:00 GMT +
+ + Dont commit changes with `-m` + /daily/2025/04/02/commit + http://localhost:8000/daily/2025/04/02/commit + +
<p>A common thing I see when reading posts or watch videos where people are using Git is using the <code>-m</code> option when committing changes.</p> + +<p><code>-m</code> allows you to specify the commit message inline or, more specifically, the first line of the commit message.</p> + +<p>If you think of a commit message as an email, the first line is the subject line which is followed by the body of the message.</p> + +<p>If you don't use <code>-m</code>, Git will open an editor and you can type the full commit message into a file and save it.</p> + +<p>This includes the subject line and, more importantly, the body of the message where you can include as much additional information as you want.</p> + +<p>The subject line summarises the change, but the body can be used to explain why it was needed.</p> + +<p>You can describe the issue or requirements in more detail (don't just link to the issue or enter the issue number).</p> + +<p>You can describe any other approaches you considered or tried.</p> + +<p>You can describe any anticipated effects or consequences of this commit, any manual deployment steps or follow up tasks that will need to be created.</p> + +<p>You can include any additional information you were aware of at the time of making the commit that could be useful to yourself or others in the future.</p> + +<p>Think what information would you like to see when you next run <code>git log</code>.</p> +
+
+ Wed, 02 Apr 2025 00:00:00 GMT +
+ + Automated tests reduce debugging time + /daily/2025/04/01/debugging + http://localhost:8000/daily/2025/04/01/debugging + +
<p>In my <a href="http://localhost:8000/presentations/tdd-test-driven-drupal">talk about automated testing and test-driven development</a>, I speak about a custom module I wrote for a client's Drupal project.</p> + +<p>It was to integrate with a third-party job application system that they would log into and enter information via a form (similar to Drupal's node forms) that they wanted to appear on the Drupal website.</p> + +<p>Once submitted, the system would send a POST request to an endpoint provided by the custom module. The module would process it and create the job node with the appropriate field values.</p> + +<p>I wrote automated tests and did test-driven development.</p> + +<p>Everything worked great for a few weeks or months.</p> + +<p>Later, we had a message from the client saying the integration was broken and we needed to fix it.</p> + +<p>I don't think I'd worked on this module for a while, but I was concerned that a Drupal core update or another change could have caused a regression.</p> + +<p>The first thing I did was run the tests I'd written and verify they still passed, which they did.</p> + +<p>This confirmed that as long as the data was being sent in the expected format, the node would be created and the integration would work.</p> + +<p>Due to an upstream issue, the data was no longer in the expected format, which meant the node could not be created.</p> + +<p>Once it was fixed, everything started working again.</p> + +<p>My debugging time was practically zero as I was able to rely on my tests and that they were still passing.</p> + +<p>I could confidently tell the client that the issue wasn't with our code and must've been an third-party issue.</p> + +<p>Writing tests takes some time, but having tests saves time.</p> +
+
+ Tue, 01 Apr 2025 00:00:00 GMT +
+ + First pull request submitted to nixpkgs + /daily/2025/03/31/nixpkgs + http://localhost:8000/daily/2025/03/31/nixpkgs + +
<p>I've been using the Nix package manager for some time.</p> + +<p>The first commit of a flake.nix file to my <a href="https://code.oliverdavies.uk/opdavies/nixos-config">nixos-config repository</a> (formerly my dotfiles repository) was on the 26th of September 2022.</p> + +<p>The following month, I started to experiment with NixOS - the operating system built on the Nix language and the package manager.</p> + +<p>Rarely have I found a package missing within the 120,000+ available, but I have added some missing Vim plugins and some custom packages to my own configuration.</p> + +<p>But I hadn't tried to contribute any package definitions back to nixpkgs until today.</p> + +<p>I've submitted <a href="https://github.com/NixOS/nixpkgs/pull/395028">my first pull request to nixpkgs</a> to add a small script that I've added to my own configuration.</p> + +<p>It's always nerve-racking contributing to a new projects, but let's see how it goes.</p> + +<p>I'm thinking of it as <a href="http://localhost:8000/daily/2024/05/14/free-code-reviews">a free code review</a>.</p> + +<p>If it goes well, I can see me contributing more packages in the future.</p> +
+
+ Mon, 31 Mar 2025 00:00:00 GMT +
+ + Why use Collections? + /daily/2025/03/30/why-collections + http://localhost:8000/daily/2025/03/30/why-collections + +
<p>Yesterday, I wrote how to create <a href="http://localhost:8000/daily/2025/03/29/collections">dependency free Collection classes in PHP</a> (thanks to Dan Leech).</p> + +<p>I said that <a href="http://localhost:8000/blog/using-laravel-collections-drupal">I've written blog posts</a> and <a href="http://localhost:8000/presentations/using-illuminate-collections-outside-laravel">given talks</a> on using Collection classes.</p> + +<p>But why do I like Collections and why may you want to use them instead of native arrays?</p> + +<p>The first reason is that I can add extra functionality to Collections, because they're objects.</p> + +<p>Whether it's a generic action such as filtering or sorting the items, or something more specific like returning a list of station codes from a collection of train stations, this can be added to specific collection classes.</p> + +<p>I'll usually have an <code>AbstractCollection</code> that has the generic methods and is extended by specific Collection types with methods more specific methods.</p> + +<p>Having specific types of Collection objects also gives my code more context.</p> + +<p>Instead of an array that could contain anything, by reading the code and seeing which Collection types are used, I know what the collection contains and what I can do with it.</p> + +<p>This is also why I like value objects.</p> + +<p>Giving objects specific names instead of relying on the language's primitive types makes the code more robust and easier to read and understand.</p> +
+
+ Sun, 30 Mar 2025 00:00:00 GMT +
+ + Dependency-free PHP Collections + /daily/2025/03/29/collections + http://localhost:8000/daily/2025/03/29/collections + +
<p>A few months ago, <a href="http://localhost:8000/podcast/6-dan-leech-php-tui">Beyond Blocks podcast guest Dan Leech</a> give another talk at the PHP South West user group.</p> + +<p>This talk was about <a href="https://www.youtube.com/watch?v=UpyVAcWGQhg">building an expression language from scratch</a>, but in one part of the talk, Dan showed how he creates Collection classes.</p> + +<p>I use Collection classes a lot, have <a href="http://localhost:8000/presentations/using-illuminate-collections-outside-laravel">given talks about them</a> and <a href="https://www.drupal.org/project/collection_class">wrote a Collections module for Drupal 7</a>.</p> + +<p>In Dan's talk, instead of using a Collection from Laravel or Doctrine, <a href="https://github.com/dantleech/onehourexpr/blob/8c4ee4a3c1680455118e16a3e1be2a08418ab207/src/Tokens.php">he created his own</a>.</p> + +<p>His extended PHP's <code>IteratorAggregate</code> class so had no external dependencies.</p> + +<p>He could then add whatever additional methods and functionality he needed.</p> + +<p>Looking at other repositories on GitHub, I was able to find other examples.</p> + +<p>I've done this on projects since Dan's talk and like taking the minimalist approach - avoiding adding a dependency to my project and only adding the functionality I need and functionality that's more specific to my domain.</p> +
+
+ Sat, 29 Mar 2025 00:00:00 GMT +
+ + Work in sprints, deploy continuously + /daily/2025/03/28/continuous + http://localhost:8000/daily/2025/03/28/continuous + +
<p>Agile and Scrum have become standard approaches in software development.</p> + +<p>Work is planned and organised into iterations/cycles/sprints, usually lasting two weeks.</p> + +<p>As one sprint is being worked on, the next is usually already planned and the following one is being discussed.</p> + +<p>It's common for code deployments and releases to follow the same pattern.</p> + +<p>When a sprint is finished, the changes are released.</p> + +<p>But that means if a task is worked on at the start of the sprint, it won't be available for at least two weeks.</p> + +<p>It may be longer if the sprint is longer or if there are steps like manual testing that also happen.</p> + +<p>What if a change is needed or a bug is found?</p> + +<p>Is that going to be at least another two weeks before it can be addressed?</p> + +<p>Do you need to start using hotfixes and dealing with multiple branches and <a href="http://localhost:8000/daily/2025/02/18/conflicts">merge conflicts</a>?</p> + +<p>I suggest separating your planning and work schedules from your deployments.</p> + +<p>Deploy as often as possible, even during a sprint.</p> + +<p>You want your feedback loop to be as small and quick as possible.</p> + +<p>But, importantly, <a href="http://localhost:8000/daily/2023/06/21/deployments-or-releases">deploying code is different to releasing features</a>.</p> + +<p>If you need to do manual testing, use feature flags to <a href="http://localhost:8000/daily/2022/12/07/separating-releases-from-deployments-with-feature-flags">separate deploying the code from releasing the feature</a>.</p> + +<p>Then, when it's ready to go live, you only need to enable the feature flag - no code deployment needed.</p> +
+
+ Fri, 28 Mar 2025 00:00:00 GMT +
+ + Drupal test writer for hire + /daily/2025/03/27/for-hire + http://localhost:8000/daily/2025/03/27/for-hire + +
<p>There are a lot of <a href="https://www.drupal.org/project/issues/search?status[]=1&amp;status[]=13&amp;issue_tags_op=%3D&amp;issue_tags=Needs+tests">open issues on Drupal.org</a> that have the "Needs tests" label.</p> + +<p>These could be maintainers leaving themselves reminders to add tests once they've finished spiking out the first version of their code, or someone contributing to project but needs an accompanying test to ensure the feature works or that a bug is fixed (and will stay fixed).</p> + +<p>There are currently 3,711 issues across all projects and 2,030 in Drupal core.</p> + +<p>If one of those is your issue or module, I can write the tests you need.</p> + +<p>After my <a href="http://localhost:8000/podcast/11-mark-conroy">first podcast episode with Mark Conroy</a>, I did this for <a href="https://www.drupal.org/project/content_access_by_path/issues/3428680">the Content Access by Path module</a>, which now has tests and automated checks with GitLab CI - so they will be run for every future merge request.</p> + +<p>You can see me writing them on <a href="https://www.youtube.com/watch?v=XTpliKd47Lg">a previous live stream</a>.</p> + +<p>If you need tests written or help writing them yourself, reply and get in touch.</p> + +<p>If you want to <a href="http://localhost:8000/sponsor">sponsor my contribution time</a>, you'll also get contribution credit for any issues or merge requests I work on during that time and, whilst it's been a while since my last live stream, you'd also be mentioned there as I work on these issues.</p> + +<p>I have around a day a week available for sponsored contribution time.</p> + +<p>If you're interested, reply and let's get it arranged.</p> +
+
+ Thu, 27 Mar 2025 00:00:00 GMT +
+ + Don't repeat yourself + /daily/2025/03/26/repeat + http://localhost:8000/daily/2025/03/26/repeat + +
<p>When most people think of "Don't repeat yourself" or DRY, they probably think about not duplicating logic in code.</p> + +<p>If you've written some functionality once, you should avoid writing it again.</p> + +<p>I was recently browsing the code for an open source package and saw this:</p> + +<pre><code class="php">/** + * Flush everything. + */ +public function flush(): void; + +/** + * Sets the formatter. + */ +public function setFormatter(FormatterInterface $formatter): void; + +/** + * Gets the formatter. + */ +public function getFormatter(): FormatterInterface; +</code></pre> + +<p>This is another instance of repetition.</p> + +<p>The docblocks are just repeating what the code already tells me.</p> + +<p>I can understand from the method names what each function does, and I can see what parameters they have and their types.</p> + +<p>I can see if each method returns anything and, if so, what type it returns - e.g. <code>getFormatter</code> returns a <code>FormatterInterface</code>.</p> + +<p>I think these docblocks aren't needed and in my projects, would suggest they be removed.</p> + +<p>Unless they're adding more information, such as <a href="http://localhost:8000/daily/2025/03/21/phpdoc">PHPStan PHPDoc types</a>, there's no need to repeat what the code already says.</p> +
+
+ Wed, 26 Mar 2025 00:00:00 GMT +
+ + Building Bootstrap components with Tailwind CSS + /daily/2025/03/25/bootstrap + http://localhost:8000/daily/2025/03/25/bootstrap + +
<p>A while ago, I started to build <a href="https://bootstrap-with-tailwind.oliverdavies.uk">some of Bootstrap's example components</a> with Tailwind CSS.</p> + +<p>Because it's a lower-level framework with less-opinionated classes, you can use Tailwind classes to make different looking UIs.</p> + +<p>There is no "Tailwind-looking website" as there is with component-level frameworks like Bootstrap and Bulma.</p> + +<p>You can also see this in the <a href="http://localhost:8000/blog/uis-ive-rebuilt-tailwind-css">other UIs I've rebuilt with Tailwind</a> - some of which I show in my <a href="http://localhost:8000/presentations/sculpin">Taking Flight with Tailwind CSS talk</a>.</p> + +<p>I've created the Album and Pricing card examples so far, but may do more soon.</p> + +<p>I've recently ported this website to be <a href="http://localhost:8000/presentations/sculpin">a Sculpin-powered website</a>, so if you want to see another Sculpin example, you can take a look <a href="https://code.oliverdavies.uk/opdavies/bootstrap-with-tailwind">at the source code</a>.</p> +
+
+ Tue, 25 Mar 2025 00:00:00 GMT +
+ + Covering icky code with automated tests + /daily/2025/03/24/icky + http://localhost:8000/daily/2025/03/24/icky + +
<p>Every codebase has "icky" code.</p> + +<p>Code that works but is difficult to read and understand, that most people will avoid working on.</p> + +<p>It could be fragile and occasionally return different results or error.</p> + +<p>It could be a suboptimal implementation.</p> + +<p>I mention in <a href="http://localhost:8000/presentations/tdd-test-driven-drupal">my test-driven drupal talk</a> when I wrote some code that worked locally but didn't work because of the hosting setup and I had to rewrite the code in a different and less optimal way.</p> + +<p>How do you build confidence around this code?</p> + +<p>Write more automated tests around it.</p> + +<p>This will make it easier to understand what the code does as the tests will act like examples and, as you find situations where the code can break, you can write tests to ensure it works as expected once fixed and will continue to work.</p> + +<p>Once there are tests, the code will be easier to add to, change, refactor, read and understand.</p> +
+
+ Mon, 24 Mar 2025 00:00:00 GMT +
+ + Legacy code is anything older than... + /daily/2025/03/22/legacy + http://localhost:8000/daily/2025/03/22/legacy + +
<p>How do you define legacy code?</p> + +<p>Code that was written by someone else?</p> + +<p>Code that doesn't have tests?</p> + +<p>Any code that has been released to production?</p> + +<p>Code that's more than a day old?</p> + +<p>In a talk I recently watched, the speaker suggested that any code written more than thirty minutes ago is legacy code.</p> + +<p>Once you've written some code and left it for half an hour, you need to re-read it to remember and re-learn what the code does and decide how you want to implement your next requirement.</p> + +<p>This is the same approach for code that was written longer ago or written by someone else.</p> + +<p>What do you think?</p> +
+
+ Sat, 22 Mar 2025 00:00:00 GMT +
+ + Extra PHPDoc types with PHPStan + /daily/2025/03/21/phpdoc + http://localhost:8000/daily/2025/03/21/phpdoc + +
<p>Here are some examples of PHP code from Drupal core:</p> + +<pre><code class="php">/** + * The weight of this role in administrative listings. + * + * @var int + */ +protected $weight; +</code></pre> + +<pre><code class="php">/** + * Path of the image file. + * + * @var string + */ +protected $source = ''; +</code></pre> + +<pre><code class="php">/** + * Alter the list of mail backend plugin definitions. + * + * @param array $info + * The mail backend plugin definitions to be altered. + */ +</code></pre> + +<p>These use some of the standard PHPDoc types of <code>int</code>, <code>string</code> and <code>array</code>.</p> + +<p>Although they are comments, docblocks are checked by static analysis tools like PHPStan to parse the code and report any potential errors.</p> + +<p>If you want to go deeper, PHPStan has <a href="https://phpstan.org/writing-php-code/phpdoc-types">its own PHPDoc types</a> that you can use to add more information and context.</p> + +<p>Instead of specifying an argument must be a <code>string</code>, you can specify it's a <code>non-empty-string</code> or a <code>class-string</code>.</p> + +<p>You can specify whether an integer is a <code>positive-int</code> or <code>negative-int</code>, or within a certain range.</p> + +<p>You can define the shape of an array or object, whether an array is empty, or the types of keys and values in an array.</p> + +<p>All of this is used by PHPStan when analysing the code and will give better results and find more potential bugs before anyone else does.</p> +
+
+ Fri, 21 Mar 2025 00:00:00 GMT +
+ + No-one is paying us to... + /daily/2025/03/19/effective + http://localhost:8000/daily/2025/03/19/effective + +
<p>A great quote I recently heard whilst watching a conference talk was "No-one is paying us to do <em>something</em>, but we need to do it to work effectively".</p> + +<p>This talk was about decoupling, but the same quote could be re-used for various topics.</p> + +<p>A common one is automated tests and test-driven development.</p> + +<p><a href="http://localhost:8000/daily/2025/02/23/line-item">Tests aren't a line item</a> that should be billed for separately and clients and customers just want their software to work.</p> + +<p>Generally, they aren't concerned that there are automated tests or whether the tests were written before the code.</p> + +<p>If I need to give an estimate, I always include time to write automated tests.</p> + +<p>It's part of my software development process.</p> + +<p>Whether people know they're paying for me to write automated tests as part or not, I can work more effectively when I have tests and am doing test-driven development.</p> + +<p>Both when writing the code, but also if it needs to be <a href="http://localhost:8000/daily/2024/10/20/test-then-refactor">refactored or changed in the future</a>.</p> +
+
+ Wed, 19 Mar 2025 00:00:00 GMT +
+ + Archiving Drupal websites as static websites + /daily/2025/03/18/archiving + http://localhost:8000/daily/2025/03/18/archiving + +
<p>Static websites can be created by writing each file by hand or using a tool like <a href="http://localhost:8000/presentations/sculpin">a static site generator</a>.</p> + +<p>But what if you've already got a dynamic website that you no longer need to be editable?</p> + +<p>What if it was for an event that has passed, like a DrupalCamp?</p> + +<p>If you no longer need to update the content via the admin UI, you could archive it by converting it to a static website.</p> + +<p>Then you no longer need to maintain and update it, and <a href="http://localhost:8000/daily/2025/03/13/deploy">simplify your hosting environment</a>.</p> + +<p>You could use <a href="http://localhost:8000/daily/2025/03/15/tome">Tome, a static site generator for Drupal</a>, or use command line tools like <code>wget</code> with options like <code>--mirror</code> to create a static version by crawling a live website.</p> + +<p>There are <a href="https://www.drupal.org/docs/administering-a-drupal-site/creating-a-static-archive-of-a-drupal-site">a few options on Drupal.org</a>, which will also work with other CMSes and frameworks.</p> + +<p>This is what I've done for old websites like our old DrupalCamp Bristol websites.</p> + +<p>That means they're still available for people to see, but without the maintenance and security overhead.</p> +
+
+ Tue, 18 Mar 2025 00:00:00 GMT +
+ + An example of feature flagging + /daily/2025/03/17/feature-flags + http://localhost:8000/daily/2025/03/17/feature-flags + +
<p>I've written a number of emails about feature flags, or feature toggles, and am a strong advocate of using them whilst developing new features.</p> + +<p>I've used them a couple of times recently on my website so I wanted to share them as examples.</p> + +<h2 id="experimenting-with-presentation-layouts">Experimenting with presentation layouts</h2> + +<p>Firstly, I wanted to experiment with a different layout for my presentation pages.</p> + +<p>They currently have a list of events, embedded slides and a video recording when there is one.</p> + +<p>Each event linked to its website, where applicable, and the slides and video were from one of the most recent versions of the presentation.</p> + +<p>I wanted to change this so each event would have links to its own slides, example code or demo.</p> + +<p>I didn't want to change this yet for all presentations, only <a href="http://localhost:8000/presentations/sculpin">my Sculpin talk</a> as it's the most recent and, if I like it, later apply it to the others.</p> + +<p>My website is built with Sculpin, so adding a feature flag was as simple as adding <code>new: true</code> to the YAML front matter at the top of the file for that presentation.</p> + +<p>This is available as <code>page.new</code> in the layout file and I can use this <a href="https://code.oliverdavies.uk/opdavies/oliverdavies.uk/commit/8b721e63fb64f3c98b81353ca9cec7545d72a595">to load different markup</a>.</p> + +<h2 id="rewriting-my-css">Rewriting my CSS</h2> + +<p>Secondly, I've been wanting to re-style my website with Tailwind CSS 4 and refactor some of the templating.</p> + +<p>As this is a change I wanted to be site-wide, I added <code>new_css: true</code> to my sculpin_site.yml file.</p> + +<p>This time, I was able to use <code>site.new_css</code> to <a href="https://code.oliverdavies.uk/opdavies/oliverdavies.uk/commit/fa884644cf5aa233ad22fb28b83c5a0b150b037d">toggle the loaded stylesheet</a> and using Sculpin's environment files - e.g. sculpin_site_dev.yml and sculpin_site_prod.yml - I can be explicit about which stylesheets are used locally and for my live website.</p> + +<h2 id="summary">Summary</h2> + +<p>Feature flags are a great approach to splitting up large changes into manageable, deployable pieces, and they don't need to be complicated.</p> + +<p>Essentially, they are a simple boolean value that you can use to execute different code based on whether it's false or true.</p> +
+
+ Mon, 17 Mar 2025 00:00:00 GMT +
+ + What's the correct way to add PHPStan to an existing codebase? + /daily/2025/03/16/what-s-the-correct-way-to-add-phpstan-to-an-existing-codebase + http://localhost:8000/daily/2025/03/16/what-s-the-correct-way-to-add-phpstan-to-an-existing-codebase + +
<p>PHPStan is a static analysis tool for PHP.</p> + +<p>It finds potential issues in PHP code without needing to run it, so Developers can find and resolve potential issues sooner.</p> + +<p>I use it on all my projects including existing ones I've inherited.</p> + +<p>But how can you add a static analysis tool to a codebase without getting a lot of errors from the existing code?</p> + +<p>PHPStan has different levels of strictness.</p> + +<p>Level 0 is the least strict and each level adds more rules and strictness, resulting in more errors.</p> + +<p>Most of the time, people will start by running PHPStan on level 0, fixing any errors and committing the changes.</p> + +<p>Then repeat the process as many times as needed until you reach the level you want to achieve.</p> + +<p>I don't think this is the right approach.</p> + +<p>This could mean that you need to edit the same files multiple times as you work through the levels.</p> + +<p>There's also a period of time where Developers can still write suboptimal code whilst you work your way up to your desired level.</p> + +<p>Another approach is to use a feature of PHPStan called the baseline.</p> + +<p>The baseline is a way of capturing and saving all the existing errors up to the selected level so they are no longer reported.</p> + +<p>If you did this for an existing project, it would return no errors as everything would be included in the baseline.</p> + +<p>Once you decide what level you want your project to run, you can start as soon as the baseline is generated and without needing to change files multiple times.</p> + +<p>Instead of spending time working through the levels one at a time, commit some time to pruning the baseline and reducing the errors in it.</p> + +<p>This I think is a better approach and how I add PHPStan to existing codebases.</p> + +<p>To learn more about static analysis and PHPStan, listen to <a href="http://localhost:8000/podcast/22-dave-liddament">episode 22 of the Beyond Blocks podcast</a> with Dave Liddament.</p> +
+
+ Sun, 16 Mar 2025 00:00:00 GMT +
+ + Building static websites with Drupal + /daily/2025/03/15/tome + http://localhost:8000/daily/2025/03/15/tome + +
<p>Over the last few days, I've written a few emails about static websites and static site generators before I <a href="http://localhost:8000/presentations/sculpin">speak about Sculpin</a> at PHP Thames Valley.</p> + +<p>They are <a href="http://localhost:8000/daily/2025/03/12/easy">easy to build</a> and deploy and <a href="http://localhost:8000/daily/2025/03/13/deploy">cheap to host</a>.</p> + +<p>As a PHP Developer, I use Sculpin as it uses Symfony components, is extendable by writing my own PHP code and uses Twig for templating.</p> + +<p>As a Drupal Developer, I like the power of Drupal's out of the box functionality and additional modules like Pathauto and Redirect, but I'd like to deploy it as a static website for it to be faster and more secure.</p> + +<p>Enter Tome, <a href="https://www.drupal.org/project/tome">a Drupal module to create static websites</a>.</p> + +<p>You build the website the same as you would locally and export the content and files to a static website - the same you would generate with a static site generator like Sculpin or writing the files by hand.</p> + +<p>Sam Mortenson (the creator of Tome) and I discussed it on <a href="http://localhost:8000/podcast/19-sam-mortenson">episode 19 of the Beyond Blocks podcast</a>.</p> + +<p>So, if you like Drupal and the benefits of static sites, Tome may be a good solution for you.</p> +
+
+ Sat, 15 Mar 2025 00:00:00 GMT +
+ + Static websites are easy to backup + /daily/2025/03/14/backup + http://localhost:8000/daily/2025/03/14/backup + +
<p>As well as being <a href="http://localhost:8000/daily/2025/03/12/easy">easy to build</a> and <a href="http://localhost:8000/daily/2025/03/13/deploy">simple to deploy</a>, static websites are easy to backup and, if needed, restore.</p> + +<p>I backup several static websites from my server using rsync - the same command I use to deploy them.</p> + +<p>rsync is fast as it only downloads files that have changed, so backing up several websites only takes a few seconds.</p> + +<p>There are are no databases to worry about - all I need to do is backup the static files themselves.</p> + +<p>Running the backups is also easy.</p> + +<p>I have a scheduled cron job that downloads everything from the <code>/var/www/vhosts</code> directory on my server and creates a local copy.</p> + +<p>If I need to restore from a backup or migrate to a different server, I just run the appropriate rsync command to re-upload them - the same as how I deployed them originally.</p> +
+
+ Fri, 14 Mar 2025 00:00:00 GMT +
+ + Static websites are easy to host and deploy + /daily/2025/03/13/deploy + http://localhost:8000/daily/2025/03/13/deploy + +
<p>Another reason I like static websites is that they're easy and quick to deploy.</p> + +<p>Whether you use write each HTML file by hand or <a href="http://localhost:8000/daily/2025/03/12/easy">use a static site generator</a>, a simple Web server like Caddy, Nginx or Apache can load and serve your website for everyone to see.</p> + +<p>My Sculpin website generates an output_prod directory after I run <code>sculpin generate</code> with my deployable files.</p> + +<p>I manage my own server with NixOS that hosts a number of static websites, such as examples from talks and blog posts.</p> + +<p>To upload my files onto the server, I just use rsync - a small command line tool to synchronise files between computers.</p> + +<p>It's a single command to upload the contents of my output_prod directory to the directory on my server.</p> + +<p>No complex CI pipelines or database migrations.</p> + +<p>It's fast, simple and minimal.</p> + +<p>If you prefer to use a service like Netlify or Vercel, they work great for static websites too.</p> +
+
+ Thu, 13 Mar 2025 00:00:00 GMT +
+ + Static websites are easy to build + /daily/2025/03/12/easy + http://localhost:8000/daily/2025/03/12/easy + +
<p>Static websites are the easiest way to build websites.</p> + +<p>You create an index.html file, type some words, open the file in a browser and you'll see the words you entered.</p> + +<p>You built a website!</p> + +<p>Then you can create any more pages you need and style it with CSS.</p> + +<p>This how I built my first website, for a Tae Kwon-Do school I used to train at.</p> + +<p>This worked great, but at some point, becomes hard to scale.</p> + +<p>What if you want to add a new link to your navigation menu? You'd need to update each HTML page separately.</p> + +<p>At this point, I started to learn about PHP and MySQL, and then Drupal.</p> + +<p>Static site generators like Sculpin, Jekyll and Hugo also fix this problem.</p> + +<p>They allow you to write HTML files with a template language like Twig and use includes, loops and conditions to make your files easier to create and maintain with a language and tools you're familiar with.</p> + +<p>It still generates a static website with HTML files, but in a more maintainable way.</p> + +<p>As a PHP Developer, I like Sculpin but also like Tome to export a Drupal website to static HTML.</p> +
+
+ Wed, 12 Mar 2025 00:00:00 GMT +
+ + You can deploy on Fridays + /daily/2025/03/11/friday + http://localhost:8000/daily/2025/03/11/friday + +
<p>Does your team have a "No deploy Friday" policy?</p> + +<p>What about not deploying after a certain time in the afternoon?</p> + +<p>These approaches are attempts to minimise risk when deploying.</p> + +<p>If there is an issue, will someone be available during the evening or weekend to resolve it?</p> + +<p>To me, this indicates the deployment process is too complicated, possibly due to a lack of automation, or deployments aren't happening frequently enough.</p> + +<p>Having a <a href="http://localhost:8000/daily/2025/01/30/gatekeeper">robust and passing CI pipeline</a> that runs automated checks and tests is crucial to know the code is deployable.</p> + +<p><a href="http://localhost:8000/daily/2023/09/28/feature-flags-enable-continuous-integration">Feature flags are a great way</a> to separate deploying code from releasing changes to users, which means you don't need to avoid pushing some code until the change is complete. It can be done incrementally and released over several deployments.</p> + +<p>Too much time between deployments is a smell.</p> + +<p>The more time there is between a deployment and the larger the changeset, the riskier the deployment will be.</p> + +<p>There is more to go wrong and it'll be harder to diagnose and resolve any issues.</p> + +<p>I always advocate for many smaller releases than larger less frequent ones.</p> + +<p>Ideally, a production release every day - even if the changes are small or everything is hidden behind feature flags.</p> + +<p>Deploying on Friday is easy if you last deployed on Thursday.</p> +
+
+ Tue, 11 Mar 2025 00:00:00 GMT +
+ + Contrib-first doesn't mean building for every use case + /daily/2025/03/10/contrib-first + http://localhost:8000/daily/2025/03/10/contrib-first + +
<p>Most Drupal projects include writing custom code to add functionality required for that project or that doesn't yet exist.</p> + +<p>Some of this may be identified as code that could be contributed back to the Drupal community and uploaded to Drupal.org as reusable code for others to use.</p> + +<p>Usually, this involves "cleaning up" the code to make it ready to be open sourced - ensuring it complies with coding standards, follows best practices and doesn't contain any sensitive or project-specific data.</p> + +<p>Unfortunately, this doesn't always happen as the next project or task is waiting to be started, and the code is never contributed.</p> + +<p>I like to do contrib-first or contrib-driven development, where the code is open sourced upfront, developed in the open and used on the project as I'm developing it.</p> + +<p>A common argument to this approach is that it takes too much time.</p> + +<p>I assume that's because people think I need to cover every use case and situation in the module because I'm open sourcing it.</p> + +<p>That's not true.</p> + +<p>When I wrote modules like <a href="https://www.drupal.org/project/system_user">System User</a>, <a href="https://www.drupal.org/project/null_user">Null User</a> and <a href="https://www.drupal.org/project/private_message_queue">Private Message Queue</a>, I wrote the same code I'd have written if I was going to contribute it later - but I didn't need to clean it up afterwards.</p> + +<p>I didn't need to ensure it didn't contain anything I'd need to remove.</p> + +<p>It wasn't a big task to open source them as they were already open sourced.</p> + +<p>If other people want to use the module and need additional features, they could open an issue, submit a patch or create their own patches.</p> + +<p>Just because code has been open sourced doesn't mean it needs to do everything for everyone.</p> +
+
+ Mon, 10 Mar 2025 00:00:00 GMT +
+ + Submit your session proposal for DrupalCon Europe + /daily/2025/03/09/submit-your-session-proposal-for-drupalcon-europe + http://localhost:8000/daily/2025/03/09/submit-your-session-proposal-for-drupalcon-europe + +
<p>The call for session proposals is currently open for DrupalCon Europe, which is happening in Vienna this October.</p> + +<p>The tracks are slightly different this year.</p> + +<p>The tracks are:</p> + +<ul> +<li>Agency &amp; Business.</li> +<li>Coding &amp; Site Building.</li> +<li>Community Health.</li> +<li>Clients &amp; Industry Experience.</li> +<li>Drupal CMS.</li> +<li>InfoSec &amp; DevOps.</li> +<li>Open Web.</li> +</ul> + +<p>I'm happy to be on the Coding &amp; Site Building track team this year, having been on the Makers &amp; Builders track previously.</p> + +<p>The call for proposals is open until the 28th of April, but feel free to <a href="https://kuonicongress.eventsair.com/drupalcon-vienna-2025/session-submission-portal">get your session proposal in now</a>.</p> + +<p>For more information and blog posts from the track teams, go to <a href="https://events.drupal.org/vienna2025">https://events.drupal.org/vienna2025</a>.</p> + +<p>If you're submitting to the Coding &amp; Site Building track, I'll look forward to reading your proposal.</p> +
+
+ Sun, 09 Mar 2025 00:00:00 GMT +
+ + CSS variables everywhere + /daily/2025/03/08/variables + http://localhost:8000/daily/2025/03/08/variables + +
<p>Now <a href="http://localhost:8000/daily/2025/02/28/preprocessors">CSS supports variables</a> (aka custom properties) and <a href="http://localhost:8000/daily/2024/07/16/tailwind-css-v4--with-even-more-css">Tailwind CSS v4 is configured using CSS</a> instead of JavaScript, I've been making heavy use of CSS variables in my front-end code.</p> + +<p>I still use Tailwind to do the heavy lifting, but I can use CSS variables to extract themeable classes with variables like <code>--color-primary</code> that can change value based on a data attribute or by something else.</p> + +<p>These variables can still use Tailwind's core variables by doing <code>--color-primary: var(--color-red-500)</code>, rather than having to recreate all its colors, spacing or whatever variables I need to use.</p> + +<p>Tailwind has a arbitrary syntax to easily use CSS variables - e.g. <code>bg-(--color-primary)</code> - and you can define one-off variables with <code>[--box-spacing:30px]</code> or <code>[--box-spacing:--spacing(3)]</code> and using the standard arbitrary class syntax.</p> + +<p>CSS variables aren't specific to Tailwind CSS, so if I wasn't using Tailwind in a codebase, I'd use the new native CSS features <a href="http://localhost:8000/daily/2024/05/26/is-it-time-to-stop-writing-sass">instead of a preprocessor like Sass</a>.</p> +
+
+ Sat, 08 Mar 2025 00:00:00 GMT +
+ + Rebase and reorder + /daily/2025/03/07/rebase-and-reorder + http://localhost:8000/daily/2025/03/07/rebase-and-reorder + +
<p>Sometimes when <a href="http://localhost:8000/daily/2025/02/11/tidy">tidying my commits</a> or updating a local branch with remote changes, the order of commits changes - making them out of order in when running <code>git log</code>.</p> + +<p>I want the commits in the log to be in the correct sequential order.</p> + +<p>If not, it would be confusing if I review the commits in the future.</p> + +<p>This is easy to fix when running <code>git rebase -i</code> to perform an interactive rebase on the commits.</p> + +<p>The commit has a <code>-x</code> or <code>--exec</code> option that will perform a given command on each commit.</p> + +<p>The commit date can be reset using <code>git reset --amend</code>, and combining these commands will amend the date of each commit.</p> + +<p>Running <code>git rebase --interactive --exec "git commit --amend --no-edit --date now"</code> will amend and update each commit, keeping the commit message the same, but changing the commit date to the current time - leaving the Git log in the correct order.</p> +
+
+ Fri, 07 Mar 2025 00:00:00 GMT +
+ + Feature branching slows delivery + /daily/2025/03/05/slow + http://localhost:8000/daily/2025/03/05/slow + +
<p>As well as <a href="http://localhost:8000/daily/2025/02/18/conflicts">causing merge conflicts</a>, feature branches slow the delivery of new features.</p> + +<p>Someone needs to be responsible for merging the branches once they've been reviewed and approved, and any further merges into release branches.</p> + +<p>This is typically the Lead Developer on the project, but this person then becomes a bottleneck.</p> + +<p>They need to oversee all the branches and merges, and know what needs to be merged and where it needs to be deployed.</p> + +<p>This slows down the speed of delivery.</p> + +<p>In a trunk-based environment, there's only one branch and everyone can commit and push to it.</p> + +<p>And, if you're <a href="http://localhost:8000/daily/2025/02/17/ci-cd">doing continuous delivery</a>, any changes will be automatically deployed.</p> + +<p>No more bottleneck.</p> +
+
+ Wed, 05 Mar 2025 00:00:00 GMT +
+ + How much would it cost to build Drupal? + /daily/2025/03/03/cost + http://localhost:8000/daily/2025/03/03/cost + +
<p>Drupal comes with a lot of features available out of the box.</p> + +<p>It has a flexible system to create complex content models with different content types and fields, rich media management and Views - a visual query builder - to create lists of content.</p> + +<p>It has the JSON:API module to expose your content via an API for use by other systems, such as mobile apps.</p> + +<p>It has user management, authentication, password resets, roles and permissions.</p> + +<p>It has configuration management to easily manage settings and configuration between environments and to provide traceability.</p> + +<p>It has the Layout Builder to create page layouts with a drag and drop interface and a built-in WYSIWYG editor for entering rich content.</p> + +<p>These are just some of the features I could mention.</p> + +<p>Running cloc on Drupal's <code>core</code> directory shows 18,289 files and 1,095,970 lines of code.</p> + +<p>If you were to get someone to build a CMS from scratch with the same features, how long would that take?</p> + +<p>How much would it cost?</p> + +<p>This is what you get for free by using free and open source software like Drupal.</p> +
+
+ Mon, 03 Mar 2025 00:00:00 GMT +
+ + Solve one problem at a time + /daily/2025/03/01/one-problem + http://localhost:8000/daily/2025/03/01/one-problem + +
<p>When using <code>git log</code> to look through the history of codebases, I often see large commits that combine several changes.</p> + +<p>These also lead to vague commit messages like "Changes", "wip" or "Fixes".</p> + +<p>These aren't helpful when reviewing the history and large commits are difficult to review and revert if there is a problem.</p> + +<p>Each commit should be focused on a single change, whether its adding part of a new feature, fixing a bug or refactoring.</p> + +<p>If it's a combination, they should be split into separate commits.</p> + +<p>Each commit should have its own well-written commit message that explains why the change was needed, any consequences or manual deployment steps, alternative approaches that were tried, issues encountered and any follow up actions.</p> + +<p>If you can't properly describe the changes made in a commit, the commit is too big.</p> + +<p>You should uncommit the changes and use <code>git add -p</code> to create a more focused commit.</p> + +<p>This is why <a href="http://localhost:8000/daily/2024/05/11/don-t-delete-my-commit-messages">I don't squash commits</a>.</p> + +<p>If people have made an effort to create good commits with good commit messages, I don't want them to be lost when the commits are merged.</p> + +<p>I want to keep the history of the changes intact and as it originally was.</p> + +<p>I do sometimes need to <a href="http://localhost:8000/daily/2025/02/11/tidy">tidy up my own commits</a>, though, before I push them for anyone else to see.</p> +
+
+ Sat, 01 Mar 2025 00:00:00 GMT +
+ + Do we still need CSS preprocessors? + /daily/2025/02/28/preprocessors + http://localhost:8000/daily/2025/02/28/preprocessors + +
<p>Before I started to use [Tailwind CSS][0], I used CSS preprocessors like Less and Sass to add features like variables and nesting to my CSS files.</p> + +<p>Stylesheets would be written in .scss, .sass or .less files and processed to create the stylesheets that would be used by browsers.</p> + +<p>But, with the recent improvements to CSS, do we still need these preprocessors?</p> + +<p>Here's a very small example of some CSS that just works:</p> + +<pre><code class="css">:root { + --color-primary: red; + --color-secondary: green; +} + +a { + color: var(--color-primary); + + &amp;:hover, &amp;:focus { + color: var(--color-secondary); + } +} +</code></pre> + +<p>It looks like a Sass file, but it's native CSS.</p> + +<p>It has variables (a.k.a. custom properties) and nesting, which I think are the most used features from preprocessors.</p> + +<p>But there's no additional build step to generate the end stylesheet. I can use this stylesheet as it is - making it easier to work on and less confusing for new Developers.</p> + +<p>If I'm not using Tailwind CSS or atomic styles, writing plain CSS files is the approach I'd use.</p> + +<p>No preprocessors needed.</p> +
+
+ Fri, 28 Feb 2025 00:00:00 GMT +
+ + Smaller modules are more reusable + /daily/2025/02/27/reusability + http://localhost:8000/daily/2025/02/27/reusability + +
<p>When you're writing open source code, such as a PHP library or a Drupal module, the larger it is, the harder it can be to reuse.</p> + +<p>Each implementation will have its own requirements and specifics, so why have code that tries to do everything?</p> + +<p>The smaller the code, the more reusable it is.</p> + +<p>The most reusable code I've written have been in smaller modules, like the <a href="https://www.drupal.org/project/system_user">System User</a> and <a href="https://www.drupal.org/project/null_user">Null User</a> Drupal modules.</p> + +<p>Both are very small and solve a specific problem.</p> + +<p>The Null User module is used by the System User module to provide a default if no system user is defined.</p> + +<p>It could have been part of the System User module, but extracting it into a separate module makes it more reusable.</p> + +<p>It also makes System User leaner, less bloated and more focused on its use case and its own functionality.</p> + +<p>This approach is based on the UNIX philosophy of a program doing one thing well, and chaining programs together when needed to solve a larger problem.</p> + +<p>Then, if you need, you can extend the code in a custom module or add features <a href="http://localhost:8000/daily/2025/02/24/patch">by applying patches</a>.</p> +
+
+ Thu, 27 Feb 2025 00:00:00 GMT +
+ + Don't create test.php files + /daily/2025/02/26/test-files + http://localhost:8000/daily/2025/02/26/test-files + +
<p>Have you written a file like test.php or scratch.php whilst developing?</p> + +<p>Do you have files like this committed to your codebase?</p> + +<p>These are often temporary files that are written by a Developer to test the code they're writing.</p> + +<p>They could include some test data, load a service or perform an action and then dump the result to the screen so they can verify the code they're writing works as expected.</p> + +<p>The same could be done with a test.js file that uses a <code>console.log</code> to output to the console.</p> + +<p>The issue is that they are only valid at the time they were written and now the code has been written and been tested, the file is likely out of date and either won't or can't be run again.</p> + +<p>A better approach is to write tests using a framework like PHPUnit, Pest or Jest instead and to stop writing temporary test files.</p> + +<p>Automated tests contain the same arrange, act and assert steps, but they can be run repeatedly.</p> + +<p>They can be run by every other Developer in the team.</p> + +<p>They can be run automatically in a CI pipeline.</p> + +<p>They can be run for every future commit and push so you know that the functionality still works as it did when it was written.</p> +
+
+ Wed, 26 Feb 2025 00:00:00 GMT +
+ + What would a suckless version of Drupal look like? + /daily/2025/02/25/suckless-drupal + http://localhost:8000/daily/2025/02/25/suckless-drupal + +
<p>suckless.org is the "home of dwm, dmenu and other quality software with a focus on simplicity, clarity, and frugality".</p> + +<p>Here's an excerpt from <a href="https://suckless.org/philosophy">the Suckless philosophy</a>:</p> + +<blockquote> + <p>We are the home of quality software such as dwm, dmenu, st and plenty of other tools, with a focus on simplicity, clarity and frugality. Our philosophy is about keeping things simple, minimal and usable. We believe this should become the mainstream philosophy in the IT sector. Unfortunately, the tendency for complex, error-prone and slow software seems to be prevalent in the present-day software industry. We intend to prove the opposite with our software projects.</p> +</blockquote> + +<p>I've previously wondered <a href="http://localhost:8000/daily/2024/11/08/what-will-be-included-in-drupal-cms">what would be included in future versions</a> of Drupal or Drupal CMS after usually needing to install the same modules on every new project.</p> + +<p>But I was recently wondering what if more was removed from Drupal?</p> + +<p>What if modules like Layout Builder and JSON:API that aren't needed on every project were moved into contrib and added via Composer when needed, making Drupal core smaller?</p> + +<p>I'm not saying that it will or should happen, but it's an interesting to think about how much of what's in Drupal currently is used.</p> + +<p>Core functionality like user registration and logins, password management, content types, fields and menus should stay.</p> + +<p>But I can think of a number of other things that could be removed.</p> +
+
+ Tue, 25 Feb 2025 00:00:00 GMT +
+ + To patch or not to patch + /daily/2025/02/24/patch + http://localhost:8000/daily/2025/02/24/patch + +
<p><a href="http://localhost:8000/daily/2025/01/13/patches">Applying patch files</a> is a common way to customise and extend open source software, and how we used to submit changes to Drupal before issue forks and merge requests were added to Drupal.org.</p> + +<p>Some software, such as dwm and st from suckless.org are released as minimal versions that you patch to add features to.</p> + +<p>If you find a line of code that you want to add, edit or delete, a patch file describes the changes so you can re-apply them whenever the source file changes.</p> + +<p>Patching offers unlimited customisation and flexibility.</p> + +<p>Whatever changes you want to make, you can.</p> + +<p>The downside is you need to maintain any patches you've written.</p> + +<p>If a change is made that causes your patch to no longer apply, you'll need to update the patch.</p> + +<p>There are some patches I commonly apply to Drupal projects, but I'll try to either contribute the changes back to the Drupal so I no longer need the patch or make the change in a custom module.</p> + +<p>Sometimes, though, <a href="http://localhost:8000/daily/2025/01/14/patching-drupal">patching is the only option</a>.</p> +
+
+ Mon, 24 Feb 2025 00:00:00 GMT +
+ + Tests aren't a line item + /daily/2025/02/23/line-item + http://localhost:8000/daily/2025/02/23/line-item + +
<p>A common mistake I see with new Developers and Development teams when starting with automated testing is to split development time and testing into separate tasks.</p> + +<p>They'll say when quoting a client or in an estimation session that the development time will take x hours and writing tests will take y hours.</p> + +<p>This is something I've always avoided.</p> + +<p>When broken out this way, it implies the automated tests are optional when they should be an integral part of the development process.</p> + +<p>They aren't a separate task that can be removed or skipped to save time or money.</p> + +<p>If I take my car to a garage, I don't ask them how long it will take to repair and how long to test what they've done.</p> + +<p>I want to know how much the whole task will cost and how long it will take.</p> + +<p>I assume they automatically test and verify the work they do and follow their industry's standard and best practices.</p> + +<p>I'm doing the same when developing software.</p> +
+
+ Sun, 23 Feb 2025 00:00:00 GMT +
+ + Speaking at PHP Thames Valley + /daily/2025/02/22/speaking-at-php-thames-valley + http://localhost:8000/daily/2025/02/22/speaking-at-php-thames-valley + +
<p>After speaking at PHP Oxford last January, I'm happy to be going back to Oxford to give my first presentation in 2025 at the renamed PHP Thames Valley meetup.</p> + +<p>The event is the 20th of March and I'll be speaking about Sculpin - the PHP static website generator that I use for my personal website.</p> + +<p>I'll explain what static site generators are, why I like them and some of Sculpin's core concepts before jumping into a live demo.</p> + +<p><a href="https://www.meetup.com/php-thames-valley/events/305915971">RSVPs are open</a> for the event and I'd love to see you there!</p> +
+
+ Sat, 22 Feb 2025 00:00:00 GMT +
+ + More code, more problems + /daily/2025/02/21/more + http://localhost:8000/daily/2025/02/21/more + +
<p>An interesting exercise is to run a tool like <a href="https://github.com/AlDanial/cloc">cloc</a> to count the number of lines in your application.</p> + +<p>It counts the number of files and lines within them, as well as how many lines are blank, comments or code.</p> + +<p>It's a metric that I like to check occasionally as I like to keep codebases lean and with the minimum amount of code.</p> + +<p>I don't like to have features that aren't used or <a href="http://localhost:8000/daily/2025/02/16/pre-optimise">pre-optimised code</a> for use cases that may not happen.</p> + +<p>The more code there is in your application, the problems you could encounter.</p> + +<p>There are more places for bugs and issues to hide.</p> + +<p>Keeping the amount of code to a minimum makes it quicker and easier to write, review and release as well as fix any bugs that are found in the future.</p> +
+
+ Fri, 21 Feb 2025 00:00:00 GMT +
+ + Don't branch, use feature toggles + /daily/2025/02/20/toggles + http://localhost:8000/daily/2025/02/20/toggles + +
<p>If <a href="http://localhost:8000/daily/2025/02/18/conflicts">feature branches cause conflicts</a>, what is the alternative?</p> + +<p>Don't branch.</p> + +<p>Keep all changes on a single branch and avoid merge conflicts between branches by not having branches.</p> + +<p>But how can you avoid releasing changes before they are ready?</p> + +<p>You can have a local branch with your changes that you don't push and rebase locally, but then <a href="http://localhost:8000/daily/2025/02/17/ci-cd">you're not doing continuous integration</a> and you're just as likely to get conflicts and have incompatible code.</p> + +<p>One of the main benefits of trunk-based development, where everything is on a single branch, is that everyone's work always works together.</p> + +<p>The better option is to use feature toggles (aka feature flags).</p> + +<p>Wrapping code in feature toggles separates deploying the code from releasing the feature.</p> + +<p>The code can be released, but the feature isn't active until the toggle is enabled.</p> + +<p>This means everyone can work on the same branch and continuously deploy changes whilst being selective about the features that are visible to the end users.</p> + +<p>If you have multiple environments, the same code can be used with different toggles enabled to allow for testing different features.</p> + +<p>Then, once the feature is ready to be enabled, there is no deployment needed.</p> + +<p>The code is already there - it just needs to be enabled.</p> + +<p>For Drupal, there is a <a href="https://www.drupal.org/project/feature_toggle">Feature Toggle module</a> and <a href="https://www.drupal.org/project/feature_toggle_twig">an extension that I wrote</a> that make it easy to use and manage feature toggles by just logging into the admin UI and selecting the active toggles you want.</p> + +<p>And, if an issue is discovered, it can also be easily disabled <a href="http://localhost:8000/daily/2025/02/19/back-or-forward">without needing to roll back to the previous version</a>.</p> +
+
+ Thu, 20 Feb 2025 00:00:00 GMT +
+ + Roll back or fix forward? + /daily/2025/02/19/back-or-forward + http://localhost:8000/daily/2025/02/19/back-or-forward + +
<p>You deploy a code change but it creates an issue.</p> + +<p>Maybe it breaks a feature, adds a bug or takes down an environment completely.</p> + +<p>How do you resolve it?</p> + +<p>Larger releases will sometimes have a roll back plan that details how to revert to the previous release.</p> + +<p>This can be difficult, especially for large releases and ones that change the database schema or values.</p> + +<p>Because of the amount of change, diagnosing an issue in a large release can take time.</p> + +<p>If it's been a while since the prior release, it can be some time since the code that introduced the issue was worked on - making it harder to fix.</p> + +<p>I prefer to do small releases and do them often.</p> + +<p>Some releases contain a single commit which was made only minutes since the previous release.</p> + +<p>This makes it easier to identify the issue, fix it and deploy a new version.</p> + +<p>No rolling back database changes or reverting to previous releases.</p> + +<p>Small, iterative deployments are less risky than large infrequent ones, easier to fix and make changes available to end users sooner.</p> +
+
+ Wed, 19 Feb 2025 00:00:00 GMT +
+ + Feature branches cause merge conflicts + /daily/2025/02/18/conflicts + http://localhost:8000/daily/2025/02/18/conflicts + +
<p>A common approach I see on software projects is where Developers create separate Git branches for each task they work on.</p> + +<p>This commonly matches issues or ticket on a sprint board or issue tracker.</p> + +<p>Each ticket is worked on independently and merged into a long-lived mainline branch once complete.</p> + +<p>This type of approach is commonly called Git Flow or GitHub Flow.</p> + +<p>It's something <a href="http://localhost:8000/presentations/git-flow">I've given presentations</a> on in the past.</p> + +<p>A common downfall is that different branches can conflict with each other - either due to a merge conflict where the same lines are changed in different branches, or incompatible code is written that works separately but not when merged together.</p> + +<p>I used to work this way, even when working on projects as the only Developer.</p> + +<p>One time, I was demoing two features to a client and needed to switch branches and doing so broke what it was trying to show.</p> + +<p>These days, I avoid conflicts between branches by not branching.</p> + +<p>Everyone works on a single branch and pulls and pushes changes regularly.</p> + +<p>If you're <a href="http://localhost:8000/daily/2025/02/17/ci-cd">doing continuous integration</a>, that should be once a day as an absolute minimum.</p> + +<p>I do <a href="http://localhost:8000/atdc">test-driven development</a> and usually commit after each passing test.</p> + +<p>If you work on a single branch and pull and push changes regularly, you're much less likely to get merge conflicts and Developers can focus on pushing code instead of fixing merge conflicts.</p> +
+
+ Tue, 18 Feb 2025 00:00:00 GMT +
+ + What are CI and CD? + /daily/2025/02/17/ci-cd + http://localhost:8000/daily/2025/02/17/ci-cd + +
<p>Whilst discussing <a href="http://localhost:8000/daily/2025/02/14/drupalisms">de-jargoning Drupal and Drupalisms</a> with Emma Horrell and Luke McCormick, I started thinking about pieces of jargon I come across regularly.</p> + +<p>Common ones are CI and CD.</p> + +<p>CI (or continuous integration) is not about whether you have a CI pipeline and use a tool like Jenkins, GitHub Actions or GitLab CI (despite how some of these tools are named).</p> + +<p>Continuous integration is how often code is integrated together.</p> + +<p>If it's been more than a day since you last merged your code into your mainline branch, you're not doing continuous integration.</p> + +<p>The less often you merge code, the more likely it is there will be conflicts or incompatibilities with other code that's been worked on - whether it's someone else's code, or code that you're writing in a different branch for a different task.</p> + +<p>CD stands for is continuous deployment or continuous delivery.</p> + +<p>When is the last time you deployed changes to production?</p> + +<p>If it's been more than a day, you're not doing continuous deployment.</p> + +<p>I've worked on teams and projects when it's been months between production releases.</p> + +<p>I much prefer releasing small, iterative and continuous improvements to production instead of doing large and risky deployments.</p> + +<p>Developers get their changes released sooner, end users get fixes and new features sooner, and it's easier to identify and resolve issues when releases are smaller and more frequent.</p> + +<p>How about you?</p> + +<p>Are you doing CI or CD?</p> + +<p>What other Drupal or techy jargon terms do you see regularly?</p> +
+
+ Mon, 17 Feb 2025 00:00:00 GMT +
+ + Don't pre-optimise + /daily/2025/02/16/pre-optimise + http://localhost:8000/daily/2025/02/16/pre-optimise + +
<p>I recently had <a href="http://localhost:8000/podcast/27-drupalisms">my first podcast episode with two guests</a>, where I discussed Drupal terminology and Drupalisms with Emma Horrell and Luke McCormick.</p> + +<p>It was a great episode, but there was something I needed to do before I could release it.</p> + +<p>Before I could release the episode, I needed to update my website to show both guest names.</p> + +<p>The other 26 episodes only had a single guest per episode, and my podcast pages were built to only show the first guest name.</p> + +<p>When building the pages for the <a href="http://localhost:8000/podcast/1-retrofit">first episode with Matt Glaman</a>, I only needed to show a single guest name.</p> + +<p>There was no need to show multiple guest names until I had an episode with multiple guests.</p> + +<p>I wrote the simplest code to achieve the requirements I had at the time.</p> + +<p>If I wrote for two guests but never had an episode with two guests, my website would be bloated and have functionality that wasn't needed or used.</p> + +<p>Now my requirements have changed.</p> + +<p>I can have an episode with one or two guests, but not three or more guests.</p> + +<p>If I have an episode with more than two guests, I'll write that functionality then.</p> + +<p>But not before.</p> +
+
+ Sun, 16 Feb 2025 00:00:00 GMT +
+ + Rebuilding Bootstrap with Tailwind CSS (and Sculpin) + /daily/2025/02/15/bootstrap + http://localhost:8000/daily/2025/02/15/bootstrap + +
<p>I've been <a href="http://localhost:8000/presentations/taking-flight-with-tailwind-css">speaking about and advocating for Tailwind CSS</a> and utility-first CSS for a long time.</p> + +<p>In my slide deck, I show screenshots of websites that are all built with Tailwind CSS - including some <a href="http://localhost:8000/blog/uis-ive-rebuilt-tailwind-css">rebuilds of existing websites</a>.</p> + +<p>I do this to show that the same approach and framework can be used to create different-looking designs because the classes are atomic and can be reused and combined as needed.</p> + +<p>This isn't the case with other CSS frameworks that focus on components first.</p> + +<p>As an experiment, some time ago I decided to <a href="https://bootstrap-with-tailwind.oliverdavies.uk">rebuild some of the Bootstrap example components using Tailwind CSS</a>.</p> + +<p>I've only done a pricing grid and album, but I may do more in the future.</p> + +<p>I originally built the examples with Astro but have ported them to Sculpin, which I'm more familiar with.</p> + +<p>If you want to see how I've built these components or how for an example of building a static website with Sculpin, <a href="https://code.oliverdavies.uk/opdavies/bootstrap-with-tailwind">check out the source code</a>.</p> +
+
+ Sat, 15 Feb 2025 00:00:00 GMT +
+ + Drupalisms and de-jargoning Drupal + /daily/2025/02/14/drupalisms + http://localhost:8000/daily/2025/02/14/drupalisms + +
<p>After the topic of Drupalisms and Drupal jargon came up in previous podcast episodes, I was happy to speak with Emma Horrell and Luke McCormick.</p> + +<p>We discussed the work happening in Drupal and Drupal CMS to de-jargon Drupal, the Drupalisms working group.</p> + +<p><a href="http://localhost:8000/podcast/27-drupalisms">Listen to the episode here</a>.</p> + +<p>If you want to be a guest on the <a href="http://localhost:8000/podcast">Beyond Blocks podcast</a>, reply and let me know.</p> +
+
+ Fri, 14 Feb 2025 00:00:00 GMT +
+ + Using readonly in PHP + /daily/2025/02/13/readonly + http://localhost:8000/daily/2025/02/13/readonly + +
<p>Since PHP 8.1, properties on PHP classes have been able to be marked as <code>readonly</code>.</p> + +<p>Once they've been set, they can't be changed or overwritten.</p> + +<p>This is great for data transfer objects (DTOs) that you want to be immutable and know the values are the same as when they were set.</p> + +<p>Here's an example:</p> + +<pre><code class="php">class MyDto { + + public function __construct( + public readonly string $name, + public readonly int $age, + ) { + } + +} +</code></pre> + +<p>I don't need to make the properties private or protected and write getter methods for them.</p> + +<p>I can make them public, knowing the values can't be changed.</p> + +<h2 id="readonly-classes">readonly classes</h2> + +<p>Since PHP 8.2, the entire class can be <code>readonly</code>, making the code cleaner and easier to read.</p> + +<p>This class does the same as the previous example:</p> + +<pre><code class="php">readonly class MyDto { + + public function __construct( + public string $name, + public int $age, + ) { + } + +} +</code></pre> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>This is a great example of how the PHP language continues to evolve and improve, and it's been a great way to make it easier to keep my code clean and with fewer bugs.</p> + +<p>If you want a way to easily add <code>readonly</code> to properties or classes, <a href="http://localhost:8000/daily/2025/01/31/rector">take a look at Rector</a> which can automate it for you.</p> +
+
+ Thu, 13 Feb 2025 00:00:00 GMT +
+ + Small and fast + /daily/2025/02/12/small-and-fast + http://localhost:8000/daily/2025/02/12/small-and-fast + +
<p>Due to an local outage, I've had no broadband connection and been mostly offline for the last few days.</p> + +<p>The only connection I've had is a weak 4G signal from my mobile phone that I've been able to tether from to check emails, etc.</p> + +<p>But this is slow and I struggled to load the majority of websites.</p> + +<p>Some would take a number of seconds or minutes to load, or fail.</p> + +<p>As websites and applications develop and grow, it's important to try and keep your codebase as lean and performant as possible.</p> + +<p>You never know what connection people will be using your code from.</p> + +<p>What if their broadband goes down or they're in an area with a poor mobile signal?</p> + +<p>They may not be able to download your large CSS or JS file, or execute the unoptimised database query.</p> + +<p>You can't assume everyone will be using a high speed Internet connection, so avoid feature bloat, optimise for performance and keep things small and fast.</p> +
+
+ Wed, 12 Feb 2025 00:00:00 GMT +
+ + Tidy then push + /daily/2025/02/11/tidy + http://localhost:8000/daily/2025/02/11/tidy + +
<p>As I said <a href="http://localhost:8000/daily/2025/02/10/refactoring">in yesterday's email</a>, sometimes you change your mind whilst working on something.</p> + +<p>Maybe you change your approach and have a commit that supersedes an earlier one, fix a typo, or find a bug and need to revert a commit.</p> + +<p>If you're pushing your changes to a branch for review, I suggest using <code>git rebase</code> to clean up your commits.</p> + +<p>You can squash the typo fix into the commit that introduced the typo, or remove the original implementation that you later moved away from.</p> + +<p>Whilst there is an option to squash all the commits when merging, I don't like it and prefer people to tidy their commits before pushing.</p> + +<p>This means the commits are easier to review and you can keep the original commit history and all the context within the messages instead of a generic <code>Merge commit..</code> message.</p> +
+
+ Tue, 11 Feb 2025 00:00:00 GMT +
+ + Refactoring and Test-Driven Development + /daily/2025/02/10/refactoring + http://localhost:8000/daily/2025/02/10/refactoring + +
<p>To do test-driven development, you start by writing a test which, because you're testing code that doesn't exist, will fail.</p> + +<p>The objective is to get the get the test to pass.</p> + +<p>You want to write just enough code to get the test to pass.</p> + +<p>It can be the simplest and naive code possible as long as it gets the tests to pass.</p> + +<p>Once the test passes, you can refactor and change the code in any way you want.</p> + +<p>This may be changing from a <code>foreach</code> loop to using a function like <code>array_map</code> or <code>array_reduce</code>, or splitting logic into separate classes.</p> + +<p>You may refactor your refactor and decide that a name or approach isn't correct and change it.</p> + +<p>You may split your code into service classes and then decide to use commands or actions.</p> + +<p>You can make whatever changes you want as long as the tests continue to pass.</p> + +<p>The implementation can change, but the outcome stays the same.</p> +
+
+ Mon, 10 Feb 2025 00:00:00 GMT +
+ + Simpler code doesn't mean less code + /daily/2025/02/09/simpler + http://localhost:8000/daily/2025/02/09/simpler + +
<p>Just because I consider some code to be simpler, that doesn't necessarily mean there's less code.</p> + +<p>It's possible to write a whole function or class on one line, but that doesn't mean it's simple to read or understand.</p> + +<p>The opposite is also true.</p> + +<p>I can extract business logic from Controller classes into different service classes, commands or actions.</p> + +<p>I can use data transfer objects (DTOs), value objects and Collection classes to give names and meanings to things instead of always using generic strings, arrays or objects.</p> + +<p>I can follow design patterns and implement Repositories, Decorators, Factories and Builders to better organise code and make it easier to use and update.</p> + +<p>I can write tests that act as examples and executable documentation for people to see how to use the code I've written.</p> + +<p>This can all make code easier to read, understand and use - even though there is more of it.</p> +
+
+ Sun, 09 Feb 2025 00:00:00 GMT +
+ + Writing more tests than production code + /daily/2025/02/08/more-tests + http://localhost:8000/daily/2025/02/08/more-tests + +
<p>As well as <a href="http://localhost:8000/daily/2025/02/07/less">having less code than you started with</a>, another good type of change is one where there are tests accompanying the production code.</p> + +<p>Whether the tests were written after the code or before with a test-driven development approach, the main thing is that there are tests when the code is committed.</p> + +<p>There should also be more test code than production code.</p> + +<p>There is only one implementation of the production code, but there should be multiple tests for it.</p> + +<p>There should be different tests for different situations, such as different types of input and no input, testing for throwing Exceptions and other failure cases and not just the "happy path".</p> + +<p>Looking at the diff of a commit or merge request, there should be more changes to the test then the code it's testing.</p> +
+
+ Sat, 08 Feb 2025 00:00:00 GMT +
+ + Having less code than you started with + /daily/2025/02/07/less + http://localhost:8000/daily/2025/02/07/less + +
<p>When running commands like <code>git log</code> or viewing pull requests, you can see the number of lines that have been added, edited or removed.</p> + +<p>When adding new features, it's likely you'll be adding code.</p> + +<p>If you're refactoring code, you may have less code than before.</p> + +<p>I like commits like this.</p> + +<p>It's not true that having fewer lines of code means the code is better, but having less code makes it easier to maintain and more secure.</p> + +<p>You don't need to upgrade and maintain code that you aren't using, so why not remove it?</p> + +<p>Why have old TODO comments, <code>dd()</code>, <code>var_dump()</code> or <code>console.log()</code> functions in the code?</p> + +<p>If they're not used, they can be removed.</p> + +<p>I recently read a post that suggested there were 5 to 10 bugs in each 1,000 lines of production code as a general rule, so the less code there is, the fewer places there are for bugs to hide.</p> + +<p>In general, for production code, less is more.</p> +
+
+ Fri, 07 Feb 2025 00:00:00 GMT +
+ + How I work around legacy code + /daily/2025/02/06/legacy + http://localhost:8000/daily/2025/02/06/legacy + +
<p>A few days ago, I mentioned <a href="http://localhost:8000/daily/2025/02/03/testable">a method that was over 150 lines long</a>.</p> + +<p>It had too many responsibilities, nested conditions, no dependency injection and no tests.</p> + +<p>Changing it would be risky, so how would I go about it?</p> + +<p>Let's assume I have this PHP method that contains the existing logic:</p> + +<pre><code class="php">function doSomething() { + // 150 lines of legacy code... +} +</code></pre> + +<p>I can create a new class that's clean and simple, with whatever automated tests and checks I want.</p> + +<p>Let's say it has an <code>execute()</code> method that returns a boolean value:</p> + +<pre><code class="php">class NewService { + + public function execute() { + return TRUE; + } + +} +</code></pre> + +<p>Because it's been tested in isolation, we can be confident it works as needed, so I can use it in the legacy function.</p> + +<p>I want to try and find a seam where I can use the new service and check for a result.</p> + +<p>Ideally, this would be as soon as possible within the function:</p> + +<pre><code class="php">function doSomething() { + $newResult = $this-&gt;newService-&gt;execute(); + + if ($newResult) { + return $newResult; + } + + // 150 lines of legacy code... +} +</code></pre> + +<p>If the new service returns a result, use it and return early, and don't execute the legacy code.</p> + +<p>Otherwise, run the original code as it would have been before, falling back to the original logic and result.</p> + +<p>Over time, more logic can be migrated into the new service until the legacy code is no longer used and can be removed.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>Writing tests for legacy code can be difficult or sometimes impossible.</p> + +<p>This approach means new code can be written using tests and test-driven development, dependency injection and whatever else you want without being limited by the existing code.</p> + +<p>You can incrementally migrate to the new approach and refactor out the old code, making it less risky than an all or nothing approach.</p> + +<p>Do you do the same thing or do you handle legacy code in a different way?</p> +
+
+ Thu, 06 Feb 2025 00:00:00 GMT +
+ + Drupal and the Open Web + /daily/2025/02/05/open + http://localhost:8000/daily/2025/02/05/open + +
<p>A few days ago, I said that <a href="http://localhost:8000/daily/2025/02/02/simple">I still like to use RSS</a> to consume a lot of online content.</p> + +<p>Unfortunately, a lot of websites either don't have RSS or Atom feeds, or they aren't easy to find without viewing the website's source code.</p> + +<p>The ability for people to publish and easily consume other people's content is a main principle of the open web, instead of relying on other websites and social media.</p> + +<p>Drupal is one of the projects <a href="https://www.drupal.org/association/open-web-manifesto">that make an open web possible</a>.</p> + +<p>It's easy to create an RSS feed of your articles, events or any other content on your Drupal website for others to consume.</p> + +<p>I also love that Drupal.org itself does this by publishing RSS feeds for project releases and various other content - not just news or blog posts.</p> + +<p>Because of this, I can subscribe to the feeds I want from any website that creates them and I can consume it how and when I want.</p> + +<p>Long live the open web!</p> +
+
+ Wed, 05 Feb 2025 00:00:00 GMT +
+ + Good software is easy to change + /daily/2025/02/04/changeable + http://localhost:8000/daily/2025/02/04/changeable + +
<p>As well as being easy to test, good software is easy to change.</p> + +<p>If requirements change or a bug is found, it should be easy to change the code.</p> + +<p>Simple code is easier to read and fix, and code split into smaller functions and classes - particularly when using design patterns - make it easier to change the existing code or its behaviour by writing new code.</p> + +<p>For example, if you need to change your payment gateway provider, ideally your software has been written so that each payment gateway has its own implementation and then can be swapped without needing to change the original code.</p> + +<p>If you need to change the behaviour of a class, you could use a Decorator to wrap and extend it.</p> + +<p>Having tests also makes the code easier to change as you can validate and verify the behaviour before and after the change.</p> + +<p>Changing code that doesn't have tests is risky, so if there aren't any, write some before changing the code.</p> +
+
+ Tue, 04 Feb 2025 00:00:00 GMT +
+ + Good software is easy to test + /daily/2025/02/03/testable + http://localhost:8000/daily/2025/02/03/testable + +
<p>One piece of criteria for whether some software is good is whether it's easy to write automated tests for.</p> + +<p>I recently worked on a method that was more than 150 lines.</p> + +<p>It was difficult to understand, had too many responsibilities and didn't use dependency injection.</p> + +<p>This made it difficult to test.</p> + +<p>If it was split into several smaller methods or classes, each could be responsible for different aspects and have clear responsibilities.</p> + +<p>Each new method or class could have its own tests to verify its own functionality works.</p> + +<p>It would be easy to understand and identify and fix any issues.</p> + +<p>If dependency injection was used, things can be mocked or changed as needed to make the logic testable.</p> + +<p>If everything is in one large function, the code isn't testable.</p> + +<p>Even if it works now, it will be difficult to change in the future.</p> +
+
+ Mon, 03 Feb 2025 00:00:00 GMT +
+ + What's the simplest solution? + /daily/2025/02/02/simple + http://localhost:8000/daily/2025/02/02/simple + +
<p>I still like RSS and consume a lot of my online content from RSS feeds.</p> + +<p>I've recently switched from a self hosted RSS reader application on my homelab to a simpler terminal-based one.</p> + +<p>It's very simple to use, fast, and easy to configure and manage feed URLs - just by editing two text files.</p> + +<p>There's no UI to click around and no Docker containers or database services running.</p> + +<p>Everything is stored in a local SQLite database, which also makes it easy to backup and move between devices if needed.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>When writing software, I always try and think of the simplest solution to solve a problem.</p> + +<p>I like solutions that are easy to use, configure, manage and maintain and want to avoid complex or "feature bloated" software.</p> +
+
+ Sun, 02 Feb 2025 00:00:00 GMT +
+ + Don't use global dependencies + /daily/2025/02/01/global-dependencies + http://localhost:8000/daily/2025/02/01/global-dependencies + +
<p>I recently watched a YouTube video of someone working on their software.</p> + +<p>Everything was working locally, but things were breaking in their CI pipeline.</p> + +<p>The issue was their CI environment had different versions of the software than the one they were using locally.</p> + +<p>To get the same results locally and in CI, you need your dependencies to be the same.</p> + +<p>The same versions of languages, such as PHP and nodejs, and packages like PHPStan, PHPUnit, Jest or eslint.</p> + +<p>Dependencies should be installed with a package manager that generates a lock file like composer.lock or package-lock.json, so use these versions instead of installing versions globally that may be different.</p> + +<p>For PHP or nodejs, I'd suggest using Docker or Nix, <a href="http://localhost:8000/daily/2025/01/20/reproducible">which has its own lock file</a>, to make your environment as reproducible and consistent as possible.</p> +
+
+ Sat, 01 Feb 2025 00:00:00 GMT +
+ + Keep your PHP code up to date using Rector + /daily/2025/01/31/rector + http://localhost:8000/daily/2025/01/31/rector + +
<p>Do you want to make it easier to keep your PHP code up to date with the latest standards and using the latest features?</p> + +<p>Rector is a tool for refactoring PHP code and it has presets for different versions of PHP.</p> + +<p>It can refactor your code to follow best practices such as using early returns, having private methods by default and enabling strict typing - making code cleaner whilst using the latest PHP features.</p> + +<p>Once installed, it can be executed and will make changes to your files, but what about keeping it up to date going forward?</p> + +<p>Why not add <code>rector --dry-run</code> to your CI pipeline and have it checked automatically?</p> + +<p>Then, if code is committed that doesn't match the latest standards, the pipeline will fail and the code can be updated.</p> + +<p>This will ensure the code is always up to date and you won't be stuck on old or unsupported PHP versions.</p> +
+
+ Fri, 31 Jan 2025 00:00:00 GMT +
+ + Your CI pipeline is your gatekeeper + /daily/2025/01/30/gatekeeper + http://localhost:8000/daily/2025/01/30/gatekeeper + +
<p>How do you know if a commit to your codebase is deployable?</p> + +<p>What gives you confidence if a change will work once it's released?</p> + +<p>This can be from manual testing but also automated testing and quality checks from tools such as static analysis and code linting.</p> + +<p>However, this relies on every Developer running them before pushing each change and for their development environments to be consistent and matching the target environment.</p> + +<p>This can be automated by using a CI pipeline - a series of checks that are run automatically for each code push.</p> + +<p>This can include running automated tests, linting code and running static analysis and anything else you need.</p> + +<p>This is what determines if the change is deployable.</p> + +<p>If your CI pipeline passes, the commit is good can be deployed.</p> + +<p>If it fails, the commit should not be deployed and you should get it passing again as quickly as possible as <a href="http://localhost:8000/daily/2023/06/28/theres-no-value-in-a-broken-ci-pipeline">there's no value in a broken CI pipeline</a>.</p> +
+
+ Thu, 30 Jan 2025 00:00:00 GMT +
+ + Make it work, then make it good + /daily/2025/01/29/make-it-work + http://localhost:8000/daily/2025/01/29/make-it-work + +
<p>I've recently been writing a new custom module for a Drupal application.</p> + +<p>I needed to create a form populated with options from an API endpoint, submitted the form values to another API endpoint and display some results to the user.</p> + +<p>I'd architected and planned the steps I was going to need and knew how I was going to approach adding this new functionality.</p> + +<p>My first objective was to make it work.</p> + +<p>To get all the functionality in place so it was working as soon as possible with minimal distractions.</p> + +<p>This meant I would find any issues sooner rather than later and what I'd created could be reviewed and tested by stakeholders, regardless of how it looked.</p> + +<p>If needed, it could have been released at this point.</p> + +<p>It was working.</p> + +<p>Next, I did a second pass to make it good.</p> + +<p>I updated the markup and styled it to match the provided designs.</p> + +<p>I made small updates to terminology and messaging to ensure the tone of voice was correct and consistent with the rest of the application and various other small tweaks and improvements.</p> + +<p>I could do all this knowing the functionality was already working, and I could focus on making it good.</p> +
+
+ Wed, 29 Jan 2025 00:00:00 GMT +
+ + Is it the application or implementation? + /daily/2025/01/28/application-or-implementation + http://localhost:8000/daily/2025/01/28/application-or-implementation + +
<p>With most things, it's easy to see where an issue is. There's only one way to build a car or a mobile phone, for example.</p> + +<p>With other things, such as software, it's not as clear.</p> + +<p>There are usually multiple ways to achieve the same result, but not all implementations are equal.</p> + +<p>It's important to understand if an issue is caused by the software itself or how it's been implemented.</p> + +<p>Some approaches may be quicker, better for end-user experience or more performant.</p> + +<p>Each has its own advantages and disadvantages.</p> + +<p>What was the correct solution then may not be the best one now.</p> + +<p>Priorities may have changed or new approaches could be available.</p> + +<p>You know more now about the problem that's being solved than you did at the time.</p> + +<p>One of the original discarded solutions could now be the best option, or a new option that wasn't previously available that solves the problem in a different way.</p> + +<p>The software may be capable of achieving what you want - it may just need to be configured in a different way.</p> +
+
+ Tue, 28 Jan 2025 00:00:00 GMT +
+ + Should you have a separate front-end for your Drupal website? + /daily/2025/01/27/separate-front-end + http://localhost:8000/daily/2025/01/27/separate-front-end + +
<p>A few years ago, "decoupled" or "headless" Drupal was a popular approach, leveraging Drupal's built-in JSON:API module to expose its data via an API which can be consumed by a separate front-end application.</p> + +<p>The front-end application would retrieve the data from Drupal via the API and generate the appropriate HTML.</p> + +<p>It's an approach I've used in the past and <a href="http://localhost:8000/presentations/decoupling-drupal-vuejs">spoken about at conferences</a>, but it comes with pros and cons.</p> + +<p>In theory, as the Drupal (or back-end application) and front-end are completely separate, there can be two separate and independent teams working on them.</p> + +<p>This adds overhead and complexity and I've found that one team will commonly be blocking the other instead of both being able to work in parallel.</p> + +<p>As I said yesterday, <a href="http://localhost:8000/daily/2025/01/26/layout-builder">previewing content in Drupal can be an issue</a> - particularly with a decoupled approach which needs a front-end to be rebuilt before the changes can be seen.</p> + +<p>If you have a separate front-end, you'll need to create everything from scratch, such as writing accessible HTML markup and othe standard features that would normally be provided by Drupal and, because you've got two separate front-end and back-end applications, you've got twice the amount of maintenance.</p> + +<p>You could also be excluding yourself from any new features that will be available in future versions of Drupal or Drupal CMS, such as the new Experience Builder.</p> + +<p>Whilst decoupled/headless builds are a viable option and can work well in some situations, it's not something I recommend often.</p> +
+
+ Mon, 27 Jan 2025 00:00:00 GMT +
+ + Why I like Drupal's Layout Builder + /daily/2025/01/26/layout-builder + http://localhost:8000/daily/2025/01/26/layout-builder + +
<p>I've built Drupal websites for 18 years and have worked with versions as old as Drupal 5.</p> + +<p>A common issue for people using the CMS has been the editorial experience when adding and editing content.</p> + +<p>Once logged in, to add or edit a node, they need to go to a separate administration screen to enter or change the content.</p> + +<p>The administration page usually looks nothing like what the page will look like once published, with the user only seeing large forms and different styles, or a different theme altogether.</p> + +<p>There is a page preview option, but this only works once the forms have been completed and requires the user to be able to "see" how the page will look beforehand and know what content will appear where.</p> + +<p>This has become trickier with the use of Paragraphs and entity reference fields, where the editor only sees the title of the content they want to reference but not what they will see or how it will look.</p> + +<p>As well as being difficult to create content, it also makes it difficult to edit it.</p> + +<h2 id="layout-builder">Layout Builder</h2> + +<p>This is why I like Drupal's Layout Builder module.</p> + +<p>Originally released as a contributed module, it has been part of Drupal core since 8.7 and allows editors to be able to create complex pages using a drag and drop interface and instantly see how they look.</p> + +<p>There's now an ecosystem of Layout Builder-related modules, <a href="https://www.drupal.org/project/layout_builder_extra_templates">including one I wrote</a>, that add more functionality to the Layout Builder, such as adding more template suggestions, restricting the available blocks, or adding inline styles - making it even more powerful.</p> + +<p>If you have a Drupal site and haven't tried Layout Builder, you can incrementally opt in and only enable it for certain content types. You don't need to go all in straight away.</p> + +<p>But, I think that once you've tried it, you won't be able to go back.</p> +
+
+ Sun, 26 Jan 2025 00:00:00 GMT +
+ + Why should you upgrade from Drupal 7? + /daily/2025/01/25/upgrade + http://localhost:8000/daily/2025/01/25/upgrade + +
<p>As of the 5th of January, Drupal 7 is no longer supported and will receive no new security or compatibility updates.</p> + +<p>But what if you already have a working Drupal 7 installation?</p> + +<p>Why should you upgrade?</p> + +<p>The major reason is security.</p> + +<p>There will be no new security updates for Drupal 7, so to stay secure and protect your data - especially if you have users or customer data - you should upgrade and continue receiving security updates.</p> + +<p>Aside from Drupal 7 core, a lot of Drupal 7 modules and themes have been unsupported for some time, so you may have other insecure or unsupported modules that you need to upgrade.</p> + +<h2 id="dependencies">Dependencies</h2> + +<p>To keep up to date with the latest versions of PHP and MySQL - the software needed to run Drupal, and other dependencies - you should upgrade to Drupal 10 or 11.</p> + +<p>Drupal 11 requires PHP 8.3 and MySQL 8.0, so by upgrading, you'll also keep your dependencies up to date, benefit from their security updates, bug fixes and performance improvements.</p> + +<h2 id="developer-experience">Developer Experience</h2> + +<p>Newer versions of software are nicer to work with, making it easier to find Developers.</p> + +<p>PHP 8 can look very different to earlier versions used by Drupal 7, making it nicer and quicker to write - and easier to deliver features.</p> + +<p>Drupal 7 was probably one of the only projects using SimpleTest for automated tests, whereas the newer versions have used PHPUnit - the defacto PHP testing tool - making automated testing and test-driven development easier and more accessible.</p> + +<p>Drupal 8 also uses Twig as its templating engine, making theming more powerful and more familiar to front-end developers.</p> + +<h2 id="more-choice">More Choice</h2> + +<p>Whilst a lot of the same Drupal modules and themes exist for Drupal 7 and 10, there are a lot of new options available and a lot more in Drupal core, such as Layout Builder, media management and built-in APIs, and a lot more in the new Drupal CMS version.</p> + +<p>The new versions of Drupal are constantly evolving, adding new features and functionality without you needing to rebuild your application and rewrite your code as you'd have needed to do for previous major version upgrades.</p> + +<p>Maybe you can replace some of your existing code with some of these new or updated modules.</p> + +<h2 id="anything-else%3F">Anything Else?</h2> + +<p>These are some of the reasons I think you should upgrade from Drupal 7, but there are others.</p> + +<p>If you want to hear more, email me and we can discuss more and, if you're stuck on Drupal 7, I can create a custom roadmap for you to get you moving towards Drupal 11.</p> +
+
+ Sat, 25 Jan 2025 00:00:00 GMT +
+ + What and why + /daily/2025/01/24/what-why + http://localhost:8000/daily/2025/01/24/what-why + +
<p>There are two parts to a good commit message.</p> + +<p>The first line summarises the change, but this can be seen by running <code>git diff</code> or <code>git show</code>.</p> + +<p>The subsequent lines should describe why the change was needed, including any additional context, alternative approaches that were considered or tried, follow up steps, issues encountered or anything else that it would be useful to read if someone runs <code>git log</code> or <code>git blame</code> to look at the commit history.</p> + +<p>If there was an issue with this code in the future, what information would be useful to know?</p> + +<p>Why did you take this approach over a different one?</p> + +<p>What error does this commit fix? Include the error message.</p> + +<p>Who requested the change and why? What information can you include that you wouldn't want to lose if you changed your project management or issue tracking software? Don't just link to the issue or add the ticket number.</p> + +<p>What other information and context did you know when you made the commit?</p> + +<p>If anything changes in the future, you can make another commit and repeat the process.</p> +
+
+ Fri, 24 Jan 2025 00:00:00 GMT +
+ + Who's Travis? + /daily/2025/01/23/travis + http://localhost:8000/daily/2025/01/23/travis + +
<p>I remember seeing a talk or listening to a podcast during which they mentioned Travis, as in the CI pipeline tool, and some of the things they were using it for.</p> + +<p>They also said that their client thought Travis was a person on their team.</p> + +<p>In some ways, having a CI pipeline performing automated checks and tests is like having another team member.</p> + +<p>Instead of waiting for a person to manually check code styles, run tests and spot potential bugs, why not automate them and run them in the CI pipeline every time code changes are pushed?</p> + +<p>You'll get results sooner than waiting for a person do check the code by hand and, arguably, more consistent results.</p> + +<p>Then, if you do peer code reviews, you know the basics are already done and the reviewer can focus on other things.</p> +
+
+ Thu, 23 Jan 2025 00:00:00 GMT +
+ + Why I use nixpkgs-unstable + /daily/2025/01/22/nixpkgs-unstable + http://localhost:8000/daily/2025/01/22/nixpkgs-unstable + +
<p>In the majority of my <code>flake.nix</code> files, such as the one <a href="http://localhost:8000/daily/2025/01/19/minimum-viable-development-environment">I've been experimenting with for Drupal projects</a>, I use <code>nixpkgs-unstable</code> as my primary input.</p> + +<p>The nixpkgs package manager has two major releases a year, in May and November, which would be 24.05 and 24.11 for 2024 respectively.</p> + +<p>Using <code>nixpkgs-unstable</code>, I get the latest packages and NixOS options and don't need to wait for the next major stable release.</p> + +<p>But doesn't that make things more likely to break when updating?</p> + +<p>The number of backwards incompatible changes and breakages on unstable are minimal, but I'd rather deal with small issues and updates regularly rather than only twice a year.</p> + +<p>Small updates more often are better, in my opinion.</p> + +<p>But, what if there is an issue with a package that I'm using, such as a build issue, regression or incompatibility?</p> + +<p>With flakes, I can import multiple versions of nixpkgs in the same configuration and use them where the configuration I need to.</p> + +<p>I need to in one project, I've added an input with nixpkgs pinned to a specific Git commit in the nixpkgs repository as some packages are using outdated versions and are no longer present in the latest releases.</p> + +<p>It's not all or nothing, I can pick which packages to be unstable and which I want to use stable or use older versions.</p> + +<p>See <a href="https://code.oliverdavies.uk/opdavies/dotfiles/src/commit/a5c1c891020c5026ca1c2d83afd6cbe8002a136c/flake.nix">my dotfiles repository</a> for an example, where I have four versions of nixpkgs imported - including unstable and master, which is even newer.</p> + +<p>Every input is still locked in the <code>flake.lock</code> file, so everything is still reproducible whichever versions of nixpkgs I decide to use.</p> +
+
+ Wed, 22 Jan 2025 00:00:00 GMT +
+ + Making ddev reproducible + /daily/2025/01/21/ddev + http://localhost:8000/daily/2025/01/21/ddev + +
<p>I sometimes work on projects that already has an existing environment configuration, usually using a tool like DDEV.</p> + +<p>Similar to using Nix, DDEV makes environments more consistent by standardising versions of PHP, nodejs, etc for each project.</p> + +<p>This is great, but how do you know everyone has the same version of DDEV?</p> + +<p>Different people having different versions of DDEV could introduce its own issues and bugs, such as pulling different images with different default package versions.</p> + +<h2 id="enter-nix">Enter Nix</h2> + +<p>The Nix package manager has over 100,000 packages, including DDEV.</p> + +<p>This means I can install DDEV via Nix and because of the flake.nix file, anyone using the same configuration will get the same version of DDEV.</p> + +<p>Here's a simple <code>flake.nix</code> file to install DDEV:</p> + +<pre><code class="nix">{ + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + outputs = + { nixpkgs, ... }: + let + system = "x86_64-linux"; + pkgs = nixpkgs.legacyPackages.${system}; + in + { + devShells.${system}.default = pkgs.mkShell { + buildInputs = with pkgs; [ ddev ]; + }; + }; +} +</code></pre> + +<p>This approach also means I can have different versions of DDEV installed at once so I can use different versions for different projects as needed.</p> + +<p>I also don't need it installed globally, so this only makes it available to the projects that need it.</p> + +<p>It's a bit meta, but it works.</p> +
+
+ Tue, 21 Jan 2025 00:00:00 GMT +
+ + Reproducible or repeatable + /daily/2025/01/20/reproducible + http://localhost:8000/daily/2025/01/20/reproducible + +
<p><a href="http://localhost:8000/daily/2025/01/19/minimum-viable-development-environment">In yesterday's email</a>, I showed how I've been using Nix and flake files to build reproducible and shareable development environments for Drupal applications.</p> + +<p>The reason it's reproducible is the <code>flake.lock</code> file.</p> + +<p>Similar to <code>composer.lock</code> or <code>package-lock.json</code>, it captures the exact versions of the packages installed from the nixpkgs repository.</p> + +<p>This file, along with <code>flake.nix</code>, can be committed alongside the application code and anyone with Nix installed can run <code>nix develop</code> to get a shell with the same packages and dependencies.</p> + +<p>This isn't the same as other solutions, where you add something like <code>FROM php:8.2</code> but, because there's no lockfile, there's no guarantee the same package versions will be installed so there could be mismatches that cause errors.</p> + +<p>With <code>flake.lock</code>, the environment isn't just repeatable - it's completely reproducible.</p> + +<p>Locally, in a CI pipeline or in production.</p> +
+
+ Mon, 20 Jan 2025 00:00:00 GMT +
+ + Minimum viable development environment + /daily/2025/01/19/minimum-viable-development-environment + http://localhost:8000/daily/2025/01/19/minimum-viable-development-environment + +
<p>What is the leanest and most minimal local environment for software development?</p> + +<p>I think if you use Linux, the most minimal approach is to install the packages you need, such as PHP and MySQL directly in your operating system.</p> + +<p>There's no overhead or complexity added by tools like containers so things will run as quick and efficiently as possible.</p> + +<p>This is great if you only work on one project and you can easily keep your installation in sync with production.</p> + +<p>But what if you work on multiple projects or with a team of Developers?</p> + +<p>You need a way for everyone to have the same software and package versions, and to be able to configure them for each project.</p> + +<p>These are the reasons why tools like Vagrant, Docker and Podman became popular, as they made it possible for environments to be easily repeatable and customisable.</p> + +<h2 id="what-about-nix%3F">What about Nix?</h2> + +<p>Another tool that can be used to install software is Nix - a package manager with over 100,000 software packages.</p> + +<p>I can use it to install the required software for each project and share the files with any team members so they have the same configuration.</p> + +<p>This is the <code>flake.nix</code> file that I've been testing with a Drupal codebase:</p> + +<pre><code class="nix">{ + inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + + outputs = + { nixpkgs, ... }: + let + system = "x86_64-linux"; + pkgs = nixpkgs.legacyPackages.${system}; + in + { + devShells.${system}.default = pkgs.mkShell { + buildInputs = with pkgs; [ + mariadb + php82 + php82Packages.composer + ]; + }; + }; +} +</code></pre> + +<p>This installs PHP 8.2, Composer and MariaDB, and nothing else.</p> + +<p>It also generates a <code>flake.lock</code> file so everyone gets exactly the same package versions.</p> + +<p>The downside is that, if everyone isn't using NixOS which handles services, I need to configure the database server for each project, running commands like <code>mysql_install_db</code> and <code>mysqld</code> before creating the database and user to put in the <code>settings.php</code> file.</p> + +<p>It's not complicated but <a href="http://localhost:8000/daily/2024/11/11/could-nix-and-devenv-replace-docker-compose">devenv is a great option</a> if you want something more fully featured and opinionated that does more out of the box.</p> + +<p>Once the database server is running and Drupal is installed, I can run <code>drush runserver</code> to run the website - no need for Apache, Caddy or Nginx.</p> + +<p>If you want to see another example, see the <a href="https://code.oliverdavies.uk/opdavies/oliverdavies.uk/src/commit/4350852406e9556b63a1df448f225abbd7883651/flake.nix">flake.nix file for this website</a>.</p> +
+
+ Sun, 19 Jan 2025 00:00:00 GMT +
+ + TypeScript for PHP + /daily/2025/01/18/typescript-php + http://localhost:8000/daily/2025/01/18/typescript-php + +
<p>During one of Nuno Maduro's live streams, he was speaking about static analysis and PHPStan when either he or someone in the chat said "PHPStan is TypeScript for PHP".</p> + +<p>I thought this was an interesting comparison.</p> + +<p>PHP has types and type checking - the same as TypeScript - but it's only performed when the code is executed.</p> + +<p>TypeScript's build step will run its type checks and identify any errors when compiling the application.</p> + +<p>Static analysis tools do the same for PHP, allowing you to execute the type checks without executing the code.</p> + +<p>The means you can do it from the command line, in an IDE or text editor, or in a CI pipeline.</p> + +<p><a href="https://www.youtube.com/watch?v=sOQC_-pkMYk">Nuno's follow-up YouTube video</a> is a great one to watch if you want to see static analysis and PHPStan in action!</p> +
+
+ Sat, 18 Jan 2025 00:00:00 GMT +
+ + Comments can lie. Code can't. + /daily/2025/01/17/lies + http://localhost:8000/daily/2025/01/17/lies + +
<p>How often have you need code like this?</p> + +<pre><code class="php">// Returns true. +return false; +</code></pre> + +<p>Whilst a comment like this could have been true when it was written, the code can change independently of the comment - making them out of sync and the comment no longer true.</p> + +<p>As the comment is not evaluated or executed, there isn't a way to validate whether it's still correct, so whilst it could be, it may not be.</p> + +<p>Comments should describe why the code is needed, not what it does.</p> + +<p>If a comment describes the functionality, it can be refactored and extracted to a class, method or function - a.k.a. self-documenting code.</p> + +<p>But, if you want something that will alert you if the functionality changes, look into <a href="http://localhost:8000/atdc">automated testing</a>.</p> + +<p>If you have a passing test that suddenly starts to fail, you know the behaviour has changed.</p> +
+
+ Fri, 17 Jan 2025 00:00:00 GMT +
+ + Learning by reading + /daily/2025/01/16/learning + http://localhost:8000/daily/2025/01/16/learning + +
<p>When I'm working as part of a Development team, working on open source projects, watching live streams or reading code that people have released online - such as their website or dotfiles - I read a lot of other people's code.</p> + +<p>It's an opportunity to learn from others and how they approach things.</p> + +<p>How they achieved a desired result or fixed a bug, which I can learn from.</p> + +<p>I can refer to it if I need to do something similar in the future.</p> + +<p>Or I may find something random that's I didn't know I needed, such as a module, library or configuration setting I wasn't aware of.</p> + +<p>I recently learned about .mailmap files from Greg Hurrell (wincent)'s dotfiles on GitHub.</p> + +<p>.mailmap is a file that is used by Git when displaying history, such as running <code>git log</code>, and allows you to define canonical names and email addresses for committers and contributors.</p> + +<p>I've accidentally used the wrong email address or typed my name incorrectly before in my Git configuration, which was there for all to see, but this file allows me to consolidate my identities within a repository so my commits are grouped together and attributed to me, regardless of which email address I used or how I wrote my name.</p> + +<p>You can see <a href="https://code.oliverdavies.uk/opdavies/oliverdavies.uk/src/commit/633e11abedfa4cc8d85d37695c1ca014874fd4c1/.mailmap">the one I added to my website directory</a>.</p> + +<p>If I hadn't looked at that repository, I wouldn't have learned about it.</p> +
+
+ Thu, 16 Jan 2025 00:00:00 GMT +
+ + Patching more things + /daily/2025/01/15/more-patches + http://localhost:8000/daily/2025/01/15/more-patches + +
<p>Since <a href="http://localhost:8000/daily/2025/01/13/patches">patching ThePrimeagen's tmux-sessionizer script</a>, I've started to think of more situations where patching would be useful.</p> + +<p>I've been using Nick Janetakis' notes script for creating and managing notes files and wanted it to generate daily files instead of monthly, which it did in a previous version.</p> + +<p>Rather than locking myself to using an older version, <a href="https://code.oliverdavies.uk/opdavies/dotfiles/commit/3e9a27874a8110f5e6af2afff3de6efbc5b71e8f">I patched the script</a> to include my changes and create the files with the names I want.</p> + +<p>If Nick releases a new version, I'll get the updates and I'll keep my changes as the patch is applied automatically by Nix.</p> + +<p>As I mentioned before, the suckless.org projects follow this approach and expect users to create and apply patches to their software to configure and customise them.</p> + +<p>As someone who creates and maintains open source software, and develops applications with open source projects like Drupal, knowing how to use patches to fix and extend them can be very useful in some situations.</p> + +<p>P.S. If you want to hear a conversation between Nick and myself about Docker, course creation and more, listen to the <a href="http://localhost:8000/podcast/12-nick-janetakis-docker">episode with Nick on the Beyond Blocks podcast</a>.</p> +
+
+ Wed, 15 Jan 2025 00:00:00 GMT +
+ + Patching Drupal + /daily/2025/01/14/patching-drupal + http://localhost:8000/daily/2025/01/14/patching-drupal + +
<p>Yesterday I wrote about <a href="http://localhost:8000/daily/2025/01/13/patches">how I used a patch file to customise a project in my Nix configuration</a>.</p> + +<p>I'm familiar with patch files from my Drupal contributions, when we used to create and upload patch files and attach them to issues to contribute changes.</p> + +<p>Although patches aren't used to contribute to Drupal any more, you can still apply patches to Drupal code in your own projects if you need to.</p> + +<p>If there's a customisation or fix you need you need to apply, instead of altering and "hacking" the source files, you can apply changes with patch files.</p> + +<p>Instead of Nix, I use <a href="https://github.com/cweagans/composer-patches">composer-patches</a> to automatically apply patches when running <code>composer install</code>.</p> + +<p>For example, in my composer.json file, I can add something like this:</p> + +<pre><code class="json">"extra": { + "patches": { + "drupal/default_content": { + "Issue #2698425: Do not reimport existing entities (https://www.drupal.org/project/default_content/issues/2698425#comment-15593214)": "patches/default_content-2698425-do-not-reimport-196.patch", + "Issue #3160146: Add a Normalizer and Denormalizer to support Layout Builder (https://www.drupal.org/project/default_content/issues/3160146#comment-14814050)": "patches/default_content-3160146-53.patch" + } + }, +}, +</code></pre> + +<p>This will apply these two patch files to the Default Content module (which are the same as running <code>git diff</code> between two commits), which I needed to do for a recent project.</p> + +<p>If the upstream issue is fixed and the patch is no longer needed, they can be removed and the module can be updated to the latest version.</p> + +<p>And this works for core and contrib projects.</p> + +<p>The same as the tmux-sessionizer example, this approach means I can apply any changes without needing to duplicate or alter the code, and it makes it easy to contribute by testing other people's patches or applying and contributing your own.</p> +
+
+ Tue, 14 Jan 2025 00:00:00 GMT +
+ + Easy customisation using patches + /daily/2025/01/13/patches + http://localhost:8000/daily/2025/01/13/patches + +
<p>I'm a long-time Vim and tmux user.</p> + +<p>I used them before <a href="http://localhost:8000/blog/going-full-vim">I switched to using Neovim full time</a> in July 2021.</p> + +<p>More recently, I've used a script that creates and attaches to tmux sessions based on the directories in my Code directory, making it easy to switch between projects.</p> + +<p>It was based on others by <a href="http://localhost:8000/podcast/25-jess-archer-drush-laravel-prompts">Jess Archer</a> and ThePrimeagen, although Prime recently created a new version of his as its own project, so I decided to switch to his version.</p> + +<p>But there was one issue - the paths to search for directory names is hard-coded and don't match mine.</p> + +<p>I started by duplicating his and changing the paths, but that would mean missing any future updates and having to maintain my version separately.</p> + +<p>Until I realised I could use his version and apply patches to it for my changes and customisations.</p> + +<p>This is something I'm familiar with from my Drupal contributions, as we used to attach patch files to issues before moving to GitLab and merge requests.</p> + +<p>I was able to make my changes and easily <a href="https://code.oliverdavies.uk/opdavies/dotfiles/commit/5fd27efa502bd48eb7d897acec8aa3344bef2247">apply the patch easily as part of my Nix derivation</a>.</p> + +<p>This means I'll get any future updates to the script, keep my changes and Nix will automatically apply my patch whenever I rebuild my system.</p> + +<p>I really like this approach, as I'm no longer duplicating the script and don't have the maintenance overhead whilst still making any customisations I need.</p> + +<p>In fact, there are Linux applications such as dwm, dmenu and st (a simple terminal), all written by suckless.org, that use this approach as the main method of configuration and customisation.</p> + +<p>They release the core package and people write and contribute patch files to customise it as they need.</p> + +<p>Although patches are no longer used on Drupal.org, they're still a great way to customise and contribute to open source software.</p> +
+
+ Mon, 13 Jan 2025 00:00:00 GMT +
+ + Don't over rely on AI + /daily/2025/01/12/reliance + http://localhost:8000/daily/2025/01/12/reliance + +
<p>I've occasionally been experimenting with AI whilst programming.</p> + +<p>Not in my editor, but asking it questions if I'm unsure how to do something or something isn't working.</p> + +<p>As a pair programming partner of sorts.</p> + +<p>Its results and suggestions are still mixed.</p> + +<p>But I've noticed I've started to ask AI for answers to simple questions I can find or figure out myself.</p> + +<p>Things like how to show more verbose output for a command, which I can find from the command's help text or manual.</p> + +<p>This was part of a larger issue which, instead of asking AI, I would usually investigate by reading the available documentation or source code.</p> + +<p>I don't want to lose my problem solving skills.</p> + +<p>I like reading documentation and code to learn more about the tools I'm using and to solve issues I encounter.</p> + +<p>I like to read articles others have written on their websites about the same or similar issues.</p> + +<p>Whilst AI can be a useful tool, I don't want to become over reliant on it.</p> + +<p>I still want to be able to investigate, solve and learn things for myself and enjoy the process.</p> +
+
+ Sun, 12 Jan 2025 00:00:00 GMT +
+ + Who is your BDFL? + /daily/2025/01/11/bdfl + http://localhost:8000/daily/2025/01/11/bdfl + +
<p>Most open source projects have a BDFL (Benevolent dictator for life) who makes the ultimate decisions for the project.</p> + +<p>The ones who spring to mind are Dries, Fabien and Taylor for Drupal, Symfony and Laravel respectively.</p> + +<p>But what about your codebase?</p> + +<p>Who is the BDFL for your application?</p> + +<p>Who decides how features should be implemented or what standards to follow?</p> + +<p>Who ensures the codebase is consistent regardless of the feature being delivered or the file being read?</p> + +<p>If you don't have one, you should.</p> +
+
+ Sat, 11 Jan 2025 00:00:00 GMT +
+ + Just use curl + /daily/2025/01/10/curl + http://localhost:8000/daily/2025/01/10/curl + +
<p>I don't use complicated or bloated applications to test HTTP requests and API endpoints.</p> + +<p>I just use <code>curl</code> on my command line.</p> + +<p>For example, if I want to query the Drupal.org API for my user information, I can run <code>curl https://www.drupal.org/api-d7/user.json?uid=381388</code> and see the response.</p> + +<p>To see the request and response headers, status code, SSL certificate information and more, I can run <code>curl -v</code> to run it in verbose mode.</p> + +<p>If the response returns JSON, I can use <code>jq</code> to format the results.</p> + +<p>If I need to make a POST request, I can use <code>-X POST</code>, I can use <code>--data</code> or <code>--json</code> to send data and <code>--header</code> to send any required headers.</p> + +<p>curl is great program with so many options and no AI bloat, complicated UIs or paid plans.</p> + +<p>Want to see what else it can do?</p> + +<p>Just open your terminal and type <code>man curl</code>.</p> +
+
+ Fri, 10 Jan 2025 00:00:00 GMT +
+ + Refactor, remove or replace + /daily/2025/01/09/rrr + http://localhost:8000/daily/2025/01/09/rrr + +
<p>There are three things you can do with legacy code.</p> + +<p>Refactor it, remove it or replace it.</p> + +<p>You can refactor it to make it easier to maintain and change in the future.</p> + +<p>If it's not needed, you can remove it.</p> + +<p>If it's not used, there's no need to keep it.</p> + +<p>Or you can replace it with something else.</p> + +<p>There isn't one right answer and the correct approach will depend on the situation and objective.</p> + +<p>In a future email, I'll give some examples of how I refactor legacy code.</p> +
+
+ Thu, 09 Jan 2025 00:00:00 GMT +
+ + Don't make assumptions + /daily/2025/01/08/don-t-make-assumptions + http://localhost:8000/daily/2025/01/08/don-t-make-assumptions + +
<p>I was recently writing code for a project and found myself making assumptions about what I was writing.</p> + +<p>I was creating my own requirements.</p> + +<p>Something no-one asked for.</p> + +<p>I was assuming a value was always going to be a certain number of digits long.</p> + +<p>I was writing code that verified this was true or throw an Exception.</p> + +<p>Until I found out that that one of the values wasn't the same length as the others.</p> + +<p>This could be an error in the data or it could correct.</p> + +<p>No-one told me the lengths were always going to be the same.</p> + +<p>So why was I checking it?</p> + +<p>Why was I adding bugs to the code?</p> + +<p>I've reverted the code that checks the length of the value and gone to find clarification.</p> + +<p>If it's an issue, it'll be fixed in the source data.</p> + +<p>If the lengths should all be the same, I'll potentially re-add the check.</p> + +<p>Until I'm sure, I'll only write what's needed to deliver the feature and stop adding my own requirements and assumptions.</p> +
+
+ Wed, 08 Jan 2025 00:00:00 GMT +
+ + It's fun to be competent + /daily/2025/01/07/competent + http://localhost:8000/daily/2025/01/07/competent + +
<p>This was one of the key takeaways for me from DHH's <a href="https://www.youtube.com/watch?v=-cEn_83zRFw">keynote talk from Rails World 2024</a>.</p> + +<p>It's more fun to be competent.</p> + +<p>In this section of the talk, which starts around 32:45, he was speaking about his move from the Cloud to on-prem infrastructure and adopting Linux as his main development environment.</p> + +<p>That people suffer from "server-phobia" when they could learn these skills from themselves.</p> + +<p>As a self-taught Developer, I started to learn coding in 2010 and had to learn everything from scratch - starting with HTML and CSS before introducing PHP.</p> + +<p>Since then, I've learned various other related technologies, including Linux, and exclusively run Linux for my computers and servers.</p> + +<p><a href="http://localhost:8000/blog/going-full-vim">I switched full-time to Neovim</a> in 2021.</p> + +<p>For the past couple of years, I've been using NixOS as my primary operating system.</p> + +<p>It was another language to learn and a different way to think about server configuration and automation.</p> + +<p>But, I recently went to add something to my configuration and knew exactly how I was going to implement it. +I was able to do it quickly and easily as I'd invested enough time to be competent and feel confident and that I knew what I was doing.</p> + +<p>I've been writing a new Drupal module this week and have been in a constant flow state as I knew how I need to implement it.</p> + +<p>I've been doing test-driven development and working through a few different iterations, guided by my tests and ensuring I don't break what I've already written.</p> + +<p>These are skills and knowledge that that took time to learn.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>Whether you're a new Developer getting into programming or an experienced Developer learning something new, you will become competent.</p> + +<p>And it is more fun.</p> + +<p>It just takes time and effort.</p> +
+
+ Tue, 07 Jan 2025 00:00:00 GMT +
+ + Actions, Commands or Services? + /daily/2025/01/06/actions-commands-services + http://localhost:8000/daily/2025/01/06/actions-commands-services + +
<p>Today I started to write a new class and was trying to decide what to name it and what pattern I wanted to follow.</p> + +<p>Option 1:</p> + +<pre><code class="php">class StoreInformationDownloader { + + public function download() { + // ... + } +} +</code></pre> + +<p>Option 2:</p> + +<pre><code class="php">class DownloadStoreInformation { + + public function execute() { + // ... + } +} +</code></pre> + +<p>Option 1 is a typical Service class.</p> + +<p>Option 2 follows the Command or Action pattern.</p> + +<p>Which would you choose?</p> +
+
+ Mon, 06 Jan 2025 00:00:00 GMT +
+ + Drupal is dead. Long live Drupal! + /daily/2025/01/05/drupal-is-dead--long-live-drupal + http://localhost:8000/daily/2025/01/05/drupal-is-dead--long-live-drupal + +
<p>Today is the day!</p> + +<p>Fourteen years after its original release, Drupal 7 is now end-of-life and will not receive any more security or compatibility updates.</p> + +<p>I remember building my first Drupal 7 website while working as a Junior Developer at an agency and have built a lot of Drupal 7 websites since 2011.</p> + +<p>I maintained the Override Node Options module and contributed to other projects, including Drupal Commerce, Commerce Kickstart and Drupal 7 core.</p> + +<p>I cut my teeth with automated testing and test-driven development in Drupal 7 with SimpleTest.</p> + +<p>It's been a big part of my career.</p> + +<p>But I'm looking forward to seeing Drupal continue to evolve and what's to come in Drupal CMS later this month.</p> + +<p>So long, Drupal 7.</p> +
+
+ Sun, 05 Jan 2025 00:00:00 GMT +
+ + Catching up with Mark Conroy + /daily/2025/01/04/catching-up + http://localhost:8000/daily/2025/01/04/catching-up + +
<p>In the last Beyond Blocks podcast of 2024, I caught up with Mark Conroy to discuss open source sustainability and sponsorship, different ways to charge for development services and setting up your own agency.</p> + +<p><a href="http://localhost:8000/podcast/26-mark-conroy-the-confident">Listen to the episode now</a>.</p> + +<p>It was great to speak with Mark again, and I'm looking forward to speaking with the guests I have lined up to be on the podcast soon.</p> + +<p>If you'd like to be on the podcast in 2025 or want to suggest a guest or topic, <a href="mailto:oliver+website@oliverdavies.uk?subject=Beyond Blocks podcast suggestion">get in touch</a>.</p> +
+
+ Sat, 04 Jan 2025 00:00:00 GMT +
+ + todo.txt is the simplest project management tool + /daily/2025/01/03/todotxt + http://localhost:8000/daily/2025/01/03/todotxt + +
<p>I've used a lot of issue trackers and project management tools since I started doing software development in 2007.</p> + +<p>Many come with a lot of features I don't use or make the software overcomplicated.</p> + +<p>I prefer simple software that does it's thing well.</p> + +<p>I manage my projects with a simple todo.txt file in the project directory that contains a list of my current and upcoming tasks.</p> + +<p>I can quickly open or add to it as I work and don't need to open a browser and click through multiple screens to find what I need.</p> + +<p>It works offline, so I don't need an Internet connection if I'm travelling or somewhere where there's no wifi or mobile connectivity.</p> + +<p>I will use a more complicated tool if needed but a simple text file will always be my preferred option.</p> +
+
+ Fri, 03 Jan 2025 00:00:00 GMT +
+ + Anyone can use open source software + /daily/2025/01/02/anyone + http://localhost:8000/daily/2025/01/02/anyone + +
<p>Another great thing about open source software is anyone can use it, contribute to it or provide services for it.</p> + +<p>You don't need to be a large company like Acquia or Red Hat.</p> + +<p>I was a self-taught solo hobbyist Developer when I started building a website for my local Tae Kwon-Do club in 2007 with PHP.</p> + +<p>In 2008, I started learning Drupal, built my own website and started doing freelance Drupal development and consulting work.</p> + +<p>I became a full-time Drupal Developer in 2010 and haven't looked back since.</p> + +<p>I was able to this for free using open source software.</p> +
+
+ Thu, 02 Jan 2025 00:00:00 GMT +
+ + Happy New Year! + /daily/2025/01/01/hny + http://localhost:8000/daily/2025/01/01/hny + +
<p>I can't believe it's 2025!</p> + +<p>It's going to be a busy year with Drupal's 23rd birthday, Drupal 7's end-of-life date and the release of Drupal CMS all happening in January!</p> + +<p>There's the inaugural DrupalCamp England in Cambridge in March that I'm planning to attend, and the Global Contribution Day event in London (and everywhere else).</p> + +<p>I'm looking forward to the upcoming episodes of <a href="http://localhost:8000/podcast">the Beyond Blocks podcast</a> and finding time for more open source contributions and <a href="https://www.youtube.com/@opdavies/streams">coding live streams</a> again.</p> + +<p>This is daily email #714, which means I'll get to 1,000 emails in October - which will be a huge milestone!</p> + +<p>Of course, I want to continue growing my email list, podcast listeners and YouTube followers, so if anything is interesting to you, please share it with anyone else you think would benefit from it.</p> +
+
+ Wed, 01 Jan 2025 00:00:00 GMT +
+ + Why do people still build their own CMSes and frameworks? + /daily/2024/12/31/build + http://localhost:8000/daily/2024/12/31/build + +
<p>A few times in the last couple of years, I've come across companies or agencies writing their own CMSes or frameworks.</p> + +<p>Not as a learning exercise, which I think every Developer does at some point as part of their learning, but to host production applications.</p> + +<p>With so many free and open source options - there are Drupal, WordPress, Symfony and Laravel just in the PHP ecosystem - why would people use custom or proprietary CMSes or frameworks?</p> + +<p>One reason is <a href="http://localhost:8000/daily/2024/12/30/domain-names">vendor lock-in</a>.</p> + +<p>If someone is locked into your CMS, framework or hosting platform, it's difficult for them to move away.</p> + +<p>That's one of the main benefits of open source software.</p> + +<p>There isn't just one company that provides services for it and customers can choose who they want to work with.</p> +
+
+ Tue, 31 Dec 2024 00:00:00 GMT +
+ + Why I don't buy domain names or host websites for clients + /daily/2024/12/30/domain-names + http://localhost:8000/daily/2024/12/30/domain-names + +
<p>I've worked for agencies who, as well as building or developing web applications, will purchase domain names and manage hosting for clients.</p> + +<p>I've worked at agencies who insisted on hosting applications they built.</p> + +<p>This is probably easier for clients initially, but things were harder on any project that needed transferring a domain name or migrating to a new hosting environment.</p> + +<p>I do this for myself, but I don't get involved with domain names or hosting for clients.</p> + +<p>I'm happy to give recommendations based on my experience using platform as a service (PaaS) offering and self hosting, but I want the client to set this up themselves.</p> + +<p>That means it's their account and they can contact someone if there is an issue, but mainly because they're in control.</p> + +<p>I want clients to work with me because they want to and not because they're locked in as I manage their domain name or hosting.</p> +
+
+ Mon, 30 Dec 2024 00:00:00 GMT +
+ + Which environment should I test? + /daily/2024/12/29/which-environment + http://localhost:8000/daily/2024/12/29/which-environment + +
<p>I was recently working with a client and in-house development team who were working in a Git Flow-like way with different branches for each sprint and feature..</p> + +<p>Each sprint branch has its own environment that can be reviewed and tested.</p> + +<p>Once the sprint is complete, the sprint branch is merged into the mainline branch.</p> + +<p>Due to the number of branches and environments, it was hard to tell what was where.</p> + +<p>If they wanted to test a particular change, it could be in various places depending on which branches had been merged and had been deployed.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>My suggestion was to simplify things by limiting the number of branches and environments.</p> + +<p>If they used trunk-based development on a single branch, it would be much easier to find a change and less likely for there to be differences or regressions.</p> + +<p>If they used feature flags, they could control features by toggling them on and off as needed, or enable features for users with a certain role, in a group or a particular email domain.</p> + +<p>There would also be less overhead and process, allowing the team to focus on delivery and not working which branch their change was on or which environment they need to test.</p> +
+
+ Sun, 29 Dec 2024 00:00:00 GMT +
+ + Import or install? + /daily/2024/12/28/import-install + http://localhost:8000/daily/2024/12/28/import-install + +
<p>How do you update your environments?</p> + +<p>I've done it in two different ways on different projects.</p> + +<p>The most common way is to download a database from another environment and import it.</p> + +<p>The downside is that there needs to be a blessed database for everyone to use and someone needs to maintain and update.</p> + +<p>It can also contain user data, such as usernames, email addresses, passwords and webform submissions that you should sanitise or remove.</p> + +<p>The other option is to install the application from scratch.</p> + +<p>For Drupal projects, this means installing using the existing configuration to re-create the content types, fields, views, block types, etc.</p> + +<p>This confirms the configuration is installable or it will fail.</p> + +<p>As it's a fresh installation, it can be run by anyone without needing to maintain an blessed database and doesn't contain any user data.</p> + +<p>Then you can recreate any data, such as users or content, that you need or seed the database with standard data.</p> + +<p>For Drupal, <a href="http://localhost:8000/daily/2024/09/16/experimenting-with-the-default-content-module">I've been using the Default Content module</a> for this, which has been working very well.</p> + +<p>If I can, I much prefer the install approach rather than importing.</p> + +<p>Which do you do, or do you do something else?</p> +
+
+ Sat, 28 Dec 2024 00:00:00 GMT +
+ + How quickly can you create or update an environment? + /daily/2024/12/27/quick + http://localhost:8000/daily/2024/12/27/quick + +
<p>A common reason why environments aren't updated and get out of sync is because it's a time-consuming or complex task.</p> + +<p>The process should be simple to run, quick, reliable and reproducible.</p> + +<p>It's the same as deploying a change to a staging or production environment.</p> + +<p>You want the same result on every time on every environment.</p> + +<p>You want every environment - including <a href="http://localhost:8000/daily/2024/12/25/localhost">local development environments</a> to be as consistent as possible to minimise bugs and errors.</p> + +<p>To do this, I automate things to make them as simple as possible.</p> + +<p>I use <a href="http://localhost:8000/daily/2022/08/15/using-run-file-simplify-project-tasks">run files</a> with commands to import databases, perform updates and run pre-update and post-update tasks.</p> + +<p>I use tools like Nix and <a href="http://localhost:8000/daily/2024/11/11/could-nix-and-devenv-replace-docker-compose">devenv</a> to create identical and reproducible environments.</p> + +<p>The simpler and quicker is it, the more it can and will be done.</p> + +<p>You can also use automation to perform long or complex tasks outside of working hours such as sanitising and importing large databases.</p> + +<p>The more you can automate, the better.</p> +
+
+ Fri, 27 Dec 2024 00:00:00 GMT +
+ + The more differences there are, the more likely there will be bugs + /daily/2024/12/26/differences + http://localhost:8000/daily/2024/12/26/differences + +
<p>The <a href="http://localhost:8000/daily/2024/12/24/moving-changes">harder it is to update an environment</a>, the less often it will be done and the more out of sync your environments will become.</p> + +<p>The more out of sync your environments are, the higher the chance there will be bugs or issues when changes are moved between environments.</p> + +<p>I've had situations where the code I wrote worked for my local database but didn't when moved to staging or production.</p> + +<p>I worked at one company where my development database was refreshed nightly, so my site was at most one day out of sync with production.</p> + +<p>I knew my code would work with the latest production data and not only the data from days, weeks or months before.</p> + +<p>It also meant that I needed to write my changes in an automated and repeatable way so they would be executed on the refreshed database and re-added instead of having to do it manually.</p> + +<p>How in sync are your environments and how similar to production is the environment you're testing against?</p> +
+
+ Thu, 26 Dec 2024 00:00:00 GMT +
+ + Everyone's localhost is an environment + /daily/2024/12/25/localhost + http://localhost:8000/daily/2024/12/25/localhost + +
<p>A few days ago, I asked <a href="http://localhost:8000/daily/2024/12/23/how-many-environments-do-you-need">how many pre-production environments</a> you have for your software application.</p> + +<p>But did you include each Developer's local environment?</p> + +<p>Each developer will have a development environment - either on their computer or a development server - to write and initially test their code before it's deployed to a test, staging or production environment.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>Each Developer's local or development site is an environment too.</p> + +<p>They also <a href="http://localhost:8000/daily/2024/12/24/moving-changes">need to be easily updatable</a> and consistent with the other environments, but they are often overlooked or forgotten.</p> + +<p>When was the last time you or your Developers updated your local environment?</p> +
+
+ Wed, 25 Dec 2024 00:00:00 GMT +
+ + How easily can you move changes between environments? + /daily/2024/12/24/moving-changes + http://localhost:8000/daily/2024/12/24/moving-changes + +
<p>Regardless of <a href="http://localhost:8000/daily/2024/12/23/how-many-environments-do-you-need">how many environments your application has</a>, you need to be able to move changes between them reliably.</p> + +<p>You don't want to configure each environment and make every change by hand.</p> + +<p>You want to automate this as much as possible so your changes are the same every time.</p> + +<p>In Drupal 7, the Features module was used to export changes once and apply them again using a <code>features revert</code> command - although its original use case was to extract reusable features for different applications.</p> + +<p>I've also written a lot of update of update hooks, like <code>mymodule_update_8001</code> to apply changes when database updates are applied.</p> + +<p>Since Drupal 8, we've had configuration management - a first-class way to export and import configuration changes - which I think was one of the best additions to Drupal 8, and something not available in some other CMSes, frameworks and applications.</p> + +<p>There's an ecosystem around configuration management, including Config Split for per-environment configurations and Config Ignore to ignore sensitive information or changes you don't want to manage via imported configuration.</p> + +<p>I recently worked on a project where we didn't have a CI pipeline running configuration imports on each change and things were very difficult to manage. Once that was in place, though, things were much easier, more consistent and changes were quicker to release.</p> +
+
+ Tue, 24 Dec 2024 00:00:00 GMT +
+ + How many environments do you need? + /daily/2024/12/23/how-many-environments-do-you-need + http://localhost:8000/daily/2024/12/23/how-many-environments-do-you-need + +
<p>How many environments do you have for your software application, other than live/production?</p> + +<p>Testing? +Staging? +UAT? +Pilot? +QA? +Dev?</p> + +<p>What do you use each one for?</p> + +<p>What if you only had one pre-prod environment and production?</p> + +<p>What if you only had production?</p> + +<p>What if you only had one branch in your code repository?</p> + +<p>No more merge conflicts.</p> + +<p>No more confusion about which environment to test a change on.</p> + +<p>How much simpler would that make things?</p> +
+
+ Mon, 23 Dec 2024 00:00:00 GMT +
+ + Easy feature flags + /daily/2024/12/22/feature-flags + http://localhost:8000/daily/2024/12/22/feature-flags + +
<p>I recently <a href="http://localhost:8000/daily/2024/12/18/self-hosting-podcast">switched to self hosting</a> the MP3 files for the episodes of the Beyond Blocks podcast.</p> + +<p>The first step was to upload the files, followed by <a href="http://localhost:8000/daily/2024/12/19/working-iteratively">updating the player</a> on the episode pages to use the HTML audio element.</p> + +<p>As I didn't want the player to switch immediately, I wrapped the new code in a feature flag (or feature toggle) to keep the original player active.</p> + +<p>Later, I could swap the player by enabling the feature flag.</p> + +<h2 id="how-i-did-it">How I did it</h2> + +<p>Feature flagging is a straight forward concept.</p> + +<p>You isolate the code you want to be togglable within a conditional - i.e. an <code>if</code> statement - that you can easily change in the future.</p> + +<p>My website built with Sculpin, so I can add <code>self_host_podcast_episodes: false</code> to my sculpin_site.yml file.</p> + +<p>This will be available as <code>site.self_host_podcast_episodes</code> and I can use this in my code to show the appropriate player.</p> + +<p>Something like:</p> + +<pre><code class="twig">{% if site.self_host_podcast_episodes %} + Show the new player. +{% else %} + Show the old player. +{% endif %}</code></pre> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>I like feature flags as you can separate deploying a feature from releasing it.</p> + +<p>The code can be deployed but not active until the feature is enabled.</p> + +<p>It's easy to enable, and easy to revert if needed.</p> + +<p>In Drupal applications, I use the <a href="https://www.drupal.org/project/feature_toggle">Feature Toggle module</a>, so I can toggle feature flags by logging in and updating a checkbox.</p> + +<p>I also <a href="https://www.drupal.org/project/feature_toggle_twig">wrote a module</a> with a Twig function so I can check if a feature toggle is enabled directly in a Twig template - the same as I'm doing in Sculpin.</p> +
+
+ Sun, 22 Dec 2024 00:00:00 GMT +
+ + CD or CDs + /daily/2024/12/21/cd + http://localhost:8000/daily/2024/12/21/cd + +
<p>This week, I re-watched Revolution OS - a 2001 documentary about the history of Linux, open source and the free software movement.</p> + +<p>I was also explaining to my son about how software used to be distributed on CD-ROMs.</p> + +<p>This got me thinking.</p> + +<p>What if we still primarily distributed software on physical media like CD-ROMs?</p> + +<p>What if every release to your software project had to be burned to a disc, transported and inserted into a server to be installed.</p> + +<p>This may take days or weeks instead of seconds or minutes.</p> + +<p>Once a version is distributed, it isn't quick or easy to fix a bug or release a feature.</p> + +<p>You can't simply update the version on every CD that was distributed with some hardware or attached to a computing magazine.</p> + +<p>How would you change writing software if you worked this way?</p> +
+
+ Sat, 21 Dec 2024 00:00:00 GMT +
+ + When would you rather fix a bug? + /daily/2024/12/20/bug + http://localhost:8000/daily/2024/12/20/bug + +
<p>You wrote some code that was pushed to production, but it contained a bug.</p> + +<p>Would you rather it be code you wrote hours, days, weeks, or months ago?</p> + +<p>If you wrote it months ago, you'll have worked on numerous other tasks since then and forgotten all the context and edge cases you had in your mind when you wrote it.</p> + +<p>Before fixing the bug, you need to remember all that context and information - if possible.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>The less time between the code being written and deployed, the more likely you are to remember what you thought when you wrote it.</p> + +<p>If it were code you wrote today or yesterday, it would be easier and quicker for you to fix it.</p> + +<p>And, the quicker your release schedule is, the sooner the fix will be deployed to your users, customers or clients.</p> +
+
+ Fri, 20 Dec 2024 00:00:00 GMT +
+ + Working iteratively + /daily/2024/12/19/working-iteratively + http://localhost:8000/daily/2024/12/19/working-iteratively + +
<p>I recently started <a href="http://localhost:8000/daily/2024/12/18/self-hosting-podcast">self-hosting the episodes for the Beyond Blocks podcast</a>.</p> + +<p>Instead of hosting the episodes on a third party service, the MP3 files are hosted on my server and played using the HTML5 audio element.</p> + +<p>The first step was to add the files to my website codebase and deploy them to the server. This was the smallest deployable iteration of this project.</p> + +<p>The second iteration was to change the player on the episode page.</p> + +<p>This is still a work-in-progress as I need to finish building the podcast feed to submit to iTunes, Spotify, etc.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>I didn't need to wait to finish this before I could change the player, and I didn't need to do that before uploading the MP3 files.</p> + +<p>Each was its own deployable step, and this is how I like to approach tasks.</p> + +<p>I like to break things down as much as possible to find the smallest piece of task that can be committed and deployed.</p> + +<p>Deploying features iteratively is less risky that deploying everything at once.</p> + +<p>You're deploying changes in smaller parts, making it less likely for something to go wrong, and easier to identify and resolve any issues.</p> + +<p>And, if you need to roll back or revert a change, you can affect only the part you need instead of everything.</p> +
+
+ Thu, 19 Dec 2024 00:00:00 GMT +
+ + Self hosting the Beyond Blocks podcast + /daily/2024/12/18/self-hosting-podcast + http://localhost:8000/daily/2024/12/18/self-hosting-podcast + +
<p>As part of my recent digital simplification, I've started hosting the episodes of the <a href="http://localhost:8000/podcast">Beyond Blocks podcast</a> myself.</p> + +<p>I've always had the podcast pages on my website and used them as the primary pages to redirect people for information about the episodes.</p> + +<p>I embedded an audio player from a third party service that were hosting the files, but have replaced it with the native HTML audio element.</p> + +<p>It's simpler, but I like it.</p> + +<p>My website is built with Sculpin, so I was able to do this easily with Twig by adding the path to the MP3 file to each episode:</p> + +<pre><code class="twig">&lt;audio controls&gt; + &lt;source src="/files/bb/episodes/{{ page.episode_filename }}" type="audio/mp3"&gt; +&lt;/audio&gt; +</code></pre> + +<p>This is already live. You can see it on <a href="http://localhost:8000/podcast/25-jess-archer-drush-laravel-prompts">any of the podcast episode pages</a>.</p> + +<p>The other thing I'm using is the feed that publishes episodes to Spotify, iTunes, etc.</p> + +<p>But I have all the information and ability to create this myself.</p> + +<p>This part is still in development and I'll need to test it before switching to it, but it will mean the feed URL will change and people may need to resubscribe.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>Using a hosted service was a great way to get the podcast up and running quickly, but I'm looking forward to having more control over it, even if it involves a little upfront development work.</p> + +<p>But, it will make it easier to post future episodes as I'll be able to do it all in one place.</p> + +<p>I have some new guests lined up for 2025, which I'm looking forward to.</p> + +<p>If you want to be a guest on the podcast or want to make a suggestion for someone I should have on, reply to this email and let me know.</p> +
+
+ Wed, 18 Dec 2024 00:00:00 GMT +
+ + RTFM + /daily/2024/12/17/rtfm + http://localhost:8000/daily/2024/12/17/rtfm + +
<p>I've recently been helping my son to learn to code in Python.</p> + +<p>I've also bought a portable scanner so I can scan documents and add them to Paperless which is <a href="http://localhost:8000/daily/2024/12/01/homelabbing-with-nixos">running on my homelab</a>.</p> + +<p>I've been explaining to my son why he needs to read the error messages to understand why something isn't working, such as a syntax error or an undefined variable.</p> + +<p>After plugging in my scanner and wondering why it wasn't showing in the gscan2pdf application, I found a switch to toggle an auto start setting.</p> + +<p>If auto start is enabled, the scanner only works with its own software.</p> + +<p>Once I disabled it, the scanner appeared.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>Both of these situations were solved by doing the same thing.</p> + +<p>RTFM.</p> + +<p>Reading The "Friendly" Manual.</p> +
+
+ Tue, 17 Dec 2024 00:00:00 GMT +
+ + Browsing in plain text + /daily/2024/12/16/browsing-in-plain-text + http://localhost:8000/daily/2024/12/16/browsing-in-plain-text + +
<p>Did you know you can use programs like lynx or w3m to browse the web on the command line?</p> + +<p>No CSS, JavaScript or images, just plain text.</p> + +<p>Similar to using a screen reader, you can quickly tab between focusable elements like links and buttons to find what you need.</p> + +<p>While it will probably not replace Firefox or Chrome as a daily driver, it is useful to know if you're on a server without a browser or need to quickly find information without distractions.</p> +
+
+ Mon, 16 Dec 2024 00:00:00 GMT +
+ + Notes on Nix + /daily/2024/12/15/notes-on-nix + http://localhost:8000/daily/2024/12/15/notes-on-nix + +
<p>After writing about <a href="http://localhost:8000/daily/2024/11/10/write-plain-text-files">making notes in text files</a>, I came across <a href="https://youtu.be/NasPBjSev88?si=nSaCxdtXuznQ_YYb">a video by Nick Janetakis</a> (previous <a href="http://localhost:8000/podcast/12-nick-janetakis-docker">Beyond Blocks podcast guest</a>), showing a simple Bash application he'd written to manage his own plain text notes.</p> + +<p>I thought I'd try it, but it wasn't available in the nixpkgs store so I wasn't able to install it.</p> + +<p>Some of the Vim and Neovim plugins I use also aren't available and I've added those to my configuration myself and wanted to do the same for this.</p> + +<p>Here's the Nix derivation I wrote:</p> + +<pre><code class="nix">{ pkgs, ... }: + +pkgs.stdenv.mkDerivation rec { + pname = "notes"; + version = "0.3.0"; + + src = pkgs.fetchFromGitHub { + owner = "nickjj"; + repo = "notes"; + rev = "v${version}"; + sha256 = "gyrsTWPT8w4DsRim3jlbjvpXwX/y+7SwLaM+3LVOJdU="; + }; + + buildInputs = with pkgs; [ bash ]; + + installPhase = '' + mkdir -p $out/bin + cp $src/notes $out/bin/notes + chmod +x $out/bin/notes + ''; +} +</code></pre> + +<p>I defined the GitHub repository name and owner, the version number and the required installation steps, and was able to add it to my configuration for use in NixOS and Home Manager.</p> + +<p>Now, I can type <code>notes</code> and easily capture whatever I wanted to document in the appropriate text file..</p> + +<hr /> + +<p>FYI, there is an existing <code>notes</code> program in nixpkgs, but I overwrote it with Nick's, which is another great thing about using Nix!</p> +
+
+ Sun, 15 Dec 2024 00:00:00 GMT +
+ + Fixing a laptop + /daily/2024/12/14/fixing + http://localhost:8000/daily/2024/12/14/fixing + +
<p>Today, I rediscovered my hardware knowledge and fixed my son's laptop - a Lenovo ThinkPad X390.</p> + +<p>Some keys weren't working when pressed, only to have the characters appear a few seconds later.</p> + +<p>Fun fact: I started my career refurbishing and repairing laptop computers.</p> + +<p>My first job was for a local laptop company that refurbished ex-business laptops for resale, followed by working for Panasonic where I'd repair laptops from blue chip clients across Europe.</p> + +<p>I used to build custom PCs, but have done little to no hardware work since I started in software development in 2010.</p> + +<p>Today, though, I took the laptop apart and after checking the motherboard and keyboard were OK, I reseated the keyboard and cleaned any dust from inside the casing.</p> + +<p>It worked!</p> + +<p>If I'd have returned it to a manufacturer or taken it to a local computer repair shop, I'd have had to pay for them to diagnose and (hopefully) resolve the issue.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>Most laptops and phones today are sealed units and not as modular, repairable or upgradable as the devices I used to work on.</p> + +<p>In some cases, you can't upgrade or repair them yourself.</p> + +<p>Similar to <a href="http://localhost:8000/daily/2024/12/13/gitea">having control and ownership of my own data</a>, I like the ability to repair a keyboard myself or to add more memory or storage to my laptop.</p> + +<p>As well as installing and running my own software, <a href="http://localhost:8000/daily/2024/12/01/homelabbing-with-nixos">starting a homelab</a> has also reignited my interest in hardware and being able to upgrade and repair my own devices.</p> +
+
+ Sat, 14 Dec 2024 00:00:00 GMT +
+ + Enjoying a cup of Gitea + /daily/2024/12/13/gitea + http://localhost:8000/daily/2024/12/13/gitea + +
<p>I recently installed an application called Gitea <a href="http://localhost:8000/daily/2024/12/01/homelabbing-with-nixos">on my homelab</a>.</p> + +<p>It's a service for hosting and managing Git repositories, similar to GitHub, GitLab and Bitbucket, but I'm hosting it on my own hardware and I'm in full control of my data.</p> + +<p>I've moved all of my client project code from private repositories in the Cloud to my Gitea instance, as well as some of my own private repositories for work-in-progress or archived projects.</p> + +<p>I plan to keep my open source project repositories on GitHub and Drupal.org, and use Gitea for personal and client project code, but I may move more to Gitea - like my website code and dotfiles - as time goes on.</p> + +<p>And, because I use NixOS for my homelab, installing Gitea was as easy as <code>services.gitea.enable = true</code>.</p> + +<p>There is also a Docker Compose image and Gitea Cloud, but running something like Gitea for your private code is something I'd like to see more Developers and agencies do.</p> +
+
+ Fri, 13 Dec 2024 00:00:00 GMT +
+ + Local merging and squashing + /daily/2024/12/12/local-squashing + http://localhost:8000/daily/2024/12/12/local-squashing + +
<p>Because <a href="http://localhost:8000/daily/2022/09/20/why-like-trunk-based-development">I do trunk-based development</a>, I typically don't create branches in Git.</p> + +<p>If I do, I usually want to keep the commit messages, but there are some situations where I want to squash everything into a single commit with a simple message.</p> + +<p>Typically, I'd do this using a rebase command like <code>git rebase -i main</code> and get an output like this, showing the commits:</p> + +<pre><code class="plain">pick d21fafc Remove encrypted disk configuration +pick ffc1296 Update mount location for media directories +pick 58f645e Add multigrep from TJ's video +pick bbd5052 Revert "Remove encrypted disk configuration" +pick 8280c49 Show more generations on boot +pick d3b0c48 Revert "Add encrypted media drive" +pick 8cdc6a5 Add paperless-ngx +pick 73d801d Add Nick Janetakis' `notes` script +pick 99b6e3b Set options for text files +</code></pre> + +<p>I can change <code>pick</code> to <code>squash</code> or <code>fixup</code> and decide what to do for each commit.</p> + +<p><code>squash</code> combines the commit with the previous one, including the commit messages.</p> + +<p><code>fixup</code> uses the previous commit message and discards the current one.</p> + +<p>Once the file is saved, the rebase is performed and the actions are executed.</p> + +<h2 id="there-is-another-way">There is another way</h2> + +<p>If you don't want any of the commit messages, you can also use <code>git merge --squash</code>.</p> + +<p>This will automatically squash all the commits - ready for you to run <code>git commit</code> and provide the final message.</p> + +<p>There's no option to merge or combine the previous messages, though.</p> + +<p>If you want to do that, you can still use the rebase approach.</p> + +<p>If not, use <code>git merge --squash</code>.</p> +
+
+ Thu, 12 Dec 2024 00:00:00 GMT +
+ + Other plain texters + /daily/2024/12/11/other-plain-texters + http://localhost:8000/daily/2024/12/11/other-plain-texters + +
<p>After writing about <a href="http://localhost:8000/daily/2024/11/10/write-plain-text-files">using text files for my personal wiki</a>, I've found others who do the same.</p> + +<ul> +<li>Derek Sivers wrote a blog post about <a href="https://sive.rs/plaintext">why he uses text files</a>.</li> +<li>Ricky Kresslein read Derek's blog post and migrated his notes, <a href="https://kressle.in/txt">wrote a blog post</a> and recorded a video.</li> +<li>Sebastian Daschner recorded a video about <a href="https://www.youtube.com/watch?v=-afP8cpBpXU">a simple knowledge system</a> using plain-text files and asciidoc.</li> +<li>Nick Janetakis, a former guest <a href="http://localhost:8000/podcast/nick-janetakis-docker">on the Beyond Blocks podcast</a>, wrote a small Bash script to maintain his plain text files, which I've <a href="https://github.com/opdavies/dotfiles/commit/73d801d7acf53d82f341f738c655fb6a2fcdcd70">added to my NixOS configuration</a>.</li> +</ul> + +<p>Do you know anyone else who uses plain text files for their notes?</p> + +<p>If you don't, I'd recommend it.</p> +
+
+ Wed, 11 Dec 2024 00:00:00 GMT +
+ + Docker and NixOS playing nicely together + /daily/2024/12/10/docker-nixos + http://localhost:8000/daily/2024/12/10/docker-nixos + +
<p>As I've added software to my NixOS homelab server, I've found some things that aren't yet available in the nixpkgs repository.</p> + +<p>A common approach seems to be to use Docker and Docker Compose to run things in containers, especially ones that need multiple services like a web server, a database and a cache or proxy.</p> + +<p><a href="http://localhost:8000/daily/2024/12/09/drupal-devenv">I've moved my development projects from Docker to devenv</a>, but I still have Docker installed and available.</p> + +<p>I knew it was easy to create a container image from a Nix-based setup, but the opposite is also true.</p> + +<p>It's very easy to run containers within NixOS.</p> + +<p>For example, here's <a href="https://github.com/opdavies/dotfiles/blob/8cdc6a511dab5a31b1bc0a90bcdcf361043498e7/nix/modules/nixos/features/homelab/tubearchivist-container.nix">how I've configured Tube Archivist</a>.</p> + +<p>This was created using a tool called <code>compose2nix</code>, but I've seen simpler examples for other services.</p> + +<p>This made it easy to take a Docker Compose file and convert it to a Docker or Podman container.</p> + +<p>My preference is to use NixOS services if they exist, but it's great to be able to do this as an alternative when needed.</p> +
+
+ Tue, 10 Dec 2024 00:00:00 GMT +
+ + Running Drupal on devenv + /daily/2024/12/09/drupal-devenv + http://localhost:8000/daily/2024/12/09/drupal-devenv + +
<p>I recently said that <a href="http://localhost:8000/daily/2024/11/30/using-nix-for-local-application-development">I've been using devenv to run Drupal applications locally</a>.</p> + +<p>Here's the <code>devenv.nix</code> file I used for <a href="http://localhost:8000/presentations/tdd-test-driven-drupal">my recent talk for the Drupal London meetup</a>:</p> + +<pre><code class="nix">{ pkgs, ... }: + +let + drupal.root = "web"; +in +{ + packages = [ pkgs.git ]; + + dotenv.disableHint = true; + + languages = { + php = { + enable = true; + version = "8.2"; + + ini = '' + memory_limit = 256M + ''; + + fpm.pools.web = { + listen = "127.0.0.1:9000"; + + settings = { + "pm" = "dynamic"; + "pm.max_children" = 75; + "pm.max_requests" = 500; + "pm.max_spare_servers" = 20; + "pm.min_spare_servers" = 5; + "pm.start_servers" = 10; + }; + }; + }; + }; + + processes = { }; + + services = { + caddy = { + enable = true; + + config = '' + { + http_port 8080 + } + + localhost:8080 { + root * ${drupal.root} + encode gzip + php_fastcgi localhost:9000 + file_server + } + ''; + }; + + mysql = { + enable = true; + initialDatabases = [ { name = "drupal_london"; } ]; + }; + }; + + enterShell = '' + if [[ ! -d vendor ]]; then + composer install + fi + ''; + + enterTest = '' + phpunit --testdox + ''; +} +</code></pre> + +<p>It installs the required version of PHP for this project, creates a web server (Caddy) and configures MariaDB with a default database.</p> + +<p>I've also added commands to run <code>composer install</code> to download dependencies when entering the shell and <code>phpunit</code> for tests.</p> + +<p>With this approach, there's no need for containers as everything is run locally and I can view the site at <a href="http://localhost:8080">http://localhost:8080</a> (or whatever port is defined).</p> + +<p>All of my current development projects are using this approach and I think it'll be my default method for future projects.</p> +
+
+ Mon, 09 Dec 2024 00:00:00 GMT +
+ + Using open source software to build open source software + /daily/2024/12/08/building-open-source + http://localhost:8000/daily/2024/12/08/building-open-source + +
<p>As well as using <a href="http://localhost:8000/daily/2024/12/07/open-source">free and open source software for my homelab</a>, I develop software applications for clients using open source software, such as PHP, Drupal, Symfony and Sculpin.</p> + +<p>I also use open source software to do this.</p> + +<p>I use <a href="http://localhost:8000/daily/2024/09/08/my-laptop-died">NixOS as the operating system on my laptop</a>.</p> + +<p>I use Alacritty as my terminal emulator and tmux as a terminal multiplexer to have different sessions, windows and tabs for each project.</p> + +<p>I write my code in Neovim and use various additional plugins.</p> + +<p>I use Git for source control and tools like PHPStan, PHPUnit and Pest to run checks on my code.</p> + +<p><a href="http://localhost:8000/daily/2024/11/30/using-nix-for-local-application-development">I use Nix and devenv</a> to run the applications locally and host them on Linux servers running Nginx.</p> + +<p>I also use OBS, Kdenlive and GIMP to record and edit my content, which are all also open source projects.</p> + +<p>I run an open source business, using open source tools and projects and contribute to and <a href="http://localhost:8000/daily/2024/03/09/override-node-options-40624-drupal-websites">maintain my own open source software projects</a>.</p> +
+
+ Sun, 08 Dec 2024 00:00:00 GMT +
+ + Open source software all the way down + /daily/2024/12/07/open-source + http://localhost:8000/daily/2024/12/07/open-source + +
<p>As someone who develops with open-source software such as Drupal, Symfony, and Sculpin and uses Linux to <a href="http://localhost:8000/daily/2024/11/28/running-nixos-in-the-cloud">host my applications</a> and <a href="http://localhost:8000/daily/2024/11/23/no-more-random-packages">configure my laptop</a>, I've recently started to explore creating a home lab and self-hosting services based on other open source software.</p> + +<p>I've started using Jellyfin for media management and playback, Immich for photos, Gitea for hosting my private Git repositories, and Tube Archivist for backing up YouTube videos.</p> + +<p>There are a lot of other popular applications that people self-host that I want to look at, as well as maybe hosting my own website.</p> + +<p>I've removed proprietary note-taking applications <a href="http://localhost:8000/daily/2024/11/10/write-plain-text-files">in favour of plain text files</a> and continued searching for free and open source alternatives to services I've used.</p> + +<p>My ethos is to be open source first and to favour an open source solution if there is one.</p> + +<p>And if there isn't, I can write one.</p> + +<p>As well as creating free and open source software, I want to use it as much as I can as well as using open source software to create my open source software.</p> +
+
+ Sat, 07 Dec 2024 00:00:00 GMT +
+ + One more month of Drupal 7 + /daily/2024/12/06/one-more-month + http://localhost:8000/daily/2024/12/06/one-more-month + +
<p>On the 5th of January, Drupal 7 will be end of life and have no further updates.</p> + +<p><a href="https://www.drupal.org/blog/drupal-70-is-released">Released in January 2011</a>, Drupal 7 will be 14 years old in January and will have outlived Drupal 8 and 9 which are already unsupported.</p> + +<p>There are a lot of Drupal 7 sites still in production, but it's been interesting to see usage numbers for the Override Node Options module <a href="http://localhost:8000/daily/2024/11/16/an-interesting-thing-i-spotted-about-the-override-node-options-module">decrease for Drupal 7 and increase for Drupal 8+</a> over the last year.</p> + +<p>I'm excited to see Drupal usage continue to move towards the newer versions so everyone can benefit from the new features and improvements in Drupal 10 and 11 and the new Drupal CMS project.</p> +
+
+ Fri, 06 Dec 2024 00:00:00 GMT +
+ + Do you even need JavaScript? + /daily/2024/12/05/javascript + http://localhost:8000/daily/2024/12/05/javascript + +
<p>I've been through several iterations of my website over the years and used different technologies.</p> + +<p>The first versions were on Drupal 6 and 7 before I started to explore static site generators like Jekyll and Sculpin.</p> + +<p>One version was written with a JavaScript-based static site generator, but it needed a lot of JavaScript to render a simple HTML website.</p> + +<p>The current version has no JavaScript at all.</p> + +<p>There is no analytics script, carousels or fancy transitions.</p> + +<p>I keep the functionality as simple as possible.</p> + +<p>It's fast, accessible and responsive.</p> + +<p>If I'm considering adding something, I ask "do I really need this?".</p> + +<p>What impact will it have? Positive or negative.</p> + +<p>Do I want to take on the overhead of maintaining it?</p> + +<p>If not, I don't add it.</p> + +<p>If you don't already ask this in your projects, start.</p> +
+
+ Thu, 05 Dec 2024 00:00:00 GMT +
+ + Sculpin from Scratch + /daily/2024/12/04/sculpin-from-scratch + http://localhost:8000/daily/2024/12/04/sculpin-from-scratch + +
<p>Over the next few weeks, I'm going to create another email course, this time about building websites with the <a href="https://sculpin.io">Sculpin static site generator</a>.</p> + +<p>I've been using Sculpin since 2015 when <a href="http://localhost:8000/presentations/test-drive-twig-with-sculpin">I used it to learn Twig</a> before Drupal 8's release and I use it to generate my website.</p> + +<p>I've recently been <a href="http://localhost:8000/presentations/building-static-websites-sculpin">speaking about it at user groups</a> like PHP South West, BrumPHP and PHP Berkshire.</p> + +<p>Similar to my <a href="http://localhost:8000/atdc">automated testing email course</a>, this will send a lesson every day straight to your inbox and will show how to build a website with Sculpin from scratch.</p> + +<p>If you want to be added when it's ready or have questions, reply to this email and let me know.</p> +
+
+ Wed, 04 Dec 2024 00:00:00 GMT +
+ + Adopt a Document + /daily/2024/12/03/adopt-a-document + http://localhost:8000/daily/2024/12/03/adopt-a-document + +
<p>The Drupal Association recently launched the "Adopt a Document" program for Drupal.org, making documentation pages available for adoption.</p> + +<p>From <a href="https://www.drupal.org/about/starshot/blog/adopt-a-document-new-program-to-bring-drupal-documentation-to-the-next-level">the blog post</a>:</p> + +<blockquote> + <p>Inspired by the “Adopt a Highway” program that is becoming more and more popular in North America, we created a way for the partners to get involved in creating great documentation by taking care of one of the Drupal CMS milestones and sponsoring creation and further maintenance of the documentation for one or multiple Drupal CMS working tracks.</p> + + <p>Thanks to your support, future users of Drupal CMS will be able to easily find answers to the questions that might arise as they discover Drupal CMS capabilities.</p> +</blockquote> + +<p>I think this is a great way for Drupal's documentation to continue improving and to give companies another way to contribute to the Drupal project.</p> + +<p>If you were looking for a way to contribute to Drupal, this may be a good option.</p> +
+
+ Tue, 03 Dec 2024 00:00:00 GMT +
+ + Override Node Options and Drupal 11 + /daily/2024/12/02/override-node-options-and-drupal-11 + http://localhost:8000/daily/2024/12/02/override-node-options-and-drupal-11 + +
<p>Last week, I released a new version of the <a href="https://www.drupal.org/project/override_node_options">Override Node Options module</a> - version 8.x-2.9.</p> + +<p>This version makes the module compatible with Drupal 11 and, as there are no breaking changes, it's still compatible with Drupal 9 and 10.</p> + +<p>It's great to see the module used on many Drupal 8+ websites and distributions such as <a href="http://localhost:8000/daily/2024/11/17/override-node-options-used-by-localgov-drupal">LocalGov Drupal</a>.</p> + +<p>Whilst <a href="http://localhost:8000/daily/2024/11/16/an-interesting-thing-i-spotted-about-the-override-node-options-module">the overall number of installations has been consistent</a>, the number of Drupal 7 installations has decreased whilst the Drupal 8+ version installations have increased.</p> + +<p>With a Drupal 11-compatible version now available, I hope it continues to increase.</p> +
+
+ Mon, 02 Dec 2024 00:00:00 GMT +
+ + Homelabbing with NixOS + /daily/2024/12/01/homelabbing-with-nixos + http://localhost:8000/daily/2024/12/01/homelabbing-with-nixos + +
<p>As well as <a href="http://localhost:8000/daily/2024/11/27/nix-as-an-operating-system">my laptop configuration</a>, <a href="http://localhost:8000/daily/2024/11/30/using-nix-for-local-application-development">local development environments</a> and <a href="http://localhost:8000/daily/2024/11/28/running-nixos-in-the-cloud">production server</a>, I've also been using Nix for something else recently.</p> + +<p>Setting up and configuring a Homelab on an old laptop.</p> + +<p>I've been able to install and configure services like Jellyfin for playing video files, Immich for photo hosting and management, Gitea as my own Git server, Vaultwarden for securely storing my passwords and Traefik as a reverse proxy.</p> + +<p>Each of these was very easy to configure with only a few lines of the Nix language and avoided a heavy use of Docker which seems common in most Homelab setups.</p> + +<p>Next, I'd like to add home automation with Home Assistant and explore running a local DNS server within my network.</p> + +<p>I'm looking forward to these, and seeing what else I can add to this setup using Nix and NixOS.</p> +
+
+ Sun, 01 Dec 2024 00:00:00 GMT +
+ + Using Nix for local application development + /daily/2024/11/30/using-nix-for-local-application-development + http://localhost:8000/daily/2024/11/30/using-nix-for-local-application-development + +
<p>Instead of using tools like Docker or nvm to manage dependencies for your projects, you can use Nix instead.</p> + +<p>Creating a Nix shell or flake for each project with its dependencies will install everything without needing containers and with the benefit of everything being locked to specific versions, <a href="http://localhost:8000/daily/2024/11/12/why-consistency-and-reproducibility-are-important">making environments reproducible</a>.</p> + +<p>If you need a specific version of PHP or node for a project, it will be available and different versions can be used for other projects.</p> + +<p>And if you need services like MySQL and you're not using NixOS, you can also use devenv to manage services, tasks and processes for each project.</p> + +<p>For me, Nix and devenv have <a href="http://localhost:8000/daily/2024/11/11/could-nix-and-devenv-replace-docker-compose">replaced Docker and Docker Compose</a> on my development projects.</p> +
+
+ Sat, 30 Nov 2024 00:00:00 GMT +
+ + Managing dotfiles with Nix + /daily/2024/11/29/managing-dotfiles-with-nix + http://localhost:8000/daily/2024/11/29/managing-dotfiles-with-nix + +
<p><a href="http://localhost:8000/daily/2024/11/25/nix-the-package-manager">As well as Nix managing packages</a> and <a href="http://localhost:8000/daily/2024/11/27/nix-as-an-operating-system">NixOS as your operating system</a>, you can use Nix to manage your user configuration and dotfiles.</p> + +<p>Enter, Home Manager.</p> + +<p>Home Manager is available as a module for NixOS and a standalone package for other Linux distributions and macOS.</p> + +<p>With it, you can install and configure programs for specific users and create and manage dotfiles such as .gitconfig, .tmux.conf and .zshrc instead of using a tool like Stow.</p> + +<p>There's also the extra benefit that <a href="http://localhost:8000/daily/2024/11/21/one-configuration-language-to-rule-them-all">you can write these files in the Nix language</a> and only focus on one configuration language.</p> + +<p>Nix and Home Manager will create the desired output file in whatever the program wants, whether it's JSON, ini, YAML or something else.</p> + +<p>A good example is <a href="https://github.com/opdavies/dotfiles/blob/3acd73f6a2e19eadcc16baf22afad5dfad5e049b/nix/modules/home-manager/features/cli/git.nix">my .gitconfig configuration</a>. It includes settings for Git itself, aliases, global excludes, adds extra packages such as <a href="https://zet.oliverdavies.uk/notes/10">git-instafix</a> and some environment variables.</p> + +<p>All in one file and all in one language.</p> +
+
+ Fri, 29 Nov 2024 00:00:00 GMT +
+ + Running NixOS in the Cloud + /daily/2024/11/28/running-nixos-in-the-cloud + http://localhost:8000/daily/2024/11/28/running-nixos-in-the-cloud + +
<p>Yesterday I explained that Nix, or specifically NixOS, <a href="http://localhost:8000/daily/2024/11/27/nix-as-an-operating-system">can be used to manage your entire operating system</a> in a declarative and reproducible way.</p> + +<p>My initial experience was running it on my laptop as a replacement for another Linux distribution, which I use to configure everything about my laptop and development environment, including my i3 window manager, Neovim and tmux configurations.</p> + +<p>I recently also started to use it on a new VPS to host several static websites, including this one and <a href="http://localhost:8000/blog/uis-ive-rebuilt-tailwind-css">various examples I've created as demos</a> or for presentations.</p> + +<p>Similarly to my laptop, I was able to declaratively install any required utilities, enable the Nginx web server, open firewall ports, add my virtual hosts and create and apply the required SSL certificates.</p> + +<p>And I can do this locally using <a href="https://github.com/opdavies/dotfiles/tree/ec2767adfb6667184f884080a4f9b5d2a388a03d/nix/hosts/hetznix">the same NixOS configuration files</a> and applying it to the remote server.</p> + +<p>Now I'm running NixOS everywhere!</p> +
+
+ Thu, 28 Nov 2024 00:00:00 GMT +
+ + Nix as an operating system + /daily/2024/11/27/nix-as-an-operating-system + http://localhost:8000/daily/2024/11/27/nix-as-an-operating-system + +
<p>Yesterday, <a href="http://localhost:8000/daily/2024/11/26/the-nix-language">I showed some of the Nix programming language</a> and how to use it to perform tasks such as installing and configuring packages.</p> + +<p>I like this declarative approach as you know everything installed on your system and its configuration.</p> + +<p><a href="http://localhost:8000/daily/2024/11/23/no-more-random-packages">There are no random or leftover packages or configuration files</a>.</p> + +<p>The Nix package manager can be installed on Linux or macOS, but you can also use Nix to configure your whole operating system in a declarative way thanks to NixOS.</p> + +<p>As well as installing and configuring packages, you can define your firmware version, boot loader, disk partitioning, user accounts and more.</p> + +<p>I used to do this type of automation with Ansible but switched to NixOS soon after trying Nix on another Linux distribution.</p> + +<p>If I need to work on a different laptop, like when mine recently died, I can apply the same configuration and get all the same programs and configurations as before.</p> + +<p>If you want to see my current NixOS setup, you can <a href="https://github.com/opdavies/dotfiles">see it on my GitHub</a>.</p> +
+
+ Wed, 27 Nov 2024 00:00:00 GMT +
+ + The Nix language + /daily/2024/11/26/the-nix-language + http://localhost:8000/daily/2024/11/26/the-nix-language + +
<p>Yesterday, I wrote about <a href="http://localhost:8000/daily/2024/11/25/nix-the-package-manager">Nix the package manager</a>.</p> + +<p>To use it, you need to write code in the Nix language in .nix files.</p> + +<p>To see an example, you can see <a href="https://github.com/opdavies/dotfiles/tree/main/nix">my dotfiles on GitHub</a> as well as lots of other people's that they've published.</p> + +<p>It's a functional language so some of the concepts were new to me, but I picked it up fairly quickly and learned some of the paradigms and conventions.</p> + +<p>This is the code that installs Nginx on my server:</p> + +<pre><code class="nix">services.nginx = { + enable = true; + serverNamesHashBucketSize = 256; +}; +</code></pre> + +<p>These are some of the packages I have installed on my laptop:</p> + +<pre><code class="nix">environment.systemPackages = with pkgs: { + devenv + dog + git + go + jq + php + phpPackages.composer + pv + tldr +} +</code></pre> + +<p>And this is how to configure processes <a href="http://localhost:8000/daily/2024/11/11/could-nix-and-devenv-replace-docker-compose">in a devenv configuration</a>, which is built with Nix:</p> + +<pre><code class="nix">processes = { + tailwind.exec = '' + cd ${drupal.theme.path} + watchexec --exts css,twig tailwindcss --config assets/tailwind.config.ts \ + --output dist/tailwind.css + ''; +}; +</code></pre> + +<p>Once you have written the configuration, you can run it and install what you've specified, and it will do it the same way every time.</p> + +<p>For a crash course in the Nix language, take a look at <a href="https://zero-to-nix.com/concepts/nix-language">https://zero-to-nix.com/concepts/nix-language</a> or one of the many open-sourced configurations on GitHub.</p> +
+
+ Tue, 26 Nov 2024 00:00:00 GMT +
+ + Nix, the package manager + /daily/2024/11/25/nix-the-package-manager + http://localhost:8000/daily/2024/11/25/nix-the-package-manager + +
<p>In previous emails, <a href="http://localhost:8000/daily/2024/11/21/one-configuration-language-to-rule-them-all">I've written briefly about Nix</a>. Over the next few days, I want to write more about it and explain the different components of the Nix ecosystem and how I use them.</p> + +<p>Firstly, Nix is a package manager, similar to apt on Ubuntu or homebrew on MacOS.</p> + +<p>It contains over 100,000 packages that can be installed once you've installed Nix and if you're on a Mac, there's nix-darwin to have it manage macOS settings too.</p> + +<p>There are two stable releases a year and a rolling "unstable" version so you can be as stable or up-to-date as you like, or you can mix and match in the same configuration.</p> + +<p>It's easy to add custom packages and apply overrides to existing packages.</p> + +<p>You can have multiple versions of the same package installed at once.</p> + +<p>And <a href="http://localhost:8000/daily/2024/11/12/why-consistency-and-reproducibility-are-important">because Nix is reproducible</a>, you can get exactly the same configuration over and over again.</p> + +<p>In another email, I'll write about NixOS, but you don't need to use it to use Nix the package manager.</p> + +<p>I used a different Linux distribution when I started using Nix and installed it as a secondary package manager.</p> + +<p>If you're looking for an alternative package manager for Linux or macOS, I recommend giving Nix a try.</p> +
+
+ Mon, 25 Nov 2024 00:00:00 GMT +
+ + A modest JavaScript framework for the HTML you already have + /daily/2024/11/24/a-modest-javascript-framework-for-the-html-you-already-have + http://localhost:8000/daily/2024/11/24/a-modest-javascript-framework-for-the-html-you-already-have + +
<p>After starting with jQuery in Drupal 6 and moving to full JavaScript frameworks like Vue, I've recently been using Stimulus when adding JavaScript to my projects.</p> + +<p>After finding it via SymfonyCasts and Symfony UX (I can't remember if Ryan and I discussed it <a href="http://localhost:8000/podcast/10-ryan-weaver-symfonycasts">in our podcast episode</a>), I like that it works on top of my existing HTML and Twig templates instead of having to rewrite or duplicate all the markup.</p> + +<p>You enable and configure it with data attributes like <code>data-controller</code> and <code>data-action</code> in your HTML which refer to separate JavaScript classes and methods, so you have some structure and organisation compared to putting all the logic in Twig.</p> + +<p>Having separate controllers also makes it easy to reuse logic in other parts of your application or extract it for use in other projects.</p> + +<p>If you want an example of how I've set up stimulus with esbuild, <a href="https://github.com/opdavies/stimulus-esbuild-example">take a look at this GitHub repository</a>, or <a href="https://symfonycasts.com/screencast/stimulus">watch the Stimulus course on SymfonyCasts</a>.</p> + +<p>I skipped the lessons on Stimulus before and didn't try it for a while, but I'm glad I have.</p> +
+
+ Sun, 24 Nov 2024 00:00:00 GMT +
+ + No more random packages + /daily/2024/11/23/no-more-random-packages + http://localhost:8000/daily/2024/11/23/no-more-random-packages + +
<p>Do you know every software package installed on your computer?</p> + +<p>Do you know every configuration file you've added or edited?</p> + +<p>If you needed to create a new computer or server that matches your current configuration, how long would that take you?</p> + +<p>This is why I started automating my system and having tools like Ansible install and configure software instead of me doing it manually.</p> + +<p>But, that doesn't prevent anyone from adding more things or changing files on the fly.</p> + +<p>Nix and NixOS have a read-only store of packages and configuration files, and I can <a href="https://github.com/opdavies/dotfiles">read my dotfiles repository</a> and see exactly what's installed and how it's configured.</p> + +<p>If I want to add or remove a package or change some configuration, I update a Nix file and rebuild my system.</p> + +<p>There are no more random, rogue or leftover packages.</p> + +<p>If I get a new computer or create a new server, I add it to my dotfiles repository and apply the configuration and I'm up and running in no time.</p> +
+
+ Sat, 23 Nov 2024 00:00:00 GMT +
+ + Building different-looking UIs with consistent class names + /daily/2024/11/22/building-different-looking-uis-with-consistent-class-names + http://localhost:8000/daily/2024/11/22/building-different-looking-uis-with-consistent-class-names + +
<p>Something I've said when <a href="http://localhost:8000/presentations/taking-flight-with-tailwind-css.md">speaking about Tailwind CSS</a> and also during <a href="http://localhost:8000/daily/2024/11/19/tailwind-css-v4-is-so-easy-to-set-up">my recent consulting engagement</a>, is that you can build dramatically different-looking UIs even though you're using the same class names.</p> + +<p>For example, I've rebuilt <a href="https://rebuilding-bartik.oliverdavies.uk">the Bartik theme</a>, <a href="https://rebuilding-acquia.oliverdavies.uk">the Acquia hosting dashboard</a> and <a href="https://rebuilding-bristol-js.oliverdavies.uk">various other UIs</a> for talk demos - none of which look the same or are even similar.</p> + +<p>This website is themed with Tailwind CSS.</p> + +<p>As the classes are very low-level, there is no "Tailwind-looking website" like there is with other CSS frameworks.</p> + +<p>And, when you move to other application, the same classes are there and available to use.</p> + +<p>You just need to customise them, use what you need and ignore what you don't.</p> +
+
+ Fri, 22 Nov 2024 00:00:00 GMT +
+ + One configuration language to rule them all + /daily/2024/11/21/one-configuration-language-to-rule-them-all + http://localhost:8000/daily/2024/11/21/one-configuration-language-to-rule-them-all + +
<p>I started using the Nix package manager <a href="https://github.com/opdavies/dotfiles.nix/commit/af1d8d37">in September 2022</a> as a replacement for Ansible.</p> + +<p>Since then, I've switched to daily-driving NixOS as my main operating system and Home Manager to manage my dotfiles (configuration files for managing application settings, like Neovim, tmux and Alacritty).</p> + +<p>A benefit I didn't initially think of was that now I can write all my configuration files in the Nix language.</p> + +<p>I don't need to write YAML, TOML, INI or JSON configuration files. Nix and Home Manager will convert it for me.</p> + +<p>For example, see <a href="https://github.com/opdavies/dotfiles/blob/172b7c9ca61d2dd6ffdc967af9102b1ca24edd81/nix/modules/home-manager/git.nix#L25">my Git configuration written in Nix</a> which is converted and written to .gitignore.</p> + +<p>And, if there isn't a built-in module for what I need, there are functions like <code>toJSON</code> that will convert Nix code to JSON that I can write to a file.</p> + +<p>This is also great if a program changes its configuration file language, which Alacritty did recently.</p> + +<p>They changed from YAML to TOML and I didn't need to change anything.</p> + +<p>Nix has become my one configuration language to rule them all.</p> +
+
+ Thu, 21 Nov 2024 00:00:00 GMT +
+ + Live coding is hard + /daily/2024/11/20/live-coding-is-hard + http://localhost:8000/daily/2024/11/20/live-coding-is-hard + +
<p>This week, I gave my <a href="http://localhost:8000/talks/tdd-test-driven-drupal">Test Driven Drupal talk</a> to the Drupal London user group.</p> + +<p>The majority of the talk was the same as in previous versions, but I decided to live code the demo section this time instead of demonstrating with slides.</p> + +<p>I've done live coding demos in talks before and have done <a href="https://www.youtube.com/@opdavies">live streams on YouTube</a>, but live coding during a talk is harder as there's a time limit before the next speaker starts or before everyone gets thrown out of the building before it closes.</p> + +<p>I've seen live coding demos go well and take a lot from them, but I've also seen them not go well and it can be stressful and awkward for the speaker as well as the audience.</p> + +<p>I had some unexpected issues during my demo which the audience were able to help me with, which is why I enjoy pair and group programming. People can spot my typos for me!</p> + +<p>When done well, I think live coding can be very impactful when it's done well.</p> + +<p>It's something I want to get better at, and I plan to do more YouTube streams soon, such as refreshing my <a href="http://localhost:8000/atdc">automated testing email course</a> with some new examples, so subscribe on YouTube if you want to know when I next go live.</p> +
+
+ Wed, 20 Nov 2024 00:00:00 GMT +
+ + Tailwind CSS v4 is so easy to set up + /daily/2024/11/19/tailwind-css-v4-is-so-easy-to-set-up + http://localhost:8000/daily/2024/11/19/tailwind-css-v4-is-so-easy-to-set-up + +
<p>I've recently been working with a client to kickstart the front-end development on their Drupal upgrade project from Drupal 7 to 10.</p> + +<p>They've been working hard on the back-end code and migrations, but hadn't done anything on the front-end yet.</p> + +<p>I was asked to come in and set up a new theme for them and provide some training to get them going.</p> + +<p>A requirement they mentioned was to make it themable, so they could later change colours of buttons and other elements based on the page type being viewed or if they added sub-sites in the future.</p> + +<p>This is something I've done a lot with Tailwind CSS and, as it's <a href="http://localhost:8000/blog/uis-ive-rebuilt-tailwind-css">the CSS framework I'm most familiar with</a>, we decided to go with Tailwind CSS - and specifically the alpha version of Tailwind CSS v4.</p> + +<p>Although it's still in an alpha phase, I've been experimenting with Tailwind v4 in personal projects and on some live streams and it seemed stable enough to use in production.</p> + +<p>It also makes theming a lot easier as it uses CSS variables everywhere, which made it a no-brainer for this project.</p> + +<p>Aside from the new features, the setup for Tailwind CSS is so easy and straight forward.</p> + +<p>There's no JavaScript configuration file and everything is done in a CSS file, making it simpler to set up and more consistent if you need to write CSS anyway to add custom fonts or work with CSS variables - these are no longer in separate places and are now combined in one stylesheet.</p> + +<p>All the colour configuration and adding plugins is done in the CSS file and Tailwind will find the content files to scan automatically so you no longer need to declare where your template files are - it just knows.</p> + +<p>It's worked well for this project, and I'll look forward to using Tailwind v4 more on YouTube and for personal and client projects.</p> +
+
+ Tue, 19 Nov 2024 00:00:00 GMT +
+ + drush deploy + /daily/2024/11/18/drush-deploy + http://localhost:8000/daily/2024/11/18/drush-deploy + +
<p>This week I learned about the <a href="https://www.drush.org/13.x/deploycommand">drush deploy command</a>.</p> + +<p>It's been available since Drush 10.3 and combines several commands together in an attempt to "standardise how Drupal deployments work".</p> + +<p>These commands are:</p> + +<pre><code class="shell">drush updatedb +drush config:import +drush cache:rebuild +drush deploy:hook +</code></pre> + +<p>Usually, I run these commands separately but grouping them and standardising makes a lot of sense.</p> + +<p><code>drush deploy:hook</code> is also very interesting and allows for adding deploy hooks similar to update hooks in .install files.</p> + +<p>I'm looking forward to updating my deployment pipelines to use this and making them simpler and easier to maintain.</p> +
+
+ Mon, 18 Nov 2024 00:00:00 GMT +
+ + Override Node Options used by LocalGov Drupal + /daily/2024/11/17/override-node-options-used-by-localgov-drupal + http://localhost:8000/daily/2024/11/17/override-node-options-used-by-localgov-drupal + +
<p><a href="http://localhost:8000/daily/2024/11/16/an-interesting-thing-i-spotted-about-the-override-node-options-module">In yesterday's email</a>, I showed some of the usage numbers of the Override Node Options module and how the current numbers match those around a year ago.</p> + +<p>Whilst the overall numbers are similar, there are a lot less Drupal 7 installations of the module and around the same number more Drupal 8+ installations.</p> + +<p>Somewhere I was happy to see it being used recently is within the <a href="https://localgovdrupal.org/products/microsites">LocalGov Drupal microsites distribution</a>.</p> + +<p>It's great when being brought into a project to see your modules being used, but it's fantastic to see a module I maintain and code that I wrote being used by a large distribution like LocalGov Drupal and benefiting local councils and people in the UK and Ireland.</p> +
+
+ Sun, 17 Nov 2024 00:00:00 GMT +
+ + An interesting thing I spotted about the Override Node Options module + /daily/2024/11/16/an-interesting-thing-i-spotted-about-the-override-node-options-module + http://localhost:8000/daily/2024/11/16/an-interesting-thing-i-spotted-about-the-override-node-options-module + +
<p>Before <a href="http://localhost:8000/daily/2024/11/13/speaking-at-the-drupal-london-meetup">my remote talk for the Drupal London meetup</a>, I'm updating the usage statistics for <a href="https://www.drupal.org/project/override_node_options">the Override Node Options module</a> - one of the modules I maintain on Drupal.org.</p> + +<p>In my slides for DrupalCamp Belgium, I showed the usage figures from October 2023, which showed 38,096 installations and it being the 173rd most installed module.</p> + +<p>This week, the number of installations has slightly increased to 38,223.</p> + +<p>What's interesting is that whilst the number of installations has been consistent, there are a lot less Drupal 7 websites using the module and a lot more Drupal 8+ sites using it.</p> + +<h2 id="october-2023">October 2023</h2> + +<ul> +<li>5.x-1.x: 1</li> +<li>6.x-1.x: 297</li> +<li>7.x-1.x: 13,717</li> +<li>8.x-2.x: 24,081</li> +<li>Total: 38,096</li> +</ul> + +<h2 id="november-2024">November 2024</h2> + +<ul> +<li>5.x-1.x: 4</li> +<li>6.x-1.x: 202</li> +<li>7.x-1.x: 10,429</li> +<li>8.x-2.x: 27,588</li> +<li>Total: 38,223</li> +</ul> + +<p>Assuming these numbers are correct, this makes me feel very positive and happy about the adoption of newer versions of Drupal and that people are upgrading their D7 websites to Drupal 10 or 11.</p> +
+
+ Sat, 16 Nov 2024 00:00:00 GMT +
+ + Git is not GitHub + /daily/2024/11/15/github-is-not-git + http://localhost:8000/daily/2024/11/15/github-is-not-git + +
<p>A common misunderstanding for new Developers is that Git and GitHub are the same thing, but they aren't.</p> + +<p>Git is decentralised, so doesn't rely on using external repositories on services like GitHub, GitLab or Bitbucket.</p> + +<p>You can run <code>git init</code> and use it locally without pushing to any remote services.</p> + +<p>These services also add extra terminology, such as forks, syncing and pull or merge requests which aren't part of Git itself.</p> + +<p>This can cause confusion, which is why <a href="http://localhost:8000/daily/2022/08/23/git-gui-command-line">I think it's important to learn Git itself</a> instead of relying on external services or desktop apps.</p> + +<p>And, if you're going to use a remote repository, consider something like Gitea, which you can host yourself and keep control of your data.</p> +
+
+ Fri, 15 Nov 2024 00:00:00 GMT +
+ + Starting with a clean slate + /daily/2024/11/14/starting-with-a-clean-slate + http://localhost:8000/daily/2024/11/14/starting-with-a-clean-slate + +
<p>Whenever you need to start a new task in a codebase, I think it's important to always try to start with a clean slate.</p> + +<p>This is having an empty staging area and no lingering or uncommitted changes from previous tasks.</p> + +<p>I do this to avoid having contaminated commits that contain multiple changes. Each commit should be related to one change.</p> + +<p>You can use <code>git add -p</code> to <a href="http://localhost:8000/daily/2024/10/25/always-review-your-changes">review, stage and commit parts of your changes</a>, but as the uncommitted changes grow, you're less likely to do that and more likely to commit them at once with a generic commit message that offers no value when viewed in the commit log.</p> + +<p>You're more likely to create better and more valuable commits and write better commit messages if you break them into chunks and commit the changes as you make them.</p> + +<p>If you have extra files you don't want to commit, add them to a <code>.gitignore</code> file or <code>.git/info/exclude</code> so they're ignored.</p> + +<p>If you have uncommitted changes that you want to hide for now but re-add later, <a href="http://localhost:8000/daily/2024/10/24/git-stash-is-underrated">git stash is your friend</a>.</p> + +<p>If you want your changes to be completely separate, maybe <a href="http://localhost:8000/daily/2022/08/12/git-worktrees-docker-compose">git worktrees are for you</a>.</p> +
+
+ Thu, 14 Nov 2024 00:00:00 GMT +
+ + Speaking at the Drupal London meetup + /daily/2024/11/13/speaking-at-the-drupal-london-meetup + http://localhost:8000/daily/2024/11/13/speaking-at-the-drupal-london-meetup + +
<p>Next Wednesday evening, I'm going to be speaking remotely at the Drupal London meetup.</p> + +<p>I was originally going to attend in person, but after injuring my foot last week, I can't travel so will have to join remotely.</p> + +<p>I'm going to be giving a talk and demo of <a href="http://localhost:8000/presentations/tdd-test-driven-drupal">automated testing and test-driven development with Drupal</a> as well as some Q&amp;A and pair programming, if time allows and we're able to do so remotely.</p> + +<p>RSVPs are still open for the event and hopefully I'll get to attend Drupal London in person in the future.</p> +
+
+ Wed, 13 Nov 2024 00:00:00 GMT +
+ + Why consistency and reproducibility are important + /daily/2024/11/12/why-consistency-and-reproducibility-are-important + http://localhost:8000/daily/2024/11/12/why-consistency-and-reproducibility-are-important + +
<p>Have you worked on a project where different Developers are developing with different tools or have different versions of packages installed?</p> + +<p>In a development team, you want all environments to be consistent - including every Developer's local environment.</p> + +<p>You want to have every environment to be as close as possible to each other or, ideally, identical.</p> + +<p>You want everyone to use the same PHP version, the same web and database servers and the same packages installed.</p> + +<p>Once things start to diverge, bugs can be introduced - such as writing code locally in a newer version of PHP than what's on production.</p> + +<p>Ideally, you also want everything to be reproducible with locked dependencies and generate the same outcome regardless of when the commands are run.</p> + +<p>This is one of the main reasons I've <a href="http://localhost:8000/daily/2024/11/11/could-nix-and-devenv-replace-docker-compose">started to use Nix and devenv for projects</a> - because they create environments that are both consistent and reproducible, reducing the chances of dependency mismatches and bugs creeping into my code.</p> +
+
+ Tue, 12 Nov 2024 00:00:00 GMT +
+ + Could Nix and devenv replace Docker Compose? + /daily/2024/11/11/could-nix-and-devenv-replace-docker-compose + http://localhost:8000/daily/2024/11/11/could-nix-and-devenv-replace-docker-compose + +
<p>I started using the Nix package manager <a href="https://github.com/opdavies/dotfiles.nix/commit/af1d8d37">in September 2022</a> as a replacement for Ansible to manage my dotfiles.</p> + +<p>Since then, I've switched to daily-driving NixOS as my main operating system and started to use Nix instead of Docker for some development projects.</p> + +<p>Even though I'm running Linux, there's a benefit to not running containers for simple projects as well as the reproducibility that Nix provides.</p> + +<p>I hadn't invested much time in using Nix as Docker replacement for Drupal projects, but this week, I've started to look at <a href="https://devenv.sh">devenv</a> - a development tool based on Nix.</p> + +<p>As well as declaring and locking the package versions I need for each project, it also manages the services I need, such as PHP-FPM and MariaDB or MySQL.</p> + +<p>It also configures Git hooks, test scripts and custom processes, such as running Tailwind CSS.</p> + +<p>I've configured it for a few projects this week and it could be a potential Docker replacement for me.</p> + +<p>If you want to see an example, <a href="https://github.com/opdavies/drupal-london-meetup/blob/main/devenv.nix">see this repo on GitHub</a>.</p> +
+
+ Mon, 11 Nov 2024 00:00:00 GMT +
+ + Write plain text files + /daily/2024/11/10/write-plain-text-files + http://localhost:8000/daily/2024/11/10/write-plain-text-files + +
<p>I recently read a post by Derek Sivers about <a href="https://sive.rs/plaintext">why he only writes in plain text files</a>.</p> + +<p>I've been doing some digital cleanup and moved my notes from different online systems to plain text files.</p> + +<p>The main advantages from Derek's post are:</p> + +<ul> +<li>Portable: plain text files are easy to migrate between computers or store in the cloud and can be opened and edited anywhere.</li> +<li>Uncommercial: you're not locked in to a proprietry or service-specific text format and not restricted by the number of notes you can have or have unwanted features pushed upon you.</li> +<li>Offline: plain text files can be accessed offline if they're stored locally without needing to go via a website.</li> +<li>No dependencies: you can edit plain text files in any editor and don't need to use heavy or bulky programs.</li> +<li>Easy to convert: plain text can be converted into anything else.</li> +</ul> + +<p>I'd also add that plain text files are easy to search and grep to find content and can be version controlled and, in software projects, easily stored alongside the code it relates to.</p> + +<p>This is why, although I work mostly with Drupal, I also like static site generators and use tools such as rst2pdf and Marp to develop presentation slides from plain text files.</p> + +<p>From the conclusion of Derek's post:</p> + +<blockquote> + <p>Reliable, flexible, portable, independent, and long-lasting. Plain text files will be readable by future generations, hundreds of years from now.</p> + + <p>I especially enjoy the tranquility of their offline, non-commercial nature. They’re quiet. They’re focused. (As I aim to be.)</p> +</blockquote> +
+
+ Sun, 10 Nov 2024 00:00:00 GMT +
+ + Discussing Drush and Laravel Prompts with Jess Archer + /daily/2024/11/09/discussing-drush-and-laravel-prompts-with-jess-archer + http://localhost:8000/daily/2024/11/09/discussing-drush-and-laravel-prompts-with-jess-archer + +
<p>In this week's Beyond Blocks podcast episode, I spoke to <a href="https://x.com/jessarchercodes">Jess Archer</a> - Engineering Team Lead at Laravel.</p> + +<p>We discuss Laravel Prompts which was released at Laracon US last year and added to Drush 13, but also about PHP, Laravel, Neovim, working in the terminal and bullet journaling.</p> + +<p><a href="http://localhost:8000/podcast/25-jess-archer-drush-laravel-prompts">Listen to the episode now</a>.</p> +
+
+ Sat, 09 Nov 2024 00:00:00 GMT +
+ + What will be included in Drupal CMS? + /daily/2024/11/08/what-will-be-included-in-drupal-cms + http://localhost:8000/daily/2024/11/08/what-will-be-included-in-drupal-cms + +
<p>In yesterday's email, I wondered <a href="http://localhost:8000/daily/2024/11/07/should-drush-be-in-drupal-core">whether Drush should be added to Drupal core</a>.</p> + +<p>I've used Drush for as long as I've used Drupal so, to me, it would be an obvious thing to consider.</p> + +<p>The Pathauto module is another thing that I use on every Drupal project and could be another good candidate.</p> + +<p>With the recent Starshot initiative and recently-named "Drupal CMS" project, could we see things like these added to that if not to Drupal core?</p> + +<p>It'll be interesting to see how both the Drupal and Drupal CMS projects evolve, what will be the same and what will be different.</p> +
+
+ Fri, 08 Nov 2024 00:00:00 GMT +
+ + Should Drush be in Drupal core? + /daily/2024/11/07/should-drush-be-in-drupal-core + http://localhost:8000/daily/2024/11/07/should-drush-be-in-drupal-core + +
<p>I've used Drush - the Drupal shell - to interact with my Drupal applications on the command line since I started around 2008.</p> + +<p>It's always been part of my Drush experience.</p> + +<p>From installing Drupal and performing routine actions such as enabling modules and clearing caches to, in newer Drupal versions, performing migrations and generating Storybook stories.</p> + +<p>Many projects I work on have custom Drush commands to perform tasks from the command line.</p> + +<p>This week, I created a new Drupal 11 project for a client using the <a href="https://github.com/drupal/core-recommended">drupal/core-recommended</a> package and initially forgot to install Drush so I could install Drupal.</p> + +<p>I'm surprised Drush isn't in Drupal core or a dependency of the recommended package.</p> + +<p>There is a basic Drupal CLI at <a href="https://git.drupalcode.org/project/drupal/-/blob/94bc96150ca726fc667be51c8176f90b56e492df/core/scripts/drupal">core/scripts/drupal</a>, but I wonder if we'll see a fully-featured CLI tool like Drush included with Drupal core, similar to Symfony's console or Laravel's artisan commands.</p> + +<p>For me, including Drush would be an obvious choice.</p> +
+
+ Thu, 07 Nov 2024 00:00:00 GMT +
+ + Drupal 11 is not Drupal 6 + /daily/2024/11/06/drupal-11-is-not-drupal-6 + http://localhost:8000/daily/2024/11/06/drupal-11-is-not-drupal-6 + +
<p>I started learning software and web development with HTML and CSS in 2007. Then I started with PHP and soon after started to learn Drupal 6 (or maybe 5).</p> + +<p>A lot of Developers' first programming language was PHP and maybe used earlier versions of Drupal before moving to another CMS, framework or language.</p> + +<p>They still remember, though, how things were when they used it and aren't aware of the advancements and improvements that have been made.</p> + +<p>The PHP language itself has improved significantly in recent versions with new features and much better peformance.</p> + +<p>Drupal is more powerful with a lot more available in core compared to when I started to use it and, since adopting modern PHP approaches and third-party code in Drupal 8, writing custom Drupal modules is different with fewer Drupalisms and more industry-standard approaches.</p> + +<p>If there's a tool you haven't tried for a while, maybe take another look and see if it's improved since you last used it.</p> +
+
+ Wed, 06 Nov 2024 00:00:00 GMT +
+ + Keep your test suite passing + /daily/2024/11/05/keep-your-test-suite-passing + http://localhost:8000/daily/2024/11/05/keep-your-test-suite-passing + +
<p>I once joined a project that had some automated tests and quality checks which were run automatically in a CI pipeline whenever code was pushed.</p> + +<p>The problem was that one of the checks were failing which caused the pipeline to fail.</p> + +<p>Because it was failing, it was ignored.</p> + +<p>But if it's already failing and being ignored, how do you know you haven't broken something unexpectedly?</p> + +<p>This is the value you get from automated tests that are run regularly, but if failures are being ignored, they aren't adding value or confidence the code works.</p> + +<p>In the end, it was a small fix to get the tests and CI pipeline passing again and we made sure to keep it passing as much as possible once it was.</p> +
+
+ Tue, 05 Nov 2024 00:00:00 GMT +
+ + Run your tests more often + /daily/2024/11/04/run-your-tests-more-often + http://localhost:8000/daily/2024/11/04/run-your-tests-more-often + +
<p>There's no value in a test suite that isn't run and the less often it's run, the less likely you are to spot a failure or regression.</p> + +<p>If the tests are only run weekly, you aren't going to be aware of an issue that could have been introduced at the start of the week until the tests are run.</p> + +<p>Then, there are more changes that need to be reviewed to find the cause of the issue compared to if it was found sooner.</p> + +<p>Ideally, everyone should be running tests locally as well as having them run automatically in a CI pipeline for each commit.</p> + +<p>As soon as a failing commit is pushed, it will be identified and it will be easy to fix or revert.</p> + +<p>Also, the more often you push changes and in smaller batches - the easier it will be to diagnose and resolve the issue.</p> +
+
+ Mon, 04 Nov 2024 00:00:00 GMT +
+ + Passing tests doesn't mean a working application + /daily/2024/11/03/passing-tests-doesnt-mean-a-working-application + http://localhost:8000/daily/2024/11/03/passing-tests-doesnt-mean-a-working-application + +
<p>Having a passing test suite doesn't mean everything in your application is working.</p> + +<p>It means the functionality you previously tested is working.</p> + +<p>There may be edge cases you haven't covered or whole tests you haven't written yet, which may be working or could be broken.</p> + +<p>If you have tests that were passing that are now failing, you know you've broken something.</p> + +<p>Something that was previously working is broken and shouldn't be deployed.</p> + +<p>That's why having tests is important - they give you the ability to identify and fix regressions before they are released.</p> +
+
+ Sun, 03 Nov 2024 00:00:00 GMT +
+ + Code reviews are about the code, not code style + /daily/2024/10/30/code-reviews-are-about-the-code-not-code-style + http://localhost:8000/daily/2024/10/30/code-reviews-are-about-the-code-not-code-style + +
<p>If you do code reviews, they should be about reviewing the code and not about the style of the code.</p> + +<p>There shouldn't be comments about whether tabs or spaces are used, how many spaces are on each line, where the braces are, or whether there should be semicolons.</p> + +<p>A code style should be defined upfront and checking code against it can be done automatically with tools such as phpcs or Prettier.</p> + +<p>You don't need to wait for a human to review the code style - automated tools can be run locally or in a CI pipeline and provide feedback much faster.</p> + +<p>And if people aren't reviewing the code style, they can focus on reviewing the code itself.</p> +
+
+ Wed, 30 Oct 2024 00:00:00 GMT +
+ + Why "no build" is appealing + /daily/2024/10/28/why-no-build-is-appealing + http://localhost:8000/daily/2024/10/28/why-no-build-is-appealing + +
<p>You're or a new team member are onboarded to a project to make some CSS changes.</p> + +<p>The project uses Sass, Less or another preprocessor.</p> + +<p>Not knowing this, the generated CSS files are changed instead of the Sass files.</p> + +<p>The changes are committed and pushed, but because they weren't added to the Sass files, they will be lost when the CSS files are re-generated in the future.</p> + +<p>Or, if the Sass and CSS files become too out of sync, everyone will be too worried about losing changes to use the Sass files, so they will be abandoned and not used.</p> + +<p>What if you change the correct files but don't know the magic command to generate the assets for that project?</p> + +<p>I've seen things like this happen on numerous projects and is an example of why the "no build" approach is appealing.</p> + +<p>Especially with enhancements to CSS, such as custom properties (variables) and nesting, a lot of the functionality from preprocessors can now be done with normal CSS.</p> + +<p>This means the front-end build steps can be simplified or removed entirely, reducing the technical overhead and dependency cost, and it's easier for new Developers to get started.</p> + +<p>Win, win!</p> +
+
+ Mon, 28 Oct 2024 00:00:00 GMT +
+ + A deep drive into test-driven Drupal development + /daily/2024/10/27/a-deep-drive-into-test-driven-drupal-development + http://localhost:8000/daily/2024/10/27/a-deep-drive-into-test-driven-drupal-development + +
<p>Are you near London and want to learn about automated testing in Drupal?</p> + +<p>I'll be presenting a session and Q&amp;A on automated testing and test-driven development in Drupal.</p> + +<p>This is one of my favourite topics to present and teach, so I'm looking forward to this event.</p> + +<p>If you want to attend, <a href="https://www.meetup.com/london-drupal-user-group/events/303500889">RSVP on the meetup event page</a>.</p> + +<p>If you can't, check out my <a href="http://localhost:8000/atdc">free Drupal testing email course</a> or <a href="http://localhost:8000/call">book a 1-on-1 consulting call</a> and I'll get you started.</p> +
+
+ Sun, 27 Oct 2024 00:00:00 GMT +
+ + Thinking of new ideas + /daily/2024/10/26/thinking-of-new-ideas + http://localhost:8000/daily/2024/10/26/thinking-of-new-ideas + +
<p>As well as my consulting and development work, and educational content such as my <a href="http://localhost:8000/atdc">automated testing email course</a>, I'd like to release something as a project people can use or a SaaS product for people to purchase.</p> + +<p>One of my original ideas was to build a website for speakers to upload and manage their presentations, but I found <a href="https://symposiumapp.com">Symposium</a>.</p> + +<p>I was building a hosted dashboard for multiple Drupal applications so people could see all their projects and available updates in one place, but someone made me aware of <a href="https://www.drupal-remote-dashboard.com">Drupal Remote Dashboard</a>. Whilst it's not exactly the same, the idea is similar, so I'm thinking whether to continue with this.</p> + +<p>I have a short list of other ideas, but if you have one, reply and let me know.</p> +
+
+ Sat, 26 Oct 2024 00:00:00 GMT +
+ + Always review your changes + /daily/2024/10/25/always-review-your-changes + http://localhost:8000/daily/2024/10/25/always-review-your-changes + +
<p><a href="http://localhost:8000/daily/2024/10/24/git-stash-is-underrated">In yesterday's email</a> where I wrote about <code>git stash</code>, I mentioned the <code>-p</code> or <code>--patch</code> options.</p> + +<p>When stashing changes, this allows you to interactively select which changes you want to stash and what you don't.</p> + +<p>This is supported by other Git commands, including <code>git add</code>.</p> + +<p>I always use <code>git add -p</code> when adding changes as it gives me the chance to review them before committing them.</p> + +<p>If I left any stray comments, debug code, trailing spaces or anything I don't want to commit, I can remove them.</p> + +<p>If I was working on multiple changes (which I avoid), I can select the appropriate changes and create commits related to each change to keep the history clean and useful.</p> + +<p>I also use <code>git diff</code> and <code>git diff --staged</code> to review changes as well as <code>git show</code> to review commits.</p> + +<p>By doing this, I know my commits will be in the best state for others to review now or for me to review if I need to in the future.</p> +
+
+ Fri, 25 Oct 2024 00:00:00 GMT +
+ + git stash is underrated + /daily/2024/10/24/git-stash-is-underrated + http://localhost:8000/daily/2024/10/24/git-stash-is-underrated + +
<p><code>git stash</code> is one of the commands I use the most.</p> + +<p>Maybe because I do trunk-based development so I very rarely create new branches, or because I intentionally make and push small atomic commits, I often find myself using <code>git stash</code> whilst debugging something or if I need to switch contexts quickly whilst in the middle of another task and I don't want to lose my changes.</p> + +<p>If it's not something I'm going to unstash and bring back almost immediately, I can create a new branch or create a named stash with <code>git stash save &lt;name&gt;</code>.</p> + +<p>If I don't need to stash everything, it supports the <code>-p</code> or <code>--patch</code> option and I can decide what to stash and what to keep.</p> + +<p>If you haven't used <code>git stash</code>, I recommend giving it a try.</p> +
+
+ Thu, 24 Oct 2024 00:00:00 GMT +
+ + Sharp blades and dull blades + /daily/2024/10/24/sharp-blades-and-dull-blades + http://localhost:8000/daily/2024/10/24/sharp-blades-and-dull-blades + +
<p>I'm not a professional Ruby or Rails Developer, but I am a professional test-driven Developer.</p> + +<p>I like to see how others do TDD and have been watching videos of <a href="https://www.codewithjason.com">Jason Swett</a>'s online meetups where he works on his Saturn CI project.</p> + +<p>A few times he's mentioned using dull and sharp blades as an alternative metaphor for technical debt.</p> + +<p>He's also written an article about it.</p> + +<p>Here's an excerpt:</p> + +<blockquote> + <p>I prefer to think of a software system as a collection of blades.</p> + + <p>A dull blade is never preferable to a sharp one. Sometimes it may be better to just cut with the dull blade instead of taking the time to sharpen the blade before cutting, but it’s clear that that’s a compromise. No one would talk about “strategic dull blades” the way they talk about strategic technical debt.</p> + + <p>Unlike debt which you can simply choose not to take on, blades unavoidably get dull with use. Having technical debt makes it sound like you did something wrong. But you don’t have to do anything wrong in order to end up with bad code. Often, bad code is simply the result of entropy. The blade metaphor makes it clear that dullness is normal and unavoidable.</p> +</blockquote> + +<p>If you like this, you can <a href="https://www.codewithjason.com/sharp-blades-and-dull-blades">read the whole article</a>.</p> + +<p>Do you have other metaphors for technical debt? Reply and let me know.</p> +
+
+ Thu, 24 Oct 2024 00:00:00 GMT +
+ + How would you write this test name? + /daily/2024/10/22/how-would-you-write-this-test-name + http://localhost:8000/daily/2024/10/22/how-would-you-write-this-test-name + +
<p>There are multiple ways I've seen people write their test method names.</p> + +<p>This is the standard PSR-compliant camel-case method name:</p> + +<pre><code class="php">public function testSomethingHappensWhenYouGoToThePage() +</code></pre> + +<p>Some people find long camel-case names hard to read and prefer to use snake-case names:</p> + +<pre><code class="php">public function test_something_happens_when_you_go_to_the_page() +</code></pre> + +<p>This still works as the method name still starts with the word <code>test</code>, but you'd need to add some overrides to phpcs for it not to complain about using snake-case words.</p> + +<p>Another option is to remove the <code>test</code> prefix and use an annotation:</p> + +<pre><code class="php">/** @test */ +public function something_happens_when_you_go_to_the_page() +</code></pre> + +<p>And in newer PHPUnit versions, you can also use an attribute:</p> + +<pre><code class="php">#[Test] +public function something_happens_when_you_go_to_the_page() +</code></pre> + +<p>Whilst this makes the method name shorter, you need to add an additional line before each test method for the annotation or attribute.</p> + +<p>Each has pros and cons, and people have their own preferences.</p> + +<p>Which do you do?</p> +
+
+ Tue, 22 Oct 2024 00:00:00 GMT +
+ + Drupal applications are modular monoliths + /daily/2024/10/21/drupal-applications-are-modular-monoliths + http://localhost:8000/daily/2024/10/21/drupal-applications-are-modular-monoliths + +
<p>"Modular monolith" has been a popular phrase in the PHP community recently with talks, podcast episodes and courses released on the topic.</p> + +<p>The idea is that instead of all the code being in one namespace, like App, it's split into different modules such as for payments or a blog - whatever is relevant and appropriate for that application.</p> + +<p>Each module contains its own classes and structure instead of everything being mixed together.</p> + +<p>If you want to change something about payments, you go to the payments module and you don't need to worry about anything else.</p> + +<p>What's interesting is that this is how I've always built Drupal applications.</p> + +<p>Each includes Drupal core and any contributed modules installed via Composer, and a specific directory for application-specific custom modules.</p> + +<p>These modules can be separate and standalone or they can interact and have dependencies and sub-modules.</p> + +<p>Each has its own routes, services, tests and more, making them easy to organise and maintain compared to having all the custom code in one large monolithic namespace or module.</p> +
+
+ Mon, 21 Oct 2024 00:00:00 GMT +
+ + Test, then refactor + /daily/2024/10/20/test-then-refactor + http://localhost:8000/daily/2024/10/20/test-then-refactor + +
<p><a href="http://localhost:8000/daily/2024/10/19/phpunit-or-pest">Whether you prefer PHPUnit or Pest PHP</a>, or if you're coding in a different language, it's important to have automated tests - especially before you refactor any code.</p> + +<p>Before you refactor, you want to have passing tests that you're confident cover all the required functionality.</p> + +<p>When you finish refactoring, the tests should still pass.</p> + +<p>Then you know the functionality is the same and the code still works after it's been refactored.</p> + +<p>If you don't have tests, how do you know everything still works, or how likely are you to do the refactor at all?</p> +
+
+ Sun, 20 Oct 2024 00:00:00 GMT +
+ + PHPUnit or Pest? + /daily/2024/10/19/phpunit-or-pest + http://localhost:8000/daily/2024/10/19/phpunit-or-pest + +
<p>This is a common question from people starting to write automated tests in PHP:</p> + +<blockquote> + <p>Should I use PHPUnit or Pest PHP?</p> +</blockquote> + +<p>I've used both.</p> + +<p>They both have pros and cons.</p> + +<p>If you're familiar with JavaScript and Jest, Pest would probably be a natural fit.</p> + +<p>If you're used to writing classes in PHP, PHPUnit may feel more familiar.</p> + +<p>Use whichever appeals to you.</p> + +<p>Maybe try both and see which you prefer.</p> + +<p>The main benefit will be you have tests, whichever you decide to use.</p> +
+
+ Sat, 19 Oct 2024 00:00:00 GMT +
+ + Is PHP a good first programming language? + /daily/2024/10/18/is-php-a-good-first-programming-language + http://localhost:8000/daily/2024/10/18/is-php-a-good-first-programming-language + +
<p>I started coding in 2007 to build a website for a Tae Kwon-Do School I used to train at.</p> + +<p>It was written with HTML and CSS and only contained a few small pages.</p> + +<p>When I wanted to add more dynamic content, such as news articles and events, I started to expand and started to use PHP and MySQL.</p> + +<p>PHP was a great first programming language for me (ignoring HTML and CSS), and it's gone through so many improvements since 2007, as well as the PHP content management systems and frameworks.</p> + +<p>I think a lot of new coders only learn or are taught JavaScript and I can understand why if they've only got a short time, such as on a bootcamp, but I also think there is a benefit to learning another language - such as PHP.</p> + +<p>PHP has Composer to manage dependencies, various robust content management systems and frameworks, PHPUnit and Pest for automated testing, PHPStan and Psalm for static analysis, and much more.</p> + +<p>I'd definitely recommend taking a look at PHP if you haven't already.</p> +
+
+ Fri, 18 Oct 2024 00:00:00 GMT +
+ + 16 years on Drupal.org + /daily/2024/10/17/16-years-on-drupal-org + http://localhost:8000/daily/2024/10/17/16-years-on-drupal-org + +
<p>As of today, <a href="https://www.drupal.org/u/opdavies">my user profile on Drupal.org</a> says I've been on it for 16 years.</p> + +<p>Originally self-learning HTML and CSS to build a website for a Tae Kwon-Do School I used to train at, I later started to learn PHP and MySQL before trying Drupal after it was suggested to me in a response to a question on a forum (which was also built with Drupal).</p> + +<p>Since then, I've done great things with Drupal and met many great people at different events.</p> + +<p>I've even <a href="http://localhost:8000/podcast">started to interview some of them</a> on my podcast.</p> + +<p>Here's to the next 16 years, and I'm very excited so see where Drupal and PHP go.</p> +
+
+ Thu, 17 Oct 2024 00:00:00 GMT +
+ + Generative AI in PHP + /daily/2024/10/16/generative-ai-in-php + http://localhost:8000/daily/2024/10/16/generative-ai-in-php + +
<p>In this week's episode of the <a href="http://localhost:8000/podcast">Beyond Blocks podcast</a>, I'm joined by Chris Ballard to discuss generative AI in PHP following his talk at PHP South West.</p> + +<p>We also talked about attending and speaking at meetups and live coding, and I spoke about some of the AI-related content I saw presented at DrupalCon Barcelona.</p> + +<p><a href="http://localhost:8000/podcast/24-chris-ballard-generative-ai">Listen to the episode now</a>.</p> + +<p>If you'd like to be a guest on the podcast or want to suggest a topic, reply and let me know!</p> +
+
+ Wed, 16 Oct 2024 00:00:00 GMT +
+ + Don't just copy and paste + /daily/2024/10/15/dont-just-copy-and-paste + http://localhost:8000/daily/2024/10/15/dont-just-copy-and-paste + +
<p>Whether you get <a href="http://localhost:8000/daily/2024/10/09/ai-as-a-pair-programming-partner">a solution from AI</a>, GitHub, Stack Overflow or somewhere else, don't blindly copy and paste it.</p> + +<p>Validate and verify it's the right solution.</p> + +<p>Process and understand it and its pros and cons so you can decide why and when to use it again in the future.</p> + +<p>Once you've committed it, you're responsible for it and need to maintain it - regardless of where it came from.</p> +
+
+ Tue, 15 Oct 2024 00:00:00 GMT +
+ + AI as a pair-programming partner + /daily/2024/10/09/ai-as-a-pair-programming-partner + http://localhost:8000/daily/2024/10/09/ai-as-a-pair-programming-partner + +
<p>Instead of <a href="http://localhost:8000/daily/2024/05/31/putting-glue-on-pizza">using AI to autocomplete code</a> or <a href="http://localhost:8000/daily/2024/10/08/ai-in-drupal">perform tasks within your Drupal website</a>, I've seen people using AI as a virtual pair programming partner.</p> + +<p>Similar to using a search engine or finding results on websites like Stack Overflow, but also being able to ask follow-up questions and for clarification as part of a conversation.</p> + +<p>You're still relying on its answers and advice to be correct, but using AI as a learning tool seems like a sensible approach if you don't have a non-AI pairing partner available.</p> + +<p>Just make sure to validate and verify whatever it gives you.</p> + +<p>Once you've committed it, you're responsible for it.</p> +
+
+ Wed, 09 Oct 2024 00:00:00 GMT +
+ + AI in Drupal + /daily/2024/10/08/ai-in-drupal + http://localhost:8000/daily/2024/10/08/ai-in-drupal + +
<p>I saw a lot of AI-related content at DrupalCon, including the Driesnote and the Track Lead keynote.</p> + +<p>The Driesnote showed a demonstration of Drupal CMS (aka Starshot), which included an AI chatbot that performed tasks such as renaming content types and building views.</p> + +<p>I also remember seeing AI populating alt text for images in one of the sessions.</p> + +<p>I'm still on the fence about AI and when and how it should be used.</p> + +<p><a href="http://localhost:8000/daily/2024/05/31/putting-glue-on-pizza">My experiences of using AI so far haven't been great</a>, but I have seen some interesting implementation and use-cases for it.</p> + +<p>I'm seeing AI being added everywhere - from my search results to suggesting social media comments and generating notes and issues within services I use.</p> + +<p>I can see how a AI-driven approach to site building in Drupal could be useful, but I'm still unsure.</p> + +<p>What do you think?</p> +
+
+ Tue, 08 Oct 2024 00:00:00 GMT +
+ + Discussing Drupal's ECA module + /daily/2024/10/07/discussing-drupals-eca-module + http://localhost:8000/daily/2024/10/07/discussing-drupals-eca-module + +
<p>In the most recent episode of the Beyond Blocks podcast, I spoke with Jürgen Haas about Drupal's ECA module.</p> + +<p>ECA stands for Events, Conditions and Actions and is a module I was aware of, but haven't used before - so I was great to learn more about it.</p> + +<p>I also got to meet Jürgen at DrupalCon in Barcelona, as well as several other podcast guests, which was fantastic.</p> + +<p><a href="http://localhost:8000/podcast/23-jurgen-haas-eca">Listen to the episode now</a>.</p> + +<p>If you'd like to be a guest on the podcast or want to suggest a topic, reply and let me know!</p> +
+
+ Mon, 07 Oct 2024 00:00:00 GMT +
+ + Is post-end-of-live support an anti-pattern? + /daily/2024/10/06/is-post-end-of-live-support-an-anti-pattern + http://localhost:8000/daily/2024/10/06/is-post-end-of-live-support-an-anti-pattern + +
<p>With Drupal 7's end-of-life date of the 5th of January 2025 quickly approaching, I've recently seen again a number of companies offering support for Drupal 7 after its end-of-life date.</p> + +<p>I've seen the same in corporate IT environments where they're running versions of software post their EOL date, so it's not only Drupal 7, but I wonder if this is a good thing?</p> + +<p>Is this deterring companies from upgrading if they know this is an option, or should everyone upgrade and we can move forward from Drupal 7 and other end-of-life software?</p> + +<p>According to <a href="https://www.drupal.org/project/usage/drupal">https://www.drupal.org/project/usage/drupal</a>, there are still 281,000 active Drupal 7 installations.</p> + +<p>It's considerably less than before - this time last year, it was over 380,000 installations - but it's still a lot of Drupal 7 being used.</p> +
+
+ Sun, 06 Oct 2024 00:00:00 GMT +
+ + Make the change easy, then make the easy change + /daily/2024/10/04/make-the-change-easy--then-make-the-easy-change + http://localhost:8000/daily/2024/10/04/make-the-change-easy--then-make-the-easy-change + +
<p>This week, I've been working on a particular component that processes and displays live data from APIs.</p> + +<p>As the data is live, it's constantly changing and it's unlikely I'll see all the different states and use-cases that the data could be in.</p> + +<p>There's also no guarantee the API will be available and I also may need to work offline with no Internet access to connect to the API.</p> + +<p>I'm reminded of the quote by Kent Beck:</p> + +<blockquote> + <p>For each desired change, make the change easy (warning: this may be hard), then make the easy change.</p> +</blockquote> + +<p>In this case, to make the change easy, I need the data to be consistent, to see all the use-cases and permutations and to be available offline.</p> + +<p>To make the change easy, I created a fake version of the service class that returns static data I can work with.</p> + +<p>I can add whatever data I need to give me the examples and situations to work with and by implementing the same methods as the real service class, I can swap them without changing any other code.</p> + +<p>Now I can make the easy change.</p> +
+
+ Fri, 04 Oct 2024 00:00:00 GMT +
+ + YAGNI + /daily/2024/10/03/yagni + http://localhost:8000/daily/2024/10/03/yagni + +
<p>I like to keep my code simple.</p> + +<p>I only write the code I need to add the functionality I need, and I only include the current requirements.</p> + +<p>There's no benefit to writing code for additional requirements that may never be needed or used.</p> + +<p>I also think this when adding new modules or packages, or implementing a design pattern.</p> + +<p>Do I really need this?</p> + +<p>What value does it add?</p> + +<p>Is there a simpler way to achieve the same thing?</p> + +<p>If so, I'm happy to call YAGNI on it.</p> +
+
+ Thu, 03 Oct 2024 00:00:00 GMT +
+ + Technical debt isn't always bad + /daily/2024/10/02/technical-debt-isn-t-always-bad + http://localhost:8000/daily/2024/10/02/technical-debt-isn-t-always-bad + +
<p><a href="http://localhost:8000/daily/2024/10/01/not-all-legacy-code-is-technical-debt">Technical debt is usually referred to negatively</a>, but technical debt isn't always bad.</p> + +<p>The key thing is to know when and why you're taking on technical debt and when it will be addressed.</p> + +<p>If you have a goal or deadline to meet, you may decide to take on technical debt to release a feature sooner or a simpler version is released now and a more complex version will come later.</p> + +<p>I've done this on multi-site Drupal projects before, where I've hard-coded a background image URL as part of a minimum-viable version and made it changeable only when it needed to be - i.e. when the second website was introduced.</p> + +<p>For the initial version, that approach was good enough and meant we could move forward.</p> + +<p>The client and I decided to take on this technical debit in advance so we could release it sooner, and we knew when and how we were going to address it and pay it back.</p> + +<p>This was a good situation, not a bad one.</p> +
+
+ Wed, 02 Oct 2024 00:00:00 GMT +
+ + Not all legacy code is technical debt + /daily/2024/10/01/not-all-legacy-code-is-technical-debt + http://localhost:8000/daily/2024/10/01/not-all-legacy-code-is-technical-debt + +
<p>Technical debt is an overused term with people often referring to any legacy code, outdated dependencies or bugs as technical debt.</p> + +<p>Technical debt specifically refers to the future cost when a short-term solution is selected over a more flexible or efficient one.</p> + +<p>For example, hard-coding something for now and making it dynamic later.</p> + +<p>It's not any code the current Developers didn't write or no longer want to support.</p> +
+
+ Tue, 01 Oct 2024 00:00:00 GMT +
+ + Is testing a chore? + /daily/2024/09/29/is-testing-a-chore + http://localhost:8000/daily/2024/09/29/is-testing-a-chore + +
<p><a href="http://localhost:8000/daily/2024/09/28/testing-personal-projects">Do some Developers skip writing tests for their personal projects</a> because they think it's a chore?</p> + +<p>If they've written the code, so they then think it's too much work to write tests or need to move on to the next task?</p> + +<p>Is it boring to write tests for code that's already written and will pass straight away?</p> + +<p>This is why I do test-driven development and start with a failing test and then write code to make it pass.</p> + +<p>Then, I add more to the test until it fails and then get that to pass.</p> + +<p>When that test is finished, I'll move to a new test for a different piece of functionality.</p> + +<p>I like this approach of working in small feedback cycles and alternating between failing and passing tests.</p> + +<p>Testing this way isn't boring or a chore and much more interesting for me compared to writing all the code first and maybe writing the tests later.</p> +
+
+ Sun, 29 Sep 2024 00:00:00 GMT +
+ + Testing personal projects + /daily/2024/09/28/testing-personal-projects + http://localhost:8000/daily/2024/09/28/testing-personal-projects + +
<p>Listening to podcasts and watching videos where Developers talk about their personal or side projects, sometimes they say that they skip writing automated tests.</p> + +<p>They'd write tests for their client or work projects, but not necessarily for their own projects.</p> + +<p>I wonder why this is.</p> + +<p>Presumably they see the benefits of testing on other projects, so why wouldn't they write tests for their own projects?</p> +
+
+ Sat, 28 Sep 2024 00:00:00 GMT +
+ + Static analysis with Dave Liddament + /daily/2024/09/27/static-analysis-with-dave-liddament + http://localhost:8000/daily/2024/09/27/static-analysis-with-dave-liddament + +
<p>My most recent guest on the Beyond Blocks podcast is PHP Developer, public speaker and PHP South West organiser, Dave Liddament.</p> + +<p>We discuss using PHPStan and static analysis in PHP projects as well as taking some trips down memory lane, including when we wrote the most recent PHP website with Tailwind CSS version 0.4. That is not a typo!</p> + +<p><a href="http://localhost:8000/podcast/22-dave-liddament">Listen to the episode now</a>.</p> +
+
+ Fri, 27 Sep 2024 00:00:00 GMT +
+ + Don't add blank lines + /daily/2024/09/26/dont-add-blank-lines + http://localhost:8000/daily/2024/09/26/dont-add-blank-lines + +
<p>I recently saw a social media post that said something like "Don't add blank lines within functions. They take up space and don't add anything".</p> + +<p>I may have misremembered the exact wording, but that was the gist of it.</p> + +<p>I like to add blank lines within my functions to create and add separation between logical blocks of code.</p> + +<p>Whilst this doesn't add anything for a computer parsing and serving the code, or a CI pipeline performing checks, it makes the code easier for people to read and understand which, it my opinion, adds its own value - even if the functionality is the same.</p> +
+
+ Thu, 26 Sep 2024 00:00:00 GMT +
+ + Are you a real Developer? + /daily/2024/09/25/are-you-a-real-developer + http://localhost:8000/daily/2024/09/25/are-you-a-real-developer + +
<p>I remember a while ago, someone could call themselves a "real" Software Developer if they don't write automated tests.</p> + +<p>This is, of course, not the case.</p> + +<p>But I'd suggest that it makes you a better Developer who writes better and more robust software.</p> +
+
+ Wed, 25 Sep 2024 00:00:00 GMT +
+ + What would get you fired? + /daily/2024/09/24/what-would-get-you-fired + http://localhost:8000/daily/2024/09/24/what-would-get-you-fired + +
<p>I'm often asked by people what the first test is they should write in their application.</p> + +<p>Matt Stauffer asked in previous talks at Laravel conferences and podcasts:</p> + +<p>What would get you fired or your company in the news for a bad reason?</p> + +<p>That seems like a good place to start.</p> +
+
+ Tue, 24 Sep 2024 00:00:00 GMT +
+ + Enforcing consistency with automation + /daily/2024/09/23/enforce-consistency-with-automation + http://localhost:8000/daily/2024/09/23/enforce-consistency-with-automation + +
<p>If you're trying to <a href="http://localhost:8000/daily/2024/09/20/be-consistent">keep your code consistent</a>, such as following the same coding style or following conventions such as <a href="http://localhost:8000/daily/2024/09/05/find-vs-get">find vs get</a> or design systems such as repositories or builder classes, instead of relying on manual code review and taking the time of a colleague, you can leverage automation to run checks for you.</p> + +<p>You can run tools such as phpcs or eslint to enforce a coding style and use Git hooks or a CI pipeline to run them automatically or integrate them into your text editor or IDE so you can see and resolve issues as the code is being written.</p> + +<p>You can use static analysis tools such as PHPStan to find potential bugs but also enforce conventions by <a href="http://localhost:8000/daily/2024/09/22/writing-custom-phpstan-rules-for-drupal-projects">writing custom rules for your project</a> or using architectural testing tools such as PHPat.</p> + +<p>By automating checks, you'll have a consistent result every time and don't need to wait for someone else to find small issues you could have fixed quickly.</p> +
+
+ Mon, 23 Sep 2024 00:00:00 GMT +
+ + Writing custom PHPStan rules for Drupal projects + /daily/2024/09/22/writing-custom-phpstan-rules-for-drupal-projects + http://localhost:8000/daily/2024/09/22/writing-custom-phpstan-rules-for-drupal-projects + +
<p>Today, I watched a video recording of a session by Ondřej Mirtes - the creator of PHPStan - on writing custom rules for PHPStan.</p> + +<p>After explaining how to write them, he showed some examples of custom rules - one of which was ensuring a <code>Person</code> class couldn't be created outside of a <code>PersonRepository</code> class.</p> + +<p>This seemed straightforward so I thought about how I could do something similar within my <a href="http://localhost:8000/atdc">Drupal automated testing email course</a> to check my Drupal code.</p> + +<p>Similar to Ondřej's example, I wondered if I could enforce that no post nodes should be created outside of the <code>PostBuilder</code> class by searching for any code like <code>Node::create(['type' =&gt; 'post'])</code>.</p> + +<p>I was able to get a version working quickly and have <a href="https://gist.github.com/opdavies/a2f9d92cf3b67db6a64b9fca4e4e6697">posted it as a gist</a>.</p> + +<p>I'm sure some improvements can be made, but it was a successful experiment and something I can see me using more in the future.</p> +
+
+ Sun, 22 Sep 2024 00:00:00 GMT +
+ + Drupal adopts ADRs + /daily/2024/09/21/drupal-adopts-adrs + http://localhost:8000/daily/2024/09/21/drupal-adopts-adrs + +
<p>Today I noticed that Drupal's Experience Builder project <a href="https://www.drupal.org/project/experience_builder/issues/3454669">has adopted architectural decision records</a>.</p> + +<p>From the issue:</p> + +<blockquote> + <p>I proposed [...] to start adopting ADRs to document decisions that were made. That’d allow us to stop rehashing past conversations and allow people to onboard with fewer meetings. + … or so I think. + Because I think that ADRs could be an excellent way to scale this project up to A) many people, B) many timezones.</p> +</blockquote> + +<p>I like this decision and have adopted and <a href="http://localhost:8000/daily/2024/06/12/recording-architectural-decisions">written about ADRs before</a>.</p> + +<p>I also like that the documents <a href="https://git.drupalcode.org/project/experience_builder/-/tree/0.x/docs/adr?ref_type=heads">are stored as markdown files in the repository</a>, alongside the code, making it easy for everyone to view or change.</p> + +<p>As the work on Experience Builder continues, new contributors can read these documents and understand why previous decisions were made, which is extremely valuable.</p> + +<p>I'm interested to see how ADRs work for the Experience Builder project and whether they're adopted more widely in other parts of the Drupal ecosystem.</p> +
+
+ Sat, 21 Sep 2024 00:00:00 GMT +
+ + Be consistent + /daily/2024/09/20/be-consistent + http://localhost:8000/daily/2024/09/20/be-consistent + +
<p>Whether you <a href="http://localhost:8000/daily/2024/09/19/the-two-ways-of-writing-php-code">prefer strict code or not</a>, <a href="http://localhost:8000/daily/2023/04/19/camel-case-or-snake-case-for-drupal-code">use snake-case or camel-case for variable names</a> or <a href="http://localhost:8000/daily/2022/11/14/camel-case-or-snake-case-for-test-methods">how you write your test methods</a>, it is important to be consistent in how code is written.</p> + +<p>If you're working in a file that uses snake-case variable names, you should do the same and follow the same conventions.</p> + +<p>When reading a file, everything should be consistent and seem like it was written by the same person following the same approaches.</p> + +<p>Otherwise, when you need to add to or refactor this code in the future, it will be harder to do so.</p> + +<p>Consistency is key.</p> +
+
+ Fri, 20 Sep 2024 00:00:00 GMT +
+ + The two ways of writing PHP code + /daily/2024/09/19/the-two-ways-of-writing-php-code + http://localhost:8000/daily/2024/09/19/the-two-ways-of-writing-php-code + +
<p>Something that came up in my discussion with Dave Liddament for the Beyond Blocks podcast was that there seem to be two ways of writing PHP code.</p> + +<p>One is writing strict code by enabling strict typing, using parameter and return types, and leveraging tools like PHPStan at a high level to analyze code.</p> + +<p>The other is no not use types and to use a more "duck typing" approach.</p> + +<p>The term "visual debt" came from a video discussing the pros and cons of these approaches.</p> + +<p>The same can be said for JavaScript and TypeScript, but PHP can do both and gives the Developer the choice of how they write their code.</p> + +<p>I prefer writing strict code and for my code to be as explicit as possible, but I appreciate not everyone does and I like that PHP caters for both.</p> + +<p>How do you write your PHP code?</p> +
+
+ Thu, 19 Sep 2024 00:00:00 GMT +
+ + De-jargoning Drupal + /daily/2024/09/18/de-jargoning-drupal + http://localhost:8000/daily/2024/09/18/de-jargoning-drupal + +
<p>This week, I learned there is a Drupalisms Working Group - a group focused on de-jargoning Drupal and making it easier for newcomers to Drupal by removing some of the Drupal-specific language.</p> + +<p>From <a href="https://www.drupal.org/association/blog/de-jargoning-drupal-working-with-the-community-to-open-up-drupals-terminology">the introductory blog post</a>:</p> + +<blockquote> + <p>If you’re familiar with Drupal, you will have learned its language. You will be familiar with words like Views, Blocks and Paragraphs, and you will appreciate their respective features and functions. But for those new to Drupal, getting to grips with what words mean can mean a steep learning curve.</p> +</blockquote> + +<p>Drupalisms is something I've discussed on a few episodes of Beyond Blocks, including <a href="http://localhost:8000/podcast/21-eirik-morland-violinist-2">the most recent episode and the seonc with Eirik Morland</a>.</p> + +<p>I didn't realise there were BoF sessions about this at DrupalCon Lille last year, so I'm hoping there will be more next week in Barcelona.</p> + +<p>Anything that helps Drupal easier to use and adopt is a good thing.</p> +
+
+ Wed, 18 Sep 2024 00:00:00 GMT +
+ + Next week is DrupalCon Barcelona + /daily/2024/09/17/next-week-is-drupalcon-barcelona + http://localhost:8000/daily/2024/09/17/next-week-is-drupalcon-barcelona + +
<p>Next week is DrupalCon in Barcelona.</p> + +<p>I'm not speaking this year but if you're there and want to discuss automated testing, test-driven development, static analysis, Sculpin, Tailwind CSS, Nix, or anything else, I'll be there all week and would love to meet up and chat.</p> +
+
+ Tue, 17 Sep 2024 00:00:00 GMT +
+ + Experimenting with the Default Content module + /daily/2024/09/16/experimenting-with-the-default-content-module + http://localhost:8000/daily/2024/09/16/experimenting-with-the-default-content-module + +
<p>I recently sent a database to a client whose new Drupal website I'm building.</p> + +<p>I'd populated it with some default users, nodes and menu links that they'd be able to review after they import the database into their hosting.</p> + +<p>That worked well, but I've also recently been using the <a href="https://www.drupal.org/project/default_content">Default Content module</a> which exports entities into YAML and saves them as code alongside the configuration.</p> + +<p>Now I can install the website from scratch using the exported configuration to re-add the content types, block types, etc, and by enabling a custom module, all the default content will also be recreated.</p> + +<p>I can tear the site down now and rebuild it as often as I like and avoid contaminating my environment with any rogue configuration or content changes.</p> + +<p>Everything is reproducible.</p> + +<p>I also wouldn't have needed to send the database to the client. They could have installed Drupal and followed the same steps I would do locally and got exactly the same result.</p> + +<p>I like this approach and can see me using it more on future projects.</p> +
+
+ Mon, 16 Sep 2024 00:00:00 GMT +
+ + Looking for alpha testers + /daily/2024/09/15/looking-for-alpha-testers + http://localhost:8000/daily/2024/09/15/looking-for-alpha-testers + +
<p>As someone who works on multiple Drupal applications, I know it can be tricky to keep on top of all the available updates.</p> + +<p>So, I'm building a SaaS project to display all your available updates in one place.</p> + +<p>If you're a freelancer or work for an agency or any team that works on multiple Drupal applications, this could be useful for you.</p> + +<p>If this is you, I'm looking for alpha testers to help me test it.</p> + +<p>If you're interested, reply and let me know.</p> +
+
+ Sun, 15 Sep 2024 00:00:00 GMT +
+ + What's your plan? + /daily/2024/09/14/whats-your-plan + http://localhost:8000/daily/2024/09/14/whats-your-plan + +
<p>A question I ask before starting a task, whether I'm working solo or as part of a team is "What is your plan?".</p> + +<p>How am I going to approach the task I'm about to start working on?</p> + +<p>What information do I need?</p> + +<p>What approach am I going to take and which am I not going to take?</p> + +<p>What will some of the puzzle pieces be? Will I need a route, a Controller, a Service, a Command, a Value Object?</p> + +<p>Asking these questions and making these initial decisions upfront greatly reduces my implementation time as I only need to focus on delivering what I've already planned.</p> + +<p>And I can <a href="http://localhost:8000/daily/2023/11/09/readme-driven-development">capture these decisions in a README file</a> <a href="http://localhost:8000/daily/2024/09/04/diagram-driven-development">or diagram</a>, that's ideal.</p> +
+
+ Sat, 14 Sep 2024 00:00:00 GMT +
+ + Violinist, render arrays and feature flags + /daily/2024/09/13/violinist-render-arrays-and-feature-flags + http://localhost:8000/daily/2024/09/13/violinist-render-arrays-and-feature-flags + +
<p>This week, I spoke with Eirik Morland again on the <a href="http://localhost:8000/podcast">Beyond Blocks podcast</a> about recent improvements to violinist.io, such as team/multi-user subscriptions.</p> + +<p><a href="http://localhost:8000/podcast/21-eirik-morland-violinist-2">Listen to the episode now</a>.</p> + +<p>I was great to speak to Eirik again and for him to be the first returning guest on the podcast.</p> + +<p><a href="http://localhost:8000/podcast/8-eirik-morland-violinist">Listen to the first episode with Eirik</a>.</p> +
+
+ Fri, 13 Sep 2024 00:00:00 GMT +
+ + When did you last deploy to production? + /daily/2024/09/12/when-did-you-last-deploy-to-production + http://localhost:8000/daily/2024/09/12/when-did-you-last-deploy-to-production + +
<p>If you've experienced issues or are worried about deploying changes to production, <a href="http://localhost:8000/daily/2024/09/11/do-you-deploy-on-fridays">on a Friday or another day</a>, when did you last deploy something?</p> + +<p>Can you make deployments smaller and more frequent?</p> + +<p>Deploying regularly makes each deployment less risky and having a smaller changeset makes it easier to find and fix any issues that arise.</p> + +<p>I'm much happier deploying to production if I've already done so that day, or at least that week.</p> + +<p>Any time more than that, or if the changeset is large, the more likely there will be issues and the longer it will take to resolve them.</p> +
+
+ Thu, 12 Sep 2024 00:00:00 GMT +
+ + Do you deploy on Fridays? + /daily/2024/09/11/do-you-deploy-on-fridays + http://localhost:8000/daily/2024/09/11/do-you-deploy-on-fridays + +
<p>Do you deploy changes to production on Fridays?</p> + +<p>Some people don't as they're worried about potential issues occurring over the weekend.</p> + +<p>When's the last time you did a deployment which caused a issue 24 or 48 hours later?</p> + +<p>In my experience, most issues are visible immediately or shortly after a deployment and not days later.</p> + +<p>Deploying on a Friday may not be as risky as you think.</p> +
+
+ Wed, 11 Sep 2024 00:00:00 GMT +
+ + Beyond Blocks passes 1,000 downloads + /daily/2024/09/10/beyond-blocks-passes-1-000-downloads + http://localhost:8000/daily/2024/09/10/beyond-blocks-passes-1-000-downloads + +
<p>Today, Beyond Blocks - <a href="http://localhost:8000/podcast">the podcast I started last year</a> and has 20 published episodes - passed 1,000 total downloads.</p> + +<p>I've had some great guests on the show and discussed some interesting topics so far and I have others recorded and guests lined up for future episodes.</p> + +<p>This week's episode will be with Eirik Morland again - the first returning guest - where we discuss the improvements and changes that have been made to Violinist.io since we spoke in January.</p> + +<p><a href="http://localhost:8000/podcast/8-eirik-morland-violinist">The first episode with Erik is here</a> if you want to listen to it beforehand.</p> + +<p>Thanks to all the guests and listeners of the podcast, and if you'd like to be a guest or suggest a topic, reply and let me know.</p> +
+
+ Tue, 10 Sep 2024 00:00:00 GMT +
+ + Avoiding primitive obsession + /daily/2024/09/09/avoiding-primitive-obsession + http://localhost:8000/daily/2024/09/09/avoiding-primitive-obsession + +
<p>Something interesting that <a href="https://www.daveliddament.co.uk">Dave Liddament</a> and I discussed was the use of value objects in application code.</p> + +<p>Instead of using a primitive type, such as <code>string</code>, you can create a new value object for a specific type of string, such as an <code>EmailAddress</code> or, in my side project, a <code>LicenceKey</code>.</p> + +<p>Both are strings, but using value objects of specific types can make the code more readable and its intent clearer.</p> + +<p>A value object can contain additional logic, such as validation to execute an ensure the value object is valid, such as making sure a string is not empty, is a specific length or only contains valid characters.</p> + +<p>This an approach that I'm going to use more going forward.</p> + +<p>I also found <a href="https://www.youtube.com/watch?v=yJpvObzKewY">a lighting talk by Dave</a> at a PHPSW meetup where he explains this further and, of course, you can listen to the podcast episode after it's been released.</p> +
+
+ Mon, 09 Sep 2024 00:00:00 GMT +
+ + My laptop died + /daily/2024/09/08/my-laptop-died + http://localhost:8000/daily/2024/09/08/my-laptop-died + +
<p>Yesterday, my daily driver laptop suddenly died.</p> + +<p>The charging lights are on and the keyboard backlights are on, but there's no display on the internal screen or when I plug it into an external monitor.</p> + +<p>Today, it's gone into a local computer repair shop so I'm writing this email from a different laptop than normal.</p> + +<p>The great thing about using NixOS and Home Manager is that all of my laptop's configuration is saved within <a href="https://github.com/opdavies/dotfiles.nix">my dotfiles repository</a> - from the operating system and window manager configuration to the programs I have installed and how they are configured.</p> + +<p>I was able to get half a laptop (an old one with a broken lid) working with my external monitor and running with an identical configuration very quickly.</p> + +<p>I didn't need to wonder what programs I had installed or how I configured them. It was all available in my dotfiles.</p> + +<p>And, because I commit early and often, the code I've been working on recently is all in remote Git repositories and using tools like Nix and Docker, I can get them running again quickly too.</p> +
+
+ Sun, 08 Sep 2024 00:00:00 GMT +
+ + Find bugs sooner + /daily/2024/09/07/find-bugs-sooner + http://localhost:8000/daily/2024/09/07/find-bugs-sooner + +
<p>Whilst speaking with Dave Liddament last week, I remembered a slide I've seen in some of his previous presentations, such as <a href="https://www.daveliddament.co.uk/talks/effective-code-review">Effective Code Review</a>.</p> + +<p>On the slide is a graph that shows the amount of time needed to fix a bug at different stages of the development lifecycle.</p> + +<p>Fixing a bug is the quickest and simplest when writing code.</p> + +<p>It's harder when being code reviewed or QA or client tested, more so when it's being released, and the most difficult and expensive once it's in the maintenance phase.</p> + +<p>Once it's live in the production environment, there's additional cleanup work to do which adds to the time and cost, and potentially damages your reputation.</p> + +<p>The sooner you can find a bug, the better.</p> + +<p>This is where tools such as automated testing, static analysis and CI pipelines shine and make it easier for you to find and fix potential bugs sooner.</p> +
+
+ Sat, 07 Sep 2024 00:00:00 GMT +
+ + Bootcamps, Hackathons, Meetups and Drupal with George Gordon + /daily/2024/09/06/bootcamps-hackathons-meetups-and-drupal + http://localhost:8000/daily/2024/09/06/bootcamps-hackathons-meetups-and-drupal + +
<p>This week on the Beyond Blocks podcast, I spoke with George Gordon - a graduate from the School of Code who was recently employed in their first Junior Developer role - about their move into tech and software development, their experiences at hack days and meetups, and their introduction to Drupal.</p> + +<p>I've been a mentor for School of Code for the last few cohorts as well as attending on-site events such as the TechConnect meetup and hack days.</p> + +<p>I wasn't George's mentor, but I met him at a hackday earlier this year and at a PHP meetup a few months later, so I was happy to have him on the podcast.</p> + +<p><a href="http://localhost:8000/podcast/20-george-gordon">Listen to the episode now</a>.</p> +
+
+ Fri, 06 Sep 2024 00:00:00 GMT +
+ + find vs. get + /daily/2024/09/05/find-vs-get + http://localhost:8000/daily/2024/09/05/find-vs-get + +
<p>This week, I spoke with <a href="https://www.daveliddament.co.uk">Dave Liddament</a> to record an upcoming episode of <a href="http://localhost:8000/podcast">the Beyond Blocks podcast</a>.</p> + +<p>In that conversion, we started to talk about conventions, specifically unwritten conventions used within projects.</p> + +<p>Dave mentioned on some of his projects, they have a convention for when to use <code>get</code> or <code>find</code> method prefix names.</p> + +<p>For example, in a <code>UserRepository</code> class, a method like <code>findUserByEmail()</code> implies that a matching user may not be found.</p> + +<p>Alternatively, <code>getUserByEmail()</code> implies that you're getting a user and will always be one found and returned.</p> + +<p>This interested me and I <a href="https://tuhrig.de/find-vs-get">found a blog post by Thomas Uhrig</a> on find vs. get, which I've read since the recording.</p> + +<p>In the context of the episode, we discussed how conventions like this can be codified and written down and checkable with static analysis tools, but it got me thinking about conventions I've consciously or unconsciously followed, as well as the impact of changing a small word in a method name on its behaviour.</p> + +<p>What about you?</p> + +<p>Do you have similar conventions that you follow in your applications?</p> +
+
+ Thu, 05 Sep 2024 00:00:00 GMT +
+ + Diagram-driven development + /daily/2024/09/04/diagram-driven-development + http://localhost:8000/daily/2024/09/04/diagram-driven-development + +
<p>You've heard of README-driven development, where you start by writing a README and documenting what you're going to code you start coding.</p> + +<p>I've recently been doing diagram-driven development, where I start with a diagram and build a flow chart of the functionality, what pieces I'll need and what the information flow or user journey looks like.</p> + +<p><a href="http://localhost:8000/daily/2024/08/18/mermaid-markdown-for-charts">I've been using Mermaid</a>, so the diagrams are easy and quick to create, are version-controlled and a stored in the same code repository.</p> + +<p>You can see an example in the <a href="https://github.com/opdavies/build-configs/tree/f02fce7ff5b5cff202ec8b893a4b3c7e7c56f3c4/docs">Build Configs repository</a>, which is <a href="http://localhost:8000/daily/2024/08/27/build-configs-is-open-source">now public and open-source</a>.</p> + +<p>Similar to writing the README first, creating a diagram upfront helps me clarify what I'm going to build and how I'm going to do it.</p> + +<p>And using Mermaid means I can create it and push a temporary branch or create a pull or merge request to share it with colleagues to review before I start.</p> +
+
+ Wed, 04 Sep 2024 00:00:00 GMT +
+ + Do your commit messages still make sense? + /daily/2024/09/03/do-your-commit-messages-still-make-sense + http://localhost:8000/daily/2024/09/03/do-your-commit-messages-still-make-sense + +
<p>Once you've <a href="http://localhost:8000/daily/2024/09/02/no-one-sees-your-clean-up-commits">cleaned up and tided your commits</a>, re-ordered them and squashed any commits together to combine them into logical changes, before you push them, you should also check if your commit messages still make sense.</p> + +<p>Using short or generic commit messages is fine when spiking a solution in very short feedback loops, but when you push your changes, you want them to be as descriptive and meaningful as possible.</p> + +<p>If someone ran <code>git log</code> weeks, months or years from now, would they get value from reading the commit messages?</p> + +<p>Are you using the subject and body correctly and wrapping text when needed?</p> + +<p>Does each message accurately reflect and describe the change?</p> + +<p>Does it capture why the change is needed, any alternative approaches that we considered or tried, or any complications that were encountered?</p> + +<p>If you're following a standard like <a href="http://localhost:8000/daily/2022/09/01/conventional-commits-changelogs">conventional commits</a>, have you correctly included the required information, such as the type, scope and any references, such as the task reference?</p> + +<p>Having a Git log with detailed history is valuable when you need to review the changes in the future, but it also makes it more likely your changes will be approved and merged, whether you're working on a paid project or volunteering on an open-source project.</p> +
+
+ Tue, 03 Sep 2024 00:00:00 GMT +
+ + No-one sees your clean-up commits + /daily/2024/09/02/no-one-sees-your-clean-up-commits + http://localhost:8000/daily/2024/09/02/no-one-sees-your-clean-up-commits + +
<p>When you're working on a task - <a href="http://localhost:8000/daily/2024/08/31/make-it-work-then-make-it-good">whether you're making it work or making it good</a>, you can commit your code changes as often as you like.</p> + +<p>You should definitely commit your changes every time you have a working iteration, even if it's not the complete or final version, or even if the code doesn't pass all the coding standards and static analysis checks.</p> + +<p>Things can be fixed or improved in subsequent commits.</p> + +<p>You can amend or squash commits locally so your clean-up and work-in-progress commits are removed before you push your final version to your remote repository.</p> + +<p>Whilst test-driven development says you should work in small feedback loops and steps, you don't need to push every commit as you wrote them.</p> + +<p>Until you run <code>git push</code>, your commits are yours and yours only.</p> + +<p>You have the opportunity to tidy up and organise your changes - making your commits easier to review and more likely to be approved in a code review.</p> +
+
+ Mon, 02 Sep 2024 00:00:00 GMT +
+ + You need tests to refactor safely + /daily/2024/09/01/you-need-tests-to-refactor-safely + http://localhost:8000/daily/2024/09/01/you-need-tests-to-refactor-safely + +
<p><a href="http://localhost:8000/daily/2024/08/31/make-it-work-then-make-it-good">Once you have working code</a>, you can refactor it to make it better.</p> + +<p>You can rename variables, extract new functions or classes, ensure the code is styled and formatted correctly - anything to make the code easier to read, understand or maintain.</p> + +<p>But, the key thing is the code still needs to work.</p> + +<p>You don't want to break working code and introduce a regression, even if it is messy or difficult to read.</p> + +<p>The best way I know to approach this is by having automated tests covering this code, and ensure they are working and passing before starting to refactor.</p> + +<p>Then you can run the tests again and ensure they still pass.</p> + +<p>If they pass, the code still works and the refactoring was successful.</p> + +<p>If not, the code is broken and you need to revert the changes and start again.</p> +
+
+ Sun, 01 Sep 2024 00:00:00 GMT +
+ + Make it work, then make it good + /daily/2024/08/31/make-it-work-then-make-it-good + http://localhost:8000/daily/2024/08/31/make-it-work-then-make-it-good + +
<p>Your first objective when writing software is to make it work by - any means necessary.</p> + +<p>If you're doing test-driven development, you want to get a failing test to pass as quickly as possible.</p> + +<p>You want to get your application into a working state, whether that's hard-coding some values, focusing on the happy path implementation, holding off on implementing design patterns, or something else - you just want the code to work, even if it's a "bad" solution.</p> + +<p>Once it's working, you can refactor it into a good solution.</p> +
+
+ Sat, 31 Aug 2024 00:00:00 GMT +
+ + Revisiting the Null Object pattern in Drupal + /daily/2024/08/30/revisiting-the-null-object-pattern-in-drupal + http://localhost:8000/daily/2024/08/30/revisiting-the-null-object-pattern-in-drupal + +
<p>The Null Object pattern is one of my favourite ways to DRY up code and reduce duplication.</p> + +<p>I wrote the <a href="https://www.drupal.org/project/system_user">System User module</a> for a client project a number of years ago, which also included the <a href="https://www.drupal.org/project/null_user">Null User module</a>.</p> + +<p>If no system user is found, it returns a null (empty) user instead that returns null or empty values by default.</p> + +<p>Today, <a href="https://x.com/opdavies/status/1829183673725284772">I posted</a> <a href="https://mastodon.social/@opdavies/113046773292848198">and tooted</a> a screenshot of some code from a side project I'm working on, which I realised would be another good time to use the Null User module.</p> + +<p>In the code, I search for a user with a given licence key.</p> + +<p>If a user is found, it is returned.</p> + +<p>If not, instead of returning FALSE or NULL, I return a null user object that I can treat the same as a real user.</p> + +<p>Now, when I create the node and assign an owner, I can just use <code>$account-&gt;id()</code> and it will always work if a user was found or not.</p> + +<p>This makes the code cleaner, simpler and easier to test and maintain.</p> +
+
+ Fri, 30 Aug 2024 00:00:00 GMT +
+ + 600 Daily Emails + /daily/2024/08/29/600-daily-emails + http://localhost:8000/daily/2024/08/29/600-daily-emails + +
<p>This is the 600th daily email I've sent to this list, containing my software developments thoughts, tips and updates.</p> + +<p>I'd like to thank all of the subscribers to the email list, including those who send me replies with their thoughts and opinions on what I write.</p> + +<p>It's been great to speak to subscribers at in-person events and conferences too, and to hear how some of my thoughts and approaches have helped others.</p> + +<p>I want to continue growing the email list and the community of subscribers, so please forward any emails that you find useful or insightful, or share them on social media so others can find them, and I'll look forward to getting to 1,000 emails and beyond.</p> +
+
+ Thu, 29 Aug 2024 00:00:00 GMT +
+ + Single File Components in Drupal with Sam Mortenson + /daily/2024/08/28/single-file-components-in-drupal-with-sam-mortenson + http://localhost:8000/daily/2024/08/28/single-file-components-in-drupal-with-sam-mortenson + +
<p>In the most recent episode of the <a href="http://localhost:8000/podcast">Beyond Blocks podcast</a>, I spoke with <a href="https://mortenson.coffee">Sam Mortenson</a> about Single File Components in Drupal as well as building static Drupal websites with Tome.</p> + +<p>As a Drupal Specialist who works with other technologies including Vue.js, Symfony and Sculpin, it was great to discuss and learn more about both topics.</p> + +<p><a href="http://localhost:8000/podcast/19-sam-mortenson">Listen to the episode now</a>.</p> + +<p>Also, prior to releasing this episode, the total downloads for the podcast are almost at 1,000. Thank you to all the previous guests and everyone who listens to the podcast!</p> +
+
+ Wed, 28 Aug 2024 00:00:00 GMT +
+ + Build Configs is now public and open-source + /daily/2024/08/27/build-configs-is-open-source + http://localhost:8000/daily/2024/08/27/build-configs-is-open-source + +
<p>Build Configs - the tool I wrote to generate build configuration files for projects - is <a href="https://github.com/opdavies/build-configs">now released as open-source software on GitHub</a>.</p> + +<p>I wrote it to generate configuration files, such as PHPUnit, PHPStan and PHPCS files, Dockerfiles, Docker Compose files and others, for personal and client projects - namely Drupal, Symfony and Sculpin projects - instead of writing them by hand every time and making them easier to maintain.</p> + +<p>I've used it for my <a href="https://github.com/opdavies?tab=repositories&amp;q=docker-example-">Docker examples on GitHub</a> done <a href="https://www.youtube.com/watch?v=Wlkcf1PLWN8">live streams where I've re-explored Behat</a> and <a href="http://localhost:8000/talks/building-build-configs">given talks on the project</a>, so it made sense to open-source it and release it publicly.</p> + +<p>There are still more I'd like to do with it, including improving documentation and adding examples, but if you'd like to contribute to it, contributions and pull requests are welcome.</p> +
+
+ Tue, 27 Aug 2024 00:00:00 GMT +
+ + Named arguments add context + /daily/2024/08/26/named-arguments-add-context + http://localhost:8000/daily/2024/08/26/named-arguments-add-context + +
<p>A couple of weeks ago, I wrote that <a href="http://localhost:8000/daily/2024/08/17/types-add-context">using types adds context to code</a>, making it easier to read and understand.</p> + +<p>Something else that also adds context is <a href="https://www.php.net/manual/en/functions.arguments.php#functions.named-arguments">named arguments</a>, which were introduced to PHP in version 8.0.0.</p> + +<p>I particularly like them when making assertions within tests, where getting the expected and actual values in the wrong order can create some confusing output.</p> + +<p>For example:</p> + +<pre><code class="php">self::assertSame(expected: 'My Drupal website', actual: $node-&gt;label()); +</code></pre> + +<p>While I can tell from the assertion that I'm checking two values are the same, adding the argument names makes it clear which is the expected value and which is the actual value.</p> + +<p>In fact, if you use named arguments, the order no longer matters, so I can put them in whichever order I want and it will work the same way.</p> +
+
+ Mon, 26 Aug 2024 00:00:00 GMT +
+ + CSS, data attributes and feature flags + /daily/2024/08/25/css--data-attributes-and-feature-flags + http://localhost:8000/daily/2024/08/25/css--data-attributes-and-feature-flags + +
<p>I recently used <a href="https://www.drupal.org/project/feature_toggle">Drupal's Feature Toggle module</a> to set a data attribute which enabled some new styling for buttons.</p> + +<p>Firstly, within the theme's .theme file, I added this code:</p> + +<pre><code class="php">/** + * Implements hook_preprocess_html(). + */ +function mytheme_preprocess_html(array &amp;$variables): void { + $variables['attributes']['data-use-new-button-styles'] = \Drupal::service('feature_toggle.feature_status')-&gt;getStatus('use_the_new_button_styling'); +} +</code></pre> + +<p>If the feature toggle is enabled, it adds a <code>data-use-new-button-styles=""</code> attribute to the <code>body</code> element.</p> + +<p>If it is disabled, the attribute is not added, so I can write CSS that's applied when the data attribute is present:</p> + +<pre><code class="css">[data-use-new-button-styles] .btn-primary { + background-color: red; +} +</code></pre> + +<p>In a Tailwind CSS project (this isn't), the same could be achiveable with a custom interaction state - similar to <code>hover</code>, <code>focus</code> and <code>active</code>. Something like <code>new-button-styles:bg-red-500</code>.</p> + +<p>I like using feature flags and was happy to find a way to use them when adding this new CSS.</p> +
+
+ Sun, 25 Aug 2024 00:00:00 GMT +
+ + Abbreviations are better than aliases + /daily/2024/08/24/abbreviations-are-better-than-aliases + http://localhost:8000/daily/2024/08/24/abbreviations-are-better-than-aliases + +
<p>Shell aliases are a useful way to shorten long or complicated commands or to easily add additional arguments when running commands.</p> + +<p>Common aliases are I see are <code>gs</code> for <code>git status</code>, <code>a</code> for <code>artisan</code> and <code>dr</code> for <code>drush</code>.</p> + +<p>I've been experimenting with Zellij for the last day or so, and have written aliases like <code>zl</code> for <code>zellij list-sessions</code>, but have also added extra arguments such as <code>zellij list-sessions | sort | grep -v EXITED</code> to sort the sessions and filter any exited sessions.</p> + +<p>Running aliases makes it easier and quicker for me to run these commands, and less likely for me to make errors and type incorrectly.</p> + +<p>The issue with aliases is that you can forget that the underlying commands are if you only type and see <code>gs</code> or <code>zl</code>.</p> + +<p>It's also not easy when giving demos or pair or mob programming for others to see and understand the commands that are being run.</p> + +<p>Instead of aliases, I mostly use abbreviations with zsh-abbr that expand automatically after pressing the space key.</p> + +<p>That way, I and others get to see and understand the commands being run.</p> + +<p>I still have some aliases that don't expand but now I use abbreviations as my default approach.</p> +
+
+ Sat, 24 Aug 2024 00:00:00 GMT +
+ + Speaking at PHP Berkshire + /daily/2024/08/23/speaking-at-php-berkshire + http://localhost:8000/daily/2024/08/23/speaking-at-php-berkshire + +
<p>This week, I'm speaking at the newly-restarted PHP Berkshire meetup.</p> + +<p>Organised by Humand Talent, the same people who now organise PHP Oxford (where I spoke in January), this time I'm <a href="http://localhost:8000/presentations/building-static-websites-sculpin">speaking about Sculpin</a> - the PHP static site generator I use for my website and to publish my <a href="https://zet.oliverdavies.uk">new Zettlekasten notes</a>.</p> + +<p>I'm looking forward to discussing again why I like static site generators and what benefits they offer.</p> + +<p>I have some things I want to make sure I mention, so expect a note in my Zettlekasten about that soon, too.</p> + +<p>If you're interested, <a href="https://phpsw-sculpin-demo.oliverdavies.uk">here's the demo website</a> I did for PHP South West, <a href="https://github.com/opdavies/phpsw-sculpin-demo">along with the code on GitHub</a>.</p> +
+
+ Fri, 23 Aug 2024 00:00:00 GMT +
+ + Drush is using Laravel Prompts + /daily/2024/08/22/drush-is-using-laravel-prompts + http://localhost:8000/daily/2024/08/22/drush-is-using-laravel-prompts + +
<p>Whilst starting to upgrade my <a href="http://localhost:8000/atdc">automated testing email course</a> to Drupal 11, I needed to update Drush and <a href="https://x.com/opdavies/status/1825940869595201905">noticed the output was different</a>.</p> + +<p>People replied to that post, saying Drush was now using Laravel Prompts.</p> + +<p>I've seen the conference talk <a href="https://www.youtube.com/watch?v=PW-2_-KxF-8">where Jess Archer unveiled Laravel Prompts</a> at Laracon EU, but wasn't aware it was being used by Drush.</p> + +<p>It's great to see Laravel Prompts being used in other PHP projects, which was one of Jess' main objectives, and the Drupal ecosystem continuing to <a href="http://localhost:8000/daily/2024/06/09/proudly-found-elsewhere">get off the island</a>.</p> +
+
+ Thu, 22 Aug 2024 00:00:00 GMT +
+ + Publishing a Zettelkasten + /daily/2024/08/21/publishing-a-zettelkasten + http://localhost:8000/daily/2024/08/21/publishing-a-zettelkasten + +
<p>Inspired by others like rwxrob and Mischa van den Burg, I've started experimenting with the Zettelkasten method of atomic note-taking alongside my current methods.</p> + +<p>Originally writing notes as text files in Vim, I've decided to <a href="https://zet.oliverdavies.uk">start publishing them online</a>, where possible. The first notes are already there.</p> + +<p>This will be separate to this email list and will contain notes on various topics, not just programming and software development, although I can imagine some notes progressing to be part of emails sent to this list.</p> + +<p>The website itself is open-source and <a href="https://github.com/opdavies/zet.oliverdavies.uk">hosted on GitHub</a>, and each page has an "Improve this page" link to edit that page and submit a pull request on GitHub.</p> + +<p>The website is a basic <a href="http://localhost:8000/presentations/building-static-websites-sculpin">Sculpin static site</a> with a notes content type.</p> + +<p>There is no styling yet, as I wanted to focus on deploying the MVP version, but I will add some styling and some additional features in the future.</p> + +<p>At the moment, I'm focusing on writing as often as possible for this email list and in my new Zettelkasten.</p> +
+
+ Wed, 21 Aug 2024 00:00:00 GMT +
+ + Writing bash scripts with Nix + /daily/2024/08/20/writing-bash-scripts-with-nix + http://localhost:8000/daily/2024/08/20/writing-bash-scripts-with-nix + +
<p>Yesterday, I wrote about <a href="http://localhost:8000/daily/2024/08/19/bash-scripting-for-fun-and-profit">a bash script I've written</a> to export a list of videos on my external hard drive.</p> + +<p>If you <a href="https://github.com/opdavies/dotfiles.nix/blob/a1ef2d1402c9c607e7a3e4427ce125d0cabeddcd/lib/shared/scripts/export-video-list.nix#L12-L31">looked at the script on GitHub</a> and you're familiar with bash scripting, you may have thought it looked a bit odd or missing some things.</p> + +<p>That's because I wrote it <a href="http://localhost:8000/daily/2022/09/26/experimenting-with-the-nix-package-manager">with Nix</a> - the tool I've been using for almost two years to manage my dotfiles and operating system configuration.</p> + +<p>Using Nix for bash scripts has advantages, like automatically adding the shebang line, setting sensible defaults for error handling, and automatically running <code>shellcheck</code> when switching.</p> + +<p>I think the main advantage, though, is it can inject any dependency packages into the script.</p> + +<p>This script needs <code>tree</code> and <code>jq</code>, so they are dependencies of the script and the script can't run if they aren't installed.</p> + +<p>But, I don't need to assume they are available. Nix can do it automatically and make any packages added as build inputs available.</p> + +<p>Now, if anyone else wanted to use this script, or I wanted to share another script with colleagues or a client, they can use it without needing to install any dependencies separately and the script will continue to work in the future using its explicitly-added dependencies.</p> +
+
+ Tue, 20 Aug 2024 00:00:00 GMT +
+ + Bash scripting for fun (and profit?) + /daily/2024/08/19/bash-scripting-for-fun-and-profit + http://localhost:8000/daily/2024/08/19/bash-scripting-for-fun-and-profit + +
<p>As well as PHP, JavaScript, HTML, CSS, etc, I write a lot of Bash scripts.</p> + +<p>From <a href="http://localhost:8000/daily/2022/08/15/using-run-file-simplify-project-tasks">project-specific run files</a> to CI pipelines, Dockerfiles and deployment scripts, Bash is used a lot in the ops side of software development.</p> + +<p>Today, though, I wrote <a href="https://github.com/opdavies/dotfiles.nix/blob/a1ef2d1402c9c607e7a3e4427ce125d0cabeddcd/lib/shared/scripts/export-video-list.nix#L12-L31">a small Bash script for my personal laptop</a>.</p> + +<p>I store an archive of video files on an external hard drive of conference talks, courses, tutorials or live streams that I want to keep or rewatch, and found that I've sometimes downloaded the same videos more than once, with the same files on my laptop and hard drive.</p> + +<p>A few days ago, I was watching a video that I thought I'd save, but wasn't sure if I already had, so this script dumps the contents of my hard drive into a JSON file that I can easily browse or search through to see if I already have a video archived.</p> + +<p>The script is fairly simple and uses <code>tree</code> and <code>jq</code> to display and format the data before saving it to a file on my laptop.</p> + +<p>When I need to update the file, I plug in the hard drive and run it again.</p> + +<p>A simple problem, and not something related to a particular project, but something I was able to solve with a Bash script.</p> +
+
+ Mon, 19 Aug 2024 00:00:00 GMT +
+ + Mermaid - markdown for charts + /daily/2024/08/18/mermaid-markdown-for-charts + http://localhost:8000/daily/2024/08/18/mermaid-markdown-for-charts + +
<p><a href="http://localhost:8000/daily/2024/08/16/what-are-err--req-and-res">Whilst mentoring at the School of Code Hackathon</a>, something the team and I discussed was documentation and documenting any decisions we made about the approaches they were taking, the techologies to use, etc.</p> + +<p>We kept it simple by adding this to a README file, but I also mentioned <a href="http://localhost:8000/daily/2022/09/23/adrs-technical-design-documents">ADRs and technical decision documents</a> and how they can be written in Markdown and stored alongside the code in the same repository.</p> + +<p>Another approach to documentation that I like is to create diagrams and flow charts.</p> + +<p>I've used various tools to do this, but recently, I've started to use <a href="https://github.com/mermaid-js/mermaid">Mermaid</a>, which is a Markdown-like syntax to generate charts and diagrams.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>There are online tools to do this, but there's also <code>mmdc</code> command-line tool that generates diagrams locally.</p> + +<p>This means that I can easily generate diagrams and store in the codebase too, and have them version-controlled so I can see the history as they evolve during the project.</p> + +<h2 id="want-an-example%3F">Want an example?</h2> + +<p><a href="https://github.com/opdavies/build-configs/blob/f02fce7ff5b5cff202ec8b893a4b3c7e7c56f3c4/docs/diagram.mmd">Here's one I did for the Build Configs tool</a>, which I recently open-sourced, and that GitHub renders by default (you can click the 'Code' tab to see the code for the chart).</p> +
+
+ Sun, 18 Aug 2024 00:00:00 GMT +
+ + Types add context + /daily/2024/08/17/types-add-context + http://localhost:8000/daily/2024/08/17/types-add-context + +
<p>In yesterday's email, I wrote about why <a href="http://localhost:8000/daily/2024/08/16/what-are-err--req-and-res">readable variable names are important</a> and why I use descriptive variable names in my code.</p> + +<p>Given this pseudo-code:</p> + +<pre><code class="php">function hande(req, res) { +} +</code></pre> + +<p>With the short variable names, whilst you can guess, it's unclear what the variable names are.</p> + +<p>However, in this code, we have the same variable names, but we also have additional type information:</p> + +<pre><code class="php">function handle(Request req, Response res): void { +} +</code></pre> + +<p>Even with the same variable names, I know what their types are and what the function returns, I have better completions and diagnostics in my editor and better static analysis of my code, making it easier to identify and fix potential bugs.</p> +
+
+ Sat, 17 Aug 2024 00:00:00 GMT +
+ + What are err, req and res? + /daily/2024/08/16/what-are-err--req-and-res + http://localhost:8000/daily/2024/08/16/what-are-err--req-and-res + +
<p>Today, I was at another School of Code hackathon event, mentoring a team of three Developers as they planned and built an application within a day.</p> + +<p>During the day, we looked at some example documentation that included variable names like <code>err</code>, <code>req</code>, and <code>res</code>.</p> + +<p>I don't like short variable names like this.</p> + +<p>I'd suggest calling them what they are - <code>error</code>, <code>request</code> and <code>response</code>.</p> + +<p>This makes the code clearer and easier to read and understand, particularly if you aren't using types and need additional context.</p> + +<p>Readability is important as <a href="http://localhost:8000/daily/2024/08/07/people-read-more-code-than-they-write">people read more code than they write</a>.</p> +
+
+ Fri, 16 Aug 2024 00:00:00 GMT +
+ + Docblocks or attributes? + /daily/2024/08/15/docblocks-or-attributes + http://localhost:8000/daily/2024/08/15/docblocks-or-attributes + +
<p>As Drupal 11 uses PHPUnit 10, we can now use the <code>#[Test]</code> attribute instead of the <code>/** @test */</code> annotation for our test methods.</p> + +<p>For example:</p> + +<pre><code class="php">// Before. + +/** @test */ +public function it_returns_the_endpoint() + +// After. + +#[Test] +public function it_returns_the_endpoint() +</code></pre> + +<p><a href="http://localhost:8000/daily/2024/04/04/php-attributes--coming-soon-to-a-drupal-version-near-you">Annotations have started to be replaced</a> in parts of Drupal since 10.2, so it's nice to be able to do it with tests, too.</p> + +<p>Which do you prefer?</p> +
+
+ Thu, 15 Aug 2024 00:00:00 GMT +
+ + Makings things frictionless + /daily/2024/08/14/makings-things-frictionless + http://localhost:8000/daily/2024/08/14/makings-things-frictionless + +
<p>If you want people to do things, you need to make them as simple as possible.</p> + +<p>If you need to need to switch to a different window or tab to run your tests, you'll be less likely to run them.</p> + +<p>I have keybindings in Neovim to run the current test, the previous test or the entire test file, which means I can easily run a test with a couple of key presses and without needing to change applications or to a different terminal.</p> + +<p>I do the same when working on presentation slides, and need to compile the reStructuredText document into a PDF <a href="http://localhost:8000/talks/building-presenting-slide-decks-rst2pdf">using rst2pdf</a>.</p> + +<p>I also use watchers with <code>entr</code> - commands that watch for changes, such as a file being saved, and running a command. This means I can also have my tests run automatically whenever I change a file.</p> + +<p>I have snippets to generate complex code or commands I need to run often, saving me time and reducing errors.</p> + +<p>The more frictionless and easy I can make running a task, the more likely I am and others are to do them.</p> +
+
+ Wed, 14 Aug 2024 00:00:00 GMT +
+ + LEGO, robotics and open-source software + /daily/2024/08/13/lego-robotics-and-open-source + http://localhost:8000/daily/2024/08/13/lego-robotics-and-open-source + +
<p>In the most recent episode of Beyond Blocks, I discuss LEGO, robotics, teaching kids to code and open-source software with Andy Hoang, who runs a company called "Beyond Blocks".</p> + +<p><a href="http://localhost:8000/podcast/18-andy-hoang">Listen to the episode here</a>.</p> + +<p>Next week, Sam Mortenson and I discuss single file components in Drupal.</p> +
+
+ Tue, 13 Aug 2024 00:00:00 GMT +
+ + Always write your code as if... + /daily/2024/08/12/always-write-your-code-as-if + http://localhost:8000/daily/2024/08/12/always-write-your-code-as-if + +
<p>There's a famous programming quote (and variations of it):</p> + +<blockquote> + <p>Always code as if the (person) who ends up maintaining your code will be a + violent psychopath who knows where you live. Code for readability.</p> +</blockquote> + +<p>I believe <a href="https://groups.google.com/g/comp.lang.c++/c/rYCO5yn4lXw/m/oITtSkZOtoUJ">it comes from this Google thread</a>.</p> + +<p>I'm currently working on some Drupal 7 code I first committed to in May 2014.</p> + +<p>That person could also be you!</p> +
+
+ Mon, 12 Aug 2024 00:00:00 GMT +
+ + Commits are cheap + /daily/2024/08/11/commits-are-cheap + http://localhost:8000/daily/2024/08/11/commits-are-cheap + +
<p>When using a version control system, such as Git, commits are quick and cheap.</p> + +<p>I like to make small commits as often as possible whilst working on a task.</p> + +<p>For example, when doing test-driven development, I commit my changes wherever the tests are passing or after a refactor.</p> + +<p>Each commit is a checkpoint I can easily roll back to if needed.</p> + +<p>I've never regretted making a commit, but I've regretted not making one.</p> + +<p>Commits are cheap, but debugging without a recent commit is expensive.</p> +
+
+ Sun, 11 Aug 2024 00:00:00 GMT +
+ + To configure or not to configure + /daily/2024/08/08/to-configure-or-not-to-configure + http://localhost:8000/daily/2024/08/08/to-configure-or-not-to-configure + +
<p>It's been <a href="http://localhost:8000/daily/2023/08/08/8-years-of-dotfiles">more than nine years</a> since I started <a href="https://github.com/opdavies/dotfiles.nix">my dotfiles repository</a>, which is a collection of configuration files for tools I use.</p> + +<p>Originally containing my <code>.gitconfig</code> configuration file for Git, it now contains my configuration for Neovim, tmux, Git and a lot more.</p> + +<p>One of the things I like is being able to see and read other peoples' dotfiles and take inspiration from their configurations.</p> + +<p>I see some configurations that are very complex and customised, and some which are simpler.</p> + +<p>Recently, I've been thinking about how much customisation is too much, and moving towards a more minimal configuration.</p> + +<p>This isn't specific to command-line tools and also applies to customising GUI programs such as PhpStorm or VSCode.</p> + +<p>A more complex configuration means more code to maintain.</p> + +<p>It's more difficult to work on different computers, whether you're pair or mob programming, or working on a remote server. Would you still be productive if you didn't have all your configurations?</p> + +<p>If you've changed the default behaviour of a command, such as not allowing merge commits in Git or rebasing by default, if someone doesn't have that same option, is that going to cause confusion or introduce inconsistencies?</p> + +<p>I'm not going to reset all my configuration files to their default values, but I'll continue to review and decide whether I want to customise something on a case by case basis and whether adding it - especially if it's a larger addition, such as adding a Vim plugin - is worth the maintenance overhead.</p> + +<p>The same as in an application, I don't want to add modules or plugins that I'm not going to use or aren't adding value, and I want to ensure I'm making the most of what the software offers.</p> +
+
+ Thu, 08 Aug 2024 00:00:00 GMT +
+ + People read more code than they write + /daily/2024/08/07/people-read-more-code-than-they-write + http://localhost:8000/daily/2024/08/07/people-read-more-code-than-they-write + +
<p>Which do you do more?</p> + +<p>Read code or write code?</p> + +<p>If you include colleagues' code, logs and CI pipeline output, open-source software, books, courses, tutorials, examples, videos, live streams, meetup and conference talks and blog posts (just to think of some), you definitely read more code than you write.</p> + +<p>Which is why it's important to <a href="http://localhost:8000/daily/2024/08/06/computers-dont-care">optimise for readability</a>.</p> +
+
+ Wed, 07 Aug 2024 00:00:00 GMT +
+ + Computers don't care + /daily/2024/08/06/computers-dont-care + http://localhost:8000/daily/2024/08/06/computers-dont-care + +
<p>Computers don't care how code is written or formatted, but humans do.</p> + +<p>A computer will parse and execute your code whether it's written on a single line or multiple.</p> + +<p>So, don't optimise for computers.</p> + +<p>Optimise your code for humans.</p> + +<p>People read more code than they write, so write your code in a readable way so it's easy for people to read and understand - not the computer.</p> +
+
+ Tue, 06 Aug 2024 00:00:00 GMT +
+ + Application code is only part of the puzzle + /daily/2024/08/05/application-code-is-only-part-of-the-puzzle + http://localhost:8000/daily/2024/08/05/application-code-is-only-part-of-the-puzzle + +
<p>Of course, your application code is important.</p> + +<p>In a Drupal project, in addition to the framework itself, you'll likely have additional community-contributed (contrib) modules that add functionality and any project-specific custom modules and themes.</p> + +<p>The application code, though, is only one piece of the puzzle.</p> + +<p>You also need infrastructure code to ensure the code runs with the correct dependencies and versions for everyone working with it.</p> + +<p>You don't want people using different versions of PHP or a different web server.</p> + +<p>You want consistency between all environments to lower the risk of introducing bugs.</p> + +<p>You also want the infrastructure code to be reliable and simple to run and bundled with the code for each access.</p> + +<p>Instead of using something like MAMP or XAMPP, software like Nix and Docker (or wrappers like Lando and DDEV) have files that can be kept with the application code.</p> + +<p>This makes it much easier for people to contribute, whether they're employees, freelancers, consultants or open-source contributors.</p> +
+
+ Mon, 05 Aug 2024 00:00:00 GMT +
+ + Drupal 7 security support changes + /daily/2024/08/04/drupal-7-security-support-changes + http://localhost:8000/daily/2024/08/04/drupal-7-security-support-changes + +
<p>As announced in the Drupal 7 end-of-life announcement, changes have been made to the Drupal 7 security support policies - effective the 1st of August.</p> + +<p>In summary:</p> + +<ul> +<li>The Drupal Security Team may post moderately critical and less critical issues affecting Drupal 7 in the public issue queue, as long as they are not mass-exploitable.</li> +<li>Drupal 7 branches of unsupported modules and themes are no longer eligible for new maintainership.</li> +<li>PHP 5.5 and below will no longer be supported on Drupal 7 (PHP 5.5 was end-of-life in July 2016).</li> +<li>Security fixes will no longer be provided for Drupal 7 Windows-only issues.</li> +</ul> + +<p><a href="https://www.drupal.org/psa-2023-06-07">There is more information in the announcement</a>, but if you're still on Drupal 7, now is the time to upgrade before its end-of-life date in five months.</p> +
+
+ Sun, 04 Aug 2024 00:00:00 GMT +
+ + Drupal 11 is here! + /daily/2024/08/03/drupal-11-is-here + http://localhost:8000/daily/2024/08/03/drupal-11-is-here + +
<p>Yesterday, the 2nd of August 2024, Drupal 11 was released.</p> + +<p>The next major version of the Drupal content management system and the newest major version since Drupal 10 on the 15th of December 2022.</p> + +<p>There are some requirement changes, such as newer versions of PHP and MySQL, and updated PHP dependencies, such as a newer version of Symfony, PHPUnit and Twig.</p> + +<p>Several Drupal core modules have been moved to contrib modules, making Drupal core simpler and leaner.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>The same as from 8 to 9 and 9 to 10, it's simple to upgrade to Drupal 11.</p> + +<p>I did it with my <a href="https://github.com/opdavies/docker-example-drupal/commit/f4aa3d8a68464327489c884358b2ba8f6212dad0">Docker Drupal example</a> my changing a few files and running <code>composer update</code>.</p> + +<p>To read more about the Drupal 11 release, go to https://www.drupal.org/project/drupal/releases/11.0.0.</p> +
+
+ Sat, 03 Aug 2024 00:00:00 GMT +
+ + Merging unrelated histories + /daily/2024/08/02/merging-unrelated-histories + http://localhost:8000/daily/2024/08/02/merging-unrelated-histories + +
<p>My website is built with Sculpin - a static site generator written in PHP.</p> + +<p>It uses a some of the same Symfony components as Drupal, uses Twig for templating and YAML for configuration, and has similar features like content types and taxonomies for structuring content.</p> + +<p>When I first created my website it was on Drupal 6 and upgraded to Drupal 7 before I started to take an interest in static site generators and later using Jekyll, Sculpin and Astro (and Sculpin, again).</p> + +<p>I enjoyed learning Sculpin and took it as an opportunity to learn Twig before Drupal 8, which I spoke about in <a href="http://localhost:8000/presentations/test-drive-twig-with-sculpin">the first Sculpin talk I gave</a>, at DrupalCamp North in July 2015.</p> + +<p>I had three Git repositories, the current Sculpin one, the Astro version, and the original Sculpin version with its first commit in March 2015 - a few months before DrupalCamp North.</p> + +<p>Static site generators keep the files in text files intead of a database, so I was wondering if it was possible to merge the repositories together and combine the information whilst keeping the same commit history so existing tags and contribtions would still apply to the original commits.</p> + +<p>In short, I was able to do it by adding the old repositories as additional remotes and using the <code>--allow-unrelated-histories</code> <a href="https://git-scm.com/docs/git-merge#Documentation/git-merge.txt---allow-unrelated-histories">option for git merge</a>.</p> + +<p>After fixing some minor merge conflicts, everything was merged successfully and I have [one repository containing 5,272 all commits][2], going back to 2015.</p> + +<p>This makes it older than <a href="http://localhost:8000/daily/2023/08/08/8-years-of-dotfiles">my dotfiles repository</a>, which I started in July 2015.</p> + +<p>Similar to <a href="http://localhost:8000/daily/2024/07/31/why-i-use-linux">why I use Linux</a>, I believe in owning your own content rather than relying on third-party platforms, so having all my content and history in one repository is great.</p> + +<p>And I learned something new about Git at the same time.</p> +
+
+ Fri, 02 Aug 2024 00:00:00 GMT +
+ + Adding automated tests to Content Access by Path + /daily/2024/08/01/adding-automated-tests-to-content-access-by-path + http://localhost:8000/daily/2024/08/01/adding-automated-tests-to-content-access-by-path + +
<p>Back in <a href="http://localhost:8000/podcast/11-mark-conroy">episode 11 of the Beyond Blocks podcast</a>, my guest was Mark Conroy and we discussed various things including his <a href="https://runningplangenerator.com">Running Plan Generator</a> and the <a href="https://www.drupal.org/project/content_access_by_path">Content Access By Path Drupal module</a>.</p> + +<p>Written for the LocalGov Drupal distribution, as its name suggests, the module allows for setting permissions to maintain content based on its path.</p> + +<p>I had a quick look at the module whilst recording and mentioned there were no automated tests, and I was invited to write and contribute some.</p> + +<p>I did, <a href="https://www.youtube.com/live/XTpliKd47Lg">live on YouTube</a>, and <a href="https://www.drupal.org/project/content_access_by_path/issues/3428680">created a merge request</a> for the module for review.</p> + +<p>Today, that merge request was approved and the tests were added to the module, so I'm officially a contributor to that module.</p> + +<p>I also added GitLab configuration, so the tests will automatically run for every change and help avoid regressions in the future.</p> +
+
+ Thu, 01 Aug 2024 00:00:00 GMT +
+ + Why I use Linux for my operating system + /daily/2024/07/31/why-i-use-linux + http://localhost:8000/daily/2024/07/31/why-i-use-linux + +
<p>A common reason when speaking with Developers who use Linux as their operating system is that their software will run on Linux in production, so it's best to run it on Linux locally.</p> + +<p>For me, it's more to do with what software I'm using instead of where it's hosted.</p> + +<p>I develop using open-source software - Git, PHP, Drupal, Sculpin, Vue.js, Ansible, Nix, Tailwind CSS, Neovim - just to name a few, as well as maintaining my own open-source projects.</p> + +<p>It aligns with my ethos and ethics that my operating system and the applications I use are open-source too, when possible.</p> + +<p>I use NixOS as my operating system.</p> + +<p>I develop in Alacritty, tmux and Neovim, and use open-source tools and language servers when coding.</p> + +<p>I edit podcasts and videos in Kdenlive, and create thumbnails in GIMP.</p> + +<p>I don't use exclusively open-source tools, but that's my preference.</p> + +<p>I'm also not locked in to one hardware supplier or one way of doing something.</p> + +<p>If I want to change to a different window manager or from stable to rolling releases, I can.</p> + +<p>I can make my tools work for me instead of adapting my workflow around them.</p> +
+
+ Wed, 31 Jul 2024 00:00:00 GMT +
+ + Maintaining backward compatibility + /daily/2024/07/30/maintaining-backward-compatibility + http://localhost:8000/daily/2024/07/30/maintaining-backward-compatibility + +
<p>I've recently decided I'm going to open source <a href="http://localhost:8000/presentations/building-build-configs">Build Configs tool</a> that I use to generate build configuration files for Drupal, Sculpin and Fractal projects.</p> + +<p>Inspired by <a href="http://localhost:8000/presentations/working-with-workspace">Workspace</a> and others, and based on previous versions of similar tools - most recently by <a href="https://github.com/ALT-F4-LLC/build-configs">TheAltF4Stream's project with the same name</a> (which is written in Rust and supports different template types) - I've been using this tool to manage configuration files for various personal, client and open-source projects.</p> + +<p>Before I open-source it, there are some changes I'd like to make, such as renaming some template types and updating the format and keys within the configuration file.</p> + +<p>Changes to the configuration file would be a breaking change and, whilst it's only me using it, I want my other projects to keep working and for me to continue supporting the prior versions - at least for now, so I want to make sure any changes are backward compatible.</p> + +<h2 id="how-it-works">How it works</h2> + +<p>There are four steps performed when generating files for a project:</p> + +<ul> +<li>Create a final configuration object from the project's configuration file as well as any defaults.</li> +<li>Validate the final configuration.</li> +<li>Create a list of files to generate.</li> +<li>Generate the files.</li> +</ul> + +<p>If I change <code>sculpin</code> to <code>sculpin-site</code> in a configuration file, for example, it will fail the validation step.</p> + +<p>But, I have an opportunity within the first step to perform any normalisation that's needed and to provide a compatibility layer - such as changing <code>sculpin-site</code>, which is an invalid value, to <code>sculpin</code>.</p> + +<p>I also renamed <code>symfony</code> to <code>symfony-cli</code> by performing the same step.</p> + +<p>This means the validation step will receive valid data that it can use and the new changes have been encapsulated within a single step of the process. I haven't needed to change any code elsewhere.</p> + +<p>I can also add deprecation warnings if legacy values are used.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>Similar to feature flags, this is temporary code that will later be removed when I'm ready to remove the compatibility layer, similar to how <code>drupal_set_message()</code> was deprecated and changed to use the <code>Messenger</code> service before being removed in Drupal 9.</p> + +<p>In the future, I can refactor the internal logic to use a different approach and when I'm ready, eventually remove the compatibility layer and tag a new major version with the breaking changes.</p> +
+
+ Tue, 30 Jul 2024 00:00:00 GMT +
+ + Don't run code formatting in your CI pipeline + /daily/2024/07/29/dont-run-code-formatting-in-your-ci-pipeline + http://localhost:8000/daily/2024/07/29/dont-run-code-formatting-in-your-ci-pipeline + +
<p>Something I commonly used to see, and did myself, was running code formatting tools, such as PHP CS Fixer or prettier, automatically their CI pipeline.</p> + +<p>And not using it as a check to fail the pipeline if your code isn't formatted correctly.</p> + +<p>Running it, fixing any code and committing any updates.</p> + +<p>It wasn't long before I stopped doing this.</p> + +<p>Firstly, I wasn't comfortable with a CI pipeline have write access to my codebase. It's fine to be able to clone it and run checks against it, but making changes to my code and pushing it makes me nervous.</p> + +<p>Particularly if you're doing trunk-based development and everyone works on the same branch.</p> + +<p>This also causes additional upstream commits, because the pipeline has committed a change. If you don't remember to pull those commits, you can <a href="http://localhost:8000/daily/2024/07/25/only-have-one-url-per-git-remote">end up in a tricky situation</a>.</p> + +<p>I'm also not a fan of having a lot of <code>php-cs-fixer fixes</code> or <code>prettier fixes</code> commits. I'd prefer the commits be correct when they're pushed, if possible, which is why I usually do micro commits locally and tidy things before pushing them.</p> + +<p>If you're working on a topic branch and creating a pull or merge request, you can squash commits when merging, but <a href="http://localhost:8000/daily/2024/05/11/don-t-delete-my-commit-messages">I'm not a fan of squashing commits</a>, either.</p> + +<p>Instead of relying on a CI pipeline to run code formatting, <a href="http://localhost:8000/daily/2022/08/15/using-run-file-simplify-project-tasks">make it easy to run those tasks locally</a>.</p> +
+
+ Mon, 29 Jul 2024 00:00:00 GMT +
+ + Using a run file in your CI pipeline + /daily/2024/07/26/using-a-run-file-in-your-ci-pipeline + http://localhost:8000/daily/2024/07/26/using-a-run-file-in-your-ci-pipeline + +
<p><a href="http://localhost:8000/daily/2022/08/15/using-run-file-simplify-project-tasks">One of my earliest daily emails was about <code>run</code> files</a> - files that contain Bash functions that combine or simplify project-specific tasks.</p> + +<p>In Drupal projects, these could be to execute Composer or Drush commands, connect to the database, or run automated tests.</p> + +<p>For my CI pipelines, I like to use a function called <code>ci:test</code> that contains all the commands to run in the pipeline.</p> + +<p>This keeps the pipeline configuration as simple and agnostic as possible.</p> + +<p>It also makes it easy for people to read and, because it's a bash file, it will run anywhere without any additional tools.</p> + +<p>For an example, <a href="https://github.com/opdavies/docker-example-drupal/blob/d18bf2242fba1291cabf1e16a5badb6fda7ce509/run#L16-L35">see my Drupal Docker example repository</a>.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>The main advantage, though, is being able to run the pipeline locally, if you need to.</p> + +<p>Maybe you need to debug a failure in the pipeline or you want to test a change to the pipeline locally before pushing it.</p> + +<p>By using a command in a <code>run</code> file, doing so is as simple as running that one command.</p> +
+
+ Fri, 26 Jul 2024 00:00:00 GMT +
+ + Only have one URL per Git remote + /daily/2024/07/25/only-have-one-url-per-git-remote + http://localhost:8000/daily/2024/07/25/only-have-one-url-per-git-remote + +
<p>Did you know Git remotes can have multiple URLs, so when you run a command like <code>git push origin main</code>, it can update more than one Git repository?</p> + +<p>I have a few situations where I thought this would be useful, where I need to mirror code between my and my client's Git repositories, or from my repository to a hosting environment to deploy a change.</p> + +<p>Originally I used <code>git remote add</code> to add a second named remote, such as <code>client</code> or <code>hosting</code>, but this needed me to push twice - once to <code>origin</code> and once to the second remote.</p> + +<p>Sometimes, this meant the repositories were out of sync until the second push was done and, if someone forgot to do it, code someone else was expecting to be on the second remote wasn't available.</p> + +<p>Adding a second URL to <code>origin</code> seemed like a good fix.</p> + +<p>Pushing once would update both remotes and everything would be in sync.</p> + +<p>But, it wasn't always the case.</p> + +<p>If the first push to <code>origin</code> failed, Git doesn't stop.</p> + +<p>It continues to push to the second repository, meaning there would be commits in one remote and not the other.</p> + +<p>This needed remedial work, such as manually running <code>git pull</code> and <code>git merge</code> to sync everything up again - and also creating merge commits, which I'm not a fan of.</p> + +<p>There's also no opportunity to check the CI pipeline checks passed before pushing to a hosting environment.</p> + +<p>Having to do a manual push allows you to check the pipeline run was successful and the tests, etc, passed before pushing potentially broken code to the hosting environment.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>There are pros and cons to both approaches and the right option will depend on each person's and team's situation.</p> + +<p>For now, I'm switching back to having separate remotes and doing two pushes.</p> +
+
+ Thu, 25 Jul 2024 00:00:00 GMT +
+ + Things aren't perfect + /daily/2024/07/24/things-aren-t-perfect + http://localhost:8000/daily/2024/07/24/things-aren-t-perfect + +
<p>As well as <a href="http://localhost:8000/daily/2024/07/22/there-isnt-just-one-way-to-do-something">there not being one right answer</a> when developing software, things aren't always perfect.</p> + +<p>You don't always need to strive for the "ideal" solution.</p> + +<p>Sometimes, what you have is good enough and can be released and can be iterated on later, if needed.</p> + +<p>The focus should be on whether it achieves what it's supposed to.</p> + +<p>There isn't always an ideal or perfect solution.</p> + +<p>Sometimes, the icky solution is the right one.</p> +
+
+ Wed, 24 Jul 2024 00:00:00 GMT +
+ + There isn't just one way to do something + /daily/2024/07/22/there-isnt-just-one-way-to-do-something + http://localhost:8000/daily/2024/07/22/there-isnt-just-one-way-to-do-something + +
<p>In software development, there are usually multiple ways to achieve the same result.</p> + +<p>There is no one "right" answer.</p> + +<p>You can make a website look the same if you write the CSS by hand or use a framework.</p> + +<p>When writing backend code, you could use design patterns and follow good clean code practices or not.</p> + +<p>How you approach a solution depends on your experience and perspective, but one approach isn't wrong when compared to another if they achieve the same result.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>This is why I like pair and mob programming.</p> + +<p>Everyone involved can discuss, plan and contribute to a solution in real-time.</p> + +<p>You're more likely to get a better and more thought-out and robust solution from the collective perspective and experience compared to working individually, with the added benefit that more than one person understands the solution if it needs to be extended or changed in the future.</p> +
+
+ Mon, 22 Jul 2024 00:00:00 GMT +
+ + Automation, Linux training and mechanical keyboards with Jochen Lillich + /daily/2024/07/19/automation-linux-training-and-mechanical-keyboards + http://localhost:8000/daily/2024/07/19/automation-linux-training-and-mechanical-keyboards + +
<p>This week, I released the most recent episode of the <a href="http://localhost:8000/podcast">Beyond Blocks podcast</a>.</p> + +<p>My guest this week is Jochen Lillich - the Monospace Mentor, aka geewiz.</p> + +<p>Jochen and I discuss content creation, live streaming on Twitch and YouTube, infrastructure automation, Linux, mentoring and, of course, mechanical keyboards.</p> + +<p><a href="http://localhost:8000/podcast/17-jochen-lillich">Listen to the episode now</a>.</p> + +<p>I've been a fan of Jochen's content and Twitch streams for a long time, and it was great to meet him at DrupalCon Lille and have him on the podcast.</p> + +<p>I have a few more recorded episodes that will be released over the next few weeks.</p> + +<p>If you want to be a guest on Beyond Blocks, <a href="https://forms.gle/5LCDdrXyqyrbC4ry9">let me know</a>.</p> +
+
+ Fri, 19 Jul 2024 00:00:00 GMT +
+ + What's the smallest number of dependencies you can have? + /daily/2024/07/18/smallest-number-of-dependencies + http://localhost:8000/daily/2024/07/18/smallest-number-of-dependencies + +
<p>Whether it's Drupal modules, PHP libraries, npm packages or Tailwind CSS plugins, what's the smallest number you can have in a project?</p> + +<p>I have a list of Drupal modules and other projects that I commonly use, but I try to keep it to as few as possible.</p> + +<p>The fewer dependencies there are, the less will need to be updated and maintained.</p> + +<p>There's no guarantee each dependency will continue to be supported by its maintainers in the future, and each dependency added is an opportunity to introduce potential bugs and security vulnerabilities.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>I'm not suggesting you need to write everything by hand as this would be ignoring a major benefit of open-source software, but consider if you need each dependency or if you can achieve the project's goals without it.</p> +
+
+ Thu, 18 Jul 2024 00:00:00 GMT +
+ + Queuing long-running tasks + /daily/2024/07/17/queuing-long-running-tasks + http://localhost:8000/daily/2024/07/17/queuing-long-running-tasks + +
<p>Do you have long-running or performance-heavy tasks in your application?</p> + +<p>What about performing complex actions when a user completes a task, such as submitting a form?</p> + +<p>What if the action took several seconds or minutes to complete?</p> + +<p>The user would assume their submission failed and may either leave or submit the form again and cause duplicate submissions.</p> + +<p>Instead, maybe use a queue.</p> + +<p>When the user submits the form, all you need to do is create a queue item for that submission and let the form submit as normal.</p> + +<p>In the background, the queue items will be processed when they can take more time or memory to perform the tasks and without blocking the user - giving them a better experience.</p> + +<p>Drupal supports queues by default, storing queue items in its database.</p> + +<p>For an example, see the <a href="https://www.drupal.org/project/private_message_queue">Private Message Queue module</a> on Drupal.org.</p> + +<p>I wrote it for a client project to use queues when sending private messages to event attendees, which could be hundreds per event.</p> + +<p>Without a queue, the request would have taken a long time to complete, or likely timed out.</p> +
+
+ Wed, 17 Jul 2024 00:00:00 GMT +
+ + Tailwind CSS v4, with even more CSS + /daily/2024/07/16/tailwind-css-v4--with-even-more-css + http://localhost:8000/daily/2024/07/16/tailwind-css-v4--with-even-more-css + +
<p>As someone <a href="http://localhost:8000/blog/uis-ive-rebuilt-tailwind-css">who has been using Tailwind CSS</a> since before 1.0, I'm looking forward to the next major version - Tailwind CSS v4.</p> + +<p>A main difference compared to previous versions is that it will be configured in CSS using CSS variables (aka custom properties).</p> + +<p>No more <code>tailwind.config.js</code> configuration files, just variables like <code>--color-blue-100</code>.</p> + +<p>As I <a href="http://localhost:8000/daily/2024/07/15/the-power-of-arbitrary-classes">already use CSS variables in some situations</a>, I like that it will make it easier to re-use them within Tailwind and give a more consistent experience.</p> + +<p>As well as simple variables, such as colours and spacing, I'm interested to see how everything else will translate to the new configuration approach in Tailwind v4, as well as anything else new that will be included.</p> +
+
+ Tue, 16 Jul 2024 00:00:00 GMT +
+ + The power of arbitrary classes + /daily/2024/07/15/the-power-of-arbitrary-classes + http://localhost:8000/daily/2024/07/15/the-power-of-arbitrary-classes + +
<p><a href="http://localhost:8000/daily/2023/01/02/dont-use-arbitrary-values-in-tailwind-css">I generally don't use aritrary classes with Tailwind CSS</a>.</p> + +<p>But, they are powerful, and I do use them in some situations.</p> + +<p>A few months ago, <a href="https://x.com/opdavies/status/1755332703308652730">I posted a screenshot of a timeline component</a> I've built for a client project.</p> + +<p>Today, I needed to fix an issue with the first and last "paths" as they were stretching further than they should.</p> + +<p>This is the class I added to fix the problem:</p> + +<blockquote> + <p>mr-[calc(50%<em>-_calc(var(--path-width)</em>/_2))]</p> +</blockquote> + +<p>It adds an arbitrary amount of right margin, which is 50% of the container minus half of the path width, which is the <code>--path-width</code> variable.</p> + +<p>This class uses the <code>calc</code> function as well as <code>var</code> to determine the correct margin to apply, whilst keeping the code adaptable in case the path width changes.</p> + +<p>And, as this is a value that's only used in this component, there's no benefit to writing this in a stylesheet - making an arbitrary value was a good option.</p> +
+
+ Mon, 15 Jul 2024 00:00:00 GMT +
+ + How I started using utility-first CSS + /daily/2024/07/11/how-i-started-using-utility-first-css + http://localhost:8000/daily/2024/07/11/how-i-started-using-utility-first-css + +
<p>Before I started using atomic CSS classes, I used to use a popular component-based CSS framework.</p> + +<p>You'd include the styles as well as the HTML markup you needed.</p> + +<p>Whilst using the other framework, I started to sprinkle CSS classes on top of my existing styles, to make it easy to set values such as text sizes, colours, margin and padding without repeating myself and duplicating styles.</p> + +<p>Later, I realised I didn't need the original framework and I could do it myself with atomic classes.</p> + +<p>Page by page and component by component, I refactored everything to use atomic classes.</p> + +<p>Once everything was refactored, I removed the original framework.</p> + +<p>I haven't looked back.</p> +
+
+ Thu, 11 Jul 2024 00:00:00 GMT +
+ + Applying all the things + /daily/2024/07/10/applying-all-the-things + http://localhost:8000/daily/2024/07/10/applying-all-the-things + +
<p>If you're adding Tailwind CSS to your existing project or can't use atomic/utility classes straight away, the <code>@apply</code> directive might be an option.</p> + +<p>It's a way to extract components within CSS using the same classes you'd add to the element.</p> + +<p>Here's a quick example:</p> + +<pre><code class="css">.btn { + @apply py-3 px-6 bg-red text-white hover:bg-green focus:bg-green; +} +</code></pre> + +<p>It works with interaction states, such as hover and focus states, too.</p> + +<p>It's not something I use or recommend often, but it could be a good way to get started.</p> + +<p>Just don't overuse it.</p> +
+
+ Wed, 10 Jul 2024 00:00:00 GMT +
+ + You can do utility-first CSS with Sass + /daily/2024/07/09/you-can-do-utility-first-css-with-sass + http://localhost:8000/daily/2024/07/09/you-can-do-utility-first-css-with-sass + +
<p>Yesterday, I said that <a href="http://localhost:8000/daily/2024/07/08/back-to-sass-and-traditional-css">I'm working on a Sass project with no utility or atomic styles</a>.</p> + +<p>But, the two aren't mutually exclusive.</p> + +<p>You can do both.</p> + +<p>You can write your own utility classes, like <code>flex</code>, <code>font-bold</code> or <code>text-red</code> in Sass or plain CSS.</p> + +<p>You can use a framework like Tailwind CSS, but you don't need to.</p> + +<p>In some projects, with existing stylesheets and usually other frameworks, you can't add anothe full framework without having unintended consequences.</p> + +<p>Usually, if I want to introduce utility classes to an existing project, I start by writing my own that are inspired by a framework such as Tailwind CSS and maybe refactor to the framework later once the concept has been introduced and the codebase is able to work with it.</p> +
+
+ Tue, 09 Jul 2024 00:00:00 GMT +
+ + Back to Sass and traditional CSS + /daily/2024/07/08/back-to-sass-and-traditional-css + http://localhost:8000/daily/2024/07/08/back-to-sass-and-traditional-css + +
<p>I'm currently working on a project that doesn't use any atomic or utility-first CSS.</p> + +<p>It uses Sass, which I haven't used for some time, but it has reminded me of some of the reasons I like the utility-first approach to CSS.</p> + +<h2 id="specificity-and-cascading">Specificity and cascading</h2> + +<p>With utility styles, there are no specificity or cascading issues as styles are added to each element and provide a local scope.</p> + +<p>With global styles, your element can be overridden or altered by another part of CSS elsewhere in the stylesheet.</p> + +<p>I've also had situations where I've had to "undo" unwanted styling that was added elsewhere, such as on a hover or focus state.</p> + +<h2 id="easier-to-read-and-understand">Easier to read and understand</h2> + +<p>With utility styles, I can read the classes on an element and understand straight away what styles are applied to it and start to make changes - especially when using a framework, such as Tailwind CSS.</p> + +<p>With generic class names or IDs, I'm not able to do that.</p> + +<h2 id="context-switching">Context switching</h2> + +<p>To make changes to an element, once I've found it in the HTML, I then need to find the stylesheet (or stylesheets) that add the styling and switch between the HTML and CSS files as many times as needed.</p> + +<p>Usually with utility styles, I rarely need to edit the stylesheet and can work almost exclusively in the HTML and not need to switch between files.</p> + +<h2 id="concatination-and-nesting">Concatination and nesting</h2> + +<p><a href="http://localhost:8000/daily/2024/07/08/back-to-sass-and-traditional-css">Something I've avoided with Sass</a>, as well as newer versions of CSS, is the over-use of nesting styles, which makes it harder to find them when searching for the correct stylesheet.</p> + +<p>If there was this CSS:</p> + +<pre><code class="css">.sidebar { + &amp;-wrapper { + a { + &amp;:hover, + &amp;:focus { + } + } + } +} +</code></pre> + +<p>If I tried searching for <code>.sidebar-wrapper</code> or <code>.sidebar-wrapper a:hover</code>, they wouldn't be found and it would take me longer to find it.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>It's taken me a while to get back into this way of working with CSS, but it does remind me <a href="http://localhost:8000/presentations/taking-flight-with-tailwind-css">why I prefer to use utility styles</a> for my own projects.</p> +
+
+ Mon, 08 Jul 2024 00:00:00 GMT +
+ + Running automated checks in a CI pipeline + /daily/2024/07/07/running-automated-checks-in-a-ci-pipeline + http://localhost:8000/daily/2024/07/07/running-automated-checks-in-a-ci-pipeline + +
<p>As well as <a href="http://localhost:8000/daily/2024/07/03/committing-ci-artifacts">committing build artifacts</a>, another common use for CI pipelines is for running automated checks.</p> + +<p>This could include code linting, static analysis, automated tests, checking for security vulnerabilities, and more.</p> + +<p>Instead of relying on Developers running these checks manually, running them automatically in a CI pipeline ensures they're run regularly and that each commit is deployable.</p> + +<p>If all the checks pass, a commit can be promoted and released.</p> + +<p>If not, the commit should not be deployed and it should be fixed.</p> + +<p>This makes a CI pipeline and automated checks vital to ensure the quality of your software, to identify regressions, and to avoid promoting and releasing broken code.</p> +
+
+ Sun, 07 Jul 2024 00:00:00 GMT +
+ + Committing CI artifacts + /daily/2024/07/03/committing-ci-artifacts + http://localhost:8000/daily/2024/07/03/committing-ci-artifacts + +
<p>One of the main uses for <a href="http://localhost:8000/daily/2024/07/02/ci-not-ci-pipeline">a CI pipeline</a> is to build artifacts for your application, such as installing your dependencies using Composer or npm, or using build tools to perform tasks such as building your CSS and JavaScript assets.</p> + +<p>Performing these tasks in a CI pipeline means the resulting files can be ignored from your code repository and not committed - making your commits smaller and easier to review, and less likely for you to encounter merge conflicts.</p> + +<p>The alternative approach is to not use a CI pipline and to perform the tasks manually and commit them to your repository.</p> + +<p>This introduces a separate set of challenges, but people like having the files in their repository and not worrying about failures in their pipeline.</p> + +<p>Which do you prefer?</p> +
+
+ Wed, 03 Jul 2024 00:00:00 GMT +
+ + CI !== CI Pipeline + /daily/2024/07/02/ci-not-ci-pipeline + http://localhost:8000/daily/2024/07/02/ci-not-ci-pipeline + +
<p>Yesterday I replied to <a href="https://x.com/ianmiell/status/1304103008242991111">a post on X</a>:</p> + +<blockquote> + <p>I have worked on many teams that use CI tooling and call their process CI, but I have never seen CI actually done as defined on Wikipedia:</p> + + <p>"CI is the practice of merging all developers' working copies to a shared mainline several times a day"</p> +</blockquote> + +<p><a href="http://localhost:8000/blog/continuous-integration-vs-continuous-integration">I've written about this before</a> and I think the term CI or CI/CD is one of the most misused or misleading in software development.</p> + +<p>CI, or continuous integration, is, as the post days, the process of everyone merging their changes at least once, or usually several, times a day.</p> + +<p>It isn't something that is configured or created - it's a process.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>You can do CI without a CI pipeline and vice versa.</p> + +<p>You can have a CI pipeline but not do continuous delivery or deployment.</p> + +<p>What most people think of as CI or CI/CD is a set of automated checks that run when code is updated - usually on a feature or topic branch.</p> + +<p>Whilst important, it's not "CI".</p> +
+
+ Tue, 02 Jul 2024 00:00:00 GMT +
+ + A new version of Drupal is only a command away + /daily/2024/07/01/a-new-version-of-drupal-is-only-a-command-away + http://localhost:8000/daily/2024/07/01/a-new-version-of-drupal-is-only-a-command-away + +
<p>Now <a href="http://localhost:8000/daily/2024/06/23/drupal-10-3-released">Drupal 10.3 has been released</a>, and <a href="http://localhost:8000/daily/2024/06/29/countdown-to-drupal-11">websites need to be updated to 10.3 before Drupal 11</a>, it's time to start updating your Drupal 10 websites.</p> + +<p>Luckily, with Composer, upgrading is one simple command.</p> + +<p>There's no need to download .tar.gz or .zip files or update version numbers in a Drush Make file any more.</p> + +<p>If you want to update everything, a simple <code>composer update</code> will do.</p> + +<p>If you want to only update Drupal core, then you'll need to run something like <code>composer update drupal/core</code> or <code>composer update drupal/core-*</code>, or something slightly different if you're using a distribution like Commerce Kickstart or LocalGov Drupal.</p> + +<p>I think adopting Composer was one of the best decisions the Drupal project made - not only <a href="http://localhost:8000/daily/2024/06/09/proudly-found-elsewhere">to include third-party code</a>, such as Symfony components, but it also makes applications so much easier to update.</p> +
+
+ Mon, 01 Jul 2024 00:00:00 GMT +
+ + Bootcamps, communities and first Developer jobs + /daily/2024/06/30/bootcamps--communities-and-first-developer-jobs + http://localhost:8000/daily/2024/06/30/bootcamps--communities-and-first-developer-jobs + +
<p>Tomorrow is the first day of cohort 17 of School of Code - a free 16-week software development bootcamp - and I'm happy to be mentoring a student again for my third time.</p> + +<p>I also mentored the winning team at a hack day event in January and <a href="http://localhost:8000/presentations/communities-contribution">spoke at the TechConnect meetup</a> last year.</p> + +<p>Last week, I spoke with George Gordon - a recent School of Code graduate who I met at the hack day and later at another meetup I was speaking at.</p> + +<p>We discussed his experience with coding bootcamps, getting into the software industry and involved with community events and how he found his first Developer job!</p> + +<p>It will be released soon on <a href="http://localhost:8000/podcast">the Beyond Blocks podcast page</a>.</p> +
+
+ Sun, 30 Jun 2024 00:00:00 GMT +
+ + Countdown to Drupal 11 + /daily/2024/06/29/countdown-to-drupal-11 + http://localhost:8000/daily/2024/06/29/countdown-to-drupal-11 + +
<p><a href="http://localhost:8000/daily/2024/06/23/drupal-10-3-released">With Drupal 10.3 released</a>, Drupal 11 will be the next major version.</p> + +<p>There will be a Drupal 10.4, which will be the first minor maintenance version and supported until Drupal 12's release in 2026.</p> + +<p>According to <a href="https://www.drupal.org/about/core/policies/core-release-cycles/schedule#current">the current development cycle</a>, Drupal 11 will be released in a month's time on the 29th of July, with 11.1 in December alongside 10.4.</p> + +<p>Drupal 7 will be unsupported as of January 2025.</p> + +<p>With Drupal 10 websites needing to be upgraded to 10.3 before 11, it gives people the chance to test their applications on the latest Drupal 10 before upgrading to 11 - a refined version of 10 without the deprecated code.</p> + +<p>It also gives module and theme maintainers the opportunity to make their projects compatible before making any breaking changes, meaning upgrades should be more stable as the main changes can be done beforehand.</p> + +<p>I'm looking forward to upgrading my applications and projects to Drupal 11, and seeing what's to come in 11.1 and beyond.</p> +
+
+ Sat, 29 Jun 2024 00:00:00 GMT +
+ + Drupal 10.3 released + /daily/2024/06/23/drupal-10-3-released + http://localhost:8000/daily/2024/06/23/drupal-10-3-released + +
<p>Last week, Drupal 10.3 was released.</p> + +<p>It's the final feature release of Drupal 10, with Drupal 11 to be released this year.</p> + +<p>As it's a minor release, it provides improvements and new functionality and does not break backward compatibility (BC) for public APIs, so it should be easy to upgrade from Drupal 10.2.</p> + +<p>An interesting change is that you <strong>need to upgrade to Drupal 10.3 prior to upgrading to Drupal 11</strong>. This is similar to Symfony's upgrade cycle and will make upgrading to Drupal 11, which will be refined version of Drupal 10 with a very similar public API, much easier.</p> + +<h2 id="what-about-drupal-10.4">What about Drupal 10.4</h2> + +<p>There will still be a Drupal 10.4 release in December, and will be the first maintenance minor version, receiving limited forward-compatible and security fixes only, and will be supported until the release of Drupal 12 in 2026.</p> + +<p>It's great to see the project evolve, and I'm looking forward to upgrading my projects to Drupal 10.3 and then 11 in the near future.</p> +
+
+ Sun, 23 Jun 2024 00:00:00 GMT +
+ + Aliases and abbreviations + /daily/2024/06/22/aliases-and-abbreviations + http://localhost:8000/daily/2024/06/22/aliases-and-abbreviations + +
<p>Yesterday, I said <a href="http://localhost:8000/daily/2024/06/21/dont-use-aliases">not to use custom shell aliases and functions</a> during presentations and group programming sessions to avoid confusion.</p> + +<p>I use aliases, but they expand after I type them, the same as as snippet in an IDE or text editor.</p> + +<p>Me and everyone else can see the underlying command, and that's also what's saved in my shell history.</p> + +<p>I still have the benefit of not having to type the whole command without obscuring it.</p> + +<p>I used to have custom code in my zsh configuration, but recently <a href="https://github.com/opdavies/dotfiles.nix/commit/0df5f17dae4328546b5d08eef141656a5de2b522">switched to zsh-abbr</a>.</p> + +<p>The first impressions are positive and I no longer need to maintain my custom code.</p> + +<p>I use aliases for commands I don't want to expand, but I've moved everything else has moved to abbreviations.</p> + +<p>If you use zsh, I recommend trying it.</p> +
+
+ Sat, 22 Jun 2024 00:00:00 GMT +
+ + Don't use aliases + /daily/2024/06/21/dont-use-aliases + http://localhost:8000/daily/2024/06/21/dont-use-aliases + +
<p>Shell aliases are a good way to increase productivity by shortening long commands, adding additional options to existing ones or creating new ones that even combine multiple commands.</p> + +<p>Common aliases are <code>g</code> for <code>git</code>, <code>gs</code> for <code>git status</code> and <code>dr</code> for <code>drush</code>, but they will be different for each person depending on what tools they use and what commands they type often.</p> + +<p>Whilst aliases are great for personal productivity, there are times I'd suggest not using them.</p> + +<p>If you're giving a demo as part of a presentation or working in a pair or mob, either use the full commands or explain what custom aliases or functions you're running, what they do, and how they differ from the default functionality.</p> + +<p>I recently watched a video where someone was using a <code>gc</code> command.</p> + +<p>It could have been an alias for <code>git clone</code>, <code>git checkout</code>, <code>git commit</code> or <code>git cherry-pick</code> - just to name a few options.</p> + +<p>It could have been something else altogether.</p> + +<p>Another approach I use is to have aliases auto-expand and show the full command. This makes it possible for others to see the commands being executed and reminds you, too.</p> +
+
+ Fri, 21 Jun 2024 00:00:00 GMT +
+ + Proof of concept + /daily/2024/06/20/proof-of-concept + http://localhost:8000/daily/2024/06/20/proof-of-concept + +
<p>How do you validate an idea from a client or stakeholder?</p> + +<p>Unlike a minimum viable product, which is the smallest amount of functionality for a feature to be released, a proof of concept is an initial investigation into whether the idea is viable.</p> + +<p>What's the least amount of development you can do to prove the concept?</p> + +<p>Can you create something using a smaller data set or with simplified functionality?</p> + +<p>What can you build within a minimal timeframe so, if the idea isn't used, too much time wasn't invested in it, but still verifies it?</p> + +<p>Do you need to write any code or do any development at all?</p> + +<p>All we need to do is find out if the concept could work.</p> +
+
+ Thu, 20 Jun 2024 00:00:00 GMT +
+ + Today I learned + /daily/2024/06/19/today-i-learned + http://localhost:8000/daily/2024/06/19/today-i-learned + +
<p>As a continuous learner, I always try to learn and incorporate new things into my development workflow.</p> + +<p>To keep track of them, I recently started to add them to a TIL.txt file.</p> + +<p>It's a simple plain-text file in my wiki directory that I append to when I learn something new, and that I can reference in the future.</p> + +<p>Whether it's a useful Drupal module, some API documentation, a Git command or something else, my TIL file is becoming a collection of small but valuable pieces of information.</p> + +<p>Here's one I added today:</p> + +<blockquote> + <p>When adding links in Drupal's menu UI, as well as using <code>&lt;nolink&gt;</code> to create an empty link, you can also use <code>&lt;button&gt;</code> to create a keyboard-accessible button. #drupal</p> +</blockquote> + +<p>I may have learned this before and forgotten it, but, now it's in my TIL file, I have somewhere to keep it.</p> + +<p>Always be learning!</p> +
+
+ Wed, 19 Jun 2024 00:00:00 GMT +
+ + Re-learning Behat + /daily/2024/06/18/re-learning-behat + http://localhost:8000/daily/2024/06/18/re-learning-behat + +
<p>A few months ago, I started doing some <a href="https://www.youtube.com/@opdavies/livhttps://www.youtube.com/watch?v=Wlkcf1PLWN8e">live coding sessions on YouTube</a> again.</p> + +<p>As well as <a href="http://localhost:8000/daily/2024/03/16/adding-tests-to-the-content-access-by-path-module">writing automated tests for the Content Access by Path module</a>, I <a href="https://www.youtube.com/watch?v=Wlkcf1PLWN8">started to re-learn Behat</a> - a behavioural testing tool for PHP.</p> + +<p>I've used Behat before but mainly use PHPUnit in Drupal to write my automated tests, so I thought I'd try it again in my <a href="http://localhost:8000/build-configs">Build Configs</a> tool written in Symfony.</p> + +<p>In that stream, I was able to install it, do some research to refresh my memory, and get some initial tests running.</p> + +<p>I've been busy finishing some client development projects recently (and still have some availability for Q3 projects), but I will pick up the live streaming again soon!</p> +
+
+ Tue, 18 Jun 2024 00:00:00 GMT +
+ + Do you still need TypeScript? + /daily/2024/06/17/do-you-still-need-typescript + http://localhost:8000/daily/2024/06/17/do-you-still-need-typescript + +
<p>There have been many discussions and videos recently asking if people still need to use TypeScript.</p> + +<p>There have also been large open-source projects moving from TypeScript back to plain JavaScript.</p> + +<p>The popular approach is using JSDoc comments - similar to PHP docblocks - to replicate some of TypeScript's features, such as autocompletion in IDEs and text editors.</p> + +<p>The suggested benefits are cleaner code and no build step, making it easier to work with and contribute to the code.</p> + +<p>I like using types in PHP and using static analysis tools such as PHPStan so, naturally, I gravitated to TypeScript.</p> + +<p>I wrote a custom form with TypeScript and Vue.js that was an entrypoint to a Drupal Commerce application - populating data from Drupal using an API endpoint and determining and creating a cart with the correct items based on the customer's selections.</p> + +<p>Whilst doing so, I found TypeScript very beneficial and I'll probably continue to use it in the future.</p> + +<p>But, leveraging JSDoc more in situations where I can't use TypeScript, such as writing Tailwind plugins and Stimulus applications, is great to know and to get the best of both.</p> +
+
+ Mon, 17 Jun 2024 00:00:00 GMT +
+ + Bug free guarantee + /daily/2024/06/16/bug-free-guarantee + http://localhost:8000/daily/2024/06/16/bug-free-guarantee + +
<p><a href="http://localhost:8000/pricing">All of my software development proposals and services</a> include a bug-free guarantee.</p> + +<p>What does that mean?</p> + +<p>For a set period after the code is delivered, if it's broken or doesn't work as you expect, I'll fix it.</p> + +<p>For free.</p> + +<p>You just let me know, and I'll make it work.</p> + +<p>No additional charges, invoices or change requests.</p> + +<p>Just working code.</p> +
+
+ Sun, 16 Jun 2024 00:00:00 GMT +
+ + Is the code extensible? + /daily/2024/06/15/is-the-code-extensible + http://localhost:8000/daily/2024/06/15/is-the-code-extensible + +
<p>Rarely you're going to find a module that does exactly what you need out of the box.</p> + +<p>Usually there will be some configuration options, or it will get you to 80% or 90% and you need to fill in any gaps.</p> + +<p>If you need to extend, overwrite or customise a module, have its Developers made it possible or easy to do that?</p> + +<p>Good code should be easy to change or extend.</p> + +<p>Are there hooks you can implement or events you can subscribe to and run your own code?</p> + +<p>Are there Interfaces and base classes you can implement or extend that allow you to use design patterns such as the Decorator pattern?</p> + +<p>Are there public services you can use instead of re-writing and duplicating existing functionality?</p> + +<p>Is there documentation or examples showing how to extend the module?</p> + +<p>Is it a stable foundation for you to build on?</p> +
+
+ Sat, 15 Jun 2024 00:00:00 GMT +
+ + Dead or done + /daily/2024/06/14/dead-or-done + http://localhost:8000/daily/2024/06/14/dead-or-done + +
<p>Yesterday, I wrote about <a href="http://localhost:8000/daily/2024/06/13/vetting-third-party-open-source-software">some things I look for when evaluating open-source projects</a>.</p> + +<p>One thing I said was "When was the most recent commit and release?".</p> + +<p>If a project hasn't had many recent commits, it could be outdated or no longer supported.</p> + +<p>Alternatively, it could be considered feature complete and not getting new features, and only getting bug fixes and maintenance updates.</p> + +<p>I see this a lot with Vim plugins that were written several years ago and are now minimally maintained and updated, but getting no new features.</p> + +<p>This happens in the Drupal space, too, when people wrote a module for a project which they have since completed, or no longer work with that client or for that company.</p> + +<p>If there are at least commits for security compatibility, such as new versions of PHP or node, that's a sign the project is in a maintenance phase.</p> + +<p>If there are no recent commits, the project could be dead and I'd carefully consider if you want to add or use it.</p> + +<p>Something that could help is if maintainers are explicit about what state their project is in.</p> + +<p>Add a note to the README.md or CONTRIBUTING.md file saying if the project is feature complete or what the maintenance state is.</p> + +<p>If the project is no longer maintained, you can also document it and potentially archive the repository too to show that it will no longer be updated and to avoid confusion.</p> +
+
+ Fri, 14 Jun 2024 00:00:00 GMT +
+ + Vetting third-party open-source software + /daily/2024/06/13/vetting-third-party-open-source-software + http://localhost:8000/daily/2024/06/13/vetting-third-party-open-source-software + +
<p>Open-source software, such as Drupal modules and themes, PHP libraries and Tailwind CSS plugins, is great.</p> + +<p>Instead of writing everything from scratch, if someone else has written what you need, you can use it.</p> + +<p>In the Drupal ecosystem, there's a saying - "There's a module for that!".</p> + +<p>But, assuming you find a project that adds the functionality you need, you should review and vet it before adding it to your application.</p> + +<p>Adding too many modules and libraries can cause performance issues, add a maintenance overhead, introduce upstream bugs and add potential security vulnerabilities.</p> + +<p>As well as reading the code, here are some questions I ask and things I look for when deciding to add a project or not:</p> + +<ul> +<li>Does it do what I need?</li> +<li>Does it add too much functionality? Is there a more minimal version that does what I need without the extra overhead?</li> +<li>Does it have a stable release? If there's only a development or alpha release (which are unstable and can break at any time), I wouldn't add it.</li> +<li>Is it extendable? Can I configure or extend it if I need to add more or alter it's default functionality?</li> +<li>How many other websites are using it? Websites like Drupal.org and Packagist will show usage statistics.</li> +<li>How many open issues are there?</li> +<li>When was the most recent commit and release?</li> +<li>Does it have automated tests? If so, are they passing?</li> +<li>Does it have a README.md file or other documentation?</li> +<li>Who are the maintainers?</li> +<li>Are they responsive to issues and accepting of outside contributions? If I find a bug, can I fix and contribute it?</li> +<li>Does the project have a published roadmap?</li> +</ul> + +<p>Finally:</p> + +<p>Do I really need it?</p> +
+
+ Thu, 13 Jun 2024 00:00:00 GMT +
+ + Recording architectural decisions + /daily/2024/06/12/recording-architectural-decisions + http://localhost:8000/daily/2024/06/12/recording-architectural-decisions + +
<p>I recently <a href="https://x.com/StefanGalescu/status/1798421063355007017">saw this post on X</a>, talking about an open-source project.</p> + +<p>This was the part that I want to highlight:</p> + +<blockquote> + <p>I was also impressed by the decisions documents. More projects should implement this concept.</p> +</blockquote> + +<p>Looking at the repository, it contains records of 36 decisions made on the project between May 2023 and April 2024.</p> + +<p>Decision records provide useful information to new people to a project, whether you want to contribute to an open-source project or it's a new client or work codebase.</p> + +<p>The code and project will likely continue once you've finished working on it, so capturing decisions for future Developers is key.</p> + +<p>I've often joined projects and wondered why or how something was done, and can only guess as the decision wasn't recorded or is somewhere not available to me, such as an old wiki or ticketing system.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>I agree with the poster.</p> + +<p>More people should record the decisions made on their project, whether it's open-source or not.</p> +
+
+ Wed, 12 Jun 2024 00:00:00 GMT +
+ + Do you separate your logic? + /daily/2024/06/11/do-you-separate-your-logic + http://localhost:8000/daily/2024/06/11/do-you-separate-your-logic + +
<p>I recently watched <a href="https://www.youtube.com/watch?v=DuozyaJQQ1U">a video about separating logic</a> into different categories within your custom code.</p> + +<p>It wasn't a PHP video, but the concept applies to any programming language or framework.</p> + +<p>This was the final structure of the directories:</p> + +<pre><code class="plain">src/ + Controllers/ + Domain/ + Persistence/ + Properties/ + Services/ +</code></pre> + +<p>It was described as using <code>Controllers</code> for presentational logic, <code>Domain</code> for domain logic, and <code>Services</code> for application logic, and reminds me of a domain-driven design (DDD) approach to organising code.</p> + +<p>I remember watching other older videos showing separating business and presentational logic in React into different components (if I remember correctly).</p> + +<p>This isn't an approach I see in Drupal code, maybe more-so in other PHP framework-based projects like Symfony or Laravel.</p> + +<p>It's something I've been thinking of trying, potentially on my website codebase.</p> + +<p>Do you organise your code in this or a similar way?</p> + +<p>If so, why?</p> + +<p>What advantages does it bring?</p> +
+
+ Tue, 11 Jun 2024 00:00:00 GMT +
+ + Online Drupal mob contribution sessions + /daily/2024/06/10/online-drupal-mob-contribution-sessions + http://localhost:8000/daily/2024/06/10/online-drupal-mob-contribution-sessions + +
<p>I enjoy and advocate for pair and mob programming, and the benefits they offer when working in a team - sharing knowledge whilst onboarding and upskilling the team members, whilst also getting continuous real-time feedback and review of the code that's being written.</p> + +<p>I'm also an open-source maintainer and contributor.</p> + +<p>I've contributed code to Drupal core.</p> + +<p>I've written and maintain Drupal projects, such as the Override Node Options module and the Tailwind CSS starter kit theme.</p> + +<p>I've been a mentor for first-time contributors at several DrupalCon events, helping them get started contributing to Drupal core.</p> + +<p>Usually, the initial problems are finding an issue to work on and how to get started.</p> + +<p>Something I've been considering organising are <strong>online Drupal mob contribution sessions</strong>, where people can work on tasks together in a mob programming approach.</p> + +<p>Not working on Drupal applications (although, I offer <a href="http://localhost:8000/pair">paid pair programming sessions</a>), this would be for contributing to Drupal core, contrib projects, or initiatives like Starshot or LocalGov Drupal.</p> + +<p>If you have ideas or feedback, or if this is something you'd be interested in attending, reply and let me know!</p> +
+
+ Mon, 10 Jun 2024 00:00:00 GMT +
+ + Proudly found elsewhere + /daily/2024/06/09/proudly-found-elsewhere + http://localhost:8000/daily/2024/06/09/proudly-found-elsewhere + +
<p>A few days ago, I wrote about <a href="http://localhost:8000/daily/2024/06/07/not-invented-here">where Drupal used to be</a>, and some of the things that Drupal predates - such as Git, GitHub and Composer.</p> + +<p>With Drupal 8, Drupal changed and switched from "not invented here" to "proudly found elsewhere" and started use and embrace third-party code.</p> + +<p>Now, we use Composer to manage Drupal core's dependencies, as well as for individual Drupal projects - a tool widely used and central to the PHP community and its projects.</p> + +<p>This allowed us to include and use third-party code within Drupal, including Symfony components, Twig templates, Doctrine libraries, Guzzle and more - using tried and tested solutions instead of writing everything from scratch.</p> + +<p>We use PHPUnit for our automated tests and PHPStan for static analysis.</p> + +<p>We use Rector, a PHP tool to automate code updates and refactorings, to automatically create updates for modules to make them compatible with the next major Drupal version.</p> + +<p>Drupal.org have migrated its code repositories to GitLab, now support merge requests instead of patch files, and will soon be migrating issues, too, making Drupal easier to maintain and contribute to.</p> + +<p>As well as the code, we've also been influenced by approaches, such as the modern Drupal release cycle and version management - following semantic versioning, backwards compatibility policies, twice-yearly feature releases and no "rewrite everything" major version upgrades.</p> + +<p>Looking back, a lot has improved since 2015 which makes Drupal a very interesting place to be.</p> +
+
+ Sun, 09 Jun 2024 00:00:00 GMT +
+ + Drupal is older than... + /daily/2024/06/08/drupal-is-older-than + http://localhost:8000/daily/2024/06/08/drupal-is-older-than + +
<p>Whilst writing <a href="http://localhost:8000/daily/2024/06/07/not-invented-here">yesterday's email</a> about "not invented here", I started thinking about how Drupal's timeline (which was created 23 years ago) compares to some of the other similar or related projects.</p> + +<p>Here's a list of project release dates from ChatGPT, which seem correct to me at a glance:</p> + +<ul> +<li>Drupal: January 15, 2001.</li> +<li>Git: April 7, 2005.</li> +<li>Symfony 1: October 22, 2005.</li> +<li>GitHub and Bitbucket: 2008.</li> +<li>GitLab: October 2011.</li> +<li>Laravel: June 9, 2011.</li> +<li>Drupal 7: January 5, 2011.</li> +<li>Symfony 2: July 28, 2011.</li> +<li>Composer: March 1, 2012.</li> +<li>Drupal 8: November 19, 2015.</li> +</ul> + +<p>Looking at this list, when speaking about "not invented here", a lot of what Drupal would come to use and depend on just didn't exist in its early years.</p> +
+
+ Sat, 08 Jun 2024 00:00:00 GMT +
+ + Not invented here + /daily/2024/06/07/not-invented-here + http://localhost:8000/daily/2024/06/07/not-invented-here + +
<p>Drupal used to suffer from a case of "not invented here" syndrome.</p> + +<p>Prior to Drupal 8, all the code within Drupal (with the exception of jQuery, I believe) was written by Drupal Developers.</p> + +<p>The PHP code was mostly procedural and didn't match how modern PHP code was being written in other projects.</p> + +<p>There was no other third-party code and we had to rely on downloading zip files from Drupal.org, using <code>drush dl</code> or Drush Make - a Drupal-only tool for specifying and downloading the specified versions of Drupal and other modules you were using - to get the latest updates.</p> + +<p>Drupal.org has its own Git hosting, testing infrastructure and issue queues.</p> + +<p>Luckily, since Drupal 8's release in 2015, this has been changing.</p> +
+
+ Fri, 07 Jun 2024 00:00:00 GMT +
+ + End the day with a failing test + /daily/2024/06/06/end-the-day-with-a-failing-test + http://localhost:8000/daily/2024/06/06/end-the-day-with-a-failing-test + +
<p>Are you unsure what task to start with tomorrow?</p> + +<p>Do you sometimes forget where you were in a task after taking a break or the start of a new workday or week?</p> + +<p>If you write a failing test, you can run your test suite and be find out what you were doing and know your next step.</p> + +<p>Get that test to pass.</p> +
+
+ Thu, 06 Jun 2024 00:00:00 GMT +
+ + Writing assertions first + /daily/2024/06/05/writing-assertions-first + http://localhost:8000/daily/2024/06/05/writing-assertions-first + +
<p>As well as <a href="http://localhost:8000/daily/2024/06/03/writing-comments-first">writing comments first</a>, when writing tests, I sometimes like to write my tests backwards and start by writing the assertions first.</p> + +<p>I know what I want to assert in the test, so it's an easy place to start.</p> + +<p>I'll run it, see the error, fix it and continue working backwards.</p> + +<p>For example, I could start with this:</p> + +<pre><code class="php">public function testOnlyPostNodesAreShown(): void { + $assert = $this-&gt;assertSession(); + $assert-&gt;pageTextContains('Post one'); + $assert-&gt;pageTextContains('Post two'); + $assert-&gt;pageTextNotContains('This is not a post'); +} +</code></pre> + +<p>This test will fail when I run it, but it makes me think about what I need to do to fix the error and how to do so in the best way.</p> + +<p>In this case, I need to make a request to the page that should render the text:</p> + +<pre><code class="php">public function testOnlyPostNodesAreShown(): void { + $this-&gt;drupalGet('/blog'); + + $assert = $this-&gt;assertSession(); + $assert-&gt;pageTextContains('Post one'); + $assert-&gt;pageTextContains('Post two'); + $assert-&gt;pageTextNotContains('This is not a post'); +} +</code></pre> + +<p>This will still fail, as I also need to create the required posts:</p> + +<pre><code class="php">public function testOnlyPostNodesAreShown(): void { + PostBuilder::create()-&gt;setTitle('Post one')-&gt;getPost(); + PostBuilder::create()-&gt;setTitle('Post two')-&gt;getPost(); + + $this-&gt;createNode([ + 'title' =&gt; 'This is not a post', + 'type' =&gt; 'page', + ]); + + $this-&gt;drupalGet('/blog'); + + $assert = $this-&gt;assertSession(); + $assert-&gt;pageTextContains('Post one'); + $assert-&gt;pageTextContains('Post two'); + $assert-&gt;pageTextNotContains('This is not a post'); +} +</code></pre> + +<p>Now the test passes.</p> + +<p>Doing test-driven development keeps my code clean and minimal, and I find this approach keeps my test clean, too.</p> +
+
+ Wed, 05 Jun 2024 00:00:00 GMT +
+ + Is the abstraction worth it? + /daily/2024/06/04/is-the-abstraction-worth-it + http://localhost:8000/daily/2024/06/04/is-the-abstraction-worth-it + +
<p>I'm currently working on a project, building the initial components and pages in Fractal - a component library tool.</p> + +<p>The templates are written in Twig (or twig.js, as Fractal is written in JavaScript), and my JavaScript is using Stimulus.</p> + +<p>So far, I've been writing the data attributes Stimulus needs, such as <code>data-controller="timeline</code> and <code>data-timeline-closed-by-default-value="true"</code> by hand but, yesterday I decided to write some custom Stimulus functions for this - inspired by the ones in the Symfony UX bundle.</p> + +<p>After a few hours, I had them working and, although they weren't feature comparable with Symfony's, they were good enough to use in my codebase and I migrated the Stimulus code I'd written so far.</p> + +<p>Today, I removed them and reverted my code.</p> + +<h2 id="causing-obscurity-through-abstraction">Causing obscurity through abstraction</h2> + +<p>Here's an example of some code with and without a helper function:</p> + +<pre><code class="html"> +{# With the helper function. #} +&lt;div {{ stimulus_controller("timeline", { closedByDefault: true, closeOtherItems: true }) }}&gt; + +{# Without the helper function. #} +&lt;div + data-controller="timeline" + data-timeline-close-other-items-value="true" + data-timeline-closed-by-default-value="true" +&gt; + +</code></pre> + +<p>Although it's less code, I prefer to be able to see and understand the generated code.</p> + +<p>I don't have to remember the parameters of the helper function, so there's less mental overhead.</p> + +<p>I can debug this easily as I can find the same code that's in the generated HTML.</p> + +<p>If I need to find <code>closed-by-default</code>, it doesn't exist in the template if I'm using the helper function as keys are written in camel-case and converted to kebab-case. This would be likely to cause me an issue in the future if I forgot, and could cause confusion if I needed to explain it to other Developers.</p> + +<h2 id="fewer-dependencies">Fewer dependencies</h2> + +<p>To convert keys from camel case in Twig for kebab case in HTML attributes, I installed a library that I no longer need. This means I can remove that dependency.</p> + +<h2 id="less-code-to-maintain">Less code to maintain</h2> + +<p>Now I've removed those functions, I no longer need to maintain them and I can focus on completing the code for my client and moving forward with the project.</p> + +<p>The functions weren't feature comparable with Symfony's and would have needed more development and potential bug fixes in the future. By removing them, I'm no longer responsible for maintaining and extending it and the codebase is leaner.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>It was an interesting experiment, but, after thinking about it and trying it out, I decided not to keep the helper functions on this project.</p> + +<p>The abstraction wasn't providing value and I prefer to keep the code as simple and easy to work on as possible.</p> +
+
+ Tue, 04 Jun 2024 00:00:00 GMT +
+ + Writing comments first + /daily/2024/06/03/writing-comments-first + http://localhost:8000/daily/2024/06/03/writing-comments-first + +
<p>Something I often do before writing new code is to write out what it needs to do, either in a README file or as code comments.</p> + +<p>It could be paragraphs of text explaining the problem I'm solving, or a short checklist of what the code needs to do.</p> + +<p>This clarifies the situation and the approach and allows uncovering any potential questions or issues.</p> + +<p>It takes time, but I can complete the task quicker as I've made the decisions and answered any questions upfront.</p> + +<p>I'll either remove the comments as I write the code or, if they still add value, I'll keep them to add more context and information.</p> +
+
+ Mon, 03 Jun 2024 00:00:00 GMT +
+ + Only seven months left of Drupal 7 support + /daily/2024/06/02/only-seven-months-left-of-drupal-7-support + http://localhost:8000/daily/2024/06/02/only-seven-months-left-of-drupal-7-support + +
<p>It's June, so there's only seven months left of security updates for Drupal 7.</p> + +<p>Originally released in 2011, Drupal 7 is now on version 7.101 and there are still over 315,000 active Drupal 7 websites according to the project page on Drupal.org.</p> + +<p>Are you stuck on D7 or haven't upgraded yet?</p> + +<p>I'm interested to know why. Reply to this email and let me know.</p> + +<p>Will you be migrating to Drupal 10, or something else?</p> + +<p>If you need help, with <a href="http://localhost:8000/drupal-upgrade">my Drupal 7 upgrade roadmap</a>, I'll create an individual plan to upgrade and enjoy easy upgrades to Drupal 11 and beyond!</p> +
+
+ Sun, 02 Jun 2024 00:00:00 GMT +
+ + Don't use AI to write your automated tests + /daily/2024/06/01/dont-use-ai-to-write-your-automated-tests + http://localhost:8000/daily/2024/06/01/dont-use-ai-to-write-your-automated-tests + +
<p>In <a href="http://localhost:8000/daily/2024/05/31/putting-glue-on-pizza">yesterday's email</a>, I mentioned some of the recent issues I've seen from AI tools.</p> + +<p>I'm wary of any code generated by AI, as I've often found it to be incorrect.</p> + +<p>If you rely on AI-generated code, I'd be especially wary if it also generates the automated tests.</p> + +<p>Automated tests verify your application works as expected, so you need to ensure they are testing the correct things and the logic is correct.</p> + +<p>Can you make a test purposely fail by changing some logic within the test or implementation code?</p> + +<p>Is it clear what each test is doing?</p> + +<p>Are the tests running the implementation code or just testing mocks or running meaningless assertions like <code>self::assertTrue(TRUE)</code>?</p> + +<p>Writing tests is about building confidence, which you can't do if you don't know what your tests are testing or how.</p> +
+
+ Sat, 01 Jun 2024 00:00:00 GMT +
+ + Putting glue on pizza + /daily/2024/05/31/putting-glue-on-pizza + http://localhost:8000/daily/2024/05/31/putting-glue-on-pizza + +
<p>I recently saw articles and videos about an AI service telling people to put glue on pizza and gave a recommended number of rocks to eat every day.</p> + +<p>From my understanding, the service had indexed some satirical articles and then returned the information within responses to questions and searches.</p> + +<p>As well as the obvious issues with AI returning incorrect or dangerous information in general, this applies to software development as a number of Developers using AI services as coding assistants or pair programming partners, or having it provide AI-powered code completion.</p> + +<p>My experience of using AI hasn't been great with it suggesting broken solutions or to use functionality that doesn't exist, so I don't use it often.</p> + +<p>If you do, make sure to double-check the results it gives you and use what it gives you as a starting point instead of just copying and pasting it into your code.</p> +
+
+ Fri, 31 May 2024 00:00:00 GMT +
+ + Ask questions + /daily/2024/05/30/ask-questions + http://localhost:8000/daily/2024/05/30/ask-questions + +
<p>As a Software Developer, it's common to hear that you need to ask questions to progress or work efficiently.</p> + +<p>You shouldn't just ask technical questions, though.</p> + +<p>Also ask questions like:</p> + +<ul> +<li>What do we want to achieve by delivering this feature?</li> +<li>How do we know if it's a success (or not)?</li> +<li>What business value does this add and who will benefit?</li> +<li>Why do this now? Why not wait a few months or not do it at all?</li> +<li>Is there an existing solution for this?</li> +<li>Can we achieve the same or a similar result in a different way?</li> +</ul> + +<p>As well as knowing how to do something, it's important to know why it needs to be done.</p> +
+
+ Thu, 30 May 2024 00:00:00 GMT +
+ + Make it easy + /daily/2024/05/29/make-it-easy + http://localhost:8000/daily/2024/05/29/make-it-easy + +
<p>What can you do to make your current task easier to achieve?</p> + +<p>Can you refactor the existing code to make it easier to change?</p> + +<p>Are there requirements that are no longer needed and can be de-scoped to make the code simpler or smaller?</p> + +<p>Is there a more minimum-viable version that you can release now and iterate on it later?</p> + +<p>Is there an existing solution, such as a library, module or plugin you can use instead of implementing it yourself?</p> + +<p>Is there a different or simpler implementation that satisfies most of the requirements that you can use?</p> + +<p>Taking an easier option means less code to maintain and a more stable application.</p> +
+
+ Wed, 29 May 2024 00:00:00 GMT +
+ + Why is everyone moving to SQLite? + /daily/2024/05/28/why-is-everyone-moving-to-sqlite + http://localhost:8000/daily/2024/05/28/why-is-everyone-moving-to-sqlite + +
<p>I've noticed a lot of Developers recently adopting SQLite for their database and I wonder why this is.</p> + +<p>Laravel changed their default database to SQLite for local development.</p> + +<p>It simplifies the development environment as there's no need for a separate database like MySQL or MariaDB but, if you'll be using one of those in production, won't that cause more issues when you migrate your local application?</p> + +<p>Drupal supports using SQLite, but, other than for <a href="http://localhost:8000/atdc">my automated testing course</a>, or when running automated tests, I've always used a MySQL or MariaDB database.</p> + +<p>Maybe this is something to keep an eye on and potentially use more for some scenarios in the future.</p> +
+
+ Tue, 28 May 2024 00:00:00 GMT +
+ + Why do you still write Sass? + /daily/2024/05/27/why-do-you-still-write-sass + http://localhost:8000/daily/2024/05/27/why-do-you-still-write-sass + +
<p>Yesterday, I asked <a href="http://localhost:8000/daily/2024/05/26/is-it-time-to-stop-writing-sass">if it's time to stop writing Sass</a>.</p> + +<p>If you still use Sass and are writing new styles with Sass, I'd like to know why.</p> + +<p>Reply to this email and let me know.</p> + +<p>I'm working on a project and migrating from the existing Sass styles to Tailwind CSS-generated utility classes, but with CSS custom properties (variables) and other Sass-like features now supported by browsers, writing vanilla CSS again is an interesting option.</p> +
+
+ Mon, 27 May 2024 00:00:00 GMT +
+ + Is it time to stop writing Sass? + /daily/2024/05/26/is-it-time-to-stop-writing-sass + http://localhost:8000/daily/2024/05/26/is-it-time-to-stop-writing-sass + +
<p>I've seen a lot of recent posts that ask questions like "Is it time to stop writing Sass?".</p> + +<p>I haven't written a Less or Sass stylesheet since I adopted utility classes and Tachyons, and then Tailwind CSS, and I moved to PostCSS.</p> + +<p>But, with recent native browser support added for some Sass features, such as <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_nesting">CSS nesting</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties">CSS custom properties</a> (variables), people are considering moving from Sass to regular CSS.</p> + +<p>Using regular CSS also makes it easier to onboard new Developers onto your project, which is particularly helpful in open-source projects, as <a href="http://localhost:8000/podcast/11-mark-conroy">Mark Conroy and I discussed</a> on the Beyond Blocks podcast.</p> +
+
+ Sun, 26 May 2024 00:00:00 GMT +
+ + Testing is a reusable skill + /daily/2024/05/25/testing-is-a-reusable-skill + http://localhost:8000/daily/2024/05/25/testing-is-a-reusable-skill + +
<p>I like skills and technologies that I can re-use.</p> + +<p>If there's a solution that's only available in one framework and another that is framework-agnostic, I'll use the agnostic one, if possible.</p> + +<p>I'd rather invest my time learning something I'll be able to re-use and get the most benefit from.</p> + +<p>Automated testing is an example of something re-usable.</p> + +<p>Regardless of the CMS, framework or language, how to write and structure automated tests is the same, and you still get the benefits from writing them and/or doing test-driven development.</p> + +<p>Whilst the syntax can be different, even in the PHP space comparing PHPUnit, Pest and Behat, the fundamentals and approaches are the same.</p> +
+
+ Sat, 25 May 2024 00:00:00 GMT +
+ + Don't put HTML in your body field + /daily/2024/05/24/dont-put-html-in-your-body-field + http://localhost:8000/daily/2024/05/24/dont-put-html-in-your-body-field + +
<p>I often see Drupal projects where people have put raw HTML code into their body or other rich-text fields.</p> + +<p>Whilst it can be useful for short-term prototyping, I don't think it should be done for content that will be reused or kept for a period of time.</p> + +<p>If you have structured HTML code in multiple nodes, you can't make changes without editing each instance.</p> + +<p>What if you need to fix a bug and have hundreds or thousands of instances?</p> + +<p>If you have inline styles, they won't be updated or affected by changes to your stylesheets, such as changing colour or font family.</p> + +<p>Instead, create first-class components that use templates that are easier to change and maintain and have a single source of truth that adheres to your design system.</p> + +<p>In Drupal, use structured data in fields with Paragraphs or Layout Builder and build the templates around them.</p> + +<p>This makes it easier to maintain and also for people to add and edit content.</p> +
+
+ Fri, 24 May 2024 00:00:00 GMT +
+ + Why I use long parameter names in scripts + /daily/2024/05/23/why-i-use-long-parameter-names-in-scripts + http://localhost:8000/daily/2024/05/23/why-i-use-long-parameter-names-in-scripts + +
<p>The other day, <a href="http://localhost:8000/daily/2024/05/21/which-commit-has-the-largest-message">I posted about a script I'd written</a> that found the longest commit message in a repository.</p> + +<p>As I couldn't find a native way to do this with Git, the script loops over each commit in the repository, calculates its length and stores the length and commit SHA in a file.</p> + +<p>The lines in the file are sorted so the longest commit is first.</p> + +<p>Whilst I commonly use short parameters, such as <code>git add -p</code> when typing commands, in scripts, I prefer to use the equivalent longer parameters, where possible.</p> + +<p>For example, in the script, I execute this command to sort the lines:</p> + +<pre><code class="bash">sort "${result_file}" --reverse --numeric-sort --output "${result_file}" +</code></pre> + +<p>This could be re-written as:</p> + +<pre><code class="bash">sort "${result_file}" -rn -o "${result_file}" +</code></pre> + +<p>Whilst the original is more verbose and longer to type, I prefer its verbosity which makes it easier for me or others to read and understand in the future.</p> +
+
+ Thu, 23 May 2024 00:00:00 GMT +
+ + `git revert` is your friend + /daily/2024/05/22/git-revert-is-your-friend + http://localhost:8000/daily/2024/05/22/git-revert-is-your-friend + +
<p>Imagine you've made a commit and want to undo it, or a particular commit is causing issues in production and you want to roll it back.</p> + +<p>Instead of having to change it back manually, <code>git revert</code> can do it for you.</p> + +<p>You specify the commit SHA you want to revert and Git will automatically try and revert that commit.</p> + +<p>It creates its own commit message which includes the original commit message and <a href="http://localhost:8000/daily/2024/05/20/referencing-other-commits-in-commit-messages">the reverted commit SHA</a>, so you can easily find or navigate to the original commit.</p> + +<p>For example:</p> + +<blockquote> + <p>Revert "Sort talks only by the event date"</p> + + <p>This reverts commit cbd1417b24a608df8b451a3ab5c9f888de41e758.</p> +</blockquote> + +<p>Next time, instead of manually reverting a commit, give <code>git revert</code> a try.</p> +
+
+ Wed, 22 May 2024 00:00:00 GMT +
+ + Which commit has the largest message? + /daily/2024/05/21/which-commit-has-the-largest-message + http://localhost:8000/daily/2024/05/21/which-commit-has-the-largest-message + +
<p><a href="http://localhost:8000/daily/2024/05/17/why-i-dont-commit-with--m">I write and advocate for others to write</a> detailed Git commit messages within the subject body to provide context and information about why the commit was needed, what other options were considered, any consequences or knock-on effects there could be, or even code snippets or pseudo code.</p> + +<p>Recently, I was curious to know which commit in a repository has the longest commit message, what code has changed in that commit, and who wrote it.</p> + +<p>I was hoping for a command like <code>git shortlog --summary --all</code> (which shows all authors to a codebase and their number of commits), but couldn't find one, so <a href="https://github.com/opdavies/git-commit-length-counter">I wrote a script to do it</a>.</p> + +<p>It's a bash script that loops over the commits in the repo, calculates the length of each message, sorts them and shows the commit with longest message.</p> + +<p>It was an interesting task and shows examples of using various UNIX commands and Linux coreutils, such as <code>find</code>, <code>cut</code>, <code>sort</code> and <code>wc</code> in combination with Git.</p> + +<p>The longest in this website's code base is <a href="https://github.com/opdavies/oliverdavies.uk-drupal/commit/cbd1417b24a608df8b451a3ab5c9f888de41e758">546 characters</a>, which is fairly small compared to some of my messages in other projects.</p> + +<p>What's the longest commit message in the repository you're working in?</p> +
+
+ Tue, 21 May 2024 00:00:00 GMT +
+ + Referencing other commits in commit messages + /daily/2024/05/20/referencing-other-commits-in-commit-messages + http://localhost:8000/daily/2024/05/20/referencing-other-commits-in-commit-messages + +
<p>Last week, I asked <a href="http://localhost:8000/daily/2024/05/15/should-you-include-issue-ids-in-your-commit-messages">whether you should include issue IDs in commit messages</a>.</p> + +<p>Another thing I like to reference in a commit message is the commit ID (or SHA) of a related commit.</p> + +<p>For example, when I run <code>git log</code> in my website repository, I see commits like this:</p> + +<pre><code class="plain">commit 0c91825c16217d0fe7eff4ea100a67550051c4a9 +Author: Oliver Davies &lt;oliver@oliverdavies.dev&gt; +Date: Sat May 11 15:32:07 2024 +0200 + + Create a cached talk counter + + Create a cached version of the talk counter service that returns a + cached result of the talk count for that day. + + This uses the Decorator design pattern to decorate the existing + `TalkCounter` service and works as they both implement the same + `TalkCounterInterface`. +</code></pre> + +<p>The sha for this commit is <code>0c91825c16217d0fe7eff4ea100a67550051c4a9</code>.</p> + +<p>If I was to make another commit that was related to this one, I can include this commit sha in my new commit message.</p> + +<p>I also don't need to include the entire thing - only enough for it to be unique (usually five or six characters).</p> + +<p>Once pushed, the commit IDs should never change, so this will be a permanent reference to the first commit.</p> + +<p>Helpfully, websites like GitHub, GitLab and Bitbucket will identify it as a commit sha and make it clickable so you can easily navigate to the referenced commit.</p> +
+
+ Mon, 20 May 2024 00:00:00 GMT +
+ + Better commit messages means better pull requests + /daily/2024/05/19/better-commit-messages-better-pull-requests + http://localhost:8000/daily/2024/05/19/better-commit-messages-better-pull-requests + +
<p>If you're working on a team or project that uses pull or merge requests, then <a href="http://localhost:8000/daily/2024/05/17/why-i-dont-commit-with--m">writing commit messages with detailed descriptions</a> using the message body and not just the subject line, will result in better pull requests.</p> + +<p>Whatever text you put in the message body will be used as the description for your pull or merge request, as well as the subject line being used for the title.</p> + +<p>So, instead of writing the message after completing the work, when you may forget important details, write it at the time in the message when writing the commit and re-use it in the PR or MR.</p> + +<p>Pull or merge requests with more information and detail are more likely to be accepted and merged.</p> + +<p>The text will also remain in the pull or merge request, even if <a href="http://localhost:8000/daily/2024/05/11/don-t-delete-my-commit-messages">someone squashes your commits and deletes the old messages</a>.</p> + +<p>The issue then is, <a href="http://localhost:8000/daily/2024/05/15/should-you-include-issue-ids-in-your-commit-messages">what if you move to a different Git hosting service</a>?</p> +
+
+ Sun, 19 May 2024 00:00:00 GMT +
+ + Should you strictly enforce the 50/72 rule? + /daily/2024/05/18/should-you-strictly-enforce-the-5072-rule + http://localhost:8000/daily/2024/05/18/should-you-strictly-enforce-the-5072-rule + +
<p><a href="http://localhost:8000/daily/2024/05/17/why-i-dont-commit-with--m">Yesterday</a>, I mentioned the 50/72 rule when writing Git commit messages.</p> + +<p>The first line in the commit message is the subject line and should be no longer than 50 characters.</p> + +<p>Any additional lines are the message body and should be wrapped at 72 characters.</p> + +<p>As I said, I have Neovim configured to format my commit messages based on these rules, although they're more like guidelines.</p> + +<p>There's no hard limit on the number of characters in the subject line or the number of characters in the body.</p> + +<p>The commit will work and not be rejected when pushing to your remote repository.</p> + +<p>There are likely post-commit <a href="http://localhost:8000/daily/2022/08/16/what-are-git-hooks-why-are-they-useful">Git hooks</a> to do this, but by default, things will work.</p> + +<p>A commit message to Drupal core today was 178 characters long, including the issue ID and contributors.</p> + +<p>When working on project teams, ideally, everyone would follow the 50/72 rule, but if they don't consistently, I don't think it's an issue.</p> + +<p>I'd rather they focused on writing a good and descriptive commit message and if it's formatted correctly, that's a bonus.</p> + +<p>Whilst I could automate checks for this, I don't think it's the best use of everyone's time and, especially for Junior Developers who already have enough to learn already, not where their focus should be.</p> +
+
+ Sat, 18 May 2024 00:00:00 GMT +
+ + Why I don't commit with -m + /daily/2024/05/17/why-i-dont-commit-with--m + http://localhost:8000/daily/2024/05/17/why-i-dont-commit-with--m + +
<p>A lot of tutorials show using <code>git commit -m</code> to create commits using an inline message.</p> + +<p>I don't do that, though.</p> + +<p>Using the <code>-m</code> option to specify an inline commit message encourages writing short one-line commit messages instead of using the subject and body effectively to document more about the change, such as why it was needed, any other approaches taken and the consequences of the change within the commit.</p> + +<p>Alternatively, people could write a longer commit message in one line and not follow the 50 character limit for the subject line.</p> + +<p>Whilst it's possible to write multi-line commits on the command-line, I use my editor (Neovim) to write my commit messages.</p> + +<p>I can write as much information as I need, I have coloured columns for the 50 character subject limit and the 72 characters for wrapping the body message and, if I write more than that, it will either wrap for me, or I can format the message for it to be compliant.</p> + +<p>These are rules that aren't also enforced in some Git tools, so I prefer knowing my setup is configured correctly.</p> + +<p>Though, whether you use <code>-m</code> or not, capture as much information in the message as you can, as future Developers on the project (including you) will thank you for it.</p> +
+
+ Fri, 17 May 2024 00:00:00 GMT +
+ + The first test is the hardest + /daily/2024/05/16/the-first-test-is-the-hardest + http://localhost:8000/daily/2024/05/16/the-first-test-is-the-hardest + +
<p>Whether you're writing tests before the implementation code or not, the first test is always the hardest to write.</p> + +<p>What will the arrange and act phases look like?</p> + +<p>What dependencies will you need?</p> + +<p>What do you need to mock or create fakes of?</p> + +<p>In a Drupal test, what other modules and configuration do you need to install?</p> + +<p>What installation profile and default theme do you need to use?</p> + +<p>What other unknown or complicated setup steps are there?</p> + +<p>Do you need to configure your environment to run the tests and get the expected output?</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>The hardest part is getting the arrange/setup phase working and getting to when the test is running your business logic.</p> + +<p>Once you've got the first test running, adding more for similar use cases will be much easier.</p> +
+
+ Thu, 16 May 2024 00:00:00 GMT +
+ + Should you include issue IDs in your commit messages? + /daily/2024/05/15/should-you-include-issue-ids-in-your-commit-messages + http://localhost:8000/daily/2024/05/15/should-you-include-issue-ids-in-your-commit-messages + +
<p>It's shown in the examples of the <a href="http://localhost:8000/daily/2023/11/24/are-conventional-commits-worth-it">conventional commits specification</a> as part of the optional footer data.</p> + +<p>But is it useful?</p> + +<p>It can be if your issue tracker is linked to your Git repository and you can click the issue ID in a commit message and see the issue.</p> + +<p>But, how often do teams change issue-tracking software or the project is passed to a different company that uses a different issue tracker?</p> + +<p>That makes the issue IDs that reference the old IDs useless as no one has access to the issues it references.</p> + +<p>I'd recommend putting as much information in the commit message itself and not relying on it being in an external source, like an issue tracker.</p> + +<p>The Git log and commit messages will remain even if a different issue tracker is used, or a different team starts working on the project, and that additional information isn't lost.</p> + +<p>I'm not against putting the issue ID in the commit message but don't do it instead of writing a descriptive commit message.</p> +
+
+ Wed, 15 May 2024 00:00:00 GMT +
+ + Free code reviews + /daily/2024/05/14/free-code-reviews + http://localhost:8000/daily/2024/05/14/free-code-reviews + +
<p>Do you want to contribute to open-source software, such as Drupal core or one of the thousands of modules or themes?</p> + +<p>Are you worried about receiving negative comments or feedback?</p> + +<p>Instead, think of it as free code reviews.</p> + +<p>Contributing to open-source is a great way to gain experience and learn from other Developers, and something that I still do often, 17 years after starting to develop software.</p> + +<p>I'm also happy to review people's open-source for free. If that would be of interest, reply and let me know.</p> +
+
+ Tue, 14 May 2024 00:00:00 GMT +
+ + DrupalCamp Ghent + /daily/2024/05/13/drupalcamp-ghent + http://localhost:8000/daily/2024/05/13/drupalcamp-ghent + +
<p>Last Friday and Saturday, I was happy to attend and present two sessions at DrupalCamp Ghent in Belgium.</p> + +<p>I was able to present talks on automated testing and Tailwind CSS, which were my 99th and 100th talks, respectively!</p> + +<p>The sessions were streamed live, or the slides and previous recordings <a href="http://localhost:8000/dcg">are on my website</a>.</p> + +<p>It was a great event and it's always nice to see and meet new people within the Drupal community, to see new places and share knowledge by giving presentations.</p> + +<p>Reply and let me know if you have any feedback on the sessions or if you'd like me to present a talk or workshop at your event.</p> +
+
+ Mon, 13 May 2024 00:00:00 GMT +
+ + Merging without merge commits + /daily/2024/05/12/merging-without-merge-commits + http://localhost:8000/daily/2024/05/12/merging-without-merge-commits + +
<p>Since I posted about <a href="http://localhost:8000/daily/2024/05/10/optimise-for-revertability">optimising for revertability</a>, I've received a few questions about how I avoid merge commits when working with Git.</p> + +<p>This is an extract from my <code>.config/git/.config</code> file:</p> + +<pre><code class="ini">[merge] + ff = "only" + +[pull] + ff = "only" + rebase = true +</code></pre> + +<p>This changes the behaviour of when I run <code>git pull</code> to always include <code>--rebase</code> by default and to only allow fast-forward merges and pulls.</p> + +<p>Only allowing fast-forward merges avoids merge commits as Git can just move the pointer for the branch to the latest commit.</p> + +<p>If I can't do a fast-forward merge, I need to rebase first to update everything and bring it up to date.</p> + +<p>Sometimes, when working in team, merge commits will still creep in sometimes and there are situations where you can only create a merge commit.</p> + +<p>In this situation, I can do <code>git merge --ff</code> to allow a merge commit temporarily, but this is the exception instead of the default.</p> + +<blockquote> + <p>Hint: there's a lot more information on the configuration and arguments if you run and read <code>man git-merge</code>.</p> +</blockquote> + +<p>When working with online tools such as GitHub and GitLab, I avoid any options like <code>Squash and merge</code> or <code>Create a merge commit</code> and will use rebase options, although I've seen where different commit IDs have been generated when merged in the UI, which is why I prefer to do merges locally.</p> + +<p>Or use <a href="http://localhost:8000/daily/2023/06/17/avoid-git-merge-hell-with-trunk-based-development">trunk-based development</a> and don't work on topic branches at all.</p> +
+
+ Sun, 12 May 2024 00:00:00 GMT +
+ + Don't delete my commit messages + /daily/2024/05/11/don-t-delete-my-commit-messages + http://localhost:8000/daily/2024/05/11/don-t-delete-my-commit-messages + +
<p>Another reason I don't like squashing commits within pull/merge request is losing detail within the commit messages.</p> + +<p>As someone who usually writes detailed commit messages that explain why the change is being made, any gotchas or alternative approaches that were tried, etc, I want that information to be retained.</p> + +<p>Previously, when working on a team and doing merge/pull requests, when merging a feature or release branch, either the tool or reviewer would delete all the messages from the squashed commits.</p> + +<p>The time I spent writing the messages was wasted, and the information was lost.</p> + +<p>I'd rather <a href="http://localhost:8000/daily/2024/05/10/optimise-for-revertability">keep the original commits intact</a> but, if you need to squash commits, please don't remove the previous messages as they could be useful in the future.</p> + +<p>People can see the changes by viewing the commits, but the information within the commit messages are valuable, too.</p> +
+
+ Sat, 11 May 2024 00:00:00 GMT +
+ + Optimise for revertability + /daily/2024/05/10/optimise-for-revertability + http://localhost:8000/daily/2024/05/10/optimise-for-revertability + +
<p>There are two things I avoid when merging changes in Git.</p> + +<p>Merge commits and squashing commits.</p> + +<p>Both make it hard to revert changes if needed once they've been merged, such as a major bug in production and you quickly need to roll back.</p> + +<p>Merge commits are difficult to revert and if a commit has been squashed into one larger commit, you can't revert it without also reverting everything else.</p> + +<p>Working with small, unsquashed commits makes it simple to revert a specific one and only that one.</p> + +<p>If I need to revert something, I want to be able to do as simply and specifically as possible.</p> + +<p>Optimise for revertability.</p> +
+
+ Fri, 10 May 2024 00:00:00 GMT +
+ + Do you have a deadline? + /daily/2024/05/09/do-you-have-a-deadline + http://localhost:8000/daily/2024/05/09/do-you-have-a-deadline + +
<p>Does your task or project have a deadline for completion?</p> + +<p>Is there a date that it must be completed by?</p> + +<p>Or, is your "deadline" an arbitrary date that can be moved?</p> + +<p>If so, you don't have a deadline.</p> + +<p>You have a goal.</p> + +<p>Understand and be clear whether you have a fixed deadline or a flexible goal.</p> +
+
+ Thu, 09 May 2024 00:00:00 GMT +
+ + Assertions aren't just for tests + /daily/2024/05/08/assertions-arent-just-for-tests + http://localhost:8000/daily/2024/05/08/assertions-arent-just-for-tests + +
<p>If you've written or seen automated tests in PHP, you'll have seen lines like this:</p> + +<pre><code class="php">self::assertTrue(FALSE); +</code></pre> + +<p>But, did you know assertions can be used outside of tests.</p> + +<p>PHP has an <code>assert()</code> function that can be used anywhere.</p> + +<p>For example, if I had this code:</p> + +<pre><code class="php">$node = Node::load(1); + +assert($node instanceof NodeInterface); +assert($node-&gt;bundle() === 'page'); +</code></pre> + +<p>I know <code>$node</code> is a node with the correct bundle type and I can continue.</p> + +<p>I've made my assumptions explicit.</p> + +<p>If <code>$node</code> is not the correct type or returns an unexpected bundle, the assertion will fail and an Exception will be thrown.</p> + +<p>I think this is better than assuming or hoping the values are as you expect, and it also makes the intent of the code much easier to see and understand.</p> + +<p>If you haven't tried <code>assert()</code> before, give it a try.</p> +
+
+ Wed, 08 May 2024 00:00:00 GMT +
+ + Drupal 7.100.2 + /daily/2024/05/07/drupal-7-100-2 + http://localhost:8000/daily/2024/05/07/drupal-7-100-2 + +
<p>In <a href="http://localhost:8000/daily/2023/04/30/will-we-see-drupal-7-100">April 2023</a>, I wondered if we'd see a 7.100 release of Drupal.</p> + +<p>I just noticed that we did.</p> + +<p>It was released on the 6th of March.</p> + +<p>From the release notes:</p> + +<blockquote> + <p>Maintenance release of the Drupal 7 series. Includes bug fixes and small API/feature improvements only (no major, non-backwards-compatible new functionality). Unusually, this release does include a new core module.</p> +</blockquote> + +<p>This is also the first Drupal release to be affected by the Year 2000 problem, a.k.a. the Y2K problem, or the Millennium Bug:</p> + +<blockquote> + <p>Note that this is the first Drupal 7 release with 3 digits. If you receive a report that your version of Drupal is over a decade out of date, see https://en.wikipedia.org/wiki/Year_2000_problem.</p> +</blockquote> + +<p>According to the usage figures on Drupal.org, there are at least 316,843 active Drupal 7 websites and, with only 243 days left until D7 is end-of-life, hopefully most of them will be upgrading to Drupal 10 (or 11) soon.</p> + +<p>If you're stuck on Drupal 7, I can help! Reply to this email and let's start a conversation.</p> +
+
+ Tue, 07 May 2024 00:00:00 GMT +
+ + Interactive staging + /daily/2024/05/06/interactive-staging + http://localhost:8000/daily/2024/05/06/interactive-staging + +
<p>A major addition to my Git workflow has been the ability to interactively add hunks of code to be committed.</p> + +<p>There's <code>git add -i</code> to interactively add, though I usually go straight to <code>git add -p</code> to use <code>patch</code>.</p> + +<p>This will ask you to confirm if you want to add each hunk to the commit (a.k.a. the staging area) or not.</p> + +<p>For example, here's the prompt I get whilst working on the post for this email:</p> + +<pre><code class="shell">diff --git a/source/_daily_emails/2024-05-06.md b/source/_daily_emails/2024-05-06.md +index 42fe48f..ef36a2b 100644 +--- a/source/_daily_emails/2024-05-06.md ++++ b/source/_daily_emails/2024-05-06.md +@@ -4,10 +4,12 @@ date: 2024-05-06 + permalink: daily/2024/05/06/interactive-staging + tags: + - software-development +- # - drupal +- # - php +- # - podcast ++ - git + cta: ~ + snippet: | + TODO + --- ++ ++A major addition to my Git workflow has been the ability to interactively add hunks of code to be committed. ++ ++There's `git add -i` to interactively add, though I usually go straight to `git add -p` to use `patch`. ++There's `git add -i` to interactively add, though I usually go straight to `git add -p` to use `patch`. +(1/1) Stage this hunk [y,n,q,a,d,s,e,?]? +</code></pre> + +<p>I can add the whole hunk, split it into smaller hunks, add all the hunks in the file or ignore this hunk and later hunks in the file.</p> + +<p>Then the process is repeated for any following hunks.</p> + +<p>This means I can add the relevant hunks to craft the commit I want and I can keep my commits small and meaningful, and easy to revert if there is an issue.</p> +
+
+ Mon, 06 May 2024 00:00:00 GMT +
+ + Making PHPStan stricter + /daily/2024/05/05/making-phpstan-stricter + http://localhost:8000/daily/2024/05/05/making-phpstan-stricter + +
<p>Continuing yesterday's thought on <a href="http://localhost:8000/daily/2024/05/04/strict-typing-in-php">strictness in PHP</a>, today I want to talk about adding more strictness to PHPStan.</p> + +<p>Adding the <a href="https://github.com/phpstan/phpstan-strict-rules">PHPStan Strict Rules extension</a> makes PHPStan stricter by adding new, more opinionated rules.</p> + +<p>For example:</p> + +<ul> +<li>Require booleans in if, elseif, ternary operator, after !, and on both sides of &amp;&amp; and ||.</li> +<li>Use the <code>$strict</code> parameter with <code>in_array</code>, <code>array_search</code>, <code>array_keys</code> and <code>base64_decode</code>.</li> +<li>Disallow empty().</li> +<li>Require calling parent constructor.</li> +</ul> + +<p>You can enable and disable rules as needed but, like setting the PHPStan level, ideally I like to enable them all by default and see how strict I go.</p> + +<p>It depends on the code being tested and the preference of the team, though I find the stricter the rules, the less bugs there are.</p> +
+
+ Sun, 05 May 2024 00:00:00 GMT +
+ + Strict typing in PHP + /daily/2024/05/04/strict-typing-in-php + http://localhost:8000/daily/2024/05/04/strict-typing-in-php + +
<p>I prefer writing and working with strictly typed code.</p> + +<p>One of the major improvements in PHP has been the option to enable strict types.</p> + +<p>For example, this code will usually not error and give the result:</p> + +<pre><code class="php">function add(int $a, int $b): void +{ + var_dump($a + $b); +} + +add(1, '1'); +</code></pre> + +<p>However, I'd prefer if it failed as I'm passing the function an integer and a string, but specifying they should both be integers.</p> + +<p>Fixing this is simple, by adding this line to the top of the file:</p> + +<pre><code class="php">declare(strict_types=1); +</code></pre> + +<p>I add this to every PHP file by default.</p> + +<p>I want my code to be as strict and predictable as possible, and to error when I want it to and make any bugs more explicit and easier to find and fix.</p> +
+
+ Sat, 04 May 2024 00:00:00 GMT +
+ + Don't add boolean arguments + /daily/2024/05/03/dont-add-boolean-arguments + http://localhost:8000/daily/2024/05/03/dont-add-boolean-arguments + +
<p>A convention I like from the Laravel framework is to avoid adding boolean arguments to methods.</p> + +<p>For example, if I have this function:</p> + +<pre><code class="php">public function getPosts() { ... } +</code></pre> + +<p>If I wanted to only get published posts, one way would be to add a boolean argument:</p> + +<pre><code class="php">public function getPosts(boolean $onlyPublished) { ... } +</code></pre> + +<p>Then, I'd need to use that within the method body to add another condition (this is referred to as control coupling, where one method affects another).</p> + +<p>The non-boolean approach would be to create a separate method with its own distinct name.</p> + +<p>For example, <code>getPosts()</code> could be named <code>getAllPosts()</code> and there could be a separate <code>getPublishedPosts()</code> method for only getting published posts:</p> + +<pre><code class="php">public function getAllPosts() { ... } + +public function getPublishedPosts() { ... } +</code></pre> + +<p>Whilst we have two methods now instead of one, it's much clearer what each does and there aren't any random <code>true</code> or <code>false</code>s wherever the method is used.</p> +
+
+ Fri, 03 May 2024 00:00:00 GMT +
+ + Re-evaluating old tools + /daily/2024/05/02/re-evaluating-old-tools + http://localhost:8000/daily/2024/05/02/re-evaluating-old-tools + +
<p>It's been a while since I used Ansible.</p> + +<p>I used it for automating infrastructure and deploying applications.</p> + +<p><a href="http://localhost:8000/presentations/deploying-php-ansible-ansistrano">I gave a talk about it</a> on several occasions, including remotely for the Ansible London meetup during COVID.</p> + +<p>I haven't had to use it recently but, today, I <a href="https://github.com/opdavies/oliverdavies.uk/commit/cd6575c6fcc091a0b7c98b6985b3a92b85e279e3">brought it back</a> out of my toolkit.</p> + +<p>I was reviewing how I manage my website's configuration and files and Ansible seemed to be a perfect choice.</p> + +<p>The files need to be uploaded to the server and, to handle redirects, I also have a custom NGINX virtual host configuration that needs to be uploaded if I change it and the NGINX service reloaded.</p> + +<p>Ansible is a great tool for this type of task and, just because it's a tool I haven't used it for a while, that doesn't mean I can't re-evaluate it and see if fits my current requirements.</p> + +<p>It's still in my toolkit, just in case I need it.</p> +
+
+ Thu, 02 May 2024 00:00:00 GMT +
+ + Broken pipeline? Fix or revert it. + /daily/2024/05/01/broken-pipeline-fix-or-revert-it + http://localhost:8000/daily/2024/05/01/broken-pipeline-fix-or-revert-it + +
<p>If you're doing trunk-based development where multiple people are committing and pushing work to the same branch, what do you do if you've pushed a commit that fails the checks and breaks the pipeline?</p> + +<p>This is a bad state and needs to be solved as soon as possible as it's causing a problem for everyone else.</p> + +<p>If the pipeline is failing, someone else could push a change and have it fail for a different reason, but wouldn't know.</p> + +<p>The responsibility is on the person who pushed the failing commit to resolve it and get the pipeline passing again as soon as possible.</p> + +<p>You break it, you bought it.</p> + +<p>Hopefully, the changes in the failing commit are small and it's something you can resolve quickly and push a fixed commit.</p> + +<p>If you can't fix it within a few minutes, revert the failing commit and try again.</p> + +<p>What if someone's pushed a failing commit and hasn't fixed it in a timely manner?</p> + +<p>Revert it for them, fix the pipeline and unblock the rest of the team.</p> +
+
+ Wed, 01 May 2024 00:00:00 GMT +
+ + Stepping back into debugging + /daily/2024/04/30/stepping-back-into-debugging + http://localhost:8000/daily/2024/04/30/stepping-back-into-debugging + +
<p>In PHP, we have functions like <code>var_dump()</code>, <code>dump()</code> and <code>dd()</code> that are used to debug code and print output to the screen.</p> + +<p>In Drupal, we have functions like <code>dpm()</code> and <code>kint()</code>, too.</p> + +<p>These functions are great for simple debugging but sometimes I need more, which is when I reach for a step debugger - namely, Xdebug.</p> + +<p>This is common when working in complex legacy code, where you need to be able to see a breakpoint and step through the code to see what path it takes and what the state is at each step.</p> + +<h2 id="enter-xdebug">Enter Xdebug</h2> + +<p>Xdebug is a tool I use fairly often and something I have configured on an individual project basis.</p> + +<p>This week, I spent some time adding it to a new project and ensured my notes and documentation still worked.</p> + +<p>I use Docker and Docker Compose on Linux, so there are slight changes compared to running PHP natively, so I wanted to make sure it still works.</p> + +<p>I've added my latest setup to my <a href="https://github.com/opdavies/docker-example-drupal">Drupal Docker Example repository</a> and plan to add it to my standard <a href="http://localhost:8000/build-configs">Build Configs</a> setup for Drupal projects.</p> + +<p>If you use Docker Compose on Linux, it may be useful for you.</p> + +<p>If you haven't tried Xdebug before, I suggest giving it a try and see if improves your debugging.</p> +
+
+ Tue, 30 Apr 2024 00:00:00 GMT +
+ + Some kind words + /daily/2024/04/29/some-kind-words + http://localhost:8000/daily/2024/04/29/some-kind-words + +
<p>I recently received these kind words from Frank Landry, who recently completed my <a href="http://localhost:8000/atdc">Drupal automated testing email course</a>:</p> + +<blockquote> + <p>The course was very informative.</p> + + <p>One of the biggest pain points with Drupal testing was that there was no clear and definitive guide on setting up the php unit XML file to get functional and kernel tests working right away.</p> + + <p>Your guide was fantastic and I will definitely be using it going forward in my module development for work.</p> +</blockquote> + +<p>Thanks, Frank, for your feedback.</p> + +<p>It's great to get feedback on my content and to know it's helping and providing value to people.</p> +
+
+ Mon, 29 Apr 2024 00:00:00 GMT +
+ + Replicating a bug with a test + /daily/2024/04/28/replicating-a-bug-with-a-test + http://localhost:8000/daily/2024/04/28/replicating-a-bug-with-a-test + +
<p>When fixing a bug, can you write a new failing test to replicate the bug?</p> + +<p>If so, you verify the bug exists, you know when it's fixed (the test passes), and you know you won't introduce the same bug again - otherwise, the new test will fail.</p> +
+
+ Sun, 28 Apr 2024 00:00:00 GMT +
+ + Can you make a test fail? + /daily/2024/04/27/can-you-make-a-test-fail + http://localhost:8000/daily/2024/04/27/can-you-make-a-test-fail + +
<p>Can you make an automated test fail when you want it to?</p> + +<p>If not, how do you know it's testing the correct thing?</p> +
+
+ Sat, 27 Apr 2024 00:00:00 GMT +
+ + Don't cherry-pick features from a branch to deploy + /daily/2024/04/26/don-t-cherry-pick-features-from-a-branch-to-deploy + http://localhost:8000/daily/2024/04/26/don-t-cherry-pick-features-from-a-branch-to-deploy + +
<p>I previously worked on a project where, after a code change had been reviewed and merged, it was pushed to a UAT environment for the client to test.</p> + +<p>This usually resulted in a group of changes pushed to the UAT environment, waiting for the client to test them.</p> + +<p>They would, and then decide which changes they wanted to be moved to production.</p> + +<p>Maybe changes 1, 2 and 4 would be asked to be deployed, but not 3 or 5.</p> + +<p>Someone would then cherry pick the relevant commits onto the mainline branch and deploy them to production.</p> + +<p>But, if the code isn't the same as on that UAT environment, how do you know it still works?</p> + +<p>Could a commit have been missed or could not including a non-selected commit have caused a regression or unintended side effects?</p> + +<p><code>git cherry-pick</code> isn't a command I use often, and definitely not in this scenario.</p> + +<p>If you want to select which changes go live, feature flags are a better option as you don't need to change the commits or code you're pushing.</p> + +<p>You push all the commits from UAT to production and enable the feature flags for the things you want to release.</p> +
+
+ Fri, 26 Apr 2024 00:00:00 GMT +
+ + If everyone branches, no-one gets the updates + /daily/2024/04/25/if-everyone-branches-no-one-gets-updates + http://localhost:8000/daily/2024/04/25/if-everyone-branches-no-one-gets-updates + +
<p>A common response I get when debating topic branches is "I regularly pull <code>develop</code> or <code>main</code> and get the latest changes.</p> + +<p>But if everyone is working on individual topic branches instead of the mainline branch, where do the changes come from?</p> +
+
+ Thu, 25 Apr 2024 00:00:00 GMT +
+ + Testing topic branches in isolation + /daily/2024/04/26/testing-topic-branches-in-isolation + http://localhost:8000/daily/2024/04/26/testing-topic-branches-in-isolation + +
<p>I was recently asked about setting up different testing environments based on topic (a.k.a. feature) branches.</p> + +<p>When a feature or bug fix was finished, it would create a new environment for it to be tested.</p> + +<p>If it passed testing, the topic branch was merged and it would be included in the next release.</p> + +<p>But, there's no guarantee it still works once it's been merged.</p> + +<p>It could conflict with changes from a different topic branch and no longer work - even if it worked when it was tested in isolation on its own branch.</p> + +<p>To ensure it still works, it will need to be tested again.</p> + +<p>So, what is the benefit of testing it in isolation if it needs to be tested again once it's merged?</p> + +<p>This is why I prefer continuous integration and delivery (CI/CD), as I always know all changes work together and not just in isolation.</p> +
+
+ Wed, 24 Apr 2024 00:00:00 GMT +
+ + Why use a static site generator + /daily/2024/04/23/why-use-a-static-site-generator + http://localhost:8000/daily/2024/04/23/why-use-a-static-site-generator + +
<p><a href="http://localhost:8000/daily/2024/04/22/building-websites-with-php-and-sculpin">In February</a>, I spoke at the PHP South West meetup about Sculpin - a static site generator written in PHP.</p> + +<p>Sculpin uses Twig, HTML and Markdown to generate static HTML files that you can upload to any web server.</p> + +<p>You don't need PHP or a database server - making it simpler and cheaper to host compared to a CMS-powered site.</p> + +<p>There's also no database for people to hack into and, as they're just static HTML pages, they are very quick to load.</p> + +<p>The downside is that files need to be created and edited locally or editing Git files on GitHub, etc, as there's no CMS to log into.</p> + +<p>Still, for some projects, static site generators are a great option.</p> + +<p>For Drupal, there's Tome - a module that creates a static website from a Drupal installation, and something I plan to investigate.</p> +
+
+ Tue, 23 Apr 2024 00:00:00 GMT +
+ + Building websites with PHP and Sculpin + /daily/2024/04/22/building-websites-with-php-and-sculpin + http://localhost:8000/daily/2024/04/22/building-websites-with-php-and-sculpin + +
<p>The video of the most recent version of my <a href="http://localhost:8000/presentations/building-static-websites-sculpin">Building static websites with PHP and Sculpin</a> from February's PHP South West meetup <a href="https://youtu.be/axy6ltc9meA?si=FtR4DZ5BVi_Se60J">has been released</a>.</p> + +<p>In the talk, I explain what Sculpin is and how it works before a live demo where I rebuild part of the PHPSW website (which I worked on originally).</p> + +<p>If you have any feedback or questions, reply to this email and let me know.</p> +
+
+ Mon, 22 Apr 2024 00:00:00 GMT +
+ + Almost at 100 talks and workshops + /daily/2024/04/21/almost-at-100-talks-and-workshops + http://localhost:8000/daily/2024/04/21/almost-at-100-talks-and-workshops + +
<p><a href="http://localhost:8000/talks">My talks page</a> on my website says I've presented 98 talks and workshops at different events since September 2012.</p> + +<p>Next month, I'm giving two talks at DrupalCamp Ghent.</p> + +<p>One on <a href="http://localhost:8000/talks/tdd-test-driven-drupal">automated testing and test-driven development</a> and another on <a href="http://localhost:8000/talks/taking-flight-with-tailwind-css">Tailwind CSS</a>.</p> + +<p>This means that these will be my 99th and 100th presentations!</p> + +<p>I enjoy speaking at events such as meetups and conferences, and I hope people have learned from them as well.</p> +
+
+ Sun, 21 Apr 2024 00:00:00 GMT +
+ + Speaking at DrupalCamp Belgium + /daily/2024/04/20/speaking-at-drupalcamp-belgium + http://localhost:8000/daily/2024/04/20/speaking-at-drupalcamp-belgium + +
<p>I'm happy to be speaking at DrupalCamp Belgium next month, on the 10th and 11th of May in Ghent.</p> + +<p>I'll be presenting two sessions on <a href="https://www.drupalcamp.be/en/drupalcamp-ghent-2024/schedule/friday">automated testing in Drupal</a> (Friday) and <a href="https://www.drupalcamp.be/en/drupalcamp-ghent-2024/schedule/saturday">Tailwind CSS</a> (Saturday).</p> + +<p>I spoke about testing most recently at DrupalCon Lille, and I'm looking forward to updating my Tailwind talk again to include the latest features within the framework.</p> + +<p><a href="https://www.drupalcamp.be/en/drupalcamp-ghent-2024/tickets">Tickets are still available</a> and it would be great to see you there.</p> +
+
+ Sat, 20 Apr 2024 00:00:00 GMT +
+ + When should you tag 1.0? + /daily/2024/04/19/when-should-you-tag-1-0 + http://localhost:8000/daily/2024/04/19/when-should-you-tag-1-0 + +
<p>Something I've seen, both with contributed Drupal modules and other open-source projects, over the past few years is they spend a lot of time in the 0.x versions or releasing alpha and beta versions rather than releasing a 1.0 or stable version.</p> + +<p>I presume it's a concern around backward compatibility and maintaining that once a stable version is released.</p> + +<p>But, if you want people to use your module or upgrade it to the latest version, that's much easier to do once there's a stable version.</p> + +<p>Some organisations prohibit using alpha or unstable versions of projects so, if there isn't a stable version, they wouldn't be able to use it.</p> + +<p>Personally, if I'm using one of my open-source modules, plugins or libraries in production, there should be a stable 1.0 version tagged.</p> + +<p>Once it's in production, I'm already making an implied commitment that it's going to be stable and I won't break everything in the next release, so why not make that explicit and tag a stable release?</p> + +<p>Version numbers are free and nothing is stopping you from deprecating code and releasing a new major version with breaking changes in the future, so go ahead and tag that stable version.</p> +
+
+ Fri, 19 Apr 2024 00:00:00 GMT +
+ + I made my first commit to LocalGov Drupal Microsites + /daily/2024/04/18/first-commit-localgov-drupal-microsites + http://localhost:8000/daily/2024/04/18/first-commit-localgov-drupal-microsites + +
<p>This week, I submitted <a href="https://github.com/localgovdrupal/localgov_microsites_project/pull/43">my first change</a> to the LocalGov Microsites project.</p> + +<p>It was a small change - correcting a typo that I spotted within the README file.</p> + +<p>It's OK to start small, especially when first working on a new codebase.</p> + +<p>It was a good introduction to the project and the first of what I hope to be numerous contributions to the Microsites distribution and LocalGov Drupal in general.</p> +
+
+ Thu, 18 Apr 2024 00:00:00 GMT +
+ + Regular releases encourage contribution + /daily/2024/04/17/regular-releases-encourage-contribution + http://localhost:8000/daily/2024/04/17/regular-releases-encourage-contribution + +
<p>As well as writing and maintaining my open-source software projects, I regularly contribute to other people's - including PHP libraries, Drupal modules and Tailwind CSS plugins.</p> + +<p>I'm more likely to contribute to your project if you release new versions regularly.</p> + +<p>Why would I if my changes may sit on a development branch for a long time or not be released at all?</p> + +<p>If I can see that my changes are likely to be released soon, I'm more likely to spend the time and effort to contribute instead of looking for a different solution.</p> +
+
+ Wed, 17 Apr 2024 00:00:00 GMT +
+ + Regularly releasing open-source software + /daily/2024/04/16/regularly-releasing-open-source-software + http://localhost:8000/daily/2024/04/16/regularly-releasing-open-source-software + +
<p>As with client applications, I recommend releasing new versions of open-source software as often as possible.</p> + +<p>This makes it easier for users to update to newer versions as they can easily see the changes, and less risky as it's easier to roll back or diagnose the problem if there is an issue.</p> + +<p>If you've added a feature or fixed a bug, fewer people will likely use it if it's not within a released version of your project and sat on a development branch or waiting for a tagged release. This means they're not benefiting from the changes and getting value from them.</p> + +<p>You can add feature flags to hide work-in-progress features that you don't want people to use yet, but you don't want to block yourself from releasing a new version until it's complete.</p> + +<p>Regular releases encourage smaller and simpler changes rather than larger and more complex - and potentially breaking - changes.</p> + +<p>I like to have a release day each month to release new versions of my open-source projects, which works well for me.</p> + +<p>I have a recurring task on my To Do list to review my projects and tag and release any new versions that are needed.</p> +
+
+ Tue, 16 Apr 2024 00:00:00 GMT +
+ + A note to open-source software maintainers + /daily/2024/04/15/a-note-to-open-source-software-maintainers + http://localhost:8000/daily/2024/04/15/a-note-to-open-source-software-maintainers + +
<p>I recently updated a website to the latest version of Drupal.</p> + +<p>Doing so introduced a bug in a contributed module I was using.</p> + +<p>A fix was committed to the 2.x branch of the module, which is still in an unstable alpha phase, and the issue was closed.</p> + +<p>There was no fix for the stable 1.x branch.</p> + +<p>There was no fix backported to the 1.x branch.</p> + +<p>There are breaking changes between the 1.x and 2.x branches that require me to update custom code that uses the module, which I don't want to do yet, and I don't want to update to an unstable version.</p> + +<p>If you maintain open-source software, don't force people to update too quickly.</p> + +<p>If you can, make it easy for people to update to the next major version by keeping breaking changes to a minimum and providing time and clear instructions for doing so.</p> + +<p>If these are too difficult, it could discourage or prevent people from using your software.</p> +
+
+ Mon, 15 Apr 2024 00:00:00 GMT +
+ + What about updating custom modules and themes? + /daily/2024/04/14/what-about-updating-custom-modules-and-themes + http://localhost:8000/daily/2024/04/14/what-about-updating-custom-modules-and-themes + +
<p><a href="http://localhost:8000/daily/2024/04/12/drupal-rector-and-the-project-update-bot">Yesterday's email</a> was about using Drupal Rector and the Automated Project Update bot to update contributed modules.</p> + +<p>But what about custom modules within your application?</p> + +<p>To do this, I use the <code>drupal-check</code> tool, which is built on PHPStan, and the Upgrade Status module.</p> + +<p>They scan your custom modules and themes and report any deprecated code within your custom projects - i.e. code that will be removed in a future major version - and tell you what new code to use instead.</p> + +<p>Once you've removed any deprecations, your module or theme will be ready for the next major version of Drupal.</p> + +<p>This is the approach I've used to upgrade numerous websites between major modern versions of Drupal, making small updates to existing code instead of having to rewrite it from scratch.</p> +
+
+ Sun, 14 Apr 2024 00:00:00 GMT +
+ + Rector is not just for Drupal + /daily/2024/04/13/rector-is-not-just-for-drupal + http://localhost:8000/daily/2024/04/13/rector-is-not-just-for-drupal + +
<p>I like framework-agnostic tools.</p> + +<p>I like to reuse knowledge and tools across projects, whether I'm working with Drupal, Symfony, Laravel or Sculpin.</p> + +<p>Rector is one of those tools.</p> + +<p><a href="http://localhost:8000/daily/2024/04/12/drupal-rector-and-the-project-update-bot">Yesterday</a> I said I use it to create automatic updates to my Drupal module code, but it can be used for other PHP projects, too.</p> + +<p>If you're upgrading a PHP library and want to use promoted constructor properties, for example, Rector can do that for you - and a lot more.</p> + +<p>You define which rules or presets you want to use, run Rector on the code, and it will make those changes.</p> + +<p>Having Rector do this work leaves me free to stay focused on other tasks.</p> +
+
+ Sat, 13 Apr 2024 00:00:00 GMT +
+ + Drupal Rector and the Project Update Bot + /daily/2024/04/12/drupal-rector-and-the-project-update-bot + http://localhost:8000/daily/2024/04/12/drupal-rector-and-the-project-update-bot + +
<p><a href="http://localhost:8000/daily/2024/04/10/resurrecting-the-speakerdeck-field-module">In Wednesday's email</a>, I said I was resurrecting the Speakerdeck Field module, and the same version works on Drupal 8, 9, 10 and 11.</p> + +<p>How do I know this?</p> + +<p>One of my favourite PHP libraries is Rector - a tool for upgrading code to the newest versions of PHP or, in this case, Drupal using Drupal Rector.</p> + +<p>It runs automatically on modules, including Speakerdeck Field, via the Project Upgrade Bot and generates a set of updates to apply.</p> + +<p>CI pipelines with GitLab CI and a reliable test suite make it much easier to upgrade modules to the latest Drupal version and ensure they still work.</p> +
+
+ Fri, 12 Apr 2024 00:00:00 GMT +
+ + Over 100 ATDC subscribers + /daily/2024/04/11/over-100-atdc-subscribers + http://localhost:8000/daily/2024/04/11/over-100-atdc-subscribers + +
<p>Since launching my Automated Testing in Drupal email course, over 100 people have subscribed and received the free ten daily lessons where I explain how to start from scratch to build a Drupal module with automated tests and test-driven development.</p> + +<p>Thanks to everyone who has taken the course so far and those who have provided feedback.</p> + +<p>Automated testing and test-driven development were game changers for me and enabled me to deliver better projects.</p> + +<p>If you'd like to take the course and learn how to do automated testing in Drupal, you can <a href="http://localhost:8000/atdc">register for free</a> and get them direct to your inbox.</p> +
+
+ Thu, 11 Apr 2024 00:00:00 GMT +
+ + Resurrecting the Speakerdeck Field module + /daily/2024/04/10/resurrecting-the-speakerdeck-field-module + http://localhost:8000/daily/2024/04/10/resurrecting-the-speakerdeck-field-module + +
<p>This week, I've resurrected the <a href="https://www.drupal.org/project/speakerdeck_field">Speakerdeck field Drupal module</a>.</p> + +<p>It's a module I wrote in 2017 for the Drupal 8 version of my website to embed Speakerdeck slides on my talk pages.</p> + +<p>My website now runs on Drupal 10, but the great thing is that the same code that worked for Drupal 8 also works for Drupal 9 and 10.</p> + +<p>There was no rewrite for each major version, and as I'm not using any deprecated code, the same code works for all the modern versions mentioned, and it looks like it will for Drupal 11, too.</p> +
+
+ Wed, 10 Apr 2024 00:00:00 GMT +
+ + Paying it forward + /daily/2024/04/09/paying-it-forward + http://localhost:8000/daily/2024/04/09/paying-it-forward + +
<p>As well as building applications with PHP and Drupal, I spend a lot of time helping others and "paying it forward".</p> + +<p>I write and contribute to open-source software and offer free online pair programming sessions to work on open-source projects.</p> + +<p>I speak at conferences and meetups.</p> + +<p>I create content, including videos and coding live streams.</p> + +<p>I mentor at in-person events, such as DrupalCon, and for bootcamps, such as School of Code.</p> + +<p>I've organised events, such as PHP South Wales and DrupalCamp Bristol, and reviewed session submissions for DrupalCon.</p> + +<p>But I wouldn't have been able to do this without others doing the same when I was learning and getting into software development.</p> + +<p>In July 2008, when I was evaluating technologies, I posted a question on a forum (I was learning HTML, PHP and MySQL then).</p> + +<p>Someone replied and recommended using Drupal, which is how I learned about the project.</p> + +<p>If they hadn't done that, I may not be in the position I am now.</p> + +<p>I might not have found Drupal, contributed to it, worked for the Drupal Association, or spoken at DrupalCon.</p> + +<p>Now, it's my turn to pay it forward.</p> +
+
+ Tue, 09 Apr 2024 00:00:00 GMT +
+ + Come for the software, stay for the community + /daily/2024/04/08/come-for-the-software--stay-for-the-community + http://localhost:8000/daily/2024/04/08/come-for-the-software--stay-for-the-community + +
<p>One of my favourite Drupal phrases is "Come for the software, stay for the community".</p> + +<p>I started using Drupal so I could build a website for the Tae Kwon-Do school I was training at, and then started to get involved with the community online and at events, such as local meetups and conferences, such as DrupalCamps and DrupalCon.</p> + +<p>I started to contribute code to Drupal core and contrib projects and was a contribution mentor at my first DrupalCon in Prague in 2017 as well as others since, helping first time Drupal contributors.</p> + +<p>I've got jobs and projects from my involvement with the community, including working for the Drupal Association itself.</p> + +<p>I've met people and travelled to places I otherwise wouldn't have because of the Drupal community, as well as the wider PHP, open source and software development communities,</p> + +<p>I came to Drupal for the code, and stayed for the code and the community.</p> +
+
+ Mon, 08 Apr 2024 00:00:00 GMT +
+ + Avoiding nesting + /daily/2024/04/07/avoiding-nesting + http://localhost:8000/daily/2024/04/07/avoiding-nesting + +
<p>One of my goals when coding is to reduce the amount of nesting in the code I write.</p> + +<p>I mean both in my PHP code where if conditions and foreach loops can be nested within each other, and CSS and Sass files, which support nesting CSS rules.</p> + +<p>My aim is to have a maximum of two or three levels of indentation, though sometimes this isn't possible.</p> + +<p>Doing so where I can, though, makes my code easier to read and understand and encourages other clean code approaches, such as having small and well-named functions.</p> + +<p>In CSS or Sass, avoiding nesting makes it easier to find a rule I'm looking for instead of having to find how rules have been nested or names have been concatenated - making it hard to search or grep for a string.</p> + +<p>This approach is part of "object callisthenics", which was introduced by Jeff Bay and includes other approaches that I like to follow, such as not using the <code>else</code> keyword and other good practices that I like to try and implement when possible.--</p> +
+
+ Sun, 07 Apr 2024 00:00:00 GMT +
+ + Drupal is a content management framework + /daily/2024/04/06/drupal-is-a-content-management-framework + http://localhost:8000/daily/2024/04/06/drupal-is-a-content-management-framework + +
<p>There used to be a saying:</p> + +<p>If you want to build a website, use <enter CMS name here>. If you want to build <CMS name>, use Drupal.</p> + +<p>I think I've heard it used with all the other content management systems, and it isn't about saying one is better.</p> + +<p>It highlights that Drupal isn't just a content management system - it's a content management framework.</p> + +<p>A framework you use to build your content management system to meet your needs.</p> + +<p>Straight away after installing Drupal, you can create as many content types as you want with as many fields as you want to build as simple or complex a content model as you need.</p> + +<p>You aren't restricted to the default page and article content types or a fixed set of fields.</p> + +<p>That, along with Views - a built-in query builder to build pages and blocks of your content, user login and registration, and JSON:API to allow other applications, such as mobile apps, to access your data, to name a few things, makes Drupal a very powerful option.</p> +
+
+ Sat, 06 Apr 2024 00:00:00 GMT +
+ + One Drupal fits all + /daily/2024/04/05/one-drupal-fits-all + http://localhost:8000/daily/2024/04/05/one-drupal-fits-all + +
<p>Two of the main things I like about Drupal are its flexibility and scalability.</p> + +<p>It works well for basic applications, such as personal websites and blogs (like mine) and for large, complex applications such as Drupal.org itself and many others.</p> + +<p>I don't need to learn one tool for small or simple projects and another for large or complex projects.</p> + +<p>Drupal is one size fits all.</p> +
+
+ Fri, 05 Apr 2024 00:00:00 GMT +
+ + PHP attributes: coming soon to a Drupal version near you + /daily/2024/04/04/php-attributes--coming-soon-to-a-drupal-version-near-you + http://localhost:8000/daily/2024/04/04/php-attributes--coming-soon-to-a-drupal-version-near-you + +
<p>Since Drupal 10.2, the framework has started to adopt PHP attributes - a new feature since PHP 8 - as an alternative to annotations when defining plugins, such as blocks and queues.</p> + +<p>From the 10.2 release notes:</p> + +<blockquote> + <p>Drupal core has started adopting PHP attributes, a modern PHP language feature, to provide better developer experience for plugin annotations. Contributed and custom code can begin adopting this improved API for their plugins, and Block and Action plugins can all be converted to the new API.</p> +</blockquote> + +<p>It seems that what's been added so far is the first phase of converting the core's plugins, with more to follow in future versions.</p> + +<p>There are also discussions about supporting both attributes and annotations in Drupal 11.</p> + +<p>I love to see Drupal continuing to evolve and adopt new features from the PHP language and Symfony framework (such as autowiring and autoconfiguration), and I'm looking forward to using attributes in my projects.</p> +
+
+ Thu, 04 Apr 2024 00:00:00 GMT +
+ + Switching web servers using Build Configs + /daily/2024/04/03/switching-web-servers-using-build-configs + http://localhost:8000/daily/2024/04/03/switching-web-servers-using-build-configs + +
<p>Have you been in a situation where you needed to switch something in a project, like the type of database or a payment provider?</p> + +<p>Today, I decided to switch a project from NGINX to Apache.</p> + +<p>Usually, this would involve using a different base Docker image, creating new configuration files, and changing things like the root directory for my project.</p> + +<p>But, because I'd built this into <a href="http://localhost:8000/talks/building-build-configs">Build Configs</a>, I was able to change a few lines in one file, and when I re-generated the configuration files, this project was running Apache.</p> + +<p>This is an excellent example of why I built this tool: to save time and reduce duplication across my projects.</p> + +<p>For this change, it did both.</p> +
+
+ Wed, 03 Apr 2024 00:00:00 GMT +
+ + Releasing a new project one page at a time + /daily/2024/04/02/releasing-a-new-project-one-page-at-a-time + http://localhost:8000/daily/2024/04/02/releasing-a-new-project-one-page-at-a-time + +
<p>How do you release a new project?</p> + +<p>Do you build everything and release everything at once?</p> + +<p>I've used the strategy of building and releasing it a page at a time and running two versions simultaneously.</p> + +<p>The main live version stays running, and you use a tool like NGINX or Cloudflare as a gatekeeper to direct traffic to the correct application - either the current one or the new one - based on the requested page.</p> + +<p>When a page is ready, you add it to the list of pages to serve from the new application to put it live.</p> + +<p>If there's an issue, it is also easy to revert to the original page.</p> + +<p>I've used this approach with my website and for client Drupal upgrade projects, where some pages are on Drupal 7 and some on Drupal 10.</p> + +<p>It's not the right approach for every situation, but it's a useful one to have in the toolkit.</p> +
+
+ Tue, 02 Apr 2024 00:00:00 GMT +
+ + I'm attending LocalGov Drupal Camp + /daily/2024/04/01/i-m-attending-localgov-drupal-camp + http://localhost:8000/daily/2024/04/01/i-m-attending-localgov-drupal-camp + +
<p>On the 24th of April, I'll be attending a LocalGov Drupal Camp in Birmingham, UK.</p> + +<p>As someone keen to get more involved and contribute more to the project, and who's <a href="http://localhost:8000/daily/2024/03/25/newport-city-council-running-localgov-drupal">local council recently switched to LocalGov</a>, I'm looking forward to the chance to learn and contribute on the day and afterwards.</p> + +<p>If you want to attend also, there are a few remaining tickets available and you can <a href="https://www.eventbrite.co.uk/e/localgov-drupal-camp-2024-tickets-847025314517">register on Eventbrite</a>.</p> +
+
+ Mon, 01 Apr 2024 00:00:00 GMT +
+ + Making Git work the way you want + /daily/2024/03/31/making-git-work-the-way-you-want + http://localhost:8000/daily/2024/03/31/making-git-work-the-way-you-want + +
<p>Another question that followed my recent Git emails was, " I assume you use rebase over merge?"</p> + +<p>The short answer is "yes". I like to keep the history of my repositories clean and simple to read by keeping the logs linear and not full of merge commits.</p> + +<p>The longer answer is that I do merges, but only fast-forward merges, at least by default.</p> + +<p>If, when merging, Git can fast-forward my branch to the latest commit without creating a merge commit, it will do so.</p> + +<p>If not, I can then rebase my changes to make them linear and fast-forwardable. Alternatively, if the commits have already been pushed and cannot be overwritten, I can explicitly allow a non-fast-forward merge in that situation.</p> + +<p>I have Git configured to work this way as that's how I want it to work, and that configurability is something I like about Git.</p> + +<p>If you want to see how I have Git configured, my settings are in <a href="https://github.com/opdavies/dotfiles.nix/blob/462eff64f227332d58c7c3652eeaa88b692c064d/lib/shared/modules/git.nix#L95-L135">my dotfiles repository</a> (note this file is written in the Nix language as I use Nix to manage my configuration).</p> + +<p>If you're working in a team, I'd suggest having a common configuration for everyone and defined rules for how you're going to use Git (branch names, merge or rebase, etc) to avoid inconsistencies.</p> +
+
+ Sun, 31 Mar 2024 00:00:00 GMT +
+ + Leaving a trail of breadcrumbs + /daily/2024/03/30/leaving-a-trail-of-breadcrumbs + http://localhost:8000/daily/2024/03/30/leaving-a-trail-of-breadcrumbs + +
<p>A great thing about committing often is that you leave a trail of breadcrumbs for yourself, and you can easily find your way back.</p> + +<p>If you are trying to get a test to pass or fix a bug and you get a bit lost, you can see what you've changed since your last commit instead of everything since you started working on your task.</p> + +<p>If needed, you can discard your changes and reset to your last working commit to try again, similar to using the "test and commit or revert" approach.</p> + +<p>If I commit too often, I can squash them before pushing.</p> + +<p>If I don't commit often enough, I can regret not committing more regularly if I get stuck or lost.</p> + +<p>I'd rather do the former.</p> +
+
+ Sat, 30 Mar 2024 00:00:00 GMT +
+ + How I Git + /daily/2024/03/29/how-i-git + http://localhost:8000/daily/2024/03/29/how-i-git + +
<p>After <a href="http://localhost:8000/daily/2024/03/27/hotfixing-without-branches">Wednesday's email</a>, someone said, "It sounds like you and I use git very differently." So, I wanted to explain what my typical Git workflow is.</p> + +<p>I used to use Git Flow, but now, I almost never create a new branch when starting a new task.</p> + +<p>I keep my workflow as simple as possible by using trunk-based development and working on a single branch as much as I can.</p> + +<p>Before I start, I make sure any uncommitted changes are committed or reset and that the automated tests, static analysis, coding standards checks, etc., are passing so I know I'm starting from a good place.</p> + +<p>Then, I start working on the task.</p> + +<p>I like to work in small steps and make small, regular commits, but I don't always push each individual commit to the remote repository.</p> + +<p>Sometimes, I'll make a number of "work in progress" commits and squash them into one before pushing them.</p> + +<p>I want the time between making and pushing the commit to be as short as possible, and I want each commit to be deployable.</p> + +<p>If I'm doing test-driven development, I'll typically commit each time a test is passing - whether it's adding a new test or extending one.</p> + +<p>I run any tests often whilst writing code to ensure they pass, either using a watch command or a keybinding in Neovim.</p> + +<p>I won't push a commit that would cause the code to not work, a test to fail, or block any other (potentially more urgent) changes from being pushed to production.</p> + +<p>If I push a commit that breaks the CI pipeline, I'll fix it quickly, which is usually possible as the changes are small.</p> + +<p>If not, I'll revert the commit to get back to a deployable state as quickly as possible.</p> + +<p>If I'm going to add a feature flag, I'll usually know that in advance and avoid rushing to add one later if a more urgent task comes in.</p> + +<p>By keeping each commit in a working and deployable state, a change can be feature flagged and deployed but not activated until the feature flag is enabled.</p> +
+
+ Fri, 29 Mar 2024 00:00:00 GMT +
+ + Starting to sprinkle JavaScript with Simulus + /daily/2024/03/28/starting-to-sprinkle-javascript-with-simulus + http://localhost:8000/daily/2024/03/28/starting-to-sprinkle-javascript-with-simulus + +
<p>I've been watching the new "Cosmic Coding with Symfony 7" series on SymfonyCasts, and today's video was about Stimulus - a "modest JavaScript framework for the HTML you already have".</p> + +<p>As I'm comfortable with other frameworks, such as Vue.js and Alpine.js, I've usually skipped videos about Stimulus, but today, it caught my eye.</p> + +<p>I was intrigued by it and experimented with it by refactoring a component from a project I am working on.</p> + +<p>I like the organisation Stimulus provides by using JavaScript classes and controllers while keeping things simple in the HTML code.</p> + +<p>I initially did this in Fractal but then created <a href="https://github.com/opdavies/stimulus-esbuild-example">an example project</a> using Stimulus with esbuild. It's now on my GitHub profile and includes other tools such as Nix, just, and Tmuxinator.</p> + +<p>After this short evaluation, I like Stimulus and will use it on other components in this project, maybe using third-party Stimulus controllers - either directly or for inspiration.</p> + +<p>Hat tip to Ryan Weaver and SymfonyCasts for showing Stimulus and Nick Janetakis for showing me esbuild.</p> +
+
+ Thu, 28 Mar 2024 00:00:00 GMT +
+ + Hotfixing without branches + /daily/2024/03/27/hotfixing-without-branches + http://localhost:8000/daily/2024/03/27/hotfixing-without-branches + +
<p>Last month, I wrote an email explaining <a href="http://localhost:8000/daily/2024/02/28/why-i-dont-branch">why I don't create branches</a>.</p> + +<p>After sending that email, I received this question from a reader (shared with permission):</p> + +<blockquote> + <p>I'm trying to work at one feature at a time, so I usually don't need feature branches.</p> + + <p>There's one thing that's difficult for me, maybe you could tell us something about this in one of your upcoming posts:</p> + + <p>How do you deal with hot fixes?</p> + + <p>I know, there's keeping features small, commit often and early. But sometimes this isn't possible, and a feature will take e.g. 3 days of work. Now, if there's an urgent bug in production that should be fixed asap, what are you doing?</p> + + <p>Git stash probably won't help here, as there might be commits already that would deliver an incomplete feature.</p> +</blockquote> + +<h2 id="option-1%3A-worktrees">Option 1: Worktrees</h2> + +<p>Whilst I don't create branches, I do use <a href="http://localhost:8000/daily/2022/08/12/git-worktrees-docker-compose">Git worktrees</a>, which allows me to have multiple versions of the code checked out at the same time - similar to having multiple clones of a repository.</p> + +<p>Having multiple worktrees, you don't need to stash the code for your incomplete feature or worry about commits you haven't pushed yet.</p> + +<p>You can create a new worktree, fix the urgent bug and switch back to your <code>main</code> worktree when you're finished.</p> + +<p>You'll still need to update the original worktree with your new changes which may result in conflicts - the same as merging or rebasing onto a branch.</p> + +<h2 id="option-2%3A-feature-flags">Option 2: Feature Flags</h2> + +<p>My preferred approach is to use <a href="http://localhost:8000/daily/2022/12/07/separating-releases-from-deployments-with-feature-flags">feature flags</a>, a.k.a. feature toggles.</p> + +<p>My wrapping the incomplete feature in a feature flag, it can be deployed but won't be active until the flag is enabled - similar to writing a new Drupal module but not enabling it.</p> + +<p>This is a technique I use often as it works well with trunk-based development and continuous integration and delivery.</p> + +<p>When the incomplete feature is feature-flagged, you can fix the bug and deploy the hotfix without stashing or rebasing any changes, and you can leave the flag disabled. +When the feature is ready, enable the feature flag to activate it and, if you need to turn it off again (maybe that's causing the next bug), you can easily disable it without needing to revert and deploy the code again.</p> + +<p>You can just turn the feature flag back off.</p> + +<p>I hope that helps!</p> +
+
+ Wed, 27 Mar 2024 00:00:00 GMT +
+ + Let someone else do the work + /daily/2024/03/26/let-someone-else-do-the-work + http://localhost:8000/daily/2024/03/26/let-someone-else-do-the-work + +
<p>Yesterday, I was investigating a CSS issue.</p> + +<p>It's a known issue due to some legacy code in the website's CSS that I'd fixed before but now needed to extend.</p> + +<p>Instead of blindly following the same path and extending my previous fix, I decided to rethink the problem and my approach.</p> + +<p>I re-read my documentation and re-reviewed potential solutions I'd evaluated previously.</p> + +<p>I decided to take a different approach to solving the problem and found an open-source plugin that someone else had written that gave me the functionality I needed instead of writing it from scratch.</p> + +<p>I read the code and did a short spike to see if it worked with the existing configuration.</p> + +<p>After some experimentation, I got it to work and added it to the project.</p> + +<h2 id="here%27s-the-thing">Here's the Thing</h2> + +<p>The plugin I used was only 34 lines of code, but these are lines I didn't need to write or will need to maintain.</p> + +<p>If it works, why would I write it myself?</p> + +<p>I'd rather use what someone else has written and contribute if I find a bug or need a new feature.</p> + +<p>That's the benefit of open-source software, and why I use Drupal, PHP, Sculpin, Tailwind CSS, Nix, and others.</p> +
+
+ Tue, 26 Mar 2024 00:00:00 GMT +
+ + Newport City Council running LocalGov Drupal + /daily/2024/03/25/newport-city-council-running-localgov-drupal + http://localhost:8000/daily/2024/03/25/newport-city-council-running-localgov-drupal + +
<p>Last week, it was announced that Newport City Council's (my local council) website is now running on LocalGov Drupal - a distribution for council websites.</p> + +<p>This increases the total to 44 councils in the UK and Ireland.</p> + +<p>Newport is the first Welsh council to use it and the first LocalGov website to be bilingual, serving content in both English and Welsh.</p> + +<p>It's great to see Drupal adoption continue to grow in Wales and within the Welsh public sector, and LocalGov being adopted by more councils across the UK and Ireland.</p> + +<p>I think LocalGov Drupal is a great project and one <a href="https://github.com/localgovdrupal/localgov_alert_banner/pull/225">I've contributed to previously</a>, and I plan to do more in the future.</p> +
+
+ Mon, 25 Mar 2024 00:00:00 GMT +
+ + Why I don't use a GUI for Git + /daily/2024/03/24/why-i-dont-use-a-gui-for-git + http://localhost:8000/daily/2024/03/24/why-i-dont-use-a-gui-for-git + +
<p>I've been a Git user since my first full-time Developer position in 2010.</p> + +<p>I've used other version control systems, too, early in my career, but I settled on Git and haven't looked back.</p> + +<p>I've used GUI tools for Git, such as Sourcetree and GitHub Desktop, but I prefer to use Git on the command line instead of a GUI or TUI.</p> + +<p>As a Developer who uses a command-line-focused workflow and works mainly in a terminal, there is less context switching, but I want to focus on learning the tool itself rather than a wrapper around it.</p> + +<p>Some GUIs add their own terminology or functionality, making it difficult for people to debug something on the command line if they experience an issue.</p> + +<p>It's easier to solve problems if you understand the tool itself.</p> + +<p>What if I had a favourite Git GUI that became no longer supported or maintained?</p> + +<p>Would any time spent learning that GUI have been wasted?</p> + +<p>This was also a reason why I switched to using Docker and Docker Compose instead of pre-built wrappers.</p> + +<p>I want to better understand and be efficient with the underlying tool, not only someone else's implementation of it.</p> +
+
+ Sun, 24 Mar 2024 00:00:00 GMT +
+ + Write programs that do one thing and do it well + /daily/2024/03/23/write-programs-that-do-one-thing-and-do-it-well + http://localhost:8000/daily/2024/03/23/write-programs-that-do-one-thing-and-do-it-well + +
<p>Over the last few days, I've written about watchers and running commands such as automated tests when files are changed.</p> + +<p>Some tools have this built in, whilst others don't.</p> + +<p>I've used different tools to do this and recently switched to <code>entr</code>.</p> + +<p>The previous one wasn't showing me the output from running Drupal automated tests, which <code>entr</code> does.</p> + +<p>I also like that it follows the UNIX philosophy of doing one thing well and working well with other programs.</p> + +<p>For example, to run my automated tests when I change a file, I need to run <code>find web/modules/custom | entr ./run test</code>.</p> + +<p><code>entr</code> isn't concerned with how to find the list of files to watch - only what to do with them.</p> + +<p>To get the list of files, I use the <code>find</code> command and provide the files to <code>entr</code>.</p> + +<p>I also like to do this with my application code. I like to write small modules and libraries with clear boundaries and responsibilities, do their tasks well, and work well with other parts of the application.</p> +
+
+ Sat, 23 Mar 2024 00:00:00 GMT +
+ + Watching all the things + /daily/2024/03/22/watching-all-the-things + http://localhost:8000/daily/2024/03/22/watching-all-the-things + +
<p><a href="http://localhost:8000/daily/2024/03/21/git-hooks---yay-or-nay">In yesterday's email</a>, I mentioned that I use watch commands such as <code>nodemon</code>, <code>watchexec</code> and <code>entr</code> whilst developing to run commands automatically when I change code.</p> + +<p>For example, running <code>find web/modules/custom | entr ./run test</code> will re-run my test suite when any custom module changes.</p> + +<p>This works well for tests, but for other checks, such as static analysis with PHPStan or coding standards with PHPCS, I have integrations in Neovim, and I get real-time feedback as I code.</p> + +<p>If a line fails static analysis or coding standards, a diagnostic message is shown so I can fix it immediately, and I don't need to use a watcher or wait for my CI pipeline to fail.</p> +
+
+ Fri, 22 Mar 2024 00:00:00 GMT +
+ + Git hooks - yay or nay? + /daily/2024/03/21/git-hooks---yay-or-nay + http://localhost:8000/daily/2024/03/21/git-hooks---yay-or-nay + +
<p>Many people are very for or against Git hooks - scripts that run automatically on events such as pre-commit and pre-push.</p> + +<p>Commonly, they are used for running tasks such as altering a commit message or running before committing automated tests and static analysis before pushing a commit.</p> + +<p>I'm on the fence.</p> + +<p>I've used them and added support for them to Build Configs, but I don't feel strongly about them.</p> + +<p>They are awkward to set up (you need to edit the configuration for them to work) and can be easily disabled or bypassed.</p> + +<p>Some people think it's the Developer's responsibility to run the tasks before pushing changes or that they'll be run in a CI pipeline, so why would they need to be run locally?</p> + +<p>As I write many small commits and push changes regularly, I can find hooks irritating and prefer to use watchers instead with tools like <code>watchexec</code> and <code>entr</code>.</p> + +<p>There are also tools like Captain Hook that are built to manage Git hooks. Maybe, I should investigate it more.</p> + +<p>What do you think? Are you yay or nay for Git hooks?</p> +
+
+ Thu, 21 Mar 2024 00:00:00 GMT +
+ + What is legacy code? + /daily/2024/03/20/what-is-legacy-code + http://localhost:8000/daily/2024/03/20/what-is-legacy-code + +
<p>How do you define legacy code?</p> + +<p>Is it code written by previous Developers who worked on the codebase?</p> + +<p>Is it code you wrote last week or last month?</p> + +<p>Is it code for features everyone no longer uses?</p> + +<p>Is it the "old" part of the application that no one wants to work on?</p> + +<p>It is any code that's not nice to work on or difficult to change?</p> + +<p>Is it code written with different conventions to your current ones or in a different style?</p> + +<p>Is it any code that doesn't have automated tests or wasn't written with test-driven development?</p> + +<p>Is it code built with outdated tooling or frameworks (like CSS libraries) that were popular then but have since been replaced by something newer?</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>These are just some of the potential definitions I can think of.</p> + +<p>The term "legacy code" and others, such as "technical debt", often mean different things.</p> + +<p>What's your definition? Reply and let me know.</p> +
+
+ Wed, 20 Mar 2024 00:00:00 GMT +
+ + Drupal Commerce: not just for selling t-shirts and hats + /daily/2024/03/19/drupal-commerce-not-just-for-selling-t-shirts-and-hats + http://localhost:8000/daily/2024/03/19/drupal-commerce-not-just-for-selling-t-shirts-and-hats + +
<p>I recently had Ryan Szrama as a guest on the <a href="http://localhost:8000/podcast/13-ryan-szrama-centarro">Beyond Blocks podcast</a>.</p> + +<p>Ryan is the CEO of Centarro - the company behind Drupal Commerce, the eCommerce platform built on the Drupal CMS.</p> + +<p>I've used Drupal Commerce for a number of projects since it was released in 2011, as well as Ubercart before that.</p> + +<p>One of the major things I like about it is its flexibility.</p> + +<p>The Commerce Kickstart distribution is a great way to create a demo Drupal Commerce project that shows a typical eCommerce store selling everything from books to hats, furniture and inflatable flamingos.</p> + +<p>I've used Drupal Commerce for these typical scenarios but also for some non-typical ones.</p> + +<p>I created a multi-site Drupal Commerce store for a gadget insurance company, dealing with many products and product variations. I built a custom Vue.js form that created an order with the required items before passing customers to a Drupal Commerce checkout flow.</p> + +<p>I created a yearly photography competition website that photographers can enter by purchasing a product and uploading their photographs to the order. I built custom judging functionality, which allows jurors to score each entry and the site owner to see the totals and which submission won the competition.</p> + +<p>I created an events management and booking website where each event was a product with different variations based on the different prices - early bird, regular and last minute. Each event had a maximum number of places and, potentially, a waitlist.</p> + +<p>This website also included a loyalty scheme for event organisers and attendees, who received coupons after organising or attending a certain number of events.</p> + +<p>Drupal Commerce can do a lot and isn't just selling t-shirts, hats, books or furniture.</p> +
+
+ Tue, 19 Mar 2024 00:00:00 GMT +
+ + Automated Drupal 11 compatibility fixes + /daily/2024/03/18/automated-drupal-11-compatibility-fixes + http://localhost:8000/daily/2024/03/18/automated-drupal-11-compatibility-fixes + +
<p>Yesterday, I received the first "Automated Drupal 11 compatibility fixes" email from the Rector-powered Project Update Bot.</p> + +<p>It was for the <a href="https://www.drupal.org/project/feature_toggle_twig">Feature Toggle Twig module</a> that adds a <code>featureIsEnabled()</code> function to Twig to check if a feature toggle is enabled.</p> + +<p>For example:</p> + +<pre><code class="language-twig">{% if featureIsEnabled('foo') %} + {# ... #} +{% endif %} +</code></pre> + +<h2 id="what-changes-were-needed%3F">What Changes Were Needed?</h2> + +<p>The only change needed to make the module Drupal 11 compatible was updating the <code>core_version_requirement</code> to <code>^10 || ^11</code> - allowing the module to support Drupal 10 and 11 at the same time as it uses no deprecated code.</p> + +<p>That's a great thing about modern Drupal compared to legacy versions - no major changes or rewrites to support a new major version!</p> + +<h2 id="here%27s-the-thing">Here's the Thing</h2> + +<p>I thought this was a great initiative in previous versions and I'm glad to see it again for Drupal 11, and it's great that it's being done with time before the Drupal 11 release as it gives maintainers the time to update their projects so as many modules as possible will be Drupal 11-compatible when it's released.</p> + +<p>I look forward to getting more of these emails for my <a href="http://localhost:8000/daily/2024/03/09/override-node-options-40624-drupal-websites">other contributed projects on Drupal.org</a>.</p> +
+
+ Mon, 18 Mar 2024 00:00:00 GMT +
+ + Patches vs Merge Requests + /daily/2024/03/17/patches-vs-merge-requests + http://localhost:8000/daily/2024/03/17/patches-vs-merge-requests + +
<p>I'm aware of the ongoing changes to the issue queues on Drupal.org, I haven't contributed or committed as much recently, so I haven't had to use the new approaches, such as issue forks and GitLab merge requests.</p> + +<p>Yesterday, though, I was able to do that whilst submitting <a href="http://localhost:8000/daily/2024/03/16/adding-tests-to-the-content-access-by-path-module">the tests I wrote to the Content Access by Path module</a>.</p> + +<p>I've made many contributions to projects on Drupal.org, including Drupal core and Drupal.org itself, so I'm very familiar with how the issue queues and the patch workflow work.</p> + +<p>When I first started contributing, we were using CVS, and before the "Great Git Migration".</p> + +<p>I'm also familiar with the pull or merge request approach from working on open-source and team projects on GitHub, GitLab and Bitbucket.</p> + +<p>I can see how a merge request-based workflow on Drupal.org will lower the barriers for new contributors, which seemed to be the case at DrupalCon Lille.</p> + +<p>I look forward to adapting and using it more now that the patch workflow is deprecated and will soon no longer work as everything will switch to merge requests and leverage more of GitLab's features.</p> + +<p>Another step forward for Drupal.org and the Drupal project.</p> +
+
+ Sun, 17 Mar 2024 00:00:00 GMT +
+ + Adding tests to the Content Access by Path module + /daily/2024/03/16/adding-tests-to-the-content-access-by-path-module + http://localhost:8000/daily/2024/03/16/adding-tests-to-the-content-access-by-path-module + +
<p>Last month, I released the <a href="http://localhost:8000/podcast/11-mark-conroy">Beyond Blocks podcast episode</a> where I spoke with Mark Conroy.</p> + +<p>We spoke about a number of things, including the 'Content Access by Path' Drupal module he wrote, and I promised I'd write some automated tests for it as there weren't any at the time.</p> + +<p><a href="https://www.youtube.com/live/XTpliKd47Lg?si=o4-my-XHGvcM_mWS">Yesterday on my Friday live stream</a>, I installed the module and learnt how it works whilst writing some automated tests.</p> + +<p>As is common, the first test took a little while to do as I got the setup steps working and learned how the module worked. Once that was passing, adding the others was fairly straight forward.</p> + +<p>After the stream, I <a href="https://www.drupal.org/project/content_access_by_path/issues/3428680">created an issue</a> with a merge request to add the tests I wrote and enabling GitLab CI so they'll be automatically run for any other changes.</p> + +<p>Hopefully, it'll be reviewed soon, but I've done what I promised, contributed to more open-source software, learned more about a new module and, hopefully, others will have learned things from the stream too.</p> + +<p><a href="https://www.youtube.com/@opdavies">Subscribe to my YouTube channel</a> to be notified of future live streams!</p> +
+
+ Sat, 16 Mar 2024 00:00:00 GMT +
+ + Everything is a trade-off + /daily/2024/03/15/everything-is-a-trade-off + http://localhost:8000/daily/2024/03/15/everything-is-a-trade-off + +
<p>I recently added a custom <code>wrapper</code> class within a Tailwind CSS project.</p> + +<p>It combined the <code>max-w-6xl</code>, <code>mx-auto</code> and <code>px-4</code> classes using <code>@apply</code>, which I rarely use.</p> + +<p>I added it so we didn't have to add the same classes multiple times.</p> + +<p>The advantage was it removed some duplication, but people needed to switch from the Twig template to the CSS file to remember what the <code>wrapper</code> class did.</p> + +<p>This undoes some of the advantages of Tailwind CSS and utility classes - the ability to stay in one file without needing to context switching, and easily reading what classes are on an element and immediately knowing what styles are applied to it.</p> + +<p>Everything is a trade-off.</p> + +<p>You need to decide which option feels right for you.</p> +
+
+ Fri, 15 Mar 2024 00:00:00 GMT +
+ + Just say Drupal + /daily/2024/03/14/just-say-drupal + http://localhost:8000/daily/2024/03/14/just-say-drupal + +
<p>Today, I've been following a conversation on Twitter and reading a blog post about Drupal naming and removing the version number from promotional materials.</p> + +<p>In the post, the author recommends stopping using the version number and "just say Drupal" and referring to Drupal 7 and older as "Legacy Drupal".</p> + +<p>Here's a quote from the article that stood out to me:</p> + +<blockquote> + <p>This is no longer true! We have successfully eliminated the need for organizations to leave Drupal because “it’s cheaper to just rebuild it in Wordpress than upgrade from 8 to 9 or from 9 to 10.” We’ve made it easier for our clients to spend less on major upgrades, we should be proud!</p> +</blockquote> + +<p>Another suggestion was to also use the term "Modern Drupal" for anything since Drupal 8.</p> + +<p>I've been doing this or calling it "Drupal 8+" on streams and podcast episodes, but I like this approach and I'm going to standardise on this, help change the perception of Drupal and that large rebuilds are no longer needed to upgrade as they were between the older legacy versions.</p> + +<p><a href="https://ten7.com/blog/post/just-say-drupal">Read the article</a> for more information about this.</p> +
+
+ Thu, 14 Mar 2024 00:00:00 GMT +
+ + 80% of PHP? + /daily/2024/03/13/80--of-php + http://localhost:8000/daily/2024/03/13/80--of-php + +
<p>Something I see on many Developers' websites and CVs is percentages or levels of how well they know certain tools and frameworks.</p> + +<p>Things like "80% of PHP" or "Advanced in HTML and CSS".</p> + +<p>But how do you quantify that?</p> + +<p>Do people alter their percentages accordingly if a new feature is added to a language or framework?</p> + +<p>Or, instead of trying to show how much you understand, focus on what problems you can solve with those tools and how you can provide value to customers or employers instead of what tools you'd use to do it.</p> +
+
+ Wed, 13 Mar 2024 00:00:00 GMT +
+ + You should know when to remove a feature flag + /daily/2024/03/12/you-should-know-when-to-remove-a-feature-flag + http://localhost:8000/daily/2024/03/12/you-should-know-when-to-remove-a-feature-flag + +
<p><a href="http://localhost:8000/daily/2024/03/11/feature-flags-should-be-short-lived">In yesterday's email</a>, I mentioned my recent <a href="https://twitter.com/opdavies/status/1767846980250714261">"minimum viable feature flag" tweet</a> and that I think feature flags should be short-lived.</p> + +<p>But how do you know when a feature flag should be removed?</p> + +<p>My approach is to add a TODO comment above where I use a feature flag.</p> + +<p>In that comment, I added why the feature flag was added and when it can be removed.</p> + +<p>It can be something like "Remove this when x is deployed" and/or a targeted date when I'd expect to be able to remove the flag.</p> + +<p>Then, when reading through the code, anyone can see when it should be possible to remove each feature flag, and it's easy to find flags that can be removed by reviewing the TODO comments.</p> +
+
+ Tue, 12 Mar 2024 00:00:00 GMT +
+ + Feature flags should be short-lived + /daily/2024/03/11/feature-flags-should-be-short-lived + http://localhost:8000/daily/2024/03/11/feature-flags-should-be-short-lived + +
<p>Today, I <a href="https://twitter.com/opdavies/status/1767846980250714261">posted a tweet</a> with a screenshot of some code.</p> + +<p>When previously working on the <a href="http://localhost:8000/daily/2024/02/19/introducing-versa">Versa CLI tool</a>, I had a TODO comment saying "What if there are multiple languages?".</p> + +<p>Versa is a tool for standardising commands between different languages and frameworks, but some projects, like my personal website, use multiple languages.</p> + +<p>The website is powered by Sculpin, a static site generator written in PHP (so there is a composer.json file) and node to compile the front-end assets (so there is also a package.json file).</p> + +<h2 id="what-problem-am-i-trying-to-solve%3F">What Problem Am I Trying to Solve?</h2> + +<p>Depending on the language, commands like <code>versa install</code> will need to execute a different command - e.g., composer install<code>or</code>npm install` (or an equivalent node package manager).</p> + +<p>I added PHP support first, so if a composer.json file is found, the PHP command is run as the default.</p> + +<p>What I thought was for projects with multiple languages, to prompt the user to select the language when running the command if no explicit language is set.</p> + +<p>This led me to do a spike of using Symfony Console's <code>choice</code> method to see what that would look like so I could add a screenshot to the GitHub issue.</p> + +<p>Once I'd finished with the spike, rather than deleting the code, I wrapped it in an <code>if (false)</code> condition so it wouldn't be executed, but I could still re-enable it in the future.</p> + +<p>The screenshot in the tweet showed this, along with the text "Minimum viable feature flag."</p> + +<p>This is only supposed to be there for a short time until I revisit the code and implement the feature I was spiking on.</p> + +<p>If it would be a long time before I looked at it again, I'd take a different approach.</p> + +<h2 id="here%27s-the-thing">Here's the Thing</h2> + +<p>One of the main rules of using feature flags is that they should be short-lived.</p> + +<p>It's less than ideal to read through a codebase and see it scattered with feature flags that are no longer needed and were used to release a feature several months ago, but the flag hasn't been removed.</p> + +<p>A feature flag is a temporary solution for separating the deployment of code from the release of a change.</p> + +<p>Once it's been released, the flag should be removed.</p> +
+
+ Mon, 11 Mar 2024 00:00:00 GMT +
+ + Visual testing and Diffy with Yuri Gerasymov + /daily/2024/03/10/visual-testing-and-diffy-with-yuri-gerasymov + http://localhost:8000/daily/2024/03/10/visual-testing-and-diffy-with-yuri-gerasymov + +
<p>This week on the Beyond Blocks podcast, I'm joined by Yuri Gerasymov to discuss Diffy visual regression testing.</p> + +<h2 id="talking-points">Talking Points</h2> + +<ul> +<li>What is visual regression testing?</li> +<li>How do you deal with false positives?</li> +<li>Different use cases for visual regression testing.</li> +<li>Automatic updates.</li> +<li>Scheduling content.</li> +<li>Visual regression testing in CI.</li> +<li>Diffy in WordPress.</li> +<li>What's a baseline?</li> +<li>Initial setup and onboarding feedback.</li> +<li>Testing dark mode?</li> +<li>Component testing with Storybook and Fractal?</li> +<li>Testing local environments.</li> +<li>Testing as authenticated users.</li> +<li>The roadmap for Diffy.</li> +</ul> + +<h2 id="quotable-quotes">Quotable Quotes</h2> + +<ul> +<li>We help development teams to have less visual bugs in their website. We take screenshots of the pages and compare them so you can see what changed and how. (YG)</li> +<li>We built tools for you to mock the content. You provide selectors for the elements with the content of the article and we'll replace it with lorem ipsum text so it will be exactly the same across multiple environments. (YG)</li> +<li>I can still write an assertion to check the text is on the page or not, but it won't confirm it's in the correct place. (OD)</li> +<li>Having a tool checking for changes on a regular basis instead of only after a deployment would be very useful. (OD)</li> +<li>So, you could have a tool like Violinst automatically creating pull requests and Diffy checking those PRs, so the two could work together? (OD)</li> +<li>With visual testing, it's very easy to get started. (YG)</li> +<li>Visual testing is great for showing your client your work. (YG)</li> +</ul> + +<p>I learned a lot during this conversation and have added visual regression testing to my testing toolbox for working on projects.</p> + +<p><a href="http://localhost:8000/podcast/14-yuri-gerasymov-diffy">Listen to the episode</a></p> +
+
+ Sun, 10 Mar 2024 00:00:00 GMT +
+ + Override Node Options is used on 40,624 Drupal websites + /daily/2024/03/09/override-node-options-40624-drupal-websites + http://localhost:8000/daily/2024/03/09/override-node-options-40624-drupal-websites + +
<p>In my <a href="http://localhost:8000/talks/tdd-test-driven-drupal">Test-Driven Drupal talk slides</a>, I show a screenshot of a tweet from February 2012 by Tim Millwood asking if anyone wanted to maintain the Override Node Options module.</p> + +<p>I said yes and took over maintainership.</p> + +<p>In April 2012, the module was installed on 9,212 Drupal websites - just over 7,000 Drupal 6 websites and just over 2,000 Drupal 7 websites.</p> + +<p>Before DrupalCon Lille, the module was used on 38,096 websites and was 173rd in the most used modules.</p> + +<p>As of the 3rd of March, the Override Node Options module is used on 40,624 Drupal websites and is up to the 169th most used module.</p> + +<p>That's pretty cool!</p> +
+
+ Sat, 09 Mar 2024 00:00:00 GMT +
+ + Conventions over readability? + /daily/2024/03/08/conventions-over-readability + http://localhost:8000/daily/2024/03/08/conventions-over-readability + +
<p>I previously wrote about why you shouldn't use variable names like <code>$x</code> and <code>$y</code> in your code and why you should use more descriptive names.</p> + +<p>But what if there is an existing convention?</p> + +<p>For example, I use Lua to configure Neovim and noticed that it's common to use shortened variable names, such as <code>buffer</code> instead of <code>buffer_number</code> or <code>bufferNumber</code>.</p> + +<p>It's also common to use the variable <code>M</code> to declare a module. For example:</p> + +<pre><code class="language-lua">local M = {} +M.find_files = function() + // ... +end + +return M +</code></pre> + +<p>Whilst <code>Module</code> would be a more descriptive name, would deviating from the convention be more confusing for anyone reading the code?</p> + +<p>Do the benefits of following a convention outweigh the benefits of using more descriptive variable and function names?</p> + +<p>Which would be easier for newcomers to your project or team to understand and allow them to be productive sooner?</p> +
+
+ Fri, 08 Mar 2024 00:00:00 GMT +
+ + Why write software for this? + /daily/2024/03/07/why-write-software-for-this + http://localhost:8000/daily/2024/03/07/why-write-software-for-this + +
<p>Recently, when speaking with prospective clients, instead of focusing on the features they want, I like to ask questions like:</p> + +<p>What's wrong with what you have now?</p> + +<p>Why not fix what you have instead of rebuilding?</p> + +<p>What problem are you trying to solve?</p> + +<p>Why write software for this at all?</p> + +<p>Why not do it manually?</p> + +<p>Why do you need to do this now?</p> + +<p>What results would you expect to see after this project?</p> + +<p>If they can answer these types of questions and explain the business outcomes they're looking to achieve and what value I can add, those are the projects I want to work on.</p> +
+
+ Thu, 07 Mar 2024 00:00:00 GMT +
+ + Types are optional + /daily/2024/03/06/types-are-optional + http://localhost:8000/daily/2024/03/06/types-are-optional + +
<p>The main programming languages I write are PHP and JavaScript.</p> + +<p>Both offer types where, as well as declaring a parameter or property, you can define what type it is - whether it's a string, array, boolean, etc.</p> + +<p>Adding types is optional.</p> + +<p>You don't need to add types to your PHP code, and you can choose to write plain JavaScript instead of TypeScript.</p> + +<p>Some people prefer simpler or cleaner code or less "visual debt".</p> + +<p>I like the extra clarity that types add.</p> + +<p>I like to be able to read some code and immediately know what types things should be.</p> + +<p>I like the clearer errors and messages if a different type is given than was expected.</p> + +<p>Tools like PHPStan know more about my code and give better recommendations than if I don't add types.</p> + +<p>I like the better autocompletion I get when writing code that has types.</p> + +<p>I like types, but I also like the flexibility of whether or not to add them and for Developers and development teams to make their own decisions based on their preferences.</p> +
+
+ Wed, 06 Mar 2024 00:00:00 GMT +
+ + Why write framework-agnostic code + /daily/2024/03/05/why-write-framework-agnostic-code + http://localhost:8000/daily/2024/03/05/why-write-framework-agnostic-code + +
<p><a href="http://localhost:8000//daily/2024/03/04/why-you-need-layers-in-your-application-code">Yesterday</a>, I wrote about writing layers in your application code and the benefits of loosely coupled code.</p> + +<p>Something else you can do with this approach is to write framework-agnostic code.</p> + +<p>By writing your business logic in code that isn't tied to a specific framework or CMS, with a small adapter layer, you can upgrade to a newer version of the framework, such as Drupal 7 to 10, or a different framework, keep most of the code the same and only update the parts that connect the business logic and the framework.</p> + +<p>This is something that Commerce Guys (now Centarro) did when creating Drupal Commerce 2.0.</p> + +<p>The logic around addressing, tax, etc., was released in separate PHP libraries, each with its own release cycle and reusable logic.</p> + +<p>This meant the Drupal modules were much smaller, and other eCommerce systems and frameworks could use the agnostic libraries.</p> + +<p>It's something to consider when writing your next Drupal module.</p> + +<p>It's something <a href="https://github.com/opdavies/national-rail-enquiries-feed-parser">I did recently</a> and have done on client projects previously, and it can be a good approach.</p> +
+
+ Tue, 05 Mar 2024 00:00:00 GMT +
+ + Why you need layers in your application code + /daily/2024/03/04/why-you-need-layers-in-your-application-code + http://localhost:8000/daily/2024/03/04/why-you-need-layers-in-your-application-code + +
<p>You need to add an integration with a new service or supplier to your application code.</p> + +<p>You should avoid writing code that interacts directly with that service or supplier - a.k.a tightly-coupled code.</p> + +<p>What if you change to a different service or supplier in the future?</p> + +<p>What if they release a new version of their API that includes breaking changes?</p> + +<p>If your code is tightly coupled to a single implementation, you'd need to rewrite the code, and there wouldn't be a way to easily switch from the old version to the new one.</p> + +<p>By introducing an adapter layer, you make your code more loosely coupled.</p> + +<p>You can have multiple implementations, with one for each different supplier, service, or version.</p> + +<p>You can have a consistent interface and API for every implementation, making them hot-swappable and making it easy to switch between different implementations.</p> + +<p>If you change supplier or they release a new version, you write a new implementation for it and switch when you're ready.</p> + +<p>Also, as every implementation satisfies a common interface, it's easy to make a fake implementation that you can use for testing.</p> +
+
+ Mon, 04 Mar 2024 00:00:00 GMT +
+ + Centarro and Drupal Commerce with Ryan Szrama + /daily/2024/03/03/centarro-and-drupal-commerce-with-ryan-szrama + http://localhost:8000/daily/2024/03/03/centarro-and-drupal-commerce-with-ryan-szrama + +
<p>This week on Beyond Blocks, I'mj oined by Ryan Szrama, CEO of Centarro, to discuss Drupal Commerce, Commerce Kickstart, Mario Kart and the dreaded <code>cache_form</code> table.</p> + +<p><a href="http://localhost:8000/podcast/13-ryan-szrama-centarro">Listen to the episode now</a>.</p> + +<h2 id="key-points">Key points</h2> + +<ul> +<li>eCommerce, Ubercart and Drupal Commerce.</li> +<li>How Commerce Guys started.</li> +<li>How Ryan started in the eCommerce space.</li> +<li>The origins of Drupal Commerce and Commerce Guys.</li> +<li>Commerce Kickstart.</li> +</ul> + +<h2 id="quotes">Quotes</h2> + +<ul> +<li>The best code isn't the most novel or most compact, it's the most easily readable and easily extendable (RS).</li> +<li>My only resource for learning PHP was php.net (RS)</li> +<li>I'm a self-taught Developer or community-taught, I suppose (OD).</li> +<li>Porting osCommerce into Drupal, which became Ubercart (RS).</li> +<li>Do we do this in Ubercart and Drupal 6 that we know works, or this new thing that was Drupal Commerce in Drupal 7 (OD).</li> +<li>It was built in Ubercart and was in production until quite recently, considering we're in 2024 (OD).</li> +<li>Once you get thoroughly entrenched in a platform, the cost to re-platform outweighs the cost of maintaining it in-house (RS).</li> +<li>If there's 10,000 of anybody, they're going to want to pay for support (RS).</li> +<li>How do we convince them to invest in an upgrade that doesn't see them jettison Drupal entirely? (RS)</li> +<li>One thing I like about Drupal Commerce is its flexibility. (OD)</li> +<li>It doesn't need to be a t-shirt or book shop. You can do some outside the box things with it. (OD)</li> +<li>Being a native extension of Drupal has a lot more advantages and pros than cons. (RS)</li> +<li>Ubercart was more "batteries included" and "this is what it does", whereas Drupal Commerce is more flexible and although it requires a bit more setup to begin with, you can plug it together the way you want to. (OD)</li> +<li>Our vision was to go the next step of getting off the Drupal island. (RS)</li> +<li>The Commerce Addressing library is now over 17,000,000 downloads. (OD)</li> +<li>Composer allowed us to separate our projects and separate our concerns. (RS)</li> +<li>Do you know when you should not use Views and when you decouple that component and use JavaScript and the REST API? (RS)</li> +<li>If we can reduce the number of times introduce those kinds of problems, that's how I would certify somebody. (RS)</li> +</ul> +
+
+ Sun, 03 Mar 2024 00:00:00 GMT +
+ + Are your tests good enough? + /daily/2024/03/02/are-your-tests-good-enough + http://localhost:8000/daily/2024/03/02/are-your-tests-good-enough + +
<p>You've been asked to add a new feature to or refactor some existing code.</p> + +<p>Assuming you have automated tests already (if not, why not?), before you change the code, you should ask whether they're good enough.</p> + +<p>They need to be passing, but do you feel confident the existing functionality will still work after you make your changes?</p> + +<p>Do you have all the existing use cases covered?</p> + +<p>If so, carry on.</p> + +<p>If not, spend some time improving your tests first to get that confidence before making any changes.</p> +
+
+ Sat, 02 Mar 2024 00:00:00 GMT +
+ + Back to live streaming + /daily/2024/03/01/back-to-live-streaming + http://localhost:8000/daily/2024/03/01/back-to-live-streaming + +
<p>Today, after saying I wanted to get back into it, I did my first live coding stream in over two years.</p> + +<p>I spent around an hour and a half working on <a href="http://localhost:8000/build-configs">Build Configs</a> - a Symfony command line application I wrote to generate build configuration files.</p> + +<p>To learn more about it, I <a href="http://localhost:8000/presentations/building-build-configs">gave a meetup talk</a> about it recently.</p> + +<p>On stream, I explained what Build Configs is, added a <code>--dry-run</code> option that prevents any files from being created and started to look again at Behat to see how I might use it to test the app's functionality.</p> + +<p>I want to do at least one stream a week going forward. Most likely, it will be on Friday afternoons. +You can <a href="https://www.youtube.com/watch?v=Wlkcf1PLWN8">watch the full stream</a> on YouTube now. I'll split it into sections this week and upload some lightly edited versions to the same channel.</p> + +<p>I won't post a link to every video here, so please subscribe to my YouTube channel to be notified when I go live next.</p> +
+
+ Fri, 01 Mar 2024 00:00:00 GMT +
+ + Experimenting with web components + /daily/2024/02/29/experimenting-with-web-components + http://localhost:8000/daily/2024/02/29/experimenting-with-web-components + +
<p>After my Beyond Blocks episode with Mark Conroy, I've spent some time this week experimenting with web components.</p> + +<p>The team and I are building a new design system in Fractal and, as Drupal is our main application, we're using Twig as our templating language within Fractal so the changes are easier to port between systems.</p> + +<p>Web components are agnostic, so they work within different languages and frameworks.</p> + +<p>We also use other technologies, such as Vue.js, and third-party suppliers, so having an agnostic web component would reduce the time and effort needed to implement the design system across different technologies and applications.</p> + +<p>If we can make self-contained web components that include their own styling and behaviour, that also improves re-usability and reduces the maintenance overhead as there would be one canonical version for everyone to use - not just a reference implementation everyone would need to recreate.</p> + +<p>After this spike, I'm intrigued to see how we could use web components and what potential issues we can solve with them whilst reducing our implementation and maintenance efforts.</p> + +<p>Are you interested in web components, too? <a href="http://localhost:8000/podcast/11-mark-conroy">Listen to the podcast episode with Mark Conroy</a>.</p> +
+
+ Thu, 29 Feb 2024 00:00:00 GMT +
+ + Why I don't branch + /daily/2024/02/28/why-i-dont-branch + http://localhost:8000/daily/2024/02/28/why-i-dont-branch + +
<p>A few days ago, I asked <a href="http://localhost:8000/daily/2024/02/25/why-do-people-still-use-git-flow">why people are still using Git Flow in 2024</a>.</p> + +<p>I moved to trunk-based development two years ago and haven't looked back.</p> + +<h2 id="why-did-i-do-that%3F">Why did I do that?</h2> + +<p>Only having a single branch instead of separate <code>main</code> and <code>develop</code> branches and branches for each feature and fix is much simpler.</p> + +<p>I don't get merge conflicts when trying to merge branches together, as everything is on a single branch.</p> + +<p>A couple of years ago, I was working on two features for a project. I was demoing them to a client and broke my local environment when switching branches from feature A to feature B.</p> + +<p>It was embarrassing, and it took me time to resolve the issues before I could continue working.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>Working mostly on a single branch avoids merge conflicts, saves time, and simplifies my workflow.</p> + +<p>No more confusion, merge conflicts or broken demos.</p> +
+
+ Wed, 28 Feb 2024 00:00:00 GMT +
+ + Building a design system in a few hours with Symfony + /daily/2024/02/27/building-a-design-system-in-a-few-hours-with-symfony + http://localhost:8000/daily/2024/02/27/building-a-design-system-in-a-few-hours-with-symfony + +
<p><a href="http://localhost:8000/podcast/11-mark-conroy">I recently spoke with Mark Conroy</a>, Director of Development at Annertech, on the Beyond Blocks podcast. +We discussed building something useful in a day and building design systems with web components.</p> + +<p>Inspired by that conversation, I spent this afternoon experimenting with web components and seeing how I could use them within a design system we're building, as I think they can solve a particular need we have.</p> + +<p>Following that, I thought I'd see how I could build my own design system and decided to do it with Symfony and see what I could achieve within a few hours.</p> + +<p>It was a great opportunity to take Symfony 7 for a spin and explore and test some things that <a href="http://localhost:8000/podcast/10-ryan-weaver-symfonycasts">Ryan Weaver and I discussed in our episode</a>, such as Symfony UX and Twig components.</p> + +<p>Each component is a Twig component with its own PHP class and Twig template, which is rendered when I visit its URL, and I can use the HTML-like <code>&lt;twig:Logo /&gt;</code> syntax to include child components.</p> + +<p>You can <a href="https://github.com/opdavies/symfony-design-system">view the code on GitHub</a>, and it may be something I use and work on in the future.</p> +
+
+ Tue, 27 Feb 2024 00:00:00 GMT +
+ + Docker and content creation with Nick Janetakis + /daily/2024/02/26/docker-and-content-creation-with-nick-janetakis + http://localhost:8000/daily/2024/02/26/docker-and-content-creation-with-nick-janetakis + +
<p>This week's guest on the Beyond Blocks podcast is Nick Janetakis - a Software Developer, Docker Captain and Teacher who focuses on building and deploying web apps.</p> + +<p>I'm a long-time consumer of Nick's content via his blog, YouTube channel and courses, so it was great to discuss Docker, content creation and more with him.</p> + +<p><a href="http://localhost:8000/podcast/12-nick-janetakis-docker">Listen to the episode</a></p> +
+
+ Mon, 26 Feb 2024 00:00:00 GMT +
+ + Why do people still use Git Flow? + /daily/2024/02/25/why-do-people-still-use-git-flow + http://localhost:8000/daily/2024/02/25/why-do-people-still-use-git-flow + +
<p><a href="http://localhost:8000/presentations/git-flow">The first conference talk I gave</a> was at DrupalCamp London 2014.</p> + +<p>The talk was about Git Flow - a popular branching scheme I was using to manage a project, where there are different branches for development and production versions of the software and separate branches for features, releases and hotfixes.</p> + +<p>It's been a standard approach as I've worked on different projects and teams, usually with pull or merge requests and code reviews.</p> + +<p>But why do people still use it?</p> + +<p>I was conducting technical interviews recently and every candidate explained how they used Git Flow or something inspired by it, and GitHub and GitLab have their own simplified versions.</p> + +<p>Vincent Driessen wrote the original Git Flow blog post in January 2010, though he updated it in March 2020 to add this note of reflection:</p> + +<blockquote> + <p>This model was conceived in 2010, now more than 10 years ago, and not very long after Git itself came into being. In those 10 years, git-flow (the branching model laid out in this article) has become hugely popular in many a software team to the point where people have started treating it like a standard of sorts — but unfortunately also as a dogma or panacea.</p> + + <p>During those 10 years, Git itself has taken the world by a storm, and the most popular type of software that is being developed with Git is shifting more towards web apps — at least in my filter bubble. Web apps are typically continuously delivered, not rolled back, and you don't have to support multiple versions of the software running in the wild.</p> + + <p>This is not the class of software that I had in mind when I wrote the blog post 10 years ago. If your team is doing continuous delivery of software, I would suggest to adopt a much simpler workflow (like GitHub flow) instead of trying to shoehorn git-flow into your team.</p> + + <p>If, however, you are building software that is explicitly versioned, or if you need to support multiple versions of your software in the wild, then git-flow may still be as good of a fit to your team as it has been to people in the last 10 years. In that case, please read on.</p> + + <p>To conclude, always remember that panaceas don't exist. Consider your own context. Don't be hating. Decide for yourself.</p> +</blockquote> + +<p>Since adopting trunk-based development and continuous integration and delivery, I've worked faster and avoided merge conflicts, which wasn't the case before, even when I was the only one working on a codebase.</p> + +<p>It's a simpler workflow.</p> + +<p>I don't need to think about whether this branch is a feature or a hotfix, and I've rarely seen release and hotfix branches used as described in the original blog post.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>Do what works best for you and your team, but don't adopt something because it's the "standard" approach.</p> +
+
+ Sun, 25 Feb 2024 00:00:00 GMT +
+ + When should you start writing tests? + /daily/2024/02/24/when-should-you-start-writing-tests + http://localhost:8000/daily/2024/02/24/when-should-you-start-writing-tests + +
<p>When you start a new project, when should you start writing tests and doing test-driven development?</p> + +<p>Is it before you write any code, or is it something you can start later?</p> + +<p>When starting <a href="http://localhost:8000/daily/2024/02/19/introducing-versa">versa</a>, I was only scaffolding the console commands and running some basic commands such as "composer install" and "phpunit".</p> + +<p>At that point, there wasn't any business logic to test.</p> + +<p>The first versions only supported PHP, so there was no complexity around which language the project used.</p> + +<p>There was no logic to determine which language or package manager was used.</p> + +<p>Now I've started to add this functionality - such as determining the project type from its composer.json or package.json file - there is logic to test, and I've started to write tests and do test-driven development.</p> + +<p>At this phase, I can see the value, which I couldn't when setting up the application to begin with, and different have any logic worth testing.</p> +
+
+ Sat, 24 Feb 2024 00:00:00 GMT +
+ + Slow down to go fast + /daily/2024/02/23/slow-down-to-go-fast + http://localhost:8000/daily/2024/02/23/slow-down-to-go-fast + +
<p>I've recently been improving my typing.</p> + +<p><img src="http://localhost:8000/assets/images/daily-emails/typing.png" alt="A screenshot of a typing test." /></p> + +<p>My speed is usually good, but my accuracy is not where I'd like it to be (it's usually around 95%, and I'd like it to be nearer 99%).</p> + +<p>It may seem counterintuitive, but I need to slow down to get faster.</p> + +<p>But not focusing on the speed and prioritising accuracy, I'll still get faster as I'm not mistyping as many keys.</p> + +<p>It's the same when I run.</p> + +<p>My first few kilometres are slower as I get into the run, find my pace, focus on breathing, etc.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>The same can also be said for automation and automated testing.</p> + +<p>They require some time investment initially but will save time as a project evolves and becomes more complex.</p> + +<p>You need to slow down initially to go faster later.</p> +
+
+ Fri, 23 Feb 2024 00:00:00 GMT +
+ + Diffy and visual regression testing + /daily/2024/02/22/diffy-and-visual-regression-testing + http://localhost:8000/daily/2024/02/22/diffy-and-visual-regression-testing + +
<p>I recently recorded an episode of <a href="http://localhost:8000/podcast">Beyond Blocks</a> with Yuri Gerasymov, who runs Diffy.</p> + +<p>He and I discussed visual regression testing, why you'd use it, and some features Diffy offers.</p> + +<p>I usually write a combination of browser, kernel and unit tests within an application framework like Drupal and Symfony or behavioural tests with Behat.</p> + +<p>These validate that my logic works as expected and that functions return the correct results or pages return the correct response codes and content.</p> + +<p>There are types of issues, though, such as breakages within the page layout or structure, which these tests won't discover but can be identified by visual regression tests that compare before and after screenshots.</p> + +<p>I added to my website during the recording and can see what value it offers as an additional layer of testing on top of the tests I already write.</p> +
+
+ Thu, 22 Feb 2024 00:00:00 GMT +
+ + Coding defensively and considering the unhappy path + /daily/2024/02/21/coding-defensively-and-considering-the-unhappy-path + http://localhost:8000/daily/2024/02/21/coding-defensively-and-considering-the-unhappy-path + +
<p>Last week, whilst speaking about Sculpin at PHP South West, I showed an example from a client project I'd built with Sculpin.</p> + +<p>The project was for a local gym and fitness centre, and I was demonstrating how I'd created the timetable page and kept the data separate from the HTML and Twig markup so the client could easily administer it (they edit the files on GitHub, which triggers a rebuild of the website).</p> + +<p>Each class should have a name and start and end times and be on a specified day.</p> + +<p>But what if the client didn't include a name or start or end time?</p> + +<p>What if they put a number or boolean instead of a string?</p> + +<p>What if there are no classes on a particular day?</p> + +<p>You don't know how people will use your software, so it's best to be defensive - validate and verify things before rendering them and prevent the page or whole application from breaking if something isn't as you expected or assumed.</p> + +<p>We also can't assume the "happy path" will always be correct.</p> + +<p>What if there aren't any classes? Do we put an empty message on the timetable or not show that day?</p> + +<h2 id="another-scenario">Another scenario</h2> + +<p>If you were retrieving or posting data to an API endpoint, would you assume it was successful or verify the response code is what you expected?</p> + +<p>If you get the data, do you check if it's in the correct format and something you can use?</p> + +<p>If not, do you verify other actions in your application haven't run and it's not in an invalid state?</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>In PHP, a common approach is to use the <code>assert()</code> function, as we saw in <a href="http://localhost:8000/daily/2024/02/20/which-level-is-right-for-you">yesterday's email</a>.</p> + +<p>Then, write automated tests that don't test just the happy path but also the unhappy paths.</p> +
+
+ Wed, 21 Feb 2024 00:00:00 GMT +
+ + Which level is right for you? + /daily/2024/02/20/which-level-is-right-for-you + http://localhost:8000/daily/2024/02/20/which-level-is-right-for-you + +
<p>Today, whilst working on <a href="http://localhost:8000/daily/2024/02/19/introducing-versa">Versa</a>, I was experimenting with different PHPStan levels.</p> + +<p>Level 1 is the least strict level, and applies the fewest rules and returns the fewest results.</p> + +<p>As you increase the level, the stricter PHPStan is.</p> + +<h2 id="levelling-up-to-9">Levelling up to 9</h2> + +<p>Here is the code to get the values of the <code>--extra-args</code> and <code>--working-dir</code> options:</p> + +<pre><code class="language-php">$extraArgs = $input-&gt;getOption('extra-args'); +$workingDir = $input-&gt;getOption('working-dir'); +</code></pre> + +<p>This passed PHPStan level 8, but these are the errors I get when running level 9:</p> + +<pre><code class="language-plain">------ ------------------------------------------------------------------------------------------------------- + Line versa +------ ------------------------------------------------------------------------------------------------------- + 61 Parameter $extraArgs of static method App\Process\Process::create() expects string|null, mixed given. + 62 Parameter $workingDir of static method App\Process\Process::create() expects string, mixed given. + 72 Parameter $extraArgs of static method App\Process\Process::create() expects string|null, mixed given. + 73 Parameter $workingDir of static method App\Process\Process::create() expects string, mixed given. + 84 Parameter $extraArgs of static method App\Process\Process::create() expects string|null, mixed given. + 85 Parameter $workingDir of static method App\Process\Process::create() expects string, mixed given. + 94 Parameter $extraArgs of static method App\Process\Process::create() expects string|null, mixed given. + 95 Parameter $workingDir of static method App\Process\Process::create() expects string, mixed given. + 104 Parameter $extraArgs of static method App\Process\Process::create() expects string|null, mixed given. + 105 Parameter $workingDir of static method App\Process\Process::create() expects string, mixed given. + 119 Parameter $extraArgs of static method App\Process\Process::create() expects string|null, mixed given. + 120 Parameter $workingDir of static method App\Process\Process::create() expects string, mixed given. +------ ------------------------------------------------------------------------------------------------------- + +[ERROR] Found 12 errors +</code></pre> + +<p>The issue is that <code>$input-&gt;getOption()</code> from Symfony's <code>InputInterface</code> returns <code>mixed</code> - even though it always returns <code>null</code> or a string.</p> + +<p>If I did <code>--working-dir 2</code>, it would return the string <code>"2"</code>.</p> + +<p>An empty string throws an error, and an empty value (if there are no extra arguments) returns <code>null</code>.</p> + +<p>So, I know <code>$workingDir</code> will always be a string and <code>$extraArgs</code> will either a string or <code>null</code>.</p> + +<h2 id="passing-level-8">Passing level 8</h2> + +<p>To pass level 8, PHPStan needs to be sure the variables are what I think they are.</p> + +<p>Here's the code I can use to get it to pass:</p> + +<pre><code class="language-php">$extraArgs = $input-&gt;getOption('extra-args'); +$workingDir = $input-&gt;getOption('working-dir'); +assert(is_string($extraArgs) || is_null($extraArgs)); +assert(is_string($workingDir)); +</code></pre> + +<p>By using <code>assert()</code> and throwing an Exception if the condition fails, PHPStan is now happy with the code and my code passes level 9.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>Although the extra code gets PHPStan to pass, it it worth it?</p> + +<p>Is this extra code adding value? Does it make the code more readable or is it likely to prevent a bug?</p> + +<p>In this case, I know the value will always be the type I expect.</p> + +<p>I can work around this using a baseline or annotations for PHPStan to ignore these lines, <strong>or is level 8 good enough for this project</strong>?</p> + +<p>Similar to 100% test coverage, is aiming for the highest PHPStan level an objective to be met, or is enough value being added added by the lower level?</p> + +<p>Which level is right for you and this codebase?</p> +
+
+ Tue, 20 Feb 2024 00:00:00 GMT +
+ + Introducing Versa - the versatile CLI tool + /daily/2024/02/19/introducing-versa + http://localhost:8000/daily/2024/02/19/introducing-versa + +
<p>Today, I started to build a new open-source project - Versa, the versatile command-line tool that standardises common commands across projects.</p> + +<p>After watching a Twitch streamer building something similar in Rust, I decided to build my take on it and add features I'd need, such as support for local vs Docker-based commands and to run different commands for different projects.</p> + +<p>For example, <code>versa run</code> would need to run different commands for Drupal and Sculpin projects, <code>versa test</code> should support different test frameworks for the same language - e.g. PHPUnit, Pest and ParaTest for PHP - and the commands will need to be different in Docker Compose-based projects.</p> + +<p>Currently, this logic is within my <a href="http://localhost:8000/build-configs">Build Configs</a> project but can be moved to Versa.</p> + +<p>This reduces the complexity within that codebase and means I can open-source it as it's a separate project.</p> + +<p>At the moment, I've added PHP as the only supported language but I will add JavaScript/TypeScript support so it supports projects like Fractal.</p> + +<p>It's still in the prototype phase and includes some initial commands, but the interesting parts will be making it clever.</p> + +<p>Instead of running <code>versa run --type sculpin</code>, I'd like to just do <code>versa run</code> and have it determine the type of project automatically.</p> + +<p><code>versa test</code> should be able to determine the testing framework automatically based on what's in the project's <code>composer.json</code> file instead of having to specify it.</p> + +<p>Likewise, for JavaScript, <code>versa install</code> should be able to determine if <code>npm</code>, <code>yarn</code> or <code>pnpm</code> is used.</p> + +<p>That'll be where the more complex code will be added and when I start doing test-driven development - maybe with Behat, which is also something I've been thinking about for Build Configs.</p> + +<p>Interested? <a href="https://github.com/opdavies/versa">Take a look at the code</a>.</p> +
+
+ Mon, 19 Feb 2024 00:00:00 GMT +
+ + Build something useful in one day with Mark Conroy + /daily/2024/02/18/build-something-useful-in-one-day-with-mark-conroy + http://localhost:8000/daily/2024/02/18/build-something-useful-in-one-day-with-mark-conroy + +
<p>This week on the Beyond Blocks podcast, I'm joined by Mark Conroy - Director of Development at Annertech to discuss all things front-end development and building something useful in one day.</p> + +<p><a href="http://localhost:8000/podcast/11-mark-conroy">Listen now</a></p> + +<h2 id="talking-points">Talking points</h2> + +<ul> +<li>Building better websites faster, including Drupal distributions, such as LocalGov.</li> +<li>What Drupal gives you out of the box and using the right tool for the job.</li> +<li>Drupal migrations and migrating Drupal configuration from a spreadsheet.</li> +<li>Saving time and improving efficiency by standardising base builds with Docksal and Composer.</li> +<li>How to build projects, like the Running Plan Generator, in a day and training for 10Ks and marathons.</li> +<li>Prioritising tasks.</li> +<li>Annertech's development workflow and standardisation by developing on remote servers.</li> +<li>Why you may not need preprocessors and complicated front-end build tools, and reducing complexity using vanilla CSS, JavaScript and web components.</li> +<li>Lowering the barrier to entry to contribute to open-source projects.</li> +<li>Building a design system/component library with web components.</li> +<li>The Content Access by Path module that was developed for Essex County Council.</li> +</ul> +
+
+ Sun, 18 Feb 2024 00:00:00 GMT +
+ + Another way to create test module configuration + /daily/2024/02/17/another-way-to-create-test-module-configuration + http://localhost:8000/daily/2024/02/17/another-way-to-create-test-module-configuration + +
<p>In one of the lessons in my <a href="http://localhost:8000/atdc">free automated testing in Drupal email course</a>, I explain how I create configuration that I need within my tests, such as adding a custom field:</p> + +<blockquote> + <p>But how do you know what to name the configuration files and what content to put in them?</p> + + <p>Rather than trying to write them by hand, I create the configuration I need, such as fields, within a Drupal site and then export and edit the files I need.</p> +</blockquote> + +<p>As well as creating the fields in the Drupal UI, I was also using it to export the configuration files I needed:</p> + +<blockquote> + <p>Once Drupal is installed and the configuration has been created, you can go to - /admin/config/development/configuration/single/export and select the configuration type and name.</p> + + <p>The filename is shown at the bottom of the page, and you can copy the content into files within your module.</p> +</blockquote> + +<h2 id="there%27s-another-way">There's another way</h2> + +<p>After reading that lesson, somene replied and reminded me that there's a <code>--destination</code> option you can use with the <code>drush config:export</code> command.</p> + +<p>Instead of exporting to the standard configuration directory, I can do it to a temporary directory:</p> + +<pre><code class="language-shell">run drush cex --destination /app/.ignored/config +</code></pre> + +<p>Everyhing in a <code>.ignored</code> direcotry is automatically ignored by Git, and to get the files I need, I can use Linux's <code>find</code> command to find any files that contain the field name and copy them into my test module directory:</p> + +<pre><code class="language-shell">find .ignored/config \ + -type f \ + -name \*drupal_project\* \ + -exec cp -r {} web/modules/custom/foo/modules/foo_test/config/install \; +</code></pre> + +<p>I still need to edit the files to remove the <code>uuid</code> and <code>_core</code> values, but this approach means less clicking in the Drupal UI which makes me more productive.</p> + +<p>I used this approach when <a href="http://localhost:8000/daily/2024/02/16/keep-logic-within-tests-for-as-long-as-you-can">writing my SaaS code yesterday</a> and it worked well.</p> +
+
+ Sat, 17 Feb 2024 00:00:00 GMT +
+ + Keep logic within tests for as long as you can + /daily/2024/02/16/keep-logic-within-tests-for-as-long-as-you-can + http://localhost:8000/daily/2024/02/16/keep-logic-within-tests-for-as-long-as-you-can + +
<p>Inspired by some recent podcast guests, I've started writing the first code for a Drupal-based SaaS product that I've been thinking of creating.</p> + +<p>Here's an early iteration of the first test I wrote:</p> + +<pre><code class="language-php">public function test_it_creates_a_project_node_from_json(): void { + self::assertNull(Node::load(id: 1)); + + $this-&gt;installEntitySchema(entity_type_id: 'node'); + $this-&gt;installConfig(modules: self::$modules); + + $projectData = json_decode(json: self::$projectJson, associative: TRUE); + self::assertNotNull($projectData); + + Node::create([ + 'title' =&gt; $projectData['list'][0]['title'], + 'type' =&gt; 'drupal_project', + ])-&gt;save(); + + $node = Node::load(id: 1); + + self::assertNotNull($node); + self::assertInstanceOf(actual: $node, expected: NodeInterface::class); + self::assertSame(actual: $node-&gt;label(), expected: 'Override Node Options'); + + self::assertSame( + actual: $node-&gt;get('field_drupalorg_node_id')-&gt;getString(), + expected: strval(107871), + ); +} +</code></pre> + +<p>It checks that, given some defined JSON data, it will create a node in my database.</p> + +<p>It confirms no node ID exists when starting, runs some setup setups (this is a Kernel test), decodes the JSON, creates the node and asserts it contains the expected values.</p> + +<p>There are two things that you may be wondering...</p> + +<ul> +<li>Why do you have test setup code that you'll need within the test? Won't you need that for every test?</li> +<li>Why are you creating the node within the test and not somewhere else?</li> +</ul> + +<p>The answer to both is that this is the first test, and I want to write <strong>as little code as possible for it to pass</strong>.</p> + +<p>When I write the second test, I'll either need to duplicate the setup lines or extract them to a <code>setUp()</code> method.</p> + +<p>I'll also need to refactor the code that creates the node.</p> + +<p>Once I've written the second test, to get it to pass, I refactored to use Repository, Builder and Action classes.</p> + +<p>If there's a regression, the test I had will fail, and I could revert to the passing version before reattempting the refactor.</p> + +<p>With test-driven development, I want to work in small and simple steps and get to green by making the smallest and easiest possible change.</p> + +<p>When I have a test that forces me to refactor and adopt a more complex approach, I'll do it.</p> +
+
+ Fri, 16 Feb 2024 00:00:00 GMT +
+ + gray or grey, and center or centre? + /daily/2024/02/15/gray-or-grey--and-center-or-centre + http://localhost:8000/daily/2024/02/15/gray-or-grey--and-center-or-centre + +
<p><a href="http://localhost:8000/blog/renaming-gray-grey-tailwind-css">I previously wrote</a> about how to change <code>gray</code> to <code>grey</code> in Tailwind CSS.</p> + +<p>So, instead of writing <code>bg-gray-200</code>, I could write <code>bg-grey-200</code>.</p> + +<p>This would be the correct spelling, as I live in the UK.</p> + +<p>But I recently realised that I don't change <code>text-center</code> or <code>items-center</code> when 'centre' would also be the correct spelling.</p> + +<p>So, what value is there to renaming <code>gray</code>?</p> + +<p>It adds inconsistency as it mixes US and UK English in this project and inconsistencies between different projects.</p> + +<p>It doesn't match the CSS colour name.</p> + +<p>And, if I'm working with others on a project, they also need to be aware of this as it's different from the default configuration and could cause some confusion and affect productivity.</p> + +<p>Keeping it the default value would make it easier to work on, collaborate on and maintain.</p> + +<p>Maybe, I'll leave it as <code>gray</code> in the future.</p> +
+
+ Thu, 15 Feb 2024 00:00:00 GMT +
+ + Major version updates are just removing deprecated code + /daily/2024/02/14/major-version-updates-are-just-removing-deprecated-code + http://localhost:8000/daily/2024/02/14/major-version-updates-are-just-removing-deprecated-code + +
<p>Today, I've been watching the new <a href="https://symfonycasts.com/screencast/symfony7-upgrade">Upgrading &amp; What's in Symfony 7</a> video course on SymfonyCasts.</p> + +<p>The first video - <a href="http://localhost:8000/podcast/10-ryan-weaver-symfonycasts">recent podcast guest Ryan Weaver</a> - explains how Symfony's release cycle works.</p> + +<p>New feature releases that contain new features are every six months.</p> + +<p>Along with the x.4 release - such as Symfony 6,4 - there is also a new major release - in this case, Symfony 7.</p> + +<p>They are essentially identical, except for code that was deprecated in Symfony 6, which has been removed.</p> + +<p>So, updating from Symfony 6.4 to 7 means you just need to remove any deprecated code from your application and make it work in the Symfony 7 way.</p> + +<p>This is also how Drupal releases new versions, too.</p> + +<p>New releases, like Layout Builder, are added in minor versions like 8.1, and Drupal 9 is Drupal 8 without its deprecated code.</p> + +<p>Because the code in major versions is so similar, <strong>contributed modules and themes can support multiple major versions at the same time</strong>.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>When upgrading projects from Drupal 8 to 9 and 9 to 10, the majority of the work can be done beforehand by keeping up to date with module releases and updating custom code to remove any deprecations.</p> + +<p>This means the upgrade can be split over several weeks or months to reduce the risk.</p> + +<p>Then, finally, you <em>just</em> update to the next major version.</p> + +<p>All the hard work has already been done.</p> +
+
+ Wed, 14 Feb 2024 00:00:00 GMT +
+ + It takes the drama out of open-source + /daily/2024/02/13/it-takes-the-drama-out-of-open-source + http://localhost:8000/daily/2024/02/13/it-takes-the-drama-out-of-open-source + +
<p>As well as the code changes, such as adopting Symfony components and other third-party code, one of my favourite things about Drupal since D8 is the new release cycle.</p> + +<p>Instead of "It'll be ready when it's ready.", since Drupal 8, Drupal releases new versions on fixed dates.</p> + +<p>If a feature is ready by that date, it will be included.</p> + +<p>If not, it will be included in a later version.</p> + +<p>As well as regular bug fixes and security updates, there is a new feature release every six months.</p> + +<p>Drupal 8.1 included features like the Layout Builder, which wasn't included in 8.0.</p> + +<p>In earlier Drupal versions, it would have been deferred to the next major version, making it longer before users get its benefits.</p> + +<p>A fixed release schedule makes it easier for users and Developers to plan their updates and prepare to upgrade to the major version.</p> + +<p>As Ryan said on a <a href="http://localhost:8000/podcast/10-ryan-weaver-symfonycasts">recent episode of Beyond Blocks</a>, "It takes the drama out of open-source".</p> +
+
+ Tue, 13 Feb 2024 00:00:00 GMT +
+ + Symfony conventions making their way to Drupal + /daily/2024/02/12/symfony-conventions-making-their-way-to-drupal + http://localhost:8000/daily/2024/02/12/symfony-conventions-making-their-way-to-drupal + +
<p>Drupal 8 was a major turning point for Drupal when it was released in 2015.</p> + +<p>Moving from "not invented here" to "proudly found elsewhere" and "getting off the island", Drupal 8 introduced Composer to Drupal core and several of the Symfony PHP components.</p> + +<p>Following Drupal 8's release, Drupal core started to follow semantic versioning and, six months later, released Drupal 8.1.0 with new features.</p> + +<p>In comparison, Drupal 7, with non-semantic versioning, is now on version 7.99.</p> + +<p>The approach to having fixed release dates instead of feature-based release dates is also used in Symfony, which makes it much easier to plan for.</p> + +<p>The differences between major versions, such as Drupal 9 and 10, is the removal of deprecated code - making it much easier than ever before to upgrade to the new major version of Drupal core. Sometimes, you won't need to update any custom code - especially if it's been maintained and updated during the minor releases.</p> + +<p>I've been pleased to see Symfony features such as autowiring and autoconfiguration start to be used within Drupal, as well as discussions around using PHP attributes to replace annotations and docblocks.</p> + +<p>Most recently, when writing my <a href="http://localhost:8000/atdc">automated testing email course</a>, I saw a number of aliases have been added to core's <code>core.services.yml</code> file, which means I can use the fully qualified class name as the service name without adding aliases to my own services files.</p> + +<p>Drupal and Symfony are separate projects, and Drupal uses more than just Symfony components, but as someone who uses Drupal and Symfony, I'm happy to see them learning from and inspiring each other.</p> + +<p>I'm looking forward to seeing the next thing - <a href="http://localhost:8000/podcast/10-ryan-weaver-symfonycasts">Twig components</a>, maybe - in Drupal.</p> +
+
+ Mon, 12 Feb 2024 00:00:00 GMT +
+ + Twig, Symfony and SymfonyCasts with Ryan Weaver + /daily/2024/02/11/ryan-weaver + http://localhost:8000/daily/2024/02/11/ryan-weaver + +
<p>This week's guest on Beyond Blocks is <a href="http://localhost:8000/podcast/10-ryan-weaver-symfonycasts">Ryan Weaver</a>.</p> + +<p>Ryan is a Symfony Developer, Symfony core team member and writer for SymfonyCasts.</p> + +<p>We discussed recent developments in Twig templates, SymfonyCasts, release cycles, and similarities between the Drupal and Symfony projects and communities.</p> + +<p>I've been a long-time subscriber of SymfonyCasts and it was a big help when I was learning Drupal 8 for Twig and object-orientated PHP, so it was great to speak with Ryan.</p> +
+
+ Sun, 11 Feb 2024 00:00:00 GMT +
+ + Do you really need it? + /daily/2024/02/10/do-you-really-need-it + http://localhost:8000/daily/2024/02/10/do-you-really-need-it + +
<p>Before adding a new feature or change to a codebase, ask if it's really needed and consider its long-term implications.</p> + +<p>Code is easy to write, but needs to be maintained as newer language or framework features are added or have breaking changes.</p> + +<p>Something I've added recently to Build Configs was an option to use an <a href="http://localhost:8000/daily/2024/01/27/gitignore-inclusive-or-exclusive">inclusive or exclusive .gitignore file</a>.</p> + +<p>Whilst it's only adding an if condition based on a value, it adds a separate path in my code and both need to be maintained.</p> + +<p>I've been thinking of adding <code>just</code> again to some projects instead of a <code>run</code> file, which would add separate files that need to be maintained and kept up-to-date with each other so both offer the same features.</p> + +<p>Is this something I want to maintain going forward? Does it add enough value to justify its maintenance?</p> + +<p>Different to a feature flag, which usually has a known lifespan, this could need be maintained for the whole lifespan of the application.</p> + +<p>On a client project, this could be having two sets of buttons with rounded and square corners.</p> + +<p>Do we need both?</p> + +<p>It could be the positioning of a title in a header. Fewer options mean there is less code to write and maintain.</p> + +<p>In a Drupal project, each choice could mean adding a different field, taxonomy term, or content or block type to achieve the desired result.</p> + +<p>The more we can achieve with fewer options means the application will be easier to maintain and work on in the future.</p> +
+
+ Sat, 10 Feb 2024 00:00:00 GMT +
+ + Defining boundaries between custom Drupal modules + /daily/2024/02/09/defining-boundaries-between-custom-drupal-modules + http://localhost:8000/daily/2024/02/09/defining-boundaries-between-custom-drupal-modules + +
<p>I recently made a change to a Drupal project with several custom modules.</p> + +<p>As part of the change, there was a method that was no longer being used, so I went ahead and removed it.</p> + +<p>There were no tests for this part of the codebase, and the existing tests were still passing.</p> + +<p>My change was successfully finished and deployed, including removing the unused method.</p> + +<p>A few days later, we noticed cron jobs were no longer running on the website.</p> + +<p>The method I'd removed was attempting to be called from within a different module.</p> + +<p>Because it no longer existed, it was causing an error and stopping the cron job from running successfully.</p> + +<p>When I re-added the method, the cron jobs worked again.</p> + +<p>There were no <code>dependencies</code> set in either module's .info.yml file, though that would only prevent you from uninstalling either module.</p> + +<p>It wouldn't prevent me from deleting code used by another module.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>This is why I've been <a href="http://localhost:8000/daily/2024/02/08/experimenting-with-architectural-testing">experimenting with architectural testing</a>. To ensure that modules only use their own code and to start enforcing layers within each module.</p> + +<p>Then, if I had the same situation again, I'd know straight away that there was an issue and not have to wait for the bug to be deployed to production.</p> +
+
+ Fri, 09 Feb 2024 00:00:00 GMT +
+ + Experimenting with Architectural Testing + /daily/2024/02/08/experimenting-with-architectural-testing + http://localhost:8000/daily/2024/02/08/experimenting-with-architectural-testing + +
<p><a href="http://localhost:8000/daily/2024/02/07/running-tests-in-parallel-with-paratest">In yesterday's email</a>, I mentioned parallel testing and speeding up your tests by running them in parallel.</p> + +<p>Something else I've been experimenting with recently in architectural testing with PHPat.</p> + +<p>For example, ensuring classes within a namespace are <code>final</code> or not, that Controller classes all extend <code>ControllerBase</code> and have the Controller suffix in their names.</p> + +<p>Going forward, I'd like to ensure that each Drupal module only uses its own classes and is separated, as I recently had an issue where I deleted a class method in one module only to find it was used in a different module.</p> + +<p>Here's what I have so far for my <a href="http://localhost:8000/atdc">testing course codebase</a>:</p> + +<pre><code class="language-php">final class ArchitectureTest { + + public function test_classes_should_be_final(): Rule { + return PHPat::rule() + -&gt;classes(Selector::inNamespace('Drupal\atdc')) + -&gt;shouldBeFinal(); + } + + public function test_controllers_should_extend_ControllerBase(): Rule { + return PHPat::rule() + -&gt;classes(Selector::inNamespace('Drupal\atdc\Controller')) + -&gt;shouldExtend() + -&gt;classes(Selector::classname(ControllerBase::class)); + } + + public function test_controllers_should_have_the_Controller_suffix(): Rule { + return PHPat::rule() + -&gt;classes(Selector::inNamespace('Drupal\atdc\Controller')) + -&gt;shouldBeNamed( + classname: '/Controller$/', + regex: TRUE, + ); + } + +} +</code></pre> + +<p>I plan to continue expanding this configuration as I become more familiar with PHPat, and because it's a PHPStan extension, it's already available to run within my projects locally and within the CI pipeline.</p> +
+
+ Thu, 08 Feb 2024 00:00:00 GMT +
+ + Running tests in parallel with Paratest + /daily/2024/02/07/running-tests-in-parallel-with-paratest + http://localhost:8000/daily/2024/02/07/running-tests-in-parallel-with-paratest + +
<p>Something that I've recently added to my PHP projects is <a href="https://github.com/paratestphp/paratest">Paratest</a>.</p> + +<p>It adds parallel testing to PHPUnit, so your tests will be run in parallel instead of sequentially.</p> + +<p>For the example module in my <a href="http://localhost:8000/atdc">automated testing in Drupal email course</a>, using Paratest reduces the execution time from ~16 seconds to ~8 seconds.</p> + +<p>In a client project with 136 tests, it reduces the time from four and a half minutes to less than two minutes.</p> + +<p>This is a big improvement just from running a single Composer command to add Paratest.</p> + +<p>There's also <a href="https://www.drupal.org/project/drupal/issues/2781123">an open issue</a> for using it for Drupal core's tests, which is something I'll keep an eye on and will look to contribute to.</p> +
+
+ Wed, 07 Feb 2024 00:00:00 GMT +
+ + Tim Lehnen and the Drupal Association + /daily/2024/02/06/tim-lehnen-and-the-drupal-association + http://localhost:8000/daily/2024/02/06/tim-lehnen-and-the-drupal-association + +
<p>Last week on the Beyond Blocks podcast, I spoke with Tim Lehnen - CTO and former colleague at the Drupal Association.</p> + +<p>We discussed what the Drupal Association is and what it does, how companies and individuals can contribute and support the Association, some recent and upcoming improvements to Drupal.org, Drupal 7's end-of-life, and more.</p> + +<p><a href="http://localhost:8000/podcast/9-tim-lehnen">Listen now</a></p> + +<p>This Friday, I'm releasing an episode with Ryan Weaver from SymfonyCasts.</p> +
+
+ Tue, 06 Feb 2024 00:00:00 GMT +
+ + .gitignore or .gitallow + /daily/2024/02/05/gitignore-or-gitallow + http://localhost:8000/daily/2024/02/05/gitignore-or-gitallow + +
<p>Following last week's email on <a href="http://localhost:8000/daily/2024/01/27/gitignore-inclusive-or-exclusive">different ways to write .gitignore files</a>, friend of the list, Daniel Harper, sent me this reply (shared with permission):</p> + +<blockquote> + <p>I had a debate once about this topic and we settled on ignore as the filename explicitly describes what it should be doing ie. It's not .gitallow 😆</p> +</blockquote> + +<p>This is a good point.</p> + +<p>What do people expect to see in a <code>.gitignore</code> file?</p> + +<p>A list of directories and files to be ignored or allowed?</p> + +<p>Based on the filename, it should be the former.</p> + +<p>This would be clearer for people when they first open the file.</p> + +<p>However, if you decide to use the allow approach instead, document it in an <a href="http://localhost:8000/daily/2022/09/23/adrs-technical-design-documents">ADR or design document</a> and why you decided to do it that way and provide context for people working on the codebase in the future.</p> +
+
+ Mon, 05 Feb 2024 00:00:00 GMT +
+ + Speaking about Sculpin at PHPSW + /daily/2024/02/04/speaking-about-sculpin-at-phpsw + http://localhost:8000/daily/2024/02/04/speaking-about-sculpin-at-phpsw + +
<p>I'm happy to be speaking again at <a href="https://www.meetup.com/php-sw/events/298880313">PHP South West</a> next week for my 98th talk or workshop and eighth at PHPSW.</p> + +<p>This talk will be on <a href="https://sculpin.io">Sculpin</a> - a static site generator written in PHP.</p> + +<p>I first presented a Sculpin talk at <a href="http://localhost:8000/presentations/test-drive-twig-with-sculpin">DrupalCamp North in July 2015</a> while learning Twig before Drupal 8.</p> + +<p>If you'd like a sneak peek of the demo site I'm building, <a href="https://github.com/opdavies/phpsw-sculpin-demo">it's on GitHub</a>, and I posted a screenshot on <a href="https://twitter.com/opdavies/status/1754629305575874738">Twitter</a>.</p> +
+
+ Sun, 04 Feb 2024 00:00:00 GMT +
+ + Reducing complexity makes contribution easier + /daily/2024/02/03/reducing-complexity-makes-contribution-easier + http://localhost:8000/daily/2024/02/03/reducing-complexity-makes-contribution-easier + +
<p>This week, I spoke with <a href="https://mark.ie">Mark Conroy</a> for an upcoming episode of the <a href="http://localhost:8000/podcast">Beyond Blocks podcast</a>.</p> + +<p>We discussed his approach to removing front-end build tools from projects, including the LocalGov Drupal base theme.</p> + +<p>Doing this removes dependencies such as Sass and tools like Webpack but also makes it easier for others to contribute as there's less to learn.</p> + +<p>The barrier to entry is lower.</p> + +<p>This is a big advantage in the open-source space and for companies and project teams.</p> + +<p>Having simpler code and fewer tools makes it easier for others to understand and work on your codebase, making them productive sooner.</p> +
+
+ Sat, 03 Feb 2024 00:00:00 GMT +
+ + Automated tests prevent you from adding regressions + /daily/2024/02/02/automated-tests-prevent-you-from-adding-regressions + http://localhost:8000/daily/2024/02/02/automated-tests-prevent-you-from-adding-regressions + +
<p>Continuing from <a href="http://localhost:8000/daily/2024/01/30/tdd-doesnt-mean-you-know-everything-upfront">my last few emails</a>, as well as adding the new use case more easily and quicker, having automated tests also saved me from adding a regression into the code I was changing.</p> + +<p>I'd written a condition in the query to ensure only results that started with the search term.</p> + +<p>Initially, I removed it, but then the tests failed.</p> + +<p>This reminded me why I'd written the condition that way, and I was able to re-add my fix differently.</p> + +<p>Without the tests, I'd likely have removed it and introduced a regression.</p> + +<p>Whilst fixing a bug, I'd have introduced a different bug.</p> + +<p>My tests saved me from doing that and I was able to rectify it quickly before pushing to CI or the staging environment.</p> +
+
+ Fri, 02 Feb 2024 00:00:00 GMT +
+ + Start with a failing test + /daily/2024/02/01/start-with-a-failing-test + http://localhost:8000/daily/2024/02/01/start-with-a-failing-test + +
<p>When fixing a bug or adding a new feature using test-driven development, it's important to see the test fail first.</p> + +<p>You should write your test so it doesn't pass by default or accidentally.</p> + +<p>When creating data, such as users or nodes within tests, do so in a way that will force the test to fail, such as explicitly setting the title or published date into a non-default order.</p> + +<p>When fixing a bug, write a test that represents the expected outcome when the bug is fixed. This confirms the bug exists and can be replicated.</p> + +<p>Then, once the tests pass, you know the feature or fix is working and that it's because of your changes and not for any other reason.</p> +
+
+ Thu, 01 Feb 2024 00:00:00 GMT +
+ + Automated tests mean you can make changes quicker + /daily/2024/01/31/automated-tests-mean-you-can-make-changes-quicker + http://localhost:8000/daily/2024/01/31/automated-tests-mean-you-can-make-changes-quicker + +
<p>Before fixing <a href="http://localhost:8000/daily/2024/01/30/tdd-doesnt-mean-you-know-everything-upfront">yesterday's bug</a>, because I'd written automated tests, I ran them to ensure they were all passing.</p> + +<p>Then, I was able to focus solely on adding the new use case - starting with a failing test to replicate the issue and then getting it to pass.</p> + +<p>Because it was already tested, I didn't need to worry about breaking any other functionality and introducing regressions.</p> + +<p>When the new test was passing, I could run the whole test suite and ensure they still passed and things continued to work.</p> + +<p>Without the tests, I'd either need to check everything else manually (which takes time) or worry that something could potentially be broken.</p> + +<p>Having tests meant I could be confident that the new and existing functionality worked.</p> +
+
+ Wed, 31 Jan 2024 00:00:00 GMT +
+ + TDD doesn't mean you know everything upfront + /daily/2024/01/30/tdd-doesnt-mean-you-know-everything-upfront + http://localhost:8000/daily/2024/01/30/tdd-doesnt-mean-you-know-everything-upfront + +
<p>I'm in the final phase of a Drupal development project for a customer.</p> + +<p>It has some custom modules and code I wrote with automated tests and test-driven development.</p> + +<p>Today, the client reported a bug.</p> + +<p>But, instead of something working incorrectly, this was a use case I hadn't considered.</p> + +<p>The tests were passing, but there wasn't one for this.</p> + +<p>I wrote the code for the use cases I was aware of when I started, and now I'm aware of another, I can add a test for it and ensure it's tested and working.</p> + +<p>To do test-driven development, you don't need to know all the use cases and functionality upfront.</p> + +<p>Write for what you know at the time, then expand and iterate in the future.</p> +
+
+ Tue, 30 Jan 2024 00:00:00 GMT +
+ + Violinist and automation with Eirik Morland + /daily/2024/01/29/violinist-and-automation-with-eirik-morland + http://localhost:8000/daily/2024/01/29/violinist-and-automation-with-eirik-morland + +
<p>In <a href="http://localhost:8000/podcast/8-eirik-morland-violinist">today's episode of the Beyond Blocks podcast</a>, I'm joined by Eirik Morland - the Developer of <a href="https://violinist.io">Violinist</a>.</p> + +<p>We discuss why you'd want to use a tool like Violinist to manage your dependency updates, why you'd want to automate repetitive tasks and focus on providing value for clients and customers.</p> + +<p>We also discuss running a SaaS business and why Drupal is a good choice for SaaS products - though this could be a full episode on its own.</p> +
+
+ Mon, 29 Jan 2024 00:00:00 GMT +
+ + Ignoring things globally + /daily/2024/01/28/ignoring-things-globally + http://localhost:8000/daily/2024/01/28/ignoring-things-globally + +
<p>Yesterday's email was about repository-specific .gitignore files and different ways to write them.</p> + +<p>But there's a setting that most people don't know about, where you can configure a global <code>.gitignore</code> file.</p> + +<h2 id="what-i-use-it-for">What I use it for</h2> + +<p>Whilst it doesn't replace repository-specific files, it's good for operating system-specific files - such as <code>.DS_Store</code> files on macOS.</p> + +<p>I have a convention where I have a <code>.ignored</code> directory in a project, and everything in it should be ignored by Git.</p> + +<p>Instead of adding this to every <code>.gitignore</code> file, and because it's specific to me, it's a good choice for a global ignore file.</p> + +<p>Anything that affects multiple users - such as ignoring <code>vendor</code> or <code>node_modules</code> should still be set in each repository.</p> + +<h2 id="how-do-you-add-it%3F">How do you add it?</h2> + +<p>Add this to your <code>~/.gitconfig</code> or <code>~/.config/git/config</code> file to set the path for your global ignore file:</p> + +<pre><code class="language-plain">[core] + excludesFile = "~/.config/git/ignore" +</code></pre> + +<p>Then, create the file and add what you want to ignore everywhere.</p> + +<p>Just remember this is specific to you, and if others have something you've ignored globally and they haven't, they could still add and commit it.</p> +
+
+ Sun, 28 Jan 2024 00:00:00 GMT +
+ + gitignore - inclusive or exclusive? + /daily/2024/01/27/gitignore-inclusive-or-exclusive + http://localhost:8000/daily/2024/01/27/gitignore-inclusive-or-exclusive + +
<p>Add everything and ignore what you don't want, or ignore everything and explicitly add what you need.</p> + +<p>There are two ways to structure a .gitignore file.</p> + +<p>The default approach is that all files can be added, and you specify the files and directories you want to ignore.</p> + +<p>For example, if my <code>.gitignore</code> file was this, these two directories would be ignored:</p> + +<pre><code class="language-plain">vendor +web +</code></pre> + +<p>The other approach is to ignore everything and unignore the things to add. For example:</p> + +<pre><code class="language-plain">* +!build.yaml +!Dockerfile +!docker-compose.yaml +!web/*/custom +</code></pre> + +<p>Both approaches work and are regularly used.</p> + +<p>Which approach do you prefer and why?</p> + +<p>Reply and let me know.</p> +
+
+ Sat, 27 Jan 2024 00:00:00 GMT +
+ + Write once, manage forever + /daily/2024/01/26/write-once-manage-forever + http://localhost:8000/daily/2024/01/26/write-once-manage-forever + +
<p>I built my <a href="http://localhost:8000/build-config">Build Configs</a> tool because I only wanted to write a file once and re-use it instead of writing it over again or copying and pasting between projects.</p> + +<p>Having standardised templates for different languages and project types, I can easily set up new projects and get them running in a few minutes.</p> + +<p>If I need to add a feature or fix a bug, I can do it once in the Build Configs tool and easily regenerate the configuration files for all projects, and they'll all get the updated files.</p> + +<p>There's no "I need to copy this feature from project A or this bug fix from project B." when starting on project C.</p> + +<p>As I only work on fixed-price engagements, it's in my interest to be able to create and maintain projects in a fast and efficient manner.</p> + +<p>The Build Configs tool enables me to do that.</p> +
+
+ Fri, 26 Jan 2024 00:00:00 GMT +
+ + Speaking at PHP Oxford + /daily/2024/01/25/speaking-at-php-oxford + http://localhost:8000/daily/2024/01/25/speaking-at-php-oxford + +
<p>I attended the relaunched PHP Oxford user group meetup this evening, organised by Humand Talent.</p> + +<p>I gave the short version of my talk on the <a href="http://localhost:8000/build-configs">Build Configs</a> tool I've built and used to manage multiple projects' configuration files.</p> + +<p>As it was a shorter talk, I didn't show the internals slides I did remotely for PHP Muninch on Tuesday evening.</p> + +<p>However, if anyone wants to see the full slides that show some of the Build Configs code, <a href="http://localhost:8000/talks/building-build-configs">they are online</a>.</p> + +<p>As well as the Drupal, Fractal, Drupal Commerce Kickstart and Drupal LocalGov examples on GitHub, I created Symfony and Laravel examples that are still unreleased in private repositories. I plan on reviewing those, making them public soon and continuing to iterate on and improve the Build Configs tool itself - making it more valuable to me and my customers and clients.</p> +
+
+ Thu, 25 Jan 2024 00:00:00 GMT +
+ + Defining Ubiquitous language + /daily/2024/01/24/defining-ubiquitous-language + http://localhost:8000/daily/2024/01/24/defining-ubiquitous-language + +
<p>A key takeaway from Rob Allen's Domain-Driven Design talk was defining ubiquitous language and avoiding the phrase "That's not what I meant".</p> + +<p>Even a simple table or glossary that lists business and domain-specific terms and their agreed meaning is very helpful to ensure everyone in the discussion is on the same page and means the same thing.</p> + +<p>Rob's example was using the words "policy" and "risk" when dealing with insurance clients.</p> + +<p>A common issue I've seen is where people are referred to as customers by the business and users within the software.</p> + +<p>Ideally, these should be consistent, and the code should match the business terminology.</p> + +<p>This can be complicated further by different areas of the business, such as a marketing team that may refer to people as subscribers.</p> + +<p>Without the ubiquitous language being defined, the requirements are more likely to be misunderstood and the wrong solution delivered, resulting in "that's not what I meant.".</p> + +<p>This then means the work needs to be re-done and delayed, which can be expensive and time-consuming.</p> + +<p>Another approach is to work in small batches, which is something I've written about before, and getting feedback from customers as early and often as possible so, if there is a misunderstanding, the minimum amount of time has been spent before it's realised and rectified.</p> + +<p>Rob, of course, covered a lot more about DDD in his talk, and I'm looking forward to re-watching it once the video from the meetup is released.</p> +
+
+ Wed, 24 Jan 2024 00:00:00 GMT +
+ + Why use automation tools for dependency updates + /daily/2024/01/23/why-use-automation-tools-for-dependency-updates + http://localhost:8000/daily/2024/01/23/why-use-automation-tools-for-dependency-updates + +
<p>Last week, I recorded an episode of <a href="http://localhost:8000/podcast">Beyond Blocks</a> with Eirik Morland - the Developer of violinist.io, a tool for automating dependency updates in PHP projects.</p> + +<p>Instead of a person manually running <code>composer update</code> in each project, tools like Violinist can do that for you and submit pull or merge requests to your project for you to review.</p> + +<p>But why would you want this?</p> + +<p>There are technical reasons, such as not having to rely on everyone having the same local environment and avoiding potential conflicts, and consistency by ensuring the same command is run every time.</p> + +<p>But, the big advantage, in my opinion, is the time it saves and allows you to reuse.</p> + +<p>Instead of manually updating each project's dependencies, I can focus on tasks that deliver value to my customers and clients and move us towards our objectives.</p> + +<p>Dependency updates, such as a new version of Drupal core or a contrib module, don't contain any perceived value compared to other tasks, such as adding a new feature or fixing a bug.</p> + +<p>They happen behind the scenes, often invisibly, without any visual changes to show an update has been done.</p> + +<p>If they aren't done often, they will be riskier to deploy due to the larger changes, and the longer it takes, the greater the potential for insecure versions to be exploited - potentially affecting your reputation and your customer's and with any remedial work taking the focus from other tasks.</p> + +<p>Having a service like Violinist performing the updates for you means they can be applied and deployed more regularly, reducing the risk and making it easier to stay up-to-date and run secure versions of your dependencies.</p> +
+
+ Tue, 23 Jan 2024 00:00:00 GMT +
+ + Tailwind CSS workshop recording + /daily/2024/01/22/tailwind-css-workshop-recording + http://localhost:8000/daily/2024/01/22/tailwind-css-workshop-recording + +
<p>At Saturday's hackathon that I mentioned in yesterday's email, most of the squads decided to try Tailwind CSS in their applications.</p> + +<p>I've been using Tailwind since it was released, and first gave <a href="http://localhost:8000/presentations/taking-flight-with-tailwind-css">a Tailwind CSS talk</a> in January 2018.</p> + +<p>It's one of my most popular talks and, during COVID lockdowns, I also gave a workshop/webinar on Tailwind CSS for some online conferences.</p> + +<p><a href="https://www.youtube.com/watch?v=phFDKF-9j0Y">Here's a recording</a> of the session I gave for DrupalCamp Florida, in which I cover some Tailwind CSS concepts and show how to install and customise Tailwind before rebuilding the Florida DrupalCamp website live.</p> + +<p>If this helps anyone on their Tailwind CSS learning journey, please reply and let me know!</p> +
+
+ Mon, 22 Jan 2024 00:00:00 GMT +
+ + Where is the value in your application? + /daily/2024/01/21/where-is-the-value-in-your-application + http://localhost:8000/daily/2024/01/21/where-is-the-value-in-your-application + +
<p>Yesterday, I was in Birmingham for a hackathon event organised by the School of Code, who I've been a Bootcamp mentor for for the last few cohorts.</p> + +<p>At the event, I was mentoring a team of three Bootcamp graduates to plan and build a Christmas-themed application.</p> + +<p>We decided on a Christmas walking application where people can view upcoming walks, log in and book a place.</p> + +<p>After designing some initial pages, such as the Home page with a login form, a registration form, a list of walks and a walk detail page, we started to code them.</p> + +<p>Naturally, we started with the Home page, the login form and the login functionality.</p> + +<p>But, is that the most valuable part of the application?</p> + +<p>What differentiates it from other applications, such as the ones being built by other squads at the event?</p> + +<p>In this case, it's the page showing the list of walks and, if you click one, the details about that walk.</p> + +<p>The squad needed to give a presentation and demo of the application by the end of the day, so we needed to prioritise.</p> + +<p>We refocused on the walk pages and built them before moving back to the other pages to complete the user journey so it could be shown.</p> + +<p>Similarly, we didn't need a fully functional user login and registration system for the demo. We just needed to show the forms we'd built, demo the user journey and show how someone would find and register for an event.</p> + +<p>When I'm building an application, I identify the most valuable part and focus on it rather than other unrelated functionality that could likely be done manually or another way until that functionality is available.</p> + +<p>The beginning of the user journey isn't always the best first thing to start developing.</p> + +<p>It was a great day, our squad won the prize for the event, and I look forward to attending more events and continuing to work with the School of Code.</p> +
+
+ Sun, 21 Jan 2024 00:00:00 GMT +
+ + PHPUnit or Pest? + /daily/2024/01/20/phpunit-or-pest + http://localhost:8000/daily/2024/01/20/phpunit-or-pest + +
<p>A recent discussion has started about which testing framework should be the default one used in Laravel.</p> + +<p>PHPUnit or Pest.</p> + +<p>I've used both, and I like both.</p> + +<p>I'm more familiar with classes, object-orientated code and PHPUnit, but for people who are more familiar with JavaScript and the Jest testing framework, Pest may be the better option.</p> + +<p>Its expectation API, where you can chain multiple assertions on the same expectation, is helpful, although I don't mind some repetition in PHPUnit tests.</p> + +<p>Pest tests can become abstract, especially with some of their one-line example tests.</p> + +<p>Whether you pick PHPUnit, Pest or another framework like Behat or Codeception, the main thing is you're taking the time to write automated tests for your code, which is valuable whichever testing tool you use.</p> +
+
+ Sat, 20 Jan 2024 00:00:00 GMT +
+ + Tests can assert multiple things + /daily/2024/01/19/tests-can-assert-multiple-things + http://localhost:8000/daily/2024/01/19/tests-can-assert-multiple-things + +
<p>Similar to "a method should only have one return statement", I've seen similar advice when working with tests: "Tests should only have a single assertion".</p> + +<p>I don't think this is true, and in my experience, you need multiple assertions to have a thorough test.</p> + +<p>And, whilst similar assertions add some duplication, they can make the intent clearer and give better error messages.</p> + +<p>Instead, I focus on one test case per test.</p> + +<p>If I'm testing the following:</p> + +<ul> +<li>A blog page exists.</li> +<li>Only post nodes are visible.</li> +<li>Only published posts are visible,</li> +<li>Posts are returned in a specified order.</li> +</ul> + +<p>These will be split into separate tests - making it easier to read and maintain the code and have faster execution times by running only the tests I want with the minimum amount of code in each - regardless of how many assertions are in each.</p> +
+
+ Fri, 19 Jan 2024 00:00:00 GMT +
+ + Is zero unlimited? + /daily/2024/01/18/is-zero-unlimited + http://localhost:8000/daily/2024/01/18/is-zero-unlimited + +
<p>Something I've seen in code is the unclear use of zero when adding limits, such as loading items from a database.</p> + +<p>In some instances, setting zero will return all items - essentially, an 'unlimited' value in disguise - rather than returning no results, which is what I'd expect.</p> + +<p>I imagine the code looks something like this:</p> + +<pre><code class="language-php">if ($limit &gt; 0) { + $query-&gt;range(0, $limit); +} +</code></pre> + +<p>If <code>$limit</code> is greater than one, add it to the query.</p> + +<p>For me, using <code>0</code> as the unlimited value doesn't seem like the best option.</p> + +<p>I'd prefer to use a <code>null</code> value as the default and only add the limit if it's set - i.e. an integer or not null.</p> + +<p>It means the value could be either an integer or null, but I think the intent of the code is more explicit.</p> + +<p>This would make the code look like this:</p> + +<pre><code class="language-php">if (is_int($limit)) { + $query-&gt;range(0, $limit); +} +</code></pre> + +<p>Whilst this is clearer, it doesn't cover all use cases.</p> + +<p>Presumably, the limit should only be a positive integer.</p> + +<p>It wouldn't make sense to set a negative number as the limit or, as the unlimited value is <code>null</code>, setting it to zero.</p> + +<p>This is the end code I'd likely write:</p> + +<pre><code class="language-php">if (is_int($limit)) { + if ($limit &lt; 1) { + throw new InvalidArgumentException('A limit must be a positive integer.'); + } + + $query-&gt;range(0, $limit); +} +</code></pre> + +<p>If the limit is not an integer, nothing happens.</p> + +<p>It throws an Exception if the value is invalid - i.e. less than one.</p> + +<p>The limit is applied if the limit is greater than or equal to one.</p> + +<p>While it's more complex as there are more checks to perform and different types in use, I think this is clearer and easier for someone reading or implementing the code to understand what it does and use it correctly.</p> +
+
+ Thu, 18 Jan 2024 00:00:00 GMT +
+ + Please don't use short variable names + /daily/2024/01/17/short-variable-names + http://localhost:8000/daily/2024/01/17/short-variable-names + +
<p>When learning to code, one of the most confusing things was using short variable names in documentation and other people's code.</p> + +<p>Things like <code>$k</code> and <code>$v</code> instead of <code>$key</code> and <code>$value</code> within loops, <code>$i</code> instead of <code>$index</code>, or <code>$e</code> when working with Exceptions.</p> + +<p>I've also seen slightly better names, such as <code>$idx</code> for index or <code>$ctx</code> for context.</p> + +<p>But what does this achieve?</p> + +<p>Why not write the full variable name and clarify what it refers to?</p> + +<p>It would be easier to read and understand for anyone reading the code, including Junior Developers and people new to your team or application.</p> + +<p>There are no limitations - at least in the languages I use - to force this, such as a maximum number of characters in a file, so why not write the full variable name?</p> + +<p>The only reason I can think of is to save time by pressing fewer keys, but code is read more than written, so it should be optimised for readability.</p> + +<p>Your tests and CI pipeline don't have a preference.</p> + +<p>The people reading the code will.</p> +
+
+ Wed, 17 Jan 2024 00:00:00 GMT +
+ + Daily or quarterly? + /daily/2024/01/16/daily-or-quarterly + http://localhost:8000/daily/2024/01/16/daily-or-quarterly + +
<p>Imagine this scenario.</p> + +<p>You have two options on how frequently you can deploy code changes to your application.</p> + +<p>Option 1: Every day.</p> + +<p>Option 2: Once a quarter.</p> + +<p>No more, no less.</p> + +<p>I'd choose daily.</p> + +<p>I much prefer to deploy changes as often as possible rather than waiting.</p> + +<p>I'm much more confident when releasing small changes - even if it's a small refactor, such as changing a variable name or extracting a small helper method.</p> + +<p>It might even seem too small to release.</p> + +<p>But the smaller the release is, the easier it is to find and fix any issues, and knowing that the next release would only be the following day makes it easier to fix forward instead of rolling back a large release with months of changes.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>Whilst it may seem counterintuitive initially, it's much less risky to release small changes often compared to large changes infrequently.</p> + +<p>Which option would you choose?</p> +
+
+ Tue, 16 Jan 2024 00:00:00 GMT +
+ + Don't be perfect, be useful + /daily/2024/01/15/don-t-be-perfect--be-useful + http://localhost:8000/daily/2024/01/15/don-t-be-perfect--be-useful + +
<p>In a recent private coaching session with <a href="https://jonathanstark.com">Jonathan Stark</a>, he said, "Don't be perfect, be useful".</p> + +<p>He meant it relating to giving a presentation, like a talk or workshop, but I thought about how the same quote applies to some of the software development topics I cover in these emails.</p> + +<p>You don't need a perfect test suite with 100% coverage. One test is better than no tests, and some tests are better than one.</p> + +<p>You don't need to run static analysis on all your code at the highest level. If you run it on some of your code, maybe the newer code or the most crucial parts of your application, that's better than not running it at all, and it will catch many issues even at the lower levels.</p> + +<p>You don't need to know and implement all the design patterns within your code. The objective is to deliver working software for your customers and clients. Do this first and refactor later when needed.</p> + +<p>You don't need always to be pair or mob programming. Do it when it works for the people and situation.</p> + +<p>These tools and techniques are useful.</p> + +<p>They don't need to be perfect.</p> +
+
+ Mon, 15 Jan 2024 00:00:00 GMT +
+ + Code is a liability, not an asset + /daily/2024/01/14/code-is-a-liability-not-an-asset + http://localhost:8000/daily/2024/01/14/code-is-a-liability-not-an-asset + +
<p>Something I mentioned during my <a href="http://localhost:8000/podcast/7-mike-karthauser-testing-legacy">podcast episode with Mike Karthauser</a> is a quote I saw recently:</p> + +<blockquote> + <p>Code is a liability, not an asset.</p> +</blockquote> + +<p>The more code you write, the more you have to maintain.</p> + +<p>The more complex the code is, the harder it is to maintain.</p> + +<p>For example, when adding a new page to a Drupal application, should you write a custom route, a Controller, and a Repository and write accompanying tests, or should you use the Views module?</p> + +<p>Both can give the same result.</p> + +<p>One involves writing and maintaining custom code; the other uses a no-code approach available in Drupal, which creates the page and output based on your selections.</p> + +<p>There isn't a correct answer.</p> + +<p>The option you choose will depend on what problem you're solving, what deadlines you're working to, and any precedents set within the project.</p> + +<p>It also depends on whether you want to maintain the code you write over time, fix any bugs, refactor it, and upgrade it as part of major Drupal version upgrades.</p> + +<p>Like a house, car or pet, any code you write will need ongoing care and maintenance in the future - not just now.</p> +
+
+ Sun, 14 Jan 2024 00:00:00 GMT +
+ + Testing Legacy with Mike Karthauser + /daily/2024/01/13/testing-legacy-with-mike-karthauser + http://localhost:8000/daily/2024/01/13/testing-legacy-with-mike-karthauser + +
<p>Last week, Mike Karthauser - Senior Software Developer at Huboo - <a href="http://localhost:8000/podcast/7-mike-karthauser-testing-legacy">joined me on the Beyond Blocks podcast</a>.</p> + +<p>Mike also gave a talk recently at the PHP South West meetup on "Testing Legacy" which explained how he works with and tests legacy applications.</p> + +<p>In the episode, we discuss e-commerce, legacy code, technical debt, automated testing, test-driven development, refactoring, code vs. no-code solutions.</p> +
+
+ Sat, 13 Jan 2024 00:00:00 GMT +
+ + Utility classes make global scope local + /daily/2024/01/12/utility-classes-make-global-scope-local + http://localhost:8000/daily/2024/01/12/utility-classes-make-global-scope-local + +
<p>In my <a href="http://localhost:8000/daily/2024/01/09/using-tailwind-css-is-a-great-way-to-learn-css">recent pair programming session</a>, building components with Tailwind CSS, we experienced another benefit of styling with utility classes.</p> + +<p>CSS usually has a global scope, but utility classes change it to a local scope.</p> + +<p>For example, having a card component with a <code>card</code> class on it, any changes to the styles will affect all instances of any card component that uses the <code>card</code> class.</p> + +<p>How do you know you haven't broken something in a different component without re-checking every component manually?</p> + +<p>How do you easily modify or extend it as requirements change or add more card types?</p> + +<p>If you need to make a change in the future, you're likely to add more styles and modifiers and add to the CSS, causing it to grow rather than change the existing styles - in fear of breaking something else.</p> + +<p>With utility classes, such as the ones generated by Tailwind CSS, you can see and understand what styles are applied to the HTML.</p> + +<p>This also makes it easier to change, and because the classes and styles are added directly to that element, you don't need to worry about breakages elsewhere.</p> + +<p>If you need to add a new class to that card type, you can do so knowing that it won't affect all card styles globally in your application - just the one you're working on.</p> + +<p>This means you can change things more easily in the future but also work quicker now as you don't need to worry about all that additional context.</p> + +<p>You can focus on what you're working on right now.</p> +
+
+ Fri, 12 Jan 2024 00:00:00 GMT +
+ + My Drupal testing email course is live + /daily/2024/01/11/my-drupal-testing-email-course-is-live + http://localhost:8000/daily/2024/01/11/my-drupal-testing-email-course-is-live + +
<p>Do you want to learn automated testing in Drupal?</p> + +<p>My free email course is live!</p> + +<p>Learn how to write tests in Drupal from a DrupalCon speaker, workshop trainer, and module maintainer (me!).</p> + +<p>Register for the course and get a lesson each day where you build a module from scratch with automated tests and test-driven development based on my previous talks and workshops.</p> + +<p>All emails are sent from my personal email address, so if you have questions or issues, you can press reply and let me know. I'll be happy to help!</p> + +<p>Happy testing!</p> +
+
+ Thu, 11 Jan 2024 00:00:00 GMT +
+ + Don't put business logic in templates + /daily/2024/01/10/dont-put-business-logic-in-templates + http://localhost:8000/daily/2024/01/10/dont-put-business-logic-in-templates + +
<p>Here is some code from my website:</p> + +<p><img src="http://localhost:8000/assets/images/talk-count-code.png" alt="A screenshot of the code that calculates the number of talks I've given." /></p> + +<p>If you want, you can also <a href="https://raw.githubusercontent.com/opdavies/oliverdavies.uk/main/source/_pages/presentations.md">view it on GitHub</a>.</p> + +<p>It is business logic responsible for counting the number of talks I've given at different events so I can display it on my Talks page.</p> + +<p>It starts at zero, loops over each talk, and increments the talk count if the event is the current day or a past date.</p> + +<p>It's only used in a single place, so the same logic isn't duplicated elsewhere.</p> + +<p>But it's in the page's Twig template.</p> + +<p>It has no test coverage.</p> + +<p>If I need to change or refactor it, I'd need to test it again manually.</p> + +<p>Don't do this.</p> + +<h2 id="so%2C-what-should-i-do%3F">So, what should I do?</h2> + +<p>It's OK to put simple presentational logic, such as looping over a list or whether to show or hide a value within a template, but not complex business logic.</p> + +<p>Business logic should be separated and executed elsewhere. The values should be passed to the template to be rendered.</p> + +<p>This makes the business logic easier to test as you can test the logic itself and determine the value passed to the template is correct without being concerned about the templating engine.</p> + +<p>In an application, you may need to output a value to a template and a terminal. You'd have one source of truth, such as a Service, Action or Command class that calculates the value before passing it to the appropriate output.</p> + +<p>Once the logic is separated, you only need to test it once.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>In a previous version of my website, I did this by creating a custom Twig function.</p> + +<p>It was as simple as adding <code></code> to the template.</p> + +<p>All the logic was moved from the template to my custom extension.</p> + +<p>The logic was separated.</p> + +<p>It had tests.</p> + +<p>This is the approach I'd take to achieve the same result for a client application.</p> + +<p>For a client, I want to run a test suite and be confident my logic works as expected - now and in the future.</p> + +<p>For myself, and for calculating something simple, this is fine.</p> +
+
+ Wed, 10 Jan 2024 00:00:00 GMT +
+ + Using Tailwind CSS is a great way to learn CSS + /daily/2024/01/09/using-tailwind-css-is-a-great-way-to-learn-css + http://localhost:8000/daily/2024/01/09/using-tailwind-css-is-a-great-way-to-learn-css + +
<p>I was in a pair programming session today, working on some Twig components with Tailwind CSS.</p> + +<p>We knew what we needed to implement and did so based on an example from a Tailwind component library and some additional styles.</p> + +<p>After implementing the feature, we could review the classes we added and review what each did.</p> + +<p>We could easily move or remove a class and see what effect it had.</p> + +<p>Something nice is that the Tailwind classes usually relate to what CSS they're applying, such as <code>block</code> and <code>flex</code> for <code>display</code> and <code>relative</code> and <code>absolute</code> for positioning.</p> + +<p>This makes Tailwind a great way to learn CSS compared to other frameworks that give you prebuilt HTML and expect you to add a generic class like <code>card</code>.</p> + +<p>In that case, the knowledge is hidden within a stylesheet the Developer doesn't see, which makes it harder to read and learn from.</p> + +<p>Other utility-class frameworks have shorter class names that are less readable.</p> + +<p>Tailwind strikes the perfect balance, in my opinion.</p> +
+
+ Tue, 09 Jan 2024 00:00:00 GMT +
+ + Try it and see + /daily/2024/01/08/try-it-and-see + http://localhost:8000/daily/2024/01/08/try-it-and-see + +
<p>I recently saw a post with a screenshot of some code they'd written using Tailwind CSS.</p> + +<p>Reading the comments, it had the usual "re-inventing the wheel" and "just use inline styles" comments, which, to me, didn't seem very helpful and not the point of the post.</p> + +<p>When I've given a <a href="http://localhost:8000/presentations/taking-flight-with-tailwind-css">talk on Tailwind CSS</a>, I've acknowledged that people have common initial reactions and suggested people try it for a week or so and see how it works for them before making up their minds.</p> + +<p>Someone did after a conference and posted this on Twitter:</p> + +<blockquote> + <p>I saw a talk on tailwind at a conference. Spent 15 minutes afterwards "discussing" with the presenter how it was horrible and against the neat way of CSS and so on. But promised to try it just once as he said it feels better than it looks.</p> + + <p>He was right. It's so freeing.</p> +</blockquote> + +<p>The same approach applies to many other things, such as pair programming, automated testing, test-driven development, static analysis, Scrum, Linux and Vim - to name a few.</p> + +<p>If you use it and don't like it, don't continue and return to what you did before.</p> + +<p>Whether you keep it or not, you better understand what you're evaluating and not dismiss it out of hand.</p> +
+
+ Mon, 08 Jan 2024 00:00:00 GMT +
+ + Things take as long as they take + /daily/2024/01/07/things-take-as-long-as-they-take + http://localhost:8000/daily/2024/01/07/things-take-as-long-as-they-take + +
<p>Today, I saw a post that asked the question:</p> + +<blockquote> + <p>Have you ever spent three days on an issue that should have taken 2 hours?</p> +</blockquote> + +<p>My thought was, "Who said it should have taken two hours?".</p> + +<p>In my experience, tasks take as long as they take.</p> + +<p>Even something that seems simple can end up being something complex.</p> +
+
+ Sun, 07 Jan 2024 00:00:00 GMT +
+ + Sculpin - the PHP static site generator + /daily/2024/01/06/sculpin-the-php-static-site-generator + http://localhost:8000/daily/2024/01/06/sculpin-the-php-static-site-generator + +
<p>If you use PHP and need to make a static website, <a href="https://sculpin.io">Sculpin</a> is a great choice!</p> + +<h2 id="what-is-it%3F">What is it?</h2> + +<p>Built with PHP and using Symfony components, it converts Markdown files and Twig templates into static HTML that can be easily deployed and hosted.</p> + +<p>Once generated, you can upload the files to a web host of your choice as the generated files are simple HTML, such as an S3 bucket with static hosting or GitHub Pages.</p> + +<h2 id="why-do-i-use-it%3F">Why do I use it?</h2> + +<p>As a PHP Developer, it's already familiar and I can immediately start instead of learning a new language or templating engine, which I'd need to do if I were to use a static site generator written in Ruby, JavaScript or Go.</p> + +<p>If I need to extend it, I already can.</p> + +<p>I already know how to write custom Twig extensions, for example, so I can do it in Sculpin in the same way. (<a href="https://github.com/opdavies/sculpin-twig-markdown-bundle">I made one</a> and open-sourced it)</p> + +<p>I already know how to use PHPUnit to write tests.</p> + +<h2 id="what-do-i-use-it-for%3F">What do I use it for?</h2> + +<p>I use Sculpin for <a href="https://github.com/opdavies/oliverdavies.uk">this website</a> and client projects that don't need the features of a CMS or framework with the ability to have reusable components and functionality provided by Twig (this works great with Tailwind CSS, too).</p> + +<p>I used it to <a href="http://localhost:8000/test-drive-twig-with-sculpin">learn Twig before Drupal 8 was released</a>, back in 2015.</p> + +<p>I've also given a talk on <a href="http://localhost:8000/building-static-websites-sculpin">building websites with Sculpin</a> which I may submit to some upcoming conferences.</p> +
+
+ Sat, 06 Jan 2024 00:00:00 GMT +
+ + PHP in Neovim + + /daily/2024/01/05/php-in-neovim + http://localhost:8000/daily/2024/01/05/php-in-neovim + +
<p>Yesterday, I posted a screenshot from my <a href="http://localhost:8000/atdc">automated testing in Drupal email course</a> on Twitter as a sneak peek of what's included.</p> + +<p>Here it is again:</p> + +<p><img src="http://localhost:8000/assets/images/php-neovim-1.png" alt="Screenshot of a lesson from my automated testing email course" /></p> + +<p>I also posted a screenshot of the accompanying code in my text editor, Neovim, which had a few likes.</p> + +<p><img src="http://localhost:8000/assets/images/php-neovim-2.png" alt="Screenshot of Drupal code in Neovim" /></p> + +<p>Since July 2021, I've used Neovim as my daily driver for writing all my code, which I spoke about during my <a href="http://localhost:8000/presentations/working-without-workspace">Working without Workspace talk</a>.</p> + +<p>You can also see my NixOS-based <a href="https://github.com/opdavies/dotfiles">dotfiles on GitHub</a> which contains my Neovim configuration.</p> + +<p>Register now to find out when the email course is live, or <a href="http://localhost:8000/daily/2023/12/25/zero-to-test">read the first lesson</a>.</p> +
+
+ Fri, 05 Jan 2024 00:00:00 GMT +
+ + Reuse what you can. + + /daily/2024/01/04/reuse-what-you-can + http://localhost:8000/daily/2024/01/04/reuse-what-you-can + +
<p>A lot is different between Drupal 7 and 10, with the introduction of Composer, object-orientated code, Symfony components and other third-party libraries, to name a few things.</p> + +<p>But, when upgrading a project, the business logic may not need to change.</p> + +<p>How it integrates with the new version of Drupal may change - it may use a different module, such as Paragraphs instead of Field Collection, or be within a Controller or Service class instead of a Drupal "hook", but if the majority of the logic can remain the same, it might as well be reused instead of written from scratch.</p> + +<p>Presumably, it works as it does in Drupal 7, which may not be the case after it's been rewritten, as bugs and issues may have been introduced.</p> + +<p>This is the approach I took when migrating the Override Node Options module to Drupal 8. I picked a test, migrated it from SimpleTest (Drupal 7) to PHPUnit (Drupal 8), ported enough code to make it pass, and moved to the next test.</p> + +<p>Most custom applications, though, tend to be rewritten and the old business logic discarded.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>If it works, why rewrite it?</p> + +<p>Migrate what you can and only rewrite what you need.</p> + +<p>As someone who works on fixed-price projects and includes a bug-free guarantee, it's in my interests to deliver working and stable software as soon as possible, which is also in my client's interests as they get their new software sooner and not from a drawn-out process where everything is re-done from scratch.</p> +
+
+ Thu, 04 Jan 2024 00:00:00 GMT +
+ + Why you need to start upgrading from Drupal 7 now + /daily/2024/01/03/why-you-need-to-start-upgrading-from-drupal-7-now + http://localhost:8000/daily/2024/01/03/why-you-need-to-start-upgrading-from-drupal-7-now + +
<p>There's only one year before support for Drupal 7 support ends, but there are still more than 337,000 Drupal 7 websites, according to <a href="https://www.drupal.org/project/usage/drupal">https://www.drupal.org/project/usage/drupal</a>.</p> + +<p>Even though there's a year left, if you've got a Drupal 7 website, you need to start upgrading now!</p> + +<p>Unlike upgrading from Drupal 8 to 9 or 9 to 10, upgrading from Drupal 7 requires major changes to your code for it to be compatible. This will take time to do and test.</p> + +<p>Most websites have some or many custom modules and at least one custom theme that must be upgraded.</p> + +<p>Most use a large number of community-contributed modules.</p> + +<p>Many of these are abandoned or minimally maintained as their maintainers have focused on newer Drupal versions or a module has been marked as unsupported in favour of another.</p> + +<p>If they do have supported versions, it is likely that they haven't had a release for some time. If that's the case, they're essentially unsupported.</p> + +<p>Because of this, there may not be the same modules available with the same functionality for Drupal 10.</p> + +<p>Depending on the available modules, your functionality may need to change, or the old functionality will need to be written within a new custom module.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>Depending on the complexity, it will take a number of months to complete an upgrade.</p> + +<p>Rather than wait until 2025, you want to do the work now whilst Drupal 7 is still supported rather than waiting and being vulnerable to security exploits if running an outdated and unsupported version.</p> +
+
+ Wed, 03 Jan 2024 00:00:00 GMT +
+ + Flexible Mob and Pair Programming + /daily/2024/01/02/flexible-pair-programming + http://localhost:8000/daily/2024/01/02/flexible-pair-programming + +
<p>Doing pair or programming doesn't mean you need to be working in groups continuously.</p> + +<p>If you've asked a colleague to review the code you're writing or help you fix a bug, that's pair programming.</p> + +<p>You don't always need a driver and navigator, a set rotation time or dedicated allocated daily pair programming time.</p> + +<p>While that works for some teams, and on some occasions, you can do what's needed or feels right at the time to complete the task at hand.</p> + +<p>If you need a short pair programming session to fix a bug or finish a feature, to review some code before you merge it or submit it for code review, or want to pair ad-hoc or for one afternoon a week, that works, too.</p> + +<p>There isn't one way to do pair or mob programming.</p> + +<p>Do what works for you.</p> +
+
+ Tue, 02 Jan 2024 00:00:00 GMT +
+ + Continuous improvement + + /daily/2024/01/01/continuous-improvement + http://localhost:8000/daily/2024/01/01/continuous-improvement + +
<p>I've written emails before about continuous delivery and continuous deployment, but there's another "continuous" I like to follow.</p> + +<p>Continuous <strong>improvement</strong>.</p> + +<p>Always focusing on improving, whether learning new things or getting better with things I already know.</p> + +<p>If you haven't written automated tests or done test-driven development before, try it.</p> + +<p>Try using static analysis tools if you haven't before.</p> + +<p>If your tools or approaches are outdated, try something more modern.</p> + +<p>Can you reduce the time your CI pipeline takes to run, or write better documentation?</p> + +<p>You may not like everything you try, but what works will pay dividends over time.</p> +
+
+ Mon, 01 Jan 2024 00:00:00 GMT +
+ + Just... + /daily/2023/12/31/just + http://localhost:8000/daily/2023/12/31/just + +
<p>If you have a request that starts with "Just", it's likely a red flag!</p> + +<p>Why is it being made?</p> + +<p>What problem is it going to solve?</p> + +<p>Whose problem does it solve?</p> + +<p>Is the problem time-sensitive?</p> + +<p>What value does it add?</p> + +<p>Is it the best solution to fix the problem?</p> + +<p>Is it the best solution to fix the problem right now?</p> + +<p>To know this, we need to know why it's needed and it "just" needs doing.</p> +
+
+ Sun, 31 Dec 2023 00:00:00 GMT +
+ + We've always done it this way + /daily/2023/12/30/weve-always-done-it-this-way + http://localhost:8000/daily/2023/12/30/weve-always-done-it-this-way + +
<p>Two of the worst phrases I've heard when working with new development teams is "We've always done it this way" or "We've never needed to do this before".</p> + +<p>Whether it's writing automated tests, using static analysis, having a CI pipeline or using a specific framework or tool, this mindset prevents experimentation, growth and improvement.</p> + +<p>Instead, try to think about what benefits alternative approaches could offer and how they could improve any situations you're currently experiencing.</p> + +<p>You don't need to do it forever. Decide on something you want to try on a trial basis and set an end date.</p> + +<p>Then, when the trial is finished, you can decide if it achieved what you wanted, and if you want to continue with it.</p> + +<p>Even if you don't continue with it, you still learned something by trying.</p> +
+
+ Sat, 30 Dec 2023 00:00:00 GMT +
+ + Decide, automate, document + /daily/2023/12/29/decide-automate-document + http://localhost:8000/daily/2023/12/29/decide-automate-document + +
<p>Decide, automate, document</p> + +<p>Here are three steps to making decisions, such as introducing new tools and processes:</p> + +<p>Decide.</p> + +<p>Automate.</p> + +<p>Document.</p> + +<p>First of all, a decision needs to be made about what you will introduce.</p> + +<p>It could be whether to write automated tests, use static analysis, choose which coding standard to use, or make architecture decisions about how you want to build your application.</p> + +<p>Once you've decided and added the tool or process, automate it if you can.</p> + +<p>A CI pipeline or Git Hooks can run tests and checks automatically to know if the code complies with what was agreed upon rather than relying on this being done manually.</p> + +<p>Finally, document it so that it's available for others to read and reference, including new team members.</p> + +<p>Ensure to document why this was added, what problem it solves, any alternatives that were considered and any side effects or consequences. Technical design documents and ADRs (architectural decision records) are great for this!</p> + +<p>In the future, you may want to revisit the decision and decide if it's still correct, and you'll appreciate having the information documented.</p> +
+
+ Fri, 29 Dec 2023 00:00:00 GMT +
+ + Don't let pride get in the way of productivity + /daily/2023/12/28/pride-and-productivity + http://localhost:8000/daily/2023/12/28/pride-and-productivity + +
<p>Today, I was reading a support request on a public forum.</p> + +<p>The poster asked a question about a technical issue and explained the problem they were experiencing. I was experiencing the same thing, which is how I found it.</p> + +<p>A community member responded and suggested a solution.</p> + +<p>To this, the original poster responded:</p> + +<blockquote> + <p>Not too proud to ask where I [make the change].</p> +</blockquote> + +<p>The response to this was what caught my attention:</p> + +<blockquote> + <p>No problem. Don't let pride get in the way of productivity.</p> +</blockquote> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>Don't be afraid to ask questions or say you don't know or understand something.</p> + +<p>No one knows everything, and there's no such thing as a stupid question.</p> +
+
+ Thu, 28 Dec 2023 00:00:00 GMT +
+ + Writing new code is quick, to begin with + /daily/2023/12/27/writing-new-code-is-quick-to-begin-with + http://localhost:8000/daily/2023/12/27/writing-new-code-is-quick-to-begin-with + +
<p>When you first start a new application, adding new features is quick and easy.</p> + +<p>There's no existing code, and everything is written from scratch.</p> + +<p>As the project progresses, you need to account for and, in some cases, work around the existing code - ensuring the existing functionality continues to work.</p> + +<p>Even if a sole Developer is working on the project, inconsistencies start to be introduced.</p> + +<p>One feature was implemented in one way and the next in a different way.</p> + +<p>One follows a particular design pattern, and the other does not.</p> + +<p>There is code within the application that isn't used and is no longer needed but hasn't been removed - creating confusion and uncertainty.</p> + +<p>It can't be removed, just in case it does something and removing it breaks something elsewhere.</p> + +<p>A task that would have taken hours to complete when the application started now takes days, weeks or longer.</p> + +<p>Having automated tests, using tools like static analysis, and minimising technical debt helps to minimise this, as does having written technical design documents, ADRs or flow charts to ensure consistency and to document the "why" for future Developers.</p> + +<p>P.S. Is your New Year's resolution to deliver better software faster? <a href="http://localhost:8000/team-coaching">I have availability for team coaching</a> starting in January 2024 and can help you avoid issues like this in your applications.</p> +
+
+ Wed, 27 Dec 2023 00:00:00 GMT +
+ + Good code is not about being easy to write + + /daily/2023/12/26/good-code-is-not-about-being-easy-to-write + http://localhost:8000/daily/2023/12/26/good-code-is-not-about-being-easy-to-write + +
<p>Good code is not about being easy to write. It's about how easy it is to change.</p> + +<p>Once you've written some code, how easily and confidently could you change it in the future, either adding new features or fixing a bug?</p> + +<p>How easily could you refactor the code or remove it altogether if it's no longer needed?</p> +
+
+ Tue, 26 Dec 2023 00:00:00 GMT +
+ + A sneak peek of my Drupal automated testing course + + /daily/2023/12/25/zero-to-test + http://localhost:8000/daily/2023/12/25/zero-to-test + +
<p>Happy Christmas!</p> + +<p>Here's my present - a sneak peek at the first lesson in my free upcoming Automated Testing for Drupal email course.</p> + +<p>In this lesson, we start from scratch and end with a working test suite.</p> + +<p>If you like it, <a href="http://localhost:8000/atdc">register for free and get the full course</a> when it launches.</p> + +<h2 id="creating-a-drupal-project">Creating a Drupal project</h2> + +<p>If you don't have one, you'll need to create a new Drupal project. I'd suggest using Drupal 10.2 and the instructions at <a href="https://www.drupal.org/download">https://www.drupal.org/download</a>.</p> + +<p>You'll need <a href="https://www.php.net/manual/en/install.php">PHP</a> and <a href="https://getcomposer.org/doc/00-intro.md">Composer</a>.</p> + +<p>First, run <code>composer create-project drupal/recommended-project drupal</code> followed by <code>cd drupal &amp;&amp; composer require --dev drupal/core-dev</code> to add the development dependencies, including PHPUnit.</p> + +<p>At this point, you should have a <code>web</code> directory and a <code>phpunit</code> file within <code>vendor/bin</code>.</p> + +<p>Finally, run <code>php -S 0.0.0.0:8000 -t web</code> to start a local web server.</p> + +<p>You don't need to install Drupal - as long as you see the installation page, that's fine.</p> + +<h2 id="creating-a-custom-module">Creating a custom module</h2> + +<p>Before adding tests, you must create a module to place them in.</p> + +<p>Run <code>mkdir -p web/modules/custom/atdc</code> to create an empty module directory, and create an <code>atdc.info.yml</code> file within it with this content:</p> + +<pre><code class="language-yaml">name: Example +type: module +core_version_requirement: ^10 +package: Custom +</code></pre> + +<p>This is the minimum content needed for a module to be installable.</p> + +<h3 id="writing-your-first-test-class">Writing your first test class</h3> + +<p>Test classes are placed within each module's <code>tests/src</code> directory.</p> + +<p>Run <code>mkdir -p web/modules/custom/atdc/tests/src/Functional &amp;&amp; touch web/modules/custom/atdc/tests/src/Functional/ExampleTest.php</code> to create the directory structure and a blank test class.</p> + +<p>Then, add this content.</p> + +<pre><code class="language-php">&lt;?php + +namespace Drupal\Tests\atdc\Functional; + +use Drupal\Tests\BrowserTestBase; +use Symfony\Component\HttpFoundation\Response; + +class ExampleTest extends BrowserTestBase { + +  public $defaultTheme = 'stark'; + +} +</code></pre> + +<p>Note: within a test class, the namespace is <code>Drupal\Tests\{module_name}</code> instead of <code>Drupal\{module_name}</code>.</p> + +<p>With the boilerplate class added, create a test method within it:</p> + +<pre><code class="language-php">public function testBasic(): void { +  self::assertTrue(FALSE); +} +</code></pre> + +<p>Note: the class name must be suffixed with <code>Test</code>, and the test method must be prefixed with <code>test</code> for them to be run.</p> + +<p>Now we have a test with an assertion, we need to run it and see if it passes.</p> + +<h2 id="running-the-test">Running the test</h2> + +<p>On the command line, run <code>vendor/bin/phpunit web/modules/custom</code>, and you'll get an error like:</p> + +<blockquote> + <p>PHPUnit\TextUI\RuntimeException: Class "Drupal\Tests\BrowserTestBase" not found.</p> +</blockquote> + +<p>This isn't an assertion failure, but that PHPUnit can't find the files it needs to run.</p> + +<p>To fix this, let's configure PHPUnit.</p> + +<h2 id="configuring-phpunit">Configuring PHPUnit</h2> + +<p>Create a new <code>phpunit.xml.dist</code> file at the root of your project, with this content:</p> + +<pre><code class="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt; +&lt;phpunit bootstrap="web/core/tests/bootstrap.php" colors="true"&gt; + &lt;php&gt; + &lt;env name="SIMPLETEST_BASE_URL" value="http://localhost:8000"/&gt; + &lt;env name="SIMPLETEST_DB" value="sqlite://localhost//dev/shm/test.sqlite"/&gt; + &lt;ini name="error_reporting" value="32767"/&gt; + &lt;ini name="memory_limit" value="-1"/&gt; + &lt;/php&gt; + &lt;testsuites&gt; + &lt;testsuite name="Example tests"&gt; + &lt;directory suffix="Test.php"&gt;./web/modules/**&lt;/directory&gt; + &lt;/testsuite&gt; + &lt;/testsuites&gt; +&lt;/phpunit&gt; +</code></pre> + +<p>This is based on <code>web/core/phpunit.xml.dist</code> with some project-specific changes.</p> + +<p>Namely, setting the <code>bootstrap</code> value to include the <code>web/core</code> path and fix the error, and populating the <code>SIMPLETEST_BASE_URL</code> and <code>SIMPLETEST_DB</code> environment variables.</p> + +<p>PHPUnit now knows where the files are, to connect to Drupal at <a href="http://localhost:8000">http://localhost:8000</a> (matching the PHP web server address) and an SQLite database.</p> + +<p>I've also added a <code>testsuite</code> that declares where any test classes will be located so the path doesn't need to be specified on the command line.</p> + +<h2 id="re-running-the-tests">Re-running the tests</h2> + +<p>Running the tests again will give the expected error about a failing assertion:</p> + +<blockquote> + <p>Failed asserting that false is true.</p> +</blockquote> + +<p>Fix the assertion in the test by changing <code>FALSE</code> to <code>TRUE</code>, run <code>vendor/bin/phpunit</code> again, and you should see a passing test.</p> + +<blockquote> + <p>OK (1 test, 2 assertions)</p> +</blockquote> + +<h2 id="improving-the-tests">Improving the tests</h2> + +<p>Now you have as passing test and know PHPUnit is working, let's improve it.</p> + +<p>Instead of the basic check, let's check whether certain pages exist and are accessible.</p> + +<p>To keep things simple and focused on writing and running tests, let's use some standard Drupal pages - the front and administration pages instead of writing your own.</p> + +<p>As you're writing functional tests by extending <code>BrowserTestBase</code>, you can make HTTP requests to the web server, and make assertions on the responses.</p> + +<p>Replace the <code>testBasic</code> test method with the following:</p> + +<pre><code class="language-php">public function testFrontPage(): void { + $this-&gt;drupalGet('/'); + + $assert = $this-&gt;assertSession(); + $assert-&gt;statusCodeEquals(Response::HTTP_FORBIDDEN); +} + +public function testAdminPage(): void { + $this-&gt;drupalGet('/admin'); + + $assert = $this-&gt;assertSession(); + $assert-&gt;statusCodeEquals(Response::HTTP_OK); +} +</code></pre> + +<p>These tests will make HTTP requests to the specified paths and assert the status code on the response matches the expected values.</p> + +<p>I'm using the constants on the <code>Response</code> class, but you can also use the status code numbers - e.g. <code>200</code> and <code>403</code>.</p> + +<h2 id="running-the-updated-tests">Running the updated tests</h2> + +<p>Running <code>vendor/bin/phpunit</code>, you'll get two errors:</p> + +<blockquote> + <p>1) Drupal\Tests\atdc\Functional\ExampleTest::testFrontPage + Behat\Mink\Exception\ExpectationException: Current response status code is 200, but 403 expected.</p> + + <p>2) Drupal\Tests\atdc\Functional\ExampleTest::testAdminPage + Behat\Mink\Exception\ExpectationException: Current response status code is 403, but 200 expected.</p> + + <p>ERRORS!<br /> + Tests: 2, Assertions: 4, Errors: 2.</p> +</blockquote> + +<p>The responses are not returning the expected status codes, so the tests are failing.</p> + +<p>Reviewing them, the front page should return a 200 response code (<code>HTTP_OK</code>) as it's accessible to all users, including anonymous users.</p> + +<p>As we're logged out, the administration page should return a 403 (<code>HTTP_FORBIDDEN</code>).</p> + +<p>Swapping the assertions should get the tests to pass.</p> + +<p>Now, running <code>vendor/bin/phpunit</code> returns no errors or failures.</p> + +<blockquote> + <p>OK (2 tests, 4 assertions)</p> +</blockquote> + +<p>Congratulations!</p> + +<h2 id="conclusion">Conclusion</h2> + +<p>In this lesson, you've created a new Drupal 10 project, configured PHPUnit and created a custom module with your first passing browser tests.</p> + +<p>From this, you can hopefully see that automated testing doesn't need to be difficult, and the configuration you've done here will work for the upcoming lessons, where you'll expand on what you've done and explore more that Drupal and PHPUnit have to offer.</p> + +<p>I hope you enjoyed this sneak peek, and if you'd like to receive the course once it's complete, <a href="http://localhost:8000/atdc">register here for free</a>.</p> +
+
+ Mon, 25 Dec 2023 00:00:00 GMT +
+ + This should never happen + + /daily/2023/12/24/this-should-never-happen + http://localhost:8000/daily/2023/12/24/this-should-never-happen + +
<p>How often do you see comments like "This should never happen" in a software codebase?</p> + +<p>If that's true, why is it there?</p> + +<p>If it truly should never happen, the additional code only adds more noise and distracts from the code that is run.</p> + +<p>You can add a test that checks a method isn't called, but what value does this offer?</p> + +<p>If it doesn't happen or some code isn't run, remove it and keep it as simple, clean and easy to understand as possible.</p> +
+
+ Sun, 24 Dec 2023 00:00:00 GMT +
+ + Using a whole framework or part of it + + /daily/2023/12/23/using-a-whole-framework-or-part-of-it + http://localhost:8000/daily/2023/12/23/using-a-whole-framework-or-part-of-it + +
<p><a href="http://localhost:8000/daily/2023/12/22/best-language-cms-or-framework">In yesterday's email</a>, I mentioned using several programming languages and frameworks.</p> + +<p>Drupal is my main specialism and the one I have the most experience and knowledge of, but you don't need to use only Drupal code.</p> + +<p>Using Composer, you can add parts of other frameworks to other projects.</p> + +<p>I don't use Laravel often as a full-stack framework, but I use the Collections library, and recently the Pipelines library, in almost every project - whether it's Drupal or Symfony, it can be added to any PHP project.</p> + +<p>Start small, keep things simple, and add what you need.</p> + +<p>You don't need to go all in on one option, you can pick the pieces you like and that work for you.</p> +
+
+ Sat, 23 Dec 2023 00:00:00 GMT +
+ + Which is the best programming language, CMS or framework? + + /daily/2023/12/22/best-language-cms-or-framework + http://localhost:8000/daily/2023/12/22/best-language-cms-or-framework + +
<p>I started developing websites in HTML and CSS in 2007 before adopting PHP and Drupal in 2008.</p> + +<p>Since then, I've used Drupal 6, 7, 8, 9 and 10 to deliver applications for clients, agencies and in-house teams.</p> + +<p>I also use Symfony and Laravel, as well as JavaScript, TypeScript and Vue.js.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>There isn't one outright "best" programming language, CMS or framework.</p> + +<p>It depends on the project and its requirements as well as the knowledge and experience of the team working on it.</p> +
+
+ Fri, 22 Dec 2023 00:00:00 GMT +
+ + New year coaching + + /daily/2023/12/21/new-year-coaching + http://localhost:8000/daily/2023/12/21/new-year-coaching + +
<p>I have availability to work with a small number of software development teams in the new year and help them ship better software, faster, using tools and techniques like automated testing, test-driven development and static analysis.</p> + +<p>Would you like to work with me?</p> +
+
+ Thu, 21 Dec 2023 00:00:00 GMT +
+ + hover + focus = hocus + + /daily/2023/12/20/hover-focus-hocus + http://localhost:8000/daily/2023/12/20/hover-focus-hocus + +
<p>When creating accessible websites, as well as hover states for focusable elements, such as buttons, you also need to add focus styles that apply when users navigate the page using a keyboard and focusing on an element.</p> + +<p>With Tailwind, that can mean a lot of duplication if your hover and focus states are similar or the same, as the same classes need to be added with both the <code>:hover</code> and <code>:focus</code> variants.</p> + +<p>One of Tailwind's best features is its extensibility, which means I can create a new interaction state - <code>:hocus</code> - that works for both.</p> + +<p>It's very easy to do by adding this code to your <code>tailwind.config.js</code> or <code>tailwind.config.ts</code> file:</p> + +<pre><code class="javascript">const plugin = require("tailwindcss/plugin"); + +module.exports = plugin(({ addVariant }) =&gt; { +  addVariant("hocus", ["&amp;:hover", "&amp;:focus"]); +}); +</code></pre> + +<p>Or, use <a href="https://www.npmjs.com/package/tailwindcss-plugin-hocus-state">the Tailwind CSS plugin I wrote</a>.</p> + +<p>Get more accessible websites and less duplication today!</p> +
+
+ Wed, 20 Dec 2023 00:00:00 GMT +
+ + PHP TUIs, CLIs and open-source with Dan Leech + + /daily/2023/12/19/php-tui-dan-leech + http://localhost:8000/daily/2023/12/19/php-tui-dan-leech + +
<p>This week on the <a href="http://localhost:8000/podcast">Beyond Blocks podcast</a>, I'm joined by Dan Leech - a PHP Developer and open-source project creator.</p> + +<p>He and I recently gave talks at the PHP South West meetup, where Dan introduced a new project - PHP-TUI - for building terminal user interfaces (TUIs) with PHP.</p> + +<p>I use one of Dan's other open-source projects - Phpactor - within Neovim, and he also presented at PHP South Wales about PHPBench, so it was great to discuss and learn more about these in this episode.</p> + +<p><a href="http://localhost:8000/podcast/6-dan-leech-php-tui">Listen to the episode now</a>, and I'll be back with more in the New Year.</p> +
+
+ Tue, 19 Dec 2023 00:00:00 GMT +
+ + Should you run static analysis on your tests? + + /daily/2023/12/18/static-analysis-on-tests + http://localhost:8000/daily/2023/12/18/static-analysis-on-tests + +
<p>I'm an advocate of both automated testing and static analysis but have mostly kept the two separate.</p> + +<p>I've typically not run PHPStan on my automated test files - ignoring them in my PHPStan configuration.</p> + +<p>As tests aren't production/implementation code, what's the benefit of analysing them?</p> + +<p>Recently, though, I've challenged this and, on some projects, started to run PHPStan on my test classes.</p> + +<p>Depending on what level PHPStan is running, the more changes you're likely to have to make to get your test classes to pass the static analysis, and whilst it results in more verbose and explicit code, I prefer that and being able to easily understand what it does rather than implicit.</p> + +<p>If you have a bug in your test, static analysis will potentially find that, too, making your test suite more robust.</p> + +<p>Looking at some large open-source PHP projects, they run static analysis on their test code.</p> + +<p>Presumably, they're benefiting from it, so I'll try it and see if I get the same.</p> +
+
+ Mon, 18 Dec 2023 00:00:00 GMT +
+ + Fail fast, fix fast + + /daily/2023/12/17/fail-fast-fix-fast + http://localhost:8000/daily/2023/12/17/fail-fast-fix-fast + +
<p>I recently listened to a podcast that discussed Elon Musk and quoted something like, "If 20% of attempts aren't failing, you aren't taking enough risk".</p> + +<p>In a software context, I'm not advocating that one in five production releases should fail, but I like trying new ideas and approaches.</p> + +<p>If you're releasing small changes regularly or practising continuous deployment, changes are easy to revert if there's a problem or the smaller the deployment and the more recently the code was written, then it should be easier to resolve the issue and "fix forward" instead of rolling back.</p> + +<p>Using feature flags lets you quickly turn off a feature flag while investigating and resolving the issue without needing another deployment.</p> + +<p>If you have an appropriate plan to follow in the case of an issue, that mitigates the risk and minimises the impact of a potential issue - making it quicker to resolve and restore the service.</p> + +<p>Two of the DORA metrics refer to failure rate and restoration time:</p> + +<ul> +<li>Deployment frequency</li> +<li>Lead time for changes</li> +<li>Change failure rate</li> +<li>Time to restore service</li> +</ul> + +<p>Then, it depends on your organisation's tolerance for risk and what's acceptable.</p> + +<p>But, the more frequent the releases, the lower the failure rate and the quicker it will be to restore the service if there is an issue.</p> +
+
+ Sun, 17 Dec 2023 00:00:00 GMT +
+ + Adding snapshot tests to Build Configs + + /daily/2023/12/16/adding-snapshot-tests-to-build-configs + http://localhost:8000/daily/2023/12/16/adding-snapshot-tests-to-build-configs + +
<p>This week, I've started to add snapshot tests to the <a href="https://www.oliverdavies.dev/build-configs">Build Configs project</a>.</p> + +<p>I had unit tests for the DTO validation to ensure the configuration was correct, but the main thing I wanted to test was I could run it for a given configuration file and get the expected files and contents to be generated.</p> + +<p>With snapshot tests, I generate the files for each configuration and compare them to a set that I know to be correct.</p> + +<p>If the files match, the tests pass, but, if they don't - such as a bug in the code, the tests will fail.</p> + +<p>This is the level that I want to be testing this project and that provides the most value.</p> + +<p>If a snapshot test fails, I can try to replicate the underlying issue in a unit test whilst also fixing the snapshot test.</p> +
+
+ Sat, 16 Dec 2023 00:00:00 GMT +
+ + Building your own in-house Drupal distribution + + /daily/2023/12/15/building-your-own-in-house-drupal-distribution + http://localhost:8000/daily/2023/12/15/building-your-own-in-house-drupal-distribution + +
<p>On several occasions, I've seen companies who build a lot of Drupal projects creating their own in-house [Drupal distribution] for new projects.</p> + +<p>Whilst this seems like a good idea, the ones I've seen are either not well maintained due to limited time between projects, or they're bloated with features and trying to include too much out of the box.</p> + +<p>If you maintain an in-house distribution, or are thinking of creating one, my advice is to ensure it's maintained by allocating enough time for this, and to keep it lean and only include the minimum amount of required functionality.</p> + +<p>Alternatively, maybe keep a template composer.json file to base new projects on instead of a full distribution.</p> + +<p>That could declare the modules and themes you want to include, without the additional overhead.</p> + +<p><a href="http://localhost:8000/daily/2023/12/14/save-time-and-effort-with-drupal-distributions">drupal distribution</a></p> +
+
+ Fri, 15 Dec 2023 00:00:00 GMT +
+ + Save time and effort with Drupal distributions + + /daily/2023/12/14/save-time-and-effort-with-drupal-distributions + http://localhost:8000/daily/2023/12/14/save-time-and-effort-with-drupal-distributions + +
<p>Drupal distributions are pre-built versions of Drupal for specific use cases.</p> + +<p>Need an eCommerce store? Use Commerce Kickstart.</p> + +<p>Are you a local council looking for a new website? Look at LocalGov Drupal.</p> + +<p>Contenta CMS is an API-first Drupal distribution for headless and decoupled content.</p> + +<p>There are numerous others - each containing a list of pre-installed and configured modules to give you more functionality out of the box.</p> + +<p>If you're starting a new project, try starting with an appropriate distribution instead of starting from scratch with a standard installation of Drupal core and save yourself time and effort.</p> + +<p>Don't know if using a distribution could help for you? <a href="http://localhost:8000/call">Book a 1-on-1 consulting call</a>, and I'll help you out.</p> +
+
+ Thu, 14 Dec 2023 00:00:00 GMT +
+ + Reviving an old PHP project + + /daily/2023/12/13/reviving-an-old-php-project + http://localhost:8000/daily/2023/12/13/reviving-an-old-php-project + +
<p>I use Gmail/Google Apps for my email address, and I used to use a LOT of filters.</p> + +<p>Inspired by a Ruby gem, I created a PHP library that allows me to declare my filters in PHP and create them in XML.</p> + +<p>I could import the generated XML into Gmail to create the filters.</p> + +<h2 id="gmail-filter-buider">Gmail Filter Buider</h2> + +<p>The project is <a href="https://github.com/opdavies/gmail-filter-builder/tree/3.x">Gmail Filter Builder</a>, and the last commit was in July 2020.</p> + +<p>It was based on PHP 7 (unsupported since November 2022) and uses five Symfony components and several other packages.</p> + +<p>Today, as I looked at my current Gmail filters, I decided to revisit and update Gmail Filter Builder.</p> + +<h2 id="updating-the-project">Updating the project</h2> + +<p>I set PHP 8.1 as the minimum required version and used a Nix Flake to ensure this was available.</p> + +<p>I updated the Symfony components from the 3.x versions to 6.x, the latest versions compatible with PHP 8.1. I also updated the other PHP packages to their compatible versions.</p> + +<p>After some small changes to the application's code, it worked with PHP 8.1 and generated filters.</p> + +<p>Once it worked, I updated <a href="https://github.com/opdavies/gmail-filter-builder/tree/3.x/examples">the examples</a>.</p> + +<p>Whilst it wasn't a difficult process in this instance, having a test suite I could rely on helped me to know it worked as expected during the upgrade.</p> + +<p>I don't know if I'll continue to use or maintain Gmail Filter Builder again, but it was an interesting exercise to do this morning.</p> +
+
+ Wed, 13 Dec 2023 00:00:00 GMT +
+ + DrupalCon session survey results + + /daily/2023/12/12/drupalcon-session-survey-results + http://localhost:8000/daily/2023/12/12/drupalcon-session-survey-results + +
<p>The survey results from my automated testing and test-driven development session at DrupalCon Lille are in, and here they are:</p> + +<h2 id="how-would-you-rate-the-speakers%27s-mastery-of-this-topic%3F">How would you rate the speaker(s)'s mastery of this topic?</h2> + +<p>24 responses.</p> + +<ul> +<li>Excellent - 33.3%</li> +<li>Very good - 50%</li> +<li>Good - 16.7%</li> +<li>Fair - 0%</li> +<li>Poor - 0%</li> +</ul> + +<h2 id="how-would-you-rate-the-speakers%27s-presentation-skills%3F">How would you rate the speaker(s)'s presentation skills?</h2> + +<p>24 responses.</p> + +<ul> +<li>Excellent - 20.8%</li> +<li>Very good - 37.5%</li> +<li>Good - 33.3%</li> +<li>Fair - 8.3%</li> +<li>Poor - 0%</li> +</ul> + +<h2 id="how-would-you-rate-the-speakers%E2%80%99s-slides-and-other-session-materials%3F">How would you rate the speaker(s)’s slides and other session materials?</h2> + +<p>25 responses.</p> + +<ul> +<li>Excellent - 24%</li> +<li>Very good - 32%</li> +<li>Good - 36%</li> +<li>Fair - 8%</li> +<li>Poor - 0%</li> +</ul> + +<h2 id="overall%2C-how-would-you-rate-this-session%3F">Overall, how would you rate this session?</h2> + +<p>25 responses.</p> + +<ul> +<li>Excellent - 36%</li> +<li>Very good - 28%</li> +<li>Good - 36%</li> +<li>Fair - 0%</li> +<li>Poor - 0%</li> +</ul> + +<p>Thanks to everyone who completed the survey for their feedback.</p> + +<p>P.S. If you'd like your own software development training or coaching, including automated testing and test-driven development, I currently have availability for <a href="http://localhost:8000/team-coaching">team coaching</a> and <a href="http://localhost:8000/pricing">private talks and workshops</a>.</p> +
+
+ Tue, 12 Dec 2023 00:00:00 GMT +
+ + Custom coding standards and conventions + + /daily/2023/12/11/custom-coding-standards-and-conventions + http://localhost:8000/daily/2023/12/11/custom-coding-standards-and-conventions + +
<p>Open-source projects like Drupal and Symfony have their own published coding standards and conventions.</p> + +<p>For example, from <a href="https://symfony.com/doc/current/contributing/code/standards.html">Symfony's coding standards</a>:</p> + +<ul> +<li>Prefix all abstract classes with <code>Abstract</code> except PHPUnit <code>*TestCase</code>.</li> +<li>Suffix interfaces with <code>Interface</code>;</li> +<li>Suffix traits with <code>Trait</code>;</li> +</ul> + +<p>And from <a href="https://www.drupal.org/docs/develop/standards/php/php-coding-standards">Drupal's</a>:</p> + +<ul> +<li>Use an indent of 2 spaces, with no tabs.</li> +<li>Lines should have no trailing whitespace at the end.</li> +<li>Variables should be named using lowercase, and words should be separated either with uppercase characters (example: <code>$lowerCamelCase</code>) or with an underscore (example: <code>$snake_case</code>). Be consistent; do not mix camelCase and snake_case variable naming inside a file.</li> +</ul> + +<p>But what about within custom applications?</p> + +<p>Do you have your own agreed coding standards and conventions to keep the code consistent?</p> + +<p>Do you explicitly follow the published coding standards, customise them, or follow something else?</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>Do you know where to put new custom modules, how to name them and what conventions to follow when writing code?</p> + +<p>Do you know where to add a new stylesheet to your theme?</p> + +<p>If not, or it's implied, it's worth writing it down and being explicit - either within your project's or company's documentation or publicly.</p> +
+
+ Mon, 11 Dec 2023 00:00:00 GMT +
+ + Suffixing names + + /daily/2023/12/10/suffixing-names + http://localhost:8000/daily/2023/12/10/suffixing-names + +
<p>When writing code, it's common to suffix the class name with the type of class it is.</p> + +<p>I do this often for most types of classes, such as Controllers, EventListeners, Factories, Repositories and Builders.</p> + +<p>However, I don't always for value objects and data transfer objects (DTOs).</p> + +<p>Also, I don't always suffix interfaces with <code>Interface</code>.</p> + +<p>I've been re-reading the documentation for the Symfony Serializer component, which references both <code>NameConverterInterface</code> and <code>MyDto::class</code>.</p> + +<p>Whilst it does make the names more verbose, it does clarify what the class is used for.</p> + +<p>Symfony's coding standards and Drupal's PHP coding standards have conventions for this, but what do you think?</p> + +<p>Do you add suffixes to your class or interface names, or do you prefer the simplified versions?</p> +
+
+ Sun, 10 Dec 2023 00:00:00 GMT +
+ + Rebuild or iterate + + /daily/2023/12/09/rebuild-or-iterate + http://localhost:8000/daily/2023/12/09/rebuild-or-iterate + +
<p>As I said in <a href="http://localhost:8000/daily/2023/12/08/dont-just-rewrite">yesterday's email</a>, I'm discussing with a team how to implement their new website design.</p> + +<p>Their website has been live for a couple of years, and now they want to refresh its look and feel.</p> + +<p>The current theme includes a lot of technical debt and legacy code, such as using an older CSS framework and build tools that need unsupported versions of packages to compile it and generate the scripts and stylesheets.</p> + +<p>There are two ways they could approach this:</p> + +<p>They could continue to iterate on the current theme, making and releasing small changes to move forward in small steps and slowly repaying the technical debt.</p> + +<p>Alternatively, they could create a new theme from scratch.</p> + +<p>This would allow them to start with new build tools, a modern CSS framework and small and simple stylesheets.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>There are advantages and disadvantages to both approaches.</p> + +<p>Iterating on the original theme allows for continuous improvement, but it would take longer to remove the old frameworks and build tools - if that would be possible at all.</p> + +<p>Rebuilding and re-inventing would mean immediately avoiding the technical debt and legacy tools, but it comes with its own risks, and you'd need to wait until the whole theme was completed before it could be launched.</p> + +<p>There isn't a right and wrong answer, and it will depend on the thoughts and objectives of the team and business.</p> +
+
+ Sat, 09 Dec 2023 00:00:00 GMT +
+ + Don't just rewrite. Re-invent. + + /daily/2023/12/08/dont-just-rewrite + http://localhost:8000/daily/2023/12/08/dont-just-rewrite + +
<p>A quote that stuck out to me from today's Neovimconf conference was this by Björn Linse (aka bfredl):</p> + +<blockquote> + <p>Don't just rewrite. Re-invent.</p> + + <p>If you're going to start over from scratch, reconsider everything.</p> +</blockquote> + +<p>This was in the context of what would be added to the core editor, what would be delegated to plugins, etc, but this also applies to other things.</p> + +<p>I'm discussing with someone whether to build a new Drupal theme or keep iterating on the existing one.</p> + +<p>If they start from scratch, they can re-invent and re-consider everything and decide which parts of the existing theme to reuse and which to leave and re-create.</p> +
+
+ Fri, 08 Dec 2023 00:00:00 GMT +
+ + A Drupal case study from Oxfam + + /daily/2023/12/07/a-drupal-case-study-from-oxfam + http://localhost:8000/daily/2023/12/07/a-drupal-case-study-from-oxfam + +
<p>In the latest episode of the Beyond Blocks podcast, Ed Crompton and I discuss his case study of upgrading 15 websites for Oxfam on how to build and test a multilingual, multisite Drupal migration.</p> + +<p>We discussed focussing on delivering value, prioritising based on usefulness to users and stakeholders, Agile vs agile, and how they pivoted from a "big bang" approach to delivering rapid increments in smaller batches.</p> + +<p><a href="http://localhost:8000/podcast/4-ed-crompton-oxfam-case-study">Listen to the episode now</a>.</p> +
+
+ Thu, 07 Dec 2023 00:00:00 GMT +
+ + Open-source first doesn't mean you need to cover every use case + + /daily/2023/12/06/open-source-first-doesnt-mean-you-need-to-cover-every-use-case + http://localhost:8000/daily/2023/12/06/open-source-first-doesnt-mean-you-need-to-cover-every-use-case + +
<p>An argument against the <a href="http://localhost:8000/daily/2023/12/01/the-contribution-first-workflow">contribution-first and open-source-first approach</a> is that it takes longer than writing custom code.</p> + +<p>I think that this is due to thinking that you need to cover all use cases within the code if it's open-sourced, whereas, in custom code, you only write the code you need.</p> + +<p>My approach is to write the same code, whether it's private and custom or open-sourced.</p> + +<p>The code is based on the same set of requirements, and the only code that should be written should be enough to satisfy those requirements.</p> + +<p>It doesn't get written if something isn't part of that objective.</p> + +<p>It could be added to a public roadmap for the future if it doesn't need to be part of the initial minimal version.</p> + +<p>If someone creates an issue to request new functionality or submits a pull request to contribute potential changes, you decide whether to accept or reject it or even add them as a maintainer to manage their own contributions as well as administrative tasks like managing the issue queues.</p> + +<p>If you accept their code, you get the benefit of it (though you also need to maintain and own it) or, if you reject it, you can continue focusing on your minimum-viable version for its initial project.</p> +
+
+ Wed, 06 Dec 2023 00:00:00 GMT +
+ + Open-source encourages more open-source + + /daily/2023/12/05/open-source-encourages-open-source + http://localhost:8000/daily/2023/12/05/open-source-encourages-open-source + +
<p><a href="http://localhost:8000/daily/2023/12/04/writing-contrib-modules-as-glue-between-your-custom-code">In yesterday's email</a>, I mentioned the Private Message Queue module - a contributed Drupal module we wrote for a project as part of a contribution-first workflow.</p> + +<p>In our experience, doing that and releasing Private Message Queue as its own open-source project encouraged more open-source contributions.</p> + +<p>We started to ask questions like, "Which user should the messages be sent from?".</p> + +<h2 id="system-users">System Users</h2> + +<p>This led us to create the <a href="https://www.drupal.org/project/system_user">System User module</a>.</p> + +<p>Inspired by system users in Linux, it provides a way to identify and retrieve system users that aren't tied to individuals' accounts and without relying on "magic" user IDs.</p> + +<p>But what if a website doesn't have a system user?</p> + +<h2 id="null-users">Null Users</h2> + +<p>This led to the <a href="https://www.drupal.org/project/null_user">Null User module</a>.</p> + +<p>Following the Null object pattern, if there isn't a system user, instead of returning <code>NULL</code> or <code>FALSE</code>, you return a null user that you use in the same way, though they'll have default empty values and won't perform any actions.</p> + +<p>This pattern simplifies your code as you don't need to check for <code>NULL</code> or <code>FALSE</code> values.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>If I remember correctly, as part of the project, we created and released around ten new contributed modules to Drupal.org.</p> + +<p>We were able to move straight onto the next phase of the project.</p> + +<p>We didn't need to clean them up or refactor them beforehand. We didn't need to dedicate any additional time as they were already released.</p> +
+
+ Tue, 05 Dec 2023 00:00:00 GMT +
+ + Writing contrib modules as glue between your custom code + + /daily/2023/12/04/writing-contrib-modules-as-glue-between-your-custom-code + http://localhost:8000/daily/2023/12/04/writing-contrib-modules-as-glue-between-your-custom-code + +
<p>A few years ago, I worked on an event booking and management website for a charity in the UK.</p> + +<p>One required piece of functionality was to allow event organisers to send a private message to all attendees of an event.</p> + +<p>We decided to use the <a href="https://www.drupal.org/project/private_message">Private Message module</a> but were concerned about hitting memory limits or timeouts, as some events had several hundred attendees.</p> + +<p>We decided that we needed to use a queue and have it process and send the messages.</p> + +<p>This split the feature into three parts:</p> + +<ul> +<li>The UI for event organisers to enter and send their messages.</li> +<li>The queue that stores and processes pending messages in the background.</li> +<li>The Private Message module that would create and send each message, provide an inbox for each user, and provide notifications.</li> +</ul> + +<p>The UI for event organisers was specific to the project and had to be custom code and not open-sourced.</p> + +<p>The Private Message module is already a contributed module, but what about the queue part?</p> + +<p>This is why the <a href="https://www.drupal.org/project/private_message_queue">Private Message Queue module</a> was created.</p> + +<p>Instead of keeping it within the project's custom code, we created it as an open-source module on Drupal.org.</p> + +<p>It contains no customer or project-specific information and only adds generic functionality - it creates a queue to hold private messages to be processed in the background.</p> + +<p>It provides the glue between our custom UI and the Private Message module.</p> + +<p>As there was no reason why we needed to keep it private, we made it public and developed it as its own project from the beginning instead of planning and hoping to do so later.</p> +
+
+ Mon, 04 Dec 2023 00:00:00 GMT +
+ + The contribution-first workflow + + /daily/2023/12/01/the-contribution-first-workflow + http://localhost:8000/daily/2023/12/01/the-contribution-first-workflow + +
<p>I've worked on many software projects with a lot of custom code.</p> + +<p>Not all the code is specific to that client or project, and often, code is identified as it can be extracted from the project and open-sourced as a Drupal module, PHP or JavaScript library, or Tailwind CSS plugin.</p> + +<p>Usually, the code is written as custom code initially, with the best intentions to revisit it once the project is complete and open-source it.</p> + +<p>But this rarely happens, as there's always the next sprint or project waiting.</p> + +<p>It takes too long to extract the code as it usually needs to be tidied or refactored beforehand.</p> + +<p>It may have been written with the client or project name within the code, which needs changing.</p> + +<p>My suggestion is to avoid this step.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>Instead of writing it as custom code and hopefully extracting it later, start it as a separate module, library or plugin, and use Composer or npm to add it to your project as another dependency.</p> + +<p>Whilst it's a slightly smaller overhead, it's better and less risky than rewriting or refactoring code later and it's already open-sourced.</p> + +<p>Plus, you may get some issues, testing and improvements from others along the way.</p> +
+
+ Fri, 01 Dec 2023 00:00:00 GMT +
+ + Are bugs good for users? + + /daily/2023/11/30/are-bugs-good-for-users + http://localhost:8000/daily/2023/11/30/are-bugs-good-for-users + +
<p>I recently listened to a podcast episode that was discussing if bugs are good for users.</p> + +<p>It suggested that bugs allowed for engagement between you and the user.</p> + +<p>If someone reports a bug, you can tell them when you've fixed it. This encourages communication, and the customer will feel more connected to the product.</p> + +<p>While this may be true, I think that users would prefer applications that just work.</p> + +<p>If your application has major bugs, they may just go elsewhere. Maybe they won't even report the issue to you.</p> + +<p>Major bugs can damage your reputation with your users and potential customers, as well as affect potential sales.</p> + +<p>If you can avoid them to begin with, that's what I'd suggest. That's what tools and processes such as automated testing, test-driven development and static analysis are for.</p> + +<p>When a user reports it (if they do at all), it could be too late.</p> +
+
+ Thu, 30 Nov 2023 00:00:00 GMT +
+ + The lowest level is better than no level + + /daily/2023/11/29/the-lowest-level-is-better-than-no-level + http://localhost:8000/daily/2023/11/29/the-lowest-level-is-better-than-no-level + +
<p><a href="http://localhost:8000/daily/2023/11/28/which-phpstan-level-should-you-use">Yesterday's email</a> introduced the different levels that PHPStan offers and which you may want to use on your codebase.</p> + +<p>In the same way as having a single test is better than none, even if you run PHPStan at the lowest level, it's better than not running it at all.</p> + +<p>Level 0 includes:</p> + +<blockquote> + <p>Basic checks, unknown classes, unknown functions, unknown methods called on $this, wrong number of arguments passed to those methods and functions, always undefined variables</p> +</blockquote> + +<p>P.S. If you want to utilise PHPStan and static analysis on your development team and get fewer bugs, I have <a href="http://localhost:8000/team-coaching">availability for team coaching</a>. I'll show you how!</p> +
+
+ Wed, 29 Nov 2023 00:00:00 GMT +
+ + Which PHPStan level should you use? + + /daily/2023/11/28/which-phpstan-level-should-you-use + http://localhost:8000/daily/2023/11/28/which-phpstan-level-should-you-use + +
<p>Which PHPStan level should you use?</p> + +<p>PHPStan has different levels.</p> + +<p>When you run it on your code, the errors you see will depend on what level you set.</p> + +<p><a href="http://localhost:8000/daily/2023/11/27/finding-the-best-test-base">In yesterday's email</a>, the first example code block didn't generate an error until level 5 was used.</p> + +<p>So, how do you know which level to use?</p> + +<h2 id="for-greenfield-projects">For greenfield projects</h2> + +<p>For new (greenfield) code, install and configure PHPStan before you write any code and have and have it run automatically as part of a CI pipeline.</p> + +<p>If you work on a team, speak with the other members and decide how strict you want PHPStan to be.</p> + +<p>Read the rule levels and decide which are the most valuable for your team.</p> + +<p>If you haven't used PHPStan or static analysis before, maybe start with a lower level.</p> + +<p>For me, typehints and return type checking are a must, though I like to use as high a level as possible.</p> + +<p>The more information you can provide to PHPStan, the more it will understand your code, give better results and be more likely to find potential bugs or issues.</p> + +<h2 id="for-brownfield-projects">For brownfield projects</h2> + +<p>For existing (brownfield) code, start at the lowest level, which will give you the least number of errors.</p> + +<p>Fix any errors, exclude any rules you want to ignore or generate a baseline containing any existing errors.</p> + +<p>If you like, increase the level and repeat the process.</p> + +<p>Keep increasing the level as long as you feel comfortable, and PHPStan gives you meaningful results.</p> + +<p>Again, if you haven't used PHPStan or static analysis before, maybe stick with a lower level.</p> + +<p>If you start with a lower level, you can increase it later.</p> +
+
+ Tue, 28 Nov 2023 00:00:00 GMT +
+ + Finding the best test base + + /daily/2023/11/27/finding-the-best-test-base + http://localhost:8000/daily/2023/11/27/finding-the-best-test-base + +
<p>As well as different base classes for types of tests - i.e. functional, kernel and unit - there are other test base classes within those that can be used to simplify things.</p> + +<p>For example, if we have this test:</p> + +<pre><code class="language-php">&lt;?php + +namespace Drupal\Tests\example\Kernel; + +use Drupal\KernelTests\KernelTestBase; +use Drupal\Tests\node\Traits\NodeCreationTrait; +use Drupal\Tests\user\Traits\UserCreationTrait; + +class ExampleTest extends KernelTestBase { + + use NodeCreationTrait; + use UserCreationTrait; + + protected static $modules = [ + 'node', + 'user', + ]; + + public function setUp(): void { + parent::setUp(); + + $this-&gt;installEntitySchema(entity_type_id: 'node'); + $this-&gt;installEntitySchema(entity_type_id: 'user'); + } + + public function testExample(): void { + $user = $this-&gt;createUser(); + + $article = $this-&gt;createNode([ + 'title' =&gt; 'Test article', + 'uid' =&gt; $user, + ]); + + self::assertSame('1', $article-&gt;getOwnerId()); + } + +} +</code></pre> + +<p>Both creation traits must be imported, the <code>node</code> and <code>user</code> modules must be enabled, and the entity tables must be installed.</p> + +<p>When writing a lot of tests, this can result in duplication and more complex tests that take longer to write.</p> + +<p>This can be simplified using <code>EntityKernelTestBase</code> instead of <code>KernelTestBase</code>:</p> + +<pre><code class="language-php">&lt;?php + +use Drupal\KernelTests\Core\Entity\EntityKernelTestBase; +use Drupal\Tests\node\Traits\NodeCreationTrait; + +class ExampleTest extends EntityKernelTestBase { + + use NodeCreationTrait; + + protected static $modules = [ + 'node', + ]; + + public function testExample(): void { + $user = $this-&gt;createUser(); + + $article = $this-&gt;createNode([ + 'title' =&gt; 'Test article', + 'uid' =&gt; $user, + ]); + + self::assertSame('1', $article-&gt;getOwnerId()); + } + +} +</code></pre> + +<p>The class is simpler, fewer modules must be specified, and the entity schemas are automatically installed.</p> + +<p>As well as the core modules, some contrib modules also provide their own base test cases.</p> + +<p>If you're using the Webform module, you may want to use <code>WebformAccessTestBase</code> instead of the standard <code>UnitTestCase</code>.</p> + +<p>It's definitely worth spending some time looking at what base test cases are available and which are the best ones to use for your own tests.</p> +
+
+ Mon, 27 Nov 2023 00:00:00 GMT +
+ + To docblock or not to docblock + + /daily/2023/11/26/to-docblock-or-not-to-docblock + http://localhost:8000/daily/2023/11/26/to-docblock-or-not-to-docblock + +
<h2 id="what-are-docblocks%3F">What are docblocks?</h2> + +<p>Docblocks are comment blocks that describe a class or method and document its parameters and return types.</p> + +<p>Unfortunately, they're often overused and are outdated compared to the code they're describing, or repeat the same information again.</p> + +<p>For example, here's some PHP code:</p> + +<pre><code class="language-php">function sayHello(string $name, array $options = ['shout' =&gt; false]): void { +     echo 'Hello, ' . $name; +} + +sayHello(''); +sayHello('Oliver', ['shout2' =&gt; true]); +sayHello('Oliver', ['shout' =&gt; 'banana']); +</code></pre> + +<p>It's readable and understandable.</p> + +<p>It has types.</p> + +<p>We know that the name needs to be a string and that an array of options can be passed.</p> + +<p>This is how I like to write my code. In a clear and readable way with minimal comments.</p> + +<p>This code works, but there are issues with it.</p> + +<p>Do we want the name to potentially be an empty string?</p> + +<p>What if different options are passed in?</p> + +<h2 id="step-1">Step 1</h2> + +<p>If you use PHPStan (a PHP static analysis tool), you won't get any errors until you test against level 6.</p> + +<p>Then you'll get this error:</p> + +<blockquote> + <p>Function sayHello() has parameter $options with no value type specified in iterable type array.</p> +</blockquote> + +<p>Now, we can use a docblock to provide more information to PHPStan to describe the structure of the <code>$options</code> array and that it has strings for keys and boolean values.</p> + +<pre><code class="language-php">/** + * @param array&lt;string, bool&gt; $options + */ +</code></pre> + +<p>Although it's not erroring, we can add the <code>$name</code> parameter and declare it as a <code>string</code>.</p> + +<pre><code class="language-php">/** + * @param string $name + * @param array&lt;string, bool&gt; $options + */ +</code></pre> + +<p>Now, the docblock adds some context and value but essentially repeats the defined types.</p> + +<h2 id="step-2">Step 2</h2> + +<p>We can do more with the docblock and tell PHPStan more about what we want the values to be.</p> + +<p>We don't want an empty string for a name and want the correct options and values.</p> + +<p>Let's change the docblock to this:</p> + +<pre><code class="language-php">/** + * @param non-empty-string $name + * @param array{shout: bool} $options + */ +</code></pre> + +<p>This specified the name cannot be an empty string and the shape of the options array.</p> + +<p>With this, we get these errors that weren't included before:</p> + +<ul> +<li>Parameter #1 $name of function sayHello expects non-empty-string, '' given.</li> +<li>Parameter #2 $options of function sayHello expects array{shout: bool}, array{shout2: true} given.</li> +<li>Parameter #2 $options of function sayHello expects array{shout: bool}, array{shout: 'banana'} given.</li> +</ul> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>While I like to write minimal, readable and "self-documenting" code and not overuse docblocks by adding them everywhere, I only add them and comments where needed and provide value, either to someone reading the code in the future or to tools like PHPStan that help me make the code better.</p> +
+
+ Sun, 26 Nov 2023 00:00:00 GMT +
+ + Community engagement for non-technical Drupal enthusiasts + + /daily/2023/11/25/community-engagement-for-non-technical-drupal-enthusiasts + http://localhost:8000/daily/2023/11/25/community-engagement-for-non-technical-drupal-enthusiasts + +
<p>This week's guest on the Beyond Blocks podcast is Niklas Franke - Digital Marketing Manager at Factorial.</p> + +<p>Niklas was a fellow speaker at the recent DrupalCon conference in Lille - presenting multiple sessions, whilst Factorial were sponsors of the event.</p> + +<p>In this episode, Niklas and I discussed his first experience of DrupalCon, presenting a session on contribution for non-technical Drupalers, the German Drupal community, what the Splash Awards are and how to organise your own event.</p> + +<p><a href="http://localhost:8000/podcast/3-non-technical-contribution">Listen to the episode now</a>.</p> +
+
+ Sat, 25 Nov 2023 00:00:00 GMT +
+ + Are conventional commits worth it? + + /daily/2023/11/24/are-conventional-commits-worth-it + http://localhost:8000/daily/2023/11/24/are-conventional-commits-worth-it + +
<p>For some time, I've written commit messages following the Conventional Commits specification, where you start the subject with the type of commit - such as <code>feat</code>, <code>fix</code>, <code>chore</code>, <code>docs</code>, etc - and provide an optional scope before completing the subject line (the first line in the message).</p> + +<p>Then, it is encouraged to add a longer body to the message and provide any links and task IDs that the change relates to.</p> + +<p>Now I've been using it for a while, I'm deciding whether it adds value for me and whether it's worth me using it.</p> + +<p>I don't create automatic CHANGELOG files from the commit types.</p> + +<p>The scopes are usually arbitrary, it's unclear which scope (or scopes) should be added, or it repeats the module name I'm working on (which I could see from the Git diff).</p> + +<p>While I see value in writing descriptive commit messages, I'm unsure if I do to format the subject line in this way.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>I like to use an iterative approach to my workflow. I like to try things and see if they work for me. If not, I can stop or continue iterating.</p> + +<p>If working with others, should you focus on writing commits that categorise commit messages within their subject or writing descriptive commit messages that capture why the change is needed?</p> + +<p>Which provides the most value when looking back at the Git log in the future?</p> +
+
+ Fri, 24 Nov 2023 00:00:00 GMT +
+ + Partial mocking + + /daily/2023/11/23/partial-mocking + http://localhost:8000/daily/2023/11/23/partial-mocking + +
<p>Today, I wrote a test whilst fixing a bug in some legacy custom Drupal code.</p> + +<p>The code is for a custom block, which can be configured using user-defined settings - including a link URL.</p> + +<p>In this case, if the link was null (one hadn't been provided), the link was generated to the home page instead of the desired destination.</p> + +<h2 id="my-first-attempt">My first attempt</h2> + +<p>There was a combination of settings needed to replicate the bug, such as the current site language, the node type the block was placed on and, of course, an empty link URL.</p> + +<p>Because the block uses the current route to get the current node, my first attempt to test this was to use a browser/functional test.</p> + +<p>That failed quickly after having to enable various other custom modules due to dependencies and to add and configure unrelated configuration settings.</p> + +<h2 id="my-second-attempt">My second attempt</h2> + +<p>My second attempt used kernel/integration tests, but as there's no <code>setParameter()</code> method on the route matcher I could use in the test, I'd need to rely on mocking.</p> + +<p>In a unit test, everything needs to be mocked, but a kernel test allows me to be more selective, only mock what I need, and use the real services for everything else - a.k.a. partial mocking.</p> + +<h2 id="the-result">The result</h2> + +<p>I replicated the bug by setting the default and current languages, creating a mock language manager, creating a node of the required type and returning it from a mocked route match.</p> + +<p>Everything else remained the same.</p> + +<p>Then, I fixed the bug and used a data provider to provide different variables into the test so each use case was covered.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>The whole test file is 129 lines and would have been much more if I'd had to replicate all the configuration in a functional test or mock everything in a unit test.</p> + +<p>An integration test with partial mocking was ideal in this case, as it gave me the most flexibility to test what I needed whilst keeping the code simple.</p> + +<p>Whilst I'm aware of over-using mocks, this was an ideal situation to use them.</p> +
+
+ Thu, 23 Nov 2023 00:00:00 GMT +
+ + Frequency reduces difficulty + + /daily/2023/11/22/frequency-reduces-difficulty + http://localhost:8000/daily/2023/11/22/frequency-reduces-difficulty + +
<p>"Frequency reduces difficulty" is a phrase I heard on a podcast today, though I have heard it before.</p> + +<p>It's a better way of saying, "If it hurts, do it more often".</p> + +<p>If software deployments and releases are large, risky, and contain bugs, break them into smaller, less-risky deployments and release them more often.</p> + +<p>If you get a lot of merge conflicts in Git, merge your code more frequently. Make your topic branches as short-lived as possible, or try trunk-based development and avoid topic branches by default.</p> + +<p>If writing tests is difficult and takes time, persevere. It will get easier the more you do it, and you'll save time in the future by releasing fewer bugs and being able to refactor safely.</p> +
+
+ Wed, 22 Nov 2023 00:00:00 GMT +
+ + Why I built "Build Configs" + + /daily/2023/11/21/why-i-built-build-configs + http://localhost:8000/daily/2023/11/21/why-i-built-build-configs + +
<p><a href="http://localhost:8000/build-configs">Build Configs</a> is a tool I wrote and recently <a href="http://localhost:8000/presentations/building-build-configs">gave a lightning talk about</a> at the PHP South West meetup in Bristol, UK.</p> + +<p>It's a command-line tool that creates customised per-project configuration files from a set of reusable templates.</p> + +<p>It's a Symfony project using components such as Console, Validator, and Twig for templating.</p> + +<p>But why did I build it?</p> + +<h2 id="what-was-i-doing-before%3F">What was I doing before?</h2> + +<p>When starting a new project, I'd copy configuration files from an existing project and alter them as needed.</p> + +<p>If a project uses a <code>docroot</code> directory instead of <code>web</code>, a different version of PHP, or Caddy instead of Nginx, I'd need to change the files manually for the new project, add any required features, or fix any bugs.</p> + +<h2 id="what-was-the-issue-with-this%3F">What was the issue with this?</h2> + +<p>It took time to set up new projects, and there would be drift between them.</p> + +<p>I may have added a feature to one project, but it wouldn't exist in the one I was working on.</p> + +<p>If I needed to move a feature from one project to another, I needed to do it manually.</p> + +<h2 id="how-does-build-configs-solve-the-issue%3F">How does Build Configs solve the issue?</h2> + +<p>Now, I have a canonical set of template files.</p> + +<p>Instead of making ad-hoc changes to each project, I can add new features and fix bugs in the templates, and re-generate the configuration files for each project.</p> + +<p>I don't need to worry about drift between projects because they're all in sync and generated automatically.</p> + +<p>It's quicker to create and onboard projects using existing configurations instead of copying files and making manual changes.</p> + +<p>If you want to see an example, <a href="https://www.youtube.com/watch?v=LkhsdmxReUc">watch this video</a> where I set up a new Drupal 10 project from nothing to running website in less than a minute.</p> +
+
+ Tue, 21 Nov 2023 00:00:00 GMT +
+ + What'll be in Drupal 11? + + /daily/2023/11/20/whatll-be-in-drupal-11 + http://localhost:8000/daily/2023/11/20/whatll-be-in-drupal-11 + +
<p>Drupal 9 is end-of-life and no longer supported.</p> + +<p>Drupal 10 is the current stable version, and the development branch for Drupal 11 has just been started.</p> + +<p>But what will be in Drupal 11?</p> + +<p>Well, we don't know yet.</p> + +<p>Drupal's release schedule is based on fixed dates and not on features.</p> + +<p>Whatever is ready on the release date will be included. If something isn't ready, it'll be in a future version.</p> + +<p>If something is ready sooner, it'll be in a minor 10 version, such as Drupal 10.2 or 10.3. We've seen it recently with various UI improvements in 10.1 - with more in 10.2.</p> + +<p>We saw new features, such as Layout Builder, added to Drupal core within the Drupal 8 lifecycle, and there's still time to add more to Drupal 10.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>But, like Drupal 9 and 10, Drupal 11 won't mean a major rewrite to your code.</p> + +<p>Drupal 11 is based on Drupal 10, so the modules and themes you use will be able to work with both, allowing you to upgrade incrementally.</p> + +<p>While there isn't a definitive list of new features or changes for Drupal 11, based on things I saw and learned about at DrupalCon, I'm excited to see what makes it.</p> +
+
+ Mon, 20 Nov 2023 00:00:00 GMT +
+ + Why I've standardised on 'run' scripts + + /daily/2023/11/19/why-ive-standardised-on-run-scripts + http://localhost:8000/daily/2023/11/19/why-ive-standardised-on-run-scripts + +
<p>In some of my first emails to this list, I wrote about <code>just</code> and <code>run</code> files.</p> + +<p>Both allow you to write project-specific aliases and commands. For example, instead of running <code>docker compose exec php phpunit</code>, you run <code>just test</code> or <code>run test</code>.</p> + +<p>While <code>just</code> uses a simple Makefile-like syntax, it requires its own program, so it needs to be installed.</p> + +<p>While I have it installed locally, I've had instances where CI pipelines have failed because they can't download and install <code>just</code> and not because of an error in my code.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>A <code>run</code> script is a file of Bash functions.</p> + +<p>Because it's written in Bash, it runs anywhere without installing additional dependencies.</p> + +<p>If a CI pipeline fails, which is less often, it's due to a failure within my code and not because of a download error.</p> +
+
+ Sun, 19 Nov 2023 00:00:00 GMT +
+ + Writing good test names + + /daily/2023/11/18/writing-good-test-names + http://localhost:8000/daily/2023/11/18/writing-good-test-names + +
<p>In PHPUnit, there are different ways to write test methods.</p> + +<p>The standard way is to use camel-case method names with a <code>test</code> prefix, for example:</p> + +<pre><code class="language-php">public function testTheProjectNameShouldBeAString(): void +{ + // ... +} +</code></pre> + +<p>Another popular way, particularly in some frameworks, is to use snake-case method names:</p> + +<pre><code class="language-php">/** @test */ +public function the_project_name_should_be_a_string(): void +{ + // ... +} +</code></pre> + +<p>This may be more readable but only works if the <code>@test</code> annotation is present.</p> + +<p>What if you need to add another annotation, such as <code>@dataProvider</code> to the test? Do you do multi-line docblocks everywhere, or mix and match single and multiple line docblocks depending on the test?</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>My preference switches between both ways of writing test methods.</p> + +<p>But, whichever way I write it, I write descriptive method names that explain what the test does - even if it means having a long method name.</p> + +<p>I avoid short and generic names like <code>testUpdate()</code>.</p> + +<p>People should be able to read the test name and understand what it does and what it's testing.</p> +
+
+ Sat, 18 Nov 2023 00:00:00 GMT +
+ + Drupal's Alternate Realities + + /daily/2023/11/17/drupal-s-alternate-realities + http://localhost:8000/daily/2023/11/17/drupal-s-alternate-realities + +
<p><a href="http://localhost:8000/podcast/2-alternate-realities">This week's episode</a> of the Beyond Blocks podcast is live, where I speak with Panagiotis Moutsopoulos (vensires on Drupal.org) - Drupal Backend Developer at E-Sepia.</p> + +<p>We discuss his first time DrupalCon and, more specifically, his session "Drupal's Alternate Realities" - a "Birds of a Feather" (BoF) session presenting some history, but mainly the different ways to tackle a problem in Drupal using different methodologies.</p> +
+
+ Fri, 17 Nov 2023 00:00:00 GMT +
+ + Avoiding over-mocking + + /daily/2023/11/16/avoiding-over-mocking + http://localhost:8000/daily/2023/11/16/avoiding-over-mocking + +
<p>In unit tests, and sometimes in kernel tests, you need to mock the dependencies you aren't testing, but you can over-mock and only be testing the mocks and not the code you want to test.</p> + +<p>Here's an example (thanks, ChatGPT, for the code).</p> + +<h2 id="the-class-to-be-tested-myclass.php">The Class to be tested (MyClass.php)</h2> + +<pre><code class="language-php">&lt;?php + +class MyClass { + + public function __construct( + private DependencyInterface $dependency + ) { + } + + public function doSomething() { + $result = $this-&gt;dependency-&gt;performAction(); + + return "Result: " . $result; + } +} +</code></pre> + +<h2 id="dependency-interface-dependencyinterface.php">Dependency Interface (DependencyInterface.php)</h2> + +<pre><code class="language-php">&lt;?php + +interface DependencyInterface { + + public function performAction(); + +} +</code></pre> + +<h2 id="a-test-class-that-ends-up-testing-mocks-myclasstest.php">A test class that ends up testing mocks (MyClassTest.php)</h2> + +<pre><code class="language-php">&lt;?php + +use PHPUnit\Framework\TestCase; + +class MyClassTest extends TestCase { + + public function testDoSomething() { + // Creating a mock of the DependencyInterface. + $dependencyMock = $this-&gt;createMock(DependencyInterface::class); + + // Setting up the mock to return a specific value. + $dependencyMock-&gt;expects($this-&gt;once()) + -&gt;method('performAction') + -&gt;willReturn('Mocked result'); + + // Creating an instance of MyClass with the mock. + $myClass = new MyClass($dependencyMock); + + // Calling the method to be tested. + $result = $myClass-&gt;doSomething(); + + // Asserting that the result matches the expected value. + $this-&gt;assertEquals('Result: Mocked result', $result); + } + +} +</code></pre> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>In this example, the test creates a mock for the <code>DependencyInterface</code> and sets up an expectation that the performAction method will be called once, returning a specific value.</p> + +<p>The test then calls the <code>doSomething</code> method on <code>MyClass</code> and asserts that the result is as expected.</p> + +<p>The issue with this test is that it's not testing the actual behaviour of <code>MyClass</code>.</p> + +<p>It's only testing that the mock is configured correctly.</p> + +<p>If the real implementation of <code>MyClass</code> has a bug, this test won't catch it.</p> +
+
+ Thu, 16 Nov 2023 00:00:00 GMT +
+ + Don't pre-optimise and over-customise + + /daily/2023/11/15/dont-pre-optimise-and-over-customise + http://localhost:8000/daily/2023/11/15/dont-pre-optimise-and-over-customise + +
<p>I've been re-watching a livestream series from a few years ago, showing a SaaS product being built.</p> + +<p>The series was created from two full-day live streams to create a minimum viable product (MVP) version of the application.</p> + +<p>Several times, things like this were said:</p> + +<ul> +<li>This will eventually need to be customisable.</li> +<li>When we have multiple users, but now we only have one.</li> +<li>Let's hard-code this for now and refactor it later.</li> +<li>This isn't pixel-perfect, but let's move on.</li> +</ul> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>As they focused on creating an MVP version, they added these things to a to-do list or wrote comments in the code to revisit it later rather than slowing down the initial development and pre-optimising the code for use cases that didn't exist yet.</p> + +<p>If there's only one user, there's no need to write code to handle multiple users.</p> + +<p>Later, once the application is launched, that feature can be added.</p> + +<p>And, because they were writing automated tests, they'd be able to refactor and extend the code and not worry about breaking it as the tests would confirm that the functionality still worked.</p> +
+
+ Wed, 15 Nov 2023 00:00:00 GMT +
+ + Why I prefer integration tests to unit tests + + /daily/2023/11/14/why-i-prefer-integration-tests-to-unit-tests + http://localhost:8000/daily/2023/11/14/why-i-prefer-integration-tests-to-unit-tests + +
<p>With unit tests, you need to mock <strong>everything</strong>.</p> + +<p>If what you're testing has a dependency, you need to create and use a mock version.</p> + +<p>If the mock has its own dependencies, you need to mock those, too.</p> + +<p>I've written unit tests with mocks and accidentally only tested the mocks instead of the functionality I intended.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>With integration tests, you can use and test the real dependencies.</p> + +<p>Whilst they may be slightly slower to run, I prefer the simpler setup, less need for mocks, and knowing the real services work - not just the mocked versions.</p> +
+
+ Tue, 14 Nov 2023 00:00:00 GMT +
+ + PHPUnit does more than unit testing + + /daily/2023/11/13/phpunit-does-more-than-unit-testing + http://localhost:8000/daily/2023/11/13/phpunit-does-more-than-unit-testing + +
<p>As the name suggests, <a href="http://phpunit.de">PHPUnit</a> is a unit testing framework for PHP.</p> + +<p>It's the defacto PHP testing framework used by the major PHP projects, such as Drupal, Symfony, Laravel.</p> + +<p>But unit testing isn't all that PHPUnit can do.</p> + +<p>These frameworks extend PHPUnit to give it more functionality.</p> + +<p>Drupal allows you to run functional/browser tests to make requests to pages, check the response codes and content, as well as integration/kernel tests to test real services instead of relying on mocking within unit tests.</p> + +<p>Symfony does the same with its web and kernel test cases.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>If you use a framework and want to more than unit testing, intsead of including another testing framework, try using some of the PHPUnit extensions that are provided by the framework.</p> +
+
+ Mon, 13 Nov 2023 00:00:00 GMT +
+ + Drupal gives you so much out-of-the-box + + /daily/2023/11/12/drupal-gives-you-so-much-out-of-the-box + http://localhost:8000/daily/2023/11/12/drupal-gives-you-so-much-out-of-the-box + +
<p>When you download and install Drupal, you get so much functionality available out-of-the-box.</p> + +<p>For example:</p> + +<ul> +<li>Content creation and editing capabilities with a flexible content type</li> +<li>A taxonomy system for categorising content.</li> +<li>Tools for managing editorial workflows.</li> +<li>User registration and authentication.</li> +<li>A role-based permission system.</li> +<li>Support for creating multilingual websites with translation tools.</li> +<li>Caching mechanisms to optimise performance.</li> +<li>Scalability features for handling high-traffic websites.</li> +<li>Built-in search functionality.</li> +<li>Robust security features, including input validation and output filtering.</li> +<li>RESTful web services for building web applications and mobile apps.</li> +<li>A flexible page and layout builder.</li> +</ul> + +<p>There are over 50,000 contributed modules on Drupal.org that you can download to add functionality like eCommerce, analytics, social media integrations, quizzes, webforms and surveys.</p> + +<p>You can add specific functionality by writing your own modules, and customise a project by creating a theme.</p> + +<p>It's a great choice for a new web project.</p> +
+
+ Sun, 12 Nov 2023 00:00:00 GMT +
+ + Work in small batches + + /daily/2023/11/11/work-in-small-batches + http://localhost:8000/daily/2023/11/11/work-in-small-batches + +
<p>Software should be designed, written, and deployed in small batches.</p> + +<p>This is the first line from a blog post by Eric Ries and is something that gets discussed with a guest in an upcoming episode of the Beyond Blocks podcast.</p> + +<p>In the post, Eric continues by saying, "Of all of the insights I've contributed to the companies I've worked at over the years, the one I am most proud of is the importance of working in small batches".</p> + +<p>Small batches mean faster feedback, more localised problems as there are fewer changes, and reduced risk and overhead.</p> + +<p>If you work in small batches and make smaller changes, merge them regularly into the mainline branch (ideally, at least once a day), and often deploy changes to production, the releases will be quicker and less stressful, and clients and customers will be happy as their changes will be available sooner.</p> + +<p>I've worked this way, and with long-lived feature branches and large, infrequent deployments, I prefer to work in small batches and deploy often.</p> + +<p>The full blog post is found at <a href="http://www.startuplessonslearned.com/2009/02/work-in-small-batches.html">http://www.startuplessonslearned.com/2009/02/work-in-small-batches.html</a>.</p> +
+
+ Sat, 11 Nov 2023 00:00:00 GMT +
+ + Retrofit with Matt Glaman + + /daily/2023/11/10/retrofit-with-matt-glaman + http://localhost:8000/daily/2023/11/10/retrofit-with-matt-glaman + +
<p>Today, I released <a href="http://localhost:8000/podcast/1-retrofit">the first episode of the "Beyond Blocks" podcast</a>.</p> + +<p>I spoke with Matt Glaman - Principal Software Engineer at Acquia and author of Retrofit.</p> + +<p>Retrofit makes it easier to upgrade from Drupal 7 by adding compatibility layers to run your legacy code within Drupal 10 - giving you more time to port it to the modern approaches and standards.</p> + +<p>I've been aware of Matt and his work for some time, including his work on Drupal Commerce, so it was great to speak with him and for him to be the first guest on the podcast.</p> + +<p>Future episodes will be <a href="http://localhost:8000/podcast">published online</a> weekly, so feel free to subscribe and let me know if you have feedback, suggestions for future guests and topics, or want to be a guest.</p> +
+
+ Fri, 10 Nov 2023 00:00:00 GMT +
+ + README-driven development + + /daily/2023/11/09/readme-driven-development + http://localhost:8000/daily/2023/11/09/readme-driven-development + +
<p>As well as test-driven development (TDD), I also like README-driven development - a.k.a. documentation-driven development.</p> + +<p>In short, you write the documentation first, followed by the code.</p> + +<p>Like writing tests in TDD, it allows you to think about how your code will work, what functions and methods it will contain, how they'll interact together and how you expect people to use your code.</p> + +<p>This can also contain flowcharts, diagrams, example code snippets and anything else that would be useful.</p> + +<p>If you like, you could have a colleague review it in a pull/merge request before progressing.</p> + +<p>Once the README file is written and you're happy with its contents, you can start coding.</p> + +<p>Also, documentation is commonly missed or skipped in projects, so moving it forward in the development process ensures it will be done and, as it's being written nearer to the writing of the code, it's more likely to be correct instead of writing it all once the code has been written.</p> +
+
+ Thu, 09 Nov 2023 00:00:00 GMT +
+ + "Building Build Configs" at PHP South West + + /daily/2023/11/08/building-build-configs + http://localhost:8000/daily/2023/11/08/building-build-configs + +
<p>This evening, I'm presenting a lightning talk at the PHP South West meetup in Bristol.</p> + +<p>The talk is 'Building "Build Configs"' - a Symfony command-line tool I wrote (based on TheAltF4Stream's Rust version with the same name) that generates project-specific build configuration files.</p> + +<p>Since creating it, I've used it for personal projects, projects for clients, and example projects on GitHub - such as <a href="https://github.com/opdavies/docker-example-drupal">my Docker Example Drupal project</a>.</p> + +<p>Having a set of standardised and configurable templates makes it much easier and quicker to start a new project, which I did when creating a <a href="https://github.com/opdavies/docker-example-drupal-commerce-kickstart">Drupal Commerce Kickstart example</a> at DrupalCon.</p> + +<p><a href="http://localhost:8000/presentations/building-build-configs">The slides are already online</a> and I've created <a href="http://localhost:8000/build-configs">an example video</a> where I go from nothing to a ready-to-work-on Drupal website in less than a minute!</p> + +<p>If you have any questions, hit reply and let me know!</p> +
+
+ Wed, 08 Nov 2023 00:00:00 GMT +
+ + It depends + + /daily/2023/11/07/it-depends + http://localhost:8000/daily/2023/11/07/it-depends + +
<p>Usually, in software development, there isn't always a definitive black-and-white answer to a question or situation.</p> + +<p>Most of the time, the answer is "it depends".</p> + +<p>How you approach a problem depends on context.</p> + +<p>How long do you have?</p> + +<p>Are you working on the final version or a prototype or minimum-viable product?</p> + +<p>Should you use a contributed module or write one yourself?</p> + +<p>What if an existing module hasn't been updated for some time or doesn't have tests or other quality checks included?</p> + +<p>Do you write custom CSS or use a framework like Tailwind CSS or Bootstrap?</p> + +<p>Should this project be written in this framework or CMS, or would a different one be better suited?</p> + +<h2 id="here%27s-the-thing...">Here's the thing...</h2> + +<p>There are usually multiple approaches to achieve the same result.</p> + +<p>Decisions will depend on a combination of various factors. In a different situation, the answer could be different.</p> + +<p>This doesn't make any solution outright wrong.</p> + +<p>It was right given the situation.</p> +
+
+ Tue, 07 Nov 2023 00:00:00 GMT +
+ + The first Beyond Blocks podcast episode is recorded + + /daily/2023/11/06/the-first-beyond-blocks-podcast-episode-is-recorded + http://localhost:8000/daily/2023/11/06/the-first-beyond-blocks-podcast-episode-is-recorded + +
<p>For my first time in the podcast host chair, I interviewed Matt Glaman - Principal Software Engineer at Acquia.</p> + +<p>I initially knew Matt from his work on Drupal Commerce, but I wanted to learn about his new project, Retrofit - a tool to make it easier to upgrade from Drupal 7 (which will be end of life in January 2025) to Drupal 10.</p> + +<p>We also talked briefly about PHPStan Drupal and drupal-check, which are some of Matt's other projects and contributions to the Drupal community.</p> + +<p>Thank you, Matt, for being the first guest on the Beyond Blocks podcast.</p> + +<p>I have other guests booked for this week, so the episode with Matt will be <a href="http://localhost:8000/podcast">published soon</a>.</p> +
+
+ Mon, 06 Nov 2023 00:00:00 GMT +
+ + I'm starting a podcast + + /daily/2023/11/05/i-m-starting-a-podcast + http://localhost:8000/daily/2023/11/05/i-m-starting-a-podcast + +
<p>This week, I'm starting my own podcast - <a href="http://localhost:8000/podcast">Beyond Blocks</a>.</p> + +<p>I'll be interviewing different people in every week, focussing on Drupal and related software development topics.</p> + +<p>I have the first guests lined up, including some speakers from last month's DrupalCon Europe conference, where we can discuss their topics in more detail.</p> + +<p>If you'd like to be a guest, <a href="https://forms.gle/wdVqmEyQSWUx8cnm8">get in touch</a> and let me know what you'd like to discuss.</p> +
+
+ Sun, 05 Nov 2023 00:00:00 GMT +
+ + Why you should contribute to open-source software + + /daily/2023/11/04/why-you-should-contribute-to-open-source-software + http://localhost:8000/daily/2023/11/04/why-you-should-contribute-to-open-source-software + +
<p><a href="http://localhost:8000/daily/2023/11/03/why-your-company-should-contribute-to-open-source-software">Yesterday's email</a> explained why your company should contribute to open-source software, but why should you contribute as an individual?</p> + +<p>Most of the same reasons apply, such as gaining experience and improved knowledge from contributing.</p> + +<p>As an individual, you can build your own reputation and personal brand.</p> + +<p>You'll get exposure from contributions and involvement with initiatives, such as the Drupal admin UI improvements and other core initiatives, which look great on your CV and LinkedIn profile.</p> + +<p>This could lead to better career opportunities and potential projects.</p> + +<p>I've had paid development work directly from my open-source code contributions, as well as public speaking and event organising, so I can vouch for this.</p> + +<p>Like companies, if you make money from open-source software - either a salary or from paid projects or courses - it's in your interest to contribute so the software you use is maintained and improved so it's the best it can be.</p> +
+
+ Sat, 04 Nov 2023 00:00:00 GMT +
+ + Why your company should contribute to open-source software + + /daily/2023/11/03/why-your-company-should-contribute-to-open-source-software + http://localhost:8000/daily/2023/11/03/why-your-company-should-contribute-to-open-source-software + +
<p>I've always thought companies should contribute to open-source software.</p> + +<p>Whether it's financial or contributing time, if companies use free, open-source software (and especially if they're directly making money from it).</p> + +<p>Contributing to the software you use will improve the software, whether adding new features, fixing a bug, or contributing translations.</p> + +<p>Having contributors to the software is a great way to differentiate your company from the competition, and the knowledge gained from contributing will result in better code for your projects.</p> + +<p>If you organise or sponsor events such as conferences and meetups, you also get exposure from the event and help support the event and community.</p> + +<h2 id="here%27s-the-thing...">Here's the thing...</h2> + +<p>I've recently pledged to donate a percentage of income from Drupal-related services to the Drupal Association and would like to see others do the same if they can.</p> + +<p>I also sponsor several individuals and organisations on GitHub who build software and create content I use so I get better tools and knowledge for myself and my projects.</p> + +<p>If your company can contribute to open-source, I'd encourage them to do so and help support open-source software projects and communities.</p> +
+
+ Fri, 03 Nov 2023 00:00:00 GMT +
+ + Is code coverage an objective or guideline? + + /daily/2023/11/02/is-code-coverage-an-objective-or-guideline + http://localhost:8000/daily/2023/11/02/is-code-coverage-an-objective-or-guideline + +
<p>Many development teams and projects use code coverage - e.g. how many lines of code are covered by automated tests - as an objective, and saying it must be 100% or another percentage.</p> + +<p>But is this an effective metric?</p> + +<p>In the same way as deleting failing tests to fix a pipeline, a code coverage amount can be faked.</p> + +<p>With this in mind, what if, instead of setting an objective such as 100% code coverage, you used it as a guideline?</p> + +<p>If you're working on a legacy project, what if you set a minimum code coverage amount as a guideline to ensure any new code has tests by not dropping under that level?</p> + +<p>Would that be better than saying every line of code needs to be covered?</p> + +<p>Code coverage is something I'm thinking of using more, so I want to know what you think.</p> +
+
+ Thu, 02 Nov 2023 00:00:00 GMT +
+ + Drupal 9 is now end of life + + /daily/2023/11/01/drupal-9-is-now-end-of-life + http://localhost:8000/daily/2023/11/01/drupal-9-is-now-end-of-life + +
<p>As of today, Drupal 9 is end-of-life and no longer supported.</p> + +<h2 id="why%3F">Why?</h2> + +<p>Drupal 9 relies on several other software projects, including Symfony, CKEditor, and Twig.</p> + +<p>With Symfony 4's end of life, CKEditor 4's end of life, and Twig 2's end of life all coming up soon, Drupal 9 went end of life on November 1st, 2023.</p> + +<h2 id="what-do-i-do-next%3F">What do I do next?</h2> + +<p>There will be no further releases of Drupal 9 so you need to upgrade.</p> + +<p>If you're stuck on Drupal 9, 8, or 7, get unstuck with a <a href="http://localhost:8000/drupal-upgrade">Drupal upgrade roadmap</a> and get directions how to upgrade to Drupal 10.</p> +
+
+ Wed, 01 Nov 2023 00:00:00 GMT +
+ + One official Drupal development environment? + + /daily/2023/10/31/one-official-drupal-development-environment + http://localhost:8000/daily/2023/10/31/one-official-drupal-development-environment + +
<p>Yesterday, I read <a href="https://kevinquillen.com/ddev-being-considered-official-drupal-development-environment">a post by Kevin Quillen</a> titled 'DDEV being considered as the "official" Drupal development environment'.</p> + +<p>I've been a DDEV user, and I've also used most, if not all, of the other popular Drupal development environments.</p> + +<p>These days, I use <a href="http://localhost:8000/daily/2022/08/21/2022-08-21">my own Docker Compose-based setup</a>, but I feel having an official development environment would be an excellent move for the Drupal community and project - and for new Drupal users, evaluators and contributors in particular.</p> + +<p>Anything that lowers the barrier to using or contributing to Drupal is a good thing and allows for more focused documentation and a simpler onboarding experience, similar to what other projects have - such as Laravel with Homestead, Valet and now, Sail.</p> + +<h2 id="collaboration-is-key">Collaboration is key</h2> + +<p>Instead of multiple similar projects creating the same features and fixing the same issues, there could be an opportunity to collaborate or even combine projects and resources in the future.</p> + +<h2 id="but-what-about-existing-teams-and-projects%3F">But what about existing teams and projects?</h2> + +<p>If your company or team already uses another option, you can continue to use it.</p> + +<p>If you have an existing solution, I don't think it will be removed or abandoned in the near future.</p> + +<p>But, for new Drupal users and teams, one official development environment would be a big plus.</p> +
+
+ Tue, 31 Oct 2023 00:00:00 GMT +
+ + Is Drupal a CMS or a framework? + + /daily/2023/10/30/is-drupal-a-cms-or-a-framework + http://localhost:8000/daily/2023/10/30/is-drupal-a-cms-or-a-framework + +
<p>Drupal is typically called a CMS (content management system) - somewhere you add and edit the content for your website.</p> + +<p>However, unlike some other CMSes, unless a Drupal website uses only the default page and article content type, no Drupal websites are the same. Each will have its specific content model with different content types and fields.</p> + +<p>Instead of being a content management system, Drupal can be described as a content management framework (CMF) - a framework for building a content management system specific to each application.</p> + +<p>But as well as a content management framework, Drupal could also be considered an application framework like Symfony or Laravel.</p> + +<p>As well as building the content model, you can extend Drupal's functionality with custom modules and add custom themes to make it visually different.</p> + +<p>But, there's a lot of functionality in Drupal core itself - such as user accounts, authentication, a query builder (Views), JSON:API endpoints, and a visual page editor (Layout Builder) - to name a few things!</p> + +<p>Drupal offers much more than just managing content.</p> +
+
+ Mon, 30 Oct 2023 00:00:00 GMT +
+ + I can drive my car blindfolded, but is it a good idea? + + /daily/2023/10/29/i-can-drive-my-car-blindfolded-but-is-it-a-good-idea + http://localhost:8000/daily/2023/10/29/i-can-drive-my-car-blindfolded-but-is-it-a-good-idea + +
<p>After sending <a href="http://localhost:8000/daily/2023/10/28/can-you-move-faster-without-tests">yesterday's email</a>, Dave Hall replied <a href="https://www.linkedin.com/feed/update/urn:li:activity:7124401304315027456">with a LinkedIn comment</a> I liked and wanted to share (done with permission):</p> + +<blockquote> + <p>In theory I can drive my car on the motorway, blind folded while not wearing a seat belt. It sounds really exciting and loads of fun. When something goes wrong it gets really messy.</p> + + <p>I tend to think of skipping automated tests in the same way.</p> +</blockquote> + +<p>Just because you can do something, it doesn't mean you should.</p> +
+
+ Sun, 29 Oct 2023 00:00:00 GMT +
+ + Can you move faster without tests? + + /daily/2023/10/28/can-you-move-faster-without-tests + http://localhost:8000/daily/2023/10/28/can-you-move-faster-without-tests + +
<p>If you don't write automated tests or do test-driven development, you might move quickly to begin with - writing code and doing small amounts of manual testing.</p> + +<p>But, once you start changing the code to add new features or fix bugs, things will begin to slow down.</p> + +<p>You'll have more to test and need to ensure you don't break the existing functionality as well as your new code works.</p> + +<p>Whilst it may be faster initially, skipping tests will cause problems later.</p> +
+
+ Sat, 28 Oct 2023 00:00:00 GMT +
+ + Work with me and support the Drupal Association + + /daily/2023/10/27/work-with-me-and-support-the-drupal-association + http://localhost:8000/daily/2023/10/27/work-with-me-and-support-the-drupal-association + +
<p>I used to work for the <a href="https://www.drupal.org/association">Drupal Association</a> - the non-profit organisation that supports the Drupal project, organises DrupalCon, and maintains Drupal.org.</p> + +<p>I improved Drupal.org, fixed Drupal 8 release blockers, improved user profiles, helped make the Drupal.org theme responsive, and assisted with new websites like Drupal Events and Jobs.</p> + +<p>At DrupalCon, one of the keynote speakers said, "The Drupal Association is a small but mighty team".</p> + +<p>On the <a href="https://www.drupal.org/association/staff">Association staff page</a>, there are 15 staff members listed, including only five people in the Engineering team.</p> + +<p>I've been in the Drupal community for a long time and worked for various companies that make money from Drupal - a free and open-source project.</p> + +<p>However, it's "free as in puppies". You can have the puppy for free, but it costs time and money to look after it.</p> + +<p>There are programs like the Supporting Partners program which provide funding to the DA, but, at least when I worked there, a large amount of income was from event revenue from DrupalCon.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>As someone who works mainly on Drupal consulting and development projects, I'm pledging to commit a fixed percentage from my Drupal-related services and products to the Drupal Association, including my new <a href="http://localhost:8000/team-coaching">team coaching</a> service.</p> + +<p>If the Association or Drupal weren't there, I wouldn't be able to provide those products and services, so it's in my interest to support the Drupal project and the Drupal Association. Maybe others will do the same.</p> + +<p><strong>Work with me and support the Drupal Association.</strong></p> +
+
+ Fri, 27 Oct 2023 00:00:00 GMT +
+ + Is decoupled Drupal still a thing? + + /daily/2023/10/26/is-decoupled-drupal-still-a-thing + http://localhost:8000/daily/2023/10/26/is-decoupled-drupal-still-a-thing + +
<p>A few years ago, decoupled or headless Drupal - where Drupal is used as an API with a separate front-end - was very popular with some large Drupal agencies and clients using it.</p> + +<p><a href="http://localhost:8000/talks/decoupling-drupal-vuejs">I gave a conference talk</a> about how to use Drupal as a backend application for a Vue.js frontend.</p> + +<p>Recently, though, I haven't heard much about it.</p> + +<p>Some previous advocates have moved back to the more traditional approach.</p> + +<p>Interestingly, though, I did hear quite a bit about it at DrupalCon and about Drupal and Next.js in particular.</p> + +<p>I want to try using Drupal with Astro in the future and see what the landscape is like now.</p> + +<h2 id="what-about-you%3F">What about you?</h2> + +<p>Do you use Drupal in a decoupled or headless way? If so, what do you use for the frontend?</p> + +<p>If not, or if you've moved back to a more traditional approach, why?</p> + +<p>Reply and let me know.</p> +
+
+ Thu, 26 Oct 2023 00:00:00 GMT +
+ + Automated tests prevent regressions when upgrading + + /daily/2023/10/25/automated-tests-prevent-regressions-when-upgrading + http://localhost:8000/daily/2023/10/25/automated-tests-prevent-regressions-when-upgrading + +
<p>With Drupal 9 almost being end-of-life and unsupported, I've recently been doing a lot of Drupal 9 to 10 upgrade work.</p> + +<p>As it's a major version update, there are breaking changes that mean you may need to change your code.</p> + +<p>Here's an example of an error caused by a breaking change:</p> + +<blockquote> + <p>Drupal\Core\Entity\Query\QueryException: Entity queries must explicitly set whether the query should be access checked or not. See Drupal\Core\Entity\Query\QueryInterface::accessCheck().</p> +</blockquote> + +<p>To fix this, you need to call the <code>accessCheck()</code> method before executing a query and specify whether it should be checked.</p> + +<p>But how do you know if it should be checked?</p> + +<p>If you have automated tests that pass beforehand and still pass afterwards, you picked the correct option.</p> + +<p>If the test passes before and fails afterwards, you found the bug and can fix it immediately to ensure the functionality works as before.</p> +
+
+ Wed, 25 Oct 2023 00:00:00 GMT +
+ + How to test code you didn't write + + /daily/2023/10/24/how-to-test-code-you-didnt-write + http://localhost:8000/daily/2023/10/24/how-to-test-code-you-didnt-write + +
<p>I was also asked at DrupalCon how you write tests for code you didn't write and how you cover all of the use cases and scenarios.</p> + +<p>This is tricky. If you didn't write the code, how can you know all the scenarios you need to test?</p> + +<p>Presumably, there is some documentation, Jira tickets, user stories or acceptance criteria from when the code was written that will help you with this.</p> + +<p>Decide the most critical functionality to be tested and focus on that, as that will provide the most value.</p> + +<p>I'd start with functional tests and test for high-level outcomes, such as whether a page loads, returns the correct status code, and contains the expected text.</p> + +<p>Then, iteratively add more tests for other scenarios. Once you have the first one written, this should be easier as you already have the setup steps, such as which modules must be enabled and what configuration needs to be installed.</p> + +<p><strong>One test is better than none.</strong></p> + +<p>If you need to add any new features or fixes, ensure they have their own tests to keep a consistent level of coverage.</p> + +<p>Even with a simple module, you're unlikely to be able to identify all of the scenarios, write all the tests in one go, and have everything covered.</p> + +<p>Start with the most important test first and continue to add more iteratively.</p> +
+
+ Tue, 24 Oct 2023 00:00:00 GMT +
+ + When should you run your tests? + + /daily/2023/10/23/when-should-run-your-tests + http://localhost:8000/daily/2023/10/23/when-should-run-your-tests + +
<p>After my talk at DrupalCon, I was asked when you should run your tests.</p> + +<p>Of course, if you're doing test-driven development, you have to run the tests as you work on them and have the red, green, refactor cycle as you work on the feature or bug fix.</p> + +<p>If you're not doing TDD, I'd recommend running the whole test suite before you push your code to know it works before a peer review or to an environment for quality assurance or user-acceptance testing.</p> + +<h2 id="what-about-ci-pipelines%3F">What about CI pipelines?</h2> + +<p>As well as running tests manually, I'd add them to a CI pipeline, such as GitHub Actions, GitLab CI or Bitbucket Pipelines.</p> + +<p>There, tasks can be run automatically each time a commit is pushed, so you don't need to rely on them being run manually.</p> + +<p>If you're doing trunk-based development, you want the CI pipeline to run on every push to prevent regressions and ensure the tests continue to pass.</p> + +<p>If you're working with feature branches and doing code review, run the tests as part of the merge or pull request so you know everything works as expected before the code is reviewed.</p> + +<p>This answers the main "Does it work?" question, and allows the reviewer to focus on reviewing the code and suggesting improvements.</p> + +<p>If the CI pipeline in the merge or pull request fails, it needs to be fixed before submitting it for review as there's no need to review the code before it changes to fix the pipeline.</p> +
+
+ Mon, 23 Oct 2023 00:00:00 GMT +
+ + Off to DrupalCon + + /daily/2023/10/15/off-to-drupalcon + http://localhost:8000/daily/2023/10/15/off-to-drupalcon + +
<p>This week, I'll be in Lille, France for DrupalCon Europe, where I'm presenting a session on automated testing and test-driven development in Drupal.</p> + +<p>I'll be taking a short break from these emails to enjoy the conference, focus on my talk, and finish my <a href="http://localhost:8000/atdc">free email course on automated testing in Drupal</a>.</p> +
+
+ Sun, 15 Oct 2023 00:00:00 GMT +
+ + Do you need to write tests for small or short-lived projects? + + /daily/2023/10/14/do-you-need-to-write-tests-for-small-or-short-lived-projects + http://localhost:8000/daily/2023/10/14/do-you-need-to-write-tests-for-small-or-short-lived-projects + +
<p>When does it make sense not to write automated tests?</p> + +<p>Tests prevent regressions in codebases that are changed and updated over time.</p> + +<p>So, if you're working on a short-term project, such as a marketing campaign website that will only be live for a few months, you won't get as much benefit from having tests, especially if you may not even change it once it's live.</p> + +<p>Likewise, tests are useful for projects on which you want to avoid downtime and outages, especially if you sell products via your website or it contains important information. For other websites, such as personal blogs, that are less critical, testing may be less beneficial.</p> + +<p>Also, one of Drupal's key benefits is that you can build websites within the Drupal UI, without needing to write custom code.</p> + +<p>If you're going down this route, there is no custom code to test and a separate tool that checks URLs exist and returns the correct response code would be a better fit.</p> + +<p>If you then need to add any custom code and functionality, you may want to add tests.</p> + +<p>Whilst there are some situations where not writing tests is reasonable, there are more reasons and situations when you should, compared to not.</p> +
+
+ Sat, 14 Oct 2023 00:00:00 GMT +
+ + Writing tests is an investment + + /daily/2023/10/13/writing-tests-is-an-investment + http://localhost:8000/daily/2023/10/13/writing-tests-is-an-investment + +
<p>It can take time to write automated tests and do test-driven development, but it is an investment in a project's future stability.</p> + +<p>Spending time to write tests upfront will save time as the project progresses, make the code easier to change, and result in better software, fewer bugs, and quicker changes.</p> + +<p>Even though it takes time, it's time well spent.</p> +
+
+ Fri, 13 Oct 2023 00:00:00 GMT +
+ + Business logic in template files? + + /daily/2023/10/12/business-logic-in-template-files + http://localhost:8000/daily/2023/10/12/business-logic-in-template-files + +
<p>I've often heard and advocated for not "putting logic" in template files and having a separation of concerns.</p> + +<p>The templates should be as simple to read and change as possible, and any complicated logic should be moved elsewhere.</p> + +<p>There can be presentational logic - such as a simple if condition to determine if a value should be shown or looping over a list of items, but business logic should be within custom code - ideally within Service, Action or Command classes - and injected into the templates.</p> + +<p>As well as making the templates simpler and cleaner, this logic can be tested separately with automated tests to ensure it works as expected. If the logic is within a template, this can be done for some things using Functional tests, such as whether some text appears on a page, but testing whether it appears in a certain order, for example, is much harder. This is best done within kernel or unit tests.</p> + +<p>As well as being more testable, extracting the logic from a template makes it more reusable. You can use a service from multiple places within your website without duplicating it, making your website code smaller and easier to maintain.</p> +
+
+ Thu, 12 Oct 2023 00:00:00 GMT +
+ + Do you need that module? + + /daily/2023/10/11/do-you-need-that-module + http://localhost:8000/daily/2023/10/11/do-you-need-that-module + +
<p>Drupal includes a lot of default functionality and additional core modules you can enable to extend it.</p> + +<p>There are also over 50,000 community-contributed modules you can download and add.</p> + +<p>But is it worth adding every module?</p> + +<p>If you're browsing the list of modules on Drupal.org and find one of interest, is the functionality it adds more valuable than the cost of maintaining it?</p> + +<p>Each module increases the number of updates you'll need to do to maintain the website.</p> + +<p>What if it becomes unmaintained in the future and prevents you from updating your website to the next version of Drupal or has a security issue and introduces a vulnerability?</p> + +<p>Some modules will be crucial to your website, others you can do without if you prefer a smaller maintenance overhead.</p> +
+
+ Wed, 11 Oct 2023 00:00:00 GMT +
+ + Why use Composer to manage Drupal dependencies? + + /daily/2023/10/10/why-use-composer-to-manage-drupal-dependencies + http://localhost:8000/daily/2023/10/10/why-use-composer-to-manage-drupal-dependencies + +
<p>One of the initial negatives when Drupal 8 launched was introducing Composer, PHP's dependency manager, and how it could affect non-technical users.</p> + +<p>When I started doing Drupal, I downloaded the .tar.gz or .zip file of Drupal, extracted it, and placed it within my project.</p> + +<p>I did the same for any additional modules I needed.</p> + +<p>To update them, I needed to delete my files and repeat the process of downloading and replacing them.</p> + +<h2 id="drush">Drush</h2> + +<p>Then, instead of doing it manually, I used Drush, the "Drupal shell", to download the files. This saved some time, but it still has down-sides.</p> + +<p>What if you needed to install a module like Pathauto, which has dependencies you also need to download and install?</p> + +<p>With Drush or downloading the files manually, you'd need to download the dependencies separately.</p> + +<h2 id="composer">Composer</h2> + +<p>Composer is a dependency manager, which means it can handle these dependencies for us.</p> + +<p>It looks at each project's <code>composer.json</code> file to find its dependencies and downloads them.</p> + +<p>For example, to install Pathauto, you run <code>composer require drupal/pathauto</code>.</p> + +<p>Within its output, you'll see this:</p> + +<pre><code class="language-plain">Package operations: 3 installs, 0 updates, 0 removals + - Downloading drupal/token (1.12.0) + - Downloading drupal/ctools (4.0.4) + - Downloading drupal/pathauto (1.12.0) +</code></pre> + +<p>As well as Pathauto, it's downloading its dependencies - ctools and pathauto.</p> + +<p>Instead of downloading three modules, we can do it with one command.</p> + +<p>In fact, we don't need to know what its dependencies are - Composer will do that.</p> + +<p>Also, updating the modules is just another simple command - <code>composer update</code>.</p> + +<p>While it may be intimidating to non-technical users, learning a few simple commands makes installing and updating modules much easier!</p> +
+
+ Tue, 10 Oct 2023 00:00:00 GMT +
+ + Spotting new things in Drupal 10.1 + + /daily/2023/10/09/spotting-new-things-in-drupal-10-1 + http://localhost:8000/daily/2023/10/09/spotting-new-things-in-drupal-10-1 + +
<p>I've recently updated my <a href="https://github.com/opdavies/docker-example-drupal">Drupal Docker example project</a> to Drupal 10.1.5 and have been spotting new things.</p> + +<p>From British English as my default option during installation, UX improvements to the admin pages, and developer settings such as Twig caching being configurable in the admin UI.</p> + +<p>Something I've loved since Drupal 8 is the constant new features and iterative improvements being added to Drupal core.</p> + +<p>I'm looking forward to finding more of these as I keep upgrading my Docker example project as well as others.</p> +
+
+ Mon, 09 Oct 2023 00:00:00 GMT +
+ + Is test-driven development difficult? + + /daily/2023/10/08/is-test-driven-development-difficult + http://localhost:8000/daily/2023/10/08/is-test-driven-development-difficult + +
<p>I recently read a post where the writer said automated testing, test-driven development, continuous integration, trunk-based development, etc, are difficult.</p> + +<p>I'm familiar with them and have been using them for some time, but I think trunk-based development is easier as there's only a single branch instead of many.</p> + +<p>Continuous integration is pulling everyone else's code at least once daily and pushing yours to ensure it doesn't conflict.</p> + +<p>Test-driven development is writing failing tests before any implementation code and refactoring once it passes.</p> + +<p>I don't think these are technically difficult, but they do require an open mind, a willingness to learn, evaluate and consider alternative approaches and, in some cases, change existing behaviour.</p> + +<p>Change can be the difficult part, not which brand you commit to, how often you push your code or when you write your tests.</p> +
+
+ Sun, 08 Oct 2023 00:00:00 GMT +
+ + A breakdown of tests from a current project + + /daily/2023/10/07/a-breakdown-of-tests-from-a-current-project + http://localhost:8000/daily/2023/10/07/a-breakdown-of-tests-from-a-current-project + +
<p>I've just added the hundredth test to a client project I'm developing.</p> + +<p>I'm following <a href="http://localhost:8000/daily/2023/09/14/outside-in-or-inside-out">the outside-in approach</a>, starting with functional tests and moving to kernel and unit tests where needed - meaning more functional tests and fewer unit tests - most of which cover some complex search functionality containing custom blocks, forms, endpoints for autocomplete lists, pattern matching and results pages, which is the core functionality of the project.</p> + +<p>Here's the breakdown of the different types of tests:</p> + +<ul> +<li>Functional - 57 tests, 180 assertions</li> +<li>Kernel - 38 tests, 495 assertions</li> +<li>Unit - 5 tests, 18 assertions</li> +</ul> + +<p>The tests are run automatically in a CI pipeline by GitHub Actions and Docker and usually run in two or three minutes.</p> + +<p>The project is still in progress, so I'll add more tests for the remaining functionality and possibly remove some too, as I refactor some of the code.</p> + +<hr /> + +<p>P.S. Are you interested in automated testing and test-driven development? I'm speaking about it at DrupalCon Europe on the 17th of October!</p> + +<p>You can also pre-register for my free 10-day automated testing email course at https://opdavi.es/testing-email-course.</p> +
+
+ Sat, 07 Oct 2023 00:00:00 GMT +
+ + Software development is like going to the Dentist + + /daily/2023/10/06/software-development-is-like-going-to-the-dentist + http://localhost:8000/daily/2023/10/06/software-development-is-like-going-to-the-dentist + +
<p>Working on software development tasks is like going to the Dentist.</p> + +<p>Releasing new features, bug fixes, dependency updates, and refactoring is better and less painful when done regularly.</p> + +<p>Doing these changes in small and regular batches is less risky than less frequent and larger releases.</p> + +<p>It's easier to identify the cause of any issues and correct course if things aren't going to plan.</p> +
+
+ Fri, 06 Oct 2023 00:00:00 GMT +
+ + Some solutions are good enough + + /daily/2023/10/05/some-solutions-are-good-enough + http://localhost:8000/daily/2023/10/05/some-solutions-are-good-enough + +
<p>Once you have a passing test and you're confident your code works as expected, what do you do next?</p> + +<p>Do you keep refactoring and changing it, or is it good enough?</p> + +<p>Do you need to extract additional classes and methods now, or as the test is passing, do you move on to the next task?</p> + +<p>The main objective is to ship the change and for it to provide value to users, not to have a perfect or "gold-plated" solution.</p> + +<p>Once it's shipped, because you have tests, you can confidently refactor it in the future.</p> +
+
+ Thu, 05 Oct 2023 00:00:00 GMT +
+ + The best solution is the one that gets the tests to pass + + /daily/2023/10/04/the-best-solution-is-the-one-that-gets-the-tests-to-pass + http://localhost:8000/daily/2023/10/04/the-best-solution-is-the-one-that-gets-the-tests-to-pass + +
<p>As I said yesterday, there is no perfect solution or approach for every situation.</p> + +<p>However, when doing test-driven development, there is a best solution.</p> + +<p>The simplest thing that gets a failing test to pass.</p> + +<p>Whether it's hard-coding a return value or putting the initial logic in a Controller, that's the objective.</p> + +<p>Once the test is passing, you can refactor the code or continue writing tests, which will each have their own best solution.</p> +
+
+ Wed, 04 Oct 2023 00:00:00 GMT +
+ + There is no perfect solution + + /daily/2023/10/03/there-is-no-perfect-solution + http://localhost:8000/daily/2023/10/03/there-is-no-perfect-solution + +
<p>Something I've said recently when mentoring bootcamp students and working with Junior Developers is that there isn't a perfect solution to each problem, and there are multiple ways to achieve the same result.</p> + +<p>Similarly, there isn't a perfect tool for every situation.</p> + +<p>You can use different tools and get the same outcome.</p> + +<p>In some of my early emails, I talked about <code>run</code> files and <code>just</code> - two ways to write project-specific commands or aliases that simplify complex commands or group multiple commands together.</p> + +<p>Both do the same thing, but each have pros and cons.</p> + +<p><code>run</code> files are files written in Bash, which are more verbose but can run anywhere.</p> + +<p>just is its own program that needs to be installed for it to be available, so Developers will need to have it installed, and it will need to be added to a CI pipeline to be available.</p> + +<p>I've had CI pipelines fail because they can't download just, and not because of anything in the application code.</p> + +<p>I like the simplicity of justfiles, though, and that the files are simpler.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>If there isn't a perfect solution, approach, or tool, it comes down to the pros and cons of each. Which option is best for you, your project, or your team?</p> + +<p>There isn't a "one size fits all" solution that works for everyone all the time.</p> +
+
+ Tue, 03 Oct 2023 00:00:00 GMT +
+ + Only comment what needs to be commented + + /daily/2023/10/02/only-comment-what-needs-to-be-commented + http://localhost:8000/daily/2023/10/02/only-comment-what-needs-to-be-commented + +
<p>"If you comment everything, people won't read them."</p> + +<p>"As soon as one comment is incorrect, the result will be ignored."</p> + +<p>These are two comments I heard recently referring to code comments.</p> + +<p>If there are too many comments, people will get lost in the information. They won't be able to differentiate boilerplate comments from valuable ones, and as soon as one comment is incorrect, people will assume that others could likely be incorrect too and ignore them.</p> + +<p>A solution to these is to only comment what needs to be commented.</p> + +<p>Only write comments that add value, and if there are fewer comments, they are easier to read and maintain - making it more likely they will be incorrect.</p> + +<p>This is an example from an open source pull request:</p> + +<pre><code class="language-php">/** + * An interface for field access override. + */ +interface FieldAccessOverrideInterface { +</code></pre> + +<p>In my opinion, this doesn't add any value or additional context as it repeats what's already there.</p> + +<p>Something I put in my project configuration files is to tweak the coding standards to not check, for example, for class and method docblocks - allowing me to add them where I want and where I think they're needed and add value.</p> + +<p>I think this is better than having comments everywhere, and I can focus on the ones that matter.</p> +
+
+ Mon, 02 Oct 2023 00:00:00 GMT +
+ + TDD: repeat and refactor + + /daily/2023/10/01/tdd--repeat-and-refactor + http://localhost:8000/daily/2023/10/01/tdd--repeat-and-refactor + +
<p>With test-driven development, you start with a failing test and focus on getting it to pass.</p> + +<p>But what do you do once it's passing?</p> + +<p>You either repeat the process, adding more assertions to the same test until you generate the next failure or write the next failing test, or take the opportunity to refactor the code.</p> + +<p>Is there something you'd like to change within the implementation or test code? You can do that whilst the tests are passing.</p> + +<p>You may want to extract a new class or method to improve the implementation or create some test helpers to reduce duplication within the tests.</p> + +<p>You just need to keep the tests passing to ensure the code still works. If something breaks, you fix it as soon as possible, either by fixing the code or reverting to the previously passing code.</p> + +<p>Then, once you've completed the refactoring, move back into the red/green phase with the next test failure.</p> +
+
+ Sun, 01 Oct 2023 00:00:00 GMT +
+ + Automated testing offers repeatability + + /daily/2023/09/30/automated-testing-offers-repeatability + http://localhost:8000/daily/2023/09/30/automated-testing-offers-repeatability + +
<p>You work on feature or bug fix.</p> + +<p>It gets tested manually by you, a tester, and the person who requested the feature or reported the bug.</p> + +<p>It may get tested on multiple environments.</p> + +<p>It passes and it moved to production.</p> + +<p>But now it's live, it's not tested again.</p> + +<p>Although it works now, there's no guarantee it's not broken or regressed by subsequent changes.</p> + +<p>It's definitely not tested manually before every future release.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>Automated testing offers repeatability.</p> + +<p>The tests pass, so the functionality works when you wrote it, and you can re-check it by re-running the test.</p> + +<p>The test suite can be run by Developers whilst working on other changes, before code review and before deployments.</p> + +<p>The test suite can be run automatically in a CI pipeline for every change that's pushed, and you'll know your change not only worked at the time but will continue to work.</p> +
+
+ Sat, 30 Sep 2023 00:00:00 GMT +
+ + 92 changed files with 885 additions and 156 deletions + + /daily/2023/09/29/92-changed-files-with-885-additions-and-156-deletions + http://localhost:8000/daily/2023/09/29/92-changed-files-with-885-additions-and-156-deletions + +
<p>Today, I refactored some code on a client's Drupal project I've been working on.</p> + +<p>This is what the commit says:</p> + +<blockquote> + <p>Showing 92 changed files with 885 additions and 156 deletions</p> +</blockquote> + +<p>So, it's not a small refactor.</p> + +<p>As I worked on the custom modules needed for the site, I noticed that some common functionality was spread over multiple modules. This refactor moved that functionality into its own module, which is now a dependency of the others.</p> + +<p>The code is better, and the responsibilities of the modules are clearer.</p> + +<p>Whilst a large amount of those additions are adding test modules with test configuration, it's not a small refactor.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>This is what it says at the bottom of the CI pipeline output:</p> + +<blockquote> + <p>OK (79 tests, 565 assertions)</p> +</blockquote> + +<p>This is the same number of passing tests and assertions I had before I started the refactor, so I know the functionality still works.</p> + +<p>If I didn't have those tests, I wouldn't have done the refactor.</p> +
+
+ Fri, 29 Sep 2023 00:00:00 GMT +
+ + Feature flags enable continuous integration + + /daily/2023/09/28/feature-flags-enable-continuous-integration + http://localhost:8000/daily/2023/09/28/feature-flags-enable-continuous-integration + +
<p>I was recently working on a feature over a few mob programming sessions.</p> + +<p>After the session, we implemented part of the feature but couldn't merge it as it wasn't complete.</p> + +<p>After working on other things that day, I'd need to rebase my local commits and push the last-but-one commit to the remote repository to keep the incomplete feature only in my local repository (I forgot a few times and almost pushed it accidentally).</p> + +<p>The commit prevented me from following continuous integration - where everyone commits and pushes their code a minimum of once daily.</p> + +<p>What if someone else wanted to look at that code between mob sessions? They couldn't, as it was only on my laptop.</p> + +<p>If I'd made a temporary branch, it would be outdated and behind other changes to our mainline branch.</p> + +<h2 id="feature-flags-to-the-rescue">Feature flags to the rescue</h2> + +<p>As this commit was part of a larger change, it couldn't be merged, so we added a feature flag around it.</p> + +<p>The values were set to null and hidden by default, and populated by the new changes and displayed if the flag was enabled.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>This meant we could safely push the commit, make the changes available to everyone and enable the feature on the environments we wanted - preventing it from showing on production.</p> + +<p>This allowed us to get back to our usual working practices, and I didn't need to worry about accidentally pushing the commit and prematurely showing our changes in production.</p> +
+
+ Thu, 28 Sep 2023 00:00:00 GMT +
+ + Separating environments with feature flags + + /daily/2023/09/27/separating-environments-with-feature-flags + http://localhost:8000/daily/2023/09/27/separating-environments-with-feature-flags + +
<p>You have two or more environments and versions of your application, but you do trunk-based development, so you only have a single branch that you use on all environments.</p> + +<p>But if all environments have the same code, how can we have differences between them?</p> + +<p>What if we want a feature enabled on one environment and not the other?</p> + +<h2 id="feature-flags">Feature flags</h2> + +<p>Feature flags are an approach I've previously written about. You have two branches of logic within your code, and the flow changes based on whether a flag is enabled.</p> + +<p>If a flag is enabled, execute the first block of code. Otherwise, execute the second.</p> + +<p>Now, you enable and disable the required feature flags for each environment.</p> + +<p><strong>The code is the same for all environments, but the enabled features and functionality are different.</strong></p> + +<p>Then, once the feature has been deployed and released in production, the feature flag can be removed.</p> +
+
+ Wed, 27 Sep 2023 00:00:00 GMT +
+ + Which branch should be in which environment? + + /daily/2023/09/26/which-branch-should-be-in-which-environment + http://localhost:8000/daily/2023/09/26/which-branch-should-be-in-which-environment + +
<p>A common question is which [Git] branch should be on which environment.</p> + +<p>Most projects I've worked on have two or more environments: production, staging (or test) and development.</p> + +<p>Earlier in my career, we used Git Flow heavily. A Git branching workflow based on having different branches - i.e. <code>develop</code>, <code>master</code> and any arbitrary short-lived feature, hotfix and release branches.</p> + +<p>These matched nicely with our three environments.</p> + +<p>Usually, the <code>develop</code> branch would be used in the development environment. The <code>master</code> branch would be on staging and a tagged release from <code>master</code> on production.</p> + +<h2 id="what-about-now%3F">What about now?</h2> + +<p>I prefer trunk-based development, where there is one long-lived branch to which everyone commits their changes.</p> + +<p>There's only one branch, so you can either follow continuous deployment and use the same branch for all environments - including production - or separate production using a dedicated branch or tag if you need more control.</p> + +<p>The mainline branch is used in all pre-production environments, such as staging and development.</p> + +<h2 id="what-about-differences-between-the-environments%3F">What about differences between the environments?</h2> + +<p>What if we need differences, such as a feature that must be enabled in a particular environment if the same code is on both?</p> + +<p>My go-to approach is feature flagging, and this approach is something I'll describe more in tomorrow's email.</p> +
+
+ Tue, 26 Sep 2023 00:00:00 GMT +
+ + Should you use a staging environment? + + /daily/2023/09/25/should-you-use-a-staging-environment + http://localhost:8000/daily/2023/09/25/should-you-use-a-staging-environment + +
<p>The purpose of any pre-production version of your website or application (any version that isn't the public live one) is to mimic the production version and as a test run for deployments.</p> + +<p><strong>If I deploy this feature, will it break the website?</strong></p> + +<p>The issue with most staging and other pre-production environments is that they differ from what's on production. It may have been some time since the latest data was added, and its value is only as good as how close to production it is and how likely it is to find potential issues in production.</p> + +<p>If they differ greatly, the staging environment offers no value.</p> + +<p>It's not a true comparison, and instead of being a test run for a deployment to production, you're only reviewing the deployment to the staging environment itself.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>The best situation I've experienced is where a sanitised snapshot was synced to all pre-production environments every night.</p> + +<p>I knew that my environment was, at most, a single day out of date, and I was confident that my changes would work in production if they did there.</p> + +<p>In that situation, the staging site works and testing it has value, but if it's weeks or months out of date, it doesn't, as there are too many differences for it to be an accurate representation.</p> +
+
+ Mon, 25 Sep 2023 00:00:00 GMT +
+ + Test to save your job + + /daily/2023/09/24/test-to-save-your-job + http://localhost:8000/daily/2023/09/24/test-to-save-your-job + +
<p>I've recently been going through my YouTube "Watch Later" list and watching (or rewatching) videos of conference and meetup talks that I saved to watch later.</p> + +<p>Today's talk was by Matt Stauffer at one of the previous Laracon conferences.</p> + +<p>I've quoted Matt previously when explaining what to test on applications. The answer: "The thing you'd lose your job for if it broke".</p> + +<p>In this talk, Matt has a slide that goes further into this, titled "Test to save your job".</p> + +<h2 id="what-matt-said">What Matt said</h2> + +<p>The best place to start your tests is by asking yourself: "What part of this app, if broken, would make me worried for my job?"</p> + +<ul> +<li>What's most likely to break?</li> +<li>What do I have the least control over?</li> +<li>What are we about to refactor?</li> +<li>What would make my clients stress out?</li> +<li>What would make me stressed out?</li> +</ul> + +<h2 id="what-about-you%3F">What about you?</h2> + +<p>Do you have any other ways to decide what code to test? Reply and let me know, as I'd love to know.</p> +
+
+ Sun, 24 Sep 2023 00:00:00 GMT +
+ + Everyone tests their code, but not everyone writes automated tests + + /daily/2023/09/23/not-everyone-writes-automated-tests + http://localhost:8000/daily/2023/09/23/not-everyone-writes-automated-tests + +
<p>Everyone tests their code.</p> + +<p>No one writes code and ships it to production before testing it works.</p> + +<p>It's just that most Developers test their code manually.</p> + +<p>They write the code, switch to a browser or terminal, and run it.</p> + +<p>It might be a quick task, such as reloading a page or executing a command, or it could be complex and time-consuming, such as filling in a long form and checking the results.</p> + +<p>In this case, automated testing is a good option.</p> + +<p>Instead of filling in the form, have the tests do it for you.</p> + +<p>It will be quicker and more reliable than doing it manually.</p> +
+
+ Sat, 23 Sep 2023 00:00:00 GMT +
+ + Documentation and comments get stale. Tests don't. + + /daily/2023/09/22/documentation-and-comments-get-stale + http://localhost:8000/daily/2023/09/22/documentation-and-comments-get-stale + +
<p>I'm sure you've seen code comments that say the code will do something when, in fact, it does something different.</p> + +<p>Or documentation that has become outdated and stale and no longer correct.</p> + +<p>Automated tests and other tools, such as static analysis, are executable documentation, so they can't become stale.</p> + +<p>If the implementation code changes and no longer matches the tests, the tests will fail, and either the implementation or test can be updated.</p> + +<p>Then, if you need to refer to the test, as long as it's passing, you know what it shows is still applicable.</p> + +<p>The same can't be said for markdown files, Confluence pages, Word documents or other static documentation.</p> +
+
+ Fri, 22 Sep 2023 00:00:00 GMT +
+ + Buggy software causes reputational damage + + /daily/2023/09/21/buggy-software-causes-reputational-damage + http://localhost:8000/daily/2023/09/21/buggy-software-causes-reputational-damage + +
<p>As well as taking longer to fix than locally or in a failed CI pipeline, shipping buggy software to production causes reputational damage.</p> + +<p>If an application goes down or doesn't run as expected, it could get noticed by your customers, clients, or your clients' customers.</p> + +<p>You want your customers and clients to be assured you're writing the correct software based on their needs and requirements, and doing so with as few bugs and issues as possible.</p> + +<p>While there are always bugs in software and occasional unexpected downtime, having these happen regularly can damage your and, if applicable, your client's reputation.</p> + +<p>Therefore, it's in everyone's interest to write high-quality and well-tested software.</p> + +<p>There will be fewer bugs, and by following practices such as feature flags and continuous integration and delivery, they can be fixed faster.</p> +
+
+ Thu, 21 Sep 2023 00:00:00 GMT +
+ + Why I prefer types + + /daily/2023/09/20/why-i-prefer-types + http://localhost:8000/daily/2023/09/20/why-i-prefer-types + +
<p>Whether it's PHP or JavaScript/TypeScript, I prefer type declarations in my code.</p> + +<p>As well as benefits like auto-completion in your IDE or text editor and being able to more effectively statically analyse the code, to me, the code is more readable and easier to understand with the types included.</p> + +<p>It's more to read, but I can do so easily and immediately know what a function expects as function arguments and what it will return.</p> + +<p>Here's the code from my previous email on types from a few days ago, with and without the types declared:</p> + +<pre><code class="js">add(...numbers) { + // ... +} + +subtract(...numbers) { + // ... +} + +add(...numbers: number[]): number { + // ... +} + +subtract(...numbers: number[]): number { + // ... +} +</code></pre> + +<p>Without types, I can infer what the function accepts and returns, but that's based on my assumption, which could be incorrect.</p> + +<p>What if <code>numbers</code> was an array of strings of numbers - e.g. <code>['one', 'two', 'three']</code> - and what if instead of returning the result, it stored it in state to return from a different method like <code>equals()</code> or <code>calculate()</code>?</p> + +<p>With the type declarations included, I don't need to presume, infer or make best guesses.</p> + +<p>It's clear from just reading the code.</p> +
+
+ Wed, 20 Sep 2023 00:00:00 GMT +
+ + Stop writing tests + + /daily/2023/09/19/stop-writing-tests + http://localhost:8000/daily/2023/09/19/stop-writing-tests + +
<p>A few years ago, I worked at an agency on a client project.</p> + +<p>We were writing automated tests and doing test-driven development. We were releasing features, and everything was going well.</p> + +<p>Mid-way through the project, we were told to stop writing tests as it was taking "too long".</p> + +<p>We did, and the number of bugs increased almost instantly.</p> + +<p>We went from essentially no bugs to having a number of tickets being rejected through our quality assurance and the client's user-acceptance testing, as well as in production by the website's users.</p> + +<p>The time we spent re-working the issues, fixing the bugs and doing remedial work in production would likely have been less than the time it would have taken to write the tests initially and avoided any reputational damage.</p> + +<p>We started writing tests again later during that project, but the middle phase was the most stressful part, and the most bugs were created.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>It's not just about how long it takes to write the tests.</p> + +<p>Having tests will save time overall by having fewer bugs that need to be fixed, less remedial work in production, and less reshuffling of priorities to re-work tickets that affect the project plan and timescales as well as the team's output and velocity.</p> +
+
+ Tue, 19 Sep 2023 00:00:00 GMT +
+ + Increasing test coverage with regression tests + + /daily/2023/09/18/increasing-test-coverage-with-regression-tests + http://localhost:8000/daily/2023/09/18/increasing-test-coverage-with-regression-tests + +
<p>Automated test suites don't tell you everything works - they tell you what you've tested isn't broken.</p> + +<p>Having tests doesn't mean your code is bug-free. There could be edge cases or scenarios you haven't tested for that contain bugs, even though your test suite is passing.</p> + +<h2 id="what-do-we-do%3F">What do we do?</h2> + +<p>When you find a bug, try replicating it within an automated test before attempting to fix it.</p> + +<p>Once you have a failing test and can replicate the issue, go ahead and fix it.</p> + +<p>If the test passes, you know you've fixed the bug and solved the issue.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>Now you have this test, you cannot re-add the bug again without the test failing. You've prevented anyone from accidentally re-introducing it in the future and increased your test coverage.</p> +
+
+ Mon, 18 Sep 2023 00:00:00 GMT +
+ + How much refactoring should I be doing? + + /daily/2023/09/17/how-much-refactoring-should-i-be-doing + http://localhost:8000/daily/2023/09/17/how-much-refactoring-should-i-be-doing + +
<p>I watched a webinar recently, and one of the panellists asked, "How much refactoring should I be doing?".</p> + +<p>The reply was, "More than you're doing now.".</p> + +<p>It was quite tongue-in-cheek, but I agree that, in general, code isn't refactored enough.</p> + +<p>One main reason is a fear of introducing regressions, and to avoid that, you need a good automated test suite.</p> + +<p>If you break the existing functionality whilst refactoring, you want the test suite to fail so you can identify and fix the regression.</p> + +<p>If the test suite passes, you can release the new code.</p> + +<p>How many tests and how much coverage do you need? There's no specfic answer - enough for you to be confident everything still works.</p> +
+
+ Sun, 17 Sep 2023 00:00:00 GMT +
+ + How not to break 36,000 websites + + /daily/2023/09/16/how-not-to-break-36-000-websites + http://localhost:8000/daily/2023/09/16/how-not-to-break-36-000-websites + +
<p>I've maintained the Override Node Options module for Drupal since early 2012.</p> + +<p>I've maintained the Drupal 7 version and written new versions for Drupal 8 onwards.</p> + +<p>At the time of writing, the module is used on at least 36,244 active Drupal websites.</p> + +<p>I don't want to break 36,000 websites, and automated testing is my way of avoiding that.</p> + +<p>I have three examples of how automated testing helped me when working on this module.</p> + +<h2 id="upgrading-to-drupal-8">Upgrading to Drupal 8</h2> + +<p>When writing the initial Drupal 8 version of the module, as I started re-writing a feature, the first thing I did was recreate the tests from the Drupal 7 version.</p> + +<p>Then, I followed a test-driven development approach to get the test passing using the Drupal 7 code for reference.</p> + +<p>When the whole test suite was in place and passing, I knew there was feature parity between the two versions.</p> + +<h2 id="resolving-a-large-merge-conflict">Resolving a large merge conflict</h2> + +<p>I was reviewing a feature request that had been open for some time, which included a large patch file with the code changes to implement it.</p> + +<p>However, the patch no longer applied to the code, which caused various merge conflicts.</p> + +<p>There were no additional tests within the patch, but I was able to use the existing tests to ensure the original functionality worked once the merge conflicts were resolved and there were no regressions caused by committing the patch.</p> + +<p>I did later add tests for the new functionality, but without the original test suite, I would have likely not have accepted the patch and not committed the feature.</p> + +<h2 id="refactoring-the-code">Refactoring the code</h2> + +<p>A few years ago, a colleague and I wanted to refactor the module code to make it more maintainable and easier to work on.</p> + +<p>Then, the module was used on around 30,000 websites, so this could have been risky.</p> + +<p>However, we had the test suite to ensure the functionality still worked and that our refactors were successful.</p> + +<p>If we broke something and introduced a regression, a test would fail, and we could fix it.</p> + +<p>Following the release containing the refactored code, there were no reported issues or regressions from the community.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>Automated testing is about providing confidence.</p> + +<p>Confidence the software works as expected and is releasable, either to clients for custom software or consumers of open-source software.</p> + +<p>With a passing test suite, you can add and change code with confidence and without worrying about breaking it.</p> +
+
+ Sat, 16 Sep 2023 00:00:00 GMT +
+ + Types or no types + + /daily/2023/09/15/types-or-no-types + http://localhost:8000/daily/2023/09/15/types-or-no-types + +
<p>Here are two versions of some example code I've recently been working on.</p> + +<p>One has types and uses TypeScript, the other is JavaScript and has no types.</p> + +<p>Which do you prefer and why?</p> + +<h2 id="typescript-with-types">TypeScript (with types)</h2> + +<pre><code class="js">add(...numbers: number[]): number { + return numbers.reduce((a: number, b: number) =&gt; a + b, 0); +} + +subtract(...numbers: number[]): number { + let total = numbers[0]; + + for (var i = 1, length = numbers.length; i &lt; length; i++) { + total -= numbers[i]; + } + return total; +} +</code></pre> + +<h2 id="javascript-no-types">JavaScript (no types)</h2> + +<pre><code class="js">add(...numbers){ + return numbers.reduce((a, b) =&gt; a + b, 0); +} + +subtract(...numbers) { + let total = numbers[0]; + + for (var i = 1, length = numbers.length; i &lt; length; i++) { + total -= numbers[i]; + } + + return total; +} +</code></pre> +
+
+ Fri, 15 Sep 2023 00:00:00 GMT +
+ + Outside-in or inside-out? + + /daily/2023/09/14/outside-in-or-inside-out + http://localhost:8000/daily/2023/09/14/outside-in-or-inside-out + +
<p><a href="http://localhost:8000/daily/2023/09/13/which-type-of-test-should-i-use">In yesterday's email</a>, I described the different types of tests available in Drupal and how to determine which test I should use.</p> + +<p>I start with functional tests and move to kernel or unit tests when needed, so I usually have more functional tests, some kernel tests and a few unit tests.</p> + +<p>This is the outside-in approach to testing.</p> + +<p>The opposite is inside-out testing when you start with unit tests and have fewer functional tests.</p> + +<p>Tests written outside-in are slower to run as they need to perform tasks like HTTP requests and interact with the database, compared to unit tests, which are fast to run but do reply on mocking dependencies rather than using real services and content.</p> + +<p>Which do you prefer? Reply and let me know.</p> +
+
+ Thu, 14 Sep 2023 00:00:00 GMT +
+ + Which type of test should I use? + + /daily/2023/09/13/which-type-of-test-should-i-use + http://localhost:8000/daily/2023/09/13/which-type-of-test-should-i-use + +
<p>Drupal offers a few different types of tests you can use, but how do you know which one to pick?</p> + +<p>This is how I decide based on what I need to test by asking these questions:</p> + +<h2 id="do-i-need-to-test-http-requests-and-responses%3F">Do I need to test HTTP requests and responses?</h2> + +<p>Do I need to make GET or POST requests to a URL and check its response code, e.g. to check the endpoint exists, that I get an expected "Page not found" or "Forbidden" message, or to check if an item was created or updated?</p> + +<p>If so, use a <code>Functional</code> test like <code>BrowserTestBase</code>.</p> + +<h2 id="do-i-need-to-test-with-javascript%3F">Do I need to test with JavaScript?</h2> + +<p><code>BrowserTestBase</code> doesn't include JavaScript, so use a <code>FunctionaJavaScript</code> test if needed.</p> + +<h2 id="do-i-need-to-retrieve-services-from-the-service%2Fdependency-injection-container%3F">Do I need to retrieve services from the service/Dependency Injection container?</h2> + +<p>If I don't need to make HTTP requests but need access to the service container, I use a Kernel test such as <code>KernelTestBase</code>.</p> + +<p>There's more setup for kernel tests compared to functional tests, such as installing the required configuration and creating schema tables in the database. However, having less installed means the tests run faster.</p> + +<p>There are different base classes, such as <code>EntityKernelTestBase</code>, that can be used depending on the situation to simplify the setup.</p> + +<h2 id="can-i-test-the-code-in-isolation-without-the-service-container%3F">Can I test the code in isolation without the service container?</h2> + +<p>If I can test the code without needing the service container and mock simple dependencies, then use a <code>Unit</code> test.</p> + +<p>These are very fast to run, but you can overuse mocks and sometimes test the mocks instead of the code you intended to test.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>As well as the regular unit test type, Drupal offers various types of test that can be used depending on the situation.</p> + +<p>I like to start with functional tests first and switch to kernel or unit tests when needed. They're slower to run but easier to write as they need less setup and arguably provide the most value.</p> + +<p>If you're just getting started with testing, they're probably the quickest to learn too.</p> +
+
+ Wed, 13 Sep 2023 00:00:00 GMT +
+ + Don't inject too many dependencies + + /daily/2023/09/12/dont-inject-too-many-dependencies + http://localhost:8000/daily/2023/09/12/dont-inject-too-many-dependencies + +
<p>While dependency injection is a good practice - i.e., passing dependencies into a class, usually via a constructor method - you want to be aware of how many dependencies you inject into each class.</p> + +<p>There's no hard and fast rule, but I usually notice when I get to three dependencies and rarely inject more than four or five into a class.</p> + +<p>Having too many dependencies suggests that the class is doing too much and has too many responsibilities and that another class may be needed.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>Having smaller and simpler classes makes them easier to read, maintain and review. Ideally, each class should only have one responsibility, so it adheres to the Single Responsibility Principle (the "S" in SOLID).</p> + +<p>Creating classes is cheap, so why not split one large and difficult-to-maintain class with many dependencies into multiple smaller and easier-to-work-with ones?</p> +
+
+ Tue, 12 Sep 2023 00:00:00 GMT +
+ + Don't use "else" + + /daily/2023/09/11/dont-use-else + http://localhost:8000/daily/2023/09/11/dont-use-else + +
<p>A popular approach to writing clean code is to avoid the "else" keyword and, if possible, avoid nesting <code>if</code> statements within other <code>if</code> statements. +If I look at some code, I want to see as few indentation levels as possible, making the code easier to read and understand.</p> + +<h2 id="what-should-i-do-instead%3F">What should I do instead?</h2> + +<p>Instead, you check for a condition; if that isn't met, you return early. +For example, here is some code I saw recently during a meetup talk:</p> + +<pre><code class="language-php">$callingClass = $scope-&gt;getClassReflection()-&gt;getName(); + +if ($callingClass === TextMessageQueueProcessor::class) { +    return []; +} + +$type = $scope-&gt;getType($node-&gt;var); + +foreach ($type-&gt;getReferencedClasses() as $targetClass) { +    if ($targetClass === TextMessageSender::class) { +        return [ +            RuleErrorBuilder::message( +                sprintf( +                    "Can not call %s from %s", +                    $targetClass, +                    $callingClass +                ) +            )-&gt;build() +        ]; +    } +} + +return []; +</code></pre> + +<p>There are no <code>else</code> statements.</p> + +<p>If the calling class isn't of the required type, it returns immediately with no violations, and we continue, knowing the calling class must be what we need.</p> + +<p>If the target class is one where the code shouldn't be called from, it returns immediately with the violation.</p> + +<p>Finally, if no violations were found within the referenced classes, it returns an empty array.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>The code always returns an array of rule violations but does so as soon as possible at each point.</p> + +<p>The code is clean and readable, and I can understand it, knowing once each condition is passed, I don't need to continue thinking about it.</p> + +<p>Whilst there are some situations to use <code>else</code>, most of the time I've found that I can use an early return instead.</p> +
+
+ Mon, 11 Sep 2023 00:00:00 GMT +
+ + Code is read more than it's written + + /daily/2023/09/10/code-is-read-more-than-it-s-written + http://localhost:8000/daily/2023/09/10/code-is-read-more-than-it-s-written + +
<p>A lot of code is written once by one person and not changed again.</p> + +<p>But it's read by multiple people at multiple times.</p> + +<p>If the code is read more than it's written, we should be optimising it for readability so it's easier for people to understand.</p> + +<p>The application doesn't care whether you wrote the code the "clever" way or the "boring" way, but people will appreciate the more readable version.</p> + +<p>If I need to review or work on the code in the future, I want to read and understand it easily.</p> +
+
+ Sun, 10 Sep 2023 00:00:00 GMT +
+ + A lack of tests discourages refactoring + + /daily/2023/09/09/a-lack-of-tests-discourages-refactoring + http://localhost:8000/daily/2023/09/09/a-lack-of-tests-discourages-refactoring + +
<p>A common cause of not refactoring code is a lack of test coverage.</p> + +<p>Why change some code if it works? What if we break it whilst refactoring it and introduce a regression?</p> + +<p>If the code has accompanying automated tests, this can't happen.</p> + +<p>The tests should be passing before starting a refactor and should be passing once the refactor is complete.</p> + +<p>If the tests fail, the refactor was unsuccessful and should be reverted.</p> + +<p>If you have a CI pipeline, a failing test should break the pipeline and prevent any further steps if you're doing continuous delivery or deployment whilst signalling to the Developer their refactor was unsuccessful and the pipeline needs to be fixed.</p> + +<p>Without tests, the code could be broken during the refactor, and we wouldn't know.</p> +
+
+ Sat, 09 Sep 2023 00:00:00 GMT +
+ + Do you proactively refactor your code? + + /daily/2023/09/08/do-you-proactively-refactor-your-code + http://localhost:8000/daily/2023/09/08/do-you-proactively-refactor-your-code + +
<p>Do you go back and proactively refactor code you've previously written?</p> + +<p>In my experience, once some code is working, it's usually not reviewed again and refactored, and any TODO comments are still there.</p> + +<p>If code is refactored, it's usually triggered by adding a new feature or fixing a bug that will be difficult or impossible with the existing code - i.e., make the change easy, then make the easy change.</p> + +<p>Very rarely have I seen code be refactored to make it easier to read or change in the future.</p> + +<p>Do you proactively refactor code or wait until something forces it to happen?</p> + +<p>Do you not refactor code at all once it's been written?</p> + +<p>Reply to this email and let me know which and why.</p> +
+
+ Fri, 08 Sep 2023 00:00:00 GMT +
+ + What's the simplest test to begin with? + + /daily/2023/09/07/what-s-the-simplest-test-to-begin-with + http://localhost:8000/daily/2023/09/07/what-s-the-simplest-test-to-begin-with + +
<p>When giving talks and workshops or coaching on automated testing and test-driven development, some people may not have written tests before and aren't familiar with the structure or know where to begin.</p> + +<p>In the workshops I ran for DrupalCamp London and DrupalCamp NYC, I wanted to cover this first before writing any implementation code.</p> + +<p>Where do you put a test class, and what does it contain?</p> + +<p>How do you run the tests, and how can you make it pass or fail?</p> + +<h2 id="what-we-did">What we did</h2> + +<p>To start, we wrote a test for existing functionality within Drupal core - anonymous users can visit the front page.</p> + +<p>This is the whole test:</p> + +<pre><code class="language-php">&lt;?php + +namespace Drupal\Tests\my_module\Functional; + +use Drupal\Tests\BrowserTestBase; +use Symfony\Component\HttpFoundation\Response; + +class MyModuleTest extends BrowserTestBase { + +  protected $defaultTheme = 'stark'; + +  /** @test */ +  public function the_front_page_loads_for_anonymous_users() { +    $this-&gt;drupalGet('&lt;front&gt;'); + +    $this-&gt;assertResponse(Response::HTTP_OK); +  } + +} +</code></pre> + +<p>This is a test someone can write, run and see the test pass.</p> + +<p>They can then experiment by changing the values to make the test fail in different ways.</p> + +<h2 id="what-next%3F">What next?</h2> + +<p>Then, we tested anonymous users cannot access the administration pages, which is also already the case in Drupal core, and then authenticated users with the correct permissions could access them.</p> + +<p>People were getting the idea by now, and we moved on to writing and testing some of our own code.</p> +
+
+ Thu, 07 Sep 2023 00:00:00 GMT +
+ + Verbosity over abstraction + + /daily/2023/09/06/verbosity-over-abstraction + http://localhost:8000/daily/2023/09/06/verbosity-over-abstraction + +
<p>Recently, a steamer said they "prefer verbosity over abstraction/confusion".</p> + +<p>In that scenario, it was regarding the name of a microservice they were creating. It was long and verbose, but it described what it did.</p> + +<p>It was clear to anyone working on that project what that service did, now and in the future.</p> + +<p>I prefer this to shorter, less-descriptive names.</p> + +<p>I hardly ever create a variable called <code>$x</code>, <code>$k</code> or <code>$v</code>. I only would if it was clear what it meant within its context.</p> + +<p>I like to write descriptive names for test methods that explain what the test is doing. Even if I start with a vague name, I'll refactor it to make it more specific and clearer.</p> + +<p>I prefer not to use PHP functions like <code>compact</code> and to write it out and avoid the abstraction and any confusion it could cause.</p> + +<p>I prefer code to be verbose, descriptive and easy to read, understand and change.</p> +
+
+ Wed, 06 Sep 2023 00:00:00 GMT +
+ + Spring clean before upgrading + + /daily/2023/09/05/spring-clean-before-upgrading + http://localhost:8000/daily/2023/09/05/spring-clean-before-upgrading + +
<p>One of the first things I do when reviewing whether an application can be upgraded is to look for anything that can be removed.</p> + +<p>If it has modules that aren't used, uninstall and remove them.</p> + +<p>A common one for Drupal projects is modules left from previous migrations, such as from Field Collections to Paragraphs that are no longer needed but still present.</p> + +<p>Remove any old themes that are no longer used.</p> + +<p>If you have content types, fields or views that aren't used, remove them, assuming you don't need the data.</p> + +<p>Remove unused routes, Controllers or other classes not used in custom modules, or any other unused custom code.</p> + +<p>Less moving parts means less to upgrade now and maintain in the future.</p> +
+
+ Tue, 05 Sep 2023 00:00:00 GMT +
+ + Charging more _not_ to write tests + + /daily/2023/09/04/charging-more-not-to-write-tests + http://localhost:8000/daily/2023/09/04/charging-more-not-to-write-tests + +
<p>Joel Clermont <a href="https://twitter.com/jclermont/status/1690872089878691840">replied to a previous email on Twitter</a>:</p> + +<blockquote> + <p>I once toyed around with the idea of charging MORE to do it without tests. Had a client pushing back very hard when I mentioned testing (what I thought they’d perceive as a benefit). Ended up not pursuing it, but was a fun idea at the time.</p> +</blockquote> + +<p>It's an interesting idea.</p> + +<p>How much more would you charge not to write tests?</p> + +<p>How much more would make you feel comfortable and confident that the application works as expected, and would cover the additional time for finding and fixing the bugs caused by not writing tests?</p> + +<p>As Joel said, it was a fun idea, though it seems counter-intuitive to me, as the a client would be paying more for worse software.</p> +
+
+ Mon, 04 Sep 2023 00:00:00 GMT +
+ + Including time for tests in estimates + + /daily/2023/09/03/including-time-for-tests-in-estimates + http://localhost:8000/daily/2023/09/03/including-time-for-tests-in-estimates + +
<p>If you need to provide estimates for your tasks and intend to write tests, include the time for the tests within the estimate.</p> + +<p>Don't provide different estimates for testing and the implementation, or with tests and without tests - give one estimate that includes both.</p> + +<p>Writing tests and testable code will take longer to begin with, but practice makes perfect and will save time in the long run.</p> +
+
+ Sun, 03 Sep 2023 00:00:00 GMT +
+ + Planning first or reviewing last? + + /daily/2023/09/02/planning-first-or-reviewing-last + http://localhost:8000/daily/2023/09/02/planning-first-or-reviewing-last + +
<p>Code reviews are something that happens after the code has been written.</p> + +<p>Usually, a task is assigned to someone who completes it and makes the code available to their team members before it's merged.</p> + +<p>The problem is that if the implementation is wrong, it's already been written, which costs time and money.</p> + +<p>If the change needed to be rewritten or majorly changed before it could be merged, as well as being an awkward conversation, it would cost more time and money to do.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>Instead of reviewing the code after it's been written, invest more time into planning how the implementation should work upfront.</p> + +<p>What potential solutions exist, and which would work best in this situation?</p> + +<p>Write technical design documents and, if needed, create a small proof of concept.</p> + +<p>Do this as a team and decide on the approach before any code is written and time or effort is spent.</p> + +<p>Then, the person working on the task will have a clearer path, and if you still want to review the code afterwards, you have something to compare it to.</p> +
+
+ Sat, 02 Sep 2023 00:00:00 GMT +
+ + Non-blocking code reviews + + /daily/2023/09/01/non-blocking-code-reviews + http://localhost:8000/daily/2023/09/01/non-blocking-code-reviews + +
<p>If your team wants or needs to do code reviews, but you don't want it to slow down development, you could implement non-blocking code reviews.</p> + +<p>Instead of creating a topic branch for a feature or fix, creating a pull or merge request and waiting for it to be reviewed before merging, the commit is merged, and the code is reviewed afterwards.</p> + +<p>The ticket workflow could look like this:</p> + +<p>To Do -> Doing -> Merged -> Reviewed -> Tested -> Deployed</p> + +<p>Or:</p> + +<p>To Do -> Doing -> Merged -> Deployed -> Tested -> Reviewed</p> + +<p>The focus is getting the update to production, and the review is deferred.</p> + +<p>The same CI pipeline rules apply - it must be passing before the code can be deployed, so the same quality checks are run.</p> + +<p>With this approach, the code is still reviewed, either in the pull or merge request or by the commits on the mainline branch if doing trunk-based development. It's just done later.</p> +
+
+ Fri, 01 Sep 2023 00:00:00 GMT +
+ + Code review discourages small refactorings + + /daily/2023/08/31/code-review-discourages-small-refactorings + http://localhost:8000/daily/2023/08/31/code-review-discourages-small-refactorings + +
<p>If, for every change to a codebase, you need to create a topic branch, create a pull or merge request, and wait for it to be reviewed and approved - which can take days, weeks or longer - does that discourage you from making small changes?</p> + +<p>If you spotted some unused commented-out code whilst working in a file and went to remove it or a variable that could have a better name, would you change it?</p> + +<p>Alternatively, if you could make the change and know from a CI pipeline within a few minutes that everything still worked, would you be more likely to make the change and improve the code for yourself and the next person to work on it?</p> +
+
+ Thu, 31 Aug 2023 00:00:00 GMT +
+ + TDD and "Unexpected errors" + + /daily/2023/08/30/tdd-and-unexpected-errors + http://localhost:8000/daily/2023/08/30/tdd-and-unexpected-errors + +
<p>When working on projects, it's common to see messages like "The website encountered an unexpected error. Please try again later.".</p> + +<p>Usually, this is the message shown to the user, whilst a more detailed error message is logged for Developers to diagnose and fix the underlying error.</p> + +<p>The wording "unexpected error" has been intriguing to me, though. When do you expect an error?</p> + +<p>The best example I can think of for an expected error is when doing test-driven development.</p> + +<p>When doing TDD, you want to see an error to start as you start with a failing test.</p> + +<p>Then, you write the code to remove the error and get the test passing.</p> +
+
+ Wed, 30 Aug 2023 00:00:00 GMT +
+ + CI pipelines are an automated code review + + /daily/2023/08/29/ci-pipelines-are-an-automated-code-review + http://localhost:8000/daily/2023/08/29/ci-pipelines-are-an-automated-code-review + +
<p>I've worked on various teams over the last 13 years on which we've needed to do feature branches, pull requests and code reviews.</p> + +<p>If the request isn't approved by (usually) two people, it won't be merged.</p> + +<p>Instead of focusing on the problem that needed to be solved and how I'd done it, many reviews focused on the small details.</p> + +<p>Do the lines have the correct number of spaces before them?</p> + +<p>Do the comments end with a full stop?</p> + +<p>Do the lines wrap at the correct point, and are your variable names in the right case?</p> + +<p>Essentially, does the code comply with the agreed coding standards?</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>Whilst important (you want the code to follow standards and be in a consistent format), doing these checks manually is not a good use of time and is not what the code review should focus on.</p> + +<p>These checks can be automated using CI pipelines or Git hooks to run tools like PHPCS to review and sometimes fix coding standards issues.</p> + +<p>Automating these checks means the Developers can focus on what they should be reviewing.</p> + +<p>How are they solving the problem, not how many spaces is the code indented by.</p> +
+
+ Tue, 29 Aug 2023 00:00:00 GMT +
+ + Pair and mob programming are continuous code review + + /daily/2023/08/28/pair-and-mob-programming-are-continuous-code-review + http://localhost:8000/daily/2023/08/28/pair-and-mob-programming-are-continuous-code-review + +
<p>Instead of a single Developer working individually on a task and submitting it for a code review once complete, try pair or mob programming.</p> + +<p>As well as extra benefits, such as knowledge sharing and shared responsibility for the code, it's continuously reviewed and improved during the session.</p> + +<p>Questions from "Why are we doing it this way?" to "What's the best name for this variable?" can be asked, and decisions and changes can be made in real time.</p> + +<p>So, why wait until the task is complete and the code is harder to change to review it?</p> + +<p>Why not have it reviewed as it's written?</p> +
+
+ Mon, 28 Aug 2023 00:00:00 GMT +
+ + Pull requests are great for open-source, but not for teams + + /daily/2023/08/27/pull-requests-are-great-for-open-source + http://localhost:8000/daily/2023/08/27/pull-requests-are-great-for-open-source + +
<p>Code review with pull and merge requests is great for open-source but not for development teams or soloists.</p> + +<p>On an open-source project, the code changes are most likely being submitted by someone you don't know and don't work with regularly, so having a step to review the code prior to merging it and decide if you want to take on the responsibility of maintaining it is a big decision.</p> + +<p>On a development team, you work closely with the person submitting the change request and you have a shared responsibility and ownership of the code being added. The person isn't going to submit their change and not be seen again.</p> + +<p>It takes time for code to be reviewed, which means it takes longer for the change to be released to users.</p> + +<p>If you're a soloist, are you going to submit a request for you to review your own code?</p> + +<p>If you don't need to do code review on your team, do you need to create feature or topic branches?</p> + +<p>I'd suggest sticking to one canonical branch and doing trunk-based development instead.</p> +
+
+ Sun, 27 Aug 2023 00:00:00 GMT +
+ + Work in small chunks + + /daily/2023/08/26/work-in-small-chunks + http://localhost:8000/daily/2023/08/26/work-in-small-chunks + +
<p>When working with teams who are starting with automated tests and test-driven development, a common issue is being stuck on a failing test and trying to do too much at once while getting it to pass.</p> + +<p>Instead, work in small bite-sized iterations and focus on getting the test to pass as quickly as possible.</p> + +<p>You don't want to be stuck in a failing state for longer than you need to.</p> + +<p>Run the tests often, write the simplest code possible and don't write any code that isn't driven by a failing test.</p> + +<p>Don't make too many changes at once so, if you do get stuck, you can reset to the last working test and start again.</p> +
+
+ Sat, 26 Aug 2023 00:00:00 GMT +
+ + Start with a vague test + + /daily/2023/08/25/start-with-a-vague-test + http://localhost:8000/daily/2023/08/25/start-with-a-vague-test + +
<p>If you're unsure what your first test should be, start by writing a test with an intentionally vague name, just to get you started.</p> + +<p>It may not be clear straight away what you're trying to test.</p> + +<p>Once you get the ball rolling and you write more, it will usually become clear.</p> + +<p>Then you can go back and rename the test method to something more specific.</p> +
+
+ Fri, 25 Aug 2023 00:00:00 GMT +
+ + Testing multiple implementations with contract tests + + /daily/2023/08/24/testing-multiple-implementations-with-contract-tests + http://localhost:8000/daily/2023/08/24/testing-multiple-implementations-with-contract-tests + +
<p>If you have multiple implementations of a service, as I <a href="http://localhost:8000/daily/2023/08/23/dont-use-third-party-services-directly">mentioned in yesterday's email</a>, you need to ensure they all provide the same functionality.</p> + +<p>You need to be able to run the same tests against each implementation and have them pass.</p> + +<h2 id="how-do-you-do-this%3F">How do you do this?</h2> + +<p>In PHP, I use a trait that contains the test methods and then have a test class for each implementation that uses the trait and sets up any test data.</p> + +<p>Then, each test class will run the methods from the contract test trait and ensure they all provide the same behaviour, regardless of how it does so - whether it communicates with an API, uses an SDK, or returns fake values.</p> + +<p>If one implementation doesn't return the same result as the others, its test will fail.</p> + +<p>If you add a new implementation, you create a new test class, use the trait and get the tests to pass.</p> +
+
+ Thu, 24 Aug 2023 00:00:00 GMT +
+ + Don't use third-party services directly + + /daily/2023/08/23/dont-use-third-party-services-directly + http://localhost:8000/daily/2023/08/23/dont-use-third-party-services-directly + +
<p>If you need to integrate your application with a third-party service, don't integrate it directly - even if it has an SDK.</p> + +<p>Doing so locks you into a single implementation and a single vendor.</p> + +<p>If you write your own integration layer, it's possible to add multiple implementations, such as different payment gateways, that conform with a single interface.</p> + +<p>If you need to switch to a different provider or add multiple options, you can without changing the existing code.</p> + +<p>It also makes it easier to test as you can write a fake implementation and use it for testing.</p> +
+
+ Wed, 23 Aug 2023 00:00:00 GMT +
+ + Deployments with your CI pipeline + + /daily/2023/08/22/deployments-with-your-ci-pipeline + http://localhost:8000/daily/2023/08/22/deployments-with-your-ci-pipeline + +
<p>You have a CI pipeline in your project.</p> + +<p>Every time you push a commit, the CI pipeline runs and performs its checks.</p> + +<p>It runs the automated tests and verifies they pass, statically analyses the code to identify any issues and validates the code follows the correct coding style and standards.</p> + +<p>Everything passes.</p> + +<h2 id="what-next%3F">What next?</h2> + +<p>If the pipeline passes, your change is deployable.</p> + +<p>So, why not extend the pipeline to deploy the change once the checks pass?</p> + +<p>If the checks don't pass, don't deploy.</p> + +<p>It could be as simple as pushing the code to an S3 bucket, a separate Git branch or repository for managing deployments, or creating an artifact like a Docker image.</p> + +<p>Instead of waiting for someone to do this manually, remove a step and automate it within the pipeline.</p> + +<p>The sooner it's deployed, the sooner it provides value for your application's users.</p> +
+
+ Tue, 22 Aug 2023 00:00:00 GMT +
+ + Which part of the CI pipeline has the most value? + + /daily/2023/08/21/which-part-of-the-ci-pipeline-has-the-most-value + http://localhost:8000/daily/2023/08/21/which-part-of-the-ci-pipeline-has-the-most-value + +
<p>The main part of my CI pipeline tasks are running the automated tests, coding standards checks and static analysis.</p> + +<p>But which of these are the most valuable?</p> + +<p>I used to run the coding standards check first as it was the quickest, followed by static analysis and the automated tests.</p> + +<p>If a task were going to fail, it would fail quickly.</p> + +<p>But is that the objective of the CI pipeline?</p> + +<p>Whilst it needs to be quick, the main reason to run these is to ensure things work as expected.</p> + +<p>Recently, I changed my pipelines to run the tests first, as these verify the code's behaviour.</p> + +<p>I want to know if the code works but has a coding standard error that needs to be fixed rather than the pipeline failing on the error and not knowing whether the code works.</p> +
+
+ Mon, 21 Aug 2023 00:00:00 GMT +
+ + PHP types and assertions + + /daily/2023/08/20/php-types-and-assertions + http://localhost:8000/daily/2023/08/20/php-types-and-assertions + +
<p>Following yesterday's email about input validation, guard clauses and assertion libraries, these can be used to compliment PHP's native types and checking.</p> + +<p>For example:</p> + +<pre><code class="language-php">function createJourney(string $from, string $to, int $duration): void { + var_dump($from, $to, $duration); +} +</code></pre> + +<p>In this code, each parameter has a type, but there's no validation on the values.</p> + +<p>If I run this:</p> + +<pre><code class="language-plain">createJourney('', '', -10); +</code></pre> + +<p>I would get this output:</p> + +<pre><code class="language-plain">string(0) "" +string(0) "" +int(-10) +</code></pre> + +<p>This is probably not what you want.</p> + +<p>I expect <code>$to</code> and <code>$from</code> to be not empty and the duration to be greater than zero.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>I can use an assertion library or throw my own Exceptions if the values pass the type checks but aren't what I need.</p> + +<p>For example:</p> + +<pre><code class="language-php">function createJourney(string $from, string $to, int $duration): void { + Assert::stringNotEmpty($from); + Assert::stringNotEmpty($to); + Assert::positiveInteger($duration); + + var_dump($from, $to, $duration); +} +</code></pre> + +<p>Now, if an empty string or negative duration is passed - in my implementation or test code - an Exception will be thrown.</p> +
+
+ Sun, 20 Aug 2023 00:00:00 GMT +
+ + Asserting all the things + + /daily/2023/08/19/asserting-all-the-things + http://localhost:8000/daily/2023/08/19/asserting-all-the-things + +
<p>As well as assertions within tests, you can also check within implementation code that things are in an expected state or contain a certain value.</p> + +<p>In PHP, this is done by throwing an Exception if a condition is met.</p> + +<p>For example:</p> + +<pre><code class="language-php">if (!is_array(false)) { + throw new \Exception('Not an array'); +} +</code></pre> + +<p>There's also the <code>assert</code> construct which, since PHP 8.0, throws an Exception by default:</p> + +<pre><code class="language-php">assert(is_array(false)); +</code></pre> + +<p>You can also use an assertion library, such as <code>webmozart/assert</code> or <code>beberlei/assert</code> which provide assertions and guard methods:</p> + +<pre><code class="language-php">use Webmozart\Assert\Assert; + +Assert::isArray(false); +</code></pre> + +<p>Similarly, if the condition fails, it throws an Exception that can be caught elsewhere.</p> + +<p>As well as basic assertions such as the item is the expected type or don't match the condition, there are more complex assertions, such as all items within an array are a certain type or that an integer is positive.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>I use guard conditions a lot within my code. If something is not as I'd expect, I like for an error to be thrown. This makes is easy to test and to debug any failures compared to failing silently.</p> +
+
+ Sat, 19 Aug 2023 00:00:00 GMT +
+ + Types vs tests + + /daily/2023/08/18/types-vs-tests + http://localhost:8000/daily/2023/08/18/types-vs-tests + +
<p>Today, I saw a Twitter poll - "Poll: Imagine your team has to build a project with either types or tests. You can't have both.".</p> + +<p>The results were:</p> + +<ul> +<li>Types - 50.9%</li> +<li>Tests - 49.1%</li> +</ul> + +<p>I was surprised by this.</p> + +<p>If I can't add type declarations (type hints) or return types, I can still write tests to check what happens when different types are used.</p> + +<p>I can also perform checks and throw errors or Exceptions if something isn't what I'd expect.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>I use types heavily, but I'd be more confident that my application would work if I had tests and that's the main objective when writing code for myself or clients and for my clients' customers.</p> +
+
+ Fri, 18 Aug 2023 00:00:00 GMT +
+ + Writing custom assertions in your tests + + /daily/2023/08/17/writing-custom-assertions-in-your-tests + http://localhost:8000/daily/2023/08/17/writing-custom-assertions-in-your-tests + +
<p>Writing custom assertions is a great way to clean up your test code.</p> + +<p>Here's an example from one of my client Drupal projects:</p> + +<pre><code class="language-php">private static function assertProductVariationHasPrice(ProductVariationInterface $productVariation, string $expectedPrice): void { + self::assertSame( + actual: $productVariation-&gt;getPrice()-&gt;getNumber(), + expected: $expectedPrice, + ); +} +</code></pre> + +<p>This one wraps a single assertion, but they could also include multiple assertions or additional steps.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>A custom assertion is a simple function but it makes the test code more readable and less repetitive.</p> +
+
+ Thu, 17 Aug 2023 00:00:00 GMT +
+ + Writing tests in your own time + + /daily/2023/08/16/writing-tests-in-your-own-time + http://localhost:8000/daily/2023/08/16/writing-tests-in-your-own-time + +
<p>This is how I first started writing automated tests.</p> + +<p>I was working at a well-known digital agency, so I didn't want it to affect my output and cause me to deliver work late.</p> + +<p>I wanted to give myself a less pressured opportunity to learn and experiment with different options and approaches.</p> + +<p>So, when creating custom modules, I wrote the implementation code during billable work hours and the tests during the evening on my own time.</p> + +<p>I was investing time in learning a new skill and one that I knew would pay dividends.</p> + +<h2 id="how-did-it-go%3F">How did it go?</h2> + +<p>As I'd already written the implementation code, I wasn't doing test-driven development, so most of the tests were confirming what I'd written was correct with a passing test and being able to make it fail in expected ways.</p> + +<p>One time, though, I found a bug I'd written that day. I think it was an incorrect permission name I'd typed.</p> + +<p>I was able to fix it before it was submitted for quality assurance checks and client testing.</p> + +<p>This saved the time and effort of creating another issue and branch, fixing it, going through the development cycle again and having it re-tested.</p> + +<p>After that, for me, there was no going back.</p> +
+
+ Wed, 16 Aug 2023 00:00:00 GMT +
+ + Writing test and implementation code are the same task + + /daily/2023/08/15/writing-test-and-implementation-code-are-the-same-task + http://localhost:8000/daily/2023/08/15/writing-test-and-implementation-code-are-the-same-task + +
<p>In Sunday's email, I said not to estimate separately for testing and implementation.</p> + +<p>But you can't do this anyway if you're doing test-driven development.</p> + +<p>With TDD, you aren't writing all of your tests and all of the implementation code or vice versa.</p> + +<p>You're continuously switching back and forth, starting by writing a failing test and then enough implementation code for it to pass.</p> + +<p>Then you write more test code, whether expanding the same test or writing a new one until you have a new failure.</p> + +<p>You get it to pass, refactor, and repeat the process until the task is complete.</p> + +<p>It's all part of the same task and the same estimate.</p> +
+
+ Tue, 15 Aug 2023 00:00:00 GMT +
+ + Why keep a dotfiles repository + + /daily/2023/08/14/why-keep-a-dotfiles-repository + http://localhost:8000/daily/2023/08/14/why-keep-a-dotfiles-repository + +
<p>A few days ago, I said it's been 8 years since I started my dotfiles repository and kept my configuration files in version control.</p> + +<p>But why do this?</p> + +<p>It makes it easy to share the same configuration across multiple computers, such as personal and work or different setups for clients.</p> + +<p>It's also a backup of my configuration. If I need to reinstall my system or start fresh, I don't need to configure everything again manually.</p> + +<p>If you work on a team, you can share your configuration with colleagues, find things that work well for the team, and anything that could cause issues.</p> + +<p>I can experiment with settings and programs and then revert them again if I want to.</p> + +<p>If I add or remove a program or change a setting, I have the Git commit log to refer to, and I can document why I made that change and what problem it solves in the message body.</p> + +<p>I can review it in the future in case I need it again or want to re-evaluate it.</p> + +<p>Like on other projects, capturing the why is important. The what can be seen in the diff and speaks for itself, but it doesn't explain why I made the change and won't help if I look at that commit in the future.</p> +
+
+ Mon, 14 Aug 2023 00:00:00 GMT +
+ + Don't estimate separately for testing + + /daily/2023/08/13/dont-estimate-separately-for-testing + http://localhost:8000/daily/2023/08/13/dont-estimate-separately-for-testing + +
<p>A common issue I see for people introducing automated testing is estimating the testing and implementation of a task separately.</p> + +<p>Something like, "It'll take x to do the work and x to write the tests for it".</p> + +<p>As well as implying you'd write all the code and then all the tests or vice versa, if you provide separate estimates, it's easy for someone to think the tests are optional and can be removed.</p> + +<p>If you provide one estimate for both, this can't happen.</p> +
+
+ Sun, 13 Aug 2023 00:00:00 GMT +
+ + Does not writing tests actually save time? + + /daily/2023/08/12/does-not-writing-tests-actually-save-time + http://localhost:8000/daily/2023/08/12/does-not-writing-tests-actually-save-time + +
<p>One of the most common responses to why people don't write tests is, "We don't have time".</p> + +<p>So, people don't write tests to save time.</p> + +<p>While you write less code, it takes longer to test everything manually during development, and more bugs will likely be found, which also takes longer to diagnose, fix, and deal with any knock-on effects.</p> + +<p>Bugs in production add more administrative overhead as new tickets need to be created and managed, prevent other tickets from being worked on whilst they're fixed, and require more communication with clients and customers.</p> + +<p>If you save ten minutes not writing a test but spend hours later fixing bugs, you didn't save time - you spent more time in the end.</p> +
+
+ Sat, 12 Aug 2023 00:00:00 GMT +
+ + Everyone tests their code + + /daily/2023/08/11/everyone-tests-their-code + http://localhost:8000/daily/2023/08/11/everyone-tests-their-code + +
<p>No one writes code, commits it and pushes it to production without checking it works.</p> + +<p>Everyone tests their code, but they usually do it manually. They switch from the code to a browser or terminal, run the code and evaluate the result.</p> + +<p>This takes time, you need to switch contexts, and the test is only valid for that time.</p> + +<p>There's no guarantee it will still work in the future.</p> + +<p>Automated testing, however, means you can write your code and run the tests without leaving your IDE or text editor.</p> + +<p>The tests can also be run in the future to ensure the functionality works without needing to re-test it manually.</p> +
+
+ Fri, 11 Aug 2023 00:00:00 GMT +
+ + Testing, fixed prices and bug-free guarantees + + /daily/2023/08/10/testing-fixed-prices-and-bug-free-guarantees + http://localhost:8000/daily/2023/08/10/testing-fixed-prices-and-bug-free-guarantees + +
<p>After my lightning talk for NWDUG on automated testing with Drupal, the question was asked how to find time for testing.</p> + +<p>I've written about whether you need to ask for permission to write tests (you don't), and if you need to estimate tasks, you include the time and effort for writing tests as part of the estimate.</p> + +<p>As someone who only offers fixed prices for development work and covers all work with a bug-free guarantee, I want to provide well-written, stable and tested software to my customers so I'm not always fixing bugs for free.</p> +
+
+ Thu, 10 Aug 2023 00:00:00 GMT +
+ + Vim is my lightsaber + + /daily/2023/08/09/vim-is-my-lightsaber + http://localhost:8000/daily/2023/08/09/vim-is-my-lightsaber + +
<p>"Vim is my lightsaber" is the final chapter of Jess Archer's Neovim course on Laracasts.</p> + +<p>In this summary, Jess explains that some Star Wars characters have lightsabers customised to their preferences and fighting style.</p> + +<p>Vim is her lightsaber - her personalised tool to develop software. It works how she wants it to, with the features she needs.</p> + +<p>I've written before about mastering your tools and thought this was a nice spin on it.</p> +
+
+ Wed, 09 Aug 2023 00:00:00 GMT +
+ + 8 years of dotfiles + + /daily/2023/08/08/8-years-of-dotfiles + http://localhost:8000/daily/2023/08/08/8-years-of-dotfiles + +
<p>Today, I was speaking with a colleague about configuring Git, which led to a conversation about dotfiles repositories - somewhere where you version the changes to your configuration files and, usually, create symlinks to their expected locations.</p> + +<p>Afterwards, I realised the first commit to <a href="https://github.com/opdavies/dotfiles">my personal dotfiles repository</a> was over eight years ago, in July 2015.</p> + +<p>What started as a repository to put my own Git configuration has undergone various changes since the tools I use changed, as well as the tools to manage the files themselves.</p> + +<p>In 2021, I switched my <code>.vimrc</code> configuration file to an <code>init.vim</code> and switched to Neovim full-time and, most recently, almost a year ago, I started to use the Nix package manager and later adopted NixOS as my primary Linux distribution.</p> + +<p>My complete NixOS and Home Manager configurations are within my <code>dotfiles</code> repository, and the configuration for tools, including Neovim, tmux and Git.</p> + +<p>I wonder what the repository will look like in another eight years...</p> +
+
+ Tue, 08 Aug 2023 00:00:00 GMT +
+ + Tests make upgrades less risky + + /daily/2023/08/07/tests-make-upgrades-less-risky + http://localhost:8000/daily/2023/08/07/tests-make-upgrades-less-risky + +
<p>I recently reviewed a client's Drupal 9 website to gather information about upgrading it to Drupal 10.</p> + +<p>The site has a number of custom modules. They all require changes to make them Drupal 10-compatible - some are a <code>core_version_requirement</code> update, and some are more complex.</p> + +<p>None of these modules have automated tests.</p> + +<p>If they did, I'd be able to run them and see if they pass, make my changes, and rerun the tests to verify they still pass, and the module works as it did previously.</p> + +<p>But I can't.</p> + +<p>I'd need to test each module beforehand to understand what it does manually and again after making the changes.</p> + +<p>This is more time-consuming, riskier, and easier for me to introduce regressions or new bugs.</p> + +<p>Whether it's a major CMS version upgrade, updating a contrib module or refactoring custom code, having a passing test suite you can use and rely on makes updates easier and less risky.</p> +
+
+ Mon, 07 Aug 2023 00:00:00 GMT +
+ + YAGNI + + /daily/2023/08/06/yagni + http://localhost:8000/daily/2023/08/06/yagni + +
<p>During a recent coding dojo session, we spoke about YAGNI - i.e. "You aren't going to need it".</p> + +<p>In this situation, we discussed how many numbers we should support passing to a method in a calculator application.</p> + +<p>There's no technical limit to how many numbers we can pass, but there may be a requirement where we only need to add or subtract two numbers. In that case, we don't need to calculate three or more numbers, so there's no benefit or value to writing that code.</p> + +<p>If we follow YAGNI, we only write the code we need now. If the requirement changes in the future, we revisit the code and change it as required.</p> +
+
+ Sun, 06 Aug 2023 00:00:00 GMT +
+ + Use Drupal to own your content + + /daily/2023/08/05/use-drupal-to-own-your-content + http://localhost:8000/daily/2023/08/05/use-drupal-to-own-your-content + +
<p>I recently saw a social media website taking over users' handles - renaming them so they can have the original.</p> + +<p>There were similar issues a few years ago when a website put a paywall in front of articles written by its users.</p> + +<p>I've written articles and tutorials for companies I've worked at that no longer exist as the companies have been acquired.</p> + +<p>This is why I like to own and control my content. I still have my versions if a website shuts down or changes its settings.</p> + +<p>There are a lot of options, including Drupal, that can help with this. It's easy to set up a new website and start publishing your own content.</p> +
+
+ Sat, 05 Aug 2023 00:00:00 GMT +
+ + Laravel Prompts and framework-agnostic tools + + /daily/2023/08/04/laravel-prompts + http://localhost:8000/daily/2023/08/04/laravel-prompts + +
<p>Although I wouldn't consider myself a "Laravel Developer", I enjoy watching the talks from the Laravel conferences, most recently Laracon US, that took place last week.</p> + +<p>I like to learn from other communities and adopt relevant practices or tools within my projects. I did this with Laravel Collections, which I use on nearly every project.</p> + +<h2 id="laravel-prompts">Laravel Prompts</h2> + +<p>I watched Jess Archer's talk on the new Laravel Prompts and was happy when she said it worked with plain PHP projects, not just Laravel.</p> + +<p>This means I can potentially use it with Drupal and Drush or Symfony Console applications, too, such as my build configuration file generator.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>I like tools I can use across multiple technologies and prefer framework-agnostic tools to specific ones.</p> + +<p>Then, if I'm working on a different project, I can use the tools I already know instead of learning something different just for that.</p> +
+
+ Fri, 04 Aug 2023 00:00:00 GMT +
+ + What problem are we trying to solve? + + /daily/2023/08/03/what-problem-are-we-trying-to-solve + http://localhost:8000/daily/2023/08/03/what-problem-are-we-trying-to-solve + +
<p>The Override Node Options module that I said yesterday is used on 35,000 websites isn't a complicated module.</p> + +<p>It doesn't add a lot of features.</p> + +<p>It solves a problem by adding additional permissions to Drupal that site administrators can assign to user roles and not need to give a more global permission like "administer nodes".</p> + +<p>The problem being solved is allowing users to set node options, like the created date or published status, in a secure way.</p> + +<p>If it wasn't solving a problem, it wouldn't be used on 35,000 Drupal websites.</p> +
+
+ Thu, 03 Aug 2023 00:00:00 GMT +
+ + A crash course into automated testing with Drupal + + /daily/2023/08/02/a-crash-course-into-drupal-testing + http://localhost:8000/daily/2023/08/02/a-crash-course-into-drupal-testing + +
<p>Next week, I'll be presenting a lightning talk at the North West Drupal user group (NWDUG)'s August meetup.</p> + +<p>It'll be a crash course into automated testing and test-driven development with Drupal, in which I plan to show how to get started by writing and running your first automated tests whilst exploring the different types of tests that are available.</p> + +<p>It's an online event, so if you'd like to see this and other lightning talks on the 8th of August, RSVP at <a href="https://www.meetup.com/nwdrupal/events/293429104">https://www.meetup.com/nwdrupal/events/293429104</a>.</p> +
+
+ Wed, 02 Aug 2023 00:00:00 GMT +
+ + Maintaining a module used on 35,000 Drupal websites + + /daily/2023/08/01/maintaining-a-module-used-on-35000-drupal-websites + http://localhost:8000/daily/2023/08/01/maintaining-a-module-used-on-35000-drupal-websites + +
<p>Note: The numbers within this post are taken from my <a href="http://localhost:8000/presentations/tdd-test-driven-drupal">Test-Driven Drupal talk</a>, in which I also talk about this.</p> + +<p>My first commit to the 7.x-1.x branch of the Override Node Options module was in March 2012. According to Drupal.org, the module was used on 9,212 websites then.</p> + +<p>As well as the 7.x-1.x branch, there's the 8.x-2.x branch which supports Drupal 9 and 10, and previously Drupal 8.</p> + +<p>The most recent statistics show the module is currently used on 34,981 websites and is consistently around 35,000.</p> + +<h2 id="what-does-that-mean%3F">What does that mean?</h2> + +<p>The module is considered feature complete, but I'm not ruling out any new additions.</p> + +<p>The main thing is ensuring that any changes don't break 35,000 websites!</p> + +<p>I do this by relying on the module's automated test suite and ensuring that tests are added for any features or bugs and that the tests are passing before any new release.</p> + +<h2 id="this-has-worked-well">This has worked well</h2> + +<p>A few years ago, I committed a feature request to both versions. While it didn't include additional tests, I verified the existing functionality worked after resolving a large merge conflict by ensuring the original tests passed.</p> + +<p>More recently, a colleague and I refactored the module and split each override into its own class, making adding and maintaining overrides easier.</p> + +<p>Because the tests were still passing, we knew our refactor was successful and not causing regressions.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>Having automated tests and ensuring they're always passing has allowed me to add features and refactor code that I wouldn't have done or had the confidence to do otherwise.</p> + +<p>It's great to have a popular module, but on the other hand, I don't want to break 35,000 websites which makes the tests invaluable.</p> +
+
+ Tue, 01 Aug 2023 00:00:00 GMT +
+ + Upgrading from Drupal 9 is easier + + /daily/2023/07/31/upgrading-from-drupal-9-is-easier + http://localhost:8000/daily/2023/07/31/upgrading-from-drupal-9-is-easier + +
<p>Upgrading from Drupal 7 to 8, 9 or 10 is a large task.</p> + +<p>You must create a new empty site, migrate your configuration and data, manually recreate anything else and rewrite any custom modules and themes.</p> + +<p>Since Drupal 8, things have been different.</p> + +<p>You can upgrade your site in place.</p> + +<p>No large code rewrite or data migration.</p> + +<p>Many contributed modules support multiple major Drupal versions simultaneously, so they may not need upgrading.</p> + +<p>Others may need small changes to remove deprecated code or be compatible with breaking changes, and some tools can automate some or all of the changes.</p> + +<p>The same applies to upgrading from Drupal 9 to 10, which will be the same for Drupal 11 next year.</p> +
+
+ Mon, 31 Jul 2023 00:00:00 GMT +
+ + Commit often, deploy often + + /daily/2023/07/30/commit-often-deploy-often + http://localhost:8000/daily/2023/07/30/commit-often-deploy-often + +
<p>This is a follow-up to Friday's email on generic commit messages.</p> + +<p>In it, I discussed seeing commit messages like <code>Week 20 development</code> in project codebases and why you should write specific commit messages for each change.</p> + +<p>The other thing is, if you only commit once a week, you can only deploy once a week.</p> + +<p>That's assuming that, on this project, the changes were committed and pushed weekly. It could have been fortnightly, monthly, or longer.</p> + +<p>As well as increasing the time between deployments, it also makes them larger and riskier as they include more changes.That commit changed 106 files, added 3,453 lines and removed 17 lines, including custom module and theme changes, contrib module updates and library updates.</p> + +<p>If the dependencies were committed and deployed first, followed by a series of small custom module and theme changes, even though it would have resulted in more deployments, each one would have been smaller and less risky as there were fewer changes and easier to debug any issues and fail forward.</p> + +<p>I recommend that everyone commits and pushes their changes at least daily and use trunk-based development so everyone's changes apply and work together and are not separated on feature branches.</p> + +<p>I recommend that they're pushed live daily, too, or as often as possible - reducing the chance of errors and getting the changes providing value for customers and clients.</p> +
+
+ Sun, 30 Jul 2023 00:00:00 GMT +
+ + Should I wait to upgrade from Drupal 7? + + /daily/2023/07/29/should-i-wait-to-upgrade-from-drupal-7 + http://localhost:8000/daily/2023/07/29/should-i-wait-to-upgrade-from-drupal-7 + +
<p>It was announced at DrupalCon that Drupal 7 support was being extended one final time until January 2025.</p> + +<p>But if you have a Drupal 7 website, does that mean you should wait to start upgrading it?</p> + +<p>I recommend starting the process as soon as possible.</p> + +<p>Even though Drupal core support is extended, I've looked at projects that use modules marked as unsupported by their maintainers for some time as they focus on versions for Drupal 8, 9 or 10.</p> + +<p>In that case, those modules will have no new features, bug fixes or security updates, although Drupal core support has been extended.</p> + +<p>You may have a lot of custom code that needs to be ported to Drupal 10 or a complex data structure that needs to be migrated, These things will take time, so it's best not to leave it until the last minute.</p> + +<p>If you're stuck on Drupal 7, book an <a href="http://localhost:8000/call">upgrade consultation call</a> with me or purchase an <a href="http://localhost:8000/drupal7">upgrade roadmap for your project</a> and I'll get you unstuck.</p> +
+
+ Sat, 29 Jul 2023 00:00:00 GMT +
+ + Don't write generic commit messages + + /daily/2023/07/28/generic-commit-messages + http://localhost:8000/daily/2023/07/28/generic-commit-messages + +
<p>This week, I reviewed a client's Drupal 9 website and investigated the steps needed to upgrade to Drupal 10.</p> + +<p>Looking at the Git log to see the commits for a module, I see <code>Week 20 development</code> as the latest commit message.</p> + +<p>That commit changed 106 files, added 3,453 lines and removed 17 lines, including custom module and theme changes, contrib module updates and library updates.</p> + +<p>This should have been several commits, each performing a clear and separate task.</p> + +<p>Updating the dependencies should be its own commit, and changes to the custom modules and theme should be separate commits.</p> + +<p>Then each commit could have its own clear and descriptive commit message explaining why each change was needed, any issues that were encountered during the change, and any alternative approaches considered.</p> + +<p>That would have been more useful for me whilst looking through the logs than <code>Week 20 development</code>.</p> +
+
+ Fri, 28 Jul 2023 00:00:00 GMT +
+ + Stick to conventions + + /daily/2023/07/27/stick-to-conventions + http://localhost:8000/daily/2023/07/27/stick-to-conventions + +
<p>If you're performing a task as there's already a convention on how to do it, stick to it.</p> + +<p>If the codebase follows a particular coding standard, use it.</p> + +<p>If a project uses repository classes instead of interacting directly with a database, do that with your code too.</p> + +<p>If you use a framework with a service container and uses dependency injection, do that instead of manually creating classes.</p> + +<p>If you need additional functionality for a Drupal project and there's an established and well-known module that adds it, use it. Unless it doesn't meet your needs, in which case, document why that's the case and why you used a different module or wrote a custom implementation.</p> + +<p>If you need to create a content listing page, use the Views module, which is a standard approach. If not, document why and then explore other solutions.</p> + +<p>If you don't follow a convention, it will be harder for you or others to work on it in the future.</p> +
+
+ Thu, 27 Jul 2023 00:00:00 GMT +
+ + Prove the concept + + /daily/2023/07/26/prove-the-concept + http://localhost:8000/daily/2023/07/26/prove-the-concept + +
<p>When starting a new task, find the simplest way to prove the concept.</p> + +<p>Investigate upfront and evaluate potential approaches.</p> + +<p>What's the smallest or quickest thing you could do to validate an idea?</p> + +<p>It could be a small script that you can run and verify something works before moving it to its correct place within your application or creating a first implementation with hard-coded data that you refactor once you've proven the concept.</p> + +<p>If you can't, you'll know it won't work without investing a large amount of time and you can move on to the next potential approach.</p> +
+
+ Wed, 26 Jul 2023 00:00:00 GMT +
+ + Working backwards + + /daily/2023/07/25/working-backwards + http://localhost:8000/daily/2023/07/25/working-backwards + +
<p>Today, I did a show-and-tell session with my team where I demonstrated an integration I've been working on for a few months and recently released to production.</p> + +<p>The simplified workflow is we collate some data, send it to a third-party system for translation, receive the translated file and import the translations into Drupal's translation system.</p> + +<h2 id="where-did-i-start%3F">Where did I start?</h2> + +<p>The first thing I did was not to collate the data and generate the file but to send a minimal, hard-coded version of the contents to the third-party system.</p> + +<p>I'd have started with the code to import the translated strings if I hadn't already done this in an earlier spike.</p> + +<p>This allowed me to send the file, check the response from the third party and ensure they could work with that file type and my proposed content structure.</p> + +<p>If needed, I could have changed direction and avoided investing much time. This wouldn't have been the case if I'd left this until the end of the process.</p> + +<p>I also have a working end-to-end test, and I can send a file and get the response I need.</p> + +<p>What if I'd written all the code and discovered something wouldn't work?</p> + +<h2 id="what-next%3F">What next?</h2> + +<p>Now, I can work backwards and start to make the content dynamic.</p> + +<p>I can introduce more authentic and complicated data, remove the hard-coded test data, and check that things still work.</p> + +<p>I still have the quick feedback loop, as I can always send the data to the third-party system and verify things work as I iterate on my implementation.</p> + +<p>With the main pieces of the puzzle in place, I can continue building and filling in the others.</p> + +<p>Once I have a complete feature with all the pieces in place, I can refactor as needed.</p> + +<p>I still have the same finished puzzle - I just built it in a different order.</p> +
+
+ Tue, 25 Jul 2023 00:00:00 GMT +
+ + Testing is all about confidence + + /daily/2023/07/24/testing-is-all-about-confidence + http://localhost:8000/daily/2023/07/24/testing-is-all-about-confidence + +
<p>Testing - manual or automated - is about building confidence.</p> + +<p>If we deploy this change or release this feature, are we confident it will work as expected and not cause regressions elsewhere?</p> + +<p>What if someone asked you on a scale between one and ten?</p> + +<p>From an automated perspective, have you written enough tests for the feature to be confident it works?</p> + +<p>If you're fixing a bug, do you have a test that reproduces the bug that was originally failing but now passing since you've added the fix?</p> + +<p>Do the tests have enough assertions, and have you covered enough use cases and scenarios?</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>You can utilise code coverage metrics, but no hard rule says that the feature will work once x percentage is covered. Something with 100% coverage can still contain bugs.</p> + +<p>For me, it's about the answer to the question:</p> + +<p>If we deploy this change, how confident are you that it will work as expected?</p> +
+
+ Mon, 24 Jul 2023 00:00:00 GMT +
+ + Tomorrow is easier if today's code is simpler + + /daily/2023/07/23/tomorrow-is-easier-if-todays-code-is-simpler + http://localhost:8000/daily/2023/07/23/tomorrow-is-easier-if-todays-code-is-simpler + +
<p>If you write simple code, it is quicker and easier to read, extend and change.</p> + +<p>If you need to work on that code in the future - either tomorrow or in months or years - it will be easier to add the new feature or fix the bug.</p> + +<p>It will also be easier for others who didn't write it, so they must understand what it does (or doesn't do) before making changes.</p> + +<p>It will also likely be easier to debug with tools like Xdebug and PHPStan or upgrade with tools like Rector.</p> +
+
+ Sun, 23 Jul 2023 00:00:00 GMT +
+ + More code, more problems + + /daily/2023/07/22/more-code-more-problems + http://localhost:8000/daily/2023/07/22/more-code-more-problems + +
<p>The more code you have, the more potential problems you have.</p> + +<p>More code means more opportunities for bugs in your software.</p> + +<p>There's more code to maintain and more chance of encountering breaking changes as you update between major software versions of your project's dependencies, such as a CMS or framework.</p> + +<p>If you can keep your amount of code to a minimum and reduce the maintenance overhead, you are less likely to experience issues.</p> +
+
+ Sat, 22 Jul 2023 00:00:00 GMT +
+ + Comments as communication + + /daily/2023/07/21/comments-as-communication + http://localhost:8000/daily/2023/07/21/comments-as-communication + +
<p>I often hear that code should be "self-documenting".</p> + +<p>Instead of writing a comment, you should create a function or class with that name instead.</p> + +<p>Whilst I agree with this, I think that code comments shouldn't describe what the code is doing - they should explain why the code is needed and provide any additional context to the person reading it.</p> + +<p>If a comment just says <code>Returns true</code> or <code>Sends an email</code>, that can be understood by reading the code, so isn't providing any extra value or context. They can also become outdated as the code changes.</p> + +<p>If a line of code is needed to fix a certain state or situation, or if a piece of code isn't particularly readable and isn't obvious what it does, those are good times to add comments.</p> +
+
+ Fri, 21 Jul 2023 00:00:00 GMT +
+ + Tests as communication + + /daily/2023/07/20/tests-as-communication + http://localhost:8000/daily/2023/07/20/tests-as-communication + +
<p>Automated tests aren't just checks to verify your software is working.</p> + +<p>They're a communication tool that shows people working on the code how it's supposed to be used.</p> + +<p>It's a form of documentation.</p> + +<p>They are living examples of how to use the code with the benefit that you can run them and see if they still work, which isn't the case for markdown files or other types of static documentation that can become outdated.</p> + +<p>Something I often do when evaluating or using software that has a test suite is review the tests and see how it's supposed to be used.</p> +
+
+ Thu, 20 Jul 2023 00:00:00 GMT +
+ + TDD is like clicker training + + /daily/2023/07/19/tdd-is-like-clicker-training + http://localhost:8000/daily/2023/07/19/tdd-is-like-clicker-training + +
<p>During a webinar I watched recently, someone described test-driven-development as clicker training for Developers.</p> + +<p>Similar to when people want to train an animal, the short cycles and quick feedback loops make it easier to stay focussed as you work on a task, and the satisfaction of getting a test to pass, completing a feature, or refactoring some messy code is the reward.</p> +
+
+ Wed, 19 Jul 2023 00:00:00 GMT +
+ + TDD as a concept is simple, but TDD is difficult + + /daily/2023/07/18/tdd-as-a-concept-is-simple-but-tdd-is-difficult + http://localhost:8000/daily/2023/07/18/tdd-as-a-concept-is-simple-but-tdd-is-difficult + +
<p>Test-driven development as a concept is easy.</p> + +<p>Before you write any code, you write a failing test and then write enough code to get it to pass.</p> + +<p>But implementing test-driven development is harder.</p> + +<p>As well as overcoming the initial reluctance to write the tests first, it takes time to learn how to use it well.</p> + +<p>Code katas are great for practising TDD, but it can take time to progress from that to using it in a full application with more pieces.</p> + +<p>As with many things, practice and perseverance are key.</p> +
+
+ Tue, 18 Jul 2023 00:00:00 GMT +
+ + Too many choices? + + /daily/2023/07/17/too-many-choices + http://localhost:8000/daily/2023/07/17/too-many-choices + +
<p>I've recently considered moving my infrastructure automation code from Pulumi to Terraform.</p> + +<p>One of Pulumi's features is that you can write your automation in a programming language instead of a domain-specific language (DSL) with Terraform.</p> + +<p>As a Developer, this seems appealing, but it poses an important question - which programming language should you use?</p> + +<p>I've written and re-written Pulumi code in TypeScript and Python and experimented with Go to see which feels best for me.</p> + +<p>If one of these were my primary language, it would be a no-brainer.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>When I go into my automation repository, I want to write my code as quickly and simply as possible. I don't want to be thinking about how to write it or what language would be best to write it in.</p> + +<p>Whilst I'd have to learn another DSL for Terraform, it would simplify my options by removing that choice for me, but also if I write automation code and hand it over to a client.</p> + +<p>It's like taking my children to a restaurant.</p> + +<p>They'll get overwhelmed if there are too many options on the menu. If we limit the options or order for them, they won't.</p> +
+
+ Mon, 17 Jul 2023 00:00:00 GMT +
+ + When writing a failing test, you're designing your code + + /daily/2023/07/16/when-writing-a-failing-test-youre-designing-your-code + http://localhost:8000/daily/2023/07/16/when-writing-a-failing-test-youre-designing-your-code + +
<p>When doing test-driven development, you start by writing a failing test and then write the code to make it pass.</p> + +<p>This is the design phase where you ask questions and decide how you want the code to work.</p> + +<ul> +<li>Are you going to use a global function or a method on a class?</li> +<li>Will you create a new class or add a method to an existing class?</li> +<li>What will it be called?</li> +<li>Will you use a named method or make the class invokable like a single-method Controller or Action?</li> +<li>Will it accept any parameters?</li> +<li>If so, what will the parameters be, what will they be named, and will they take any default values?</li> +<li>Will it return a value?</li> +<li>Should it throw an Exception?</li> +</ul> + +<p>You might iterate on the design and change it as you write more assertions or tests, but take the opportunity to decide how you want the code to work at this point before you start writing the implementation.</p> +
+
+ Sun, 16 Jul 2023 00:00:00 GMT +
+ + Test-driven development makes you more productive + + /daily/2023/07/15/test-driven-development-makes-you-more-productive + http://localhost:8000/daily/2023/07/15/test-driven-development-makes-you-more-productive + +
<p>I think that test-driven development (TDD) makes you productive.</p> + +<p>Firstly, you save time by not needing to switch from your code to a terminal or browser to test it.</p> + +<p>But, just as importantly, TDD reduces procrastination. It's much clearer to see what the next steps are.</p> + +<p>You're either thinking and designing your code when writing a failing test or fixing the test failures in the implementation code to get the test to pass. You can focus on each failure and message separately and get them to pass instead of thinking about the whole feature or the rest of the application.</p> + +<p>Once you have a working test, you can focus on refactoring any code or moving on to writing the next assertion or the next test.</p> + +<p>I think that achieving small tasks with short feedback loops using test-driven development makes it much easier to remain productive and focussed.</p> +
+
+ Sat, 15 Jul 2023 00:00:00 GMT +
+ + Automated testing is more than just unit testing + + /daily/2023/07/06/automated-testing-is-more-than-just-unit-testing + http://localhost:8000/daily/2023/07/06/automated-testing-is-more-than-just-unit-testing + +
<p>When I speak to people about automated testing or hear others speak about it, it's often confused with unit testing.</p> + +<p>Drupal and many other projects use PHPUnit. Its website describes PHPUnit as "a programmer-oriented testing framework for PHP", whereas Wikipedia explicitly says "PHPUnit is a unit testing framework".</p> + +<p>But automated testing includes much more.</p> + +<h2 id="other-types-of-tests">Other types of tests</h2> + +<p>In Drupal, whilst there are unit tests, PHPUnit also does feature (aka functional or browser) testing, where it can make an HTTP request to an endpoint and perform assertions on the response's status code and the returned text. This is great for checking if a page exists at a certain path, whether the correct users can access it and whether it contains the expected text. This isn't something that can be done with a unit test.</p> + +<p>There are also integration (aka kernel) tests which don't access the browser but have access to Drupal's service container to interact with its services and can save and retrieve values from the database. Again, this isn't available in a unit test which relies heavily on mocking.</p> + +<h2 id="what-else-is-there%3F">What else is there?</h2> + +<p>In Drupal, I've also used Behat - a behaviour-driven development testing framework that allows writing tests in a plain English syntax called Gherkin instead of PHP.</p> + +<p>Pest PHP has an architecture plugin.</p> + +<p>There are dedicated front-end testing frameworks like Cypress for UI testing.</p> + +<p>Other quality tools like static analysis can also be considered testing tools.</p> + +<h2 id="want-to-learn-more%3F">Want to learn more?</h2> + +<p>If you want to learn more about automated testing in Drupal, <a href="http://localhost:8000/talks/tdd-test-driven-drupal">I have a talk about that</a>.</p> +
+
+ Thu, 06 Jul 2023 00:00:00 GMT +
+ + Services vs Actions + + /daily/2023/07/05/services-vs-actions + http://localhost:8000/daily/2023/07/05/services-vs-actions + +
<p>When creating a custom module, where do you put your business logic?</p> + +<p>You want to keep classes like Controllers and Commands simple and move any reusable logic into separate files.</p> + +<p>Usually, this means 'Service' classes, but another approach I like is to use 'Action' classes.</p> + +<h2 id="what-is-an-action-class%3F">What is an Action class?</h2> + +<p>An Action is a PHP class representing a single action that must be performed.</p> + +<p>It usually contains a single method with a descriptive name summarising the task, such as <code>GetAccessToken</code>.</p> + +<p>This differs from a generic service like <code>ApiService</code> with multiple methods like <code>getAccessToken()</code>.</p> + +<h2 id="using-action-classes">Using Action classes</h2> + +<p>I'll register Action classes in the service container to use dependency injection and autowiring and easily inject the Action into other classes that need it, like Controllers and Commands.</p> + +<p>If you need multiple implementations, multiple actions can implement the same Interface and make them swappable, such as having <code>GetAccessToken</code> and <code>GetAndCacheAccessToken</code> implement the same <code>GetsAccessToken</code> interface.</p> + +<p>That also enables using design patterns like Decorators with Actions.</p> + +<h2 id="why-i-like-actions">Why I like Actions</h2> + +<p>I like more readable and meaningful class names and prefer working with multiple simpler classes than those with fewer complex ones.</p> + +<p>I like leveraging design patterns I'm used to, such as the Decorator pattern, by having common interfaces and contracts.</p> + +<p>I like that if I need to add another implementation, I can add it without changing the existing code, so it follows the SOLID principles.</p> + +<h2 id="what-about-you%3F">What about you?</h2> + +<p>Do you use Action classes in your code, or do you use Services or something else?</p> + +<p>Reply to this email and let me know.</p> +
+
+ Wed, 05 Jul 2023 00:00:00 GMT +
+ + Think smaller with TDD + + /daily/2023/07/04/think-smaller-with-tdd + http://localhost:8000/daily/2023/07/04/think-smaller-with-tdd + +
<p>I've recently added several custom search blocks and pages to a client project.</p> + +<p>Each requires a results page, a list of autocomplete suggestions for users to select from, a custom form, and a block to place it on the required pages.</p> + +<p>For each search, I'm first testing the results page, ensuring it exists and contains the correct results before testing the autocomplete results, creating the block and form, and linking everything together.</p> + +<h2 id="thinking-small">Thinking small</h2> + +<p>This could seem like a large and daunting task, but with test-driven development, I can break everything into smaller, more manageable tasks.</p> + +<p>My objective is either to write the next failing test and then get it to pass or to refactor what I've written.</p> + +<p>I can focus on these small steps and make progress towards the end goal, guided by the tests I'm writing, instead of needing to focus always on one large and complex task.</p> +
+
+ Tue, 04 Jul 2023 00:00:00 GMT +
+ + Why write custom assertions in your tests? + + /daily/2023/07/03/why-write-custom-assertions-in-your-tests + http://localhost:8000/daily/2023/07/03/why-write-custom-assertions-in-your-tests + +
<p>I'm refactoring some code on a client project - creating a Repository class to centralise some logic before implementing the next feature.</p> + +<p>The repository class is responsible for finding and returning any nodes with a specified field value and some base conditions (it must be the correct node type, published, etc.).</p> + +<h2 id="adding-a-custom-assertion">Adding a custom assertion</h2> + +<p>I'm using PHPUnit's native assertions to check it returns a Collection (I regularly include the <code>illuminate/collections</code> library from Laravel in other projects) and that each item is an instance of a <code>NodeInterface</code>, but there isn't an assertion to check each node is of the correct type.</p> + +<p>My initial implementation was to loop over each node and use <code>assertSame</code> on its bundle before refactoring to create an array of unique bundle names and comparing it to my expected names:</p> + +<pre><code class="language-php">self::assertSame( + expected: [$nodeType], + actual: $haystack + -&gt;map(fn (NodeInterface $item): string =&gt; $item-&gt;bundle()) + -&gt;unique() + -&gt;toArray(), +); +</code></pre> + +<h2 id="why-write-a-custom-assertion%3F">Why write a custom assertion?</h2> + +<p>Whilst this works, it likely won't be clear in the future what it's testing.</p> + +<p>My initial thought was to add a comment describing it, but then I decided to wrap it in a custom assertion - <code>assertContainsOnlyNodesOfType</code> - a private static function within my test class that wraps the native assertions.</p> + +<p>This approach makes the test more readable now and in the future and more domain-focused by giving it a descriptive name.</p> + +<p>It can be easily reused within the same test case or elsewhere.</p> + +<p>Although I only perform one assertion in this case, I can combine multiple assertions and perform any other required steps.</p> + +<p>Finally, I can contain any implementation details within the custom assertion. Here, I'm matching the result against an array of expected values, not just a single node type which is what I want. This detail can be contained within the assertion, making it easier to read and reuse in the future.</p> +
+
+ Mon, 03 Jul 2023 00:00:00 GMT +
+ + Docker or Nix? + + /daily/2023/07/02/docker-or-nix + http://localhost:8000/daily/2023/07/02/docker-or-nix + +
<p>I've been a Nix user for about a year, starting with its package manager on my previously installed Linux distribution.</p> + +<p>I started to use Home Manager for my user configuration and dotfiles and later switched to the NixOS operating system.</p> + +<h2 id="using-nix-for-software-development">Using Nix for software development</h2> + +<p>I've also been using Nix Flakes for per-project configuration.</p> + +<p>A Flake file is a simple file written in the Nix language that defines the project's dependencies and installs them from the Nix package manager.</p> + +<p>Here is an example Flake for a PHP CLI application:</p> + +<pre><code class="nix">{ +  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + +  outputs = inputs@{ flake-parts, ... }: +    flake-parts.lib.mkFlake { inherit inputs; } { +      systems = [ "x86_64-linux" ]; + +      perSystem = { config, self', inputs', pkgs, system, ... }: { +        devShells = { +          default = pkgs.mkShell { +            buildInputs = with pkgs; [ php82 php82Packages.composer ]; +          }; +        }; +      }; +    }; +} +</code></pre> + +<p>It declares that PHP 8.2 and Composer are available, even if I have different versions installed globally.</p> + +<h2 id="will-nix-replace-docker%3F">Will Nix replace Docker?</h2> + +<p>Nix and Flakes have replaced Docker for me on some projects.</p> + +<p>If I have a simple setup and need a specific version of PHP or Node and some additional programs, I can get those from the Flake.</p> + +<p>I don't know if it'll replace Docker for me completely and work on more complex projects, but it's working well for me where I'm using it.</p> +
+
+ Sun, 02 Jul 2023 00:00:00 GMT +
+ + Once you start writing tests, you can't stop + + /daily/2023/07/01/once-you-start-writing-tests-you-cant-stop + http://localhost:8000/daily/2023/07/01/once-you-start-writing-tests-you-cant-stop + +
<p>Once you start testing/TDD, you can't go back</p> + +<p>Once you start writing automated tests or doing test-driven development, you can't go back to not doing it.</p> + +<p>When adding a new feature, you'd need to test every situation and use case manually in a browser or command line - and, very likely, do so multiple times.</p> + +<p>When fixing a bug, you'd need to follow the exact steps to replicate it and see it before attempting a fix. Again, you'd also need to test it manually.</p> + +<p>Also, because it passes a manual test, there's no guarantee it won't break unexpectedly in the future.</p> + +<p>While refactoring code without tests, bugs and regressions could be introduced as there's no test suite to run and ensure they're still passing.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>When you're used to writing tests and doing test-driven development, you get used to the quick feedback loops and the confidence to make changes.</p> + +<p>It's easier to create a test that proves a bug exists and shows it'safixed because the test passes.</p> + +<p>Once you have these things, you can't stop and go back to not having tests.</p> +
+
+ Sat, 01 Jul 2023 00:00:00 GMT +
+ + How did you learn automated testing? + + /daily/2023/06/30/how-did-you-learn-automated-testing + http://localhost:8000/daily/2023/06/30/how-did-you-learn-automated-testing + +
<p>I started to learn about automated testing in 2012 when I took over maintainership of the Drupal module, Override Node Options.</p> + +<p>At the time, it had around 9,000 active installations (now more than 35,000), and I was concerned about breaking those sites if I pushed a bug and wanted confidence to make changes without introducing regressions.</p> + +<p>Later, I started to learn about test-driven development and how to develop and design software by writing the tests first.</p> + +<p>Although I'm not a Laravel Developer, Adam Wathan's "Test Driven Laravel" course was a great resource and information, as well as a number of presentations and talks at PHP meetups and conferences.</p> + +<p>Once I understood how to write tests and do TDD, I took any opportunity to practice it, whether on projects or coding katas.</p> + +<p>At one point, I was writing module code during work hours and writing tests for it during the evening.</p> + +<h2 id="what-about-you%3F">What about you?</h2> + +<p>How did you learn automated testing or test-driven development? Reply to this email and let me know.</p> + +<p>Bonus points if it was at one of my conference talks or workshops!</p> +
+
+ Fri, 30 Jun 2023 00:00:00 GMT +
+ + Tests won't tell you if your code works + + /daily/2023/06/29/tests-wont-tell-you-if-your-code-works + http://localhost:8000/daily/2023/06/29/tests-wont-tell-you-if-your-code-works + +
<p>Having a passing test suite or CI pipeline doesn't tell you if your appliction works.</p> + +<p>There could be scenarios or edge-cases that aren't covered within the test suite and contain bugs, but aren't covered by the test suite.</p> + +<p>There could be untested code that isn't covered at all.</p> + +<p>A passing test suite proves that the tests that have been written so far pass and that there are no regressions introduced by the latest change.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>Instead of telling you that your application works by passing, the test suite tells you something is broken when it fails.</p> + +<p>If a previously-passing test is failing, the application is broken and should not be deployed - not that the application is working if the tests don't fail.</p> +
+
+ Thu, 29 Jun 2023 00:00:00 GMT +
+ + There's no value in a broken CI pipeline + + /daily/2023/06/28/theres-no-value-in-a-broken-ci-pipeline + http://localhost:8000/daily/2023/06/28/theres-no-value-in-a-broken-ci-pipeline + +
<p>The value in a CI pipeline is when its commands and checks are running successfully, and the pipeline is passing.</p> + +<p>And then keeping it passing.</p> + +<p>If the pipeline fails, it loses all of its value.</p> + +<p>Passing should be its default state, and effort should be made to ensure it continues to pass.</p> + +<p>If a pipeline fails, the change is not deployed, and the failure should be investigated and rectified so the pipeline is returned to a passing state and providing value.</p> +
+
+ Wed, 28 Jun 2023 00:00:00 GMT +
+ + A CI pipeline is like an additional team member + + /daily/2023/06/27/ci-pipeline-additional-team-member + http://localhost:8000/daily/2023/06/27/ci-pipeline-additional-team-member + +
<p>Imagine having a team member who tests each feature before every deployment, reviews the code to ensure its style is consistent, runs quality checks like linting and static analysis, and checks for insecure dependencies.</p> + +<p>If those checks all pass, they automatically deploy the changes and make them live or update a pre-production environment.</p> + +<h2 id="here%27s-the-thing">Here's the thing</h2> + +<p>A CI pipeline can do that. Automatically. For every commit and change to your codebase.</p> + +<p>Having a CI pipeline is like having an additional team member and one who's always ready and waiting for someone to push a change to test.</p> +
+
+ Tue, 27 Jun 2023 00:00:00 GMT +
+ + Is any code without tests legacy code? + + /daily/2023/06/26/is-any-code-without-tests-legacy + http://localhost:8000/daily/2023/06/26/is-any-code-without-tests-legacy + +
<p>While I can't find the original quote, I've heard numerous people describe any code without automated tests as legacy.</p> + +<p>Legacy code is typically inherited from other Developers and is riskier to work on and harder to change, as there is no guarantee changing one piece of code won't cause breakages elsewhere in the codebase.</p> + +<p>This is true for code that doesn't have accompanying automated tests, regardless of when it was written.</p> + +<p>I can release a feature to an environment, and although it may be checked and tested at the time, it likely won't be again for every subsequent release. Automated tests can run automatically for every commit and before every deployment, ensuring the code continues to work and for it to be edited without causing regressions.</p> +
+
+ Mon, 26 Jun 2023 00:00:00 GMT +
+ + Do you need permission to do Test-Driven Development? + + /daily/2023/06/25/do-you-need-permission-test-driven-development + http://localhost:8000/daily/2023/06/25/do-you-need-permission-test-driven-development + +
<p>Something I've been asked before is "Do you need permission to write automated tests or do Test-Driven Development?".</p> + +<p>A response I heard recently was "Does a surgeon ask if they can wash their hands before operating?".</p> + +<p>The same as when I take my car to a garage, I expect the mechanic to do the appropriate diagnostic, repair work and testing to make it's working correctly and safely.</p> +
+
+ Sun, 25 Jun 2023 00:00:00 GMT +
+ + Credited on 200 fixed issues on Drupal.org + + /daily/2023/06/24/credited-on-200-fixed-issues + http://localhost:8000/daily/2023/06/24/credited-on-200-fixed-issues + +
<p>While writing yesterday's email, I saw this on my Drupal.org profile (https://www.drupal.org/u/opdavies):</p> + +<blockquote> + <p>Credited on 200 fixed issues</p> +</blockquote> + +<p>This is not the number of commits I've made to projects and doesn't include issues on other websites like GitHub, but that I've been tagged as a contributor in 200 fixed issues on Drupal.org - aka "contribution credits".</p> + +<p>It includes issues for projects I maintain, like Override Node Options and the Tailwind CSS starter kit, as well as contributions for events I've spoken at, like DrupalCon Europe and BADCamp, and modules for Drupal.org itself that I worked on whilst at the Drupal Association.</p> + +<p>It also includes contrib projects others maintain, such as the Feature Toggle module and Drupal Commerce, and distributions like Commerce Kickstart and Open Atrium.</p> + +<p>In particular, I'm proud of the 17 issues for Drupal core - some for patches I've contributed, some I've reviewed, and some whilst I've mentored at events like DrupalCons and DrupalCamps.</p> + +<p>I've been allocating more open-source time recently, so expect the number of contributions on Drupal.org and GitHub to continue.</p> +
+
+ Sat, 24 Jun 2023 00:00:00 GMT +
+ + Why keep a Changelog? + + /daily/2023/06/23/why-keep-a-changelog + http://localhost:8000/daily/2023/06/23/why-keep-a-changelog + +
<h2 id="what-is-a-changelog%3F">What is a Changelog?</h2> + +<p>A Changelog is a file that documents changes made to a codebase.</p> + +<p>In its simplest form, it's a plain text file within a code repository, or it can be written in Markdown or reStructuredText or kept within a separate tool like Confluence or Sharepoint. Regardless of where it's kept, the main thing is the content.</p> + +<p>It's not a copy of the Git log. It's a summary of the changes to be read by humans and for them to see what's changed, not a list of Git commits.</p> + +<p>If you're considering using my open-source package, you can see what changes I've released, when, and what changes are due to release.</p> + +<h2 id="how-is-it-structured%3F">How is it structured?</h2> + +<p>Each version should have a heading specifying the version number and/or release date and a list of changes grouped by their type - whether something was added, changed, deprecated, removed, etc. This works well if you use conventional commits!</p> + +<p>I like to follow a format called <a href="https://keepachangelog.com">Keep a Changelog</a>.</p> + +<p>The headings link to a diff so you can see all of the commits and changes made to the code, and there's an <code>Unreleased</code> section that shows commits that have yet to be tagged and released.</p> + +<p>If you need to present changes to a review or approval board, having an easy-to-read list of changes, separated by their type, is a much clearer format than a list of Git commits.</p> + +<h2 id="when-should-it-be-updated%3F">When should it be updated?</h2> + +<p>I recommend continually updating the Changelog rather than leaving it to just before a deployment. It's quick to add it to the <code>Unreleased</code> section as part of the commit and update the headings later.</p> + +<h2 id="can-i-see-an-example%3F">Can I see an example?</h2> + +<p>Sure. See the one I added to the <a href="https://github.com/opdavies/national-rail-enquiries-feed-parser/blob/main/CHANGELOG.md">National Rail Enquiries feed parser library</a> I've been working on or the <a href="https://git.drupalcode.org/project/tailwindcss/-/blob/5.x/CHANGELOG.md">Tailwind CSS starter kit theme for Drupal</a>.</p> +
+
+ Fri, 23 Jun 2023 00:00:00 GMT +
+ + It's only a bad situation if you fail to learn from it + + /daily/2023/06/22/fail-to-lear + http://localhost:8000/daily/2023/06/22/fail-to-lear + +
<p>I heard this on a non-tech podcast but it applies to tech too:</p> + +<blockquote> + <p>It's only a bad situation if you fail to learn from it</p> +</blockquote> + +<p>Whether its a missed deadline, a failed deployment, a production outage or pushing a bug to production, there are opportunities to learn and improve.</p> +
+
+ Thu, 22 Jun 2023 00:00:00 GMT +
+ + Deployments or releases + + /daily/2023/06/21/deployments-or-releases + http://localhost:8000/daily/2023/06/21/deployments-or-releases + +
<p>"Deployments" and "releases" are often used interchangeably but mean different things.</p> + +<p>A deployment moves a change from one place to another, such as some updated code from a staging environment to production.</p> + +<p>A release is when the change made is available to users.</p> + +<p>They can happen at the same time, or you can use feature flags to separate them, deploying the code in advance, and the change is only released (or unreleased) by toggling the feature flag.</p> +
+
+ Wed, 21 Jun 2023 00:00:00 GMT +
+ + Should you deploy on a Friday? + + /daily/2023/06/20/should-you-deploy-on-a-friday + http://localhost:8000/daily/2023/06/20/should-you-deploy-on-a-friday + +
<p>There's a common saying about not deploying changes on a Friday to prevent outages or issues before the weekend.</p> + +<p>I've also seen this where people won't deploy after a particular time of the day as it's too close to the evening.</p> + +<h2 id="when-did-you-last-deploy%3F">When did you last deploy?</h2> + +<p>The longer it's been since the last deployment, the risker each deployment is.</p> + +<p>If there are weeks or months of changes, it will be risky regardless of which day it is.</p> + +<p>If your last deployment was an afternoon, deploying a small change the following morning will be low risk, even on a Thursday and Friday.</p> + +<h2 id="conclusion">Conclusion</h2> + +<p>If you're nervous about deploying on a Friday, I think you need to aim for smaller and more frequent deployments to minimise the risk.</p> + +<p>The issue isn't when you're deploying. You likely need to do so more often.</p> + +<p>If there is an issue after a large release, it will take more time to debug or roll back compared to a small release which is easier to find and fix the problem or revert that single change.</p> +
+
+ Tue, 20 Jun 2023 00:00:00 GMT +
+ + Should you upgrade from Drupal 7 to Drupal 10? + + /daily/2023/06/19/should-you-upgrade-from-drupal-7-to-drupal-10 + http://localhost:8000/daily/2023/06/19/should-you-upgrade-from-drupal-7-to-drupal-10 + +
<p>If you're stuck on Drupal 7, why should you upgrade to Drupal 10 instead of moving to another platform?</p> + +<h2 id="familarity">Familarity</h2> + +<p>Although there is a new admin theme, it works in the same way as it did in earlier versions of Drupal, so it will be familiar to anyone who is used to using or developing in it.</p> + +<p>And whilst there have been differences since Drupal 8, creating modules and themes should seem familiar too with similar concepts such as <code>.info</code> files (now <code>.info.yml</code> files), hooks, preprocess functions and templates.</p> + +<h2 id="code-reuse">Code reuse</h2> + +<p>If you have existing custom modules, you should be able to reuse some of its code in newer versions of Drupal. It might need to be restructured or refactored and will depend on how tightly coupled your business logic is to your Drupal code, but if, for example, you have a custom module that creates a node programmatically, the same can be done in newer Drupal versions.</p> + +<h2 id="built-in-migration-tools">Built-in migration tools</h2> + +<p>Whilst you do need to migrate your data from Drupal 7 into Drupal 10, the newer versions come with built-in migration tools that recreate your content types etc first as well as moving the data so you don't need to start from scratch.</p> + +<h2 id="new-versions-of-existing-modules">New versions of existing modules</h2> + +<p>Many modules and themes you use in Drupal 7 will either have equivalent versions for Drupal 10 or a nominated alternative (such as Field Collection to Paragraphs), so you don't need to look for a new solution if there's an updated version of something you already use.</p> + +<h2 id="conclusion">Conclusion</h2> + +<p>These are the initial things that I thought of, but there are many other reasons to upgrade to Drupal 10 instead of moving to something else.</p> + +<p>TLDR - you don't need to start from scratch if you can re-use parts of your existing code, and you can use the built-in modules to handle the data migration.</p> + +<p>If you have a reason, reply to let me know, and I'll post another list.</p> +
+
+ Mon, 19 Jun 2023 00:00:00 GMT +
+ + Are you really doing CI/CD? + + /daily/2023/06/18/are-you-doing-ci-cd + http://localhost:8000/daily/2023/06/18/are-you-doing-ci-cd + +
<p>Does your team really do CI/CD (continuous integration and delivery)?</p> + +<p>I recently watched a recording of a talk from another daily emailer, <a href="https://jhall.io">Jonathan Hall</a>, who started by asking the audience to raise their hands and keep them raised if...</p> + +<ul> +<li>They did a release to production in the last week.</li> +<li>They did a release to production yesterday.</li> +<li>They do not have a permanent branch called <code>develop</code>.</li> +<li>They do not have a special "hotfix" procedure.</li> +<li>Every Developer on their team merged work into <code>main</code> yesterday.</li> +<li>They have no pull requests more than 24 hours old.</li> +</ul> + +<h2 id="ci%2Fcd-is-about-process">CI/CD is about process</h2> + +<p>This is an excellent way to highlight that CI/CD is about processes, not technologies.</p> + +<p>Continuous integration is about how often everyone merges and pushes their changes, and continuous delivery and deployment is how you create releasable software and how long it takes for a change to get into production - not whether or not you use GitHub Actions, GitLab CI, CircleCI or Jenkins.</p> +
+
+ Sun, 18 Jun 2023 00:00:00 GMT +
+ + Avoid Git merge hell with trunk-based development + + /daily/2023/06/17/avoid-git-merge-hell-with-trunk-based-development + http://localhost:8000/daily/2023/06/17/avoid-git-merge-hell-with-trunk-based-development + +
<p>Regardless of whether I was working as part of a team or individually, I used to create a new branch for every change. I'd do the work and merge the feature branch back into the mainline branch - usually either <code>main</code> or <code>develop</code>.</p> + +<p>When working on a team, conflicts are common that prevent different changes from being merged, and they're more likely the more time it's been since the last merge and how large the change is.</p> + +<p>Once, when demoing some in-progress work to a freelance client, I needed to switch branches as the changes were on different feature branches. Doing so broke my local environment and derailed the demo.</p> + +<h2 id="trunk-based-development">Trunk-based development</h2> + +<p>These days, I advocate for and prefer to use trunk-based development. Instead of committing changes to feature branches, they are made directly to the mainline branch.</p> + +<p>As there are no feature branches, there are no merge conflicts when merging into mainline.</p> + +<p>Although there can still be conflicts when pulling remote changes, trunk-based development pairs nicely with continuous integration - when everyone works in small commits and pushes changes at least once daily. This makes conflicts less likely but also smaller and easier to resolve, and because everyone's changes are not hidden on feature branches, you know they will integrate and work together.</p> + +<p>It also stops me from breaking my environment during client demos!</p> +
+
+ Sat, 17 Jun 2023 00:00:00 GMT +
+ + Make the change easy, then make the easy change + + /daily/2023/06/16/make-the-easy-change + http://localhost:8000/daily/2023/06/16/make-the-easy-change + +
<p>Have you worked on some code, whether it's to add or extend functionality or fix a bug and thought, "This would be much easier if..."?</p> + +<p>A quote by Kent Beck:</p> + +<blockquote> + <p>For each desired change, make the change easy (warning: this may be hard), then make the easy change</p> +</blockquote> + +<p>If you can refactor the code and make it easier to implement your required change, do so.</p> + +<p>Any automated tests should be passing before and after, and I'd commit the refactored code to ensure any CI pipelines and checks are still passing.</p> + +<p>Then, go ahead and make the required change - add the feature or fix the bug - which should now be easy.</p> +
+
+ Fri, 16 Jun 2023 00:00:00 GMT +
+ + Done is better than perfect + + /daily/2023/06/15/done-is-better-than-perfect + http://localhost:8000/daily/2023/06/15/done-is-better-than-perfect + +
<p>Whether I'm doing test-driven development or not, I focus on making the feature work or fixing the bug in the simplest way possible.</p> + +<p>Then, once this is done, I can pass through the code again and refactor it as needed.</p> + +<p>I can split code into separate files, move logic from the Controller into separate services, ensure that patterns like dependency injection are followed, and checks like coding standards and static analysis are passing.</p> + +<p>I might deploy a change in its passing state and leave TODO comments or create follow-up issues to describe potential refactors or changes to be addressed later.</p> + +<p>The main goal is to get the application working and providing value for its users.</p> +
+
+ Thu, 15 Jun 2023 00:00:00 GMT +
+ + What if there was no open-source software + + /daily/2023/06/14/what-if-there-was-no-open-source-software + http://localhost:8000/daily/2023/06/14/what-if-there-was-no-open-source-software + +
<p>I was listening to a podcast today, and the question was mentioned - "What if there was no open-source software?".</p> + +<p>As a self-taught Developer who has worked with open-source technologies and become an expert in Drupal - an open-source content management system - this would have had a big effect.</p> + +<p>If there were no open-source frameworks or CMSes like Drupal, Symfony, Laravel or Vue.js, Developers would need to write everything from scratch, and companies would need to pay for the extra time.</p> + +<p>There would be no reusable knowledge as Developers move to different companies as everything would be written in-house.</p> + +<p>There would be no communities and events like conferences and meetups for open-source technologies.</p> + +<p>As well as frameworks, what about languages like PHP that are open-sourced? Would companies also need to write and maintain their own programming languages?</p> + +<p>What about Linux, which I use every day for my desktop environment and servers, and other tools like Neovim, PHPStan, PHPUnit and Pest that are all open-source?</p> + +<p>Even as someone who contributes to and sponsors open-source projects and their Developers, it's still a shock to think how things would work without open-source software.</p> + +<p>If you use open-source software, please consider sponsoring, supporting or contributing to the projects you use and depend on.</p> +
+
+ Wed, 14 Jun 2023 00:00:00 GMT +
+ + Should you feature flag everything? + + /daily/2023/06/13/should-you-feature-flag-everything + http://localhost:8000/daily/2023/06/13/should-you-feature-flag-everything + +
<p>Whilst it's probably impractical to feature flag every new feature or change to your application, considering it is something I do for each change.</p> + +<p>If you find a bug after a feature has been released, if it's feature flagged, you can quickly disable it without needing to make a code change or release another version.</p> + +<p>Instead of needing a rollback plan for a release and reverting to the previous version, it can be as simple as disabling the feature flag again to turn it off whilst the cause is investigated.</p> + +<p>There is a maintenance overhead to adding a feature flag, and it can cause complexity by creating separate paths within the code but using feature flags gives a lot of benefits too.</p> +
+
+ Tue, 13 Jun 2023 00:00:00 GMT +
+ + Feature flags in a multi-tenancy application + + /daily/2023/06/12/feature-flags-in-a-multi-tenancy-application + http://localhost:8000/daily/2023/06/12/feature-flags-in-a-multi-tenancy-application + +
<p>A scenario for having long-lived feature flags is in a multi-tenant application where the same codebase serves multiple projects - e.g. a multi-site Drupal application or a module reused on multiple websites.</p> + +<p>This is the use case I have for a client project which uses a multi-site setup to serve multiple websites from the same Drupal codebase.</p> + +<p>If I need to add a feature to sites 1 and 3 but not site 2 or test a change to only site 2, I can do this by enabling a per-site feature flag.</p> + +<p>Instead of being removed, these flags will remain until the change can be made permanent on all websites, meaning it can be toggled on and off as needed - allowing each site to be configured separately whilst keeping it easy to maintain by having a single canonical codebase.</p> +
+
+ Mon, 12 Jun 2023 00:00:00 GMT +
+ + Failing fast + + /daily/2023/06/11/failing-fast + http://localhost:8000/daily/2023/06/11/failing-fast + +
<p>Usually, failing fast in software development refers to errors in code, but another interpretation of this is "What's the quickest and simplest way to validate or prove an idea"?</p> + +<p>If it's fixing a bug or architecting a new feature, what's the simplest thing that can be done to achieve that? It could be writing a technical document or creating a simple proof of concept rather than building the entire feature.</p> + +<p>If it fails, your sunk cost is low as you've not invested much time, money or energy.</p> +
+
+ Sun, 11 Jun 2023 00:00:00 GMT +
+ + How do you know when to remove a feature flag? + + /daily/2023/06/10/how-do-you-know-when-to-remove-a-feature-flag + http://localhost:8000/daily/2023/06/10/how-do-you-know-when-to-remove-a-feature-flag + +
<p>But once a feature has been enabled, how do you know if you can remove its flag?</p> + +<p>A simple suggestion I've heard is to add a "Remove when..." comment above where the flag is used and detail what conditions need to be in place before the flag can be removed.</p> + +<p>It might be a period of time or after another feature has been shipped, but it will give some clarity when you see the flag in the code to if or when it can be removed.</p> +
+
+ Sat, 10 Jun 2023 00:00:00 GMT +
+ + Drupal is built by people + + /daily/2023/06/09/drupal-is-built-by-people + http://localhost:8000/daily/2023/06/09/drupal-is-built-by-people + +
<p>A quote I heard from DrupalCon, made by my former colleague Tim Lehnen - CTO at the Drupal Association - when talking about contribution:</p> + +<blockquote> + <p>Contribution is what makes Drupal thrive.</p> + + <p>Drupal is built by people.</p> + + <p>No one entity, no one person builds Drupal.</p> + + <p>It's you who builds Drupal.</p> +</blockquote> +
+
+ Fri, 09 Jun 2023 00:00:00 GMT +
+ + Write less Drupal code + + /daily/2023/06/08/write-less-drupal-code + http://localhost:8000/daily/2023/06/08/write-less-drupal-code + +
<p>An approach I like to make my Drupal code more maintainable is to write less of it and move any generic PHP code into separate framework-agnostic libraries.</p> + +<p>These could be in open-sourced packages installed from Packagist or kept within the same repository.</p> + +<p>Having less Drupal-specific code keeps modules smaller and easier to maintain and upgrade.</p> + +<p>If I need to upgrade a module from Drupal 7 to Drupal 10, I can reuse the generic code and focus on changing its integration points with Drupal to make it compatible.</p> +
+
+ Thu, 08 Jun 2023 00:00:00 GMT +
+ + What does the Drupal 7 EOL extension mean to you? + + /daily/2023/06/07/what-does-the-drupal-7-eol-extension-mean-to-you + http://localhost:8000/daily/2023/06/07/what-does-the-drupal-7-eol-extension-mean-to-you + +
<p>Following the announcement at DrupalCon, the official announcement has been released regarding Drupal 7's end-of-life extension:</p> + +<blockquote> + <p>Today, we are officially announcing that Drupal 7 will reach its end of life on January 5, 2025.</p> + + <p>With this final extension, the Drupal Security Team is also adjusting the level of support provided.</p> + + <p>This will be the final extension.</p> +</blockquote> + +<p>If you're involved with one of the ~400,000 Drupal 7 websites (according to https://www.drupal.org/project/usage/drupal), what does the additional year and a half of Drupal 7 mean to you?</p> + +<p>Will you use the time to find the budget to upgrade, assemble a migration plan, port any missing contributed modules or refactor your custom modules or theme to make it easier to upgrade?</p> + +<p>Is it different knowing it’s the final extension compared to the previous time when it wasn’t known if it would be extended again?</p> + +<p>If you're stuck on Drupal 7 or staying on it into 2024 or 2025, what's stopping you from upgrading?</p> +
+
+ Wed, 07 Jun 2023 00:00:00 GMT +
+ + Drupal 7 end-of-life extended + + /daily/2023/06/06/drupal-7-end-of-life-extended + http://localhost:8000/daily/2023/06/06/drupal-7-end-of-life-extended + +
<p>It was announced this week at DrupalCon that the end-of-life date for Drupal 7 has been extended until the 5th of January 2025, giving another year for people to upgrade the 40,000+ Drupal 7 websites.</p> + +<p>Whilst there's no official announcement yet, it seems this is the final extension and will be when support ends.</p> + +<p>Of course, it doesn't mean all of the contributed modules, themes and distributions will be supported or updated until then, but this will give additional time to plan and migrate the remaining Drupal 7 websites whilst still giving clarity by setting a final date that will not be extended again.</p> + +<p>If you're still on Drupal 7 and need to learn more about upgrading to Drupal 10, <a href="http://localhost:8000/call">book an advisory call</a> or <a href="http://localhost:8000/drupal7">an upgrade roadmap</a> for your Drupal 7 project.</p> +
+
+ Tue, 06 Jun 2023 00:00:00 GMT +
+ + How long should a feature flag live? + + /daily/2023/06/05/how-long-should-a-feature-flag-live + http://localhost:8000/daily/2023/06/05/how-long-should-a-feature-flag-live + +
<p>Instead of creating a branch that lives for as long as the code takes to write, if it's behind a feature flag, the code can be merged into the mainline branch without affecting the rest of the codebase.</p> + +<p>Being able to release changes incrementally lowers the risk compared to releasing a large change all at once.</p> + +<p>But the same issue can occur with feature flags, and the longer that code is behind a feature flag, the more risk there will be when enabling the feature.</p> + +<p>So, like feature branches, feature flags should be short-lived and only used for as long as is needed to create the first releasable version of the feature. The feature flag can be removed once the feature is live, and the feature can continue to be iterated on and improved.</p> +
+
+ Mon, 05 Jun 2023 00:00:00 GMT +
+ + Drupal 9: almost end-of-life already + + /daily/2023/05/21/drupal-9-almost-end-of-life-already + http://localhost:8000/daily/2023/05/21/drupal-9-almost-end-of-life-already + +
<p>With the focus being on Drupal 7's potential end-of-life this November, it's also worth being aware that Drupal 9 (released in June 2020) will also be unsupported as of November 2023.</p> + +<p>This is because of its dependency on Symfony 4, which is ending its security support in November.</p> + +<p>Drupal 10 was released in December 2022 and will be the main version until Drupal 11 is released in 2024.</p> + +<p>Luckily, major upgrades of Drupal are much easier these days, with no large code rewrite or data migration required.</p> +
+
+ Sun, 21 May 2023 00:00:00 GMT +
+ + A minor breaking change + + /daily/2023/05/20/a-minor-breaking-change + http://localhost:8000/daily/2023/05/20/a-minor-breaking-change + +
<p>Today, in the repository of an open-source project, I saw an issue comment saying, "This is a minor breaking change.".</p> + +<p>It detailed what was removed and suggested an alternative.</p> + +<p>I don't know how you define a "minor" breaking change.</p> + +<p>Is it because it's a single line and easy to replace, or is it part of the code that is rarely used by consumers and less likely to cause an issue?</p> + +<p>Regardless, if it's a breaking change, anyone using that project will need to update their code if they use something that was removed, whether it's minor or not.</p> +
+
+ Sat, 20 May 2023 00:00:00 GMT +
+ + Semantic versioning + + /daily/2023/05/19/semantic-versioning + http://localhost:8000/daily/2023/05/19/semantic-versioning + +
<p>The version number of a release is a key indicator of whether it’s compatible with existing code.</p> + +<p>Semantic versioning is a popular approach used by Drupal core, and many contributed modules, themes and distributions.</p> + +<p>It uses version numbers like 1.0.0 to show the major, minor and patch versions.</p> + +<p>If the second or third number changes, e.g. 1.1.0 or 1.0.1, the release contains new backwards-compatible features or fixes, so it’s safe to update.</p> + +<p>If the first number changes, e.g. 2.0.0, the release is not backwards compatible and contains breaking changes that you’ll need to review and update your code accordingly.</p> +
+
+ Fri, 19 May 2023 00:00:00 GMT +
+ + Why is backward compatibility important? + + /daily/2023/05/18/why-is-backward-compatibility-important + http://localhost:8000/daily/2023/05/18/why-is-backward-compatibility-important + +
<p>In yesterday's email, I mentioned that deprecating code allows it to be backwards compatible - but what does that mean?</p> + +<p>If I were to remove a function like <code>drupal_set_message()</code> that is used code elsewhere in an application, the code would no longer work and would break.</p> + +<p>As a module or library maintainer, I don't want to cause applications to break by making backward incompatible changes (a "BC break" or a "breaking change").</p> + +<p>Maintaining backward compatibility means that people who use the code can update to the latest version without breakages, and if they use any deprecated code, they know to update it to be compatible with future versions.</p> +
+
+ Thu, 18 May 2023 00:00:00 GMT +
+ + What is deprecated code? + + /daily/2023/05/17/what-is-deprecated-code + http://localhost:8000/daily/2023/05/17/what-is-deprecated-code + +
<p>Deprecating code is a way of identifying code that will be removed in a future major version.</p> + +<p>For example, the <code>drupal_set_message()</code> function was deprecated in Drupal 8.5 and removed in Drupal 9 as the <code>messenger</code> service replaced it.</p> + +<p>Once it was deprecated, the function was changed to use the new service to avoid duplicating code and a message was added to notify Developers:</p> + +<pre><code class="language-php">function drupal_set_message($message = NULL, $type = 'status', $repeat = FALSE) { + @trigger_error('drupal_set_message() is deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Messenger\MessengerInterface::addMessage() instead. See https://www.drupal.org/node/2774931', E_USER_DEPRECATED); + + $messenger = \Drupal::messenger(); + if (isset($message)) { + $messenger-&gt;addMessage($message, $type, $repeat); + } + + return $messenger-&gt;all(); +} +</code></pre> + +<p>This approach means that code can be refactored without breaking backwards-compatibility and, to upgrade any custom code to be compatible with Drupal 9, any references to <code>drupal_set_message()</code> just needed to be updated to use the new Messenger service.</p> + +<p>No large rewrite needed!</p> +
+
+ Wed, 17 May 2023 00:00:00 GMT +
+ + Mastering your tools improves productivity + + /daily/2023/05/16/mastering-your-tools-improves-productivity + http://localhost:8000/daily/2023/05/16/mastering-your-tools-improves-productivity + +
<p>Spending time to learn and master your tools, whether it's your IDE or text editor, CMS or framework, or something else that you use regularly like Git, spending time to expand on the basics and learn it more thoroughly is an investment in your future.</p> + +<p>I try to keep up to date with the latest additions and changes to Drupal and the PHP language so that I can be more productive and write better and cleaner code.</p> + +<p>I invest time to make my development environment and tools work as effectively and efficiently as possible. If I can make it easier and quicker to perform simple tasks like navigating to and creating files, and scaffolding new content within them, that time will be paid back in future time saved.</p> + +<p>If I find a new plugin, module, theme or tool that will make future tasks easier - even something as easy as commenting out a line or block of code - putting aside some time to investigate and evaluate it and decide if I want to use it is time well spent.</p> + +<p>As someone said in a video recently, "you invest time now to buy back time later".</p> +
+
+ Tue, 16 May 2023 00:00:00 GMT +
+ + Is the Drupal release cycle too fast? + + /daily/2023/05/15/is-the-drupal-release-cycle-too-fast + http://localhost:8000/daily/2023/05/15/is-the-drupal-release-cycle-too-fast + +
<p>Compared to Drupal 7, <a href="https://oliverdavies.dev/daily/2023/04/30/will-we-see-drupal-7-100">which is almost at version 100</a>, the release cycle from Drupal 9 onwards has been quite different.</p> + +<p>We've adopted semantic versioning with new feature releases every six months, and we've already sunsetted Drupal 8 and moved on to Drupal 9 and 10.</p> + +<p>Major versions are released more quickly, with Drupal 9 support ending in November 2023 and Drupal 11 potentially being released in May or November 2024.</p> + +<p>But is it too quick, as someone asked on Twitter?</p> + +<p>The main reason I'm aware of is to keep in sync with major versions of projects that Drupal uses, such as the components used by Symfony. As they update and release new major versions, we also need to do so.</p> + +<p>As a module and theme maintainer, I don't think it's too fast and have been happy with the number of changes to upgrade them and make them Drupal 10 compatible. In most cases, I only had to change the <code>core_version_requirement</code> key to include the new version.</p> + +<p>It depends on how well the project is maintained during the earlier versions. If you keep up to date with changes in minor versions and remove any deprecated code, there will be less to do in the future.</p> + +<p>The same applies to any custom code within website projects, not just contributed modules and themes.</p> + +<p>The more often you maintain and update what you have; the easier it will be.</p> +
+
+ Mon, 15 May 2023 00:00:00 GMT +
+ + Releasing small changes often is less risky + + /daily/2023/05/14/releasing-small-changes-often-is-less-risky + http://localhost:8000/daily/2023/05/14/releasing-small-changes-often-is-less-risky + +
<p>Contrary to what many think (at least to begin with), releasing small, incremental changes often - maybe daily or weekly - is less risky than larger releases containing weeks or months of changes.</p> + +<p>If there is an issue after a large release, diagnosing and fixing it can take longer than a smaller release with fewer changes.</p> + +<p>You might have to revert and roll back to the previous release instead of being able to push a fix for a small change.</p> + +<p>With a small change, the risk of an issue is much lower as it has less chance to impact any other part of the application and is much easier for others to read, understand and debug.</p> +
+
+ Sun, 14 May 2023 00:00:00 GMT +
+ + When You Do Things Right, People Won't Be Sure You've Done Anything at All + + /daily/2023/05/13/when-you-do-things-right + http://localhost:8000/daily/2023/05/13/when-you-do-things-right + +
<p>I think this quote from Futurama also applies well to software development:</p> + +<blockquote> + <p>When You Do Things Right, People Won’t Be Sure You’ve Done Anything at All</p> +</blockquote> + +<p>Today I did multiple releases to production websites. There were no outages or downtime, or messages from clients or colleagues.</p> + +<p>Processes were followed, and everything worked as it should. As far as everyone's concerned, everything still "just works".</p> + +<p>So, did I do anything at all?</p> +
+
+ Sat, 13 May 2023 00:00:00 GMT +
+ + Always listen to the mob + + /daily/2023/05/12/always-listen-to-the-mob + http://localhost:8000/daily/2023/05/12/always-listen-to-the-mob + +
<p>Something I'm aware of when working in a pair or mob is to always listen to input from other team members.</p> + +<p>A pair or mob session isn't just focused on one person's ideas or solution. It's a collaborative effort based on everyone's input, experience and ideas.</p> + +<p>If there are conflicting or different ideas or approaches, they can be discussed, and everyone can agree on the way forward. Approaches may also change as you learn more together during the session.</p> + +<p>Together, you will come up with the best solution - even if it's different from what you had in mind to begin with.</p> +
+
+ Fri, 12 May 2023 00:00:00 GMT +
+ + Why I like pair and mob programming + + /daily/2023/05/11/why-i-like-pair-and-mob-programming + http://localhost:8000/daily/2023/05/11/why-i-like-pair-and-mob-programming + +
<p>When working as part of a team, I like to do pair or mob programming as much as possible.</p> + +<p>I like being able to give and receive feedback in real-time; everyone can contribute to the solution and more shared knowledge, so there's less siloing, and it's much harder to block multiple people than a single person.</p> + +<p>Working in pairs or a mob is a great opportunity to onboard new team members, train and mentor, and share tips and tricks.</p> + +<p>It's usually more productive than working separately, and it's nice to speak and socialise with other team members whilst working on a task.</p> +
+
+ Thu, 11 May 2023 00:00:00 GMT +
+ + Tailwind CSS makes change easier + + /daily/2023/05/10/tailwind-css-makes-change-easier + http://localhost:8000/daily/2023/05/10/tailwind-css-makes-change-easier + +
<p>CSS usually has a global scope which can make change risky.</p> + +<p>How do you know that changing CSS in a global stylesheet to fix a bug won't cause more elsewhere?</p> + +<p>Tailwind CSS and utility classes change CSS to a local scope.</p> + +<p>You can see what styling is applied to an element by looking at its classes and easily make changes without worrying that it will cause unexpected issues elsewhere.</p> +
+
+ Wed, 10 May 2023 00:00:00 GMT +
+ + The single responsibility principle + + /daily/2023/05/09/the-single-responsibility-principle + http://localhost:8000/daily/2023/05/09/the-single-responsibility-principle + +
<p>Today, I added a new feature to a project that allows a member to search for a node based on either its title or a specified field on that node, select a result from an autocomplete list and then be redirected to their selected node.</p> + +<p>I've already implemented this for other node types but needed to do the same for this node type.</p> + +<p>There are some differences, such as the node type to query for; the additional field depends on which node type as does the text shown in the autocomplete list.</p> + +<p>To do this, I needed to add a custom block and form, update the <code>AutocompleteController</code>, create a new instance of a <code>NodeQuery</code> class (a custom class within the custom module), register it as a service and update the <code>SearchQueryFactory</code> class.</p> + +<p>A principle that I follow as much as possible is the single responsibility principle, or SRP (the 'S' in SOLID), where each function or class only has one responsibility - such as returning a response for the autocomplete list, determining the correct node query to use based on the search being run or building the query itself - these are separated and split into their own files.</p> + +<p>Although more files and functions are created when coding in this way, though they are smaller and more straightforward to work with - which makes them easier to read, debug and maintain. It also makes code like the node query classes reusable as they aren't embedded within a larger class and are easier to test.</p> +
+
+ Tue, 09 May 2023 00:00:00 GMT +
+ + Only write enough code to get a failing test + + /daily/2023/05/08/only-write-enough-code-to-get-a-failing-test + http://localhost:8000/daily/2023/05/08/only-write-enough-code-to-get-a-failing-test + +
<p>Instead of writing a whole test and then attempting to make it pass, only write enough code to get the test to fail.</p> + +<p>This could be by starting with a failing assertion that is fixed with a hard-coded value and then iterating on the test to introduce the next failure before repeating the process.</p> + +<p>This allows you to keep the feedback loop small and not write more code than is needed, to focus on the objective of the test, and not code yourself into a corner.</p> +
+
+ Mon, 08 May 2023 00:00:00 GMT +
+ + You don't need to think about what to do next + + /daily/2023/05/07/you-dont-need-to-think-about-what-to-do-next + http://localhost:8000/daily/2023/05/07/you-dont-need-to-think-about-what-to-do-next + +
<p>When practising test-driven development, you don't need to think about what to do next.</p> + +<p>If you have a failing test, the objective is to get that test to pass as quickly and simply as possible, and you rely on the error from the failing test to tell you the next step.</p> + +<p>It could be a failing assertion, an undefined variable or function, or a yet-to-be-implemented service.</p> + +<p>Once you've fixed that error, the test will either pass or fail. If it fails, you'll have a new error and know the next step.</p> + +<p>When working in this way, it's like being on autopilot. You can focus only on fixing each error until the feature or fix is complete and the test is passing.</p> +
+
+ Sun, 07 May 2023 00:00:00 GMT +
+ + Why it's important to see the test fail + + /daily/2023/05/06/why-its-important-to-see-the-test-fail + http://localhost:8000/daily/2023/05/06/why-its-important-to-see-the-test-fail + +
<p>With automated testing and test-driven development, it's important to see a test fail. +If a test passes straight away, how do you know that you're testing the right thing? You could be accidentally testing a different piece of functionality, or it could be a false positive.</p> + +<p>If the functionality already exists, do you need another test for it?</p> + +<p>When you see a test fail, you know that the functionality hasn't been implemented, that you're testing the correct thing, and you have a clear goal to work towards.</p> + +<p>If you're fixing a bug, writing a test and seeing it fail verifies the bug exists and that, once the bug is fixed, the test will pass.</p> + +<p>Usually, you can anticipate why a test will fail as it evolves and know when it will pass. If a test passes before I expect, I'm immediately sceptical and will look into why rather than assuming it passed for the right reasons.</p> +
+
+ Sat, 06 May 2023 00:00:00 GMT +
+ + Getting to green + + /daily/2023/05/05/getting-to-green + http://localhost:8000/daily/2023/05/05/getting-to-green + +
<p>When doing test-driven development, once you've written a failing (a.k.a. red) test, the main objective is to get to a green (a.k.a. passing) test as quickly as possible via the simplest method.</p> + +<p>Even if that means returning a hard-coded value.</p> + +<p>Once the test passes, it or the code it's testing can be refactored.</p> + +<p>Or you can move on to the next test and, if you need to change the hard-coded value to get both tests to pass, then it is the right time to do so.</p> +
+
+ Fri, 05 May 2023 00:00:00 GMT +
+ + Speaking at the Symfony UK meetup in London + + /daily/2023/05/04/speaking-at-the-symfony-uk-meetup-london + http://localhost:8000/daily/2023/05/04/speaking-at-the-symfony-uk-meetup-london + +
<p>After registering as an attendee, I've been upgraded to a speaker for next week's event and the first in-person Symfony UK meetup in two years.</p> + +<p>I'll be speaking about my experiences so far with mob programming - something that I spoke about at a PHP South Wales meetup last year.</p> + +<p>If you're interested and in London on the 11th of May, <a href="https://www.meetup.com/symfony/events/293153957">find out more and register</a>.</p> + +<p>Also, Happy Star Wars Day!</p> +
+
+ Thu, 04 May 2023 00:00:00 GMT +
+ + Just because core is supported... + + /daily/2023/05/03/just-because-core-is-supported + http://localhost:8000/daily/2023/05/03/just-because-core-is-supported + +
<p>Just because Drupal core is supported, it doesn't mean that all of the other modules your project uses are still also supported or actively worked on.</p> + +<p>Since the Drupal 8 release in 2015, I've seen numerous examples of modules that haven't been updated for some time as maintainers focus on writing and maintaining the versions for Drupal 8 and now 9 and 10.</p> + +<p>Whilst it isn't marked as unsupported, it isn't being updated either.</p> + +<p>I recently explained to a client that their site uses a module last released in 2014. It’s buggy, but there’s no newer version to use and issues in the queue are no longer being fixed or triaged.</p> + +<p>Override Node Options - a popular module I maintain - is in a similar situation. The most recent Drupal 9/10 version was in February 2023, whereas the last Drupal 7 release was in March 2018.</p> + +<p>I don't have any official figures to support this, but feel the contribution rate for Drupal 7 projects (as in modules, themes and distributions) has been slowing for some time.</p> + +<p>Meanwhile, Drupal 7 core is still supported, at least for now.</p> +
+
+ Wed, 03 May 2023 00:00:00 GMT +
+ + Does it depend on who you ask? + + /daily/2023/05/02/does-it-depend-on-who-you-ask + http://localhost:8000/daily/2023/05/02/does-it-depend-on-who-you-ask + +
<p>The answer to "<a href="http://localhost:8000/daily/2023/05/01/should-drupal-7-support-be-extended-again">Should Drupal 7 support be extended again?</a>" might depend on who you ask.</p> + +<p>If you ask a Developer or agency, the answer would likely be that it shouldn't be extended again, and D7 should be sunsetted.</p> + +<p>Clients and end-users who haven't yet upgraded would likely say it should be extended.</p> + +<p>But if it's extended - and maybe again next year and the next - where's the incentive to upgrade?</p> + +<p>Some people won't upgrade regardless and will continue until their site breaks.</p> + +<p>That would be messy (and costly) to fix, though!</p> + +<p>P.S. Are you still using Drupal 7 and don’t know what’s involved to upgrade to Drupal 10? Book a <a href="http://localhost:8000/call">Drupal 7 upgrade consultation call</a>.</p> +
+
+ Tue, 02 May 2023 00:00:00 GMT +
+ + Should Drupal 7 support be extended again? + + /daily/2023/05/01/should-drupal-7-support-be-extended-again + http://localhost:8000/daily/2023/05/01/should-drupal-7-support-be-extended-again + +
<p>Drupal 7, which was released in 2011, was originally supposed to be end-of-life in November 2022.</p> + +<p>This would mean there would be no more Drupal 7 versions and there would be no further support or bug fixes from the core Developer team.</p> + +<p>However, in February 2022, support was extended until November 2023 with a potential for it to be extended by another 12 months, which would be announced by July 2023.</p> + +<p>There are still over 425,000 active Drupal 7 websites according to <a href="https://www.drupal.org/project/usage/drupal">https://www.drupal.org/project/usage/drupal</a> - almost 100,000 fewer than Feburary 2022. That's still a lot of websites!</p> + +<p>Should support be extended again or should Drupal 7 be marked as end-of-life and unsupported?</p> + +<p>If so, those websites will continue to work, and an argument could be made that there's been enough time to upgrade projects since Drupal 8's first release in 2015.</p> + +<p>That said, it's a tricky upgrade - migrating data into a new Drupal website and re-writing custom modules and themes to make them compatible.</p> + +<p>It's definitely work upgrading, in my opinion, as the updates between Drupal 8, 9 and 10 are much simpler and quicker, and don't involve large rewrites or data migrations.</p> + +<p>Hit reply and let me know what you think.</p> + +<p>P.S. Are you still using Drupal 7 and don’t know what’s involved to upgrade to Drupal 10? Book a <a href="http://localhost:8000/call">Drupal 7 upgrade consultation call</a>.</p> +
+
+ Mon, 01 May 2023 00:00:00 GMT +
+ + Will we see Drupal 7.100? + + /daily/2023/04/30/will-we-see-drupal-7-100 + http://localhost:8000/daily/2023/04/30/will-we-see-drupal-7-100 + +
<p>Drupal 7, first released in January 2011, is now on version 7.97.</p> + +<p>As things stand, it's supported until November 2023, which makes me wonder whether we'll see the first version of Drupal to get 100 releases.</p> + +<p>It was originally supposed to be end-of-life in November 2022, I also wonder if it'll be extended again and whether it should be.</p> +
+
+ Sun, 30 Apr 2023 00:00:00 GMT +
+ + Write the test backwards + + /daily/2023/04/27/tdd-write-the-test-backwards + http://localhost:8000/daily/2023/04/27/tdd-write-the-test-backwards + +
<p>When writing a test, something that I like to do is start by writing the first assertion first, and then work backwards.</p> + +<p>My first assertion might be <code>self::assertTrue($result)</code>.</p> + +<p>If I ran this test, it would fail because of the undefined <code>$result</code> variable - but it's clear to me what I need next by asking, "Where does <code>$result</code> come from?".</p> + +<p>If I need to call a method on another class and get the result, I'll add it before the assertion. Then I repeat the process and ask, "What do I need for this to work?".</p> + +<p>Maybe I need to create some users or content in the application for the class to query and return a result based on it, so I'll create those.</p> + +<p>With this approach, I'm not making any assumptions about the test's prerequisites, and I usually find that I end up with cleaner and more focused tests.</p> +
+
+ Thu, 27 Apr 2023 00:00:00 GMT +
+ + Structure a new test by writing comments first + + /daily/2023/04/26/tdd-structure-a-new-test-by-writing-comments-first + http://localhost:8000/daily/2023/04/26/tdd-structure-a-new-test-by-writing-comments-first + +
<p>Test cases are usually split into two or three sections - "Arrange, Act, Assert" or "Given, When, Then".</p> + +<p>If the test has a prerequisite, such as some users or content to be created or in a given state, you create the required environment for the test. With unit tests, this would include mocking any dependencies you need to use.</p> + +<p>Then you perform an action on the subject under test and, finally, assert that the system is in the desired state.</p> + +<p>Maybe a user was pending initially, and they're active after running a command or a cron task. You can assert the initial state of the user as well as the final state to ensure the action did what it was supposed to do.</p> + +<h2 id="option-1">Option 1</h2> + +<p>To help me get started, I'll sometimes write a test like this with placeholders to separate the test into its separate stages:</p> + +<pre><code class="language-php">/** @test */ +function should_activate_a_pending_user(): void { + // Arrange. + + // Act. + + // Assert. +} +</code></pre> + +<p>This makes me think about the different stages and what each might need to contain.</p> + +<h2 id="option-2">Option 2</h2> + +<p>Or I might write it out in the "Given, When, Then" format:</p> + +<pre><code class="language-php">/** @test */ +function should_activate_a_pending_user(): void { + // Given I have a user. + // And the user is pending. + + // When I run the user update command. + + // Then the user should no longer be 'pending'. + // And the user status should be 'active'. +} +</code></pre> + +<p>This takes a little longer to write but feels more familiar if you're used to a behavioural testing framework like Behat.</p> + +<p>Either way, sometimes, I'll remove the comments once I've written the code around them or leave them to provide additional context.</p> +
+
+ Wed, 26 Apr 2023 00:00:00 GMT +
+ + Shortening the feedback loop even more + + /daily/2023/04/25/shortening-the-feedback-loop-even-more + http://localhost:8000/daily/2023/04/25/shortening-the-feedback-loop-even-more + +
<p>Yesterday's email was about shortening the feedback loop of a CI pipeline by running some of the checks like PHPCS and PHPStan locally and dealing with any errors before they get pushed to the code repository.</p> + +<p>What's even better than writing and committing code and then waiting until just before pushing it to test it? Seeing the errors in real-time and being able to fix them immediately.</p> + +<p>In my Neovim setup (I talked about this at the PHP London meetup recently), I have the Intelephense language server configured to add IDE-like features such as code completion, go-to definition, symbol renaming, etc.</p> + +<p>I also have another plugin - <code>null-ls.nvim</code> - that adds errors from command-line tools to Neovim's diagnostics list.</p> + +<p>So, if I try to do something that would cause a PHPStan failure, such as using an unknown variable or missing a return type, I can see that and fix it immediately and not even wait for a Git hook to run.</p> +
+
+ Tue, 25 Apr 2023 00:00:00 GMT +
+ + CI pipelines should start locally + + /daily/2023/04/24/ci-pipelines-should-start-locally + http://localhost:8000/daily/2023/04/24/ci-pipelines-should-start-locally + +
<p>This week, Solomon Hykes (@solomonstre on Twitter) said:</p> + +<blockquote> + <p>Your CI/CD pipeline should start on the developer's laptop. If it only starts after a git push, you're slowing your team down and throwing money down the drain.</p> +</blockquote> + +<h2 id="what-does-this-mean%3F">What does this mean?</h2> + +<p>My interpretation of this is that if a pipeline is going to fail, and fail for something that could been easily tested locally, then it should have been tested and fixed locally rather than waiting to push the changes to a CI pipeline, watch it fail there, and then need to push a fix and go through the process again.</p> + +<p>In PHP projects, a common CI pipeline failure after working on a change is that PHPCS (code style) or PHPStan (static analysis) checks will fail - even though the feature, fix or refactor is working. If those checks can be run locally to shorten the feedback loop and allow the error to be fixed before the pipeline runs, it would be better to do so.</p> + +<h2 id="my-preferred-solution">My preferred solution</h2> + +<p>My approach for this is using Git hooks - specifically on a pre-push event.</p> + +<p>These are scripts that run automatically before the commits are pushed, and if it fails, it prevents the push and allows you to fix the issue.</p> + +<p>They're easy to enable using a command like <code>git config core.hooksPath .githooks</code> (assuming the scripts are in a directory called <code>.githooks</code>), and whilst I wouldn't run everything that I would in a CI pipeline, I would run tasks like PHPCS and PHPStan checks, and maybe the unit and integration tests if they're fast to run.</p> + +<p>Anything that would take a longer time to run, like functional or behavioural tests, would only be executed within the pipeline, but for common failures like PHPCS and PHPStan errors, these can be quickly fixed locally and not pushed to a CI pipeline at all.</p> + +<p><a href="https://twitter.com/solomonstre/status/1649118014594502656">Here is Solomon's original tweet</a>.</p> +
+
+ Mon, 24 Apr 2023 00:00:00 GMT +
+ + Laravel Pipelines + + /daily/2023/04/23/laravel-pipelines + http://localhost:8000/daily/2023/04/23/laravel-pipelines + +
<p>I've seen a lot on social media and posts and videos recently about Laravel Pipelines - functionality that's been present in Laravel and used within the framework for some time - but there was only documentation added for it last month as part of the Laravel 10 release.</p> + +<p>This is an example from the new documentation:</p> + +<pre><code class="language-php">$user = Pipeline::send($user) + -&gt;through([ + GenerateProfilePhoto::class, + ActivateSubscription::class, + SendWelcomeEmail::class, + ]) + -&gt;then(fn (User $user) =&gt; $user); +</code></pre> + +<p>Once a user has registered, it is passed through different classes - each performing a task and calling the next class in the list, similar to middleware. Once finished, a final action is performed or a value is returned.</p> + +<p>As someone who doesn't use Laravel often but does use standalone components - like <code>illuminate/collections</code> - in other PHP projects, I'm interested to see how I can use this via <code>illuminate/pipeline</code> to refactor some of my existing code.</p> +
+
+ Sun, 23 Apr 2023 00:00:00 GMT +
+ + Configuration files as a service + + /daily/2023/04/22/build-configs-as-a-service + http://localhost:8000/daily/2023/04/22/build-configs-as-a-service + +
<p>After using my "build-configs" tool to generate and manage configuration files for several personal and client Drupal projects for the past few months, I'm offering it as a product to purchase.</p> + +<p>I can provide an initial Drupal project skeleton built using <code>build-configs</code>, and for a recurring monthly amount, ongoing updates and unlimited support via Slack.</p> + +<p>I can retrofit it to an existing codebase, but as this will be different every time, I'll need more information to give a price.</p> + +<p>See http://localhost:8000/build-configs for more information and, as a list subscriber, use the code <code>dailyconfigs</code> to get an early-bird discount.</p> +
+
+ Sat, 22 Apr 2023 00:00:00 GMT +
+ + Making my Drupal module template Drupal 10 compatible + + /daily/2023/04/21/making-my-drupal-module-template-drupal-10-compatible + http://localhost:8000/daily/2023/04/21/making-my-drupal-module-template-drupal-10-compatible + +
<p>Today, I made my <a href="https://github.com/opdavies/drupal-module-template">Drupal module template</a> compatible with Drupal 10.</p> + +<p>Because Drupal 10 is developed from Drupal 9, the changes between the two and the changes required are minimal.</p> + +<p>In this case, I only needed to change the <code>core_version_requirement</code> in <code>drupal_module_template.info.yml</code> from <code>^9</code> to <code>^9 || ^10</code> - meaning the template works for both Drupal 9 and 10, so there's no need for different versions.</p> + +<p>If it used any deprecated code that was removed before Drupal 10, I'd have needed to update it to use the Drupal 10-compliant code.</p> + +<p>Updating to new major Drupal versions is much easier than it used to be!</p> +
+
+ Fri, 21 Apr 2023 00:00:00 GMT +
+ + Micro-refactorings + + /daily/2023/04/20/micro-refactorings + http://localhost:8000/daily/2023/04/20/micro-refactorings + +
<p>Today, I saw a <a href="https://www.linkedin.com/posts/petermorlion_refactoring-technicaldebt-softwaredevelopment-activity-7054378097051095040-I545">LinkedIn post by Peter Morlion</a>, in which he describes micro-refactoring - very small and safe refactors like renaming a variable, moving a method in a file to a more logical place, or adding a comment.</p> + +<p>He says that micro-refactors are safer than big refactors and have less chance of introducing a regression, are easy to do, improve the code quality over time, make larger refactorings easier later, increase the readability of your code, and reduce technical debt bit by bit.</p> + +<h2 id="what%27s-the-issue%3F">What's the issue?</h2> + +<p>I agree with what Peter says and also encourage people to make small refactors to the code they're working on and follow the "<a href="http://localhost:8000/daily/2022/12/22/the-boy-scout-rule">Boy Scout rule</a>".</p> + +<p>The main blocker to this I've seen is Git workflows that require people to work in separate branches that need to be reviewed before being merged, as this slows down the process.</p> + +<p>As Peter also says, micro-refactorings can be done anytime while working on other tasks. This is hard to do if each refactor requires its own branch and pull request, and the number of pull requests would quickly become unmanageable and take time to review.</p> + +<p>If I know that a micro-refactor like renaming a variable is going to take hours, days or weeks to be reviewed and merged, I'd probably be less likely to do the refactor in the first place. Not to mention having to update my working code once it's been merged.</p> + +<h2 id="what-do-i-prefer-to-do%3F">What do I prefer to do?</h2> + +<p>I prefer to follow trunk-based development where everyone commits to a single branch and continuous integration where everyone is committing, pushing and pulling changes often - removing the need for merging branches and merge conflicts.</p> + +<p>Instead of asynchronous code review, I prefer to pair- or mob-program or to rely on automation with CI/CD pipelines.</p> + +<p>If the commit before my change is passing the pipeline checks, and they're still passing after my change, they're good to deploy.</p> + +<p>Making the process as frictionless as possible encourages people to make small changes and micro-refactors, and to make codebases better a small piece at a time.</p> +
+
+ Thu, 20 Apr 2023 00:00:00 GMT +
+ + Camel-case or snake-case for Drupal code? + + /daily/2023/04/19/camel-case-or-snake-case-for-drupal-code + http://localhost:8000/daily/2023/04/19/camel-case-or-snake-case-for-drupal-code + +
<p>For some time, <a href="https://www.drupal.org/docs/develop/standards/php/php-coding-standards#s-functions-and-variables">Drupal's PHP coding standards</a> allows for writing variables in either snake-case (e.g. <code>$my_variable</code>) or lower camel-case (<code>e.g. $myVariable</code>).</p> + +<p>It originally only allowed for snake-case variable names but once it accepted both, I switched to camel-case as my default.</p> + +<p>Why? I didn't like the inconsistency of using one approach for variable names and one for method and property names in PHP classes (which were always camel-case).</p> + +<p>I'd have had code like this with a mixture of both:</p> + +<pre><code class="language-php">class MyClass { + + private EntityTypeManagerInterface $entityTypeManager; + + public function __construct(EntityTypeManagerInterface $entity_type_manager) { + $this-&gt;entityTypeManager = $entity_type_manager; + } + +} +</code></pre> + +<p>Or even more simply:</p> + +<pre><code class="language-php">$entity_type_manager = \Drupal::entityTypeManager(); +</code></pre> + +<p>I prefer not to have to consistently think about which to use and, if possible, like to use standard approaches in different codebases whether I'm working on a Drupal project, a Symfony project, or a PHP library.</p> + +<p>Plus, I get to use new PHP features like <a href="http://localhost:8000/daily/2023/04/12/cleaner-php-code-with-promoted-constructor-properties">promoted constructor properties</a> if everything is named in the same format.</p> +
+
+ Wed, 19 Apr 2023 00:00:00 GMT +
+ + Consistency is key + + /daily/2023/04/18/consistency-is-key + http://localhost:8000/daily/2023/04/18/consistency-is-key + +
<p>A side effect of <a href="http://localhost:8000/daily/2023/03/04/why-i-built-a-tool-to-generate-configuration-files">using a tool to generate build configuration files</a> with templates is the consistency that it introduces.</p> + +<p>The majority of my projects use a PHP-FPM or PHP CLI container. In my Docker Compose file, the service was mostly named <code>php</code> but sometimes it was <code>php-fpm</code>. In the templated file, it's always named <code>php</code>.</p> + +<p>Some projects would use <code>mysql</code> or <code>mariadb</code> for the database service and <code>nginx</code> or <code>caddy</code> depending on which server was being used. These are now always <code>database</code> and <code>web</code> respectively.</p> + +<p>As well as being easier to switch between projects and not having to think about which names are used in each codebase, it's also much easier to write tools and automation when the names are consistent.</p> + +<p>For example, I'd always write a long-ish command to import a database file - reading and unzipping it, and importing it by connecting to the database running in its container. The command would essentially be the same with slight changes based on that project - such as the database service name.</p> + +<p>Now the command is the same for all projects, and I can automate it by writing a script that works on any project meaning I no longer need to write the long command at all.</p> +
+
+ Tue, 18 Apr 2023 00:00:00 GMT +
+ + Introducing feature flags to "build-configs" + + /daily/2023/04/17/introducing-feature-flags-to-build-configs + http://localhost:8000/daily/2023/04/17/introducing-feature-flags-to-build-configs + +
<p>Yesterday, I wanted to make a breaking change to my <a href="http://localhost:8000/daily/2023/03/04/why-i-built-a-tool-to-generate-configuration-files">build-configs project</a> - changing the default database credentials that are used by Docker Compose.</p> + +<p>As I have several projects based on generated files by the tool, changing the values could cause issues in those projects in the future and this is something that I wanted to avoid.</p> + +<h2 id="what-did-i-do%3F">What did I do?</h2> + +<p>To avoid this issue and needing to update all of my projects at once, I added a feature flag to the <code>build.yaml</code> file so I can opt-in to this feature on a per-project basis.</p> + +<p>If a project, like my <a href="https://github.com/opdavies/docker-example-drupal">Drupal</a> and <a href="https://github.com/opdavies/docker-example-drupal-localgov">LocalGov Drupal</a> Docker examples, are opted in, its files will get the new credentials. If not, it will continue to use the original ones.</p> + +<p><a href="https://github.com/opdavies/docker-example-drupal/commit/3f496168d5c32f9706970519023b431ee02c4b19">In this commit</a>, you'll see where I enabled the feature flag and committed the resulting change.</p> + +<h2 id="what-does-this-achieve%3F">What does this achieve?</h2> + +<p>I can continue to work on existing projects without them breaking, and migrate projects one at a time by using the feature flag instead of needing to do them all once.</p> + +<p>Once all active projects have been migrated or completed, the feature flag can be removed and I can refactor and simplify the code - removing the feature flag and the legacy values.</p> +
+
+ Mon, 17 Apr 2023 00:00:00 GMT +
+ + Refactoring with readonly classes in PHP 8.2 + + /daily/2023/04/16/refactoring-with-readonly-classes-in-php-8-2 + http://localhost:8000/daily/2023/04/16/refactoring-with-readonly-classes-in-php-8-2 + +
<p>Marian Kostadinov (<a href="https://twitter.com/stochnagara">stochnagara on Twitter</a>) replied to Friday's email about DTOs and value objects to tell me about <code>readonly</code> classes, which can be done in PHP 8.2.</p> + +<p>Looking at the previous class:</p> + +<pre><code class="language-php">class AccountDetails { + + public function __construct( + public readonly string $accountNumber, + public readonly string $sortCode, + ) {} + +} +</code></pre> + +<p>Instead of setting each property as <code>readonly</code>, the whole class can instead be marked as <code>readonly</code>:</p> + +<pre><code class="language-php">readonly class AccountDetails { + + public function __construct( + public string $accountNumber, + public string $sortCode, + ) {} + +} +</code></pre> + +<p>Thanks for the suggestion, Marian!</p> +
+
+ Sun, 16 Apr 2023 00:00:00 GMT +
+ + Automatically running commands with nodemon + + /daily/2023/04/15/automatically-running-commands-with-nodemon + http://localhost:8000/daily/2023/04/15/automatically-running-commands-with-nodemon + +
<p>Instead of running the same commands over and over again, some of the command-line tools that I use have a <code>watch</code> mode and will automatically re-run when files are changed, however, not all do.</p> + +<p>Instead of doing this manually, I can achieve the same thing though using a tool called <code>nodemon</code> - a script monitor for node projects, but it can work with non-node projects too.</p> + +<p>Here's an example where I'm automatically re-running my PHPUnit tests using a <code>just test</code> recipe each time a file within my custom modules directory changes:</p> + +<pre><code>nodemon \ + --watch web/modules/custom \ + --ext '*' \ + --exec "just test --stop-on-failure --colors=always" +</code></pre> + +<p>I specify the files or directories I want to watch, which file extensions should trigger a restart (in this case, I've enabled all extensions), and the command to execute.</p> + +<p>This watches all files and run all of the tests, but I can filter either if I need to.</p> + +<p>As well as PHP tools like PHPUnit and PHPStan, I also use this for other things such as when creating presentation slides with rst2pdf where I can automatically re-generate the PDF whenever I update the rst file.</p> +
+
+ Sat, 15 Apr 2023 00:00:00 GMT +
+ + Data transfer objects and value objects + + /daily/2023/04/14/data-transfer-objects-and-value-objects + http://localhost:8000/daily/2023/04/14/data-transfer-objects-and-value-objects + +
<p>In the last few emails, we've seen a data transfer object called <code>AccountDetails</code> that stores the account number and sort code for a bank account.</p> + +<p>A data transfer object only stores data but we could also use a value object that can do more tasks, like validating the values.</p> + +<p>For example, we could validate that the account number is the correct length and the sort code is the correct format:</p> + +<pre><code class="language-php">class AccountDetails { + + public function __construct( + public readonly string $accountNumber, + public readonly string $sortCode, + ) { + // Validate the account number. + if (!preg_match('/^\d{8}$/', $accountNumber)) { + throw new \InvalidArgumentException('Account number must be an 8-digit number'); + } + + // Validate the sort code. + if (!preg_match('/^\d{2}-\d{2}-\d{2}$/', $sortCode)) { + throw new \InvalidArgumentException('Sort code must be in the format 00-00-00'); + } + } + +} +</code></pre> + +<p>Helper methods can also be added to a value object as long as they don't modify the state of the object, such as getting the raw sort code without the separating dashes or performing further checks on the input values.</p> +
+
+ Fri, 14 Apr 2023 00:00:00 GMT +
+ + Immutable read-only properties in PHP 8.1 + + /daily/2023/04/13/immutable-read-only-properties-in-php-8-1 + http://localhost:8000/daily/2023/04/13/immutable-read-only-properties-in-php-8-1 + +
<p>Continuing with yesterday's data transfer object (DTO) example, something that can be done since PHP 8.1 is to make properties read-only:</p> + +<pre><code class="language-php">class AccountDetails { + + public function __construct( + public readonly string $accountNumber, + public readonly string $sortCode, + ) {} + +} +</code></pre> + +<p>This means the public properties can be read and used without the need for getter methods, but cannot be overridden - making the DTO immutable.</p> + +<p>Without <code>readonly</code>, a DTO can be created and the property values can be changed:</p> + +<pre><code class="language-php">$accountDetails = new AccountDetails('12345678', '00-00-00'); +$accountDetails-&gt;accountNumber = 'banana'; +</code></pre> + +<p>With <code>readonly</code> set, you'd get a fatal error instead:</p> + +<blockquote> + <p>Fatal error: Uncaught Error: Cannot modify readonly property AccountDetails::$accountNumber in /home/opdavies/tmp/example.php:13</p> +</blockquote> +
+
+ Thu, 13 Apr 2023 00:00:00 GMT +
+ + Cleaner PHP code with promoted constructor properties + + /daily/2023/04/12/cleaner-php-code-with-promoted-constructor-properties + http://localhost:8000/daily/2023/04/12/cleaner-php-code-with-promoted-constructor-properties + +
<p>One of my favorite features that was introducted in PHP 8 was promoted constructor properties.</p> + +<p>If I'm passing arguments into a constructor, I can declare a visibility and it will be promoted to a property on the class.</p> + +<p>Here's an example of a value of a data transfer object that accepts a sort code and account number as strings:</p> + +<pre><code class="language-php">class AccountDetails { + + public function __construct( + public string $accountNumber, + public string $sortCode, + ) {} + +} +</code></pre> + +<p>Without promoted constructor properties, I'd need to create the properties and assign them manually, and I'd have this:</p> + +<pre><code class="language-php">class AccountDetails { + + public string $accountNumber; + + public string $sortCode; + + public function __construct( + string $accountNumber, + string $sortCode, + ) { + $this-&gt;accountNumber = $accountNumber; + $this-&gt;sortCode = $sortCode; + } + +} +</code></pre> + +<p>Whilst text editors and IDEs can create the properties automatically, I prefer this as it's less code, more readable and easier to understand.</p> +
+
+ Wed, 12 Apr 2023 00:00:00 GMT +
+ + just vs make + + /daily/2023/04/11/just-vs-make + http://localhost:8000/daily/2023/04/11/just-vs-make + +
<p><code>just</code> compared to <code>make</code> is something that was asked during my PHP London talk, and whilst they are similar, <code>just</code> has differences for me that explains why I use it:</p> + +<h2 id="tabs-or-spaces">Tabs or spaces</h2> + +<p>A Makefile needs to use tabs. Justfiles are more flexible and work with tabs or any number of spaces.</p> + +<h2 id=".phony">.PHONY</h2> + +<p>With a Makefile, you need to declare some targets as "phony". I believe that this is for targets that don't generate artifact files with that name, so as I'm not compiling and building files with <code>make</code>, this is redundant and adds visual noise.</p> + +<h2 id="passing-arguments">Passing arguments</h2> + +<p>This is how a <code>composer</code> target looks like in a Makefile:</p> + +<pre><code class="make">composer: + docker compose exec php composer +</code></pre> + +<p>With this, I'd expect to be able to pass arguments to it - e.g. <code>make composer info drupal/core</code>.</p> + +<p>But, instead of seeing the expected output, I get an error: <code>make: *** No rule to make target 'info'.  Stop.</code>.</p> + +<p>This is what I'd need to do to pass arguments to the <code>composer</code> target:</p> + +<pre><code class="make">composer: + docker compose exec php composer $(COMPOSER_ARGS) +</code></pre> + +<p>Now I can run <code>make composer COMPOSER_ARGS="info drupal/core"</code> and see what I was expecting but the syntax isn't what I'd want.</p> + +<p><code>just</code>, on the other hand, allows for defining parameters to its recipes:</p> + +<pre><code class="language-yaml">composer *args: +  docker compose exec php composer +</code></pre> + +<p>Here, I can create as many named parameters as needed and use them in the recipe with the syntax that I wanted - <code>just composer info drupal/core</code>.</p> + +<p>I can think of a few others but this is is the main reason why I moved from <code>make</code> and later adopted <code>just</code>.</p> + +<p><code>just</code>, for me, gives the flexibilty that I need whilst using a simple and familiar syntax but without some of the confusing and complicated behaviours of <code>make</code>.</p> +
+
+ Tue, 11 Apr 2023 00:00:00 GMT +
+ + How I use Neovim for writing PHP + + /daily/2023/04/10/how-i-use-neovim-for-writing-php + http://localhost:8000/daily/2023/04/10/how-i-use-neovim-for-writing-php + +
<p>Since <a href="http://localhost:8000/blog/going-full-vim">July 2021</a>, I've used Neovim full-time for my development work and writing.</p> + +<p>Whilst Vim and Neovim are minimal by default, its functionality can be extended by adding plugins.</p> + +<p>I use nvim-lspconfig along with Intelephpense and Phpactor to add code actions and functionality like "go to definition" for PHP code.</p> + +<p>I use null-ls to apply code formatting and add diagnostic messages for PHPCS and PHPStan so I can see them on the appropriate lines.</p> + +<p>I use nvim-dap and nvim-dap-ui to leverage the debug adapter protocol to do step debugging with Xdebug.</p> + +<p>Along with others like dadbod.vim for databases, rest.nvim for making HTTP requests, Gitsigns and Fugitive for Git, and Telescope for fuzzy searching, I have all of the functionality that I've previously had in other browsers with the benefit that it's open source and I can add more plugins or write my own if I need to add anything else.</p> +
+
+ Mon, 10 Apr 2023 00:00:00 GMT +
+ + Nix, NixOS, Home Manager, and WSL2 + + /daily/2023/04/09/nix-nixos-home-manager-and-wsl2 + http://localhost:8000/daily/2023/04/09/nix-nixos-home-manager-and-wsl2 + +
<p>Although I've been using Linux for work computers for a lot longer, a few years ago, I switched from macOS and an Apple MacBook Pro to using Linux full-time on my personal computers.</p> + +<p>My current daily driver laptop is a Tuxedo InfinityBook that I've installed NixOS on.</p> + +<p>NixOS, as the name suggests, is a Linux operating system based on the Nix package manager. It has access to the 80,000+ packages in the <code>nixpkgs</code> repository and can still install and manage them, but also does a lot more including managing hardware.</p> + +<p>For user-level configuration, I use Home Manager. I can configure my home directory including user-specific packages and manage my dotfiles, creating files like <code>.gitconfig</code> and linking them to the required destination.</p> + +<p>Home Manager can be a NixOS module or a standalone tool - it can be added to NixOS or installed on any Linux distribution, even in WSL2, so I have the same packages that I need installed on every machine as well as having my dotfiles in the correct place and ready to be used.</p> +
+
+ Sun, 09 Apr 2023 00:00:00 GMT +
+ + Why I use tmux + + /daily/2023/04/08/why-i-use-tmux + http://localhost:8000/daily/2023/04/08/why-i-use-tmux + +
<p>tmux is a "terminal multiplexer" - a program that makes one terminal window into many.</p> + +<p>I use the terminal a lot and am often switching between codebase so this is very useful for me.</p> + +<p>I use a different session for each codebase. If I'm working on one and need to switch to another, I can detatch from one session and attach to another. Later, when I'm ready to switch back, I do the same again.</p> + +<p>I usually have multiple windows inside a session. Neovim will be in the main one, and I can run separate commands - especially long-running ones like watchers - with their own windows.</p> + +<p>If I need to split a window and have Neovim on the left and a terminal for running tests on the right, I can do this with different panes.</p> + +<p>Being able to organise my workspace in this way has become a big part blogof my development workflow and my productivity.</p> +
+
+ Sat, 08 Apr 2023 00:00:00 GMT +
+ + Speaking at PHP London + + /daily/2023/04/07/speaking-at-php-london + http://localhost:8000/daily/2023/04/07/speaking-at-php-london + +
<p>After a trip away with the family earlier this week, I was happy to speak at the PHP London meetup yesterday evening.</p> + +<p>Following on from my previous "Working from Workspace" talk, I spoke about my current development setup for my personal and work laptops with Nix and NixOS, Linux, Neovim, tmux, just, Docker and more!</p> + +<p>Of course, I spoke about PHP, Drupal, and Symfony too as well as some of the work that we're doing at Transport for Wales.</p> + +<p>I learned that the call for papers for the PHP UK conference 2024 is already open so I'll definitely be submitting some proposals for next year's conference. Hopefully, I'll be back in London again before that.</p> +
+
+ Fri, 07 Apr 2023 00:00:00 GMT +
+ + Software development is about solving problems and adding value + + /daily/2023/03/27/software-development-solving-problems-and-adding-value + http://localhost:8000/daily/2023/03/27/software-development-solving-problems-and-adding-value + +
<p>I've recently started as a Mentor for the School of Code, working with a student on their cohort that started last week. Whilst the Bootcamp is focussed on JavaScript and node, and is currently looking at front-end development and using APIs, we've already started talking about different back-end technologies and frameworks.</p> + +<p>Something that I've believed for a while is that software development is about solving problems and different languages and frameworks are tools to use to solve the problem and achieve the desired result. The programming fundamentals that he's learning now can be applied to different languages and some may be better depending on the problem that needs to be solved.</p> + +<p>I've also said when speaking with a client this week that development is about adding business value.</p> + +<p>Whilst there is some value to doing security updates and maintenance to keep an application running and secure, I'd much rather be working on tasks that directly add value for the client like some of the tasks I've done for them recently like improving eCommerce conversions and adding new payment methods or revenue streams.</p> + +<p>It's easier for the client to justify and see the results of the work, it improves the experience for their customers or users, and it's more interesting and fulfilling for me.</p> +
+
+ Mon, 27 Mar 2023 00:00:00 GMT +
+ + With utility styles, your CSS stops growing + + /daily/2023/03/22/with-utility-styles-your-css-stops-growing + http://localhost:8000/daily/2023/03/22/with-utility-styles-your-css-stops-growing + +
<p>When working with traditional CSS, the first thing you do when you need to style a new page or component is to open a stylesheet and start writing new CSS.</p> + +<p>Also when editing existing styles, sometimes you'll write new styles to avoid breaking the existing pages in unexpected ways in other parts of the page. This is why I've not seen many projects where the styling is maintained or refactored over time - only added to.</p> + +<p>By writing new CSS for every new page or component, the size of your stylesheets will continue to grow and be less performant and harder to maintain over time.</p> + +<p>With utility styles and frameworks like Tailwind CSS, this happens a lot less or sometimes not at all.</p> + +<p>If you need to use a class like <code>flex</code> or <code>grid</code> and you already use that in your project, there's no new CSS to add and the stylesheet doesn't grow. Because these type of classes are so specific, they're much more reusable which means less duplication of CSS rules and less CSS in total.</p> +
+
+ Wed, 22 Mar 2023 00:00:00 GMT +
+ + There isn't a standard "Tailwind-looking" site + + /daily/2023/03/21/there-isnt-a-tailwind-looking-site + http://localhost:8000/daily/2023/03/21/there-isnt-a-tailwind-looking-site + +
<p>When looking at some websites, you can tell that it was built using a certain CSS framework.</p> + +<p>Whether it's using a standard component like a navbar or card, or it's using the default colour palette, you can confidently know that website uses ___.</p> + +<p>As Tailwind includes no components (other than the <code>container</code>) and only has low-level utility classes for things like spacing and colours (amongst many other things), you can build radically different-looking websites with the same classes.</p> + +<p>There are some <a href="http://localhost:8000/blog/uis-ive-rebuilt-tailwind-css">examples of UIs that I've rebuilt</a> and some websites that I show in my <a href="http://localhost:8000/talks/taking-flight-with-tailwind-css">Taking Flight with Tailwind CSS talk</a>.</p> + +<p>If you take a look, you'll see what I mean.</p> +
+
+ Tue, 21 Mar 2023 00:00:00 GMT +
+ + The benefits of automation + + /daily/2023/03/15/the-benefits-of-automation + http://localhost:8000/daily/2023/03/15/the-benefits-of-automation + +
<p>I've been working on a project with a client, using <a href="https://fractal.build">Fractal</a> for our component library. I've been working on it locally but yesterday I needed to make it public for the client to review the work I've done.</p> + +<p>I recently added <code>node</code> support to my <a href="http://localhost:8000/daily/2023/03/04/why-i-built-a-tool-to-generate-configuration-files">build configuration file generator</a> and used that to generate and use a consistent set of configuration files for a Fractal project.</p> + +<p>I've used Terraform in <a href="https://github.com/opdavies/rebuilding-acquia">some codebases</a> to create and configure AWS resources like S3 buckets and Cloudfront distributions, so I was able to use that to create what I needed as well as adding the DNS record for a new subdomain to access Cloudfront.</p> + +<p>I use a <code>justfile</code> to automate tasks such as generating production assets from Fractal and uploading it to the S3 bucket, so I can do this in a single command instead of multiple and in a consistent and reproducible way.</p> + +<p>I started with a standalone project on my laptop and finished with a consistent cloud-based environment for my client and their project to review their component library, and because of the automation that I've built and used, this only took a few minutes to do.</p> +
+
+ Wed, 15 Mar 2023 00:00:00 GMT +
+ + Automating all the things + + /daily/2023/03/14/automating-all-the-things + http://localhost:8000/daily/2023/03/14/automating-all-the-things + +
<p>As a solo Developer and Consultant, I rely a lot on automation to get my tasks done.</p> + +<p>I use tools like <a href="http://localhost:8000/daily/2023/03/09/in-what-language-should-i-write-my-automation">Pulumi, Ansible and Terraform</a> to automate creating and configuring infrastructure - recently creating new client GitHub repositories using a standard configuration with Pulumi.</p> + +<p>I use Nix and Home Manager to automate my local development environment, install packages I need and create files like <code>.gitconfig</code> for my needs. These are reusable and stored in my <a href="https://github.com/opdavies/dotfiles">dotfiles</a> repository.</p> + +<p>For projects, I use tools like Docker, Docker Compose and Nix flakes for consistency.</p> + +<p><a href="http://localhost:8000/daily/2023/03/04/why-i-built-a-tool-to-generate-configuration-files">I wrote a tool for generating configuration files for each project</a> - reducing the time it takes me to set up, configure and maintain standard files for tools like Docker and Docker Compose that I always use. If I need to add a new feature or fix a bug in a configuration file, I can do that in my templated version and re-generate each project's files rather than making the same change manually.</p> + +<p>Today, I wrote a script that loops over all projects where I commit those files to GitHub, clones a fresh version of it, re-generates the files and pushes any new files or changes back to the repository. This means that any changes will be automatically applied and all of my projects will remain in sync.</p> + +<p>Automating all of these things reduces the time that I need to spend on them - allowing me to focus on delivering value and solving problems for my clients.</p> +
+
+ Tue, 14 Mar 2023 00:00:00 GMT +
+ + What problem are we trying to solve? + + /daily/2023/03/13/what-problem-am-i-trying-to-solve + http://localhost:8000/daily/2023/03/13/what-problem-am-i-trying-to-solve + +
<p>This is a question that I always ask before starting any development task, and I was very happy to see a slide in one of the keynote sessions at nor(DEV):con asking this question.</p> + +<p>I'd much rather have a collegue or client come to me with a problem that needs solving rather than a self-diagnosis and a decided solution.</p> + +<p>Depending what on the problem is, and other factors like timescales, I may suggest a different solution or approach. This happened recently when I suggested a different approach using Drupal's built-in maintenance mode feature.</p> + +<p>If there's no problem to solve, what's the reason for doing the work?</p> +
+
+ Mon, 13 Mar 2023 00:00:00 GMT +
+ + In what language should I write my automation? + + /daily/2023/03/09/in-what-language-should-i-write-my-automation + http://localhost:8000/daily/2023/03/09/in-what-language-should-i-write-my-automation + +
<p>Of the three tools I mentioned in yesterday's email - Ansible, Terraform and Pulumi - I mostly use Pulumi these days for writing automation code.</p> + +<p>Why? As someone who is already familiar with writing code in certain programming languages, I like that I can use those languages to also write automation, avoid learning a domain-specific language, and those existing approaches like functions and classes to make reusable components and resources.</p> + +<p>I like that it supports a number of languages. I started with TypeScript but since watching a recent episode of the AltF4Stream, I'm looking at Python too. I've written Python before with Fabric and what you can achieve in Pulumi with a small amount of Python code is very impressive compared to other languages like Go and TypeScript.</p> + +<p>If you prefer those other languages, why not give Pulumi a try with it?</p> +
+
+ Thu, 09 Mar 2023 00:00:00 GMT +
+ + Automating infrastructure with IaC + + /daily/2023/03/08/automating-infrastructure-with-iac + http://localhost:8000/daily/2023/03/08/automating-infrastructure-with-iac + +
<p>Are you responsible for creating and maintaining resources like GitHub repositories, DigitalOcean servers, Amazon S3 buckets and Cloudfront distributions or DNS records in Cloudflare?</p> + +<p>Do you do that through the various web UIs?</p> + +<p>I'd recommend looking into infrastructure as code tools such as Ansible, Terraform and Pulumi.</p> + +<p>You can create, manage and destroy these resources by writing text files rather than clicking around in web UIs, which you can store and share using version control tools.</p> + +<p>Infrastructure as code (IaC) tools are something that I use when working with infrastructure, whether it's for a client project or <a href="https://github.com/opdavies/rebuilding-acquia/blob/4efe94398f4d8715d22ca677756beb36017d4e74/main.tf">a talk demo</a> (this uses Terraform to create an Amazon S3 bucket and Cloudflare distribution, link it to an SSL certificate and create my DNS records - all automatically).</p> +
+
+ Wed, 08 Mar 2023 00:00:00 GMT +
+ + Mentoring for School of Code + + /daily/2023/03/05/mentoring-for-school-of-code + http://localhost:8000/daily/2023/03/05/mentoring-for-school-of-code + +
<p>I've been a Mentor for students on a coding bootcamp a few times before, and I'm looking forward to doing this again - this time with <a href="https://www.schoolofcode.co.uk">School of Code</a>.</p> + +<p>After hearing about them at a PHP meetup last year, I was keen to get involved after the previous bootcamp I was involved with closed its doors.</p> + +<p>I've always been impressed to see people with little, or usually no prior coding experience, on these bootcamps and what they can learn and achieve in a short amount of time - and one of the things that drew me to School of Code is that the bootcamp is free for learners.</p> + +<p>This week is the mentor kick-off session, with the new cohort starting next week.</p> + +<p>I'm looking forward to seeing who I'm paired up with, and helping them take their first steps into a career in software development.</p> +
+
+ Sun, 05 Mar 2023 00:00:00 GMT +
+ + Why I built a tool to generate configuration files + + /daily/2023/03/04/why-i-built-a-tool-to-generate-configuration-files + http://localhost:8000/daily/2023/03/04/why-i-built-a-tool-to-generate-configuration-files + +
<p>I'm always working on various personal and client projects, and they contain a lot of the same configuration files. I exclusively use Docker and Docker Compose on all projects, I use a <code>justfile</code> for running tasks, and for PHP projects, I need configuration files for tools like PHPStan, PHPCS and PHPUnit.</p> + +<p>The majority of those files are the same with some slight configuration for each project - such as whether it uses <code>web</code> or <code>docroot</code>, or which paths are checked with static analysis or for coding standards issues.</p> + +<p>I've given a talk called <a href="http://localhost:8000/presentations/working-with-workspace">Working with Workspace</a> - a tool that we used at an agency I worked at. It had two functions - to execute project tasks and to generate configuration files from templates.</p> + +<p>I use a <code>justfile</code> to execute tasks and commands but needed to write my own tool to generate the configuration files.</p> + +<p>The result is that I can add one YAML file to a project, enter the values that it needs and when I run the CLI tool, it will generate all of the files the project needs.</p> + +<p>As well as being faster to set up a project, like a <a href="https://github.com/opdavies/docker-examples/tree/main/drupal-localgov">LocalGov Drupal Docker example</a>, having a canonical set of templated configuration files I can enhance and maintain by adding new features and fixes and they'll be added to every project when I next generate its files.</p> +
+
+ Sat, 04 Mar 2023 00:00:00 GMT +
+ + Adding a LocalGov Drupal example + + /daily/2023/03/03/adding-a-localgov-drupal-example + http://localhost:8000/daily/2023/03/03/adding-a-localgov-drupal-example + +
<p>After <a href="http://localhost:8000/daily/2022/10/24/looking-at-localgov-drupal">previously looking at the LocalGov Drupal distribution</a>, today I added a LocalGov Drupal example to my <a href="https://github.com/opdavies/docker-examples/tree/main/drupal-localgov">Docker Examples repository</a>.</p> + +<p>Based on the Drupal 10 example, the LocalGov Drupal example used Composer's <code>create-project</code> command to scaffold the project plus some additional configuration files such as the <code>Dockerfile</code>, <code>docker-compose.yaml</code> and a <code>justfile</code>.</p> + +<p>A feature on my list is to add to both examples is to install Drupal automatically instead of opening the installation screen or having to run an install command. Once that's in place, it'll remove a manual step and allow people to see the application more quickly.</p> + +<p>I have some issues for examples and enhancements to add within <a href="https://github.com/opdavies/docker-examples/issues">the repository's issue queue</a> and would welcome any suggestions or questions.</p> + +<p>For LocalGov itself, I hope to find more opportunities to use and contribute to it in the future.</p> +
+
+ Fri, 03 Mar 2023 00:00:00 GMT +
+ + Busy working on client projects + + /daily/2023/03/02/busy-working-on-client-projects + http://localhost:8000/daily/2023/03/02/busy-working-on-client-projects + +
<p>As well as starting to speak again at conferences and meetups, I've been very busy lately with various client projects. I've been working on completing the next phase and launch of the next website for a long-term client and handed over some other completed projects.</p> + +<p>This has affected me sending out these emails every day and I missed a few days last month.</p> + +<p>I'll do better!</p> +
+
+ Thu, 02 Mar 2023 00:00:00 GMT +
+ + Tailwind CSS at the Norfolk Developer Conference + + /daily/2023/03/01/tailwind-css-at-the-norfolk-developer-conference + http://localhost:8000/daily/2023/03/01/tailwind-css-at-the-norfolk-developer-conference + +
<p>Last week, I was at the Norfolk Developer's Conference, aka <code>nor(DEV):con</code> - my first in-person conference since DrupalCamp London in February 2020.</p> + +<p>I've been excited about this conference since I received the acceptance email in November, inviting me to give my <a href="http://localhost:8000/talks/taking-flight-with-tailwind-css">Taking Flight with Tailwind CSS</a> talk.</p> + +<p>The talk itself went well and included some changes following the Bristol Software Development meetup last month. The last time I gave this talk prior to that was remotely for Nashville PHP in February 2021 and a lot of new things have been added to Tailwind CSS since then for me to include.</p> + +<p>My next talk will be at the PHP London meetup next month (subject TBC) as I continue working towards my hundredth presentation.</p> +
+
+ Wed, 01 Mar 2023 00:00:00 GMT +
+ + Tailwind: why I prefer to extract HTML components + + /daily/2023/02/20/tailwind-why-i-prefer-to-extract-html-components + http://localhost:8000/daily/2023/02/20/tailwind-why-i-prefer-to-extract-html-components + +
<p>Tailwind offers the @apply directive that you can use to extract components in your CSS by applying the styles the classes would have added.</p> + +<p>For example:</p> + +<pre><code class="css">/* Input */ + +.btn { + @apply inline-block rounded-3xl bg-blue-500 px-8 py-3 text-lg text-white hover:bg-blue-800 focus:bg-blue-800; +} + +/* Output */ + +.btn { + --tw-bg-opacity: 1; + --tw-text-opacity: 1; + background-color: rgb(59 130 246 / var(--tw-bg-opacity)); + border-radius: 1.5rem; + color: rgb(255 255 255 / var(--tw-text-opacity)); + display: inline-block; + font-size: 1.125rem; + line-height: 1.75rem; + padding-bottom: 0.75rem; + padding-left: 2rem; + padding-right: 2rem; + padding-top: 0.75rem; +} + +.btn:hover { + --tw-bg-opacity: 1; + background-color: rgb(30 64 175 / var(--tw-bg-opacity)); +} + +.btn:focus { + --tw-bg-opacity: 1; + background-color: rgb(30 64 175 / var(--tw-bg-opacity)); +} +</code></pre> + +<p>Whilst this works and reduced duplication, I prefer to handle this within my HTML instead.</p> + +<p>All templating engines I've used have ways to loop over or map through items and including separate files such as separate partials or different components.</p> + +<p>The main benefit of this is that you get the HTML structure of the component and not just the styling. If you extract a .btn component, it may depend on a span or other element within it to display correctly but as this isn't obvious, it may be missed when writing an implementation of it in HTML - this can't happen when working inside a loop or importing a canonical version.</p> + +<p>Also, by working just in the HTML markup, we continue to get the lower cognitive load and speed benefits that we're used to when styling with utility classes rather than switching between multiple files.</p> +
+
+ Mon, 20 Feb 2023 00:00:00 GMT +
+ + Clients dont care which design pattern you use + + /daily/2023/02/19/clients-dont-care-which-design-pattern-you-use + http://localhost:8000/daily/2023/02/19/clients-dont-care-which-design-pattern-you-use + +
<p>In most cases, clients don't care which CMS, framework, CSS library or design patterns you use. Clients are focused on the business value that those tools can provide, such as increasing traffic or conversion rate to increase revenue or awareness.</p> + +<p>Improving the load speed of a page is good, but what business value does that offer? Are customers more likely to make a purchase if the page is faster?</p> + +<p>If you can build the same page with different CMSes or frameworks, and style it in the same way with different CSS libraries, is there a business case for one over the other, such as better maintainability or a quicker time to launch the application?</p> + +<p>Can you ship a simple MVP sooner, and refactor to use design patterns later?</p> + +<p>Instead of focusing on technologies, tools and strategies, focus on the benefits and business value they can offer.</p> +
+
+ Sun, 19 Feb 2023 00:00:00 GMT +
+ + Creating a Drupal 10 compatible version of Override Node Options + + /daily/2023/02/18/drupal-10-version-of-override-node-options + http://localhost:8000/daily/2023/02/18/drupal-10-version-of-override-node-options + +
<p>Today, I reviewed <a href="https://www.drupal.org/project/override_node_options/issues/3269901">the issue and merge request</a> to make the <a href="https://www.drupal.org/project/override_node_options">Override Node Options</a> module compatible with Drupal 10.</p> + +<p>It's a small patch that mainly affects the module's test suite so has a low risk of breaking the functionality of the module.</p> + +<p>As well as the automated tests, I've done some manual testing with the patch applied on both Drupal 9.5 and 10.</p> + +<p>The project page shows the module is currently used on over 34,000 sites including 18,565 Drupal 8 and 9 websites on the 8.x-2.x branch. Hopefully this number will continue to increase once the Drupal 10 version is released.</p> +
+
+ Sat, 18 Feb 2023 00:00:00 GMT +
+ + Upgrading my Drupal example project to Drupal 10 + + /daily/2023/02/17/upgrading-my-drupal-example-project-to-drupal-10 + http://localhost:8000/daily/2023/02/17/upgrading-my-drupal-example-project-to-drupal-10 + +
<p>Today I upgraded my <a href="https://github.com/opdavies/docker-examples/tree/main/drupal">Drupal Docker example</a> to Drupal 10.</p> + +<p>Admittedly, it's a simple project, but upgrading from Drupal 9 to 10 was a straightforward process.</p> + +<p>I had to remove the Examples module as there's no Drupal 10 compatible version yet, but updating to Drupal 10 only needed me to change the version constraints in the <code>composer.json</code> file and run the <code>composer update</code> command.</p> + +<p>After completing a number of Drupal 7 upgrade projects which involve migrating content, rewriting custom modules, and rebuilding themes, it's great to be able to upgrade between major modern versions with a few simple commands.</p> +
+
+ Fri, 17 Feb 2023 00:00:00 GMT +
+ + Tailwind CSS at the Bristol Software Development Meetup + + /daily/2023/02/16/tailwind-css-at-the-bristol-software-development-meetup + http://localhost:8000/daily/2023/02/16/tailwind-css-at-the-bristol-software-development-meetup + +
<p>Tonight I was lucky to speak at the first <a href="https://www.meetup.com/south-wales-tech/events/291092930">Bristol Software Development Meetup event</a>, organised by South Wales Tech.</p> + +<p>Following a great talk from Tom Vaughan, I gave an updated version of my <a href="http://localhost:8000/talks/taking-flight-with-tailwind-css">Taking Flight with Tailwind CSS</a> talk.</p> + +<p>It's a talk that I first gave in January 2018 and one that I need to continuously update so that it's up to date with the latest version of the framework. The last time I gave this talk was in February 2021 so there was quite a lot of new things to cover.</p> + +<p>I was also able to show my recent <a href="https://bootstrap-with-tailwind.oliverdavies.uk">Tailwind versions of Bootstrap components</a> as a demonstration at the end of the talk.</p> + +<p>I'm glad to have had the opportunity to test out the updated version before <a href="https://nordevcon.com">Norfolk Developers conference</a> next week and I'm looking forward to attending more South Wales Tech events in Swansea and Bristol in the future.</p> +
+
+ Thu, 16 Feb 2023 00:00:00 GMT +
+ + Creating API endpoints with Astro + + /daily/2023/02/09/creating-api-endpoints-with-astro + http://localhost:8000/daily/2023/02/09/creating-api-endpoints-with-astro + +
<p>As well as <a href="http://localhost:8000/daily/2023/02/08/fetching-api-data-with-astro">fetching API data</a>, you can also use Astro to generate your own API endpoints.</p> + +<p>This is an example of an endpoint that I recently created as part of a demo application:</p> + +<pre><code class="javascript">// trains.json.ts + +import data from "@/data.json"; +import type { APIRoute } from "astro"; +import type { Train } from "@/types"; + +export const get: APIRoute = () =&gt; { + return { + body: JSON.stringify(data.trains as Train[]), + }; +}; +</code></pre> + +<p>The train data is imported from a JSON file, and Astro's <code>APIRoute</code> is responsible for setting the appropriate response headers.</p> + +<p>For server-side rendered applications, you can also have endpoints for <code>post</code>, <code>del</code> and <code>all</code>, though for this example, I only needed to support GET requests.</p> + +<p>This is something that I've used a db-json library for previously, but being able to do this in Astro seemed like a good fit as I can easily manage lists of stations as well as a single station from one JSON file but I can just take the static HTML that Astro generates and upload it to a static hosting solution which simplifies the hosting side of things a lot.</p> + +<p>And, as the example application that consumes the data is also written with Astro, having them both using the same solution has advantages too.</p> +
+
+ Thu, 09 Feb 2023 00:00:00 GMT +
+ + Fetching external API data with Astro + + /daily/2023/02/08/fetching-api-data-with-astro + http://localhost:8000/daily/2023/02/08/fetching-api-data-with-astro + +
<p>As well as using static data like Astro components and Markdown files, Astro allows you to pull in data from external APIs even if you're generating a static website.</p> + +<p>Astro's <a href="https://docs.astro.build/en/guides/data-fetching">Data Fetching documentation</a> shows how to do this using the global <code>fetch</code> function. This is the same approach that I've been using to build <a href="https://github.com/opdavies/oliverdavies.uk/blob/39314de34ce22b14cf85f816e4469cc4d6fb822c/website/src/pages/open-source.astro">a page of my open-source projects</a>. I'm still working on it, but it fetches project information from both Drupal.org and GitHub and displays them on a page.</p> + +<p>This is also makes Astro a good option to be used as a front-end for decoupled projects that use a separate back-end CMS like Drupal to store and manage the content which it fetches and uses when generating the site.</p> +
+
+ Wed, 08 Feb 2023 00:00:00 GMT +
+ + Astro as a static site generator + + /daily/2023/02/07/astro-as-a-static-site-generator + http://localhost:8000/daily/2023/02/07/astro-as-a-static-site-generator + +
<p>Since first setting it first during Simon Vrachliotis' workshops, I've started evaluating <a href="https://astro.build">Astro</a> as an option for building static websites alongside PHP options like Sculpin and Jigsaw, and used it to rebuild <a href="http://localhost:8000">my personal website</a>.</p> + +<p>As well as being a good opportunity to get more experience with JavaScript, TypeScript and JSX templating, Astro includes a number of features that were useful for my website, such as code syntax highlighting and markdown support out of the box, as well as integrations to set up Tailwind CSS, sitemaps and images that I needed.</p> + +<p>Its file-based routing is very familiar to what I'd been working with in Next.js, including dynamic and nested routes such as my Blog, Talks, and email archive pages.</p> + +<p>A static HTML version of the site can be generated which can be uploaded to a web server - like with other static site generators - with the advantage that Astro includes no JavaScript by default.</p> + +<p>As well as my personal website, I've been using Astro for some other mini-projects including <a href="http://localhost:8000/daily/2023/01/22/building-bootstrap-css-examples-with-tailwind">building Bootstrap components with Tailwind</a>, and I'm using it for the updated examples for my Tailwind CSS talk at nor(DEV):con in a couple of weeks time.</p> +
+
+ Tue, 07 Feb 2023 00:00:00 GMT +
+ + To squash or not to squash + + /daily/2023/01/25/to-squash-or-not-to-squash + http://localhost:8000/daily/2023/01/25/to-squash-or-not-to-squash + +
<p>When reviewing a pull or merge request, tools like GitHub and GitHub offer the option to squash the commits before merging.</p> + +<p>If the request had twenty commits, they'd be combined into a single commit before being merged.</p> + +<p>But should you do it?</p> + +<p>The answer will be "it depends" based on the project or team, but I'm personally not a fan of squashing commits.</p> + +<p>Even though I commit small changes often, I put quite a bit of effort into <a href="http://localhost:8000/daily/2023/01/24/small-commits-and-good-commit-messges">crafting commits and writing detailed commit messages</a> that capture the reason for each change. If the commits are squashed, either the messages will be combined into one extra-long commit message or I've seen them be deleted completely.</p> + +<p>One large commit message would be very difficult to read and connect specific messages with their changes, and deleting the commit body would lose the history completely and waste the time it took to write the messages and craft the commits. It may be available within the pull or merge request page but there's no guarantee that you'll continue to use the same repository hosting service in the future.</p> + +<p>One large commit would also be difficult to debug if there was an error. If the whole feature was added in a single commit, tools like <a href="http://localhost:8000/daily/2023/01/23/debugging-with-git-bisect">git bisect</a> would no longer work and a single commit couldn't be simply reverted if it contained a bug.</p> + +<p>I prefer to keep the original small commits and instead prefer to use rebasing and only fast-forward merges to avoid merge commits and keep a simple, linear history in my Git log, and be able to easily read, find and, if needed, fix the code that's been committed.</p> +
+
+ Wed, 25 Jan 2023 00:00:00 GMT +
+ + Small commits and good commit messges + + /daily/2023/01/24/small-commits-and-good-commit-messges + http://localhost:8000/daily/2023/01/24/small-commits-and-good-commit-messges + +
<p>An important thing when using a tool like <a href="http://localhost:8000/daily/2023/01/23/debugging-with-git-bisect">git bisect</a> as well as reviewing pull/merge requests and commits is to have small (aka "atomic") commits.</p> + +<p>Commits with small changes make them easier to review and, if needed, to revert and debug with bisect. If a commit has ten new or changed lines, it's much easier to see and fix a bug than if the commit had a hundred lines.</p> + +<p>If you're doing code reviews or "Showing" in a Ship/Show/Ask format, you'll likely get better and more valuable feedback if the commits are smaller and only doing one thing.</p> + +<p>If you're submitting a change for review, commit a failing test first so that it can be seen failing before committing the code to make it pass. This makes it easier to see that the code is actually making the test pass.</p> + +<p>Also take some time to write good, informative commit messages.</p> + +<p>As well as the short one-line subject, you can add as much detail as you need to the body of the message about the change that's being committed, why it's needed, what other approaches were considered or tried, as well as links to supporting documentation such as ADRs, technical design documents or diagrams.</p> + +<p>Having as much information as possible makes it much easier when someone needs to review or fix a specific commit. I like to use the <a href="http://localhost:8000/daily/2022/09/01/conventional-commits-changelogs">conventional commits specification</a>, though the main objective is to have all of the information documented so it's available in the future.</p> +
+
+ Tue, 24 Jan 2023 00:00:00 GMT +
+ + Debugging with git bisect + + /daily/2023/01/23/debugging-with-git-bisect + http://localhost:8000/daily/2023/01/23/debugging-with-git-bisect + +
<p>Last week, I had to debug a regression in a codebase.</p> + +<p>Something was working at the last release but is now broken.</p> + +<p>There have been around 20 commits to the mainline branch since the last release, and the first step to fixing the issue is to determine which commit caused the regression.</p> + +<p>Git has a great tool for this - <code>git bisect</code>.</p> + +<p>You tell Git what the last known working commit was, such as the tag of the last release, and it will start to split the commits and prompt you to tell it whether the commit is good or bad.</p> + +<p>If there are 20 commits, it may pick commit number 10, and based on whether the commit is good or bad, it may pick commit 5 or 15.</p> + +<p>Based on your answers, Git will then tell you which the first bad commit is.</p> + +<p>Even better, if it's something that you can script or is covered with an automated test, <code>git bisect</code> can run a command for you and find the failure automatically rather than a human needing to check manually.</p> + +<p>Once you've found the commit that breaks, you can view it and find and fix the bug.</p> +
+
+ Mon, 23 Jan 2023 00:00:00 GMT +
+ + Building Bootstrap CSS examples with Tailwind + + /daily/2023/01/22/building-bootstrap-css-examples-with-tailwind + http://localhost:8000/daily/2023/01/22/building-bootstrap-css-examples-with-tailwind + +
<p>Previously when I gave my <a href="http://localhost:8000/presentations/taking-flight-with-tailwind-css">Taking Flight with Tailwind CSS</a> talk, I created examples that relate to the event such as something related to that technology or event.</p> + +<p>The nor(DEV):con website already uses Tailwind CSS so I've been looking for other examples and have started to build some of the <a href="https://getbootstrap.com/docs/4.0/examples">Bootstrap CSS components</a> with Tailwind CSS.</p> + +<p>I've created the <a href="http://bootstrap-with-tailwind.s3-website.eu-west-2.amazonaws.com/album">Album</a> and <a href="http://bootstrap-with-tailwind.s3-website.eu-west-2.amazonaws.com/pricing">Pricing</a> components so far but will likely do more before the conference as I think they're good examples.</p> + +<p><a href="https://github.com/opdavies/bootstrap-with-tailwind">The source code</a> is on my GitHub profile, uses Astro and its Tailwind CSS integration, the default Tailwind configuration with some slight colour and font tweaks, the official Typography plugin and a small custom plugin that adds a "hocus" variant for both hover and focus styles.</p> +
+
+ Sun, 22 Jan 2023 00:00:00 GMT +
+ + Tailwind: Not just translating CSS to utility classes + + /daily/2023/01/21/tailwind-not-just-translating-css-to-utility-classes + http://localhost:8000/daily/2023/01/21/tailwind-not-just-translating-css-to-utility-classes + +
<p>Whilst a number of classes that Tailwind CSS generates are single-value utility classes such as <code>.block { display: block; }</code> and <code>.relative { position: relative; }</code>, it also includes additional things such as <a href="https://tailwindcss.com/docs/font-size#setting-the-font-size">setting a default line height with a font size</a>, adding variants for <a href="https://tailwindcss.com/docs/hover-focus-and-other-states#styling-based-on-parent-state">group-hover</a> and <a href="https://tailwindcss.com/docs/hover-focus-and-other-states#focus-within">focus-within</a> (as well as various other pseudo states).</p> + +<p>It includes utilities for adding <a href="https://tailwindcss.com/docs/space#add-horizontal-space-between-children">horizontal or vertical space</a> or <a href="https://tailwindcss.com/docs/divide-style">styling dividers between elements</a>.</p> + +<p>As well prefixing a class using a screen modifier, such as <code>md:flex</code>, you can stack them and set a maximum screen value. For example, <code>md:max-lg:flex</code> will only flex between the <code>md</code> and <code>lg</code> breakpoints.</p> + +<p>If has official plugins for typography styles, forms, line-clamp (truncating text) and aspect ratios, as well as numerous community plugins and supports arbitrary values if you need them.</p> + +<p>If you take a look at the <a href="https://tailwindcss.com/docs">Tailwind CSS documentation</a>, you'll definitely find some interesting and useful things.</p> +
+
+ Sat, 21 Jan 2023 00:00:00 GMT +
+ + Tailwind's classes are your classes + + /daily/2023/01/20/tailwinds-classes-are-your-classes + http://localhost:8000/daily/2023/01/20/tailwinds-classes-are-your-classes + +
<p>In my <a href="http://localhost:8000/talks/taking-flight-with-tailwind-css">Taking Flight with Tailwind CSS talk</a>, I've described Tailwind as a CSS utility class generator. You write a configuration file that Tailwind reads and generates the appropriate classes.</p> + +<p>Yesterday I mentioned the <a href="https://phpsw.uk">PHP South West user group</a> website. It's a project that <a href="https://twitter.com/opdavies/status/934488762276564993">I worked on in November 2017</a> and uses Tailwind 0.5.</p> + +<p>For me, a big advantage of Tailwind was that once it generates the classes, they are ours. They aren't hidden within the framework or an npm package. We can see them, commit them to version control and keep them for as long as we need.</p> + +<p>Even if the Tailwind CSS framework disappeared tomorrow (I hope it doesn't), I'd be able to continue using the styles that it generated, and my projects would continue to work and look the same.</p> +
+
+ Fri, 20 Jan 2023 00:00:00 GMT +
+ + Long-term maintainability with utility classes and Tailwind CSS + + /daily/2023/01/19/long-term-maintainability-with-utility-classes-and-tailwind-css + http://localhost:8000/daily/2023/01/19/long-term-maintainability-with-utility-classes-and-tailwind-css + +
<p>One of the common initial concerns of utility classes and Tailwind CSS is its maintainability.</p> + +<p>How you manage all the classes within the HTML and how easy is it to make changes?</p> + +<p>Today, <a href="https://twitter.com/mauro_codes/status/1615726036737576960">I saw this tweet</a>:</p> + +<blockquote> + <p>You can say whatever you want about @tailwindcss, but I just opened a legacy project with tailwind 0.7 that I didn't touch for almost three years... and I was able to update the whole branding in 30 minutes.</p> +</blockquote> + +<p>One of my earliest Tailwind projects was the <a href="https://phpsw.uk">PHP South West website</a>. We <a href="https://twitter.com/opdavies/status/934488762276564993">worked on this in November 2017</a> and it still uses Tailwind CSS 0.5.</p> + +<p>These are the classes used on the main menu:</p> + +<p><code>bg-grey-lightest hidden absolute z-20 w-full border border-grey-lighter sm:flex sm:relative sm:w-auto sm:border-none</code></p> + +<p>Even though Tailwind CSS is now on version 3.2.4, I can still read this and know exactly what the classes do, and I'm confident that I could easily make changes to this or any other element on the website.</p> + +<p>That probably isn't something that I could say for other projects that use different approaches to styling, and definitely an advantage of styling with small, reusable utility classes.</p> +
+
+ Thu, 19 Jan 2023 00:00:00 GMT +
+ + Drupal turns 22! + + /daily/2023/01/18/drupal-turns-22 + http://localhost:8000/daily/2023/01/18/drupal-turns-22 + +
<p>On Sunday, the Drupal project turned 22 years old.</p> + +<p>I've personally been working with Drupal primarily for almost 15 years <a href="https://www.drupal.org/u/opdavies">according to my Drupal.org profile</a>, self teaching and working on personal and freelance projects since 2007, and in full-time roles since 2010 when I started at Horse and Country TV to work on their Drupal 6 website.</p> + +<p>Last month, I started my first Drupal 10 project for a client.</p> + +<p>I'm proud that over a year of that time was spent working for the Drupal Association where I improved the Drupal.org websites and resolved issues that would have blocked the Drupal 8 release in 2015.</p> + +<p>I'm also happy to have <a href="http://localhost:8000/presentations">presented talks and workshops</a> at events like DrupalCamps, Drupal Developer Days and twice at DrupalCon. I've also organised Drupal events such as the Drupal Bristol meetup and DrupalCamp Bristol conference.</p> + +<p>I've had opportunities to contribute to open-source by having patches committed to Drupal core and maintaining modules and themes like <a href="https://www.drupal.org/project/override_node_options">Override Node Options</a> and the <a href="https://www.drupal.org/project/tailwindcss">Tailwind CSS starter kit</a>. I've also mentored and helped others to get their first commits to Drupal core and open-source.</p> + +<p>I plan to continue working with Drupal and being part of the community for a long time, hopefully for at least another 22 years.</p> +
+
+ Wed, 18 Jan 2023 00:00:00 GMT +
+ + Things to know about PHP + + /daily/2023/01/17/things-to-know-about-php + http://localhost:8000/daily/2023/01/17/things-to-know-about-php + +
<p>The talk that I gave last week at PHP Stoke was <a href="http://localhost:8000/presentations/things-you-should-know-about-php">Things to know about PHP</a> - a talk that I was originally asked to give at the Swansea Software Development Meetup (SSDC) in January 2019, and this was the second time that I've been asked to give this talk at a PHP meetup.</p> + +<p>Originally to give a group of various Software Developers an introduction to PHP, I didn't want the talk to be focused just on the language itself and be a walkthough the PHP manual.</p> + +<p>Whilst there is some code in the talk so demonstrate what it can do, the other half of the talk is about the other parts of the PHP ecosystem such as tooling and the community. I talk about tools like Composer, PHPStan, Xdebug, PHPUnit and Pest, the CMSes and frameworks that are available, and the various online learning tools like SymfonyCasts and Laracasts as well as certification programs that are available.</p> + +<p>It's great to give this talk at meetups as that's where the community starts, where people come to share and learn, and what larger conferences like PHP UK, php[tek] and others are built on. I love attending meetups and events and meeting other members of the PHP community.</p> + +<p>Oh, and elePHPants!</p> + +<p>The latest versions of the slides are on the Talks section of my website (the link is at the top of the email).</p> + +<p>If you'd like me to come and speak at your meetup or conference, please get in touch!</p> +
+
+ Tue, 17 Jan 2023 00:00:00 GMT +
+ + Back after PHP Stoke + + /daily/2023/01/16/back-after-php-stoke + http://localhost:8000/daily/2023/01/16/back-after-php-stoke + +
<p>I took a bit of time off from these emails whilst I was preparing for the first PHP Stoke event which happened last week in Stoke-on-Trent.</p> + +<p>It was a great event with around 35 attendees and two other speakers as well as myself.</p> + +<p>The <a href="http://localhost:8000/presentations/things-you-should-know-about-php">latest version of my slides are online</a> as well <a href="http://localhost:8000/things-about-php">some updated resources</a>.</p> + +<p>My next talk will be at the Norfolk Developers conference next month where I'll be presenting an updated version of <a href="http://localhost:8000/presentations/taking-flight-with-tailwind-css">Taking Flight with Tailwind CSS</a>.</p> + +<p>If you have a topic idea for a talk or would like me to speak at your meetup or conference, please get in touch.</p> +
+
+ Mon, 16 Jan 2023 00:00:00 GMT +
+ + Reducing utility class duplication + + /daily/2023/01/07/reducing-utility-class-duplication + http://localhost:8000/daily/2023/01/07/reducing-utility-class-duplication + +
<p>One of the main concerns for Developers evaluating or starting with a utility-first approach to styling is maintainability due to the number and duplication of classes.</p> + +<p>For example, with this component, as I duplicate it to add more links, I also duplicate the classes applied to the link - making it harder to maintain:</p> + +<pre><code class="html">&lt;li&gt; + &lt;a class="block py-2 border-b-2 border-transparent hover:text-blue-500 hover:border-blue-500 focus:bg-yellow-400 focus:border-current focus:outline-none" href="#0"&gt;About&lt;/a&gt; +&lt;/li&gt; +</code></pre> + +<p>There are some solutions to this, which you can also see in an example Astro project at https://github.com/opdavies/reducing-utility-class-duplication.</p> + +<h2 id="extracting-a-variable-of-class-names">Extracting a variable of class names</h2> + +<p>The simplest way to remove the duplication is to create a variable that holds the class names which can be reused. For example, Astro allows variables to be created within the frontmatter section and used within the template. Creating variables can be done in other templating engines too.</p> + +<pre><code class="astro">--- +const linkClasses = "py-2 block border-b-2 border-transparent hover:border-blue-500 hover:text-blue-500 focus:outline-none focus:bg-yellow-400 focus:border-current"; +--- + +&lt;li&gt;&lt;a class={linkClasses} href="#0"&gt;About&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a class={linkClasses} href="#0"&gt;Talks&lt;/a&gt;&lt;/li&gt; +&lt;li&gt;&lt;a class={linkClasses} href="#0"&gt;Blog&lt;/a&gt;&lt;/li&gt; +</code></pre> + +<h2 id="extracting-css-components">Extracting CSS components</h2> + +<p>Instead of extracting a variable, you could also extract a CSS component. With Tailwind, the <code>@apply</code> directive within a stylesheet will create a reusable CSS component:</p> + +<pre><code class="astro">&lt;Layout&gt; + &lt;ul + class="flex flex-col gap-4 justify-center sm:flex-row sm:flex-wrap sm:gap-6" + &gt; + &lt;li&gt;&lt;a class="link" href="#0"&gt;About&lt;/a&gt;&lt;/li&gt; + &lt;li&gt;&lt;a class="link" href="#0"&gt;Blog&lt;/a&gt;&lt;/li&gt; + &lt;li&gt;&lt;a class="link" href="#0"&gt;Talks&lt;/a&gt;&lt;/li&gt; + &lt;/ul&gt; +&lt;/Layout&gt; + +&lt;style&gt; + .link { + @apply py-2 block border-b-2 border-transparent hover:border-blue-500 hover:text-blue-500 focus:outline-none focus:bg-yellow-400 focus:border-current; + } +&lt;/style&gt; +</code></pre> + +<p>The <code>link</code> class can be added to any link items, and whilst the approach works, I prefer to use template-based solutions and keep the classes within the HTML markup.</p> + +<h2 id="loops-and-maps">Loops and maps</h2> + +<p>Templating engines will have a way to loop over a list of items. This can be used to remove the duplication and only have a single list of classes whilst keeping the benefits of keeping them in the HTML code.</p> + +<p>For example, in Astro:</p> + +<pre><code class="astro">--- +const links = [ + { name: "About" }, + { name: "Blog" }, + { name: "Talks" }, + { name: "Daily list" }, + { name: "Search" }, +]; +--- + +{links.map(link =&gt; ( + &lt;li&gt; + &lt;a class="block py-2 border-b-2 border-transparent hover:text-blue-500 hover:border-blue-500 focus:bg-yellow-400 focus:border-current focus:outline-none" href="#0"&gt; + {link.name} + &lt;/a&gt; + &lt;/li&gt; +))} +</code></pre> + +<h2 id="extracting-html-components">Extracting HTML components</h2> + +<p>Finally, if the component needs to be reused, it can be extracted into its own file and re-imported where needed.</p> + +<p>This is the extracted component:</p> + +<pre><code class="astro">--- +const { name } = Astro.props; +--- + +&lt;a class="block py-2 border-b-2 border-transparent hover:text-blue-500 hover:border-blue-500 focus:bg-yellow-400 focus:border-current focus:outline-none" href="#0"&gt; + {name} +&lt;/a&gt; +</code></pre> + +<p>And the original file:</p> + +<pre><code class="astro">--- +import Layout from "~/layouts/Layout.astro"; +import Link from "~/components/Link.astro"; +--- + +&lt;Layout&gt; + &lt;ul class="flex flex-col gap-4 justify-center sm:flex-row sm:flex-wrap sm:gap-6"&gt; + &lt;li&gt;&lt;Link name="About" /&gt;&lt;/li&gt; + &lt;li&gt;&lt;Link name="Blog" /&gt;&lt;/li&gt; + &lt;li&gt;&lt;Link name="Talks" /&gt;&lt;/li&gt; + &lt;li&gt;&lt;Link name="Daily list" /&gt;&lt;/li&gt; + &lt;li&gt;&lt;Link name="Search" /&gt;&lt;/li&gt; + &lt;/ul&gt; +&lt;/Layout&gt; +</code></pre> + +<p>This can now be used anywhere within this project and could be combined with the loop approach, passing the appropriate value to the <code>name</code> prop.</p> + +<h2 id="summary">Summary</h2> + +<p>All of these approaches remove duplication, either using features provided by Tailwind CSS or a templating engine, to make the code more maintainable.</p> + +<p>I've shown <a href="https://astro.build">Astro</a> in these examples but the same can be done with PHP, Twig, Blade, Vue, React, etc.</p> + +<p>The examples are public on GitHub at https://github.com/opdavies/reducing-utility-class-duplication/tree/main/src/pages.</p> +
+
+ Sat, 07 Jan 2023 00:00:00 GMT +
+ + Utility-first or utility-last? + + /daily/2023/01/06/utility-first-or-utility-last + http://localhost:8000/daily/2023/01/06/utility-first-or-utility-last + +
<p>Tailwind CSS is based on the "utility-first" approach, where the majority of styling is done using utility CSS classes. You can still add additional custom styles if needed if there is something that can't be achieved with utilities or would be better placed in a custom CSS stylesheet.</p> + +<p>Utility-last is the opposite approach, where a small number of utilities are used alongside other CSS styles or another framework. This is common when <a href="http://localhost:8000/daily/2023/01/05/adding-tailwind-to-an-existing-project">adding a utility-based framework like Tailwind CSS</a> to an existing project and you want to use it for a new page or component whilst keeping the existing styles.</p> + +<p>This is what I did when I started learning Tachyons, and later Tailwind CSS. My project already had styling from another CSS framework, I started using utility classes and over time refactored until only utilities were used and the other styles could be removed.</p> + +<p>You could do the opposite too, and refactor groups of utilities into a CSS component, either by using Tailwind's <code>@apply</code> directive or <a href="http://localhost:8000/daily/2023/01/03/tailwind-css-extensibility-is-one-of-its-best-features">writing a plugin</a>.</p> + +<p>If you did want to use Tailwind for a small number of utilities, by default, it will only generate classes that are used within HTML or template files, but it can also be configured to only use the core modules that you specify - preventing Developers from using unwanted utility based on the rules for that project.</p> + +<p>You could, of course, write your own utilities, or they're becoming common in other frameworks now too for things like padding, margin and colours.</p> + +<p>Do you typically do styling utility-first or utility-last, or are you transitioning from one to the other?</p> +
+
+ Fri, 06 Jan 2023 00:00:00 GMT +
+ + Adding Tailwind CSS to an existing project + + /daily/2023/01/05/adding-tailwind-to-an-existing-project + http://localhost:8000/daily/2023/01/05/adding-tailwind-to-an-existing-project + +
<p>Tailwind's configuration file also makes it easy to add it to an existing codebase, whether it uses custom CSS or another CSS framework.</p> + +<p>You can opt-out of Tailwind's CSS reset and normalisation by adding <code>corePlugins: { preflight: false }</code> to tailwind.config.js, and if you have duplicate classes with existing classes and Tailwind-generated ones, you can add <code>prefix: 'tw-'</code> to prefix all of Tailwind's class names and avoid the conflicts.</p> + +<p>If you need to deal with specificity, you can mark Tailwind-generated classes as important by adding <code>important: true</code> so that they can override any existing styling. You can do the same in HTML code by prefixing a class name with an exclamation mark - e.g. <code>!flex</code>.</p> + +<p>Or, if you know that your Tailwind styles will only be used within a certain element, like <code>#app</code>, instead of making all classes important, by adding <code>important: "#app"</code> to the configuration, Tailwind classes will be prefixed with that selector - making them more specific.</p> + +<p>Once these options are set, you can start using Tailwind without affecting the existing styles.</p> + +<p>Doing this, and starting small by using a small number of utilities can be a good step towards migrating an existing codebase to use Tailwind and removing the existing styles at a later date.</p> +
+
+ Thu, 05 Jan 2023 00:00:00 GMT +
+ + Testable Tailwind CSS plugins + + /daily/2023/01/04/testable-tailwind-css-plugins + http://localhost:8000/daily/2023/01/04/testable-tailwind-css-plugins + +
<p>A great thing about <a href="http://localhost:8000/daily/2023/01/03/tailwind-css-extensibility-is-one-of-its-best-features">Tailwind CSS plugins</a> being written in JavaScript is that they can be tested using tools like Jest.</p> + +<p>Here's an example from https://github.com/opdavies/tailwindcss-plugin-jest-example (it may need updating to work with the latest Tailwind versions or to use the latest best practices):</p> + +<pre><code class="javascript">function run(options = {}) { + return postcss( + tailwindcss({ + corePlugins: false, + plugins: [plugin(options)] + }) + ) + .process('@tailwind utilities;', { + from: undefined + }) +} + +expect.extend({ + toMatchCss: cssMatcher +}) + +test('it generates the correct classes with no variants', () =&gt; { + const output = ` + .test { + display: block + } + ` + + run().then(result =&gt; { + expect(result.css).toMatchCss(output) + }) +}) + +test('it generates the correct classes with variants', () =&gt; { + const output = ` + .test { + display: block + } + .hover\\:test:hover { + display: block + } + .focus\\:test:focus { + display: block + } + ` + + run({ variants: ['hover', 'focus'] }).then(result =&gt; { + expect(result.css).toMatchCss(output) + }) +}); +</code></pre> + +<p>Within the test, Tailwind can be run using PostCSS and generates styles based on a provided configuration, which is then checked against some expected output. If the generated styles match what was expected, the tests pass and the plugin is working as expected.</p> +
+
+ Wed, 04 Jan 2023 00:00:00 GMT +
+ + Tailwind CSS' extensibility is one of its best features + + /daily/2023/01/03/tailwind-css-extensibility-is-one-of-its-best-features + http://localhost:8000/daily/2023/01/03/tailwind-css-extensibility-is-one-of-its-best-features + +
<p>As well as the library of utility CSS classes that Tailwind provides, the ability to easily add your own has been one of my main advantages of using it.</p> + +<p>You can add your own CSS to a stylesheet within a specific layer, customise the theme settings within a <code>tailwind.config.js</code> file, and write your own custom plugins that you can reuse and even release as separate open-source projects.</p> + +<p>Here is a complete plugin that I've added to some projects recently:</p> + +<pre><code class="js">const plugin = require('tailwindcss/plugin'); + +plugin(function({ addVariant }) { + addVariant('hocus', ['&amp;:hover', '&amp;:focus']) +}); +</code></pre> + +<p>By adding these lines of JavaScript, Tailwind will generate a new set of utility classes that apply styles to both hover and focus states for an element - reducing the number of classes that need to be added and making it more maintainable.</p> + +<p>Other plugins that I've written recently include creating different variants of buttons, styling elements within an HTML table (similar to the Tailwind typography plugin), and adding multi-theme support to make components themable with different colours.</p> + +<p>I've also released some <a href="https://github.com/opdavies?tab=repositories&amp;q=tailwind-plugin">as open-source projects on GitHub</a>.</p> + +<p>The interesting thing is that the plugin API is how Tailwind itself generates its own classes, and I like the stability that provides.</p> + +<p>However you extend Tailwind, the fact you can tweak and extend it for each project is great and something that you can't always do or do easily with other tools or frameworks.</p> +
+
+ Tue, 03 Jan 2023 00:00:00 GMT +
+ + Don't use arbitrary values in Tailwind CSS + + /daily/2023/01/02/dont-use-arbitrary-values-in-tailwind-css + http://localhost:8000/daily/2023/01/02/dont-use-arbitrary-values-in-tailwind-css + +
<p>It's been almost five years since I gave the first version of my "<a href="http://localhost:8000/talks/taking-flight-with-tailwind-css">Taking Flight with Tailwind CSS</a>" talk at the Drupal Bristol meetup in January 2018.</p> + +<p>It's a talk that I've updated every time I've given it, showing new rebuilt example UIs and the new features in the framework, as well as tweaking content like installation steps for different audiences.</p> + +<p>As I prepare for the Norfolk Developers Conference (nordevcon) next month, I'm updating the talk again.</p> + +<p>One feature that wasn't in the last version of the talk is arbitrary values in class names. This, I think, is an antipattern and something that I avoid using.</p> + +<p>Whilst you could always add arbitrary values in custom CSS, one of my original reasons for liking and adopting Tailwind CSS was that there was a predefined list of classes to choose from.</p> + +<p>A set list of colours, text sizes, margins and padding, for example, meant that UIs looked more consistent and the generated CSS was smaller.</p> + +<p>If you need an additional value or to replace the existing values, you can do so within your <code>tailwind.config.js</code> file, and Tailwind will generate classes accordingly.</p> + +<p>Whilst writing custom CSS, inline styles, and arbitrary values are all possible, I'd suggest using the configuration file, generating the classes that you need, and sticking to them as much as you can and enjoying the consistency and benefits that brings.</p> +
+
+ Mon, 02 Jan 2023 00:00:00 GMT +
+ + Types and static analysis saved me today + + /daily/2023/01/01/types-and-static-analysis-saved-me-today + http://localhost:8000/daily/2023/01/01/types-and-static-analysis-saved-me-today + +
<p>Today I was writing Pulumi code in Typescript, and I although I have autocompletion, I mistyped the name of a property.</p> + +<p>But, because of the types that I'd declared and how I have Neovim configured, the error was immediately shown, and I was able to fix the typo and what would have been a compilation error if I'd tried to run it.</p> + +<p>It only took a second or two for me to make and fix the mistake.</p> + +<p>I didn't need to run the code or commit it and push it to a CI pipeline to find out that there was a bug.</p> + +<p>I found out and fixed it immediately, and moved on as if I hadn't written the typo at all. This is why I like types and static analysis.</p> +
+
+ Sun, 01 Jan 2023 00:00:00 GMT +
+ + Just start writing + + /daily/2022/12/31/just-start-writing + http://localhost:8000/daily/2022/12/31/just-start-writing + +
<p>Whether it's writing code or a technical design document, diagnosing an issue, or working on a blog post or a presentation deck, sometimes, the hardest part for me is getting started.</p> + +<p>When this happens, I try to find the smallest thing I can do to start the task and get the ball rolling.</p> + +<p>This could be just opening Neovim or Notion and finding the smallest task I can do to get going.</p> + +<p>In code, it could be making the smallest change to start on the task or committing a small refactoring on another part of the code to get into the flow.</p> + +<p>With a technical design document, once I've written the first heading, "What problem are we trying to solve?" and started to describe the problem, it's easy to continue writing the rest of the document.</p> + +<p>For a blog post or presentation, I've found that writing the headings first is a good starting point, adds structure and makes it easier to add the main content.</p> + +<p>Things are great once the ball is rolling; the question sometimes is, "what is the simplest thing I can do to start it?".</p> +
+
+ Sat, 31 Dec 2022 00:00:00 GMT +
+ + Tests are living documentation + + /daily/2022/12/30/tests-are-living-documentation + http://localhost:8000/daily/2022/12/30/tests-are-living-documentation + +
<p>Today I was working on a project and made a one-line change that updated a single value within an API response.</p> + +<p>Unexpectedly, it caused a test to fail. The API response returned a 500 status code instead of the expected 201 code.</p> + +<p>I reverted the change locally and ensured that the test passed again, so I knew it was causing the failure.</p> + +<h2 id="fixing-the-failure">Fixing the failure</h2> + +<p>The change was removing a hard-coded part of a URL to a dynamic one, using Drupal's <code>Settings</code> class.</p> + +<p>I was retrieving a value from it, but as there was no value being set within the test, it was returning a null value and causing the 500 error code.</p> + +<h2 id="how-does-the-settings-class-work%3F">How does the Settings class work?</h2> + +<p>To fix the test failure, I needed to learn how to set a Settings value within a test.</p> + +<p>To do this, I looked for and found the test for the Settings class itself. I saw how it was being set there, did the same in my test, fixed the failure and got my test passing again.</p> + +<h2 id="tests-are-living-documentation">Tests are living documentation</h2> + +<p>As well as verifying things work when they are written, tests also act as long-term documentation. They can be run at any point to ensure that they still pass and are a reference to other developers on how the code should work.</p> + +<p>Rather than a Confluence page, a README file or code comments which can become out of date, if a test becomes outdated, it will fail and make developers aware, as well as break any CI pipeline that it runs in.</p> + +<p>By writing tests, you're ensuring that your code works as expected and documenting it at the same time.</p> +
+
+ Fri, 30 Dec 2022 00:00:00 GMT +
+ + What is the cost of a bug? + + /daily/2022/12/29/what-is-the-cost-of-a-bug + http://localhost:8000/daily/2022/12/29/what-is-the-cost-of-a-bug + +
<p>All software has bugs, but each has a different cost depending on when and where it's found.</p> + +<h2 id="in-production">In production</h2> + +<p>If a bug was found in production, it was probably found by a user, will time to replicate, diagnose and fix, and could have caused some reputational or financial damage.</p> + +<p>You'd need to create a ticket and schedule the work, meaning you're not working on something else.</p> + +<p>Cost: High</p> + +<h2 id="in-a-pre-production-environment">In a pre-production environment</h2> + +<p>It's likely found by a test analyst or product owner but hidden from the end users and customers.</p> + +<p>It will still take time to replicate, diagnose and fix, and you'd still need to create a ticket and schedule the work, as well as the bug potentially blocking releases to production until it's resolved.</p> + +<p>Cost: High</p> + +<h2 id="in-a-ci-pipeline">In a CI pipeline</h2> + +<p>This is likely to be a static analysis or a test failure. It should be quicker to fix though it will need to be done quickly or reverted if you're doing trunk-based development to unblock other Developers.</p> + +<p>If you're doing code reviews, you'll need to ensure that the pipeline passes before the review and continues to pass after making any changes.</p> + +<p>Cost: Medium</p> + +<h2 id="locally">Locally</h2> + +<p>If an automated check fails in CI, why not run it locally before you commit and push the change?</p> + +<p>If it's a coding standards issue or static analysis failure, you could configure your text editor or IDE to show you issues in real time. Then they can be fixed immediately and not even committed and pushed to a CI pipeline.</p> + +<p>If you were working in a pair or mob, could someone else have spotted the issue and suggested a fix?</p> + +<p>Cost: Low</p> + +<p>The sooner you can find a bug, the less time and cheaper it is to fix, and we can use tools like automated tests and static analysis to help us write fewer bugs and to fix them sooner and faster.</p> +
+
+ Thu, 29 Dec 2022 00:00:00 GMT +
+ + Debugging gitignore rules + + /daily/2022/12/28/debugging-gitignore-rules + http://localhost:8000/daily/2022/12/28/debugging-gitignore-rules + +
<p>Have you wondered why a file or directory is being ignored in your Git repository and isn't appearing when you run <code>git status</code>?</p> + +<p>Maybe you have multiple <code>.gitignore</code> files and you don't know which is causing the issue, or you ignore everything and explicitly unignore the files that you want to add.</p> + +<h2 id="%60check-ignore%60-to-the-rescue"><code>check-ignore</code> to the rescue</h2> + +<p><code>git check-ignore</code> accepts a list of paths and checks if each is ignored.</p> + +<p>There's also a <code>--verbose</code> option that will tell you which <code>.gitignore</code> file is causing it to be ignored as well as the line number and the pattern - great information for debugging, and no more force adding files.</p> +
+
+ Wed, 28 Dec 2022 00:00:00 GMT +
+ + Speaking at PHP Stoke and nor(DEV):con + + /daily/2022/12/23/speaking-at-php-stoke-and-nordevcon + http://localhost:8000/daily/2022/12/23/speaking-at-php-stoke-and-nordevcon + +
<p>I had some early presents this month and have been accepted to present talks at the new PHP Stoke meetup and the Norfolk Developers Conference.</p> + +<h2 id="php-stoke">PHP Stoke</h2> + +<p>The first PHP Stoke meetup will be held at Genr8 Smithfield Works on the 12th of January. I'll be presenting "<a href="http://localhost:8000/presentations/things-you-should-know-about-php">Things you should know about PHP</a>". It'll be great to be the first speaker at this new meetup.</p> + +<h2 id="nordev%3Acon">nor(DEV):con</h2> + +<p>The Norfolk Developers conference is in Norwich on the 23rd and 24th of February.</p> + +<p>I'll be presenting "<a href="http://localhost:8000/presentations/taking-flight-with-tailwind-css">Taking Flight about Tailwind CSS</a>". It's been a while since I last gave this talk so I'm looking forward to updating it with the latest changes and new features in Tailwind CSS.</p> + +<p>It'll be great to be speaking at these events and hopefully others in 2023.</p> +
+
+ Fri, 23 Dec 2022 00:00:00 GMT +
+ + The Boy Scout rule + + /daily/2022/12/22/the-boy-scout-rule + http://localhost:8000/daily/2022/12/22/the-boy-scout-rule + +
<p>The Boy Scout rule is "Always leave the campground cleaner than you found it".</p> + +<p>From a programming perspective, "Always leave the code better than you find it".</p> + +<p>If you're working on some code and see some commented-out lines or unused variables, once you've finished with your changes, remove them too.</p> + +<p>If there's a variable that could have a more descriptive name, change it.</p> + +<p>If there are any other refactors you can do, do them and leave the code better than before and easier for the next Developer (whether it's you or someone else) to work with.</p> +
+
+ Thu, 22 Dec 2022 00:00:00 GMT +
+ + Duck typing + + /daily/2022/12/21/duck-typing + http://localhost:8000/daily/2022/12/21/duck-typing + +
<p>If it looks like a duck, walks like a duck, and quacks like a duck, it must be a duck.</p> + +<p>If I pass a parameter into a function, as long as it works with the code within the function - it is of the correct type and has the expected properties and methods - it will work, although nothing is enforcing that the correct thing is passed in.</p> + +<p>If not, you'd get an error that the method or property doesn't exist when trying to invoke or access it.</p> + +<p>This is the opposite of strict typing, where you'd get a Type error if you passed the wrong thing.</p> + +<p>Both are valid approaches, though I prefer to use types and get specific error messages and leverage tools like static analysis, which work better the more it understands your code.</p> + +<p>In particular when writing and using open-source code that anyone can use however they like, I'd suggest leveraging types and type-checking, but it depends on what you, your team or your community prefer.</p> +
+
+ Wed, 21 Dec 2022 00:00:00 GMT +
+ + Automating all the things, including infrastructure + + /daily/2022/12/20/automating-all-the-things,-including-infrastructure + http://localhost:8000/daily/2022/12/20/automating-all-the-things,-including-infrastructure + +
<p>I'm a big advocate for automation, from having a reproducible Docker and Docker Compose environment, automated tests, static analysis, and code linting in my projects to running them automatically on every push to a remote repository using a CI pipeline.</p> + +<p>I've written scripts to simplify common complex tasks and automated my local development environment setup before using tools like Ansible and, recently, the Nix package manager, NixOS and Home Manager.</p> + +<p>Another main piece of automation I use is to create and manage the infrastructure my applications run in.</p> + +<p>I've talked about it in my <a href="http://localhost:8000/talks/deploying-php-ansible-ansistrano">Deploying PHP applications with Ansible and Ansistrano talk</a>, that before you can deploy an application, you need a server with the required packages installed to run it. You may also need a CDN endpoint, SSL certificate, DNS records, CORS policy, database server, container registry, object storage bucket or a number of various other things.</p> + +<p>Using automation tools like Terraform, Pulumi or Ansible, you can run a single command to create, update or destroy resources or your entire stack.</p> + +<p>The automation code acts as living documentation, can be version-controlled, stored alongside the code, and read by the Developers or anyone else involved with the project.</p> + +<p>If you need to hand over the hosting requirements to a client or create their own version, you can do that with the same automation code.</p> + +<p>And if a disaster scenario was to happen and you needed to recreate your infrastructure from scratch or move to a different cloud provider, you could do that quickly using automation tools, knowing that the new infrastructure would be consistent with the original, all of the pieces would be in place, and without needing to recreate everything manually from scratch.</p> +
+
+ Tue, 20 Dec 2022 00:00:00 GMT +
+ + Happy Drupal 10 release day! + + /daily/2022/12/15/happy-drupal-10-release-day + http://localhost:8000/daily/2022/12/15/happy-drupal-10-release-day + +
<p>Today, Drupal 10 was released alongside Drupal 9.5.</p> + +<p>This means that, as of today, Drupal 9.3 is no longer supported, so people should update to 9.4, 9.5, or 10.</p> + +<p>As things stand, Drupal 7 is still supported until November 2023.</p> + +<p>I haven't tried upgrading any existing websites from Drupal 9 to 10 yet, but I have already started a new client project with Drupal 10.</p> + +<p>It's a project for a new client that we've been discussing for a few months, and we wanted to wait until Drupal 10 was released before starting. My preparation work has been on the release candidate version of Drupal 10, so I can't foresee any issues updating to the stable 10.0.0 version.</p> + +<p>As an open-source Drupal project maintainer, I'll soon release Drupal 10 compatible versions of my maintained modules and themes, such as Override Node Options and the Tailwind CSS starter kit theme.</p> + +<p>If your company has a Drupal module that needs to be upgraded to be Drupal 10 compatible, or needs a module to integrate your service with Drupal 10, reply to this email and let's arrange a call and see if we can work together.</p> +
+
+ Thu, 15 Dec 2022 00:00:00 GMT +
+ + Managing databases with Neovim and Docker + + /daily/2022/12/10/managing-databases-with-neovim-and-docker + http://localhost:8000/daily/2022/12/10/managing-databases-with-neovim-and-docker + +
<p>If you work on software projects that use a database, you need a convenient way to connect, inspect, and query them.</p> + +<p>Some IDEs like PhpStorm have an integrated database client, which I've recently added to my Neovim setup and working with Docker and Docker Compose.</p> + +<h2 id="configuring-docker-compose">Configuring Docker Compose</h2> + +<p>A local port needs to be exposed from the database container that Neovim can connect to.</p> + +<p>I usually do this with a <code>docker-compose.override.yaml</code> file:</p> + +<pre><code class="language-yaml">services: + database: + ports: + - 3307:3306 +</code></pre> + +<p>Docker Compose recognises this by default, extends the normal <code>docker-compose.yaml</code> file and adds a port forwarding to the <code>database</code> service.</p> + +<h2 id="configuring-neovim">Configuring Neovim</h2> + +<p>The two plugins I'm using are tpope/vim-dadbod and kristijanhusak/vim-dadbod-ui, and should work with Vim and Neovim. Thanks to both of these project maintainers.</p> + +<p>Once installed, run <code>:DBUIAddconnection</code> to add a database connection.</p> + +<p>Here is an example connection string:</p> + +<pre><code>mysql://drupal:drupal@localhost:3307/drupal?protocol=tcp +</code></pre> + +<p>Using the local port mapping, this connects to a MySQL database within the <code>database</code> service.</p> + +<p>Then run <code>:DBUI</code> to toggle the UI and see a list of databases in the left-hand sidebar.</p> + +<p>You can enter <code>&lt;leader&gt;R</code> to refresh databases or <code>r</code> to rename or update connection details for a specific database.</p> + +<p>Once connected, you can enter and save queries, using table and column name completion, which are executed every time the query is saved.</p> + +<p>I like not needing to switch contexts and leave my editor in order to query a database, and feel a lot more productive being able to write, execute and save queries within this UI.</p> + +<p>If you're a Vim or Neovim user, I'd suggest trying this setup and seeing if it works for you.</p> +
+
+ Sat, 10 Dec 2022 00:00:00 GMT +
+ + How and why I started using PostCSS + + /daily/2022/12/09/how-and-why-i-started-using-postcss + http://localhost:8000/daily/2022/12/09/how-and-why-i-started-using-postcss + +
<p>I assume that, like many other Developers, when I started learning front-end development, I wrote normal, plain CSS and later discovered and adopted pre-processors like Less and Sass that added features such as variables and nesting to my stylesheets.</p> + +<p>This was the case when I first saw what became Tailwind CSS, which were some stylesheets written in Less and ported manually between projects.</p> + +<p>I remember watching one of those streams, and a fellow viewer suggested PostCSS, which Tailwind CSS would later be written in.</p> + +<p>PostCSS, a CSS post-processor rather than a pre-processor, has become my preferred way of writing CSS because of Tailwind.</p> + +<p>When I started using Tailwind in my projects, I was layering it on top of another CSS framework or styles that were written using Less or Sass, so I needed to pre-process them into CSS first and then run PostCSS - essentially running two build steps and adding to the build time.</p> + +<p>I moved to use PostCSS by default - removing one of the build steps.</p> + +<p>What I liked about it, as well as the quicker build times, was that I could start with plain CSS and add the extra features I needed. I didn't use all of Sass and Less' features, and now, if I needed nesting or real-time imports, I could add it via a PostCSS plugin or write my own.</p> + +<p>It's also quick and easy to use, using the PostCSS CLI tool and without more complex tools like Webpack.</p> + +<p>If you haven't tried PostCSS, I recommend taking a look.</p> +
+
+ Fri, 09 Dec 2022 00:00:00 GMT +
+ + The Decorator design pattern + + /daily/2022/12/08/the-decorator-design-pattern + http://localhost:8000/daily/2022/12/08/the-decorator-design-pattern + +
<p>Decorator is a structural design pattern that allows you to add extra functionality, such as if you want to add caching or logging to a service, without changing the original class.</p> + +<p>As long as a class implements an Interface, it can be decorated.</p> + +<p>For example, if I have this PHP interface:</p> + +<pre><code class="language-php">interface DoesSomething +{ + public function doSomething(): void; +} +</code></pre> + +<p>I could have this class that does something:</p> + +<pre><code class="language-php">final class FirstClass implements DoesSomething +{ + public function doSomething(): void + { + // Does something. + } +} +</code></pre> + +<p>If I need to do something else, like caching or logging the result, I can decorate it.</p> + +<p>To do this, I need another class that implements the same interface and inject the original version.</p> + +<pre><code class="language-php">final class SecondClass implements DoesSomething +{ + public function __constuct( + private DoesSomething $originalClass + ) {} + + public function doSomething() + { + // Do something else before. + + $this-&gt;originalClass-&gt;doSomething(); + + // Do something else afterwards. + } +} +</code></pre> + +<p>Within the new class, the methods can be overridden, extra functionality can be added, and the original method can be run to execute the original functionality.</p> + +<p>As the two classes implement the same interface, I can swap between different versions and decorate multiple times if needed.</p> + +<p>This a pattern that I used recently to extend a service that retrieved some data from an API and saved it to a file, to change some arguments and do more work with it.</p> + +<p>The original class was unchanged, the new class was minimal and easy to understand as it only had a single responsibility, and if I needed to switch back to the original version, I could easily do that.</p> +
+
+ Thu, 08 Dec 2022 00:00:00 GMT +
+ + Separating releases from deployments with feature flags + + /daily/2022/12/07/separating-releases-from-deployments-with-feature-flags + http://localhost:8000/daily/2022/12/07/separating-releases-from-deployments-with-feature-flags + +
<p>In a typical feature release process, a feature is released when you merge the code and push it to production.</p> + +<p>If a bug is found after the release, the code needs to be reverted (and any conflicts or issues dealt with) and deployed again.</p> + +<p>Also, features can only be merged once they are complete, which may take hours, days or weeks, depending on the size of the feature.</p> + +<p>These are some reasons I like to use feature flags (aka feature toggles) and separate the code deployment from releasing the feature. The code is deployed as before, but the feature isn't released, and the code isn't executed until a feature flag is enabled.</p> + +<p>If there is a bug, the feature flag can be disabled, and the feature is turned off until a fix can be pushed - without needing another code deployment.</p> + +<p>If my feature is incomplete, if it's feature flagged, I can commit and deploy it without users seeing it or affecting the running application, resulting in smaller and more manageable commits and deployments.</p> + +<p>If you wanted, you could enable a feature flag for a subset or a certain subsection of your users - allowing them to test it before making it available to everyone.</p> + +<p>Another way I use feature flags is within a <a href="http://localhost:8000/daily/2022/11/13/how-i-manage-multiple-drupal-websites-using-the-same-codebase">multi-site Drupal application</a> to enable a different feature set per site and allow me to keep one version of the code for all sites to keep this easy to manage and maintain.</p> +
+
+ Wed, 07 Dec 2022 00:00:00 GMT +
+ + Should you comment your code? + + /daily/2022/12/06/should-you-comment-your-code + http://localhost:8000/daily/2022/12/06/should-you-comment-your-code + +
<p>Something that I hear often is "self-documenting code", and that code should be easy to understand without comments.</p> + +<p>If you need to write a comment explaining what something does, move the code into a new class or function and have its name describe what it does.</p> + +<p>I agree with this, but I still think there is a place for comments within the code as long as they add value.</p> + +<h2 id="what-comments-shouldn%27t-be">What comments shouldn't be</h2> + +<p>Comments shouldn't just repeat what the code says.</p> + +<p>If the comment says "Returns false", and I can read that from the code, the comment isn't adding any value.</p> + +<p>I use types a lot in PHP and TypeScript, and if docblocks are just repeating the native types, I'd rather not include them and keep the code minimal.</p> + +<p>Comments like "Constructor" and "This shouldn't happen" add no context.</p> + +<h2 id="what-comments-should-be">What comments should be</h2> + +<p>Comments need to add value by adding extra information or explaining why the code does what it does.</p> + +<p>I will add comments like "This class decorates ... so that ..." and "This returns ... if ...". These comments add value and make the code easier to read and understand. The downside is that you then need to maintain the comments, make sure they remain correct, and if the functionality changes the comment is also changed.</p> + +<p>If a static analysis tool requires me to provide more information in a docblock that describes some parameters or a return type, I'll do that. It makes the code quality better, reduces bugs, and makes the Developer experience better. As I'm improving the native types by providing more information, I'm not just repeating them so this is OK with me.</p> + +<p>Comments should make the code easier to read, understand, and work with for yourself and other team members.</p> +
+
+ Tue, 06 Dec 2022 00:00:00 GMT +
+ + Outcomes or output + + /daily/2022/12/05/outcomes-or-output + http://localhost:8000/daily/2022/12/05/outcomes-or-output + +
<p>I was reading a Twitter thread recently that <a href="https://twitter.com/allenholub/status/1594786089994067969">started with this tweet</a>.</p> + +<p>TL;DR:</p> + +<blockquote> + <p>"Measuring output (e.g. velocity) is a waste of time. It's outcomes you care about - customer happiness."</p> +</blockquote> + +<p>This is definitely something that I agree with.</p> + +<p>I can't remember the last time I looked at an output burndown chart or a velocity score.</p> + +<p>I'd rather focus on what outcomes have been delivered and adding value to clients and customers.</p> + +<p>What features did we release, what bugs did we fix, and what impact and value did that provide?</p> + +<p>What increased revenue or improved a customer's experience?</p> + +<p>I may have a sprint or cycle where I only deliver one or two large tasks, but those are major features that have been requested, a bug affecting a user's experience, or a task that unblocks future tasks.</p> + +<p>In that situation, my velocity may not be good and could be improved by working on multiple smaller tasks, but which outcomes provide the most value?</p> +
+
+ Mon, 05 Dec 2022 00:00:00 GMT +
+ + Writing "Why first" user stories + + /daily/2022/12/04/writing-why-first-user-stories + http://localhost:8000/daily/2022/12/04/writing-why-first-user-stories + +
<p>I've usually written user stories that follow this format:</p> + +<p>As a... +I want... +So that...</p> + +<p>You focus on the person who needs the task first, followed by what they want, and why they want it.</p> + +<p>Lately, I've been re-ordering stories to follow this order:</p> + +<p>So that... +As a... +I want...</p> + +<p>What I like about this is that it focuses on capturing the "why" of the story first rather than at the end or maybe even forgetting it altogether.</p> + +<p>What's needed and who needs it doesn't matter unless you also capture why it's needed.</p> +
+
+ Sun, 04 Dec 2022 00:00:00 GMT +
+ + What to do with TODO comments + + /daily/2022/12/03/what-to-do-with-todo-comments + http://localhost:8000/daily/2022/12/03/what-to-do-with-todo-comments + +
<p><a href="http://localhost:8000/daily/2022/11/20/version-controlled-commented-out-code">In a previous email</a>, I wrote about commented-out code and whether it should remain in a codebase - especially if it's version controlled and there's a commit log of all changes.</p> + +<p>But what about TODO comments that remind you to do something?</p> + +<p>When I think of TODO comments, I think of times when I've found them left in codebases from a long time ago by Developers who no longer work on it - never to be looked at again.</p> + +<p>I recently removed a small number from a codebase I added while working on the first MVP.</p> + +<p>They were comments like "Move this to a repository" or "Remove this hard-coded value and make this configurable".</p> + +<p>These weren't things that would affect the functionality of the code - but nice-to-haves, things that could be done differently, or reminders of things where hard-coded values were fine but would need to be replaced in the future.</p> + +<p>Instead of being code comments that only I was aware of, I moved them into the issue-tracking system I'm using. This allows the client to have visibility of them and that they can be scheduled and prioritised alongside other work, providing a truer reflection of the current tasks.</p> + +<p>Some have since been addressed, but some will remain and be actioned in the future. If they were still code comments, they might not have been addressed at all, so the issue tracker seems like the better place for them to be.</p> +
+
+ Sat, 03 Dec 2022 00:00:00 GMT +
+ + Commit and push something every day + + /daily/2022/12/02/commit-and-push-something-every-day + http://localhost:8000/daily/2022/12/02/commit-and-push-something-every-day + +
<p>One of the significant changes that I've found and that's benefitted me whilst developing is to commit and push something every day.</p> + +<p>It doesn't need to be a whole feature. It could be a new class with its passing tests, but it isn't used anywhere yet, so it won't affect the existing functionality, or it could be a new test for some existing functionality that was missing previously.</p> + +<p>It could be a small refactor - renaming a variable or class name that makes some code easier to read or removing some commented-out code that isn't doing anything other than adding visual clutter.</p> + +<p>It could be updating some documentation or <a href="http://localhost:8000/daily/2022/09/23/adrs-technical-design-documents">writing a technical document</a>; if you keep those in your version control repository, that would help you implement the following change or to make the documentation clearer for the next reader - whether that's you or someone else.</p> + +<p>Committing something at least once a day creates a different mindset to "I'll write everything and push it when it's done".</p> + +<p>It makes you break up large tasks into multiple smaller ones and set mini-deadlines for yourself. I used to do the same when I commuted to work on a train and had a task for a freelance project to complete before I arrived. I used to think, "What can I start, finish and commit before I get there?" instead of leaving something incomplete.</p> + +<p>You don't need to push your change to mainline. If you use the <a href="http://localhost:8000/daily/2022/11/30/ship-show-or-ask">"Ship, Show, Ask" approach</a> then you could commit to a temporary branch that you either merge yourself once you know it passes the checks, or to show or get feedback from other team members.</p> + +<p>Practicing this becomes a habit, and if you're doing test-driven development and committing after every passing test or refactor, you'll find yourself pushing numerous changes a day.</p> + +<p>This became my normal approach instead of having long-lived feature branches or lots of unpushed local commits.</p> + +<p>I much prefer making as small charges as possible and pushing them as often as I can.</p> +
+
+ Fri, 02 Dec 2022 00:00:00 GMT +
+ + Writing readable code + + /daily/2022/12/01/writing-readable-code + http://localhost:8000/daily/2022/12/01/writing-readable-code + +
<p>This week, I needed to investigate and fix a bug within some existing code.</p> + +<p>It's code written some time ago and not by anyone working on the team.</p> + +<p>The code wasn't very readable, so before I could fix the bug, I needed to figure out what the code was supposed to be doing.</p> + +<p>I started to write a list of things that would make the code easier to read and understand - no single-letter variable names, reduced levels of indentation and splitting some nested ternary operators to use separate return statements.</p> + +<p>I also watched a video of a conference talk titled "Writing code you won't hate tomorrow", which re-introduced me to Object Callisthenics.</p> + +<p>They are from "The ThoughtWorks Anthology" book and are some steps that include some of the points that I had written:</p> + +<ol> +<li>Use only one level of indentation per method.</li> +<li>Don’t use the else keyword.</li> +<li>Wrap all primitives and strings.</li> +<li>Use only one dot per line.</li> +<li>Don’t abbreviate.</li> +<li>Keep all entities small.</li> +<li>Don’t use any classes with more than two instance variables.</li> +<li>Use first-class collections.</li> +<li>Don’t use any getters/setters/properties</li> +</ol> + +<p>As well as the original book, there are numerous blog posts and videos on this topic.</p> + +<p>Why try some of them on the next code you write and see if it's easier to read and understand?</p> +
+
+ Thu, 01 Dec 2022 00:00:00 GMT +
+ + Ship, Show or Ask + + /daily/2022/11/30/ship-show-or-ask + http://localhost:8000/daily/2022/11/30/ship-show-or-ask + +
<p>"Ship / Show / Ask" describes itself as a self-described modern branching strategy that combines the features of pull or merge requests with the ability to keep shipping changes.</p> + +<p>Each change is either a "Ship", "Show", or "Ask".</p> + +<h2 id="ship">Ship</h2> + +<p>"Ship" changes are what I most commonly do with continuous integration and trunk-based development projects. Changes are committed directly to the main branch and shipped - assuming that the CI pipeline and any other automated checks pass. There is no blocking code review or pull request for "Ship" changes.</p> + +<h2 id="show">Show</h2> + +<p>"Show" changes are made on a temporary branch instead of the mainline. This branch is then used to create a pull request which is closed once the CI checks pass without any code review.</p> + +<p>Once the pull request is merged, the change is deployed, but the pull request is still available as somewhere to have feedback and conversation about the change.</p> + +<p>This is something that I did today after starting to write a new feature by implementing the Decorator design pattern, and wanted others to be aware of this approach and ask any questions.</p> + +<p>Another reason for this approach is if someone wants to ensure that the CI checks will pass before merging to mainline. Some Developers worry about breaking the CI pipeline with a trunk-based approach and blocking others, so want to know that the pipeline will continue to work and they aren't disrupting other team members.</p> + +<h2 id="ask">Ask</h2> + +<p>This is how I've worked on most projects before, and still with some clients where they want to review changes before merging, or maybe I want to have a discussion and review a change beforehand.</p> + +<p>This is also common when new Developers join a project or need to change some code they haven't worked on before.</p> + +<h2 id="conclusion">Conclusion</h2> + +<p>I like the flexibility and balance of this approach. I prefer to work on "mostly ship" projects, and most of the show and ask conversations happen during pair or mob programming sessions.</p> + +<p>Some of my clients where I work with teams are "mostly ask" and work in a more Git Flow-type way and want to review any changes before they're merged and deployed.</p> + +<p>But if you're working on a "mostly ask" project and want to move to "mostly ship", then aiming to move to "mostly show" is a good intermediate step and a way to reduce shipping time or develop and build confidence in the CI pipeline before moving to trunk-based development and removing the manual review step.</p> + +<p>The full article with more information is at https://martinfowler.com/articles/ship-show-ask.html.</p> +
+
+ Wed, 30 Nov 2022 00:00:00 GMT +
+ + Plan, then code + + /daily/2022/11/25/plan-then-code + http://localhost:8000/daily/2022/11/25/plan-then-code + +
<p>Recently I <a href="https://twitter.com/OneJKMolina/status/1303047499238776832">saw this tweet</a> in a screenshot on a LinkedIn post.</p> + +<p>The post was about improving business writing, but the original tweet was about software engineering.</p> + +<p>For me, the main sentence within the tweet is, "Resist the urge to do something before having a plan." - or as the LinkedIn post said, "Resist the urge to start typing before having a plan.".</p> + +<p>This is something that I've focused on a lot over the last few years, always asking, "What problem are we trying to solve?" and using flow charts and ADRs or technical design documents to come up with a plan before starting to write any code.</p> + +<p>Doing this makes me think of and answer as much as possible upfront - what we need, how things should work, the required steps, and what edge cases and pitfalls there might be. I'll usually have two or three solutions I'll consider and document, as well as which I decided to use and why.</p> + +<p>Once I've planned the solution, coding is usually very fast and straightforward, as most or all questions should already have been answered. I don't need to stop and answer questions whilst writing the code, and the code should be cleaner as I'm coding to the plan rather than figuring it out as I go.</p> +
+
+ Fri, 25 Nov 2022 00:00:00 GMT +
+ + Doing the simplest possible thing + + /daily/2022/11/24/doing-the-simplest-possible-thing + http://localhost:8000/daily/2022/11/24/doing-the-simplest-possible-thing + +
<p>I spent most of today working on some code I wrote for the first phase of a client project a few months ago.</p> + +<p>The previous version used hard-coded data within a Vue.js application, which was sufficient for the previous criteria and in line with the lean approach we decided to take.</p> + +<p>It was the simplest thing that worked.</p> + +<p>A new requirement means that the hard-coded data no longer works, so I need to refactor and enhance the code to work with different configurations. I have a proof of concept of this working using JSON from an API endpoint, but I would also like to use a static JSON file when needed for local development.</p> + +<p>I experimented with a few different ways to approach this before asking myself, "What's the simplest possible thing I can do to get this working?".</p> + +<p>I already knew that I needed to make a change to the structure of the data, which I was able to do quickly with the hard-coded data that I already had, and whilst a static JSON file would be a nice-to-have, I could quickly move the hard-coded data into the API endpoint that I'd already created and continue building on my proof of concept.</p> + +<p>The idea of "What's the simplest thing?" is something that I use regularly.</p> + +<p>When teaching or coaching test-driven development, I want to write the smallest failing test and then find the quickest and simplest way to get it to pass - even if it means returning a hard-coded value for now.</p> + +<p>When working on development tasks, I like to break things down as much as possible and find the smallest thing I can do, commit, and push. This gets the ball rolling, and then I repeat the process.</p> + +<p>Even when writing an email or blog post, once I start writing, it's much easier to continue once I'm in the flow.</p> + +<p>Taking the simplest approach and not making assumptions about future requirements or scope means less and more maintainable code, as well as being a productivity hack.</p> +
+
+ Thu, 24 Nov 2022 00:00:00 GMT +
+ + Do you need to branch if you're the only one working on a project? + + /daily/2022/11/23/do-you-need-to-branch + http://localhost:8000/daily/2022/11/23/do-you-need-to-branch + +
<p>Yesterday, I <a href="https://twitter.com/scottkeckwarren/status/1594752744165847040">saw a tweet</a> where the writer said they were “falling into the branch, pull request, and merge after actions pass I use at work even though I'm the only one working on it”.</p> + +<p>After reading this, my question is, "Should you, or do you need to, create branches if you're the only person working on a project?".</p> + +<p>These days, I use trunk-based development as much as possible, so I hardly ever create new branches, whether working on a project myself or with a team.</p> + +<p><a href="http://localhost:8000/presentations/git-flow">I used to use Git Flow</a> and create branches for every new feature and bug fix, but I remember, whilst demonstrating two work-in-progress features to a client, switching between the different branches caused my local site to break. Whilst it wasn’t a major issue, it wouldn't have seemed professional.</p> + +<p>In a team environment, feature branches are intended to keep different changes and different people's work separate.</p> + +<p>But is this needed if you’re the only in the team?</p> + +<p>Assumingly, you're only working on one change at a time, so what's the benefit of creating a separate branch?</p> + +<p>If you need to switch to a different task, another approach could be to revert your work-in-progress commits, move them onto another local branch temporarily, or wrap them within a feature flag so that the changes are committed but not active.</p> + +<p>The other part of the tweet said, “I like the little integrations to make sure the tests pass”.</p> + +<p>I’m comfortable working on a single branch and committing and pushing small changes often.</p> + +<p>My CI pipelines run for every change that I push, and if one fails, I’ll either push a small fix to get it passing again or revert the failing change and investigate further.</p> + +<p>For me, working on a single branch keeps my workflow simple and lean, allowing me to focus on the changes and the tasks that I need to work on and not worry about which branch I’m working on.</p> +
+
+ Wed, 23 Nov 2022 00:00:00 GMT +
+ + tldr + + /daily/2022/11/22/tldr + http://localhost:8000/daily/2022/11/22/tldr + +
<p><code>tldr</code> is a command-line tool that I've been using a lot recently.</p> + +<p>Usually, on the command line, you'd use the <code>man</code> command to show a manual page for a certain command - like <code>man ls</code>.</p> + +<p><code>tldr</code> is "a collection of simplified and community-driven man pages".</p> + +<p>After installing it, run <code>man tldr</code> or even <code>tldr tldr</code> to learn more about it.</p> + +<p>Then, run a command like <code>tldr ls</code> to get output for a specific command.</p> + +<p>I like that it shows a short description of what the command does, followed by a link to find out more information and then several valuable examples demonstrating the various options, flags, and arguments the command takes.</p> + +<p>For <code>ls</code>, it shows how to list one file per line, list hidden files, use a long format list, show human-readable size units, long format sorted by size or modification date, and only show directories.</p> + +<p>For commands like <code>tar</code>, <code>rsync</code>, and <code>scp</code> that I don't use that often or can't remember all of the different options, I like being able to see these examples and figure out what I need at that time.</p> +
+
+ Tue, 22 Nov 2022 00:00:00 GMT +
+ + Git tricks to avoid committing commented-out and other unneeded code + + /daily/2022/11/21/git-tricks-to-avoid-committing-commented-out-and-other-unneeded-code + http://localhost:8000/daily/2022/11/21/git-tricks-to-avoid-committing-commented-out-and-other-unneeded-code + +
<p><a href="http://localhost:8000/daily/2022/11/20/version-controlled-commented-out-code">Yesterday's email</a> talked about whether commented-out code should be present if your code is version-controlled, but how do you avoid committing it in the first place?</p> + +<p>You could make sure that you remove everything manually before you stage and commit your changes, or I like to use <code>git add --patch</code> (or <code>git add -p</code>) to interactively stage my changes, allowing me to select which parts of files I want to include in my commit and ignore anything else. The <code>--patch</code> option also works for other commands, including <code>checkout</code> and <code>reset</code>.</p> + +<p>If you've already committed something like some debug code, you can use <code>git commit --amend</code> to amend the previous commit before pushing it, or if you have a separate clean-up commit, you can use <code>git rebase --interactive</code> and either the squash or fixup options to amend the original commit.</p> + +<p>If some old code has been removed and you want to find it, you can use <code>git log -S</code> with a string to search for, and Git will show a list of commits where the specified string was changed.</p> + +<p>If instead, you wanted to search for text within the commit message, you can use <code>git log --grep</code> with a string like an issue number to see a list of commits with that text in the commit message subject line or body.</p> + +<p>I hope these tips help keep unwanted code out of your version-control repository.</p> +
+
+ Mon, 21 Nov 2022 00:00:00 GMT +
+ + Version-controlled commented-out code + + /daily/2022/11/20/version-controlled-commented-out-code + http://localhost:8000/daily/2022/11/20/version-controlled-commented-out-code + +
<p>Today, whilst debugging some legacy code within an application, I found several blocks of commented-out code.</p> + +<p>Some were previous debugging code which had been commented out, and some were old components or previous implementations - but instead of being removed when they were no longer needed, they remained in the codebase as commented-out lines - inactive but adding noise and complexity around the code that I was trying to understand and debug.</p> + +<p>To make it easier for me to figure out this code, I'd like it to be as clean to read and as simple to understand as possible.</p> + +<p>The codebase is version-controlled, so why would there be a need to comment out and keep the lines?</p> + +<p>Version control systems have a log of each change, so if you need to see previous changes, you can view the log and see what changed, when, and by who.</p> + +<p>You can also see any other files that were changed in the same commit, and usually, there will be a reference to the issue or ticket that required that change.</p> + +<p>If you need to re-add a change that had been removed, you can either do this manually or by reverting the commit.</p> + +<p>Should there be commented-out code within a codebase if it's version controlled? I'd say no unless there's a good reason for it to be there and it's providing some additional context or for a specific purpose. If it's an outdated implementation, some old debugging code, or a component that's no longer needed, I think that it should be removed, and people can use version control tools to find or re-introduce those changes if needed.</p> +
+
+ Sun, 20 Nov 2022 00:00:00 GMT +
+ + Are missing tests a blocker to refactoring? + + /daily/2022/11/19/are-missing-tests-a-blocker-to-refactoring + http://localhost:8000/daily/2022/11/19/are-missing-tests-a-blocker-to-refactoring + +
<p>Is having automated tests a prerequisite for refactoring a piece of code?</p> + +<p>Without passing tests for that code, any changes made could introduce a regression, and bugs can be accidentally introduced.</p> + +<p>When refactoring with tests, you run them before making any changes to ensure they pass. The tests are rerun after every change to ensure that they still pass and that the change didn't introduce a regression that caused the test to fail. If a test fails, the change is reverted and re-attempted.</p> + +<p>If I need to refactor some code without tests, the first thing that I'll do is add some initial tests before the main work.</p> + +<p>Whilst nothing is stopping me from refactoring without the tests, the risk isn't something that I'd want to take on, and I'd much prefer to have some tests in place - just in case!</p> +
+
+ Sat, 19 Nov 2022 00:00:00 GMT +
+ + One test a day keeps bugs away + + /daily/2022/11/18/one-test-a-day-keeps-bugs-away + http://localhost:8000/daily/2022/11/18/one-test-a-day-keeps-bugs-away + +
<p>This is a quote from a presentation by Diego Aguiar at SymfonyCon that I saw from <a href="https://twitter.com/SymfonyCasts/status/1593551105471938560?t=A8wnRUa0tLbb2q5qLhcQnA">a tweet from SymfonyCasts</a>.</p> + +<p>I haven't seen the rest of the presentation, but I liked this quote and the idea of continuously improving a codebase using automated tests.</p> + +<p>The talk was titled "Advanced Test Driven Development" so I assume that it was focused on ensuring that new functionality also has accompanying tests but it could also apply to existing code.</p> + +<p>A lot of existing code that I've worked on wasn't covered by tests, so going back and writing tests for that code would be beneficial too - even if it's only one test a day. It would help to prevent and uncover existing bugs, enable the code to be refactored and changed without introducing regressions, and make the codebase more maintainable.</p> + +<p>Small changes over time add up.</p> +
+
+ Fri, 18 Nov 2022 00:00:00 GMT +
+ + Agnostic CI pipelines with run files + + /daily/2022/11/17/agnostic-ci-pipelines-with-run-files + http://localhost:8000/daily/2022/11/17/agnostic-ci-pipelines-with-run-files + +
<p>As I work on various projects, I use several different CI tools, such as GitHub Actions, Bitbucket Pipelines, and GitLab CI, as well as hosting providers that have build and deploy steps.</p> + +<p>Some only run continuous integration checks, like automated tests and static analysis, some build and push Docker images, and some use Ansible and Ansistrano to deploy the changes to production.</p> + +<p>Each tool has its configuration file with different settings and formats.</p> + +<p>Rather than being too tightly coupled to a particular tool, I like to keep things as agnostic as possible and <a href="http://localhost:8000/daily/2022/08/15/using-run-file-simplify-project-tasks">use a run file</a> with separate <code>ci:build</code> and <code>ci:deploy</code> tasks.</p> + +<p>This means that all the logic is within the run file rather than the CI tool-specific configuration file, so the file is shorter and cleaner; I can make changes to the CI tasks locally and quickly test changes and iterate, and also, as the logic is within the run file, I can easily switch to a different CI tool if needed without making changes to the tasks themselves.</p> +
+
+ Thu, 17 Nov 2022 00:00:00 GMT +
+ + Why don't you write automated tests? + + /daily/2022/11/16/why-don't-you-write-automated-tests + http://localhost:8000/daily/2022/11/16/why-don't-you-write-automated-tests + +
<p>Many projects I’ve worked on in the past haven’t had an automated test suite.</p> + +<p>If you don't or can't write tests for your project for some reason, I'd love it if you could reply to this email or <a href="https://twitter.com/opdavies">let me know on Twitter</a> and let me know why.</p> + +<p>I know some of the classic reasons, like "I don't have time" and "My clients won't pay for me to write them", but I'd like to get some more real-world examples.</p> + +<p>Then I'll do some follow-up posts to look into and address them.</p> +
+
+ Wed, 16 Nov 2022 00:00:00 GMT +
+ + Writing good automated test names + + /daily/2022/11/15/writing-good-automated-test-names + http://localhost:8000/daily/2022/11/15/writing-good-automated-test-names + +
<p>Something that I often see in code examples or tutorials are test methods like <code>testGet</code> or <code>testAdd</code>, or <code>testSubtract</code>. Short method names that don't describe the scenario that they're testing in much detail.</p> + +<p>What if a <code>get</code> method returns different types of value based on the input or a string is passed into a calculator method like <code>add</code> or <code>subtract</code>?</p> + +<p>I'd assume that the result of the calculation returns the total, but the test method doesn't say this.</p> + +<p>I'd rather be overly descriptive and write methods like <code>should_add_two_or_more_numbers_and_return_the_total()</code> rather than <code>testAdd</code>. It's a lot more readable and easier to see what the intention of the test is, and it's much better to use longer descriptive names when using options like <code>--testdox</code> with PHPUnit, which converts the method name into a sentence, which is what I do when running tests in CI pipelines.</p> + +<p>Something that I've picked up and recommend is to start each test case with the word "It" or "Should". This gives it a more behavioural feel and puts you in the mindset of what you're testing and not the methods that you're executing.</p> + +<p>A method like 'testAdd' might make sense within a unit test focusing on a single class and method, but as I usually do outside-in testing - which mostly uses functional and integration tests - this approach works well for me.</p> +
+
+ Tue, 15 Nov 2022 00:00:00 GMT +
+ + Camel-case or snake-case for test methods? + + /daily/2022/11/14/camel-case-or-snake-case-for-test-methods + http://localhost:8000/daily/2022/11/14/camel-case-or-snake-case-for-test-methods + +
<p>When writing object-orientated code, particularly in PHP, you usually write method names using camel-case letters - such as:</p> + +<pre><code class="language-php">public function doSomething(): void { + // ... +} +</code></pre> + +<p>This is also true when writing methods within a test class - only that the method name is prefixed with the word <code>test</code>:</p> + +<pre><code class="language-php">public function testSomething(): void { +} +</code></pre> + +<p>This is probably expected and complies with the PSR code style standards like PSR-12.</p> + +<p>Something that I've seen some PHP developers and some frameworks prefer is to write their test methods using snake-case letters and commonly removing the <code>test</code> prefix in favour of using an annotation:</p> + +<pre><code class="language-php">/** @test */ +public function the_api_should_return_a_200_response_code_if_everything_is_ok(): void { + // ... +} +</code></pre> + +<p>This is something that I've done myself for a while, but now I'm starting to reconsider both options.</p> + +<p>Whilst it's more readable, especially for longer test names (which I like to write), it's not consistent with method names in non-test files or non-test methods in test files; it looks odd if I need to add another annotation (do I keep a single annotation on one line, or just those with multiple annotations on the separate lines), and to do this, I need to disable some code sniffer rules for code to pass the PHPCS checks.</p> + +<p>If I used camel-cased names, I wouldn't need the PHPCS overrides, the annotations would be simpler, and the code would be more consistent - so I think I'll try that way again in the next tests that I write and see how it feels.</p> + +<p>Which do you prefer, and which would you expect to see in your project?</p> +
+
+ Mon, 14 Nov 2022 00:00:00 GMT +
+ + How I manage multiple Drupal websites using the same codebase + + /daily/2022/11/13/how-i-manage-multiple-drupal-websites-using-the-same-codebase + http://localhost:8000/daily/2022/11/13/how-i-manage-multiple-drupal-websites-using-the-same-codebase + +
<p>In my last email, I mentioned that I maintain several Drupal websites for a client using the same codebase, but how do I do that?</p> + +<p>The sites use Drupal's multisite functionality to have a separate directory for each site, each with its own settings file and files, and potentially modules and themes. Whilst there are some downsides to this approach, and we did evaluate alternatives, this approach allows us to keep one hosting account and save the client money compared to hosting each site separately.</p> + +<p>Each site has a separate database and configuration files, so out of the box, I can customise what functionality is needed on each site by turning modules on and off. Whilst this is fine for larger pieces of functionality, for smaller pieces I like to use feature flags.</p> + +<p>I use feature flags on single-site projects to separate deploying code from releasing a change, but I can also use them here to toggle something per-site. This could be using a module like <a href="https://www.drupal.org/project/feature_toggle">Feature toggle</a> or another way like a checkbox on a settings form. Anything that I can use to say "Do this if that is enabled".</p> + +<p>Settings such as setting an endpoint URL or storing some API credentials would be set in an admin form and stored as configuration per site.</p> + +<p>I've tried various iterations of this - initially duplicating the custom code and having several near-identical versions of the same modules (this wasn't good for maintenance). We also used environment variables. However, this didn't scale as I added more sites and needed to create a new set of environment variables every time.</p> + +<p>This approach has worked well for the last few years on their original websites and should continue to work well as I upgrade and migrate them to their next versions.</p> +
+
+ Sun, 13 Nov 2022 00:00:00 GMT +
+ + Building a minimum viable product and managing technical debt + + /daily/2022/11/12/building-a-minimum-viable-product-and-managing-technical-debt + http://localhost:8000/daily/2022/11/12/building-a-minimum-viable-product-and-managing-technical-debt + +
<p>Yesterday's email was about a proof-of-concept application that I’d quickly built to validate an idea and explore some initial approaches.</p> + +<p>Today, I’ve been working on a client project that I’ve improved and maintained for a few years.</p> + +<p>When I started working with this client, they had one website, built with Drupal 7 and Drupal Commerce. Now, there are x websites using the same codebase due to Drupal’s multi-site functionality.</p> + +<p>My main task for the last few months has been to get one of their sites onto Drupal 9 (which I did, it went live in October).</p> + +<p>This first site was the "minimum viable product" (MVP) - the least amount of functionality required to make it releasable to customers. This is different to a proof of concept which is to validate the idea and start a conversation about requirements and scope - where we define the MVP.</p> + +<p>For example, there is the ability to create products and product variations from a CSV file. It loads the file from disk and creates the products, but it doesn't update a product variation if a row with an existing SKU is changed, or disable the variation if a row is removed from the file. There is no admin UI for the client to upload a file - the only file that it'll use is the one that's path is hard-coded within the module.</p> + +<p>There are user stories for this, but we decided that we didn't need it for the initial launch and that we were happy to take on some technical debt, knowing that we can address it later when the original solutions are no longer sufficient.</p> + +<p>Now the minimum viable solution has been released, we can continue to iterate and enhance it based on customers' feedback, add more functionality, and address the technical debt as needed and as requirements require us to do so.</p> +
+
+ Sat, 12 Nov 2022 00:00:00 GMT +
+ + Creating a small proof-of-concept application in an afternoon + + /daily/2022/11/12/creating-small-proof-of-concept-application-afternoon + http://localhost:8000/daily/2022/11/12/creating-small-proof-of-concept-application-afternoon + +
<p>This morning, I was asked a “Could you build…” question.</p> + +<p>It was an idea mentioned a short while ago and involves a simple, interactive form on the front end that sends requests to a public API, filters the results from the response and displays them to the user.</p> + +<p>I’d probably want to hide the API request behind a service responsible for interacting with the API and filtering the results - ensuring that the API could be switched with something else later if needed.</p> + +<p>This afternoon, I built a small proof-of-concept application with Vue.js and TypeScript.</p> + +<p>There’s no API, or service retrieving real-time results. All of the data is hard-coded within the App component, as well as the code that filters, sorts and returns the results.</p> + +<p>The results are shown by adding a <code>&lt;pre&gt;&lt;/pre&gt;</code> to the page, with a <code>&lt;pre&gt;&lt;/pre&gt;</code> to show the input data.</p> + +<p>There isn’t even any styling, with just some basic horizontal rules to split the page - similar to <a href="https://twitter.com/taylorotwell/status/1203356860818087944">these screenshots from Taylor Otwell</a> of some work-in-progress versions of Vapor and Nova.</p> + +<p>A working proof of concept, or a "spike", answers the initial "Can we build..." question. It can be shown to a client or other stakeholders, act as a starting point for discussions and requirements gathering and then be turned into user stories. It also allows the Developers to validate their initial thoughts and experiment with different approaches.</p> + +<p>If the spike is successful, the idea can then be moved forward and implemented in a full way, otherwise, it can be stopped with a minimal amount of effort and time.</p> +
+
+ Fri, 11 Nov 2022 00:00:00 GMT +
+ + Your conference talk has been accepted + + /daily/2022/11/09/your-conference-talk-has-been-accepted + http://localhost:8000/daily/2022/11/09/your-conference-talk-has-been-accepted + +
<p>I’m happy to have had a conference talk proposal accepted for what will be my first in-person conference since DrupalCamp London in February 2020.</p> + +<p>I’ll be giving my "<a href="http://localhost:8000/presentations/taking-flight-with-tailwind-css">Taking Flight with Tailwind CSS</a>" talk for the first time since February 2021, and in front of an in-person audience since June 2019.</p> + +<p>The talk itself will need some updating. The last time I gave it, Tailwind CSS was on version 2.0.3. It’s now on version 3.2.2 and includes features like the just-in-time engine, arbitrary values and variants, container queries, and a load of new utility classes.</p> + +<p>I gave a lot of talks at online events in 2020, so after taking a bit of a break last year, it will be nice to speak in front of an in-person conference audience again.</p> +
+
+ Wed, 09 Nov 2022 00:00:00 GMT +
+ + Are sprints incompatible with Continuous Deployment? + + /daily/2022/11/08/are-sprints-incompatible-with-continuous-deployment + http://localhost:8000/daily/2022/11/08/are-sprints-incompatible-with-continuous-deployment + +
<p>It's been common for me whilst working on software projects to have work organised into sprints or cycles - a period, usually between 1 and 3 weeks, where the team is working on stories and tasks for that project.</p> + +<p>In my experience, those changes are usually released at the end of that cycle. But it seems that's not always the case; see <a href="https://scrumdictionary.com/term/release-sprint">release sprints</a>:</p> + +<blockquote> + <p>A specialised sprint whose purpose is to release deliverable results; it contains stories specific to release activities and finishing undone work. A release sprint usually contains no additional development.</p> +</blockquote> + +<p>If we worked in two-week cycles and released at the end of each one, it would be at least two weeks before a change could be deployed to production. But what if we wanted to follow continuous deployment and release more frequently? Maybe daily or hourly?</p> + +<p>Instead of waiting for a release sprint, if we released multiple times within a single sprint, how would this fit into or affect the process?</p> + +<p>Does the release cycle need to be tightly coupled to the sprint cycle or can they be separate and independent of each other?</p> + +<p>I've worked on projects - including a current one - where I've done multiple releases in a sprint, so of course, it can be done from a technical perspective, but how do we get the best from both processes - whether they work together or separately?</p> + +<p>This is something that I'm going to continue to experiment with, iterate on, and learn more about going forward.</p> +
+
+ Tue, 08 Nov 2022 00:00:00 GMT +
+ + Refactoring one large test into multiple smaller tests + + /daily/2022/10/30/refactoring-one-large-test-into-multiple-smaller-tests + http://localhost:8000/daily/2022/10/30/refactoring-one-large-test-into-multiple-smaller-tests + +
<p>Today I spent some time refactoring a large test within a client project, splitting it into several smaller tests. The commit removed 169 lines but added 233 lines.</p> + +<p>So, why did I do this?</p> + +<p>This test is responsible for testing the creation of products and product variants within Drupal Commerce from a custom CSV file and originally had a very generic name - "Should create a product and product variations from an array of data".</p> + +<p>But it did much more than that:</p> + +<ol> +<li>It asserted that there are no initial existing products or product variations.</li> +<li>It ran a product import using some stub data.</li> +<li>It asserted that there are two products, each with two variations.</li> +<li>It asserted that each product has the correct title.</li> +<li>It asserted that each product variation has the correct title.</li> +<li>It asserted that each variation has the correct SKU.</li> +<li>It asserted that each variation has the correct price.</li> +<li>It asserted that each variation has the correct value for 10 product attributes.</li> +</ol> + +<p>All of this was hidden within a single test.</p> + +<p>Whilst it was great as the original test name (I usually start with a vague name whilst I'm spiking the first test and until it's clearer to me what it's testing and what the correct name is), what I actually want this test to do is to check that the correct number of products and variations are created.</p> + +<p>This refactoring task was to split the remaining assertions into their own named tests, after which I had six different tests.</p> + +<p>This means that each piece of functionality and related assertions are now contained within their own named tests. I can read the test file and see the expected functionality within the test names rather than everything being grouped and hidden within a single vaguely-named test.</p> + +<p>If an assertion fails, I can easily see in which test the failure occurred.</p> + +<p>Each test is very simple and only a few lines long - it runs the product import, loads the created variation, and runs the appropriate assertions.</p> + +<p>It'll be much easier to add new functionality to the importer by adding a new separate test rather than continuously adding to the large original one.</p> + +<p>Even though there are more lines in the file after the refactoring, most of those are just because of adding the additional test functions. There are only 72 lines of actual test methods, and the reusable steps, such as running the product import as well as custom assertions, are defined as private methods to avoid duplication.</p> + +<p>In my opinion, this was a good refactor to do, and now was a good time to do it before we get started on the next phase of the project.</p> +
+
+ Sun, 30 Oct 2022 00:00:00 GMT +
+ + The open-source-first development workflow + + /daily/2022/10/29/the-open-source-first-development-workflow + http://localhost:8000/daily/2022/10/29/the-open-source-first-development-workflow + +
<p>Yesterday's email talked about <a href="http://localhost:8000/daily/2022/10/28/why-write-framework-agnostic-packages">writing reusable, framework-agnostic packages</a> but didn't mention where those packages could be located.</p> + +<p>They could be kept within a private repository and still have the same benefits, such as re-usability for internal projects, but I like to open-source code as often as I can and make it available publicly to see and use.</p> + +<p>My preference is to follow an open-source-first workflow, identify which parts of a solution can be open-sourced and create them as open-source libraries or modules from the beginning rather than planning to extract them later. They can then be included within the main project using a dependency manager tool like Composer, npm or Yarn.</p> + +<p>The eBook integration project that I mentioned was an example of this. I identified which pieces could be open-sourced, set up a public repository and put together an MVP based on that project's requirements. Issues were created for nice-to-have additions that could be added later, and the working version was installed with Composer.</p> + +<p>There was no need to extract the code from the main project, no need to "clean it up" or check that it contained no client information, and I had the full Git history for the project - not just a new history from the point when the code was extracted and open-sourced.</p> + +<p>I've worked on projects that contained a number of potential open-source components that would be released after project completion, but this didn't always happen - I assume due to time pressures to move on to the next project, a focus on adding new features or avoiding the risk of introducing breakages into the code. If the code had been open-sourced from the beginning, these things wouldn't have been an issue.</p> + +<p>I've also worked on projects where I've followed an open-source-first approach and released a number of PHP libraries and Drupal modules, including <a href="https://www.drupal.org/project/private_message_queue">Private Message Queue</a>, <a href="https://www.drupal.org/project/system_user">System User</a>, and <a href="https://www.drupal.org/project/null_user">Null User</a> modules. I've also been working on some legacy code recently and started to replace it with a library that I've already open-sourced, even though I'm in the early stages of developing it.</p> + +<p>As someone who enjoys creating and working on open-source software, I would encourage you to open-source your code if you can and to do so sooner rather than later and not wait until the end of your project.</p> +
+
+ Sat, 29 Oct 2022 00:00:00 GMT +
+ + Why write framework agnostic packages? + + /daily/2022/10/28/why-write-framework-agnostic-packages + http://localhost:8000/daily/2022/10/28/why-write-framework-agnostic-packages + +
<p>A couple of years ago, I wrote an integration for a client's Drupal Commerce website with an online eBook service as they wanted to sell eBook variations of their products.</p> + +<p>They provided me with some example code for different PHP frameworks, each were separate and tightly-coupled to each framework, so there was no code shared between them. Because of this, and because there was no Drupal Commerce example, I wrote my own version.</p> + +<p>However, I decided to make my version as reusable and loosely-coupled as possible. This meant that I'd be able to potentially reuse it for other clients and the same code could be used for different implementations.</p> + +<p>Reusable code such as the configuration, different types of Requests, value objects for Customers, Orders and OrderItems, were all written within a separate, reusable PHP library. It contains it's own tests, has it's own CI pipeline, and it's own static analysis - ensuring that things work as expected.</p> + +<p>With this code separated, the Drupal module was much smaller and responsible only for bridging the library's code with Drupal Commerce and adding any other Drupal-specific code.</p> + +<p>The client is currently considering an upgrade from Drupal 7 to Drupal 9, which would also mean upgrading this module. But, with this approach, there's a lot less to upgrade. The library code can still be used, and I can focus on any Drupal-specific changes within the Drupal module.</p> + +<p>I recently had an enquiry from someone who needs an integration with the same service. Whilst their requirements may be different, I could still re-use the reusable library code, and write any client-specific code within a custom module.</p> + +<p>Finally, if I wanted to reuse this code within a different PHP eCommerce framework then I could by installing the library with Composer. This means that I'd get the same code without needing to manually copy it, keeping a single source that can be maintained separately upstream. I'd get the same code that I'm already familiar with, so I could focus only on how to integrate the library with that framework - again meaning less framework-specific code and a much lower development effort.</p> +
+
+ Fri, 28 Oct 2022 00:00:00 GMT +
+ + Getting back into live streaming + + /daily/2022/10/27/getting-back-into-live-streaming + http://localhost:8000/daily/2022/10/27/getting-back-into-live-streaming + +
<p>Surprisingly, it's been two and a half years since I last did a live coding stream.</p> + +<p>As well as talk recordings and demos, I did a few live streams back then, working on the "Test-Driven Drupal" project and submitting a merge request to Drupal core.</p> + +<p>It's been something that I'd like to get back into and pick up again, and I plan on doing that within the next few weeks.</p> + +<p>I have a new freelance project due to start in December but getting back into streaming seems like a good way to make sure that I put aside time for open-source and side projects, as well as for writing longer form blog posts and hopefully starting to prepare more meetup and conference talks again.</p> + +<p>I'll be streaming again on my YouTube channel, so if you'd like to be notified when I do, <a href="https://www.youtube.com/channel/UCkeK0qF9HHUPQH_fvn4ghqQ?sub_confirmation=1">please subscribe</a>.</p> +
+
+ Thu, 27 Oct 2022 00:00:00 GMT +
+ + Neovim as a Personalised Development Environment + + /daily/2022/10/26/neovim-as-a-personalised-development-environment + http://localhost:8000/daily/2022/10/26/neovim-as-a-personalised-development-environment + +
<p>A few months ago, TJ DeVries (a Neovim core team member) coined the phrase "Personalised Development Environment" or PDE.</p> + +<p><a href="http://localhost:8000/blog/going-full-vim">I've been using Neovim full-time</a> since July 2021 - starting with no configuration to configuring it with Vimscript and later with Lua - setting options like line numbers and relative numbers, tabs and spaces, and indent and fold levels.</p> + +<p>I evaluated and installed some initial plugins to add functionality that I needed. Some of them I still use, and some I've replaced with alternative plugins or built-in solutions that have been included in newer versions of Neovim.</p> + +<p>I added my own keymaps that made sense to me that either, in my opinion, improved on default keymaps or created new ones that made sense to me or configured a plugin that I'd added.</p> + +<p>Recently, I found and added plugins that added a <a href="https://github.com/rest-nvim/rest.nvim">HTTP client</a> and a <a href="https://github.com/kristijanhusak/vim-dadbod-ui">database connection manager</a> to Neovim - two pieces of functionality that I'd used in other IDEs or separate applications.</p> + +<p>I also <a href="https://github.com/opdavies/toggle-checkbox.nvim">wrote my own Neovim plugin</a> for toggling checkboxes within Markdown lists.</p> + +<p>Like Drupal and other open-source solutions that I use, I love being able to add or edit functionality as needed.</p> + +<p>In the last year or so, I've definitely been able to personalise my Neovim setup to meet my needs, and have it work as a fully-fledged solution for PHP and JavaScript development, DevOps work, and technical writing (including this email).</p> +
+
+ Wed, 26 Oct 2022 00:00:00 GMT +
+ + What are Drupal distributions? + + /daily/2022/10/25/what-are-drupal-distributions + http://localhost:8000/daily/2022/10/25/what-are-drupal-distributions + +
<p>Yesterday's email was about the LocalGov Drupal distribution that I've been looking at, but I glossed over what a Drupal distribution is.</p> + +<p>It's an interesting topic for me, having <a href="https://www.linuxjournal.com/content/speed-your-drupal-development-using-installations-and-distributions">written an article for Linux Journal</a> about it in 2012.</p> + +<p>Distributions are pre-configured versions of Drupal that include additional modules, themes, or configuration than you'd get if you installed a standard version of Drupal core.</p> + +<p>By default, LocalGov includes content types for service pages, service landing pages and sub-pages, and additional menus and taxonomies, a different administration theme and a base theme to use for custom themes, and multiple additional modules that add alert banners, events, content reviews, search, media types, and sub-sites. This is all in addition to what Drupal core itself provides, and can be extended further with additional contrib or custom modules.</p> + +<p>Commerce Kickstart was a distribution for Drupal 7 that added eCommerce functionality such as product and order types, shipping and payment methods, stock levels and discounts. Again, this could be extended further by adding more contrib or custom modules.</p> + +<p>A few months ago, I started developing a distribution for managing meetup group websites, like PHP South Wales.</p> + +<p>If you're starting a new Drupal website, there could be a distribution that exists that could provide some or all of the functionality that you need, and if new features or fixes are added, then they benefit everyone who uses it.</p> + +<p>There are 1,430 distributions listed on https://www.drupal.org/project/project_distribution so take a look there and see if anything matches your needs.</p> +
+
+ Tue, 25 Oct 2022 00:00:00 GMT +
+ + Looking at LocalGov Drupal + + /daily/2022/10/24/looking-at-localgov-drupal + http://localhost:8000/daily/2022/10/24/looking-at-localgov-drupal + +
<p>Today, I've been looking at <a href="https://localgovdrupal.org">LocalGov</a> - a Drupal distribution for building council websites, with a focus on code reuse and collaboration.</p> + +<p>After a few small changes, I was able to get it running based on my <a href="https://github.com/opdavies/docker-examples">Docker Examples</a> repository.</p> + +<p>As someone who has worked with one of the Councils who are now using the platform, and was involved in early similar discussions around code reuse and collaboration between Councils, this has been something that I've been keen to try for a while.</p> + +<p>I was able to get a basic site running after a fresh installation, and was interested to explore how some of the functionality was built. I've recently been looking at implementing similar functionality to LocalGov's alert banners onto a project and will be able to gain some inspiration from that or will look into the LocalGov version could be used.</p> + +<p>I was happy to find some initial ways to contribute back. I had an error during the installation which I was able to fix and assist with in the <a href="https://www.drupal.org/project/localgov/issues/3307516#comment-14759989">LocalGov issue queue on Drupal.org</a> by answering a support request, and after spotting a potential issue within the alert banner styling, <a href="https://github.com/localgovdrupal/localgov_alert_banner/pull/225">submitted a pull request with a fix</a>.</p> + +<p>I like what the project is doing and agree with its goals, so hopefully I'll get an opportunity to use and contribute more in the future.</p> +
+
+ Mon, 24 Oct 2022 00:00:00 GMT +
+ + Automated testing and test-driven development are not the same + + /daily/2022/10/21/automated-testing-and-test-driven-development-are-not-the-same + http://localhost:8000/daily/2022/10/21/automated-testing-and-test-driven-development-are-not-the-same + +
<p>Automated testing is where you write tests to ensure that your code works as expected, which can be re-run as needed and executed automatically without user input.</p> + +<p>Test-driven development (TDD) is when you write the tests before the production code. You see the tests fail and write code until they pass, and then repeat the process. However, TDD is not just about the tests - it's about the design of the code.</p> + +<p>By writing the tests first, you guarantee that the code that you write will be testable, which isn't something that you can't do if the production code is written first.</p> + +<p>You may need to refactor your initial working implementation before it can be tested - which means that you could also break it during that time and need to spend time debugging and fixing any regressions. Ideally, you want the tests in place first before any refactoring, so if something breaks, you'll know because of the failing test.</p> + +<p>TDD keeps your code cleaner and simpler, as you only write enough code to make the tests pass. Once a test is passing, you stop writing code, so you'll end up with less, simpler code as it's easy to know when to stop.</p> + +<p>If you don't write the tests first, you may be tempted to skip writing them completely, leaving untested code or adding <code>TODO: add test</code> comments that may never get reviewed.</p> + +<p>Also, where's the fun in writing tests for code that you've already written, that you know are going to pass?</p> +
+
+ Fri, 21 Oct 2022 00:00:00 GMT +
+ + Cherry picking commits is an anti-pattern + + /daily/2022/10/20/cherry-picking-commits-is-an-anti-pattern + http://localhost:8000/daily/2022/10/20/cherry-picking-commits-is-an-anti-pattern + +
<p><code>git cherry-pick</code> is a command that allows you to re-apply changes from existing commits - typically moving commits from one branch to another. Whilst it's good for some use-cases, I believe that it's generally an anti-pattern.</p> + +<p>As I mostly do trunk-based development so only have a single branch, it's not a command that I'd run often - but I have seen it used in a Git Flow-type scenario where there are multiple long-lived branches and various other short-lived ones. Commits can be cherry-picked between a long-term branch like <code>develop</code> onto a feature branch rather than merging or rebasing, or to re-apply a hotfix from the <code>main</code> branch.</p> + +<p>The main issue that I've seen with <code>cherry-pick</code> is where a number of changes have been merged into a branch which is being used for user acceptance testing by a client or product owner. They decide to approve some of the changes but not all, and the approved commits are cherry-picked onto a production branch and deployed.</p> + +<p>In my opinion, this is very risky as there's no guarantee that the cherry-picked changes will work without the others, and as the artifact that's pushed to production is different to what was tested against, it arguably affects the value of doing the testing at all. Ideally, once the release has been tested and approved, the same artifact will be deployed - ensuring consistency and reducing the risk of any errors.</p> + +<p>Potentially, the cherry-picked changes could be moved onto a release branch and tested again together without the other changes, but this would increase the testing overhead and the time for the changes to release production.</p> + +<p>A good automated test suite would help, ensuring that the tests still pass once the cherry picking is done.</p> + +<p>In this situation, I'd rather use feature flags (also known as "feature toggles"). This would mean that the code between the two environments would be the same, and allow for functionality to be enabled or disabled as needed. If a feature wasn't selected to be released, then it's feature flag would be disabled until it's approved.</p> + +<p>A feature flag would also allow a feature to be switched off if it was causing errors without the need for a code deployment. If a change did need a code change, if you're following continuous integration and delivery, it would be easy to apply and push a fix.</p> + +<p>These are the use-cases that I can think of or have seen for <code>git cherry-pick</code>. If you know of any others or use <code>cherry-pick</code> in your workflow in another way, reply to the email and let me know.</p> +
+
+ Thu, 20 Oct 2022 00:00:00 GMT +
+ + run file vs task runners + /daily/2022/10/19/run-vs-task-runners + http://localhost:8000/daily/2022/10/19/run-vs-task-runners + +
<p><a href="http://localhost:8000/daily/2022/08/15/using-run-file-simplify-project-tasks">I've written a few earlier emails</a> about <code>run</code> files - a simple bash file that I add to my projects to simplify or combine common commands that I need to run often.</p> + +<p>Recently, I've looked at a couple of alternatives to see how they compare.</p> + +<p>One is very YAML based where all commands are written within a YAML file, and one is very Makefile-like and it does fix some of the confusion and issues that I've made with Makefiles in the past - such as passing arguments to commands, and dealing with <code>.PHONY</code>.</p> + +<p>Whilst I like both of these approaches and that they offer small additional features like auto-completion of task names, after using one of them in a project for a short while, I think that I'm going to stick with the <code>run</code> file.</p> + +<p>The main reason for this is that I like the simplicity of the <code>run</code> file, and that it's just a Bash file that contains functions.</p> + +<p>There were a couple of things that I couldn't quite get to work in one of the other tools, such as setting the TTY value in a Docker Command - which is something that I was able to do with bash within the <code>run</code> file. The fact that I can write regular bash and reuse existing knowledge is a big plus rather than having to try to learn another syntax or DSL for another tool.</p> + +<p>The main reason though is because bash is already installed everywhere. There's no additional tool for Developers to download and install so it keeps the barrier to entry low, and there's no additional dependencies to add to my CI pipeline for it to work.</p> + +<p>I was able to use one of these other tools in GitHub Actions as someone had already written a workflow for it, and although I could possibly install it via a package manager, just being able to run a bash file in any CI tool was probably the deciding factor to stick with <code>run</code> files.</p> +
+
+ Wed, 19 Oct 2022 00:00:00 GMT +
+ + Pair and mob programming + /daily/2022/10/18/pair-mob-programming + http://localhost:8000/daily/2022/10/18/pair-mob-programming + +
<p>As well as my recent session at PHP South Wales, I've also been involved with a lot more mob programming recently with members of my team.</p> + +<p>We recently added a new feature to our codebase that we completed over a couple of mob sessions - starting by describing the problem and some potential solutions within a <a href="http://localhost:8000/daily/2022/09/23/adrs-technical-design-documents">technical design document</a> before moving on to the implementation.</p> + +<p>I was already familiar with the existing code that we needed to extend, so had some ideas of how to approach parts of the solution which we discussed - but there were other parts that I hadn't thought of.</p> + +<p>What was very interesting was that an approach was suggested that I probably wouldn't have thought of myself, which become part of the final solution. This is an advantage of pair programming and is multiplied when working in groups - that you get to include everyone's thoughts, experience and perspective, and collectively decide on the best approach to take in real-time.</p> + +<p>As a side effect, we had continuous code review from members of the group, and if we need to work on this code again in the future, everyone will already be familiar with it.</p> + +<p>As it was already reviewed, we didn't need to wait before pushing the feature to production so it was delivered quickly and providing value by fixing an issue that someone was experiencing.</p> + +<p>We're already working on the next feature as a group, and if you haven't tried pair or mob programming before, I'd recommend that you give it a try.</p> +
+
+ Tue, 18 Oct 2022 00:00:00 GMT +
+ + 14 years on Drupal.org and working with PHP and Drupal + /daily/2022/10/17/14-years-drupalorg + http://localhost:8000/daily/2022/10/17/14-years-drupalorg + +
<p>Today I saw that my Drupal.org profile is showing that I’ve been on that website for 14 years.</p> + +<p><img src="http://localhost:8000/assets/images/14-drupalorg.jpg" alt="A screenshot of my Drupal.org profile showing &quot;On Drupal.org for 14 years 1 hour&quot;." /></p> + +<p>Drupal.org is the online home of the open-source Drupal CMS project, and where I registered to ask questions on the forums as I started to learn Drupal. More recently, it’s been where I’ve uploaded and maintain my own contributed projects and contribute patches to others, including Drupal core.</p> + +<p>I even spent time working for the Drupal Association on Drupal.org itself.</p> + +<p>I've talked about T-shaped Developers in a previous email, and whilst I've added complementary skills to my toolkit over the years, Drupal has been my main specialism and what I focused on when I started freelancing and later switched careers into software development.</p> + +<p>With Drupal 10 just around the corner, I'm looking forward to seeing how Drupal continues to evolve and develop.</p> +
+
+ Mon, 17 Oct 2022 00:00:00 GMT +
+ + Overcoming deployment anxiety + /daily/2022/10/12/overcoming-deployment-anxiety + http://localhost:8000/daily/2022/10/12/overcoming-deployment-anxiety + +
<p>As a Developer with 15 years of experience, I still sometimes get "deployment anxiety" - when I've backed up the database and tagged a release, but even though the CI pipelines are passing and the staging site is working, I'm holding off on pushing the latest code to be released to production - trying to think of any potential issues that could arise from this deployment and avoid any downtime.</p> + +<p>When I thought about this further, the releases that I've felt anxious or nervous about have usually been in at one or both of the following categories:</p> + +<ul> +<li>The release includes a lot of changes, and maybe a combination of different types of changes such as framework or CMS updates, bug fixes, or new features.</li> +<li>It's been a long time, maybe weeks or months, since the last production release.</li> +</ul> + +<p>The best way to resolve both of these issues, I think, is to break down the large releases into smaller ones, and to deploy them more frequently.</p> + +<p>In the opposite scenario, the releases where the changes are small and it's been a short time since the previous release - ideally minutes or hours - have been the ones where I've been the least nervous.</p> + +<p>If a single commit is being released, then I can be confident that if there is a failure, I can either revert it and put things back the way they were or quickly identify the issue and push a fix. This isn't the case for large changes as the potential source of the failure is larger and it will take longer to find and fix.</p> + +<p>If a bug fix or a feature needs to be reverted, I'm happy knowing that I can do that easily without also reverting the CMS update that was deployed separately - rather than them all being released together.</p> + +<p>There are other advantages too - clients or product owners are generally happier if the new feature or fix that they requested is on production within hours or days rather than weeks or months, and having your latest code deployed to production rather than on a staging branch makes it a lot easier if you need to deploy an urgent fix or security update.</p> + +<p>If you're familiar with the DevOps Research and Assessment (DORA) team, three of their key metrics are deployment frequency, lead time for changes, and time to restore service. All of these are improved by small and frequent releases.</p> + +<p>In my <a href="http://localhost:8000/presentations/deploying-php-ansible-ansistrano">Deployments with Ansible and Ansistrano talk</a>, I mention that there is a separate rollback role, but I don't think that I've ever used it.</p> + +<p>Because I'm deploying small changes often, it's usually much easier to fix forward than it is to rollback, and knowing this makes me a lot less anxious when deploying changes.</p> +
+
+ Wed, 12 Oct 2022 00:00:00 GMT +
+ + Not long until Drupal 10 + /daily/2022/10/11/not-long-until-drupal-10 + http://localhost:8000/daily/2022/10/11/not-long-until-drupal-10 + +
<p>I was surprised to see this week that it’s only two months until Drupal 10 is released (14th December 2022).</p> + +<p>I’m starting a new Drupal development project in December so will be looking to get that on Drupal 10 as soon as possible.</p> + +<p>From the client’s perspective, getting their new project on Drupal 10 and them not needing to upgrade from D9 in the future is a big plus, even if the code differences between D9 and D10 are not that big - similar to Drupal 8 and 9.</p> + +<p>As a module maintainer, it’s been great to again see issues being created with automated Drupal 10 compatibility patches - thanks to Rector.</p> + +<p>It’s great to see these regular updates and new versions of Drupal, but also for the PHP language, with PHP 7 being end-of-life next month.</p> + +<p>It’s a big difference compared to the long-term releases that we had for Drupal 6 and 7, and PHP 5, but one that I definitely prefer.</p> +
+
+ Tue, 11 Oct 2022 00:00:00 GMT +
+ + Contributing to open-source software, one small change at a time + /daily/2022/10/10/contributing-open-source-software-one-small-change-time + http://localhost:8000/daily/2022/10/10/contributing-open-source-software-one-small-change-time + +
<p>Since looking more into Astro, I was looking through the GitHub repository - specifically within the examples - and spotted a typo within the title of one of the examples.</p> + +<p>Rather than leaving it, I decided to follow the "boy-scout rule" and submit a fix and leave the code in a better state than I found it.</p> + +<p>The Astro repository is hosted on GitHub so I was able to fork the repository, fix the typo and create a pull request with a few clicks in the GitHub UI.</p> + +<p>Contributing to open-source software - particularly if you're new to it - doesn't mean that you need to always add large and complex changes. Small changes such as fixing a typo, updating documentation, fixing a small bug, or adding additional tests are all valid contributions that improve open-source projects.</p> +
+
+ Mon, 10 Oct 2022 00:00:00 GMT +
+ + Coding defensively, and Implicit vs explicit coding + /daily/2022/10/09/coding-defensively-implicit-explicit + http://localhost:8000/daily/2022/10/09/coding-defensively-implicit-explicit + +
<p>As well as <a href="http://localhost:8000/daily/2022/10/08/first-impressions-astro">being introduced to Astro</a> in Simon's most recent Pro Tailwind workshop, something else that we discussed was implicit vs explicit coding, and coding defensively.</p> + +<p>For example, if you had this code:</p> + +<pre><code class="javascript">const sizeClasses = { + small: 'px-3 py-1 text-sm', + medium: 'px-5 py-2', + large: 'px-7 py-2.5 text-lg', +} + +const shapeClasses = { + square: '', + rounded: 'rounded', + pill: 'rounded-full', +} +</code></pre> + +<p>Both the <code>medium</code> size and <code>square</code> shape have an implicit value.</p> + +<p>The <code>small</code> size has a text size class of <code>text-sm</code> and the <code>large</code> size has <code>text-lg</code>. As there isn't a text size added for <code>medium</code>, it is implicitly <code>text-base</code> - the default text size.</p> + +<p>Likewise, the <code>rounded</code> shape has a class of <code>rounded</code> and the <code>pill</code> shape has <code>rounded-full</code>. As a square button doesn't have any rounding, it has an empty string but it is implicitly <code>rounded-none</code> - the default border radius value.</p> + +<p>If we were to code this explicitly, <code>text-base</code> and <code>rounded-none</code> would be added to their respective size and shape classes.</p> + +<p>It's mostly personal preference, but explicitly adding the additional classes could potentially future-proof the components if there was a situation where the text size or border radius was being overridden.</p> + +<p>It also makes it more obvious to anyone reading the code that these values are being set, rather than them needing to make that assumption - assuming that they're aware of the default values at all.</p> + +<p>It's similar to having this example PHP code:</p> + +<pre><code class="language-php">function __invoke(string $type, int $limit): void {}; +</code></pre> + +<p>Whilst I'm using type hints for the parameters to ensure that the values are a string and an integer respectively, it's also safe to assume that the type shouldn't be an empty string, so do we check for that?</p> + +<p>I'd also suggest that the limit shouldn't be a negative integer, so we'd want to check that the value is not less than zero, or if zero isn't being used as an "all" value, then we'd want to check that the limit is greater than one.</p> + +<p>In this case, the type hints add some explicitness to the parameters, but checking for these additional conditions adds another defensive layer to the code - forcing it to return earlier with an explicit error message rather than causing a vaguer error and elsewhere in the application.</p> + +<p>Personally, I like to be explicit and code defensively, making sure that I try and cover as many edge cases as possible and writing test cases for them.</p> + +<p>Coming back to the Tailwind example, the majority of us decided to add in extra classes after the exercise and it was an interesting discussion and part of the workshop.</p> +
+
+ Sun, 09 Oct 2022 00:00:00 GMT +
+ + First impressions of Astro + /daily/2022/10/08/first-impressions-astro + http://localhost:8000/daily/2022/10/08/first-impressions-astro + +
<p>This week I attended another of Simon Vrachliotis' Pro Tailwind workshops.</p> + +<p>The workshop again was great, teaching us about multi-style Tailwind components, such as a button that has props for variants like size, shape and impact, and how to create them in a flexible and maintainable way as well as making use of Headless UI.</p> + +<p>For this workshop though, the examples and challenges used a tool that I wasn't familiar with - the Astro web framework.</p> + +<p>I've seen a lot of blog posts and streams mentioning it but I hadn't tried it out for myself until the workshop.</p> + +<p>What I find interesting is that it comes with a number of available integrations - from Tailwind CSS, to Vue, React, and Alpine.js, and you can use the all within the same project, or even on the same page. Installing an integration is as simple as <code>yarn astro add tailwindcss</code>.</p> + +<p>The templates feel familiar and make use of front matter within Astro components, and regular YAML front matter works within Markdown files - which are supported out of the box.</p> + +<p>I've been thinking of redoing my personal website and evaluating options, but I think that Astro might be a new one to add to the list.</p> +
+
+ Sat, 08 Oct 2022 00:00:00 GMT +
+ + Refactoring to value objects + /daily/2022/10/03/refactoring-value-objects + http://localhost:8000/daily/2022/10/03/refactoring-value-objects + +
<p>Here's a snippet of some Drupal code that I wrote last week. It's responsible for converting an array of nodes into a Collection of one of it's field values.</p> + +<pre><code class="language-php">return Collection::make($stationNodes) + -&gt;map(fn (NodeInterface $station): string =&gt; $station-&gt;get('field_station_code')-&gt;getString()) + -&gt;values(); +</code></pre> + +<p>There are two issues with this code.</p> + +<p>First, whilst I'm implicitly saying that it accepts a certain type of node, because of the <code>NodeInterface</code> typehint this could accept any type of node. If that node doesn't have the required field, the code will error - but I'd like to know sooner if an incorrect type of node is passed and make it explicit that only a certain type of node can be used.</p> + +<p>Second, the code for getting the field values is quite verbose and is potentially repeated in other places within the codebase. I'd like to have a simple way to access these field values that I can reuse anywhere else. If the logic for getting these particular field values changes, then I'd only need to change it in one place.</p> + +<h2 id="introducing-a-value-object">Introducing a value object</h2> + +<p>This is the value object that I created.</p> + +<p>It accepts the original node but checks to ensure that the node is the correct type. If not, an Exception is thrown.</p> + +<p>I've added a helper method to get the field value, encapsulating that logic in a reusable function whilst making the code easier to read and its intent clearer.</p> + +<pre><code class="language-php">namespace Drupal\mymodule\ValueObject; + +use Drupal\node\NodeInterface; + +final class Station implements StationInterface { + + private NodeInterface $node; + + private function __construct(NodeInterface $node) { + if ($node-&gt;bundle() != 'station') { + throw new \InvalidArgumentException(); + } + + $this-&gt;node = $node; + } + + public function getStationCode(): string { + return $this-&gt;node-&gt;get('field_station_code')-&gt;getString(); + } + + public static function fromNode(NodeInterface $node): self { + return new self($node); + } + +} +</code></pre> + +<h2 id="refactoring-to-use-the-value-object">Refactoring to use the value object</h2> + +<p>This is what my code now looks like:</p> + +<pre><code class="language-php">return Collection::make($stationNodes) + -&gt;map(fn (NodeInterface $node): StationInterface =&gt; Station::fromNode($node)) + -&gt;map(fn (StationInterface $station): string =&gt; $station-&gt;getStationCode()) + -&gt;values(); +</code></pre> + +<h1>&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD:website/source/_daily_emails/2022-10-03.md</h1> + +<blockquote> + <blockquote> + <blockquote> + <blockquote> + <blockquote> + <blockquote> + <blockquote> + <p>b9cea6d (chore: replace Sculpin with Astro):website/src/pages/daily-emails/2022-10-03.md + I've added an additional <code>map</code> to convert the nodes to the value object, but the second map can now use the new typehint - ensuring better type safety and also giving us auto-completion in IDEs and text editors. If an incorrect node type is passed in, then the Exception will be thrown and a much clearer error message will be shown.</p> + </blockquote> + </blockquote> + </blockquote> + </blockquote> + </blockquote> + </blockquote> +</blockquote> + +<p>Finally, I can use the helper method to get the field value, encapsulating the logic within the value object and making it intention clearer and easier to read.</p> +
+
+ Mon, 03 Oct 2022 00:00:00 GMT +
+ + Minimum viable CI pipelines + /daily/2022/10/02/minimum-viable-pipelines + http://localhost:8000/daily/2022/10/02/minimum-viable-pipelines + +
<p>When I start a new project, and sometimes when I join an existing project, there are no CI (continuous integration) pipelines, no automated tests, and sometimes no local environment configuration.</p> + +<p>In that case, where should you start when adding a CI pipeline?</p> + +<p>I like to start with the simplest solution to get a passing build and to prove the concept - even if it's a "Hello, world" message. I know that the pipeline is configured correctly and runs when expected, and gives the output that I expect.</p> + +<p>I like to use Docker for my development environments, partly because it's very easy to reuse the same set up within a CI pipeline just by running <code>docker image build</code> or <code>docker compose build</code>.</p> + +<p>Having a task that ensures the project builds correctly is a great next step.</p> + +<p>Within a Dockerfile, I run commands to validate my lock files, download and install dependencies from public and private repositories, and often apply patch files to third-party code. If a lock file is no longer in sync with its composer.json or package.json file, or a patch no longer applies, this would cause Docker and the CI pipeline to fail and the error can be caught and fixed within the pipeline.</p> + +<p>Next, I'd look to run the automated tests. If there aren't any tests, I'd create an example test that will pass to prove the concept, and expect to see the number of tests grow as new features are added and as bugs are fixed.</p> + +<p>The big reason to have automated tests running in a pipeline is that all the tests are run every time, ensuring that the test suite is always passing and preventing regressions across the codebase. If any test fails, the pipeline fails. This is knows as continuous delivery - ensuring that code is always in a releasable state.</p> + +<p>From there, I'd look to add additional tasks such as static analysis and code linting, as well as anything else to validate, build or deploy the code and grow confidence that a passing CI pipeline means that the code is releasable.</p> + +<p>As more tasks are added to the pipeline, and the more of the code the tasks cover (e.g. test coverage) the more it can be replied upon.</p> + +<p>If there is a failure that wasn't caught in the CI pipeline, then the pipeline itself should be iterated on and improved.</p> + +<p>Having a CI pipeline allows you to identify issues sooner and fix them quicker, encourages best practices like automated testing and test-driven development, and enables continuous deployment where code is automatically deployed after a passing build.</p> + +<p>If you have a project without a CI pipeline, I'd encourage you to add one, to start small, and continuously iterate on it over time - adding tasks that are useful and valuable, and that build confidence that you can safely release when you need to.</p> +
+
+ Sun, 02 Oct 2022 00:00:00 GMT +
+ + Why do code katas? + /daily/2022/10/01/code-katas + http://localhost:8000/daily/2022/10/01/code-katas + +
<h2 id="what-are-code-katas%3F">What are code katas?</h2> + +<p>Code katas are programming exercises which, like katas in martial arts, use practice and repetition to improve your skills.</p> + +<p>Common katas are Fizzbuzz, the Bowling score calculator, and the Gilded Rose.</p> + +<p>Each gives you the criteria of what the kata should do before it can be considered complete along with any specific information, and some websites will also give you a suite of failing tests to make pass - though I prefer to write my own and follow a test-driven development approach.</p> + +<p>Once you have completed the solution and the criteria is satisfied, the kata is complete.</p> + +<h2 id="why-i-do-code-katas">Why I do code katas</h2> + +<p>As I said, doing code katas improves your skills by solving problems and identifying patterns that you may see when working in your project code.</p> + +<p>Different katas focus on different patterns. For example, the Fibonacci Number kata focuses on recursion, whereas the Gilded Rose kata is all about refactoring complex legacy code.</p> + +<p>Doing code katas keeps your skills sharp and gives you a different perspectives as you work through different katas. You can then use and apply these within your main projects.</p> + +<p>If you want to learn a new programming language then working on a kata that you've already solved in a language that you're familiar with allows you to focus on the syntax and features of the new language. I've been working on some code katas in TypeScript as I've been working with that recently, and would like to do some in Go.</p> + +<p>If you work as part of a team or a part of a meetup, code katas can be worked on as a group and can introduce new skills like automated testing and test-driven development as well as providing some opportunities for team-building and socialising. If you're trying to introduce pair or mob programming, then working on code katas could be a good first step.</p> + +<p>If you're just getting started with programming, working on code katas will help you learn the fundamentals and problem solving, but I'd also encourage you to put the code on GitHub and blog about each kata that you complete. Doing so will help and encourage others and also look good when applying for roles.</p> + +<p>P.S. There are lists of code katas at https://github.com/gamontal/awesome-katas and https://codingdojo.org/kata, and online versions at https://www.codewars.com/join and https://exercism.org/tracks. There are many others - if you have a favourite, reply to this email and let me know.</p> + +<p>I have <a href="https://github.com/opdavies?tab=repositories&amp;q=katas">some GitHub repositories for my code kata solutions</a> and will continue to build these as I do more.</p> +
+
+ Sat, 01 Oct 2022 00:00:00 GMT +
+ + Store Wars: different state management in Vue.js + /daily/2022/09/30/store-wars-vuejs + http://localhost:8000/daily/2022/09/30/store-wars-vuejs + +
<p>I'm currently working on a Vue.js application that I started building in Vue 2 before starting to use the Composition API, and then moved it to Vue 3.</p> + +<p>In the original version, I was using Vuex for state management within the application, and interacting with Vuex directly within my Vue components - calling <code>getters</code> and <code>dispatch</code> to retrieve and update data.</p> + +<p>As part of moving to Vue 3, I wanted to evaluate any new options, like Pinia which is now the default state management library for Vue.</p> + +<p>But because I was integrating with Vuex directly, switching to an alternative would mean changing code within my components.</p> + +<h2 id="defining-a-store-interface">Defining a Store interface</h2> + +<p>This is a situation that often occurs in back-end development - where you may need to switch to a different type of database or a different payment provider in an eCommerce application.</p> + +<p>In that situation, you need a generic interface that can be used by different implementations. Because they have consistent methods, one implementation can be replaced with another or multiple can be added at the same time. This is called the Strategy design pattern, and related to the open-closed principle in SOLID.</p> + +<p>This is what I did by adding a <code>Store</code> interface:</p> + +<pre><code class="javascript">export default interface Store { + actions: { + addRow(): void; + init(): void; + removeRow(index: Number): void; + }; + + state: { + isLoading: boolean; + selection: { + items: []; + }; + }; +} +</code></pre> + +<p>Any store that I want to work with needs to have these defined actions and state values, so I can use them within my components knowing that they will always be available.</p> + +<h2 id="creating-a-native-vue-store">Creating a native Vue store</h2> + +<p>This is one implementation of the <code>Store</code> interface, using just Vue's <code>reactive</code> function from the Composition API:</p> + +<pre><code class="javascript">let state = reactive({ + isLoading: false, + selection: { + items: [], + }, +}); + +let actions = { + addRow(): void { + state.selection.items.push({ + // ... + }); + }, + + init(): void { + state.isLoading = true; + + // ... + }, + + removeRow(index: number): void { + state.selection.items.splice(index, 1); + }, +}; + +const vueStore: Store = { + actions, + state: readonly(state), +}; + +export default vueStore; +</code></pre> + +<p>If I needed to add a Pinia version or another library, I can create another implementation that complies with same interface.</p> + +<p>Each implementation being responsible for any specifics for that library - extracting that logic from the component code making it more flexible and reusable.</p> +
+
+ Fri, 30 Sep 2022 00:00:00 GMT +
+ + Mob programming at PHP South Wales + /daily/2022/09/28/mob-programming-php-south-wales + http://localhost:8000/daily/2022/09/28/mob-programming-php-south-wales + +
<p>Tonight was our September meetup for the PHP South Wales user group, where I ran a hands-on session on mob programming.</p> + +<p>I created <a href="https://speakerdeck.com/opdavies/an-introduction-to-mob-programming">a small slide deck</a> before we started a mob session with the group.</p> + +<p>We worked on the FizzBuzz kata in PHP, using Pest for our automated tests.</p> + +<p>We followed the Driver and Navigator model, with one person responsible for the typing and interpreting the instructions from the Navigators, and switched roles every ten minutes.</p> + +<p>You can <a href="https://github.com/opdavies/code-katas/blob/1da5dd5a79bc7ca083c0c4216fc3b4b0854f623d/php/tests/FizzBuzzTest.php">see the code that we wrote</a> on my code katas GitHub repository.</p> + +<p>It was a fun experience and nice to code with some people who I hadn't coded with before.</p> + +<p>We did some code kata sessions during our online meetups which also seemed to go well, so coding nights on katas or personal or open-source projects might be something that we do more of in the future.</p> +
+
+ Wed, 28 Sep 2022 00:00:00 GMT +
+ + Mentoring with Drupal Career Online + /daily/2022/09/27/mentoring-with-drupal-career-online + http://localhost:8000/daily/2022/09/27/mentoring-with-drupal-career-online + +
<p>Today, I met my new mentee from the Drupal Career Online program.</p> + +<p><a href="http://localhost:8000/daily/2022/09/21/being-drupal-contribution-mentor">As well as mentoring at events like DrupalCamps and DrupalCons</a>, I enjoy mentoring and working with new Developers going through bootcamps and training programmes like Drupal Career Online, some who are experienced Developers who are learning a new skill, and some who are learning how to code and are taking their first steps into programming.</p> + +<p>I've talked about <a href="http://localhost:8000/daily/2022-08-28/how-started-programming">how I got started programming</a>, but as self-taught Developer, it would have been great to have had a mentor to ask questions of, to help me get me started, and to make sure that I was going down the right track and learning the correct things.</p> + +<p>Maybe this is more applicable these days with more people learning and working from home since COVID-19?</p> + +<p>Similar to helping mentees at a contribution sprint work towards their first commits to Drupal, it's great to be able to introduce new Developers to a open-source project and community such as Drupal, help develop their skills, and hopefully enable them to get the new job and career that they want.</p> +
+
+ Tue, 27 Sep 2022 00:00:00 GMT +
+ + Experimenting with the Nix package manager + /daily/2022/09/26/experimenting-with-the-nix-package-manager + http://localhost:8000/daily/2022/09/26/experimenting-with-the-nix-package-manager + +
<p>After seeing it on some recent live streams and YouTube videos, I've recently been trying out the Nix package manager and looking into how I might use it for my local environment setup - potentially replacing some of my current Ansible configuration.</p> + +<p>Separate from the NixOS operating system, Nix is a cross-platform package manager, so instead of using <code>apt</code> on Ubuntu and <code>brew</code> on macOS, you could run Nix on both and install from the 80,000 packages listed on https://search.nixos.org/packages.</p> + +<p>There is a community project called Home Manager which can be installed alongside Nix which, similar to Stow or what I'm doing with Ansible, can manage your dotfiles or even create them from your Home Manager configuration, and can manage plugins for other tools such as ZSH and tmux.</p> + +<p>There's also a Nix feature called "Flakes" which allow you to separate configuration for different operating systems. I currently have a flake for Pop!&#95;OS which installs all of my packages and a minimal flake for my WSL2 environment as some of the packages are installed in Windows instead of Linux.</p> + +<p>I can see Ansible still being used to set up my post-setup tasks such as cloning my initial projects, but the majority of my current Ansible setup where I'm installing and configuring packages I think could be moved to Nix.</p> + +<p>I have a work-in-progress Nix-based version <a href="https://github.com/opdavies/dotfiles/tree/7c3436c553f8b81f99031e6bcddf385d47b7e785">in my dotfiles repository</a> where you can also see <a href="https://github.com/opdavies/dotfiles/blob/7c3436c553f8b81f99031e6bcddf385d47b7e785/home-manager/modules/git.nix">how I've configured Git with Home Manager</a>.</p> + +<p>I may install NixOS on an old laptop to test that out too.</p> +
+
+ Mon, 26 Sep 2022 00:00:00 GMT +
+ + Using a component library for front-end development + /daily/2022/09/25/using-component-library-for-front-end-development + http://localhost:8000/daily/2022/09/25/using-component-library-for-front-end-development + +
<p>On a current project, I've decided to use a component library as the first place to do front-end development.</p> + +<p>I'm using <a href="https://fractal.build">Fractal</a> as I can use Twig for templates. As Drupal also uses Twig templates, I have more reusabilty between the components in Fractal and Drupal compared to converting them from a different templating language like Handlebars or Nunjucks.</p> + +<p>Rather than developing directly within the custom Drupal theme, I've been creating new components and pages initially within Fractal.</p> + +<p>I have been able to create new components quickly and easily with the views uing Twig templates and inject data to it using a context file - a YAML file for each component that contains data that is injected automatically into the view.</p> + +<p>This meant that I've been able to develop new components from scratch without needing to set up content types or paragraphs within Drupal, validate and confirm my data model, and present the templates to the client for review in Fractal. If a change is needed, it's quick to do.</p> + +<p>I've also moved my asset generation step into Fractal. No CSS or JavaScript is being compiled within the Drupal theme, it is created within Fractal and copied over with the Twig templates.</p> + +<p>In most cases, I've been able to copy the Twig templates into Drupal and replace the static context data with dynamic data from Drupal without needing to make any further changes.</p> + +<p>In a couple of situations, I've needed to change my implementation slightly when moving a template into Drupal, so in this workflow, I've made the changes in Fractal and re-exported them to keep things in sync between the two systems.</p> + +<p>In situations where there is existing markup and/or styles from the Drupal side, I've copied those into Fractal so that they match before adding the additional styling and any markup changes.</p> + +<p>In general, I like the approach as it gives me more flexibility upfront to make changes before needing to configure Drupal. I can see how things could get out of sync between the two systems, but hopefully having the assets compiled in Fractal and needing to copy them into Drupal will keep things synced up.</p> + +<p>I don't think that I'd use this approach for all projects, but for this one, where I'm working with multiple themes and will need to later add different variants of pages and components, it's worked well so far.</p> +
+
+ Sun, 25 Sep 2022 00:00:00 GMT +
+ + ADRs and Technical Design Documents + /daily/2022/09/23/adrs-technical-design-documents + http://localhost:8000/daily/2022/09/23/adrs-technical-design-documents + +
<h2 id="architectural-decision-records">Architectural Decision Records</h2> + +<p>Architectural Decision Records (ADRs) are documents to record software design choices. They could be saved in your code repository as plain-text or Markdown files, or stored in Confluence or a wiki - wherever your team stores its documentation.</p> + +<p>They usually consist of the sections:</p> + +<ul> +<li>Status - is it proposed, accepted, rejected, deprecated, superseded, etc.?</li> +<li>Context - what is the issue that is causing the decision or change?</li> +<li>Decision - what is the change that's being done or proposed?</li> +<li>Consequences - what becomes easier or more difficult to do?</li> +</ul> + +<p>Any change that is architecturally significant should require an ADR to be written, after which it can be reviewed and potentially actioned.</p> + +<p>These will remain in place to form a decision log, with specific ADRs being marked as superseded if a newer ADR replaces it.</p> + +<h2 id="technical-design-documents">Technical Design Documents</h2> + +<p>A similar type of document are Technical Design Documents (TDDs), that I first saw on TheAltF4Stream. I like to think of these as lightweight ADRs.</p> + +<p>The first heading is always "What problem are we trying to solve?", or sometimes just "The problem".</p> + +<p>Similar to the Context heading in an ADR, this should include a short paragraph describing the issue.</p> + +<p>Unlike ADRs, there are no other set headings but these are some suggested ones:</p> + +<ul> +<li>What is the current process?</li> +<li>What are any requirements?</li> +<li>How do we solve this problem?</li> +<li>Alternative approaches</li> +</ul> + +<p>I like after describing the problem, being able to move straight into describing what's appropriate and relevant for this task and ignore sections that aren't needed.</p> + +<p>When I started writing ADRs, they all had the 'Accepted' status as I was either writing them for myself or in a pair or mob. As wasn't adding any value, I've removed it since switching to writing TDDs.</p> + +<p>Whether you use ADRs, TDDs or another approach, it's very useful to have a log of all of your architectural design decisions, both looking back in the future to remember why something was done in a certain way, or before you start implementing a solution to review the problem, evaluate the requirements and all potential solutions and document the selected one any why it was selected.</p> + +<p><a href="https://adr.github.io">Find our more about ADRs</a> or <a href="https://altf4.wiki/t/how-do-i-write-a-tdd/21">find out more about TDDs</a>.</p> +
+
+ Fri, 23 Sep 2022 00:00:00 GMT +
+ + Releasing a Drupal module template + /daily/2022/09/22/releasing-drupal-module-template + http://localhost:8000/daily/2022/09/22/releasing-drupal-module-template + +
<p>Today, I an the idea to create a reusable template for new Drupal modules, based on how I like to build modules and how I've shown others to do so in my Drupal testing workshop.</p> + +<p>So I did, and released it for free <a href="https://github.com/opdavies/drupal-module-template">on my GitHub account</a>.</p> + +<p>Like my Tailwind CSS starter theme on Drupal.org, it's not intended to be added as a module directly, but something that can be cloned and used as a base for people's own modules.</p> + +<p>It includes an example route and Controller that load a basic page, and has a test to ensure that the page exists and loads correctly.</p> + +<p>The Controller is defined as a service and uses autowiring to automatically inject the its dependencies, the same as in my workshop example code.</p> + +<p>It's the initial release so it's rough around the edges still. I'll use it tomorrow to create a new module and document the steps to add to the README as well as other pieces of documentation.</p> + +<p>If you're creating a new Drupal module and try it out, start a discussion on the GitHub repository or <a href="https://twitter.com/opdavies">let me know on Twitter</a>. If you have questions, create a discussion or just reply to this email and I'll get back to you.</p> +
+
+ Thu, 22 Sep 2022 00:00:00 GMT +
+ + Being a Drupal contribution mentor + /daily/2022/09/21/being-drupal-contribution-mentor + http://localhost:8000/daily/2022/09/21/being-drupal-contribution-mentor + +
<p>This week is DrupalCon Prague, and although I'm not at this event, I'd like to write about some my experiences at DrupalCon - in particular about being a contribution mentor.</p> + +<h2 id="my-first-drupalcon">My first DrupalCon</h2> + +<p>The first DrupalCon that I attended was in 2013, also in Prague.</p> + +<p>I was enjoying the session days when I stopped at the mentoring table to find out more about the contribution sprints that were happening on the Friday.</p> + +<p>I didn't have any commits in Drupal core but had already worked on and released some of my own contributed modules, so I was familiar with the tools and the Drupal.org contribution workflow. In short, I was signed up to be a mentor during the sprints.</p> + +<p>I remember being involved in the preparation too, sitting in a hotel lobby, identifying potential issues for new contributors to work on, alongside people who I'd previously interacted with in the issue queues on Drupal.org.</p> + +<p>On the day, I helped new contributors get their local environments up and running, select issues to work on, and perform tasks like creating and re-rolling patch files and submitting them for review.</p> + +<p>One of my highlights at the end of the day was the live commit, when a patch that a new contributor had worked on that day was committed to Drupal core live on stage!</p> + +<p>Whenever I've attended DrupalCon events since, I've always volunteered to be a contribution mentor, as well as mentoring and organising sprints at other Drupal events.</p> + +<h2 id="the-five-year-issue">The Five Year Issue</h2> + +<p>One of the most memorable times mentoring was whilst working with a group of contributors at DrupalCon in May 2015.</p> + +<p>Someone was working on a Drupal core issue that was very similar to <a href="https://www.drupal.org/project/drupal/issues/753898">one that I'd looked at</a> a few years before.</p> + +<p>We focused on the original issue that I'd commented on, reviewed, tested, and re-rolled the patch, fixed a failing test, and marked it as "reviewed and tested by the community".</p> + +<p>A few days after the conference, and just over five years after my original comment, the patch was committed - giving my contributors their first commits to Drupal 8 core, and also <a href="https://git.drupalcode.org/project/drupal/-/commits/9.5.x?search=opdavies">one of mine</a>.</p> +
+
+ Wed, 21 Sep 2022 00:00:00 GMT +
+ + Why I like trunk-based development + /daily/2022/09/20/why-like-trunk-based-development + http://localhost:8000/daily/2022/09/20/why-like-trunk-based-development + +
<p>For the majority of my software development career, I've worked with version control in a very similar way.</p> + +<p>There are one or two long-lived branches, usually a combination of <code>develop</code>, <code>master</code> or <code>main</code>, that contain the production version of the code. When starting work on a new feature or bug fix, a new branch is created where the changes are made in isolation, and is submitted for review once complete. This is typically referred to as "Git Flow" or "GitHub Flow".</p> + +<p>Whilst those changes are awaiting review, a new task is started and the process is repeated.</p> + +<h2 id="trunk-based-development">Trunk-based development</h2> + +<p>Something that I've been practicing and advocating for lately is trunk-based development, where there's only one branch that everyone works on, and commits and pushes to instead of creating separate per-task branches.</p> + +<p>Even on a client project where I was the only Developer, I was used to creating per-task branches and I can recall when trying to demo two features to a client and the application broke when switching between branches.</p> + +<p>The vast majority of the time, whether working individually or on a team, I've found that the per-task branches weren't needed and working on a single branch was easier and simpler.</p> + +<p>There are still occassions when a temporary branch is needed, but in general, all changes are made to the single branch.</p> + +<p>Trunk-based development ties in nicely with the continuous integration approach, where everyone commits and pushes their work at least once a day - ideally, multiple times a day. This eliminates long-running feature or bug fix branches that get out of sync with the main branch as well as conflicting with each other.</p> + +<p>It seemed scary to begin with, having been used to per-task branches and asynchronous peer reviews via pull or merge requests, but trunk-based development has made things simpler and encourages other best practices such as pair and mob programming. having a good CI pipeline to identify regressions, using feature flags to separate code deployments from feature releases, and frequent code integration and deployment via continuous commits and pushes.</p> +
+
+ Tue, 20 Sep 2022 00:00:00 GMT +
+ + Useful Git configuration + /daily/2022/09/19/useful-git-configuration + http://localhost:8000/daily/2022/09/19/useful-git-configuration + +
<p>Here are some snippets from my Git configuration file.</p> + +<p>These days, I use a much simpler workflow and configuration since doing more trunk-based development, but in general, I rebase instead of merging by default, and prefer to use fast-forward merges that doesn't create a merge commit.</p> + +<p><code>branch.autosetuprebase = always</code> and <code>pull.rebase = true</code> configure Git to always rebase instead of pull. It does this for all branches, though I might override this for <code>main</code> branches.</p> + +<p><code>pull.ff = only</code> and <code>merge.ff = only</code> prevents creating a merge commit and will prevent the merge if it would create one. If I needed to override this, I could by using the <code>--no-ff</code> option on the command line.</p> + +<p>I use <code>checkout.defaultRemote = origin</code> to ensure that the <code>origin</code> remote is used if I have multiple remotes configured, and <code>push.default = upstream</code> to set the default remote to push to.</p> + +<p><code>merge.autoStash</code> allows for running merges on a dirty worktree by automatically creating and re-applying a stash of the changes, and <code>fetch.prune</code> will automatically prune branches on fetch - keeping things tidy.</p> + +<p>I also have and use a number of aliases.</p> + +<p>Some like <code>pl = pull</code> and <code>ps = push</code> are shorter versions of existing commands, and some like <code>aa = add --all</code>, <code>fixup = commit --fixup</code> and some additional arguments to commands.</p> + +<p>I also have some like <code>current-branch = rev-parse --abbrev-ref HEAD</code> and <code>worktrees = worktree list</code> which add simple additional commands, and some like <code>repush = !git pull --rebase &amp;&amp; git push</code> which use execute shell commands to execute more complex commands or combine multiple commands.</p> + +<p>This is a snapshot of my Git configuration. The <a href="https://github.com/opdavies/dotfiles/blob/7e935b12c09358adad480a566988b9cbfaf5999e/roles/git/files/.gitconfig">full version is on GitHub</a>.</p> +
+
+ Mon, 19 Sep 2022 00:00:00 GMT +
+ + Thoughts on automated code formatting + /daily/2022/09/17/thoughts-automated-code-formatting + http://localhost:8000/daily/2022/09/17/thoughts-automated-code-formatting + +
<p>For a long time, I've been focused on writing code that complies with defined coding standards, either to pass an automated check from a tool like PHP Code Sniffer (PHPCS) or eslint, or a code review from a team member.</p> + +<p>Complying with the standards though is something that I've done manually.</p> + +<p>As well as automated tools for linting the code, there are tools like PHP Code Beautifier and Fixer, and Prettier for formatting the code based on the same standards, which I've started to use more recently.</p> + +<p>These tools can be run on the command line, VS Code has a "Format on save" option, and I can do the same in Neovim using an auto-command that runs after writing a file if an LSP is attached. I typically use a key mapping for this though so I can run it when I need, rather than it running automatically every time a file is saved.</p> + +<p>One of my concerns with automated code formatting is what to do when working with existing code that doesn't already follow the standards. If I need to make a change to a file, with automated formatting, the rest of the file can change due to formatting being applied when I save my change.</p> + +<p>I recently introduced a PHPCS step to a CI pipeline for an existing project. I knew that it was going to fail initially, but I was able to see the list of errors. I ran the code formatter on each of the files to fix the errors, committed and pushed the changes, and watched the pipeline run successfully.</p> + +<p>This meant that I had a commit reformatting all of the affected files, but it was good to combine these together rather than having them separate, and not mixed with any other changes like a new feature or a bug fix.</p> + +<p>Since doing this, it's been nice when working in this codebase to not have to worry about code style violations, and I can focus on writing the code that I need to, knowing that I can rely on the automated formatting to fix any issues before I commit them.</p> +
+
+ Sat, 17 Sep 2022 00:00:00 GMT +
+ + Why I mostly write functional and integration tests + /daily/2022/09/16/why-mostly-write-functional-and-integration-tests + http://localhost:8000/daily/2022/09/16/why-mostly-write-functional-and-integration-tests + +
<p>In <a href="http://localhost:8000/daily/2022/09/14/simpletest-drupal-test">Wednesday's email</a>, I showed how quick it is to get started writing automated tests for a new Drupal module, starting with a functional test.</p> + +<p>I prefer the outside-in style (or London approach) of test-driven development, where I start with a the highest-level test that I can for a task. If the task needs me to make a HTTP request, then I’ll use a functional test. If not, I’ll use a kernel (or integration) test.</p> + +<p>I find that these higher-level types of tests are easier and quicker to set up compared to starting with lower-level unit tests, cover more functionality, and make it easier to refactor.</p> + +<h2 id="an-example">An example</h2> + +<p>For example, this <code>Device</code> class which is a data transfer object around Drupal's <code>NodeInterface</code>. It ensures that the correct type of node is provided, and includes a named constructor and a helper method to retrieve a device's asset ID from a field:</p> + +<pre><code class="language-php">final class Device { + + private NodeInterface $node; + + public function __construct(NodeInterface $node) { + if ($node-&gt;bundle() != 'device') { + throw new \InvalidArgumentException(); + } + + $this-&gt;node = $node; + } + + public function getAssetId(): string { + return $this-&gt;node-&gt;get('field_asset_id')-&gt;getString(); + } + + public static function fromNode(NodeInterface $node): self { + return new self($node); + } + +} +</code></pre> + +<h2 id="testing-getting-the-asset-id-using-a-unit-test">Testing getting the asset ID using a unit test</h2> + +<p>As the <code>Node::create()</code> method (what I'd normally use to create a node) interacts with the database, I need to create a mock node to wrap with my DTO.</p> + +<p>I need to specify what value is returned from the <code>bundle()</code> method as well as getting the asset ID field value.</p> + +<p>I need to mock the <code>get()</code> method and specify the field name that I'm getting the value for, which also returns it's own mock for <code>FieldItemListInterface</code> with a value set for the <code>getString()</code> method.</p> + +<pre><code class="language-php">/** @test */ +public function should_return_an_asset_id(): void { + // Arrange. + $fieldItemList = $this-&gt;createMock(FieldItemListInterface::class); + + $fieldItemList + -&gt;method('getString') + -&gt;willReturn('ABC'); + + $deviceNode = $this-&gt;createMock(NodeInterface::class); + + $deviceNode + -&gt;method('bundle') + -&gt;willReturn('device'); + + $deviceNode + -&gt;method('get') + -&gt;with('field_asset_id') + -&gt;willReturn($fieldItemList); + + // Act. + $device = Device::fromNode($deviceNode); + + // Assert. + self::assertSame('ABC', $device-&gt;getAssetId()); +} +</code></pre> + +<p>This is quite a long 'arrange' section for this test, and just be confusing for those new to automated testing.</p> + +<p>If I was to refactor from using the <code>get()</code> and <code>getString()</code> methods to a different implementation, it's likely that the test would fail.</p> + +<h2 id="refactoring-to-a-kernel-test">Refactoring to a kernel test</h2> + +<p>This is how I could write the same test using a kernel (integration) test:</p> + +<pre><code class="language-php">/** @test */ +public function should_return_an_asset_id(): void { + // Arrange. + $node = Node::create([ + 'field_asset_id' =&gt; 'ABC', + 'type' =&gt; 'device' + ]); + + // Assert. + self::assertSame('ABC', Device::fromNode($node)-&gt;getAssetId()); +} +</code></pre> + +<p>I can create a real <code>Node</code> object, pass that to the <code>Device</code> DTO, and call the <code>getAssetId()</code> method.</p> + +<p>As I can interact with the database, there's no need to create mocks or define return values.</p> + +<p>The 'arrange' step is much smaller, and I think that this is easier to read and understand.</p> + +<h3 id="trade-offs">Trade-offs</h3> + +<p>Even though the test is cleaner, because there are no mocks there's other setup to do, including having the required configuration available, enabling modules, and installing schemas and configuration as part of the test - and having test-specific modules to store the needed configuration files.</p> + +<p>Because of this, functional and kernel tests will take more time to run than unit tests, but an outside-in approach could be worth considering, depending on your project and team.</p> +
+
+ Fri, 16 Sep 2022 00:00:00 GMT +
+ + The simplest Drupal test + /daily/2022/09/14/simpletest-drupal-test + http://localhost:8000/daily/2022/09/14/simpletest-drupal-test + +
<p>Most of my work uses the Drupal framework, and I've given talks and workshops on automated testing and building custom Drupal modules with test-driven development. Today, I wanted to see how quickly I could get a working test suite on a new Drupal project.</p> + +<p>I cloned a fresh version of my <a href="https://github.com/opdavies/docker-examples">Docker Examples repository</a> and started the Drupal example.</p> + +<p>I ran <code>mkdir -p web/modules/custom/example/tests/src/Functional</code> to create the directory structure that I needed, and then <code>touch web/modules/custom/example/tests/src/Functional/ExampleTest.php</code> to create a new test file and populated it with some initial code:</p> + +<pre><code class="language-php">&lt;?php + +namespace Drupal\Tests\example\Functional; + +use Drupal\Tests\BrowserTestBase; +use Symfony\Component\HttpFoundation\Response; + +class ExampleTest extends BrowserTestBase { + + protected $defaultTheme = 'stark'; + +} +</code></pre> + +<p>For the simplest test, I decided to test some existing Drupal core functionality - that an anonymous user can view the front page:</p> + +<pre><code class="language-php">/** @test */ +public function the_front_page_loads_for_anonymous_users() { + $this-&gt;drupalGet('&lt;front&gt;'); + + $this-&gt;assertSession()-&gt;statusCodeEquals(Response::HTTP_OK); +} +</code></pre> + +<p>To execute the test, I ran <code>SIMPLETEST_DB=sqlite://localhost//dev/shm/test.sqlite SIMPLETEST_BASE_URL=http://web phpunit -c web/core web/modules/custom</code>. The environment variables could be added to a <code>phpunit.xml.dist</code> file but I decided to add them to the command and use Drupal core's PHPUnit configuration file.</p> + +<p>As this is existing functionalty, the test passes. I can change either the path or the response code to ensure it also fails when expected.</p> + +<p>With the first test working, it's easy to add more for other functionality, such as whether different users should be able to access administration pages:</p> + +<pre><code class="language-php">/** @test */ +public function the_admin_page_is_not_accessible_to_anonymous_users() { + $this-&gt;drupalGet('admin'); + + $this-&gt;assertSession()-&gt;statusCodeEquals(Response::HTTP_FORBIDDEN); +} + +/** @test */ +public function the_admin_page_is_accessible_by_admin_users() { + $adminUser = $this-&gt;createUser([ + 'access administration pages', + ]); + + $this-&gt;drupalLogin($adminUser); + + $this-&gt;drupalGet('admin'); + + $this-&gt;assertSession()-&gt;statusCodeEquals(Response::HTTP_OK); +} +</code></pre> + +<p>Hopefully, this shows how quickly you can get tests running for a Drupal module. If you'd like to see more, the slides and video recording of my <a href="http://localhost:8000/presentations/tdd-test-driven-drupal">Test-Driven Drupal talk</a> are online.</p> +
+
+ Wed, 14 Sep 2022 00:00:00 GMT +
+ + A month of daily emails + /daily/2022/09/12/month-daily-emails + http://localhost:8000/daily/2022/09/12/month-daily-emails + +
<p>It’s already been a month since I started my email list and writing daily emails.</p> + +<p>Since then, I’ve written emails on various development and workflow-based topics, including Drupal, Git, Docker, Neovim, Ansible and Tailwind CSS.</p> + +<p>The first email was written on Thursday the 12th of August and after initially wondering whether I should start on the upcoming Monday, or how often to post, I decided to jump in with both feet and wrote the first daily post that day. The first few weren't actually emailed as I waited to see if I could sustain writing a daily post (I was just posting them to my website), but after a few days, I set up the email list and started sending the posts.</p> + +<p>I can confirm what <a href="https://jonathanstark.com">Jonathan Stark</a> and <a href="https://jhall.io">Jonathan Hall</a> have said - that it's easier to write daily and that you start to see topic ideas everywhere. I started with a list of between 20 and 25 ideas and still have most of them as I've pivoted on a day's topic based on an article or tweet that I saw, some code that I'd written, or some approach that I took.</p> + +<p>If you're considering starting a daily email list, I'd recommend it.</p> +
+
+ Mon, 12 Sep 2022 00:00:00 GMT +
+ + Custom styles in Tailwind CSS: `@apply`, `theme` or custom plugins + /daily/2022/09/11/custom-styles-tailwind-css-apply-theme-custom-plugins + http://localhost:8000/daily/2022/09/11/custom-styles-tailwind-css-apply-theme-custom-plugins + +
<p>There are three ways to add custom styles to a Tailwind CSS project. As there have been <a href="https://twitter.com/adamwathan/status/1559250403547652097">some recent tweets</a> around one of them - the <code>@apply</code> directive - I'd like to look at and give examples for each.</p> + +<h2 id="what-is-%60%40apply%60%3F">What is <code>@apply</code>?</h2> + +<p><code>@apply</code> is a PostCSS directive, provided by Tailwind, to allow re-using it's classes - either when extracting components or overriding third-party styles.</p> + +<p>The CSS file is the same as if you were writing traditional CSS, but rather than adding declarations to a ruleset, you use the <code>@apply</code> directive and specify the Tailwind CSS class names that you want to apply.</p> + +<p>For example:</p> + +<pre><code class="css">fieldset { + @apply bg-primary-dark; +} +</code></pre> + +<p>This is a simple example but it's easy to see how this could be used in ways that weren't intended and how edge-cases can be found.</p> + +<p>Adam said in a another tweet:</p> + +<blockquote> + <p>I estimate that we spend at least $10,000/month trying to debug extremely edge-case issues people run into by using <code>@apply</code> in weird ways.</p> +</blockquote> + +<h2 id="using-the-%60theme%60-function">Using the <code>theme</code> function</h2> + +<p>As well as <code>@apply</code>, Tailwind also provides a <code>theme</code> function that you can use in your CSS file. This removes the abstraction of using the class names and adds the ability to retrieve values from the <code>theme</code> section of your tailwind.config.js file.</p> + +<pre><code class="css">fieldset { + backgroundColor: theme('colors.primary.dark'); +} +</code></pre> + +<p>This seems to be the preferred approach over using <code>@apply</code>.</p> + +<h2 id="creating-a-custom-plugin">Creating a custom plugin</h2> + +<p>The <code>theme</code> function is also available if you write a custom Tailwind CSS plugin:</p> + +<pre><code class="javascript">const plugin = require('tailwindcss/plugin') + +plugin(({ addBase, theme }) =&gt; { + addBase({ + fieldset: { + backgroundColor: theme('colors.primary.dark'), + } + }) +}) +</code></pre> + +<p>This is an approach that I've used for <a href="https://github.com/opdavies?tab=repositories&amp;q=%23tailwindcss-plugin">generic, open-source plugins</a> but for project-specific styling, I've mostly used <code>@apply</code> or the <code>theme</code> function.</p> + +<p>That said, I like the modular architecture of having different custom plugins - especially if they're separated into their own files - and being able to easily toggle plugins by simply adding to or removing from the <code>plugins</code> array.</p> + +<p>I usually don't write many custom styles in a Tailwind project but I think that I'll focus on using the <code>theme</code> function going forward, either in a stylesheet or a custom plugin.</p> +
+
+ Sun, 11 Sep 2022 00:00:00 GMT +
+ + Automating Ansible deployments in CI + /daily/2022/09/10/automating-ansible-deployments-ci + http://localhost:8000/daily/2022/09/10/automating-ansible-deployments-ci + +
<p>Once you have a deployment that's run using Ansible, rather than running it manually, it's easy to automate it as part of a continuous integration pipeline and have your changes pushed automatically by tools like GitHub Actions and GitLab CI.</p> + +<p>You'll need to configure SSH by adding a known hosts file and a private key so the tool can connect to your server, but after that, it's just running the same Ansible commands.</p> + +<p>If you're using Ansistrano or other roles, you can install dependencies by using <code>ansible-galaxy</code>, and <code>ansible-vault</code> to decrypt and use any encrypted variables - securely storing the Vault password and any other secrets as environment variables within your pipeline.</p> + +<p>Here's an example using GitHub Actions:</p> + +<pre><code>- name: Download Ansible roles + run: ansible-galaxy install -r requirements.yml + +- name: Export the Ansible Vault password + run: echo $ANSIBLE_VAULT_PASS &gt; .vault-pass.txt + env: + ANSIBLE_VAULT_PASS: $ + +- name: Deploy the code + run: &gt; + ansible-playbook deploy.yml + -i inventories/$INVENTORY_FILE.ini + -e "project_git_branch=$GITHUB_SHA" + --vault-password-file=.vault-pass.txt + +- name: Remove the Ansible Vault password file + run: rm .vault-pass.txt +</code></pre> + +<p>Before these steps, I've added the SSH key and determined which inventory file to use by the updated branch. The Vault password is exported and then removed once it has been used.</p> + +<p>Automated tests and other code quality checks can be run in prior job, ensuring that the deployment only happens if those checks pass, but assuming that all is good, the playbook will be run and the changes will be deployed automatically.</p> +
+
+ Sat, 10 Sep 2022 00:00:00 GMT +
+ + Refactoring a Tailwind CSS component + /daily/2022/09/09/refactoring-tailwind-component + http://localhost:8000/daily/2022/09/09/refactoring-tailwind-component + +
<p>After last night's Pro Tailwind theming workshop, I decided to revisit and refactor some similar code that I'd worked on before.</p> + +<p>It was a demo for a presentation on utility-first CSS and Tailwind whilst I was at Inviqa.</p> + +<p>I'd taken one of the components from the website that we'd lauched and rebuilt it - in particular to show how Tailwind could be used for responsive and themeable components.</p> + +<p><a href="https://play.tailwindcss.com/Yfmw8O5UNN">The original version</a> was written in Tailwind 1 and used custom CSS with <code>@apply</code> rules to include text or background colours to elements based on the theme being used on that page or component.</p> + +<p>As well as moving it into a Next.js application, <a href="https://github.com/opdavies/inviqa-tailwindcss-example">the new version</a> uses techniques covered in Simon's workshop - using CSS custom properties (aka variables) to override the colours, and writing custom plugins to generate the required styles. It doesn't include everything from the workshop, but enough for this refactor.</p> + +<p>I also moved the <code>flex-basis</code> classes into their own standalone plugin and might release that as it's own open-source plugin.</p> + +<p>I'm working on a client project at the moment which will need switchable themes so I'm looking forward to putting these techniques to use again in the near future.</p> +
+
+ Fri, 09 Sep 2022 00:00:00 GMT +
+ + Keeping secrets with Ansible Vault + /daily/2022/09/08/keeping-secrets-with-ansible-vault + http://localhost:8000/daily/2022/09/08/keeping-secrets-with-ansible-vault + +
<p>In the last few posts, I've talked about using Ansible for configuring servers and local environments, during both of which, you're likely to have some sensitive or secret values. These could be database credentials within your application and on your server, and your SSH private keys within your local environment.</p> + +<p>Rather than committing these to a code repository in plain text, Ansible includes the <code>ansible-vault</code> command to encrypt values.</p> + +<p>To see this working, run <code>ansible-vault encrypt_string my-secret-password</code>, enter a password, and then you should see something like this:</p> + +<pre><code>!vault | + $ANSIBLE_VAULT;1.1;AES256 + 33353031663366313132333831343930643830346531666564363562666136383838343235646661 + 6336326637333230396133393936646636346230623932650a333035303265383437633032326566 + 38616262653933353033376161633961323666366132633033633933653763373539613434333039 + 6132623630643261300a346438636332613963623231623161626133393464643634663735303664 + 66306433633363643561316362663464646139626533323363663337363361633333 +</code></pre> + +<p>This is the encrypted version of that password, and this could be committed and pushed to a code repository.</p> + +<p>You can use it within a playbook, and you'll be prompted to re-enter the password so that Ansible can decrypt and use it.</p> + +<p>Rather than a single string, you could have a file of variables that you want to encrypt. You can do this by running <code>ansible-vault encrypt vault.yml</code> and include it as before. Again, you'll be prompted by Ansible so that it can decrypt and use the values.</p> + +<p>For an example of how I'm using Ansible Vault, see <a href="https://github.com/opdavies/dransible/tree/986ba5097d62ff4cd0e637d40181bab2c4417f2e/tools/ansible">the Dransible repository</a> on GitHub or my <a href="http://localhost:8000/presentations/deploying-php-ansible-ansistrano"> Deploying PHP applications with Ansible, Ansible Vault and Ansistrano</a> talk.</p> + +<hr /> + +<p>Want to learn more about how I use Ansible? <a href="http://localhost:8000/ansible-course">Register for my upcoming free email course</a>.</p> +
+
+ Thu, 08 Sep 2022 00:00:00 GMT +
+ + My Tailwind CSS origin story + /daily/2022/09/07/my-tailwind-css-origin-story + http://localhost:8000/daily/2022/09/07/my-tailwind-css-origin-story + +
<p>Tomorrow night, I'm attending one of Simon Vrachliotis (simonswiss)'s Pro Tailwind workshops, so I thought that it would be a good time, as Simon has done himself recently on the Navbar podcast, to describe how I started using Tailwind CSS.</p> + +<p>I remember watching a lot of Adam Wathan's live streams on YouTube before Tailwind CSS, and I remember when he started a new project - a SaaS product called KiteTail.</p> + +<p>It was a Laravel and Vue.js project, and although I'm not a Laravel Developer primarily, I got a lot of other information from Adam's streams about automated testing, test-driven development, and Vue.js as I was learning Vue at the time.</p> + +<p>One of the episodes was about styling a card component using some styles that Adam was copying between projects - which would eventually be the starting point for Tailwind CSS.</p> + +<p>In fact, I think I watched some of the episode and stopped as I was happy with the Sass and BEM or SMACSS approach that I was using at the time, and didn't initially see the value of the utility CSS approach that I was seeing for the first time (everyone has a similar reaction initially).</p> + +<p>After a while, I did re-visit it but because Tailwind CSS wasn't released as it's own project yet, I (like Simon) started to experiment with Tachyons - another utility CSS library.</p> + +<p>I rebuilt a particularly tricky component that I'd just finished working on and had caused me some issues, and managed to re-do it in only a few minutes.</p> + +<p>I started to use Tachyons on some personal and client projects as a layer on other frameworks like Bootstrap and Bulma, and later moved on to Tailwind CSS once it has been released.</p> + +<p>I was working in this way on a project when I released that I could use Tailwind for all of the styling instead of just adding small sprinklings of utilities here and there. I refactored everything and removed the other framework that I'd been using - leaving just Tailwind CSS.</p> + +<p>With the exception of some legacy projects, now I use Tailwind CSS exclusively and have used it for a number of projects. I've given lunch and learn sessions to teams that I've worked on, <a href="http://localhost:8000/presentations/taking-flight-tailwind-css">presented a Tailwind CSS talk</a> at a number of PHP, Drupal, WordPress, and JavaScript events, and maintain <a href="https://www.drupal.org/project/tailwindcss">a starter-kit theme</a> for using Tailwind in custom Drupal themes.</p> + +<p>I've also rebuilt a <a href="http://localhost:8000/blog/uis-ive-rebuilt-tailwind-css">number of existing sites</a> as examples and written some <a href="http://localhost:8000/blog/tags/tailwind-css">Tailwind CSS related blog posts</a>.</p> + +<p>I'm looking forward to attending Simon's workshop tomorrow and quickly putting that knowledge to use in the next phase of a project that I'm currently working on.</p> +
+
+ Wed, 07 Sep 2022 00:00:00 GMT +
+ + Deploying applications with Ansible + /daily/2022/09/06/deploying-applications-with-ansible + http://localhost:8000/daily/2022/09/06/deploying-applications-with-ansible + +
<p>The last few days' emails have been about using Ansible to create and configure infrastructure, but it can also be used to deploy application code.</p> + +<p>The simplest way being that an artifact is built locally - e.g. a directory of static HTML pages from a static site generator - and uploaded onto the server, and for this you could use Ansible's <code>synchronize</code> module.</p> + +<p>It's a wrapper around the <code>rsync</code> command and makes it as simple as specifying <code>src</code> and <code>dest</code> values for the local and remote paths.</p> + +<p>For more complicated deployments, I like to use a tool called Ansistrano - an Ansible port of a deployment tool called Capistrano.</p> + +<p>It creates a new directory for each release and updates a <code>current</code> symlink to identify and serve the current release, and can share files and directories between releases.</p> + +<p>As well as being able to configure settings such as the deployment strategy, how many old releases to keep, and even the directory and symlink names, there are a number of hooks that you can listen for an add your own steps as playbooks so you can install dependencies, generate assets, run migrations, or rebuild a cache as part of each deployment.</p> + +<p>If you're running your applications in Docker, you could use Ansible to pull the latest images and restart your applications.</p> + +<p>For more information and examples, I've given a talk on Ansible at various PHP events, which covers some Ansible basics before moving on to <a href="http://localhost:8000/talks/deploying-php-ansible-ansistrano">deploying applications with Ansistrano</a>.</p> + +<hr /> + +<p>Want to learn more about how I use Ansible? <a href="http://localhost:8000/ansible-course">Register for my upcoming free email course</a>.</p> +
+
+ Tue, 06 Sep 2022 00:00:00 GMT +
+ + Using Ansible for local environment configuration + /daily/2022/09/05/using-ansible-for-local-configuration + http://localhost:8000/daily/2022/09/05/using-ansible-for-local-configuration + +
<p>As well as <a href="http://localhost:8000/daily/2022/09/04/using-ansible-for-server-configuration">configuring servers</a>, you can use Ansible to configure your own local machine and development environment.</p> + +<p>The change that you need to make is within the <code>hosts.ini</code> file:</p> + +<pre><code>127.0.0.1 ansible_connection=local +</code></pre> + +<p>Instead of the server's IP address or hostname, use the localhost IP address and set <code>ansible_connection</code> to <code>local</code> to tell Ansible to run locally instead of using an SSH connection.</p> + +<p>Another way to do this is to set <code>hosts: 127.0.0.1</code> and <code>connection: true</code> in your playbook.</p> + +<p>Once this is done, you can run tasks, roles, and collections to automate tasks such as installing software, adding your SSH keys, configuring your project directories, and anything else that you need to do.</p> + +<p>For an example of this, you can see <a href="https://github.com/opdavies/dotfiles">my dotfiles repository on GitHub</a>.</p> + +<hr /> + +<p>Want to learn more about how I use Ansible? <a href="http://localhost:8000/ansible-course">Register for my upcoming free email course</a>.</p> +
+
+ Mon, 05 Sep 2022 00:00:00 GMT +
+ + Using Ansible for server configuration + /daily/2022/09/04/using-ansible-for-server-configuration + http://localhost:8000/daily/2022/09/04/using-ansible-for-server-configuration + +
<p><a href="http://localhost:8000/archives/2022/09/03/creating-infrastructure-with-ansible">In yesterday's email</a>, I described how to set up a blank server with Ansible.</p> + +<p>Now that we've done that, it needs to be configured.</p> + +<p>Once the server’s IP address or hostname has been added to a <code>hosts.ini</code> file, you can run ad-hoc commands against it - such as <code>ansible all -i hosts.ini -m ping</code> to run Ansible's <code>ping</code> module on all of the hosts in your inventory and check that you can connect to them.</p> + +<p>Another useful one that you can use is the <code>shell</code> module, that runs ad-hoc run commands on each host. If you need to check the uptime of each of your servers, run <code>ansible all -i hosts.ini -m shell -a uptime</code>. You can replace the last argument with any other shell command that you need to run, like <code>df</code> or <code>free</code>.</p> + +<p>Running commands in this way is great for getting started, for routine maintenance, or an emergency free disk space check, but for more complex tasks like configuration management, using playbooks is the better option. They are YAML files that contain lists of tasks that Ansible will run through and execute in order.</p> + +<p>If you have a group of related tasks, such as for installing a piece of software, then you can combine them into roles. In fact, Ansible Galaxy has thousands of pre-built collections and roles that you can download, include in your playbooks, configure, and run.</p> + +<p>Very quickly, you can get a full stack installed and configured - ready to serve your application.</p> + +<hr /> + +<p>Want to learn more about how I use Ansible? <a href="http://localhost:8000/ansible-course">Register for my upcoming free email course</a>.</p> +
+
+ Sun, 04 Sep 2022 00:00:00 GMT +
+ + Creating infrastructure with Ansible + /dailys/2022/09/03/creating-infrastructure-with-ansible + http://localhost:8000/dailys/2022/09/03/creating-infrastructure-with-ansible + +
<p>Let's start at the beginning.</p> + +<p>If we want to automate our infrastructure then we first need to create it. This could be done manually or we can automate it.</p> + +<p>Popular tools for this include Terraform and Pulumi, but Ansible also includes modules to interface with hosting providers such as Amazon Web Services, Microsoft Azure, DigitalOcean, and Linode.</p> + +<p>By using one of these tools, you can programatically provision a new, blank server that is ready for you to be configered.</p> + +<p>For example, to <a href="https://docs.ansible.com/ansible/latest/collections/community/digitalocean/digital_ocean_module.htm">create a DigitalOcean droplet</a>:</p> + +<pre><code class="language-yaml">--- +- community.digitalocean.digital_ocean_droplet: + image: ubuntu-20-04-x64 + name: mydroplet + oauth_token: "..." + region: sfo3 + size: s-1vcpu-1gb + ssh_keys: [ .... ] + state: present + wait_timeout: 500 + register: my_droplet +</code></pre> + +<p>Running this playbook will create a new Droplet with the specified name, size, and operating system, and within the specified region.</p> + +<p>If you needed to create a separate database server or another server for a new environment, then the file can be updated and re-run.</p> + +<p><a href="https://docs.ansible.com/ansible/latest/collections/amazon/aws/ec2_instance_module.html#ansible-collections-amazon-aws-ec2-instance-module">Creating an Amazon EC2 instance</a> looks very similar:</p> + +<pre><code class="language-yaml">--- +- amazon.aws.ec2_instance: + image_id: ami-123456 + instance_type: c5.large + key_name: "prod-ssh-key" + name: "public-compute-instance" + network: + assign_public_ip: true + security_group: default + vpc_subnet_id: subnet-5ca1ab1e +</code></pre> + +<p>This doesn't apply just to servers - you can also use Ansible to create security groups and S3 buckets, manage SSH keys, firewalls, and load balancers.</p> + +<p>Once we have our infrastructure in place, we can start using Ansible to set and manage its configuration, which we'll do in tomorrow's email.</p> + +<hr /> + +<p>Want to learn more about how I use Ansible? <a href="http://localhost:8000/ansible-course">Register for my upcoming free email course</a>.</p> +
+
+ Sat, 03 Sep 2022 00:00:00 GMT +
+ + Automating all the things with Ansible + /daily/2022/09/02/automating-all-the-things-with-ansible + http://localhost:8000/daily/2022/09/02/automating-all-the-things-with-ansible + +
<p>Ansible is a tool for automating IT tasks. It's one of my preferred tools to use, and one that I've written about and <a href="http://localhost:8000/talks/deploying-php-ansible-ansistrano">presented talks on</a> previously.</p> + +<p>It's typically thought of as a tool for managing configuration on servers. For example. you have a new VPS that you want to use as a web server, so it needs Nginx, MySQL, PHP, etc to be installed - or whatever your application uses. You define the desired state and run Ansible, which will perform whatever tasks are needed to get to that state.</p> + +<p>Ansible though does include modules for interacting with services like Amazon AWS and DigitalOcean to create the servers and resources, and not just configure them.</p> + +<p>It also doesn't just work on servers. I use Ansible to configure my local development environment, to ensure that dependencies and tools are installed, and requirements like my SSH keys and configuration are present and correct.</p> + +<p>Lastly, I use Ansible to deploy application code onto servers and automatically run any required steps, ensuring that deployments are simple, robust and repeatable.</p> + +<p>In the next few emails, I'll explain how I've been able to utilise Ansible for each of these situations.</p> + +<hr /> + +<p>Want to learn more about how I use Ansible? <a href="http://localhost:8000/ansible-course">Register for my upcoming free email course</a>.</p> +
+
+ Fri, 02 Sep 2022 00:00:00 GMT +
+ + Conventional commits and CHANGELOGs + /daily/2022/09/01/conventional-commits-changelogs + http://localhost:8000/daily/2022/09/01/conventional-commits-changelogs + +
<p>One of the things that I've done since joining my current team is to implement a standard approach for our commit messages.</p> + +<p>We're using the <a href="https://www.conventionalcommits.org">Conventional Commits specification</a>, which gives some additional rules to follow when writing commit messages.</p> + +<p>For example:</p> + +<pre><code>build(deps): update Drupal to 9.4.5 + +Updated Drupal's `drupal/core-*` packages to 9.4.5. + +See https://www.drupal.org/project/drupal/releases/9.4.5. + +Refs: #123 +</code></pre> + +<p>We can see that this is a <code>build</code> task that relates to our project dependencies, in this example, we're updating Drupal core. We can also see this in the subject line.</p> + +<p>In the commit body, I add as much information as possible to do with the change and include any relevant links, just in case I need to refer to them again, and the list the names of anyone else who worked with me. I also typically include any ticket numbers or links in the commit footer.</p> + +<p>So far, I've mostly used the <code>build</code>, <code>chore</code>, <code>ci</code>, <code>docs</code> and <code>refactor</code> commit types, which are types that are recommended and used by <a href="https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#-commit-message-guidelines">the Angular convention</a>.</p> + +<p>Following this standard means that it's very easy to look at the Git log and see what type of changes are going to be included within a release and, if you're using scopes, which part of the application are affected.</p> + +<p>Conventional commits also works nicely with something else that we've introduced, which is a CHANGELOG file.</p> + +<p>There are tools that can generate and update CHANGELOGs automatically from conventional commits, but so far, we've been following the <a href="https://keepachangelog.com">Keep a Changelog</a> format.</p> + +<p>It's easy to match the commits to the <code>Added</code>, <code>Changed</code> or <code>Fixed</code> types, and although it needs to be updated manually, it's easy to add to the <code>Unreleased</code> section of the file and re-organise everything within the appropriate headings as needed as part of a release.</p> + +<p>What I like about this format is that it's more human-friendly and gives a higher level overview of the changes rather than a reformatted Git log.</p> + +<p>As we do trunk-based development and continuous integration on our projects, there can be numerous commits related to the same change, so I'd rather only see a single line in the CHANGELOG for each change. This also makes it easier to share the CHANGELOG file with others, and we can still view and grep the Git log to see the individual commits if we need to.</p> +
+
+ Thu, 01 Sep 2022 00:00:00 GMT +
+ + To monorepo, or not to monorepo? + /daily/2022/08/31/monorepo-or-not + http://localhost:8000/daily/2022/08/31/monorepo-or-not + +
<p>I listened to a podcast episode recently which talked about monorepos - i.e. code repositories that contain multiple project codebases rather than a single repository for each codebase - and this got me thinking about whether I should be using these more.</p> + +<p>It's something that I've been trialling recently in my <a href="https://github.com/opdavies/docker-examples">Docker examples</a> and <a href="https://github.com/OliverDaviesLtd/docker-images">Docker images</a> repositories, where one repository contains and builds multiple Docker images.</p> + +<p>I'm not suggesting that I put all of my client projects into one repository, but at least combining the different parts of the same project into the same repository.</p> + +<p>For example, I'm working for one client on their current Drupal 7 websites whilst developing the new Drupal 9 versions, which are currently in two separate repositories. I'm also developing an embeddable Vue.js application as part of the Drupal 9 website, and using Fractal as a component library. These are also in their own repositories.</p> + +<p>Using a monorepo approach, all of these projects would be in the same repository.</p> + +<p>I can see advantages to being able to see cross-project changes in the same place - such as an API change in Drupal that needs a update to be made in Vue.js, or vice-versa - rather than needing to look at separate repositories. This could also make versioning easier as everything will be stored and tagged inside the same repository.</p> + +<p>Each project has it's own CI pipeline, so it would require some changes where I set a specific pipeline to run only when a directory is changed.</p> + +<p>I see how deployments may be tricker if I need to push an update within a directory to another Git repository, which makes me wonder if I'll need to look into using subtree splits to create separate deployment repositories - similar to how the Symfony project has one main repository and then each component split into its own repository.</p> + +<p>I'll keep trialling it in my open-source projects and maybe test it with some client projects, but if you have experience with monorepos that you'd like to share, then please reply to this email - I'd love to hear about it.</p> +
+
+ Wed, 31 Aug 2022 00:00:00 GMT +
+ + Why I don't only use Drupal + /daily/2022/08/30/why-dont-only-use-drupal + http://localhost:8000/daily/2022/08/30/why-dont-only-use-drupal + +
<p>Yesterday, <a href="http://localhost:8000/daily/2022/08/29/why-like-drupal">I shared some of the reasons</a> why I like Drupal and why I use it for the majority of my projects. But, as I said, I don't use it exclusively and for some projects I used various different tools.</p> + +<p>Essentially, I always try to recommend and use the best tool for the job.</p> + +<p>I previously interviewed for a job and was asked to complete a coding test. The role was mostly Drupal-focussed, but as the test asked for a command-line application, I completed it using Symfony and Symfony Console, and was able to discuss why I'd made that decision. In my opinion, it was the best choice based on the requirements.</p> + +<p>This is the same approach that I use when making recommendations for a new project.</p> + +<p>I've delivered projects using other tools like the Symfony framework or a static site generator, as long as it fitted the requirements.</p> + +<p>If there's a SaaS solution that can be used instead, or an off-the-shelf tool that can be integrated instead of writing a custom solution, then that should be evaluated.</p> + +<p>There may be other constraints like budgets or deadlines to consider - maybe something can be delivered faster or cheaper using a particular technology, even if it's not the final solution.</p> + +<p>There are situations though where a tool may be the best choice even though it's not the ideal fit based purely on the technical requirements. Maybe the client is already familiar with publishing content in Drupal, or an in-house development team is used to working with a certain tool or language. In that case, those things should be considered too.</p> + +<p>Also, for me, having a chance to evaluate other technologies and explore what's happening outside of the Drupal ecosystem is a good opportunity. A lot of what I've learned about automated testing, for example, is from the wider PHP and JavaScript communities, as well as tools like <a href="http://localhost:8000/presentations/taking-flight-with-tailwind-css">Tailwind CSS</a> and <a href="http://localhost:8000//presentations/using-illuminate-collections-outside-laravel">Illuminate Collections</a> that I've been able to bring back into my other Drupal projects.</p> +
+
+ Tue, 30 Aug 2022 00:00:00 GMT +
+ + Why I like Drupal + /daily/2022/08/29/why-like-drupal + http://localhost:8000/daily/2022/08/29/why-like-drupal + +
<p>As I said in yesterday's email, I developed my first website project on Drupal. It allowed me to take a static HTML and CSS website and convert it into something that was much easier and quicker for me to update, and allowed me to create more users with permissions to do those tasks too.</p> + +<p>I worked on various Drupal projects, and my first full-time job was on an in-house team where we maintained and enhanced a Drupal 6 website.</p> + +<p>I've since used Drupal for projects of all shapes and sizes with different levels of complexity. Everything from a simple brochure website to large and complex, multilingual, API-driven projects.</p> + +<p>I've been able to build eCommerce websites with Drupal using Ubercart and Drupal Commerce. I've built traditional stores where customers purchase physical products, a photography competition website with custom judging functionality, a site for purchasing commercial and residential property and land searches, and a fully-fledged events booking and management platform.</p> + +<p>Whatever the size and complexity of the project, Drupal is flexible enough to fit it.</p> + +<p>I've loved some of the ecosystem improvements within the last few years. Moving to object-orientated code by default, integrating code from other projects like Symfony, shipping new features every six months as part of the new release cycle, and embracing tools like Composer, PHPStan and Rector.</p> + +<p>I also love being part of the Drupal community. Collaborating on tasks, speaking on Slack, and attending events like DrupalCon where I've been lucky enough to attend, speak and mentor.</p> + +<p>Although Drupal is my specialty and the tool that I've used the most, I don't use it exclusively. I'll talk more about this in tomorrow's email.</p> +
+
+ Mon, 29 Aug 2022 00:00:00 GMT +
+ + How I started programming + /daily/2022-08-28/how-started-programming + http://localhost:8000/daily/2022-08-28/how-started-programming + +
<p>In 2007, I was working in the IT sector in a Desktop Support role but hadn't done any coding professionally.</p> + +<p>In my spare time, I was a black belt in Tae Kwon-Do and enjoyed training at a few different schools. Because of my IT experience, I was asked if I could create a website for one of the schools - somewhere that we could post information and class times for new starters, as well as news articles and competition results.</p> + +<p>This would be my introduction to programming.</p> + +<p>I started learning what I needed to know, starting with HTML and CSS - experimenting with a template that I found online and was able to tweak to match the school's colours.</p> + +<p>I was able to complete the first version of the website with static HTML pages and CSS but had to manually create a new HTML page for every new news article and edit existing pages manually.</p> + +<p>I wanted to make it more dynamic, and started to learn about PHP and MySQL from video courses and online forums.</p> + +<p>After posting a question about some PHP code that I'd written, someone suggested that I look at content management systems - namely Drupal, which was used for that forum (I have <a href="https://twitter.com/opdavies/status/1185456825103241216">a screenshot of the reply</a>). This was a new concept to me as until that point, I'd written everything so far myself whilst learning it.</p> + +<p>I remember evaluating Drupal alongside some others - rebuilding the same website a few different times, but stuck with Drupal and relaunched it on Drupal 6 and a custom theme that I'd created from the original templates.</p> + +<p>I signed up for a Drupal.org account, started to do some freelance work for a local web design agency, and built a new website for a local cattery.</p> + +<p>I started blogging, attending meetups, and when an opportunity to switch careers to software development came along, I applied for and got the job.</p> + +<p>That job was also using Drupal and, in another email, I'll write more about why I still like and use Drupal years later.</p> +
+
+ Sun, 28 Aug 2022 00:00:00 GMT +
+ + Giving back + /daily/2022/08/27/giving-back + http://localhost:8000/daily/2022/08/27/giving-back + +
<p>Today, I've been at an event run by a local animal rescue charity. It's one that we attend often as my children like to enter the dog show, but this year, I've also sponsored one of the categories.</p> + +<p>As well as organising the PHP South Wales user group, I'm also now a sponsor - donating books and elePHPant plushies for raffle prizes and paying the group's Meetup.com subscription costs.</p> + +<p>Giving back and supporting open-source maintainers and content creators is a big priority of mine. If I use some open-source software or find that someone's Twitch or YouTube channel is useful, if that person or organisation is on GitHub or Patron, then I'll sponsor them, or I'll subscribe to their channel.</p> + +<p>If I find a useful blog post or video, I'll add a comment or link to it on Twitter, thanking them and letting them know that it helped me.</p> + +<p>Especially if it's something that I've used within my projects, it makes sense to support it and it's maintainers, so that they keep working on and improving the software, continue streaming, and keep writing blog posts and recording videos for me to learn from.</p> +
+
+ Sat, 27 Aug 2022 00:00:00 GMT +
+ + Always be learning + /daily/2022/08/26/always-be-learning + http://localhost:8000/daily/2022/08/26/always-be-learning + +
<p>I've been a Developer for 15 years and one thing that I've always focussed on is to always keep learning.</p> + +<p>From starting as a self-taught Developer, initially learning HTML and CSS, to later learning PHP and Drupal as well as other languages, frameworks and tools.</p> + +<p>For the last couple of days, I've been experimenting with Next.js - a React-based web framework. I hadn't used React before and have typically reached for Vue.js or sometimes Alpine.js based on what I needed to do. However, I'm always looking for opportunities to learn and implement new things, and see how I can use them in any of my projects.</p> + +<p>This afternoon, I started a new Next.js and TypeScript project, and refactored a small codebase that used a static site generator to create a small number of landing pages from Markdown files.</p> + +<p>It took me a short time to set up a Docker environment for it based on some of my Vue.js projects, ported across the application to recreate the pages, and finally, updated the CI pipeline that generated the static pages and uploaded them to an S3 bucket.</p> + +<p>The end result is the same - the same HTML pages are generated and uploaded - but, for me, trying and experimenting with new things keeps my work interesting and my knowledge fresh, which benefits me as well as my colleagues and clients.</p> + +<p>As I said in a previous email, one of the great things about software development is that there's always something new to learn.</p> +
+
+ Fri, 26 Aug 2022 00:00:00 GMT +
+ + Why I work in Neovim + /daily/2022/08/25/why-i-work-in-neovim + http://localhost:8000/daily/2022/08/25/why-i-work-in-neovim + +
<p>Over a year ago, I posted that I was <a href="http://localhost:8000/blog/going-full-vim">switching to using Neovim full-time</a> for my development work.</p> + +<p>I'd used Vim one file at a time on remote servers, and added Vim plugins in other IDEs and editors, so I was already familiar with a lot of the key bindings and motions before I decided to use it full-time.</p> + +<p>Still, it was tough to begin with, but once I'd learned how to configure Neovim, I also learned that being able to customise and extend it as much as you need to is one of its main advantages compared to other IDEs and code editors.</p> + +<p>TJ DeVries - a Neovim core team member - has recently coined the term "PDE" (a personalised development environment) which, for me, describes Neovim perfectly.</p> + +<p>Currently, I have a fuzzy-finder to quickly open files (as well as many other things), an LSP client to add code intelesense, auto-completion, refactoring tools, custom snippets, and very recently, a database client and a HTTP client.</p> + +<p>Just as important to me, I've found a growing community of other Neovim users who stream on Twitch, post YouTube videos, write blog posts, or publish their dotfiles for others to see and reference.</p> + +<p>I've learned Lua. Not just for my own Neovim configuration, but I recently wrote and open-sourced my own simple plugin.</p> + +<p>Like Git, I enjoy and prefer using tools that I can configure and adapt to my workflow.</p> + +<p>Given Neovim's flexibility and configurability, its expanding feature set both in core and community plugins, and the growing community, I think that Neovim is going to be something that I continue to use and adapt for a long time.</p> +
+
+ Thu, 25 Aug 2022 00:00:00 GMT +
+ + How I've configured Git + /daily/2022/08/24/2022-08-24 + http://localhost:8000/daily/2022/08/24/2022-08-24 + +
<p>After yesterday's post on why I prefer using Git on the command line rather than using a GUI tool, today I thought that I'd post about how I've configured Git.</p> + +<p>First, I rarely ever run the <code>git</code> command - I usually run a <code>g</code> function that I've created within my zsh configuration.</p> + +<p>Rather than being an simple alias, it's a shell function that will run <code>git status -sb</code> to show the current status of the repository if there are no additional arguments. If there are, such as when running <code>g add</code>, then this is executed as a normal Git command. (This is something that I first saw from Thoughtbot, if I remember correctly).</p> + +<h2 id="using-.gitconfig">Using .gitconfig</h2> + +<p>The main part of my configuration is within Git's <code>~/.gitconfig</code> file, where I can configure Git to work how I want.</p> + +<p>For example, I like to avoid merge conflicts, so I always want to use fast-forward merges whilst pulling and also to rebase by default. I can do this by adding <code>ff = only</code> and <code>rebase = true</code> to the <code>[pull]</code> section of my <code>~/.gitconfig</code> file.</p> + +<p>I can do this manually, or running <code>git config --global pull.rebase true</code> will set the option but also update the file automatically.</p> + +<p>Some of the tweaks that I've made are to only allow fast-forward merges by adding <code>merge.ff = only</code>, automatically squash commits when rebasing by setting <code>rebase.autosquash = true</code>, and automatically pruning branches by adding <code>fetch.prune = true</code>.</p> + +<h3 id="simple-aliases">Simple aliases</h3> + +<p>Another way that I configure Git is using aliases, which are also within the <code>~/.gitconfig</code> file.</p> + +<p>For example, if I ran <code>git config --global alias.b "branch"</code>, then running <code>git b</code> would just run <code>git branch</code> which shortens the command and saves some time and keystrokes.</p> + +<p>I have similar one- or two letter "short" aliases for pushing and pulling code, and some that also set some additional arguments such as <code>aa</code> for <code>add --all</code> and <code>worktrees</code> for <code>worktree list</code>.</p> + +<h3 id="more-complicated-aliases">More complicated aliases</h3> + +<p>Aliases can be more complex if needed by prefixing it with a <code>!</code>, meaning that it executes it as a shell command.</p> + +<p>This means that I can have <code>repush = !git pull --rebase &amp;&amp; git push</code> to chain two separate Git commands and combine them into one, and <code>ureset = !git reset --hard $(git upstream)</code> which executes the full command, including another alias as part of it.</p> + +<p>I also have <code>issues = !gh issue list --web</code> and <code>pulls = !gh pr list --web</code> to open the current repository's GitHub issues or pull requests respectively, which can be done as it's not limited to just running <code>git</code> commands.</p> + +<h3 id="custom-functions">Custom functions</h3> + +<p>Finally, if an alias is getting too long or complex, then it can extracted to it's own file.</p> + +<p>Any executable file within your <code>$PATH</code> that starts with <code>git-</code> will automatically become a Git command.</p> + +<p>One example that I have is <a href="https://github.com/opdavies/dotfiles/blob/2b20cd1e59ae3b1fa81074077e855cbdfa02f146/bin/bin/git-cm">git-cm</a> which, similar to the <code>g</code> function`, is a bash script that checks for any arguments passed to it and runs a slightly different command. It achieves the same thing as if it were an alias, but it does make it easier to write and maintain as it's in a separate file.</p> + +<p>These are just some examples. If you want to see my entire configuration, then check out <a href="https://github.com/opdavies/dotfiles/tree/2b20cd1e59ae3b1fa81074077e855cbdfa02f146/roles/git/files">my dotfiles repository on GitHub</a>.</p> + +<p>How have you configured Git for your workflow? Reply to this email and let me know.</p> +
+
+ Wed, 24 Aug 2022 00:00:00 GMT +
+ + Git: GUI or command-line? + /daily/2022/08/23/git-gui-command-line + http://localhost:8000/daily/2022/08/23/git-gui-command-line + +
<p>I’ve been using Git for a long time. My first full-time Developer role in 2010 was working on an in-house team and that project used Git as it’s version control system.</p> + +<p>I remember typing commands into an Ubuntu terminal and trying to wrap my head around the process of adding and staging files, (sometimes) pulling, and then pushing to a remote. I think the remote was a simple bare repository on a server, so there was no UI like there is in GitHub and similar tools today.</p> + +<p>In fact, GitHub only started two years earlier in 2008, and GitLab wasn’t around until 2014.</p> + +<p>Looking back, my introduction to Git as a Junior Developer wasn't easy and I remember starting to get frustrated until it eventually "clicked" and made sense.</p> + +<p>I don't remember if there were GUIs at that time (I remember using gitk but I can't think when), but having a tool like GitHub where I could see the code, branches and commits, would probably have been helpful with my initial learning.</p> + +<p>Whilst working locally, I've tried some of the desktop GUI tools like Sourcetree, gitkraken and Tower, but I always come back to using Git on the command line.</p> + +<p>While a Git GUI tool may make it easier to learn Git initially as a Junior Developer, I'd recommend trying to learn the command line too.</p> + +<p>In my opinion, understanding what’s happening "under the hood" when is important working with a GUI - just in case you find yourself unexpectedly having to use the command line. I’ve seen an error in a Git GUI that suggests running commands in the terminal to debug or fix the issue. If you aren't familiar with the terminal commands or what they do, then I'd expect this to be intimidating and confusing.</p> + +<p>If you're working as part of a team or contributing to an open-source project then the consistency that the command line provides will make it easier when working with colleagues or getting help from project maintainers. You're also learning Git itself rather than a tool that may add it's own terminology or change how Git itself works, also causing confusion.</p> + +<p>There's a lot of Git functionality and concepts that I wouldn't have explored if I wasn't using the command line and relying on a GUI, such as adding and removing code in chunks using patch mode, using bisect to find when a bug was introduced, worktrees for local code organisation, and understanding merging vs rebasing, interactive and non-interactive rebases, and merge commits and fast-forward merges.</p> + +<p>Of course, if you prefer to use a GUI and it works for you, then that's fine. Personally, I like to dig deep when learning tools, to know them inside-out and understand how to use them well, and I think that the time that I've spent learning Git and optimising my workflow paid for itself a long time ago.</p> + +<p>How do you like to use Git? Do you prefer to use the command line or a GUI tool? Reply to this email and let me know.</p> +
+
+ Tue, 23 Aug 2022 00:00:00 GMT +
+ + Being a T-shaped Developer + /daily/2022/08/22/2022-08-22 + http://localhost:8000/daily/2022/08/22/2022-08-22 + +
<p>A blog post appeared on my feed this morning, titled <a href="https://www.nomensa.com/blog/how-to-be-t-shaped">How to be T-Shaped</a>.</p> + +<p>"T-shaped Developers" is a term that I've also used before. Being T-shaped means that you have a deep knowledge in one particular area and a breadth of knowledge in other areas.</p> + +<p>I would say that I'm T-shaped.</p> + +<p>My main area of knowledge is PHP and Drupal software development - they're the programming language and content management system that I've used throughout most of my career so far, since I started in 2007.</p> + +<p>As I worked on my own personal and client projects, I needed to learn more complementary skills.</p> + +<p>I needed to learn how to style websites and build themes so I started to learn front-end development with CSS and frameworks like Bootstrap, Bulma and Tailwind CSS, and JavaScript frameworks like Angular, Vue.js and Alpine, as well as TypeScript.</p> + +<p>I also needed to host these projects somewhere, which introduced me to Linux servers, virtual hosts, (S)FTP and SSL, web servers like Apache, Nginx and Caddy, MySQL and MariaDB databases, and as projects got more complicated, I started using tools like Vagrant and Puppet, Ansible, and Docker for configuring environments to work in.</p> + +<p>I don't use Drupal for every project. I've used static site generators and frameworks like Symfony based on the project's requirements, and have projects that use several different technologies at the same time.</p> + +<p>The main benefits are that I can either deliver entire projects or projects with more complicated architectures, or work across different teams - mentoring a team of Front-End Developers in Drupal theming, or working with System Administrators to start hosting PHP applications. Having these additional skills is definitely valuable to employers and clients.</p> + +<p>I've said that one of the best and worst things about software development is that there's always something new to learn!</p> +
+
+ Mon, 22 Aug 2022 00:00:00 GMT +
+ + Why I use Docker and Docker Compose for my projects + /daily/2022/08/21/2022-08-21 + http://localhost:8000/daily/2022/08/21/2022-08-21 + +
<p>For the last few years, I've used Docker and Docker Compose exclusively on all of my projects. When I start a new project or onboard a new client, usually one of the first things that I need to do is get an application running in Docker so that I can work on it.</p> + +<!-- Since I started programming, I've used a number of different local environments. Starting with WAMP and XAMPP on Windows, MAMP on macOS, Laravel Valet, the Symfony local server, and various open-source Docker-based solutions. --> + +<p>I've inherited projects with no environment configuration or documentation at all and I need to start from scratch to get it running. Ideally, each project would have it's local environment configuration in the same Git repository as the application code.</p> + +<p>For my own projects, these days I prefer to use Docker and Docker Compose - creating my own Dockerfiles for each project so that the correct dependencies are present and the required build steps are executed, as well as acting as documentation.</p> + +<p>It's lean as the environment is built specifically for each project, and easy to configure using Docker and Docker Compose directly using native patterns such as override files, environment variables and interpolation, and multi-stage builds.</p> + +<p>The configuration can be as simple or complicated as it needs to be for each project rather than using "a one size fits all" approach. If I'm working with Drupal, Fractal, Vue.js, a PHP library, a Go command line tool, or something else entirely, I can use the most appropriate starting point.</p> + +<p>As well as local developments, it's easy to use Docker and Docker Compose in CI environments with tools like GitHub Actions and Bitbucket Pipelines. They will either be present by default or will be easy to install, and it's simple to run a <code>docker-compose build</code> or <code>docker-compose run</code> command within a pipeline to check that the project builds correctly and to execute tasks such as automated tests or static analysis.</p> + +<p>As well as using it for projects, Docker has been useful for me in other situations where I need to run small tools such as rst2pdf for generating presentation slides, and ADR Tools for working with architectural decision records.</p> + +<p>For some situations like an open-source contribution day, using an off-the-shelf solution would probably be a better option, and some teams will have their own preferences, but I prefer to use Docker and Docker Compose when I can.</p> + +<p>Personally, I like to invest time into learning tools that provide reusable knowledge, such as Docker and Docker Compose. I'd prefer to spend time learning something, even if it may take longer compared to other tools, if it's going to give me a return on that investment in the medium- to long-term.</p> + +<p>For some examples of how I work with Docker and Docker Compose, you can <a href="https://github.com/opdavies?tab=repositories&amp;q=docker">see my public GitHub repositories</a> and how things are put together there.</p> +
+
+ Sun, 21 Aug 2022 00:00:00 GMT +
+ + A return to offline meetups and conferences + /daily/2022/08/20/return-to-offline-meetups-conferences + http://localhost:8000/daily/2022/08/20/return-to-offline-meetups-conferences + +
<p>Yesterday, I dusted off our Meetup page and posted our next <a href="https://www.meetup.com/php-south-wales">PHP South Wales meetup</a> event.</p> + +<p>We've had online meetups and code practice sessions throughout the pandemic and during lockdowns, but this will be our first offline/in person/IRL meetup since February 2020.</p> + +<p>As well as organising our online meetups during COVID, I attended a lot of other online events, <a href="http://localhost:8000/blog/speaking-remotely-during-covid-19">usually giving various talks or workshops</a>, and whilst they were good for a while, I eventually started to get burned out by them.</p> + +<p>I've been an organiser of various meetups and conferences for a long time, and attending events has been a very large part of my career so far - providing opportunities to learn, to network and socialise with other attendees, and pass knowledge on through talks, workshops and mentoring.</p> + +<p>It's been great to see some offline events returning, from local user groups to conferences such as DevOpsDays, DrupalCon and SymfonyLive.</p> + +<p>I've given one talk this year - a lot less than this time last year - but it was in front of an audience instead of a screen, and whilst it seemed strange, I'm sure that it's something that will feel normal again in time.</p> + +<p>I'm thinking of attending a conference next month, I've submitted some talk suggestions to some other conferences which I'm waiting to hear from, and am considering travelling to some of the other UK user groups as they restart - some of which I joined or spoke at online but it would be great to meet them in person.</p> + +<p>For next week, I'll be glad to have PHP South Wales events running again and to see our community back together in person, and then do it again and start getting ready for next month's event.</p> +
+
+ Sat, 20 Aug 2022 00:00:00 GMT +
+ + Pair programming or code reviews? + /daily/2022/08/19/pair-programming-or-code-reviews + http://localhost:8000/daily/2022/08/19/pair-programming-or-code-reviews + +
<p>It's been almost a year and a half since I last pushed a feature branch, created a pull request, and waited for it to be reviewed and (hopefully) merged and deployed.</p> + +<p>On the majority of teams and projects that I've worked on, this was how things were done.</p> + +<p>Tasks would be worked on in separate branches which would need to be reviewed by one or more other Developers before being merged.</p> + +<p>I'm an advocate for continuous integration and trunk-based development (both I plan on writing about in more depth) in which there is no formal code review step, but instead, I encourage people to pair program as much as possible.</p> + +<p>Pair or mob (group) programming, for me, is like a real-time code review where you can discuss and make changes instantly, rather than waiting until the work is complete and someone reviewing it after the fact. If a bug is spotted as you're typing it or something could be named better, you can update it there and then.</p> + +<p>But there are other benefits too.</p> + +<p>Instead of one person writing some code, and others reviewing it after the fact, multiple people have written it together and the knowledge is shared amongst those people.</p> + +<p>As you've worked together, you don't need to ask or wait for someone to set time aside to review your changes, so it's quicker for them to be merged and deployed. It's already been reviewed, so as long as any automated checks pass, the code can be merged.</p> + +<p>I've worked in pairs where I've taught someone how to write automated tests and do test-driven development, which I suspect wouldn't have been quite the same if they'd just read the finished code afterwards.</p> + +<p>Of course, some Developers and teams will prefer the typical code review process - it's worked well for me and projects that I've worked on in the past - but personally, I like the speed, agility, mentoring and learning, and social benefits that I can get more easily from pair programming.</p> +
+
+ Fri, 19 Aug 2022 00:00:00 GMT +
+ + 'Talking Drupal' and Tailwind CSS + /daily/2022/08/18/talking-drupal-tailwind-css + http://localhost:8000/daily/2022/08/18/talking-drupal-tailwind-css + +
<p>In March, I was a guest again on the Talking Drupal podcast. This time I was talking about utility CSS and, in particular, the Tailwind CSS framework.</p> + +<p>I've become a big fan of this approach to styling websites and was an early adopter of Tailwind, and have released <a href="https://www.drupal.org/project/tailwindcss">a starter-kit theme</a> for building custom Drupal themes with Tailwind CSS based on what I was using for my own client projects.</p> + +<h2 id="rebuilding-talking-drupal-with-tailwind">Rebuilding Talking Drupal with Tailwind</h2> + +<p>Usually when I give a Tailwind CSS talk at a conference or user group, I rebuild something familiar - maybe a page of their website - as an example and to explain some of the concepts and anything that was particularly interesting during the build. (I have <a href="http://localhost:8000/blog/uis-ive-rebuilt-tailwind-css">a blog post</a> that lists the ones that I've done before).</p> + +<p>After this podcast episode, I built a <a href="https://talking-drupal-tailwindcss.oliverdavies.uk">Tailwind version of the Talking Drupal homepage</a>.</p> + +<p>But, given that Drupal uses Twig and that we'd talked about best practices around using a templating engine to use loops and extract components to organise code and reduce duplication, I definitely wanted to build this example using Twig templates.</p> + +<p>Drupal seemed like too much for a single page example, and Symfony or Sculpin could distract from the main focus of the demo, so I decided to start from scratch with an empty PHP file and add Twig and any other dependencies myself.</p> + +<p><a href="https://github.com/opdavies/talking-drupal-tailwindcss">The code repository</a> is publicly viewable on my GitHub profile so people can look at the code and see some of the things that I talked about during the episode in practice and not just the resulting HTML a browser.</p> + +<p>You can <a href="https://talkingdrupal.com/338">listen to the episode</a>, and if you want any more information, the slides and video from my <a href="http://localhost:8000/talks/taking-flight-with-tailwind-css">Taking Flight with Tailwind CSS talk</a> are on my website.</p> +
+
+ Thu, 18 Aug 2022 00:00:00 GMT +
+ + One more "run" command, for Git worktrees + /daily/2022/08/17/one-more-run-command-git-worktrees + http://localhost:8000/daily/2022/08/17/one-more-run-command-git-worktrees + +
<p>Here's another <code>run</code> file example, this time relating to Git worktrees...</p> + +<p>One project that I work on is a multilingual Drupal application that needs to work in both English and Welsh. As I'm cloning a fresh version today, I'm doing it as a bare repository so I can use worktrees.</p> + +<p>To work on it locally, just like in production, I need to use a different URL for each language so that Drupal can identify it and load the correct content and configuration.</p> + +<p>For fixed environments like production or staging, the URLs are set in configuration files, but for ad-hoc environments such as local worktrees, I thought that the best approach was to override them as needed per worktree using Drush (a Drupal CLI tool).</p> + +<p>I could do this manually each time or I could automate it in a <code>run</code> command. :)</p> + +<p>Here's the function that I came up with:</p> + +<pre><code class="bash">function drupal:set-urls-for-worktree { + # Set the site URLs based on the current Git worktree name. + local worktree_name="$(basename $PWD)" + + local cy_url="cy-projectname-${worktree_name}.docker.localhost" + local en_url="projectname-${worktree_name}.docker.localhost" + + # Update the URLs. + drush config:set language.negotiation url.domains.cy -y $cy_url + drush config:set language.negotiation url.domains.en -y $en_url + + # Display the domains configuration to ensure that they were set correctly. + drush config:get language.negotiation url.domains +} +</code></pre> + +<p>It builds the worktree URL for each language based on the directory name, executes the configuration change, and finally displays the updated configuration so I can confirm that it's been set correctly.</p> + +<p>This is a good example of why I like using <code>run</code> files and how I use them to automate and simplify parts of my workflow.</p> +
+
+ Wed, 17 Aug 2022 00:00:00 GMT +
+ + What are Git hooks and why are they useful? + /daily/2022/08/16/what-are-git-hooks-why-are-they-useful + http://localhost:8000/daily/2022/08/16/what-are-git-hooks-why-are-they-useful + +
<p>In yesterday's email, I mentioned Git hooks but didn't go into any detail. So, what are they?</p> + +<p>Git hooks are Bash scripts that you add to your repository that are executed when certain events happen, such as before a commit is made or before a push to a remote.</p> + +<p>By default, the script files need to be within the <code>.git/hooks</code> directory, have executable permissions, and be named to exactly match the name of the hook - e.g. <code>pre-push</code> - with no file extension.</p> + +<p>If it returns an error exit code then the process is stopped and the action doesn't complete.</p> + +<p>This is useful if, for example, you or your team use a specified format for commit messages and you want to prevent the commit if the message doesn't match the requirements.</p> + +<p>But, the main benefit that I get from Git hooks if from the <code>pre-push</code> hook.</p> + +<p>I use it to run a subset of the checks that are run within project's CI pipeline to limit failures in the CI tool and fix simple errors before I push the code.</p> + +<p>Typically, these are the quicker tasks such as ensuring the Docker image builds, running linting and static analysis, validating lock files, and some of the automated tests if they don't take too long to run.</p> + +<p>If a build is going to fail because of something simply like a linting error, then I'd rather find that out and fix it locally rather than waiting for a CI tool to fail.</p> + +<p>Also, if you're utilising trunk-based development and continuous integration where team members are pushing changes regularly, then you want to keep the pipeline in a passing, deployable state as much as possible and prevent disruption.</p> + +<p>But what have Git hooks got to do with the "run" file?</p> + +<p>Firstly, I like to keep the scripts as minimal as possible and move the majority of the code into functions within the <code>run</code> file. This means that the scripts are only responsible for running functions like <code>./run test:commit</code> and returning the appropriate exit code, but also means that it's easy to iterate and test them locally without making fake commits or trying to push them to your actual remote repository (and hoping that they don't get pushed).</p> + +<p>Secondly, I like to simplify the setup of Git hooks with their own functions.</p> + +<p>For security reasons, the <code>.git/hooks</code> directory cannot be committed and pushed to your remote so they need to be enabled per user within their own clone of the repository.</p> + +<p>A common workaround is to put the scripts in a directory like <code>.githooks</code> and either symlink them to where Git expects them to be, or to use the <code>core.hooksPath</code> configuration option and change where Git is going to look.</p> + +<p>I like to lower the barrier for any team members by creating <code>git-hooks:on</code> and <code>git-hooks:off</code> functions which either set or unset the <code>core.hooksPath</code>. If someone wants to enable the Git hooks then they only need to run one of those commands rather than having to remember the name of the configuration option or manually creating or removing symlinks.</p> + +<p>There are other Git hooks that can be used but just using <code>pre-commit</code> and <code>pre-push</code> has saved me and teams that I've worked on both Developer time and build minutes, provides quicker feedback and fewer disruptions in our build pipelines, and I like how simple it can be by creating custom functions in a <code>run</code> file.</p> + +<p>Lastly, I've created <a href="https://github.com/opdavies/git-hooks-scratch">https://github.com/opdavies/git-hooks-scratch</a> as an example with a minimal <code>run</code> file and some example hooks.</p> +
+
+ Tue, 16 Aug 2022 00:00:00 GMT +
+ + Using a "run" file to simplify project tasks + /daily/2022/08/15/using-run-file-simplify-project-tasks + http://localhost:8000/daily/2022/08/15/using-run-file-simplify-project-tasks + +
<p>Every project has its own set of commands that need to be run regularly.</p> + +<p>From starting a local server or the project's containers with Docker or Docker Compose, running tests or clearing a cache, or generating the CSS and JavaScript assets, these commands can get quite complicated and time-consuming and error-prone to type over and over again.</p> + +<p>One common way to simplify these commands is using a <code>Makefile</code>.</p> + +<p>A Makefile contains a number of named targets that you can reference, and each has one or more commands that it executes.</p> + +<p>For example:</p> + +<pre><code class="language-language-yaml"># Start the project. +start: + docker-compose up -d + +# Stop the project. +stop: + docker-compose down + +# Run a Drush command. +drush: + docker-compose exec php-fpm drush $(ARGS) +</code></pre> + +<p>With this Makefile, I can run <code>make start</code> to start the project, and <code>make stop</code> to stop it.</p> + +<p>Makefiles work well, but I don't use the full functionality that they offer, such as dependencies for targets, and passing arguments to a command - like arguments for a Drush, Symfony Console, or Artisan command, doesn't work as I originally expected.</p> + +<p>In the example, to pass arguments to the <code>drush</code> command, I'd have to type <code>ARGS="cache:rebuild" make drush</code> for them to get added and the command to work as expected.</p> + +<p>An agency that I worked for created and open-sourced their own Makefile-like tool, written in PHP and built on Symfony Console. I gave a talk on it called <a href="http://localhost:8000/presentations/working-with-workspace">Working with Workspace</a> and used it on some of my own personal and client projects.</p> + +<h2 id="what-i%27m-using-now">What I'm using now</h2> + +<p>The solution that I'm using now is a <code>run</code> file, which is something that I learned from Nick Janetakis' blog and YouTube channel.</p> + +<p>It's a simple Bash file where you define your commands (or tasks) as functions, and then execute them by typing <code>./run test</code> or <code>./run composer require something</code>.</p> + +<p>Here's the Makefile example, but as a <code>run</code> script:</p> + +<pre><code class="bash">#!/usr/bin/env bash + +function help() { + # Display some default help text. + # See examples on GitHub of how to list the available tasks. +} + +function start { + # Start the project. + docker-compose up -d +} + +function stop { + # Stop the project. + docker-compose down +} + +function drush { + # Run a Drush command with any additional arguments. + # e.g. "./run drush cache:rebuild" + docker-compose exec php-fpm drush "${@}" +} + +# Execute the command, or run "help". +eval "${@:-help}" +</code></pre> + +<p>As it's Bash, I can just use <code>$1</code>, <code>$2</code> etc to get specific arguments, or <code>$@</code> to get them all, so <code>./run drush cache:rebuild</code> works as expected and any additional arguments are included.</p> + +<p>You can group tasks by having functions like <code>test:unit</code> and <code>test:commit</code>, and tasks can run other tasks. I use this for running groups of commands within a CI pipeline, and to extract helper functions for tasks like running <code>docker-compose exec</code> within the PHP container that other commands like <code>drush</code>, <code>console</code> or <code>composer</code> could re-use.</p> + +<p>As well as running ad-hoc commands during development, I also use the run file to create functions that run Git pre-commit or pre-push hooks, deploy code with Ansible, or build, push or pull the project's latest Docker images.</p> + +<p>I also use one within my Talks repository to generate PDF files using rst2pdf, present them using phdpc, and generate thumbnail images.</p> + +<p>For examples of <code>run</code> files that I use in my open-source code, <a href="https://github.com/search?l=Shell&amp;q=user%3Aopdavies+filename%3Arun&amp;type=Code">you can look in my public GitHub repositories</a>, and for more information, here is <a href="https://nickjanetakis.com/blog/replacing-make-with-a-shell-script-for-running-your-projects-tasks">Nick's blog post where I first found the idea</a>.</p> +
+
+ Mon, 15 Aug 2022 00:00:00 GMT +
+ + Why I write automated tests + /daily/2022/08/14/why-i-write-tests + http://localhost:8000/daily/2022/08/14/why-i-write-tests + +
<p>In February 2012, I saw a tweet from Tim Millwood asking if anyone wanted to maintain or co-maintain a Drupal module called <a href="https://www.drupal.org/project/override_node_options">Override Node Options</a>.</p> + +<p>It had more than 9,200 active installations at that time, with versions for Drupal 5, 6 and 7.</p> + +<p>I said yes and became the module’s maintainer.</p> + +<p>The module now has versions for Drupal 7, 8 and 9, with (at the latest count, according to Drupal.org) 32,292 active installations - which makes it currently the 197th most installed module.</p> + +<p>There have been two main things that come to mind with this module, related to automated testing.</p> + +<p>Before I become the maintainer, a feature request had been created, along with a large patch file, to add some new permissions to the module. There were some large merge conflicts that stopped me from just committing the changes but I was able to fix them manually and, because the tests still passed, ensure that the original functionality still worked. There weren’t tests for the new permissions but I committed the patch and added the tests later.</p> + +<p>Without the tests to ensure that the original functionality still worked, I probably wouldn’t have committed the patch and would have just closed the issue.</p> + +<p>More recently, a friend and ex-colleague and I decided to refactor some of the module's code.</p> + +<p>We wanted to split the <code>override_node_options.module</code> file so that each override was in its own file and its own class. This would make them easier to edit and maintain, and if anyone wanted to add a new one, they’d just need to create a new file for it and add it to the list of overrides.</p> + +<p>Without the tests ensuring that the module still worked after the refactor, we probably wouldn’t have done it as it was used on over 30,000 sites that I didn't want to break.</p> + +<p>When I was learning about testing, I was working on projects where I was writing the code during the day and the tests in the evening on my own time.</p> + +<p>I remember once when my manual testing had been fine, but when writing the test, I found that I’d used an incorrect permission name in the code that was causing the test to fail. This was a bug that, rather than waiting for a QA Engineer or the client to discover and report, I was able to fix it locally before I'd even committed the code.</p> + +<p>I also worked on an event booking and management website, where we had code responsible for calculating the number of available spaces for an event based on orders, determining the correct price based on the customer's status and the time until the event, creating voucher codes for new members and event leaders, and bulk messaging event attendees. All of the custom functionality was covered by automated tests.</p> + +<p>The great thing about testing is that it gives you confidence that everything still works how you expect - not only when you wrote the code, but also in the future.</p> + +<p>I've talked about this, and how to get started with automated testing in Drupal, in a presentation called <a href="http://localhost:8000/presentations/tdd-test-driven-drupal">TDD - Test-Driven Drupal</a>. If you want to find out more, the slides and a video recording are embedded there.</p> +
+
+ Sun, 14 Aug 2022 00:00:00 GMT +
+ + I wrote a Neovim plugin + /daily/2022/08/13/i-wrote-a-neovim-plugin + http://localhost:8000/daily/2022/08/13/i-wrote-a-neovim-plugin + +
<p>I enjoy writing and working with open-source software, starting back to when I started working with PHP and Drupal in 2007.</p> + +<p>Since then, I've written and maintained a number of Drupal modules and themes, PHP libraries, npm packages, Ansible roles and Docker images - all of which are available on my GitHub and Drupal.org pages.</p> + +<p>Just over a year ago, <a href="/blog/going-full-vim">I switched to using Neovim full-time</a> for my development and DevOps work, and last week, I wrote my first Neovim plugin, written in Lua.</p> + +<p>I've used Lua to configure Neovim but this is the first time that I've written and open-sourced a standalone Neovim plugin.</p> + +<p>It's called <a href="https://github.com/opdavies/toggle-checkbox.nvim">toggle-checkbox.nvim</a> and is used toggle checkboxes in Markdown files - something that I use frequently for to-do lists.</p> + +<p>For example, this a simple list containing both checked and unchecked checkboxes:</p> + +<pre><code class="markdown">- [x] A completed task +- [ ] An incomplete task +</code></pre> + +<p>To toggle a checkbox, the <code>x</code> character needs to be either added or removed, depending on whether we're checking or unchecking it.</p> + +<p>This is done by calling the <code>toggle()</code> function within the plugin.</p> + +<p>In my Neovim configuration, I've added a keymap to do this:</p> + +<pre><code class="lua">vim.keymap.set( + "n", + "&lt;leader&gt;tt", + "require('toggle-checkbox').toggle()" +) +</code></pre> + +<p>This means that I can use the same keymap by running <code>&lt;leader&gt;tt</code> to check or uncheck a checkbox. I could use Vim's replace mode to do this, but I really wanted to have one keymap that I could use for both.</p> + +<p>As it's my first Neovim plugin, I decided to keep it simple.</p> + +<p>The main <code>toggle-checkbox.lua</code> file is currently only 41 lines of code, and whilst there is an existing Vim plugin that I could have used, I was excited to write my own plugin for Neovim, to start contributing to the Neovim ecosystem, and add a Neovim plugin to my portfolio of open-source projects.</p> + +<p>You can view the plugin at <a href="https://github.com/opdavies/toggle-checkbox.nvim">https://github.com/opdavies/toggle-checkbox.nvim</a>, as well as my Neovim configuration (which is also written in Lua) as part of <a href="https://github.com/opdavies/dotfiles/tree/main/roles/neovim/files">my Dotfiles repository</a>.</p> +
+
+ Sat, 13 Aug 2022 00:00:00 GMT +
+ + Git Worktrees and Docker Compose + /daily/2022/08/12/git-worktrees-docker-compose + http://localhost:8000/daily/2022/08/12/git-worktrees-docker-compose + +
<p>I've recently started trialing Git worktrees again as part of my development workflow.</p> + +<p>If you are unfamiliar with Git worktrees, they allow you to have muliple branches of a repository checked out at the same time in different directories.</p> + +<p>For example, this is what I see within my local checkout of my website repository:</p> + +<pre><code>. +├── config +├── HEAD +├── main +│   ├── ansible +│   ├── nginx +│   ├── README.md +│   └── website +├── new-post +│   ├── ansible +│   ├── nginx +│   ├── README.md +│   └── website +├── objects +│   ├── info +│   └── pack +├── packed-refs +├── refs +│   ├── heads +│   └── tags +└── worktrees + ├── main + └── new-post +</code></pre> + +<p>The first thing that you'll notice is, because it's a bare clone, it looks a little different to a what you usually see in a Git repository.</p> + +<p>Each worktree has it's own directory, so my "main" branch inside the <code>main</code> directory.</p> + +<p>If I need to work on a different branch, such as <code>new-post</code>, then I can create a new worktree, move into that directory and start working. I don't need to commit or stash any in-progress work and switch branches.</p> + +<h2 id="complications-with-docker-compose">Complications with Docker Compose</h2> + +<p>I use Docker and Docker Compose for my projects, and this caused some issues for me the last time that I tried using worktrees.</p> + +<p>By default, Docker Compose will use the name of the directory that the Compose file is in to name its containers. If the directory name is "oliverdavies-uk", then the containers will be <code>oliverdavies-uk-web_1</code>, <code>oliverdavies-uk-db_1</code> etc.</p> + +<p>This doesn't work so well if the directory is a worktree called "main" or "master" as you'll have containers called <code>main_web_1</code> or <code>master_db_1</code>.</p> + +<p>The way to solve this is to use the <code>COMPOSE_PROJECT_NAME</code> environment variable.</p> + +<p>If you prefix Docker Compose commands with <code>COMPOSE_PROJECT_NAME=your-project</code>, or add it to an <code>.env</code> file (Docker Compose will load this automatically), then this will override the prefix in the container names to be <code>your-project-{service}</code>.</p> + +<h2 id="container-names-per-worktree">Container names per worktree</h2> + +<p>Whilst you could use the same Compose project name within all of your worktrees, I prefer to include the worktree name as a suffix - something like <code>my-project-main</code> or <code>my-project-staging</code> - and keep these stored in an <code>.env</code> file in each worktree's directory.</p> + +<p>As each worktree now has unique container names, I can have multiple instances of a project running at the same time, and each worktree will have it's own separate data - meaning that I can make changes and test something in one worktree without affecting any others.</p> + +<p>You can also use the <code>COMPOSE_PROJECT_NAME</code> variable inside Docker Compose files.</p> + +<p>For example, if you use Traefik and needed to override the host URL for a service, the string will be interpolated and the project name would be injected as you'd expect.</p> + +<pre><code class="language-yaml">labels: + - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.rule=Host( + `${COMPOSE_PROJECT_NAME}.docker.localhost`, + `admin.${COMPOSE_PROJECT_NAME}.docker.localhost` + )" +</code></pre> + +<p>This means that Traefik would continue to use a different URL for each worktree without you needing to make any changes to your Docker Compose file.</p> +
+
+ Fri, 12 Aug 2022 00:00:00 GMT +
+
+
\ No newline at end of file diff --git a/files/public/media-icons/generic/audio.png b/files/public/media-icons/generic/audio.png new file mode 100644 index 000000000..8ae7f9999 Binary files /dev/null and b/files/public/media-icons/generic/audio.png differ