diff --git a/config/sync/core.base_field_override.node.daily_email.promote.yml b/config/sync/core.base_field_override.node.daily_email.promote.yml new file mode 100644 index 0000000..00bd54c --- /dev/null +++ b/config/sync/core.base_field_override.node.daily_email.promote.yml @@ -0,0 +1,22 @@ +uuid: 1cec9098-9a97-48a0-8b75-c0129fe2187a +langcode: en +status: true +dependencies: + config: + - node.type.daily_email +id: node.daily_email.promote +field_name: promote +entity_type: node +bundle: daily_email +label: 'Promoted to front page' +description: '' +required: false +translatable: true +default_value: + - + value: 0 +default_value_callback: '' +settings: + on_label: 'On' + off_label: 'Off' +field_type: boolean diff --git a/config/sync/core.entity_form_display.node.daily_email.default.yml b/config/sync/core.entity_form_display.node.daily_email.default.yml new file mode 100644 index 0000000..8595277 --- /dev/null +++ b/config/sync/core.entity_form_display.node.daily_email.default.yml @@ -0,0 +1,82 @@ +uuid: 4de70dfa-13cf-4eef-a1c5-df22a9f227a0 +langcode: en +status: true +dependencies: + config: + - field.field.node.daily_email.body + - node.type.daily_email + module: + - path + - text +id: node.daily_email.default +targetEntityType: node +bundle: daily_email +mode: default +content: + body: + type: text_textarea_with_summary + weight: 121 + region: content + settings: + rows: 9 + summary_rows: 3 + placeholder: '' + show_summary: false + third_party_settings: { } + created: + type: datetime_timestamp + weight: 10 + region: content + settings: { } + third_party_settings: { } + path: + type: path + weight: 30 + region: content + settings: { } + third_party_settings: { } + promote: + type: boolean_checkbox + weight: 15 + region: content + settings: + display_label: true + third_party_settings: { } + status: + type: boolean_checkbox + weight: 120 + region: content + settings: + display_label: true + third_party_settings: { } + sticky: + type: boolean_checkbox + weight: 16 + region: content + settings: + display_label: true + third_party_settings: { } + title: + type: string_textfield + weight: -5 + region: content + settings: + size: 60 + placeholder: '' + third_party_settings: { } + uid: + type: entity_reference_autocomplete + weight: 5 + region: content + settings: + match_operator: CONTAINS + match_limit: 10 + size: 60 + placeholder: '' + third_party_settings: { } + url_redirects: + weight: 50 + region: content + settings: { } + third_party_settings: { } +hidden: { } diff --git a/config/sync/core.entity_view_display.node.daily_email.default.yml b/config/sync/core.entity_view_display.node.daily_email.default.yml new file mode 100644 index 0000000..552e72b --- /dev/null +++ b/config/sync/core.entity_view_display.node.daily_email.default.yml @@ -0,0 +1,28 @@ +uuid: 29a419e1-d32b-480f-b2a5-5b53bd304fa0 +langcode: en +status: true +dependencies: + config: + - field.field.node.daily_email.body + - node.type.daily_email + module: + - text + - user +id: node.daily_email.default +targetEntityType: node +bundle: daily_email +mode: default +content: + body: + type: text_default + label: hidden + settings: { } + third_party_settings: { } + weight: 101 + region: content + links: + settings: { } + third_party_settings: { } + weight: 100 + region: content +hidden: { } diff --git a/config/sync/core.entity_view_display.node.daily_email.teaser.yml b/config/sync/core.entity_view_display.node.daily_email.teaser.yml new file mode 100644 index 0000000..8113ef1 --- /dev/null +++ b/config/sync/core.entity_view_display.node.daily_email.teaser.yml @@ -0,0 +1,30 @@ +uuid: 49c930bc-7524-41ba-a8e0-241c657f5f66 +langcode: en +status: true +dependencies: + config: + - core.entity_view_mode.node.teaser + - field.field.node.daily_email.body + - node.type.daily_email + module: + - text + - user +id: node.daily_email.teaser +targetEntityType: node +bundle: daily_email +mode: teaser +content: + body: + type: text_summary_or_trimmed + label: hidden + settings: + trim_length: 600 + third_party_settings: { } + weight: 101 + region: content + links: + settings: { } + third_party_settings: { } + weight: 100 + region: content +hidden: { } diff --git a/config/sync/field.field.node.daily_email.body.yml b/config/sync/field.field.node.daily_email.body.yml new file mode 100644 index 0000000..74ca33e --- /dev/null +++ b/config/sync/field.field.node.daily_email.body.yml @@ -0,0 +1,24 @@ +uuid: c909819c-22ba-462f-8277-9bc514799ad7 +langcode: en +status: true +dependencies: + config: + - field.storage.node.body + - node.type.daily_email + module: + - text +id: node.daily_email.body +field_name: body +entity_type: node +bundle: daily_email +label: Body +description: '' +required: false +translatable: true +default_value: { } +default_value_callback: '' +settings: + display_summary: true + required_summary: false + allowed_formats: { } +field_type: text_with_summary diff --git a/config/sync/node.type.daily_email.yml b/config/sync/node.type.daily_email.yml new file mode 100644 index 0000000..79ab975 --- /dev/null +++ b/config/sync/node.type.daily_email.yml @@ -0,0 +1,17 @@ +uuid: c40f9dd1-769b-4ebf-8fbf-2fef1f2b34cb +langcode: en +status: true +dependencies: + module: + - menu_ui +third_party_settings: + menu_ui: + available_menus: { } + parent: '' +name: 'Daily email' +type: daily_email +description: '' +help: '' +new_revision: true +preview_mode: 1 +display_submitted: true diff --git a/config/sync/rabbit_hole.behavior_settings.node_type_daily_email.yml b/config/sync/rabbit_hole.behavior_settings.node_type_daily_email.yml new file mode 100644 index 0000000..8b3b9d8 --- /dev/null +++ b/config/sync/rabbit_hole.behavior_settings.node_type_daily_email.yml @@ -0,0 +1,14 @@ +uuid: 5ecc8902-0c21-4e80-9bf4-a8db1ab93de5 +langcode: en +status: true +dependencies: + config: + - node.type.daily_email +id: node_type_daily_email +entity_type_id: node_type +entity_id: daily_email +action: display_page +allow_override: 0 +redirect: '' +redirect_code: 301 +redirect_fallback_action: access_denied diff --git a/config/sync/views.view.daily_emails.yml b/config/sync/views.view.daily_emails.yml new file mode 100644 index 0000000..acee894 --- /dev/null +++ b/config/sync/views.view.daily_emails.yml @@ -0,0 +1,431 @@ +uuid: 5ea1f419-9ff4-4ef8-8ba5-5faf94279ac8 +langcode: en +status: true +dependencies: + config: + - node.type.daily_email + - system.menu.main + module: + - node + - user +id: daily_emails +label: 'Daily emails' +module: views +description: '' +tag: '' +base_table: node_field_data +base_field: nid +display: + default: + id: default + display_title: Default + display_plugin: default + position: 0 + display_options: + title: 'The Daily Drupaler: Email Archive' + fields: + view_node: + id: view_node + table: node + field: view_node + relationship: none + group_type: group + admin_label: '' + entity_type: node + plugin_id: entity_link + label: '' + exclude: true + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: false + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + text: view + output_url_as_text: true + absolute: false + created: + id: created + table: node_field_data + field: created + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: created + plugin_id: field + label: '' + exclude: true + alter: + alter_text: true + text: '{{ created }}:' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '0' + element_class: '' + element_label_type: '0' + element_label_class: '' + element_label_colon: false + element_wrapper_type: '0' + element_wrapper_class: '' + element_default_classes: false + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: timestamp + settings: + date_format: medium + custom_date_format: '' + timezone: '' + tooltip: + date_format: long + custom_date_format: '' + time_diff: + enabled: false + future_format: '@interval hence' + past_format: '@interval ago' + granularity: 2 + refresh: 60 + description: '' + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + title: + id: title + table: node_field_data + field: title + relationship: none + group_type: group + admin_label: '' + plugin_id: field + label: '' + exclude: false + alter: + alter_text: true + text: '{{ created }} {{ title }}' + make_link: true + path: '{{ view_node }}' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: false + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + click_sort_column: value + type: string + settings: + link_to_entity: false + group_column: value + group_columns: { } + group_rows: true + delta_limit: 0 + delta_offset: 0 + delta_reversed: false + delta_first_last: false + multi_type: separator + separator: ', ' + field_api_classes: false + pager: + type: mini + options: + offset: 0 + items_per_page: 31 + total_pages: null + id: 0 + tags: + next: ›› + previous: ‹‹ + expose: + items_per_page: false + items_per_page_label: 'Items per page' + items_per_page_options: '5, 10, 25, 50' + items_per_page_options_all: false + items_per_page_options_all_label: '- All -' + offset: false + offset_label: Offset + exposed_form: + type: basic + options: + submit_button: Apply + reset_button: false + reset_button_label: Reset + exposed_sorts_label: 'Sort by' + expose_sort_order: true + sort_asc_label: Asc + sort_desc_label: Desc + access: + type: perm + options: + perm: 'access content' + cache: + type: tag + options: { } + empty: { } + sorts: + created: + id: created + table: node_field_data + field: created + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: created + plugin_id: date + order: DESC + expose: + label: '' + field_identifier: '' + exposed: false + granularity: second + arguments: { } + filters: + status: + id: status + table: node_field_data + field: status + entity_type: node + entity_field: status + plugin_id: boolean + value: '1' + group: 1 + expose: + operator: '' + type: + id: type + table: node_field_data + field: type + relationship: none + group_type: group + admin_label: '' + entity_type: node + entity_field: type + plugin_id: bundle + operator: in + value: + daily_email: daily_email + group: 1 + exposed: false + expose: + operator_id: '' + label: '' + description: '' + use_operator: false + operator: '' + operator_limit_selection: false + operator_list: { } + identifier: '' + required: false + remember: false + multiple: false + remember_roles: + authenticated: authenticated + reduce: false + is_grouped: false + group_info: + label: '' + description: '' + identifier: '' + optional: true + widget: select + multiple: false + remember: false + default_group: All + default_group_multiple: { } + group_items: { } + style: + type: html_list + options: + grouping: { } + row_class: '' + default_row_class: true + type: ul + wrapper_class: item-list + class: '' + row: + type: fields + options: + default_field_elements: true + inline: { } + separator: ' - ' + hide_empty: false + query: + type: views_query + options: + query_comment: '' + disable_sql_rewrite: false + distinct: false + replica: false + query_tags: { } + relationships: { } + header: + area: + id: area + table: views + field: area + relationship: none + group_type: group + admin_label: '' + plugin_id: text + empty: false + content: + value: '

This is an archive of the 520+ email messages I have sent to my daily mailing list since the 12th of August, 2022. Enjoy!

' + format: basic_html + tokenize: false + footer: { } + display_extenders: { } + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url.query_args + - 'user.node_grants:view' + - user.permissions + tags: { } + feed_1: + id: feed_1 + display_title: Feed + display_plugin: feed + position: 2 + display_options: + title: "The Daily Drupaler: Oliver Davies' Daily Email List" + pager: + type: some + options: + offset: 0 + items_per_page: 1 + row: + type: node_rss + options: { } + defaults: + title: false + sitename_title: false + display_extenders: { } + path: rss/daily.xml + sitename_title: false + displays: + page_1: page_1 + default: '0' + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - 'user.node_grants:view' + - user.permissions + tags: { } + page_1: + id: page_1 + display_title: Page + display_plugin: page + position: 1 + display_options: + display_extenders: { } + path: archive + menu: + type: normal + title: 'Daily email archive' + menu_name: main + parent: '' + cache_metadata: + max-age: -1 + contexts: + - 'languages:language_content' + - 'languages:language_interface' + - url.query_args + - 'user.node_grants:view' + - user.permissions + tags: { } diff --git a/tools/scripts/daily.json b/tools/scripts/daily.json new file mode 100644 index 0000000..54137c1 --- /dev/null +++ b/tools/scripts/daily.json @@ -0,0 +1,3686 @@ +{ + "emails": [ + { + "title": "Make it easy", + "date": "1716940800", + "permalink": "/daily/2024/05/29/make-it-easy", + "text": "

What can you do to make your current task easier to achieve?<\/p>

Can you refactor the existing code to make it easier to change?<\/p>

Are there requirements that are no longer needed and can be de-scoped to make the code simpler or smaller?<\/p>

Is there a more minimum-viable version that you can release now and iterate on it later?<\/p>

Is there an existing solution, such as a library, module or plugin you can use instead of implementing it yourself?<\/p>

Is there a different or simpler implementation that satisfies most of the requirements that you can use?<\/p>

Taking an easier option means less code to maintain and a more stable application.<\/p>", + "cta": "d7eol", + "tags": ["software-development"] + }, { + "title": "Why is everyone moving to SQLite?", + "date": "1716854400", + "permalink": "/daily/2024/05/28/why-is-everyone-moving-to-sqlite", + "text": "

I've noticed a lot of Developers recently adopting SQLite for their database and I wonder why this is.<\/p>

Laravel changed their default database to SQLite for local development.<\/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>

Drupal supports using SQLite, but, other than for my automated testing course<\/a>, or when running automated tests, I've always used a MySQL or MariaDB database.<\/p>

Maybe this is something to keep an eye on and potentially use more for some scenarios in the future.<\/p>", + "cta": "d7eol", + "tags": ["software-development"] + }, { + "title": "Why do you still write Sass?", + "date": "1716768000", + "permalink": "/daily/2024/05/27/why-do-you-still-write-sass", + "text": "

Yesterday, I asked if it's time to stop writing Sass<\/a>.<\/p>

If you still use Sass and are writing new styles with Sass, I'd like to know why.<\/p>

Reply to this email and let me know.<\/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>", + "cta": "", + "tags": ["software-development","css"] + }, { + "title": "Is it time to stop writing Sass?", + "date": "1716681600", + "permalink": "/daily/2024/05/26/is-it-time-to-stop-writing-sass", + "text": "

I've seen a lot of recent posts that ask questions like \"Is it time to stop writing Sass?\".<\/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>

But, with recent native browser support added for some Sass features, such as CSS nesting<\/a> and CSS custom properties<\/a> (variables), people are considering moving from Sass to regular CSS.<\/p>

Using regular CSS also makes it easier to onboard new Developers onto your project, which is particularly helpful in open-source projects, as Mark Conroy and I discussed<\/a> on the Beyond Blocks podcast.<\/p>", + "cta": "", + "tags": ["software-development","css"] + }, { + "title": "Testing is a reusable skill", + "date": "1716595200", + "permalink": "/daily/2024/05/25/testing-is-a-reusable-skill", + "text": "

I like skills and technologies that I can re-use.<\/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>

I'd rather invest my time learning something I'll be able to re-use and get the most benefit from.<\/p>

Automated testing is an example of something re-usable.<\/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>

Whilst the syntax can be different, even in the PHP space comparing PHPUnit, Pest and Behat, the fundamentals and approaches are the same.<\/p>", + "cta": "testing_course", + "tags": ["software-development","automated-testing","test-driven-development"] + }, { + "title": "Don't put HTML in your body field", + "date": "1716508800", + "permalink": "/daily/2024/05/24/dont-put-html-in-your-body-field", + "text": "

I often see Drupal projects where people have put raw HTML code into their body or other rich-text fields.<\/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>

If you have structured HTML code in multiple nodes, you can't make changes without editing each instance.<\/p>

What if you need to fix a bug and have hundreds or thousands of instances?<\/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>

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>

In Drupal, use structured data in fields with Paragraphs or Layout Builder and build the templates around them.<\/p>

This makes it easier to maintain and also for people to add and edit content.<\/p>", + "cta": "d7eol", + "tags": ["software-development","drupal"] + }, { + "title": "Why I use long parameter names in scripts", + "date": "1716422400", + "permalink": "/daily/2024/05/23/why-i-use-long-parameter-names-in-scripts", + "text": "

The other day, I posted about a script I'd written<\/a> that found the longest commit message in a repository.<\/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>

The lines in the file are sorted so the longest commit is first.<\/p>

Whilst I commonly use short parameters, such as git add -p<\/code> when typing commands, in scripts, I prefer to use the equivalent longer parameters, where possible.<\/p>

For example, in the script, I execute this command to sort the lines:<\/p>

sort \"${result_file}\" --reverse --numeric-sort --output \"${result_file}\"<\/code><\/pre>

This could be re-written as:<\/p>

sort \"${result_file}\" -rn -o \"${result_file}\"<\/code><\/pre>

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>", + "cta": "", + "tags": ["software-development","bash","zsh","linux"] + }, { + "title": "`git revert` is your friend", + "date": "1716336000", + "permalink": "/daily/2024/05/22/git-revert-is-your-friend", + "text": "

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>

Instead of having to change it back manually, git revert<\/code> can do it for you.<\/p>

You specify the commit SHA you want to revert and Git will automatically try and revert that commit.<\/p>

It creates its own commit message which includes the original commit message and the reverted commit SHA<\/a>, so you can easily find or navigate to the original commit.<\/p>

For example:<\/p>

Revert \"Sort talks only by the event date\"<\/p>

This reverts commit cbd1417b24a608df8b451a3ab5c9f888de41e758.<\/p><\/blockquote>

Next time, instead of manually reverting a commit, give git revert<\/code> a try.<\/p>", + "cta": "", + "tags": ["software-development","git"] + }, { + "title": "Which commit has the largest message?", + "date": "1716249600", + "permalink": "/daily/2024/05/21/which-commit-has-the-largest-message", + "text": "

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>

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>

I was hoping for a command like git shortlog --summary --all<\/code> (which shows all authors to a codebase and their number of commits), but couldn't find one, so I wrote a script to do it<\/a>.<\/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>

It was an interesting task and shows examples of using various UNIX commands and Linux coreutils, such as find<\/code>, cut<\/code>, sort<\/code> and wc<\/code> in combination with Git.<\/p>

The longest in this website's code base is 546 characters<\/a>, which is fairly small compared to some of my messages in other projects.<\/p>

What's the longest commit message in the repository you're working in?<\/p>", + "cta": "", + "tags": ["software-development","git"] + }, { + "title": "Referencing other commits in commit messages", + "date": "1716163200", + "permalink": "/daily/2024/05/20/referencing-other-commits-in-commit-messages", + "text": "

Last week, I asked whether you should include issue IDs in commit messages<\/a>.<\/p>

Another thing I like to reference in a commit message is the commit ID (or SHA) of a related commit.<\/p>

For example, when I run git log<\/code> in my website repository, I see commits like this:<\/p>

commit 0c91825c16217d0fe7eff4ea100a67550051c4a9Author: Oliver Davies <oliver@oliverdavies.dev>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>

The sha for this commit is 0c91825c16217d0fe7eff4ea100a67550051c4a9<\/code>.<\/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>

I also don't need to include the entire thing - only enough for it to be unique (usually five or six characters).<\/p>

Once pushed, the commit IDs should never change, so this will be a permanent reference to the first commit.<\/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>", + "cta": "", + "tags": ["software-development","git"] + }, { + "title": "Better commit messages means better pull requests", + "date": "1716076800", + "permalink": "/daily/2024/05/19/better-commit-messages-better-pull-requests", + "text": "

If you're working on a team or project that uses pull or merge requests, then writing commit messages with detailed descriptions<\/a> using the message body and not just the subject line, will result in better pull requests.<\/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>

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>

Pull or merge requests with more information and detail are more likely to be accepted and merged.<\/p>

The text will also remain in the pull or merge request, even if someone squashes your commits and deletes the old messages<\/a>.<\/p>

The issue then is, what if you move to a different Git hosting service<\/a>?<\/p>", + "cta": "", + "tags": ["software-development","git"] + }, { + "title": "Should you strictly enforce the 50/72 rule?", + "date": "1715990400", + "permalink": "/daily/2024/05/18/should-you-strictly-enforce-the-5072-rule", + "text": "

Yesterday<\/a>, I mentioned the 50\/72 rule when writing Git commit messages.<\/p>

The first line in the commit message is the subject line and should be no longer than 50 characters.<\/p>

Any additional lines are the message body and should be wrapped at 72 characters.<\/p>

As I said, I have Neovim configured to format my commit messages based on these rules, although they're more like guidelines.<\/p>

There's no hard limit on the number of characters in the subject line or the number of characters in the body.<\/p>

The commit will work and not be rejected when pushing to your remote repository.<\/p>

There are likely post-commit Git hooks<\/a> to do this, but by default, things will work.<\/p>

A commit message to Drupal core today was 178 characters long, including the issue ID and contributors.<\/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>

I'd rather they focused on writing a good and descriptive commit message and if it's formatted correctly, that's a bonus.<\/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>", + "cta": "", + "tags": ["software-development","git"] + }, { + "title": "Why I don't commit with -m", + "date": "1715904000", + "permalink": "/daily/2024/05/17/why-i-dont-commit-with--m", + "text": "

A lot of tutorials show using git commit -m<\/code> to create commits using an inline message.<\/p>

I don't do that, though.<\/p>

Using the -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>

Alternatively, people could write a longer commit message in one line and not follow the 50 character limit for the subject line.<\/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>

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>

These are rules that aren't also enforced in some Git tools, so I prefer knowing my setup is configured correctly.<\/p>

Though, whether you use -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>", + "cta": "", + "tags": ["software-development","git"] + }, { + "title": "The first test is the hardest", + "date": "1715817600", + "permalink": "/daily/2024/05/16/the-first-test-is-the-hardest", + "text": "

Whether you're writing tests before the implementation code or not, the first test is always the hardest to write.<\/p>

What will the arrange and act phases look like?<\/p>

What dependencies will you need?<\/p>

What do you need to mock or create fakes of?<\/p>

In a Drupal test, what other modules and configuration do you need to install?<\/p>

What installation profile and default theme do you need to use?<\/p>

What other unknown or complicated setup steps are there?<\/p>

Do you need to configure your environment to run the tests and get the expected output?<\/p>

Here's the thing<\/h2>

The hardest part is getting the arrange\/setup phase working and getting to when the test is running your business logic.<\/p>

Once you've got the first test running, adding more for similar use cases will be much easier.<\/p>", + "cta": "", + "tags": ["software-development","drupal","php","automated-testing","test-driven-development"] + }, { + "title": "Should you include issue IDs in your commit messages?", + "date": "1715731200", + "permalink": "/daily/2024/05/15/should-you-include-issue-ids-in-your-commit-messages", + "text": "

It's shown in the examples of the conventional commits specification<\/a> as part of the optional footer data.<\/p>

But is it useful?<\/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>

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>

That makes the issue IDs that reference the old IDs useless as no one has access to the issues it references.<\/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>

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>

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>", + "cta": "", + "tags": ["software-development","git"] + }, { + "title": "Free code reviews", + "date": "1715644800", + "permalink": "/daily/2024/05/14/free-code-reviews", + "text": "

Do you want to contribute to open-source software, such as Drupal core or one of the thousands of modules or themes?<\/p>

Are you worried about receiving negative comments or feedback?<\/p>

Instead, think of it as free code reviews.<\/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>

I'm also happy to review people's open-source for free. If that would be of interest, reply and let me know.<\/p>", + "cta": "", + "tags": ["software-development","open-source"] + }, { + "title": "DrupalCamp Ghent", + "date": "1715558400", + "permalink": "/daily/2024/05/13/drupalcamp-ghent", + "text": "

Last Friday and Saturday, I was happy to attend and present two sessions at DrupalCamp Ghent in Belgium.<\/p>

I was able to present talks on automated testing and Tailwind CSS, which were my 99th and 100th talks, respectively!<\/p>

The sessions were streamed live, or the slides and previous recordings are on my website<\/a>.<\/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>

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>", + "cta": "", + "tags": ["software-development","drupal","php","drupalcamp"] + }, { + "title": "Merging without merge commits", + "date": "1715472000", + "permalink": "/daily/2024/05/12/merging-without-merge-commits", + "text": "

Since I posted about optimising for revertability<\/a>, I've received a few questions about how I avoid merge commits when working with Git.<\/p>

This is an extract from my .config\/git\/.config<\/code> file:<\/p>

[merge]    ff = \"only\"[pull]    ff = \"only\"    rebase = true<\/code><\/pre>

This changes the behaviour of when I run git pull<\/code> to always include --rebase<\/code> by default and to only allow fast-forward merges and pulls.<\/p>

Only allowing fast-forward merges avoids merge commits as Git can just move the pointer for the branch to the latest commit.<\/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>

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>

In this situation, I can do git merge --ff<\/code> to allow a merge commit temporarily, but this is the exception instead of the default.<\/p>

Hint: there's a lot more information on the configuration and arguments if you run and read man git-merge<\/code>.<\/p><\/blockquote>

When working with online tools such as GitHub and GitLab, I avoid any options like Squash and merge<\/code> or 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>

Or use trunk-based development<\/a> and don't work on topic branches at all.<\/p>", + "cta": "", + "tags": ["software-development","git"] + }, { + "title": "Don't delete my commit messages", + "date": "1715385600", + "permalink": "/daily/2024/05/11/don-t-delete-my-commit-messages", + "text": "

Another reason I don't like squashing commits within pull\/merge request is losing detail within the commit messages.<\/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>

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>

The time I spent writing the messages was wasted, and the information was lost.<\/p>

I'd rather 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>

People can see the changes by viewing the commits, but the information within the commit messages are valuable, too.<\/p>", + "cta": "", + "tags": ["software-development","git"] + }, { + "title": "Optimise for revertability", + "date": "1715299200", + "permalink": "/daily/2024/05/10/optimise-for-revertability", + "text": "

There are two things I avoid when merging changes in Git.<\/p>

Merge commits and squashing commits.<\/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>

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>

Working with small, unsquashed commits makes it simple to revert a specific one and only that one.<\/p>

If I need to revert something, I want to be able to do as simply and specifically as possible.<\/p>

Optimise for revertability.<\/p>", + "cta": "", + "tags": ["software-development","git"] + }, { + "title": "Do you have a deadline?", + "date": "1715212800", + "permalink": "/daily/2024/05/09/do-you-have-a-deadline", + "text": "

Does your task or project have a deadline for completion?<\/p>

Is there a date that it must be completed by?<\/p>

Or, is your \"deadline\" an arbitrary date that can be moved?<\/p>

If so, you don't have a deadline.<\/p>

You have a goal.<\/p>

Understand and be clear whether you have a fixed deadline or a flexible goal.<\/p>", + "cta": "", + "tags": ["software-development"] + }, { + "title": "Assertions aren't just for tests", + "date": "1715126400", + "permalink": "/daily/2024/05/08/assertions-arent-just-for-tests", + "text": "

If you've written or seen automated tests in PHP, you'll have seen lines like this:<\/p>

self::assertTrue(FALSE);<\/code><\/pre>

But, did you know assertions can be used outside of tests.<\/p>

PHP has an assert()<\/code> function that can be used anywhere.<\/p>

For example, if I had this code:<\/p>

$node = Node::load(1);assert($node instanceof NodeInterface);assert($node->bundle() === 'page');<\/code><\/pre>

I know $node<\/code> is a node with the correct bundle type and I can continue.<\/p>

I've made my assumptions explicit.<\/p>

If $node<\/code> is not the correct type or returns an unexpected bundle, the assertion will fail and an Exception will be thrown.<\/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>

If you haven't tried assert()<\/code> before, give it a try.<\/p>", + "cta": "", + "tags": ["software-development","drupal","php","automated-testing"] + }, { + "title": "Drupal 7.100.2", + "date": "1715040000", + "permalink": "/daily/2024/05/07/drupal-7-100-2", + "text": "

In April 2023<\/a>, I wondered if we'd see a 7.100 release of Drupal.<\/p>

I just noticed that we did.<\/p>

It was released on the 6th of March.<\/p>

From the release notes:<\/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>

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>

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>

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>

If you're stuck on Drupal 7, I can help! Reply to this email and let's start a conversation.<\/p>", + "cta": "d7eol", + "tags": ["software-development","drupal","php"] + }, { + "title": "Interactive staging", + "date": "1714953600", + "permalink": "/daily/2024/05/06/interactive-staging", + "text": "

A major addition to my Git workflow has been the ability to interactively add hunks of code to be committed.<\/p>

There's git add -i<\/code> to interactively add, though I usually go straight to git add -p<\/code> to use patch<\/code>.<\/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>

For example, here's the prompt I get whilst working on the post for this email:<\/p>

diff --git a\/source\/_daily_emails\/2024-05-06.md b\/source\/_daily_emails\/2024-05-06.mdindex 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>

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>

Then the process is repeated for any following hunks.<\/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>", + "cta": "", + "tags": ["software-development","git"] + }, { + "title": "Making PHPStan stricter", + "date": "1714867200", + "permalink": "/daily/2024/05/05/making-phpstan-stricter", + "text": "

Continuing yesterday's thought on strictness in PHP<\/a>, today I want to talk about adding more strictness to PHPStan.<\/p>

Adding the PHPStan Strict Rules extension<\/a> makes PHPStan stricter by adding new, more opinionated rules.<\/p>

For example:<\/p>