<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mat</title>
    <description>The latest articles on DEV Community by Mat (@matmooredev).</description>
    <link>https://dev.to/matmooredev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F91905%2F2a3cf0e0-84db-4f7f-9575-364d43bee1a8.jpeg</url>
      <title>DEV Community: Mat</title>
      <link>https://dev.to/matmooredev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/matmooredev"/>
    <language>en</language>
    <item>
      <title>Reviving an old Macbook Air with Ubuntu MATE</title>
      <dc:creator>Mat</dc:creator>
      <pubDate>Mon, 02 Jun 2025 18:54:08 +0000</pubDate>
      <link>https://dev.to/matmooredev/reviving-an-old-macbook-air-with-ubuntu-mate-29o9</link>
      <guid>https://dev.to/matmooredev/reviving-an-old-macbook-air-with-ubuntu-mate-29o9</guid>
      <description>&lt;p&gt;I recently installed Ubuntu MATE on my 11 year old Macbook Air as a way of keeping it running, given that Apple are no longer supporting the device. My idea was to replace it with Linux, while preserving the look &amp;amp; feel of the Mac as much as possible.&lt;/p&gt;

&lt;p&gt;Below are my notes of what I needed to do. The exact model is &lt;strong&gt;MacBookAir6,2 (MacBook Air (13-inch, Early 2014))&lt;/strong&gt;, but I think the process should be pretty similar for other MacBooks made around the same time.&lt;/p&gt;

&lt;h2&gt;
  
  
  hi MATE
&lt;/h2&gt;

&lt;p&gt;I settled on an Ubuntu variant because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;it's familiar to me&lt;/li&gt;
&lt;li&gt;Canonical do a reasonable job at making things usable&lt;/li&gt;
&lt;li&gt;it's easy to find help online.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I chose Ubuntu MATE&lt;sup id="fnref1"&gt;1&lt;/sup&gt; over basic Ubuntu, as I liked how you can configure MATE to look a lot like MacOS.&lt;/p&gt;

&lt;h2&gt;
  
  
  The install process
&lt;/h2&gt;

&lt;p&gt;Installing linux on this device was much like installing it on any device, except for some issues with WiFi. I cobbled together these instructions from various blog posts such as &lt;a href="https://int3ractive.com/blog/2019/things-to-do-after-installing-ubuntu-mate/" rel="noopener noreferrer"&gt;this one&lt;/a&gt; and &lt;a href="https://linuxbsdos.com/2024/11/10/install-ubuntu-24-04-or-ubuntu-24-10-on-macbook-air/" rel="noopener noreferrer"&gt;this one&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Before doing anything, back up your files.&lt;/li&gt;
&lt;li&gt;Make a bootable USB stick with Linux on it.&lt;/li&gt;
&lt;li&gt;Boot from the USB. The WiFi didn't work for me, but I fixed that later. The keyboard and trackpad worked fine. At this point I also tested that the SSD was detected ok.&lt;/li&gt;
&lt;li&gt;Proceed with the install. I made the mistake of tethering to my phone at this stage, which caused the installer to fail part way through. But it worked fine when I switched off the wifi.&lt;/li&gt;
&lt;li&gt;After booting into Linux, you need to tether to a phone via USB, then install the proprietary broadcom drivers so that you can connect to WiFi. Run &lt;code&gt;sudo apt install broadcom-sta-common broadcom-sta-dkms&lt;/code&gt; then restart.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;sudo apt install mbpfan&lt;/code&gt; to ensure the CPU fan works.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;sudo apt install ubuntu-restricted-extras&lt;/code&gt; to install proprietary stuff.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;sudo apt update &amp;amp;&amp;amp; sudo apt upgrade&lt;/code&gt; to update all the packages.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Colour profiles
&lt;/h2&gt;

&lt;p&gt;I didn't seem to need this for the laptop's display but if you have an external monitor you can optionally &lt;a href="https://int3ractive.com/blog/2019/things-to-do-after-installing-ubuntu-mate/#heading-13-correct-monitor-color-with-custom-icc-profile" rel="noopener noreferrer"&gt;restore the colour profile from an .icc file&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Brightness buttons and backlights
&lt;/h2&gt;

&lt;p&gt;On MacOS, the F5, F6 buttons affected screen brightness, but in MATE only the F1, F2 do screen brightness, and F5, F6 manage the keyboard backlight brightness. This confused me a lot but ultimately it all works.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://help.ubuntu.com/community/MacBookAir6-2/Wily" rel="noopener noreferrer"&gt;Info from the Mactel team in 2015&lt;/a&gt; mentioned needing a custom kernel module for the backlight but this is no longer accurate.&lt;/p&gt;

&lt;p&gt;Don't set the &lt;code&gt;acpi_backlight&lt;/code&gt; &lt;a href="https://docs.kernel.org/admin-guide/kernel-parameters.html" rel="noopener noreferrer"&gt;kernel parameter&lt;/a&gt;. I found some older info recommending setting this to video or something but this broke the brightness control for me. &lt;a href="https://wiki.archlinux.org/title/Backlight" rel="noopener noreferrer"&gt;More info on backlight settings&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Webcam issue
&lt;/h2&gt;

&lt;p&gt;The webcam didn't work out of the box. I got it working using by compiling the &lt;a href="https://github.com/patjak/facetimehd/wiki" rel="noopener noreferrer"&gt;facetimehd&lt;/a&gt; kernel module. &lt;/p&gt;

&lt;h2&gt;
  
  
  Tweaking things
&lt;/h2&gt;

&lt;p&gt;MATE comes with a tool called Mate Tweak. Under "Panel", select the Cupertino layout and enable the dock for a Mac-like experience.&lt;/p&gt;

&lt;p&gt;Customise the dock in "Plank preferences". I made the icons bigger. If you want to get rid of an icon that doesn't have a "keep in dock" checkbox, you can click and drag it out of the dock to remove it.&lt;/p&gt;

&lt;p&gt;You can change the colour scheme in the "Appearance" app. I switched to the dark variant of the MATE default theme.&lt;/p&gt;

&lt;p&gt;In "Keyboard preferences", the layout should match your Locale, e.g. "English (UK)". The "Keyboard model" in my case is "MacBook/MacBook Pro (intl)". Minor annoyance: on UK MacBooks, the "#" is the third symbol on the "3" key. Normally to type it you use the left alt, but on Linux you have to use the right alt.&lt;/p&gt;

&lt;p&gt;Also on the Layouts tab is an "Options..." button. This contains a lot of useful key remapping features. Under "Alt and Win behaviour" you can select "Ctrl is mapped to Win and the usual Ctrl". This is the best way I've found of making Mac-style Cmd+C, Cmd+V etc shortcuts work on Linux. Just bear in mind:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It will no longer be possible to type the "Super" key, which breaks some default shortcuts&lt;sup id="fnref2"&gt;2&lt;/sup&gt;.&lt;/li&gt;
&lt;li&gt;You can't use it for copying from a terminal, because Ctrl+C will still send the ^C byte to trigger a SIGINT. I configured Gnome terminal to use Ctrl+Shift+C (Cmd+Shift+C) for Copy, and Ctrl+V (Ctrl+Shift+V) for Paste&lt;sup id="fnref3"&gt;3&lt;/sup&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I also used "Ibus Preferences" to configure a second input method, and I use Ctrl + Space as the key to toggle them.&lt;/p&gt;

&lt;h2&gt;
  
  
  mDNS search domain issue
&lt;/h2&gt;

&lt;p&gt;Out of the box I couldn't use shortnames to ssh to other machines on my local network, because the search domain was set to "home", where previously I was using "local".&lt;/p&gt;

&lt;p&gt;I fixed this with &lt;code&gt;resolvectl domain wlp3s0 local&lt;/code&gt;, where &lt;code&gt;wlp3s0&lt;/code&gt; is the interface name.&lt;/p&gt;

&lt;h2&gt;
  
  
  Linux touchpad drivers suck
&lt;/h2&gt;

&lt;p&gt;The touchpad works out of the box, but the usability is rubbish compared to MacOS. Ubuntu MATE uses libinput, which has an intentionally limited feature set. It doesn't have "momentum scrolling" or &lt;a href="https://pavelfatin.com/scrolling-with-pleasure/" rel="noopener noreferrer"&gt;smooth scrolling&lt;/a&gt;. &lt;a href="https://web.archive.org/web/20180724152830/https://williambharding.com/blog/linux-to-macbook/linux-with-a-macbook-touchpad-feel-pt-2/" rel="noopener noreferrer"&gt;The older Synaptics driver is a lot more configurable&lt;/a&gt; but it's no longer maintained. I haven't tried using it myself yet but I'm curious if it's worth tinkering with to get a better touchpad experience.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;If your laptop is even lower spec than mine, you might consider the Xubuntu or Lubuntu flavours, which have much lower minimum requirements. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;If you want to keep one of the command keys as super, and map the other to control, you can instead use &lt;a href="https://wiki.archlinux.org/title/Xmodmap" rel="noopener noreferrer"&gt;xmodmap&lt;/a&gt; to do the remapping, but this is very confusing to configure.  ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;I think it should be possible to remap the key combination that the terminal emulator uses for ^C (e.g. to Ctrl+Shift+C), but I couldn't find a way to do this in Gnome terminal. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
    </item>
    <item>
      <title>Some thoughts on when to use LLMs</title>
      <dc:creator>Mat</dc:creator>
      <pubDate>Sun, 05 Jan 2025 18:58:17 +0000</pubDate>
      <link>https://dev.to/matmooredev/some-thoughts-on-when-to-use-llms-3281</link>
      <guid>https://dev.to/matmooredev/some-thoughts-on-when-to-use-llms-3281</guid>
      <description>&lt;p&gt;As with any overhyped technology, I think it's wise to be a bit skeptical about claims made by companies pushing large language models (LLMs).&lt;/p&gt;

&lt;p&gt;The eagerness to add AI to everything over the last couple of years reminds me of the hype around "big data" when that was a thing. The technology was useful, but teams were uncritically adopting them even if more established technologies would have been fine in their business context. There is definitely pressure to do the same with LLMs and AI in general, but as technologists we should consider what these things are actually good at and what they're not good at.&lt;/p&gt;

&lt;p&gt;With that in mind, here are some observations from the sidelines, based on my personal usage of ChatGPT as a senior developer, how I've seen LLMs being misused, and my attempt to follow along with the general discourse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reasonable use cases
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Speeding up work tasks
&lt;/h3&gt;

&lt;p&gt;If you don't mind some manual review, LLMs are good at things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;coming up with names for things&lt;/li&gt;
&lt;li&gt;classifying items in a long list (&lt;a href="https://interconnected.org/home/2023/02/07/braggoscope" rel="noopener noreferrer"&gt;example&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;formatting data (e.g. converting to CSV/JSON, or different date formats)&lt;/li&gt;
&lt;li&gt;data extraction from unstructured text (e.g. email addresses or URLs)&lt;/li&gt;
&lt;li&gt;rephrasing or adjusting the tone of your writing&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Code assistants
&lt;/h3&gt;

&lt;p&gt;LLMs can be good for quick prototyping, providing you can understand the code they are generating. &lt;a href="https://newsletter.pragmaticengineer.com/i/154200840/how-developers-are-actually-using-ai" rel="noopener noreferrer"&gt;You will likely have better results if you split the task into very small steps and commit often&lt;/a&gt;. Perhaps consider using the &lt;a href="https://www.manning.com/books/the-mikado-method" rel="noopener noreferrer"&gt;Mikado method&lt;/a&gt; with the LLM.&lt;/p&gt;

&lt;p&gt;If AI is generating the code, I believe you should write the unit tests yourself, so that you are forced to check its correctness.&lt;/p&gt;

&lt;p&gt;Be careful not to use code assistant tools or paste non-public code into LLMs without your employer's (or the copyright holder's) permission.&lt;/p&gt;

&lt;p&gt;I think code assistants are a bad idea for learning new frameworks and libraries, for several reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it discourages you from reading the docs and forming a good mental model of how the thing works&lt;/li&gt;
&lt;li&gt;it's not always smart enough to fix bugs for you or explain why your code isn't working&lt;/li&gt;
&lt;li&gt;you can't recognise when the coding style is outdated or there is simpler way of doing things&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I think refactoring is best done by hand unless you want to apply a single refactoring many times across a large codebase. &lt;a href="https://codescene.com/hubfs/whitepapers/Refactoring-vs-Refuctoring-Advancing-the-state-of-AI-automated-code-improvements.pdf" rel="noopener noreferrer"&gt;AI generated refactorings are not safe and need checking for correctness&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Retrieval-augmented generation (RAG)
&lt;/h3&gt;

&lt;p&gt;A &lt;a href="https://en.wikipedia.org/wiki/Retrieval-augmented_generation" rel="noopener noreferrer"&gt;RAG&lt;/a&gt; is a multi-step process that first uses word vectors to fetch content from a knowledge base, and then feeds it to an LLM to answer a user's question. For question answering, I'd expect this to outperform traditional search in cases where information is scattered amongst a lot of similar looking documents, for example slack messages or helpdesk tickets. It only really makes sense if your knowledge base is large enough that it would be costly for a technical writer to trawl and summarise.&lt;/p&gt;

&lt;p&gt;I'm a bit skeptical of building such systems in-house though - as opposed to something like &lt;a href="https://runllm.com/" rel="noopener noreferrer"&gt;RunLLM&lt;/a&gt; - it feels like this effort would be better invested in improving your own product/service or its documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Questionable use cases
&lt;/h2&gt;

&lt;h3&gt;
  
  
  LLMs are not oracles
&lt;/h3&gt;

&lt;p&gt;LLMs are not good at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;doing research for you&lt;/li&gt;
&lt;li&gt;communicating factual information&lt;/li&gt;
&lt;li&gt;weighing up evidence&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is nonsense to ask an LLM for opinions on ideas, because LLMs can support any position depending on their prompt and context.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building user-facing services on top of LLMs is risky
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;LLMs are costly to train and run due to the amount of compute required. This has a high energy cost, to the point that big tech companies have walked back their commitments to carbon neutrality in order to expand data centres. I wouldn't be surprised if companies hike up prices as the technology matures.&lt;/li&gt;
&lt;li&gt;LLM outputs cannot be trusted to be free of copyrighted or sensitive data without more transparency over how they were trained&lt;/li&gt;
&lt;li&gt;Allowing LLMs to act as "agents" is open to abuse from &lt;a href="https://en.wikipedia.org/wiki/Prompt_injection" rel="noopener noreferrer"&gt;prompt injection&lt;/a&gt; attacks, and they can be &lt;a href="https://simonwillison.net/2024/Dec/31/llms-in-2024/#-agents-still-haven-t-really-happened-yet" rel="noopener noreferrer"&gt;misled by untrustworthy information&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;LLMs will happily &lt;a href="https://www.bbc.com/travel/article/20240222-air-canada-chatbot-misinformation-what-travellers-should-know" rel="noopener noreferrer"&gt;lie to customers&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;It might be possible for AI to perform more complex reasoning by chaining many LLM operations, but I think this is unproven and expensive at this point(?)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>llms</category>
    </item>
    <item>
      <title>A quick way to run axe-core from python</title>
      <dc:creator>Mat</dc:creator>
      <pubDate>Tue, 13 Feb 2024 19:13:05 +0000</pubDate>
      <link>https://dev.to/matmooredev/a-quick-way-to-run-axe-core-from-python-4p0h</link>
      <guid>https://dev.to/matmooredev/a-quick-way-to-run-axe-core-from-python-4p0h</guid>
      <description>&lt;p&gt;I used this code snippet today to run &lt;a href="https://www.npmjs.com/package/@axe-core/cli" rel="noopener noreferrer"&gt;axe-core&lt;/a&gt; from a python selenium test. It calls out to the command line interface, using &lt;a href="https://docs.npmjs.com/cli/v7/commands/npx" rel="noopener noreferrer"&gt;npx&lt;/a&gt; so I don't have to worry about managing node packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_for_accessibility_issues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;npx&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;@axe-core/cli&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-q&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;selenium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_url&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;returncode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should work with anything that runs your app in a browser, not just selenium.&lt;/p&gt;

&lt;p&gt;Axe is an automated tool to detect some (but not all) &lt;a href="https://web.dev/articles/accessibility#what_is_accessibility" rel="noopener noreferrer"&gt;web accessibility&lt;/a&gt; issues. It's not a substitute for actual usability testing with real people, but it's still useful. For a rough idea of what it can do, check out this blog post: &lt;a href="https://www.craigabbott.co.uk/blog/axe-core-vs-pa11y/" rel="noopener noreferrer"&gt;Axe-Core vs PA11y&lt;/a&gt;. In the author's testing, axe-core detected 27% of known issues on a test page.&lt;/p&gt;

&lt;p&gt;I was originally hoping to use &lt;a href="https://github.com/mozilla-services/axe-selenium-python" rel="noopener noreferrer"&gt;axe-selenium-python&lt;/a&gt; for this. It uses selenium's &lt;code&gt;execute_script&lt;/code&gt; to inject the axe javascript into the page, similar to &lt;a href="https://www.browserstack.com/docs/automate/selenium/accessibility-testing#python" rel="noopener noreferrer"&gt;this method&lt;/a&gt;. Unfortunately, &lt;a href="https://github.com/mozilla-services/axe-selenium-python/issues/192#issuecomment-1675245578" rel="noopener noreferrer"&gt;axe-selenium-python has been unmaintained since its author left mozilla&lt;/a&gt;. &lt;a href="https://github.com/mozilla-services/axe-selenium-python/issues/192#issuecomment-1936997374" rel="noopener noreferrer"&gt;It looks like some nice people are going to take this over&lt;/a&gt;, but I wanted a workaround in the meantime.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: There is also &lt;a href="https://www.deque.com/axe/devtools/" rel="noopener noreferrer"&gt;Deque DevTools&lt;/a&gt;, which appears to have python bindings, but this is not open source.&lt;/em&gt; 😞&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>python</category>
      <category>selenium</category>
    </item>
    <item>
      <title>How to avoid meetings and take a day off</title>
      <dc:creator>Mat</dc:creator>
      <pubDate>Sun, 07 May 2023 09:36:17 +0000</pubDate>
      <link>https://dev.to/matmooredev/how-to-avoid-meetings-and-take-a-day-off-23ji</link>
      <guid>https://dev.to/matmooredev/how-to-avoid-meetings-and-take-a-day-off-23ji</guid>
      <description>&lt;p&gt;Recently I've been experimenting with recording videos to asynchronously relay information to my team, instead of waiting for a meeting.&lt;/p&gt;

&lt;p&gt;The videos I've made have been really well received by my team, and I've also appreciated when other coworkers have done this for my benefit. We tend to use it when we have been investigating problems by ourselves, and need to present information back to the rest of the team. Anything that can be presented in a real time meeting or video call can be recorded as a video instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Async video vs live meetings
&lt;/h2&gt;

&lt;p&gt;The main disadvantage of async video is you can't respond to feedback right away and adapt the content to the audience on the fly. So for it to be useful you need a good understanding of what your audience need to know.&lt;/p&gt;

&lt;p&gt;That said, async provides a lot of flexibility:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;when you are working part time, it's a way to check in with your team when you're not working&lt;/li&gt;
&lt;li&gt;when you have leave coming up, it can function as a handover&lt;/li&gt;
&lt;li&gt;if the team have different schedules, it can be quicker than finding a time when everyone can meet&lt;/li&gt;
&lt;li&gt;team members know up front how long it will take to watch and can watch at a time that suits them&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have a lot of information to present, you can chunk up the content into smaller videos. If not all of the information is relevant to the whole team, then you can target videos to different audiences and tell the team to skip any they don't need to watch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Video vs text
&lt;/h2&gt;

&lt;p&gt;Another alternative to video is preparing written documentation. Sometimes this is more appropriate, but I think video has a number of advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it's likely to be less formal and easier for people to consume&lt;/li&gt;
&lt;li&gt;it's multi-sensory: you can &lt;em&gt;show&lt;/em&gt; things at the same time as talking about them&lt;/li&gt;
&lt;li&gt;it's sometimes quicker to produce or just fits the content better, e.g. when demoing a product feature&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can always combine the two by providing slides or written notes alongside the video. I recently prepared a quick presentation in markdown, presented it via the &lt;a href="https://help.obsidian.md/Plugins/Slides" rel="noopener noreferrer"&gt;Obsidian slides plugin&lt;/a&gt; and then shared the raw markdown alongside the video recording.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to record video
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.loom.com/" rel="noopener noreferrer"&gt;Loom&lt;/a&gt; is a tool that is built for this, but they limit the number of recordings and video length unless you pay for a license.&lt;/p&gt;

&lt;p&gt;Another option is to join a video call with just yourself, and record it. This gives you much of the same functionality as loom, but it's a bit fiddly if you want to restart the recording. I've done this with Zoom, but I think Google Hangouts or other tools would work equally well.&lt;/p&gt;

&lt;p&gt;I like this solution because it comes with automatically generated subtitles. This doesn't work perfectly, but it makes the content more accessible for anyone who is hard of hearing or doesn't speak english as a native language. When I post one of these recordings into slack, it also generates a transcript, allowing people to skim the talk or jump ahead.&lt;/p&gt;

&lt;p&gt;If you are using a mac you can also &lt;a href="https://support.apple.com/en-gb/guide/quicktime-player/qtp97b08e666/mac" rel="noopener noreferrer"&gt;record your screen in quicktime&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>communication</category>
      <category>video</category>
      <category>async</category>
      <category>meetings</category>
    </item>
    <item>
      <title>8 tips to make code reviews less painful</title>
      <dc:creator>Mat</dc:creator>
      <pubDate>Fri, 03 Feb 2023 15:01:46 +0000</pubDate>
      <link>https://dev.to/matmooredev/8-tips-to-make-code-reviews-less-painful-1g95</link>
      <guid>https://dev.to/matmooredev/8-tips-to-make-code-reviews-less-painful-1g95</guid>
      <description>&lt;p&gt;Teams use code review in different ways, and your experience may vary depending on who is doing the code review, but there are always things you can do as an author to make the process go more smoothly.&lt;/p&gt;

&lt;p&gt;When submitting changes for review, you should try to reduce the &lt;a href="https://www.dabapps.com/blog/cognitive-load-programming/" rel="noopener noreferrer"&gt;cognitive load&lt;/a&gt; on the reviewer. As an author you have spent way more time thinking about the problem than the reviewer will, so what is easy to read for you isn't necessarily easy for them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it matters
&lt;/h2&gt;

&lt;p&gt;It's always tempting to take shortcuts and throw something up for review quickly, but I think it's always worth pausing first and considering what we are demanding of our teammates. Bear in mind people don't operate at 100% all the time. Your reviewer may be having a bad day, they may be tired or sick, they may be context switching between different tasks. Make their lives easier.&lt;/p&gt;

&lt;p&gt;Complex pull requests are also slower to resolve. At best, you have to wait ages for feedback (which is frustrating if you want to move onto new tasks), at worst, the reviewer focuses on surface level details, rather than giving you feedback that is actually useful (e.g. about the correctness of the solution, or maintainability).&lt;/p&gt;

&lt;p&gt;If your team follows &lt;a href="https://docs.github.com/en/get-started/quickstart/github-flow" rel="noopener noreferrer"&gt;Github Flow&lt;/a&gt;, or similar workflows where reviews block merging, then the longer reviews take, the less frequently work is integrated, the more unfinished work is hanging around. In my experience this leads to extra work in the form of conflicts, deployment issues, and coordination between engineers. By doing a bit of extra work up front, you can avoid extra work later on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 1: Start with small commits
&lt;/h2&gt;

&lt;p&gt;Development can be messy, and the code we start with is not necessarily the code we end up with when we are ready to push our changes. But it's easier to combine lots of smaller commits later than it is to break apart big ones, so &lt;a href="https://blog.beanstalkapp.com/post/147799908084/commit-early-commit-often" rel="noopener noreferrer"&gt;commit early and often&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Capture your reasoning as you go. You can include&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;any assumptions you're making&lt;/li&gt;
&lt;li&gt;anything you've learned about the code since the previous commit&lt;/li&gt;
&lt;li&gt;things you've thought about but not changed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is stuff you will forget if you don't write it down.&lt;/p&gt;

&lt;p&gt;You can rewrite these messages before you push the code, so it's better to overexplain than leave something out and then forget about it.&lt;/p&gt;

&lt;p&gt;Passing a &lt;code&gt;-p&lt;/code&gt; (or &lt;code&gt;--patch&lt;/code&gt;) flag to &lt;code&gt;git add&lt;/code&gt; is really helpful. It  breaks your changes into "hunks" and you type &lt;code&gt;y&lt;/code&gt; or &lt;code&gt;n&lt;/code&gt; after each one to choose what gets staged. That way you can create more than one commit even if you've changed multiple things since your last commit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 2: Learn how to rewrite your git history
&lt;/h2&gt;

&lt;p&gt;When you're committing as you go, you might end up with a &lt;br&gt;
simple sequence of commits where each commit builds on the previous one. This is great, because the reviewer can look over the work commit-by-commit, focusing on one thing at a time. Essentially, your commits &lt;a href="https://blog.mocoso.co.uk/talks/2015/01/12/telling-stories-through-your-commits/" rel="noopener noreferrer"&gt;tell a story&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you're working on something complicated, your commits will probably be messier than this, but that's fine if you rewrite the git history before sharing. This way, reviewers don't need to relive the whole journey you took to arrive at the solution.&lt;/p&gt;

&lt;p&gt;I regularly use some combination of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://git-scm.com/book/en/v2/Git-Branching-Rebasing" rel="noopener noreferrer"&gt;rebase&lt;/a&gt; to remove conflicts with the main branch&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://thoughtbot.com/blog/git-interactive-rebase-squash-amend-rewriting-history" rel="noopener noreferrer"&gt;interactive rebase&lt;/a&gt; to squash many commits into one or edit commit messages&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://git-scm.com/docs/git-cherry-pick" rel="noopener noreferrer"&gt;cherry-pick&lt;/a&gt; to pick out commits I care about into a fresh branch which can be reviewed separately&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jwiegley.github.io/git-from-the-bottom-up/3-Reset/3-doing-a-soft-reset.html" rel="noopener noreferrer"&gt;reset&lt;/a&gt; to completely redo some commits while keeping the code I have in my working tree&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These commands can be pretty confusing, but they are worth learning, and &lt;a href="https://ohshitgit.com/" rel="noopener noreferrer"&gt;you can undo almost anything in git&lt;/a&gt; if it goes wrong.&lt;/p&gt;
&lt;h2&gt;
  
  
  Tip 3: Squash commits that don't provide new information
&lt;/h2&gt;

&lt;p&gt;Similar kinds of changes can be squashed together before sharing for review (see above). You might do the work in stages - so you have lots of checkpoints to rollback to - but then present it as one logical step in the PR. This makes it much easier for the reviewer to check that the change was done consistently across the codebase.&lt;/p&gt;

&lt;p&gt;Prefer commits that are small in scope but "complete". If you are aiming to write tested, documented code, then &lt;a href="https://simonwillison.net/2022/Oct/29/the-perfect-commit/" rel="noopener noreferrer"&gt;the perfect commit might contain implementation, tests, and documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Don't push code where you try something in one commit and then undo it in another. This is confusing: it adds noise to the commit history that makes it harder to review the individual commits in your PR. Each commit should make sense on its own.&lt;/p&gt;

&lt;p&gt;I deal with these problems using an interactive rebase:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git rebase -i &amp;lt;commit you branched from&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This asks you what to do with each commit on branch. &lt;code&gt;squash&lt;/code&gt; combines it with the previous commit, &lt;code&gt;drop&lt;/code&gt; removes it entirely, &lt;code&gt;edit&lt;/code&gt; lets you amend the commit (which you can use to change the commit message). &lt;br&gt;
Imagine you were doing the work from scratch - which steps would you take? If you make the existing commits match those steps then somebody else should be able to follow along.&lt;/p&gt;
&lt;h2&gt;
  
  
  Tip 4: Stop when you have something useful and deployable
&lt;/h2&gt;

&lt;p&gt;The more changes you have on a branch, the riskier it is to deploy and the longer it takes to review.&lt;/p&gt;

&lt;p&gt;If your branch gets out of control, look for any commit part way through that is deployable. Make that your pull request, get it reviewed and deployed, and then come back to the following commits once that it is merged.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git checkout -b &amp;lt;new-branch&amp;gt;
git reset --hard &amp;lt;deployable-commit&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can feel slower, because you're waiting for things to happen before doing the next part, but it's actually faster. You can get code into production earlier, reviews happen quicker, and if something goes wrong you can fix it quicker.&lt;/p&gt;

&lt;p&gt;You may find that if some refactoring was done first, then the rest of the pull request would be simpler to understand. In that case, you can raise one PR for the refactoring, and then redo the original PR. That way, the first review only needs to consider whether the code functions the same as before, and the second review only needs consider the impact of the new change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 5: Review and edit your commit messages
&lt;/h2&gt;

&lt;p&gt;The commit message should help reviewers review your code quickly.&lt;/p&gt;

&lt;p&gt;Reviewers need to&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;understand what has changed&lt;/li&gt;
&lt;li&gt;understand the bigger picture of what your change enables&lt;/li&gt;
&lt;li&gt;follow your thought process&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So provide this information in an easy to digest form. &lt;/p&gt;

&lt;p&gt;When code is presented without context, the reader has to rediscover the context themselves and make assumptions about what you were trying to do (which could be wrong and waste time).&lt;/p&gt;

&lt;p&gt;If you reference an older commit, include the commit hash. Then anyone can find the commit using the CLI, and it will be hyperlinked in github.&lt;/p&gt;

&lt;p&gt;If the work was prompted by a ticket, link to the ticket. But it's worth also including a summary of what you are trying to achieve, so that people looking at the repo in the future can still understand your code, even if the ticketing software is no longer used.&lt;/p&gt;

&lt;p&gt;If you fixed a bug, link to any external information you used to understand the problem, e.g. manual pages or CVEs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 6: Keep pull request descriptions short and to the point
&lt;/h2&gt;

&lt;p&gt;If the commit messages are detailed, then your pull request description can be a short summary or introduction, to give the reviewer a basic understanding of what you set out to do and why before they dive into the details.&lt;/p&gt;

&lt;p&gt;Other things you can include in a PR description:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A discussion of the problem you are solving and how chose to approach it&lt;/li&gt;
&lt;li&gt;Specific questions for the reviewer, or areas you want to highlight&lt;/li&gt;
&lt;li&gt;A discussion of trade-offs you made&lt;/li&gt;
&lt;li&gt;Screenshots for any UI changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The pull request is for the team right now, whereas the commit messages are part of a permanent record that your future self/team can refer back to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 7: Refactor code to eliminate confusion
&lt;/h2&gt;

&lt;p&gt;If something is unclear to the reviewer, use the opportunity to make it clearer, for example by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;improving how you name things&lt;/li&gt;
&lt;li&gt;splitting or inlining code&lt;/li&gt;
&lt;li&gt;improving commit messages&lt;/li&gt;
&lt;li&gt;adding comments to the code&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tip 8: Don't bite off more than you can chew
&lt;/h2&gt;

&lt;p&gt;If you're starting something complicated, consider treating it as a &lt;a href="https://en.wikipedia.org/wiki/Spike_(software_development)" rel="noopener noreferrer"&gt;spike&lt;/a&gt; and then redo it from scratch once you know how you want to implement it. I like this approach because the whole team understands that the work is experimental and can be abandoned if it's not going to be worth the effort to continue.&lt;/p&gt;

&lt;p&gt;It's ok to throw everything away and start again, building on what you've learned about the problem so far.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other perspectives on this topic
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.thepete.net/blog/2019/05/10/6-practices-for-effective-pull-requests/" rel="noopener noreferrer"&gt;6 Practices for Effective Pull Requests&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mtlynch.io/code-review-love/" rel="noopener noreferrer"&gt;How to Make Your Code Reviewer Fall in Love with You&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>review</category>
      <category>git</category>
      <category>process</category>
      <category>teamwork</category>
    </item>
    <item>
      <title>How to break up a rails monolith</title>
      <dc:creator>Mat</dc:creator>
      <pubDate>Fri, 20 Jan 2023 18:17:39 +0000</pubDate>
      <link>https://dev.to/matmooredev/how-to-break-up-a-rails-monolith-1c81</link>
      <guid>https://dev.to/matmooredev/how-to-break-up-a-rails-monolith-1c81</guid>
      <description>&lt;p&gt;My last couple of jobs have both been with companies that were struggling with complex rails applications. There was nothing fundamentally wrong with the technology choices, the software had just organically become difficult to work with.&lt;/p&gt;

&lt;p&gt;In this kind of an environment, engineers are unhappy with the technical debt, product are unhappy with the slow delivery. But it's not easy to fix.&lt;/p&gt;

&lt;p&gt;There is a temptation to start over with a &lt;a href="https://softwareengineering.stackexchange.com/questions/6268/when-is-a-big-rewrite-the-answer" rel="noopener noreferrer"&gt;big rewrite&lt;/a&gt;, but rewrites take ages, and divert resources away from improving the user experience.&lt;/p&gt;

&lt;p&gt;These are some notes I've gathered about improving the structure of these rails applications without rebuilding the system from scratch or spinning up new infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;your rails app is turning into a &lt;a href="http://www.laputan.org/mud/" rel="noopener noreferrer"&gt;big ball of mud&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;you need to think about &lt;em&gt;everything&lt;/em&gt; to change &lt;em&gt;anything&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;it's hard to understand what the intended structure is supposed to be.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Goals
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;reduce cognitive load on engineers&lt;/li&gt;
&lt;li&gt;enable teams to work autonomously and take ownership of components&lt;/li&gt;
&lt;li&gt;this might be a preparation step for rebuilding functionality or extracting separate services&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Enter the modular monolith
&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://www.kamilgrzybek.com/design/modular-monolith-primer/" rel="noopener noreferrer"&gt;Modular monoliths&lt;/a&gt; are applications that are deployed as one service but whose code is split into &lt;a href="https://en.wikipedia.org/wiki/Loose_coupling" rel="noopener noreferrer"&gt;loosely coupled&lt;/a&gt; components. This allows you to reduce levels of coupling at coding time, without having to change runtime behaviour, infrastructure etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Domain driven design helps establish boundaries
&lt;/h2&gt;

&lt;p&gt;You can start introducing new structure by &lt;a href="https://martinfowler.com/bliki/BoundedContext.html" rel="noopener noreferrer"&gt;modelling the different business contexts and reflecting that in your code&lt;/a&gt;. Each context comes with its own special terminology. If you get together with domain experts, you can agree shared terms (a.k.a a &lt;a href="https://martinfowler.com/bliki/UbiquitousLanguage.html" rel="noopener noreferrer"&gt;ubiquitous language&lt;/a&gt;) and a shared model that both the software engineers and the domain experts understand. You can then use this to decide where to split the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decide where to start
&lt;/h2&gt;

&lt;p&gt;If you are starting from a monolith, then you can gradually evolve the system towards a modular monolith by&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;organising code into logical components or packages&lt;/li&gt;
&lt;li&gt;strengthening boundaries and decoupling the components from each other&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are different ways of going about this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 1: Depth first
&lt;/h3&gt;

&lt;p&gt;One way is to pick out one or two candidate components to focus on, follow the steps below to decouple them from the rest of the application, and then continue component-by-component. &lt;/p&gt;

&lt;p&gt;The advantage of this option is it doesn't require a huge up-front commitment, and you can earn buy-in from stakeholders over time. A small number of people can work on it and then present the results to the rest of the engineering team. You then have good examples for other engineers and teams to follow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 2: Breadth first
&lt;/h3&gt;

&lt;p&gt;An alternative is to do a very rough pass over the whole codebase, that introduces the desired structure, but accepts that the interfaces are poor and components are still tightly coupled together. You then refine the components.&lt;/p&gt;

&lt;p&gt;The advantage of this approach is that engineers can start to benefit straight away from code being easier to navigate. But there's a risk that you never finish the work. You can mitigate this by implementing tooling to inform engineers of issues that need to be corrected, and showing them why it is important.&lt;/p&gt;

&lt;h2&gt;
  
  
  Steps to introduce a component
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Move the existing code, including models&lt;/li&gt;
&lt;li&gt;Establish a public API for the component&lt;/li&gt;
&lt;li&gt;Make sure all calls to the component use the public API&lt;/li&gt;
&lt;li&gt;Introduce an "anti-corruption layer"

&lt;ol&gt;
&lt;li&gt;Introduce value objects for the component&lt;/li&gt;
&lt;li&gt;The anti-corruption layer transforms from ActiveRecord models into value objects of the component&lt;/li&gt;
&lt;li&gt;The public API can now be changed to accept and return value objects, not ActiveRecord models&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kellysutton.com/2019/10/29/taming-large-rails-codebases-with-private-activerecord-models.html" rel="noopener noreferrer"&gt;Prevent outside code from instantiating component models directly&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;Modify calling code so you no longer need the anti-corruption layer (by using the modified, ActiveRecord-free API directly)&lt;/li&gt;

&lt;li&gt;Remove associations between component models and outside models

&lt;ul&gt;
&lt;li&gt;If you depend on data outside of the component, then access it through a method call that returns a value object (avoid ActiveRecord)&lt;/li&gt;
&lt;li&gt;Remove foreign keys that cross the component boundary&lt;/li&gt;
&lt;li&gt;Prefer polymorphic associations for linkages to things outside of the component. This inverts the dependency&lt;/li&gt;
&lt;li&gt;(optional) This isolation can be enforced by &lt;a href="https://learnitnow.medium.com/working-with-multi-schema-database-in-rails-7e60aef8ff86" rel="noopener noreferrer"&gt;migrating data to a separate schema&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Remove cyclical dependencies between components (&lt;a href="https://en.wikipedia.org/wiki/Acyclic_dependencies_principle" rel="noopener noreferrer"&gt;Acyclic dependencies principle&lt;/a&gt;)&lt;/li&gt;

&lt;/ol&gt;

&lt;h2&gt;
  
  
  Useful tools
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Shopify/packwerk" rel="noopener noreferrer"&gt;https://github.com/Shopify/packwerk&lt;/a&gt; allows you to make dependencies between components explicit&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://guides.rubyonrails.org/engines.html" rel="noopener noreferrer"&gt;Rails engines&lt;/a&gt; are another way of organising a rails app into components&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Additional resources on this topic
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=-ovkkvvTiRM&amp;amp;list=PL9_jjLrTYxc3dTbvb8fIuzDFGTCaEdO3a&amp;amp;index=8" rel="noopener noreferrer"&gt;RubyHack 2019: Taming Monoliths Without Microservices by Kelly Sutton&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@dan_manges/the-modular-monolith-rails-architecture-fb1023826fc4" rel="noopener noreferrer"&gt;The Modular Monolith: Rails Architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kellysutton.com/2019/10/29/taming-large-rails-codebases-with-private-activerecord-models.html" rel="noopener noreferrer"&gt;Taming large rails applications with private ActiveRecord models&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://shopify.engineering/shopify-monolith" rel="noopener noreferrer"&gt;Under deconstruction: the state of shopify's monolith&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://painlessrails.com/" rel="noopener noreferrer"&gt;Painless Rails without Overengineeriug&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pragprog.com/titles/d-kegrap/growing-rails-applications-in-practice/" rel="noopener noreferrer"&gt;Growing rails applications in practice&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sustainable-rails.com/" rel="noopener noreferrer"&gt;Sustainable web development with Ruby on Rails&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leanpub.com/package-based-rails-applications" rel="noopener noreferrer"&gt;Gradual modularization for Ruby and Rails&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://link.springer.com/chapter/10.1007/978-3-030-31646-4_3" rel="noopener noreferrer"&gt;Migrating to microservices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://noti.st/palkan/VWPOSd/between-monoliths-and-microservices" rel="noopener noreferrer"&gt;Between monoliths and microservices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>architecture</category>
      <category>rails</category>
      <category>ddd</category>
      <category>coupling</category>
    </item>
    <item>
      <title>Starting my new job NOT in lockdown</title>
      <dc:creator>Mat</dc:creator>
      <pubDate>Tue, 12 Jul 2022 18:49:43 +0000</pubDate>
      <link>https://dev.to/matmooredev/starting-my-new-job-not-in-lockdown-gbe</link>
      <guid>https://dev.to/matmooredev/starting-my-new-job-not-in-lockdown-gbe</guid>
      <description>&lt;p&gt;Last time I started a new job, &lt;a href="https://dev.to/matmooredev/starting-my-new-job-in-lockdown-39ol"&gt;everything was on fire&lt;/a&gt; and I unexpectedly became a remote worker. And it was fine (eventually). I kept working from home and I didn't actually meet most of my coworkers until my very last day when the tech team in London met for a picnic. &lt;/p&gt;

&lt;p&gt;Since I am very bothered by loud noise, having my own space makes it a LOT easier to focus on my work and be productive, without feeling completely exhausted at the end of the day. Not having to commute 2 hours a day and being able to take short breaks to cook, clean, take a walk or answer phone calls massively improves my quality of life.&lt;/p&gt;

&lt;p&gt;(Elon Musk can go fuck himself)&lt;/p&gt;

&lt;p&gt;So this time, I made the deliberate decision to look for a company that supported full remote working, and I knew a little of what to expect. I am also continuing to work part time (4 days a week). I started this arrangement in my previous role and it has helped a lot with my mood and anxiety levels. It gives me extra time to unwind and I can use the extra time for study or travel. Currently this is worth more to me than 20% of my salary.&lt;/p&gt;

&lt;p&gt;The onboarding process here was ok but not as good as my previous job. I initially felt very overwhelmed with information - it's an unfamiliar industry and I'm having to do a lot of intros with different parts of the business. There seems to have been a lot of turnover, just like my previous company, which is challenging for figuring out what's going on and how to get stuff done.&lt;/p&gt;

&lt;p&gt;The nice thing is that technically they are in the process of transitioning to microservices, and they are quite well set up for this, having leaned into &lt;a href="https://en.wikipedia.org/wiki/Domain-driven_design" rel="noopener noreferrer"&gt;Domain-Driven Design&lt;/a&gt; and with engineering management starting to think about the structures needed for teams to work autonomously without pulling in different directions. This is very interesting to me and is a nice continuation of the work I was doing in my previous company to break apart a large monolith by establishing &lt;a href="https://www.martinfowler.com/bliki/BoundedContext.html" rel="noopener noreferrer"&gt;bounded contexts&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My team are working on authorization, so I have a well defined problem area to work within but I will also get to interact with a lot of other teams since authorization affects the whole platform.&lt;/p&gt;

&lt;p&gt;Two weeks in I am starting to get the hang of things and have picked up a couple of tickets. I always find it a bit of a shock going from quite high understanding of a codebase to zero understanding, but it's helped to slow down a bit and dig into the codebase. This week I realised I didn't fully understand the data model I was working with so I spent some time creating an entity relationship diagram in mermaid and figuring out all the constraints (&lt;a href="https://github.blog/changelog/2022-02-28-gists-now-support-mermaid-diagrams/" rel="noopener noreferrer"&gt;this is now built into github and gists!&lt;/a&gt;). I feel like there is a lot of pressure to pair a lot when onboarding, but going through this process by myself helped make things click for me in a way that I don't get from just pairing.&lt;/p&gt;

</description>
      <category>onboarding</category>
      <category>career</category>
    </item>
    <item>
      <title>Get linux samba shares to show up in windows again</title>
      <dc:creator>Mat</dc:creator>
      <pubDate>Fri, 17 Jun 2022 08:45:55 +0000</pubDate>
      <link>https://dev.to/matmooredev/get-linux-samba-shares-to-show-up-in-windows-again-3b03</link>
      <guid>https://dev.to/matmooredev/get-linux-samba-shares-to-show-up-in-windows-again-3b03</guid>
      <description>&lt;p&gt;I have a media server that runs ubuntu, and today I wanted to copy some files off of it from my windows laptop. But the &lt;a href="https://www.samba.org/" rel="noopener noreferrer"&gt;samba&lt;/a&gt; shares weren't showing up in file explorer (but they showed up on fine on my macbook).&lt;/p&gt;

&lt;p&gt;I thought the whole point of Samba was compatibility with windows, so I was confused why this doesn't work now.&lt;/p&gt;

&lt;p&gt;After a &lt;a href="https://askubuntu.com/questions/661611/make-samba-share-visible-in-windows-network" rel="noopener noreferrer"&gt;bit of googling&lt;/a&gt;, it seems like this compatibility might have stopped working after the &lt;a href="https://en.wikipedia.org/wiki/WannaCry_ransomware_attack" rel="noopener noreferrer"&gt;2017 wannacry vulnerability&lt;/a&gt; was discovered.&lt;/p&gt;

&lt;p&gt;In the past windows used a broadcast protocol called NetBIOS for discovering samba shares. When windows removed support for earlier versions of the SMB protocol (vulnerable to wannacry) they also switched off this discovery mechanism.&lt;/p&gt;

&lt;p&gt;Apparently WINS is another mechanism for discovering network shares, and &lt;a href="https://documentation.clearos.com/content:en_us:5_howtos_-_samba_and_wins" rel="noopener noreferrer"&gt;you can configure samba to use it&lt;/a&gt;, but it's a bit shit as it requires you to set up a separate server that resolves names to hosts on the network (a bit like DNS).&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.truenas.com/community/resources/how-to-kill-off-smb1-netbios-wins-and-still-have-windows-network-neighbourhood-better-than-ever.106/" rel="noopener noreferrer"&gt;solution I found&lt;/a&gt; was to use a newer protocol called "WS-Discovery" (WSD).&lt;/p&gt;

&lt;p&gt;This is not built into samba, but there is an open source package you can use called &lt;code&gt;wsdd&lt;/code&gt;. I followed the &lt;a href="https://github.com/christgau/wsdd#debianubuntu" rel="noopener noreferrer"&gt;ubuntu/debian installation instructions&lt;/a&gt; and then ran it with &lt;code&gt;wsdd -v&lt;/code&gt;. My samba share instantly appeared on the windows machine.&lt;/p&gt;

&lt;p&gt;I don't know if this is the best long term solution, but as a quick fix to let me copy some files, it worked!&lt;/p&gt;

</description>
      <category>troubleshooting</category>
      <category>windows</category>
    </item>
    <item>
      <title>Please stop using ||= all the time</title>
      <dc:creator>Mat</dc:creator>
      <pubDate>Mon, 18 Apr 2022 18:17:08 +0000</pubDate>
      <link>https://dev.to/matmooredev/please-stop-using-all-the-time-2l8b</link>
      <guid>https://dev.to/matmooredev/please-stop-using-all-the-time-2l8b</guid>
      <description>&lt;p&gt;In ruby code I often see people writing code like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;
    &lt;span class="vi"&gt;@foo&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="s1"&gt;'bla bla bla'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
    &lt;span class="vi"&gt;@foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'bla bla bla'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:foo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The duck operator &lt;code&gt;||=&lt;/code&gt;  is used to lazily initialize the instance variable, instead of initializing the instance variable in the &lt;code&gt;initialize&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;This is possible in ruby because assignments are also expressions, and all methods have an implicit return value. So &lt;code&gt;@foo ||= 123&lt;/code&gt; always evaluates to &lt;code&gt;123&lt;/code&gt; but never assigns to &lt;code&gt;@foo&lt;/code&gt; more than once.&lt;/p&gt;

&lt;p&gt;While this is very concise, I found it odd when I first came to ruby after coding in other languages. It feels like a very roundabout way of doing things. If you are initializing instance variables why not use the initializer?&lt;/p&gt;

&lt;p&gt;The lazy initialization pattern is one way to avoid running an expensive computation that might not be needed. However, if you know that the initialization is always going to be required for the object to be usable, then there are no performance benefits to structuring it this way.&lt;/p&gt;

&lt;p&gt;Structuring code like this makes dependencies a bit harder to notice just from looking at the class. Users of the class might be misled into thinking that the object is fully initialized when it is not, and this can make it harder to initialize objects in a test or in other parts of the codebase.&lt;/p&gt;

&lt;p&gt;In practice, I get suspicious even when the initialization &lt;em&gt;is&lt;/em&gt; optional, because the optional initialization is still separate from the initializer and so it is less obvious when reading the code. Optional behaviour can also be sign that the class has &lt;a href="https://en.wikipedia.org/wiki/Single_responsibility_principle" rel="noopener noreferrer"&gt;more than one responsibility&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can always refactor the class to avoid this. For example, consider using &lt;a href="https://en.wikipedia.org/wiki/Dependency_injection" rel="noopener noreferrer"&gt;dependency injection&lt;/a&gt; to depend on the result of the computation instead of doing the computation internally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;foo: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="c1"&gt;# methods that use `foo`&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;

&lt;span class="kp"&gt;private&lt;/span&gt;

&lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:foo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lazy initialization is a special case of &lt;a href="https://www.pluralsight.com/tech-blog/forms-of-temporal-coupling_" rel="noopener noreferrer"&gt;temporal coupling&lt;/a&gt;: the behaviour of the class is allowed to vary depending on what order you call its methods in. The more complex the behaviour, the harder it is to reason about the code and the easier it is to introduce bugs. In this case, if other methods of your class depend on the instance variable, then they could read &lt;code&gt;nil&lt;/code&gt; if the initializer method isn't called first.&lt;/p&gt;

&lt;p&gt;Side note: I see this problem a lot with rails controllers, which do not get initialized on a per-request basis, and force you to set instance variables within your action methods as a way to communicate with the view. This design is unfortunate, but you can still avoid the problem elsewhere in your codebase.&lt;/p&gt;

&lt;p&gt;On the other hand, if all the initialization for the class is handled by the &lt;code&gt;initialize&lt;/code&gt; method, it's quite easy to make the behaviour of the class predictable. You can use &lt;a href="https://en.wikipedia.org/wiki/Command%E2%80%93query_separation" rel="noopener noreferrer"&gt;command-query responsibility segregation&lt;/a&gt; (CQRS) to ensure that messages you send to the object either ask for information, or change the objects state, but not both.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.pexels.com/photo/brown-and-white-duck-on-water-7745377/" rel="noopener noreferrer"&gt;Photo by Francesco Altamura from Pexels&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>refactoring</category>
      <category>readability</category>
    </item>
    <item>
      <title>Turn Windows into a usable development environment with WSL</title>
      <dc:creator>Mat</dc:creator>
      <pubDate>Sat, 12 Dec 2020 15:19:30 +0000</pubDate>
      <link>https://dev.to/matmooredev/turn-windows-into-a-usable-development-environment-with-wsl-4kkj</link>
      <guid>https://dev.to/matmooredev/turn-windows-into-a-usable-development-environment-with-wsl-4kkj</guid>
      <description>&lt;p&gt;I recently set up a development environment on my windows computer, using &lt;a href="https://docs.microsoft.com/en-us/windows/wsl/about" rel="noopener noreferrer"&gt;Windows Subsystem for Linux (WSL)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This lets you effectively install a Linux OS on top of windows that works seamlessly with windows tools, and it's so much better than my previous experience of trying to develop on windows.&lt;/p&gt;

&lt;h1&gt;
  
  
  Basic setup
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.microsoft.com/en-us/windows/wsl/install-win10" rel="noopener noreferrer"&gt;Install WSL&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.microsoft.com/en-us/p/windows-terminal-preview/9n0dx20hk701?activetab=pivot:overviewtab" rel="noopener noreferrer"&gt;Install a tabbed terminal&lt;/a&gt; (Optional - I expect this will be fixed eventually, but the current default terminal is pretty useless)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://code.visualstudio.com/docs/remote/wsl" rel="noopener noreferrer"&gt;Set up Visual Code to work with WSL&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://code.visualstudio.com/blogs/2020/03/02/docker-in-wsl2" rel="noopener noreferrer"&gt;Install Docker into WSL&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These steps will let you work with Visual Code and terminals, and things will mostly just work as if you were actually working on Linux.&lt;/p&gt;

&lt;p&gt;You can also run any Docker command from within WSL (after running &lt;code&gt;apt install docker&lt;/code&gt;) and it will use the Docker daemon provided by Docker for Windows.&lt;/p&gt;

&lt;h1&gt;
  
  
  (Optional) Configure the tabbed terminal
&lt;/h1&gt;

&lt;p&gt;The terminal is a bit fiddly to configure as it requires editing a JSON file. You can open it by pressing &lt;code&gt;CTRL+,&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The profile settings look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"guid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{2c4de342-38b7-51cf-b940-2309a097f518}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"hidden"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ubuntu"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Windows.Terminal.Wsl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"colorScheme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"One Half Dark"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fontSize"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At a minimum I would recommend changing the color scheme. Even after swapping out the terminal app, I found the default colors hard to read against a dark background.&lt;/p&gt;

&lt;p&gt;For more info about the options you can refer to the &lt;a href="https://docs.microsoft.com/en-gb/windows/terminal/customize-settings/profile-settings" rel="noopener noreferrer"&gt;Windows Terminal documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  WSL is pretty cool
&lt;/h1&gt;

&lt;p&gt;So far I've been really impressed with how well this all works.&lt;/p&gt;

&lt;p&gt;If you want to know more about WSL, I recommend this Hanselminutes podcast: &lt;a href="https://www.hanselminutes.com/646/inside-linux-on-windows-with-wsl-and-tara-raj" rel="noopener noreferrer"&gt;Inside Linux on Windows with WSL and Tara Raj&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Windows 10 runs Linux natively! How is that possible? Scott talks to Microsoft's Tara Raj, the Program Manager for the Windows Subsystem for Linux. How does this technology work? Tara explains the internals of WSL to Scott in this episode.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>windows</category>
      <category>productivity</category>
      <category>docker</category>
    </item>
    <item>
      <title>Please stop saying agile all the time</title>
      <dc:creator>Mat</dc:creator>
      <pubDate>Tue, 10 Nov 2020 13:34:59 +0000</pubDate>
      <link>https://dev.to/matmooredev/what-agile-development-actually-means-4k85</link>
      <guid>https://dev.to/matmooredev/what-agile-development-actually-means-4k85</guid>
      <description>&lt;p&gt;I try and avoid the word "agile". It gets used too much as a buzzword (by consulting companies and tech leaders) or as a euphemism for "whatever we're currently doing, but faster."&lt;/p&gt;

&lt;p&gt;At best, it's a vague, useless term, and at worst it can shut down valuable conversations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agile Methodologies != Agile
&lt;/h2&gt;

&lt;p&gt;People talk about agile methodologies and frameworks - SCRUM, Kanban etc. But regardless of how useful these things are, they are not really the spirit of "agile". They are just ways to organise teams that come with some "agile" baked in. Kind of like how Ruby on Rails isn't the same as "a well built web app", but having a framework can make it a lot easier to get started (providing you know what you're doing).&lt;/p&gt;

&lt;p&gt;Just like developers can still build shitty rails apps, managers can implement &lt;a href="https://www.halfarsedagilemanifesto.org/" rel="noopener noreferrer"&gt;half-arsed agile&lt;/a&gt; without really embracing the mindset.&lt;/p&gt;

&lt;h2&gt;
  
  
  The good stuff behind agile
&lt;/h2&gt;

&lt;p&gt;If we're going to pick a meaning of "agile", we could do worse than look at the agile manifesto, which proposed the idea in the first place.&lt;/p&gt;

&lt;p&gt;The writers of this manifesto came up with &lt;a href="https://www.agilealliance.org/agile101/12-principles-behind-the-agile-manifesto/" rel="noopener noreferrer"&gt;12 principles&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The agile principles
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Our highest priority is to satisfy the customer through early and continuous delivery of valuable software&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Welcome changing requirements, even late in development&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deliver working software frequently&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Business people and developers must work together daily throughout the project&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Give team members the environment and support they need, and trust them to get the job done&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The most efficient and effective method of conveying information to and within a development team is face-to-face conversation. (&lt;em&gt;IMO, this one is less important now we have better tools for remote working&lt;/em&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Working software is the primary measure of progress&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The sponsors, developers, and users should be able to maintain a constant pace indefinitely&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Continuous attention to technical excellence and good design enhances agility&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Simplicity–the art of maximizing the amount of work not done–is essential.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The best architectures, requirements, and designs emerge from self-organizing teams.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;At regular intervals, the team reflects on how to become more effective, then tunes and adjusts its behavior accordingly&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The agile principles stand on their own
&lt;/h2&gt;

&lt;p&gt;Each of these principles is clear and objective.&lt;/p&gt;

&lt;p&gt;As a team you should be able to reflect on any of them and ask "are we &lt;em&gt;really&lt;/em&gt; doing this?"&lt;/p&gt;

&lt;p&gt;If you &lt;em&gt;really&lt;/em&gt; value early and continuous delivery of valuable software, you should expect to be prioritising work based on value to the customer. If not, then why not? This is a far more interesting and productive conversation than simply "are we agile?"&lt;/p&gt;

&lt;p&gt;If you value delivering working software frequently, alarm bells should be ringing if there are long delays between writing and testing code, or if deploying code to production is too scary to do every day. &lt;/p&gt;

&lt;p&gt;If you value simplicity, you should be able to challenge scope creep. If you value technical excellence, then you should have things like code review, tests, linters etc. which enforce a standard.&lt;/p&gt;

&lt;p&gt;If &lt;em&gt;working software&lt;/em&gt; is really the primary measure of progress, you should spend time measuring how well your software is actually working for the people that use it! If the things you measure and reward just relate to inputs, like velocity, tickets completed, or lines of code written, then you probably don't value working software as much as you think.&lt;/p&gt;

&lt;h2&gt;
  
  
  This is still relevant
&lt;/h2&gt;

&lt;p&gt;This was written in 2001, but for the most part it's still very relevant today. &lt;/p&gt;

&lt;p&gt;Ron Jeffries said in 2010 that &lt;a href="https://ronjeffries.com/xprog/articles/beyond-agile-new-principles/" rel="noopener noreferrer"&gt;the agile manifesto states an ideal, and the industry has fallen short of it&lt;/a&gt;. I agree - we should be able to do better.&lt;/p&gt;

&lt;p&gt;Ultimately, it doesn't matter what you call things, you can implement any of this advice to your own team with or without calling it "agile", and doing &lt;em&gt;some&lt;/em&gt; of these things is still better than doing none of it.&lt;/p&gt;

</description>
      <category>agile</category>
      <category>teams</category>
      <category>people</category>
    </item>
    <item>
      <title>How to make a comfortable face mask</title>
      <dc:creator>Mat</dc:creator>
      <pubDate>Sat, 29 Aug 2020 16:33:48 +0000</pubDate>
      <link>https://dev.to/matmooredev/how-to-make-a-comfortable-face-mask-1eie</link>
      <guid>https://dev.to/matmooredev/how-to-make-a-comfortable-face-mask-1eie</guid>
      <description>&lt;p&gt;So I decided to learn to sew my own face masks. It's been a good project to take my mind off the apocalypse.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmkm6b7zxf8cgv28wdrx6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmkm6b7zxf8cgv28wdrx6.gif" alt="Michael: Everything okay? Janet: [giving a thumbs up] nope!" width="500" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When I first started I had never used a sewing machine before, but I learned a lot through trying stuff out and starting with something simple. This is a good way to learn things!&lt;/p&gt;

&lt;h1&gt;
  
  
  5 Patterns of Varying Difficulty
&lt;/h1&gt;

&lt;p&gt;So far I've just been focusing on making masks for myself that fit well, since better fitting masks are a) more comfortable&lt;br&gt;
and b) more effective. I've experimented with various different ways of making masks, and I've now tried 5 different patterns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rectangle
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://medium.com/@ong_yu_yang/diy-cloth-mask-tutorial-9ec8c1eacc95" rel="noopener noreferrer"&gt;How to sew a rectangular face mask&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the very first mask I used a simple rectangle pattern, because that meant I only had to sew straight lines. This worked surprisingly well, but it wasn't that comfortable because the fabric bunches up.&lt;/p&gt;

&lt;p&gt;Although I didn't make more of these, it's easy to sew (I was still figuring out how my machine worked when making it) and all the stitches are on the inside of the two layers so it doesn't matter if it's messy. My biggest mistake was mismatching the &lt;a href="https://mellysews.com/what-is-fabric-grain-understanding-grainline/" rel="noopener noreferrer"&gt;grain&lt;/a&gt; of the 2 fabric layers, and then sewing the nose wire to the wrong side so the visible design ended up upside down. Whoops.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0urp6a1qsrong35281k7.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0urp6a1qsrong35281k7.JPG" alt="Rectangle mask pattern" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pleated (I)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.nytimes.com/article/how-to-make-face-mask-coronavirus.html" rel="noopener noreferrer"&gt;How to sew a pleated face mask&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The pleated pattern is the same as the rectangle one, but it allows the mask to be shaped to the face instead of laying flat against the mouth. In practice, I found this quite tricky to make and my sewing was very messy. I made some mistakes sewing the pleats. The end result didn't fit very well and I only wore it once. I didn't use a nose wire with this one which also didn't help. Although the mask wasn't very good, it taught me a new technique (adding pleats), and it prompted me to get a &lt;a href="https://www.cucicucicoo.com/2020/01/how-to-use-a-seam-ripper-correctly-and-fast/" rel="noopener noreferrer"&gt;seam ripper&lt;/a&gt; to fix all my mistakes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Duckbill
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://shanniemakes.com/duckbill-style-face-mask-pattern/" rel="noopener noreferrer"&gt;How to sew a duckbill style face mask&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I see people wearing masks like this a lot. Each layer is made from 2 pieces of fabric stitched together in the middle. This is harder to sew because of the curves and it's fiddly to sew the layers together. It seems like with these masks it's harder to get a good fit, and I made a few of these while trying to adjust the size for my face. I found that if the straps weren't pulled tight there were a lot of gaps around the mask, and it was a lot more comfortable with a behind the head strap. By making more of these masks I got better at using my machine and I also learned how to adjust patterns.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2abm1kph1edokgc5wkmt.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2abm1kph1edokgc5wkmt.JPG" alt="Duckbill mask pattern" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Boat shape
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://hellosewing.com/3d-face-mask-diy/" rel="noopener noreferrer"&gt;How to sew a boat shaped face mask&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this pattern there is a piece of the mask that is flat against the mouth, and then there are bits above and below it that go over the nose and chin. I found this fit me a lot better than the duckbill style pattern, and it's my favourite pattern overall, although it's not so great for talking because it doesn't leave room around the mouth or stretch vertically.&lt;/p&gt;

&lt;p&gt;This pattern doesn't need to use curved lines but I still found it very tricky because you need to fold parts of the mask a certain way, which broke my brain a bit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftkoyabigtvt5294negb8.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftkoyabigtvt5294negb8.JPG" alt="Boat shaped mask pattern" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pleated (II)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.irisluckhaus.de/en/2020/diy-cloth-mask/" rel="noopener noreferrer"&gt;How to make a pleated face mask (hybrid pattern)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This one is similar to the first pleated pattern I tried, but part of the mask folds backwards to fit over the nose. I found this one reasonably comfortable but it left some gaps at the top of the mask. I'll probably make some more of these.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnv7f0xl8ws1sr9day66e.JPG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnv7f0xl8ws1sr9day66e.JPG" alt="Pleated pattern with a gap to thread the nose wire into" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Materials
&lt;/h1&gt;

&lt;p&gt;For all the masks I've made so far I've used 2 layers of cotton. I've mostly got this from &lt;a href="https://www.spoonflower.com/" rel="noopener noreferrer"&gt;Spoonflower.com&lt;/a&gt; which lets you select from lots of custom designs people have contributed. This has motivated me to learn Adobe illustrator so I can try creating my own design as well.&lt;/p&gt;

&lt;p&gt;Some of the patterns have an optional pocket to put a filter in, but I skipped this, since I didn't have anything to put in it at the time. I've since found you can buy filters for masks on amazon though.&lt;/p&gt;

&lt;p&gt;The instructions for the duckbill style mask mention that you could add some interfacing material to the fabric to make it hold it's shape better. This is material you iron on, and it makes the fabric thicker, like a shirt collar. I tried this a couple of times because some of the early masks I made deformed quite a lot when I breathed in and out. However, the thicker fabric makes it a bit harder to breathe through and I had better results changing the sewing pattern instead of the material.&lt;/p&gt;

&lt;p&gt;There is a study by &lt;a href="https://pubs.acs.org/doi/10.1021/acsnano.0c03252?goto=supporting-info" rel="noopener noreferrer"&gt;Abhiteja Konda et al&lt;/a&gt; which compares how effective different materials are at filtering droplets and suggests that some lighter fabrics can be as effective as quilter's cotton. Apparently &lt;a href="https://youtu.be/4P8_RC4oXMs" rel="noopener noreferrer"&gt;you can create really comfortable masks out of a material called "stretch chiffon"&lt;/a&gt;, which is made of silk and lycra, but I haven't tried this yet.&lt;/p&gt;

&lt;h1&gt;
  
  
  Nose wire
&lt;/h1&gt;

&lt;p&gt;I found that the nose wire is essential for getting a good fit, but the wire should be quite flexible so it fits around the nose rather than digging into it.&lt;/p&gt;

&lt;p&gt;Originally I was using some bits of electrical wire I had lying around. I also bought some wire strips that you can stick to the outside of masks, but I found them to be too stiff, and they look pretty ugly.&lt;/p&gt;

&lt;p&gt;The wire I'm using now is 20 gauge floristry wire. The higher the gauge the thinner/more flexible it is.&lt;/p&gt;

&lt;p&gt;Most of the patterns that feature a nose wire have you stitch the wire in place, but this means you can never replace it. The pleated pattern is nice because it leaves a little channel for you to insert the nose wire into at the end.&lt;/p&gt;

&lt;h1&gt;
  
  
  Straps
&lt;/h1&gt;

&lt;p&gt;Some patterns have you sew a piece of extra fabric around either side to thread the straps through, but when I've used &lt;a href="https://www.thesprucecrafts.com/bias-tape-qanda-2977495" rel="noopener noreferrer"&gt;bias tape&lt;/a&gt; it's turned out much neater. I think the masks I've made this way are less likely to fall apart.&lt;/p&gt;

&lt;p&gt;For all the masks I've made the straps are replaceable. I originally made ear straps, but I this makes the masks really uncomfortable to wear for extended periods of time, and you have to make sure that both straps are more or less the same length.&lt;/p&gt;

&lt;p&gt;The alternative is to create straps that go behind the head, which is simpler and more comfortable. To make this I thread 50cm piece of elastic through both sides, instead of a shorter piece on either side.&lt;/p&gt;

&lt;p&gt;So far I've just been tying knots to create a loop in the straps, but ideally I'd like to make them adjustable as well. I bought some toggles (like you get on drawstrings) that can be used to make an adjustable head strap, but they are a bit bulky. I think there is probably a way to make some kind of adjustable knot without using a fastener like this but I don't know how to do that yet (I'll update this post if I figure it out).&lt;/p&gt;

</description>
      <category>sewing</category>
      <category>covid</category>
      <category>hobbies</category>
    </item>
  </channel>
</rss>
