<?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>Forem: Alex Lau</title>
    <description>The latest articles on Forem by Alex Lau (@keep_calm_and_code_on).</description>
    <link>https://forem.com/keep_calm_and_code_on</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%2F1098631%2F607569b5-7d02-4293-9bea-5d550f39f9ce.jpg</url>
      <title>Forem: Alex Lau</title>
      <link>https://forem.com/keep_calm_and_code_on</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/keep_calm_and_code_on"/>
    <language>en</language>
    <item>
      <title>The Trailblazing Tax: Why Writing New Code Takes So Long</title>
      <dc:creator>Alex Lau</dc:creator>
      <pubDate>Mon, 15 Apr 2024 11:50:42 +0000</pubDate>
      <link>https://forem.com/keep_calm_and_code_on/the-trailblazing-tax-1llc</link>
      <guid>https://forem.com/keep_calm_and_code_on/the-trailblazing-tax-1llc</guid>
      <description>&lt;p&gt;I've given a lot of absolutely terrible estimates for software over the years. (But let's be honest, can anyone really say they haven't?) One factor I've grown to appreciate is "The Trailblazing Tax."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The trailblazing tax is the idea that trying to trailblaze a new pattern in code is an order of magnitude more difficult than making a change that abides by an existing pattern.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's say you're working on an application and are prompted to estimate the time it takes to create a new report to track how many new users signed up. If there's already a reporting subsystem in place, there's a reasonable chance that all of the "new" logic you'll need to add for the request is copying some existing files and writing the database query itself. If you wanted to, you could find an existing report and list out the exact new files and layers of logic that would be needed for this new report.&lt;/p&gt;

&lt;p&gt;However, let's suppose there's no reporting framework in place already. In this case, there is &lt;em&gt;so much more&lt;/em&gt; to think about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What format should the reports be generated in: PDF, CSV, etc. Engineering-wise this begs the question of if you need to add a new library as a dependency to your project.&lt;/li&gt;
&lt;li&gt;Who has permissions to see these reports?&lt;/li&gt;
&lt;li&gt;How are users accessing reports? Are they being delivered via email or are they able to download them directly in the browser? Maybe this answer depends on how long the report takes to generate and the system has to have both.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The list goes on and on... the more novel this functionality is, the more questions will certainly arise as the developer embarking on the feature request will have. Additionally, there are the "unknown unknowns" of not knowing what roadblocks will be stumbled upon throughout this process.&lt;/p&gt;

&lt;p&gt;Yes, this example compares having everything already in place to having nothing already in place. You may even be thinking to yourself, "&lt;em&gt;of course&lt;/em&gt; writing a new subsystem from scratch is going to be harder." And you're right, that notion on its own isn't too surprising I suppose.&lt;/p&gt;

&lt;p&gt;But where the danger lies is where we as developers find ourselves estimating "just one more thing" in terms of adding new functionality to a system. We often think things will be easy when there's one seemingly innocuous change.&lt;/p&gt;

&lt;p&gt;Developers give happy path estimates because that's what has the most concrete information. They don't account for non-happy path factors because it's hard/impossible to predict what those are.&lt;/p&gt;

&lt;p&gt;A happy path estimate doesn't account for a new package that has installation errors and is incompatible with other dependencies in the project until they've dug in and started writing the actual code. Maybe there's email logic in place that would let you send a report for small files, but once they hit a certain size a mail server would reject them.&lt;/p&gt;

&lt;p&gt;I know when I'm trying to conjure these types of scenarios during an estimation meeting, I feel like I come across as overly-paranoid. And I can't say that I blame anyone for thinking that I'm paranoid when everything that I'm talking about is theoretical.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spike Tickets
&lt;/h2&gt;

&lt;p&gt;In my experience, the best solution to the problem of hard-to-estimate tickets is to prove out subtasks via "spike tickets" - i.e. tickets whose sole purpose is to have a &lt;a href="https://dev.to/keep_calm_and_code_on/in-praise-of-the-timebox-3l54"&gt;timeboxed&lt;/a&gt; research task of proving out an unfamiliar concept.&lt;/p&gt;

&lt;p&gt;Revisiting the new bits of functionality from the reporting example earlier, some spike tickets could cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generating reports in new formats like PDF and CSV&lt;/li&gt;
&lt;li&gt;Creating a basic permissions framework for who can see reports&lt;/li&gt;
&lt;li&gt;Sending out even one report over email and testing various file sizes with this email attachment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The beauty of this approach is that it minimizes unknowns for a task. Rather than biting off a giant chunk of functionality, spike tickets offer bite sized bits of research effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Unknown factors have a compounding effect on scope creep for a task. Have you ever seen a task blow up to 2 or 3 times its original estimate? I'd be willing to bet that there were multiple unknowns at play that made the task much harder than it initially seemed.&lt;/p&gt;

&lt;p&gt;Software is always going to be hard to estimate, there's no getting around that. Trailblazing code is a necessary part of software development, but acknowledging and planning around it helps keep estimates in check before they get out of hand.&lt;/p&gt;

&lt;p&gt;Do you have any strategies you use for handling hard-to-estimate code? Leave a comment below!&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>development</category>
      <category>coding</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Visual Guides For Any Skill With Cloudflare AI</title>
      <dc:creator>Alex Lau</dc:creator>
      <pubDate>Sat, 13 Apr 2024 12:37:57 +0000</pubDate>
      <link>https://forem.com/keep_calm_and_code_on/visual-guides-for-any-skill-with-cloudflare-ai-5fdd</link>
      <guid>https://forem.com/keep_calm_and_code_on/visual-guides-for-any-skill-with-cloudflare-ai-5fdd</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/devteam/join-us-for-the-cloudflare-ai-challenge-3000-in-prizes-5f99"&gt;Cloudflare AI Challenge&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;My app, Guide-ify, makes a visual guide for a given life. This is done with a 3-step process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make a request to research more about the given skill.&lt;/li&gt;
&lt;li&gt;Summarize these findings.&lt;/li&gt;
&lt;li&gt;Create a corresponding visual aid for each step in the summary.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These steps each correspond to a different Cloudflare AI model (see "Using AI Models" below for more details).&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;You can try out the app here: &lt;a href="https://cloudflare-playground-bn1.pages.dev/"&gt;https://cloudflare-playground-bn1.pages.dev/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frkl9rw92v0tw2evoiipa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frkl9rw92v0tw2evoiipa.png" alt="App demo" width="800" height="1021"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My Code
&lt;/h2&gt;

&lt;p&gt;Here's the repo for my project: &lt;a href="https://github.com/MrAlexLau/cloudflare-playground"&gt;https://github.com/MrAlexLau/cloudflare-playground&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tech stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The front end app was built with Sveltekit&lt;/li&gt;
&lt;li&gt;Styling is done with Tailwind with some snippets used from Flowbite&lt;/li&gt;
&lt;li&gt;The app itself is hosted on Cloudflare Pages&lt;/li&gt;
&lt;li&gt;The logic to generate output is done with Cloudflare Workers&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Journey
&lt;/h2&gt;

&lt;p&gt;My goal was to build something fun and useful that stitched together multiple AI models. One of the exciting aspects with new AI tools is how easily they can be integrated together.&lt;/p&gt;

&lt;h4&gt;
  
  
  Getting Started With Cloudflare
&lt;/h4&gt;

&lt;p&gt;This was my first time using the Cloudflare's Pages and Workers, and I was pleased that they were both a breeze to use! In particular, &lt;a href="https://developers.cloudflare.com/pages/framework-guides/deploy-a-svelte-site/"&gt;the guide for deploying a sveltekit page&lt;/a&gt; made it very quick to get a site up and running on Pages.&lt;/p&gt;

&lt;p&gt;My next step was to submit a request to a Cloudflare Worker. The two main issues I ran into were that my requests were coming across CORS errors and that I wasn't sure how to parse the params within the worker itself.&lt;/p&gt;

&lt;p&gt;Fortunately there was sample code for both of these issues as well. I specifically referenced the &lt;a href="https://developers.cloudflare.com/workers/examples/cors-header-proxy/"&gt;CORS Header Proxy&lt;/a&gt; and the &lt;a href="https://developers.cloudflare.com/workers/examples/fetch-json/"&gt;Fetch JSON&lt;/a&gt; when writing the code for my Workers. For reference, you can view all of Cloudflare's Worker examples &lt;a href="https://developers.cloudflare.com/workers/examples/"&gt;here&lt;/a&gt; as well as longer form &lt;a href="https://developers.cloudflare.com/workers/tutorials/"&gt;tutorials&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Using AI Models
&lt;/h4&gt;

&lt;p&gt;My app uses 3 task types of &lt;a href="https://developers.cloudflare.com/workers-ai/models"&gt;Cloudflare's AI models&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Text-to-Text (for researching the given topic)&lt;/li&gt;
&lt;li&gt;Summarization (for summarizing the research results if the given research can't be easily parsed)&lt;/li&gt;
&lt;li&gt;Text-to-Image (for creating images for each step in the guide)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The code for these workers ended up being quite straightforward. For example - within a Worker, calling the Text-to-Text model is as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ai&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@cf/mistral/mistral-7b-instruct-v0.1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tell me everything there is to know about turtles.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I decided to create separate Workers for each task, which results in a separate REST endpoint that I can call for each of these operations. This worked well since I was able to test them independently and makes them more composable going forward (eg - I could create another app that uses the same Workers without having to change them at all!).&lt;/p&gt;

&lt;p&gt;One thing that's really nice about working with Cloudflare Workers is that the editor has the ability to test out the Worker on the fly before you deploy it. Specifically you can log output (as shown in the red boxes) and preview output (as shown in the blue boxes):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9mfruk8b065ynovu17yn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9mfruk8b065ynovu17yn.png" alt="Cloudflare Worker editor" width="800" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Handling Inconsistent Output
&lt;/h4&gt;

&lt;p&gt;If you work heavily with any text-based AI model, one issue you're bound to run into is output data inconsistency. Specifically, my prompt to the models would request something like "Give me an list of actionable steps for learning how to juggle." Sometimes the output would be a numeric list, other times it would be a bulleted list or simply be a paragraph of text. The AI model being called also affected the output, for example the &lt;code&gt;@cf/tiiuae/falcon-7b-instruct&lt;/code&gt; model seems to prefer bulleted lists.&lt;/p&gt;

&lt;p&gt;I needed some way of ensuring that the data I parsed could be read reliably every time while still making sure I was preserving the intended content of the message. My approach to normalizing this data was with a 2-step process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check and see if the output text from the Text-to-Text model is a numbered list. If it is, parse out each step in that list.&lt;/li&gt;
&lt;li&gt;If the output is &lt;em&gt;not&lt;/em&gt; a numbered list, then send a request to the &lt;a href="https://developers.cloudflare.com/workers-ai/models#summarization"&gt;Summarization AI model&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Using the Summarization model is especially useful since strips out any exclamations added in by the Text-to-Text model. For example, I noticed that the llama text model will often add phrases like &lt;code&gt;*giggles*&lt;/code&gt; to its output which aren't relevant for my purposes. Using the summarization model strips out this type of extraneous text as well as preserving the main points from the text model's output.&lt;/p&gt;

&lt;h4&gt;
  
  
  Next Steps
&lt;/h4&gt;

&lt;p&gt;Being that this is a project that's submitted for a hackathon, it's not production-ready code. My next steps for cleanup would be to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Restrict CORS settings used by my Worker endpoints. Right now they're open to make development easier, but that's not a practice I'd ever use for a production-grade application.&lt;/li&gt;
&lt;li&gt;Add error handling and tests.&lt;/li&gt;
&lt;li&gt;Moving worker logic directly into my project and developing via &lt;a href="https://developers.cloudflare.com/workers/wrangler/"&gt;Cloudflare's Wrangler&lt;/a&gt;.

&lt;ul&gt;
&lt;li&gt;I developed my workers directly within the Cloudflare web ui which worked well for my use case, but I feel like Wrangler would be a more smooth developer experience going forward since I wouldn't have to leave my text editor when modifying Worker code.&lt;/li&gt;
&lt;li&gt;You can find the source code for my Workers &lt;a href="https://github.com/MrAlexLau/cloudflare-playground/blob/main/workers.md"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  What I Learned
&lt;/h4&gt;

&lt;p&gt;There was quite a bit that I learned with this project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Working within Cloudflare

&lt;ul&gt;
&lt;li&gt;How to deploy to Pages and Workers&lt;/li&gt;
&lt;li&gt;Using the Workers web editor to modify, preview, and deploy updates&lt;/li&gt;
&lt;li&gt;I like that the code for Workers is version controlled on the Cloudflare side, you can easily roll back to a previous version if you'd like&lt;/li&gt;
&lt;li&gt;I was surprised that the free tier comes with &lt;strong&gt;100k Worker requests per day&lt;/strong&gt; for on the free plan&lt;/li&gt;
&lt;li&gt;Having set up these tools now, I feel like creating a new project with Pages and Workers would be &lt;em&gt;very&lt;/em&gt; fast&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Sharpening up my Svelte and Tailwind skills&lt;/li&gt;
&lt;li&gt;Submitting requests and reading responses from Cloudflare AI Models. For example, here's how to turn binary image output into a url that my app can use:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageSrc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="c1"&gt;// This worker calls the Text-to-Image `@cf/stabilityai/stable-diffusion-xl-base-1.0` AI model&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://text-to-image.mralexlau.workers.dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;requestOptions&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sentence&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// response is binary output&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;objectURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// now the return value will be a string that an &amp;lt;img&amp;gt; tag can use for its src attr&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;objectURL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Multiple Models and/or Triple Task Types&lt;/strong&gt;&lt;br&gt;
Again my app uses 3 task types of &lt;a href="https://developers.cloudflare.com/workers-ai/models"&gt;Cloudflare's AI models&lt;/a&gt;. Specifically the models that I used were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Summarization&lt;/strong&gt; - &lt;code&gt;@cf/facebook/bart-large-cnn&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Text-to-Image&lt;/strong&gt; - &lt;code&gt;@cf/stabilityai/stable-diffusion-xl-base-1.0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Text-to-Text&lt;/strong&gt; - Defaults to &lt;code&gt;@cf/tiiuae/falcon-7b-instruct&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also noticed that many of the text models have similar interfaces, so I added a setting to try out other text models. Users can select any of these text models:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@cf/meta/llama-2-7b-chat-fp16
@cf/meta/llama-2-7b-chat-int8
@cf/mistral/mistral-7b-instruct-v0.1
@cf/tiiuae/falcon-7b-instruct
@hf/google/gemma-7b-it
@hf/nousresearch/hermes-2-pro-mistral-7b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F41ixvyncbhit757inink.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F41ixvyncbhit757inink.png" alt="Selecting a text model" width="506" height="162"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;I had fun on this project! Using a new ecosystem can be daunting at times, but I felt like I was able to get up and running quickly and that future projects would be a breeze to set up. Looking forward to seeing what other AI models Cloudflare adds in the future.&lt;/p&gt;

&lt;p&gt;If you have any questions about the code or running this project, please reach out in the comments!&lt;/p&gt;

</description>
      <category>cloudflarechallenge</category>
      <category>devchallenge</category>
      <category>ai</category>
    </item>
    <item>
      <title>Make Yourself Expendable</title>
      <dc:creator>Alex Lau</dc:creator>
      <pubDate>Tue, 02 Apr 2024 10:37:46 +0000</pubDate>
      <link>https://forem.com/keep_calm_and_code_on/make-yourself-expendable-292l</link>
      <guid>https://forem.com/keep_calm_and_code_on/make-yourself-expendable-292l</guid>
      <description>&lt;p&gt;I used to idolize the "rock stars" and lone wolves who knew everything about a particular subsystem in the projects that we were working on. The only problem is that they were the &lt;em&gt;only&lt;/em&gt; ones who knew about what they were working on.&lt;/p&gt;

&lt;p&gt;I've heard people half-joke that knowing a particularly gnarly domain of an application is a form of job security. And while I think that's a grain of truth to that notion, I don't think it should ever end there.&lt;/p&gt;

&lt;p&gt;In fact, I think taking this idea to the opposite end of the spectrum is actually what makes you a better software developer. Rather than being the only person who knows how something works, being able to make a subsystem approachable to everyone else on the team is the mark of an excellent teammate. &lt;strong&gt;Paradoxically by making yourself expendable, you become an integral part of the team.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Someone who optimizes for job security ends up subconsciously embracing practices that don't help the team too much. They tend to leave the complex system as-is and only make minimal changes to it when absolutely required. If anything ever happens to them or they take a vacation, the team has to start learning everything from scratch and scramble to make changes.&lt;/p&gt;

&lt;p&gt;Whereas a developer who looks to make themselves expendable embraces habits that help amplify the team:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensuring there is adequate testing in place&lt;/li&gt;
&lt;li&gt;Refactoring the system to make it easier to work in&lt;/li&gt;
&lt;li&gt;Writing documentation so others can grasp concepts more quickly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A colleague of mine once summarized this perspective nicely when he was reflecting on his vacation time and told me, "I want to be missed, but not needed." And that's really the goal, isn't it? You want your team to appreciate your contributions and expertise, but you also want to have done enough to enable them to help themselves in your absence.&lt;/p&gt;

&lt;p&gt;Taking this notion of legacy a step further - the day is going to come when you part ways with your job. Not to be overly-existential, but how do you want to be remembered on your team? Do you want to be known as the person who knew everything but whose absence sets a tone of panic among the team? Or do you want to be remembered as the person who tackled messy code head-on and made the entire team better for it?&lt;/p&gt;

</description>
      <category>mentalmodels</category>
      <category>softwareengineering</category>
      <category>productivity</category>
      <category>beginners</category>
    </item>
    <item>
      <title>The Virtuous Cycle of Testing</title>
      <dc:creator>Alex Lau</dc:creator>
      <pubDate>Fri, 29 Mar 2024 13:18:03 +0000</pubDate>
      <link>https://forem.com/keep_calm_and_code_on/the-virtuous-cycle-of-testing-4ddp</link>
      <guid>https://forem.com/keep_calm_and_code_on/the-virtuous-cycle-of-testing-4ddp</guid>
      <description>&lt;p&gt;There are many good reasons to write tests for your code: catching bugs, preventing functionality regressions, defining better interfaces to your modules, the list goes on.&lt;/p&gt;

&lt;p&gt;But one reason isn't discussed quite as frequently that I like to call "the virtuous cycle of testing." Put simply, this virtuous cycle is the idea that &lt;em&gt;writing more tests makes it easier to write other tests in the future&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I've seen it time and time again: everybody happily embraces tests until they get too hard to write. It's easy to write the test confirming that a banner component renders on a page; it's harder to write the test that confirms that the login flow and all of its error states are working properly. Similarly, testing interactions with external APIs is a common point for teams to draw the line of what they're willing to test since setting up a tool to mock responses can be daunting.&lt;/p&gt;

&lt;p&gt;At a more abstract level, it's easy to write tests when you have a very similar bit of logic you can copy over and apply to the tests you need to write, but it's more challenging to write tests that explore a new domain and require extra setup and overhead. On top of that, if the new code being added isn't mission-critical, there may be a conscious decision to not write tests at all.&lt;/p&gt;

&lt;p&gt;And hey, I get it. Timelines force us into uncomfortable situations when it comes to delivering aspirational code. We can't write tests for every single use case in a system and that's ok. But that doesn't mean we should throw our hands in the air every time something is hard. A test suite that only covers easy-to-write use cases is better than nothing, but not much more.&lt;/p&gt;

&lt;p&gt;Just as I've seen teams where a testing suite has dwindled over time, I've also seen teams who have a culture of excellence and go to the extra mile for testing even when it's difficult.&lt;/p&gt;

&lt;p&gt;The most powerful mental model I've seen with having effective test coverage is to have the attitude "we should default to writing tests for new code unless there is a &lt;em&gt;very&lt;/em&gt; good reason not to." Once that's in place, I've seen other pieces fall into place for how the team operates: engineers pointedly ask where the tests are during code review and offer to pair program when tests are missing.&lt;/p&gt;

&lt;p&gt;Those are the codebases and teams I've enjoyed working in the most over the course of my career. The difference is palpable - from having fewer regressions in a system to making tests a normalized part of the development workflow. I suppose there's something to be said for the social dynamic as well in these cases - if everyone else is writing tests, I don't want to be "that guy" who just skips over them.&lt;/p&gt;

&lt;p&gt;The next time you find yourself tempted to skip over a test because it's difficult, I would encourage you to not think of extra testing overhead as aspirational and instead &lt;strong&gt;think of it as an investment&lt;/strong&gt;. Each time someone contributes a new test that covers one of those hard-to-trailblaze areas of code, it becomes a reference point that pays dividends for every developer on your team in the future. The more others can utilize these reference points, the more tests they'll write that will in turn help you in the future.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>tdd</category>
      <category>testing</category>
      <category>refactoring</category>
    </item>
    <item>
      <title>In Praise of the Timebox</title>
      <dc:creator>Alex Lau</dc:creator>
      <pubDate>Tue, 26 Mar 2024 12:39:04 +0000</pubDate>
      <link>https://forem.com/keep_calm_and_code_on/in-praise-of-the-timebox-3l54</link>
      <guid>https://forem.com/keep_calm_and_code_on/in-praise-of-the-timebox-3l54</guid>
      <description>&lt;p&gt;You may have heard before that when you work in software development, you eventually build up a sort of "mental toolbox." The more you try out different tools, the more you familiarize yourself with them and know when to apply the right tool for a specific task at hand. One of the most valuable tools I've found myself reaching for over the years is to use the technique of timeboxing.&lt;/p&gt;

&lt;p&gt;The concept of timeboxing is extremely simple - within a given task, you limit the maximum amount of time that you're willing to spend on that task.&lt;/p&gt;

&lt;p&gt;For example, I may dedicate an hour to adding extra flourish to a congratulatory screen after a user signs up for a product. Once that hour passes, it's "pencils down" mode. If I'm done, great! But if I'm still working on it, too bad. I can save my progress and potentially come back to it in a future task, but for now I've spent all the time I'm willing to allocate to this request.&lt;/p&gt;

&lt;p&gt;Given that this concept is so simple, why is it so powerful? I think it's because writing software involves a lot of uncertainty. Software developers are notoriously terrible at providing estimates for code, and timeboxing allows for freedom of exploration within a task that may seem daunting.&lt;/p&gt;

&lt;p&gt;Let's take a look at some of the types of tasks I frequently find myself timeboxing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Front end designs.&lt;/strong&gt; If a designer has a mockup with an unusual interaction or layout that I suspect is difficult to implement, timeboxing lets me say "let me give this a shot for an hour or two" rather than a flat out "no, we can't do this". Sometimes I'll surprise myself and the interaction is easier to implement than I expected. Other times I'll have to forgo this request and either abandon it altogether or save it for another day.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refactor suggestions from code reviews.&lt;/strong&gt; There are many code review comments I receive where teammates have suggestions for refactoring a particular piece of code, but they don't consider it a blocking suggestion for the code review. These suggestions can fall anywhere on the spectrum of impact: from having little/no side effects to refactoring critical areas of the system. If I'm unsure of the impact, timeboxing lets me investigate this difficulty without being beholden to polishing every bit of code to pristine levels before being able to merge it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What do both of these examples have in common? In general, they're tasks that are nice-to-haves rather than need-to-haves in terms of importance, as well as being fuzzy in terms of amount of effort they'll take to solve. These qualities directly tie into why exactly I find timeboxing so useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Focus on what's truly important within a request.&lt;/strong&gt; Just because the design of the submit button on the form isn't 100% implemented to spec, that shouldn't prevent all of the rest of the work from the form from being shipped. By timeboxing more fringe aspects of a feature request, you're finding that right balance between "we've done our due diligence on this and have decided to delay/forgo it" and "this can't ship until it's absolutely perfect according to the spec."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Embrace unexpected ease of implementation.&lt;/strong&gt; I happily admit that I don't always know how difficult a task may be. I'm pleasantly surprised when implementing I thought was going to be difficult turns out to be easy. This saves me from giving up on a task too eagerly. Sometimes when you say "we'll do this in another task" that day never comes, especially on teams where tackling tech debt is viewed as a luxury rather than a necessary practice within software development.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And there we have it, timeboxing! Drop a comment to let me know what you think - do you use timeboxing yourself? If so, what has your experience been like?&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>lifehacks</category>
      <category>mentalmodels</category>
      <category>webdev</category>
    </item>
    <item>
      <title>OO to Elixir: Clean Code With Pattern Matching</title>
      <dc:creator>Alex Lau</dc:creator>
      <pubDate>Mon, 29 Jan 2024 17:30:19 +0000</pubDate>
      <link>https://forem.com/keep_calm_and_code_on/oo-to-elixir-clean-code-with-pattern-matching-1abk</link>
      <guid>https://forem.com/keep_calm_and_code_on/oo-to-elixir-clean-code-with-pattern-matching-1abk</guid>
      <description>&lt;p&gt;One of the joys of working with new programming languages is uncovering new ways to solve problems. New patterns and tools within languages expand your horizons in terms of how to structure code.&lt;/p&gt;

&lt;p&gt;A few years ago, I went from working with Ruby to working with Elixir and was very impressed with how the concept of "pattern matching" allowed me to write elegant functions. Even if you don't know Ruby or Elixir, the explanations below should be simple enough to demonstrate the power of pattern matching.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern Matching Tuples
&lt;/h3&gt;

&lt;p&gt;In Ruby, although it's not a particularly common pattern, you can assign multiple variables on the left hand side of an assignment:&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="n"&gt;irb&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"world"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;irb&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="ss"&gt;:hello&lt;/span&gt;
&lt;span class="n"&gt;irb&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="s2"&gt;"world"&lt;/span&gt;
&lt;span class="n"&gt;irb&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
&lt;span class="mi"&gt;42&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Per the &lt;a href="http://elixir-lang.github.io/getting-started/pattern-matching.html"&gt;Elixir pattern matching docs&lt;/a&gt; you can do the same thing and assign multiple variables to a tuple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"world"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"world"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="ss"&gt;:hello&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="s2"&gt;"world"&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
&lt;span class="mi"&gt;42&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, in Elixir you can also specify explicit literals on the left hand side of the assignment that will &lt;em&gt;only&lt;/em&gt; perform the assignment when the right side matches that literal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"world"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"world"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:not_hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"world"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;MatchError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="n"&gt;hand&lt;/span&gt; &lt;span class="n"&gt;side&lt;/span&gt; &lt;span class="ss"&gt;value:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"world"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Knowing this, a common pattern in Elixir is to return a tuple that begins with &lt;code&gt;:ok&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;update_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; was updated."&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="s2"&gt;"This clause will match when there is an error and update_users can populate the errors variable"&lt;/span&gt;
  &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="s2"&gt;"This clause matches any unmatched paths in this case statement"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whereas in Ruby, you may write something like this:&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;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# assume this returns a boolean&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; was updated."&lt;/span&gt;
&lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt;
  &lt;span class="s2"&gt;"The user class can hold errors in its corresponding function in the object"&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="s2"&gt;"Something went wrong with the update"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One thing that's nice about the Elixir approach is that you get all of the data you need from the &lt;code&gt;update_user&lt;/code&gt; function itself (whether it's error details or a user object). Rather than the equivalent ruby approach where you have to call a separate &lt;code&gt;errors&lt;/code&gt; function to get the error details.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern Matching function Signatures
&lt;/h3&gt;

&lt;p&gt;Pattern matching becomes especially powerful when used in function signatures since it narrows the scope of the function before the actual body of the function is executed. The &lt;a href="http://elixir-lang.github.io/getting-started/modules-and-functions.html#named-functions"&gt;Elixir function documentation&lt;/a&gt; give this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;Math&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;zero?&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="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;zero?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;when&lt;/span&gt; &lt;span class="n"&gt;is_integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;false&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, &lt;code&gt;Math.zero?(0)&lt;/code&gt; would execute the first version of the function, whereas calling &lt;code&gt;Math.zero?(1)&lt;/code&gt; would call the 2nd version of the function. Although this example is trivial, it's easy to see how narrow the scope becomes for each of the function bodies. By the time you reach the code in the body of the &lt;code&gt;zero?(0)&lt;/code&gt; version of the function, you're guaranteed to know that the value being passed in is zero.&lt;/p&gt;

&lt;p&gt;Conditionals are less frequently needed in Elixir since you can just pattern match against arguments and have multiple versions of the same function. Take this example in ruby:&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;character_damage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;weapon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_damage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;# bonus damage is zero by default&lt;/span&gt;
  &lt;span class="n"&gt;bonus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;character_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:paladin&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;weapon&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:bow&lt;/span&gt;
      &lt;span class="n"&gt;bonus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base_damage&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
    &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;weapon&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:sword&lt;/span&gt;
      &lt;span class="n"&gt;bonus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base_damage&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;character_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:wizard&lt;/span&gt;
    &lt;span class="c1"&gt;# wizards always get a bonus&lt;/span&gt;
    &lt;span class="n"&gt;bonus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;base_damage&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;bonus&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Elixir, you could rewrite all of these as a bunch of 1 line functions!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;character_damage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:paladin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:bow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_damage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;base_damage&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;character_damage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:paladin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:sword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_damage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;base_damage&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# _ matches anything&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;character_damage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:wizard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_damage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;base_damage&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# if none of the other signatures match, default to no bonus damage&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;character_damage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_damage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;base_damage&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An alternative way to write the ruby example with a more object-oriented approach is:&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="n"&gt;paladin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Paladin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;paladin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_base_damage!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;paladin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;damage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:bow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 1 + 5 = 6&lt;/span&gt;
&lt;span class="n"&gt;paladin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;damage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:sword&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 1 + 10 = 11&lt;/span&gt;

&lt;span class="n"&gt;wizard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Wizard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;wizard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_base_damage!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;wizard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;damage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 1 + 12 = 13&lt;/span&gt;

&lt;span class="n"&gt;generic_character&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;GenericCharacter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="n"&gt;generic_character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_base_damage!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;generic_character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;damage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;#=&amp;gt; 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Bringing It Together
&lt;/h3&gt;

&lt;p&gt;I love pattern matching because it allows me to write very concise, specific functions. Coming from a background of working in object-oriented languages, another way to think about what pattern matching in function signatures does is affords you the convenience of specificity that object-oriented models has, but at the function level rather than the class level.&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>ruby</category>
      <category>cleancode</category>
      <category>refactorit</category>
    </item>
    <item>
      <title>How the 80/20 Rule Can Make You a Better Developer</title>
      <dc:creator>Alex Lau</dc:creator>
      <pubDate>Wed, 24 Jan 2024 16:55:13 +0000</pubDate>
      <link>https://forem.com/keep_calm_and_code_on/how-the-8020-rule-can-make-you-a-better-developer-45nb</link>
      <guid>https://forem.com/keep_calm_and_code_on/how-the-8020-rule-can-make-you-a-better-developer-45nb</guid>
      <description>&lt;p&gt;In a previous job, I had a brief stint where I had transitioned from a software engineer to a product manager. I learned many things in this role (including that I &lt;em&gt;did not&lt;/em&gt; want to be a product manager in the long term), but by far the biggest lesson was seeing the 80/20 rule in practice and how it affected my approach to software.&lt;/p&gt;

&lt;p&gt;In case you're not familiar with the 80/20 rule (formally known as the &lt;a href="https://en.wikipedia.org/wiki/Pareto_principle"&gt;Pareto Principle&lt;/a&gt;), it's the idea that 80% of results come from 20% of the work. Conversely (and arguably more importantly!) is the idea that the remaining 20% of work takes 80% of the time to finish.&lt;/p&gt;

&lt;p&gt;I had known about this principle before working as a product manager, but seeing it in action while working with developers was another story. I was used to being an engineer and going to my product manager to tell them that a feature was more difficult than originally estimated. It was a strange feeling to be on the other side of the table where developers would come to me echoing that same sentiment.&lt;/p&gt;

&lt;p&gt;What was particularly interesting was seeing &lt;em&gt;exactly what parts&lt;/em&gt; of a feature request developers were getting hung up on. &lt;strong&gt;Often the specific parts they were struggling to accommodate was of little/no consequence to me as the feature requester.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example, a feature may be something like "Add a new pricing page to our website as depicted by the attached mockup." And if a developer came to me saying something like, "this is actually going to take much longer than we estimated because the design of these dropdown boxes is totally new." Chances are I'd be happy to downscope that design or maybe even drop it altogether. The main goal was to add a new pricing page, not recreate a mockup verbatim for the sake of pedantry.&lt;/p&gt;

&lt;p&gt;I'd be lucky if that conversation happened early on in the sprint, but often I wouldn't hear about problems until we were close to feature freeze which was always the most painful and rushed time to have to make those sorts of decisions. Many headaches would've been curbed or avoided entirely had I had better insight into which parts of a feature request were going to be the most problematic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leveraging the 80/20 Rule to Your Advantage
&lt;/h2&gt;

&lt;p&gt;Now that I've returned to my role as an engineer, I feel like I have more empathy and can communicate better than ever with my teammates.&lt;/p&gt;

&lt;p&gt;How can the 80/20 rule make you a better software developer? &lt;strong&gt;By knowing that not every feature needs to be implemented verbatim.&lt;/strong&gt; In fact, you should always be asking yourself: "What is the problem that this feature is trying to solve?" when building software. In the case of the pricing page, the answer was to convey pricing information to users, not to have the fanciest dropdowns a user had ever seen. Know that your product manager and designers don't have complete information about a system, and you're actively doing them a favor by calling out hard-to-implement features early on in the development process.&lt;/p&gt;

&lt;p&gt;Hope you enjoyed this read! If you liked this article, I talk about the 80/20 rule more in my book &lt;a href="https://keepcalmandcodeon.com/"&gt;Keep Calm and Code On&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>softwaredevelopment</category>
      <category>productivity</category>
      <category>selfimprovement</category>
    </item>
    <item>
      <title>Supercharge Your Workflow With Chrome Profiles</title>
      <dc:creator>Alex Lau</dc:creator>
      <pubDate>Fri, 25 Aug 2023 19:37:00 +0000</pubDate>
      <link>https://forem.com/keep_calm_and_code_on/supercharge-your-workflow-with-chrome-profiles-pc9</link>
      <guid>https://forem.com/keep_calm_and_code_on/supercharge-your-workflow-with-chrome-profiles-pc9</guid>
      <description>&lt;p&gt;One of the most impactful changes I’ve made to my workflow in the past couple of years is to use &lt;a href="https://support.google.com/chrome/answer/2364824"&gt;multiple profiles&lt;/a&gt; in Chrome. This allows me to run Chrome for multiple users at the same time.&lt;/p&gt;

&lt;p&gt;You may be thinking, “Ok, that’s kind of nice – but I can already &lt;a href="https://support.google.com/accounts/answer/1721977?hl=en"&gt;sign in to different gmail accounts&lt;/a&gt; within the same browser window. So what’s the additional benefit here?” &lt;/p&gt;

&lt;h3&gt;
  
  
  Separate Contexts
&lt;/h3&gt;

&lt;p&gt;The real advantage to maintaining these profiles is that each profile gives you a &lt;strong&gt;separate, isolated Chrome context&lt;/strong&gt;. That means that all of your bookmarks, history, extensions, saved usernames, etc. all stay within the context of one profile.&lt;/p&gt;

&lt;p&gt;As a developer, this is a lifesaver. I can have one profile linked to my personal email and one linked to my work email. The work one has an uncluttered list of bookmarks that only pertain to work, remembers my test account usernames, only uses extensions that are relevant to work, and doesn’t reveal embarrassing personal search history like "Can you live off of pizza alone?" when I’m in the middle of a demo. If I were to leave my company my personal profile remains unaffected - there’s no need to rearrange my bookmarks or do any kind of cleanup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing With Multiple User Accounts
&lt;/h3&gt;

&lt;p&gt;You’re probably already done something like this anyway by using an incognito window when testing an application. This does accomplish the goal of keeping your main browser in an isolated environment from your test environment. But separate profiles lets you take this a step further.&lt;/p&gt;

&lt;p&gt;Profiles let you test an application with multiple user sessions simultaneously with ease. I regularly keep a separate browser tab open for a demo admin user, a logged out user, and a logged user for applications. Each one has their own history and can persist their session across days or weeks unlike an incognito window which will be lost when it’s closed.&lt;/p&gt;

&lt;p&gt;Setting up multiple profiles separates your concerns for how your browser experience is organized. When you only have one Chrome profile, it’s easy for browser data to get out of hand – especially with regard to having too many bookmarks and extensions. Adopting multiple profiles allows you to keep what’s most relevant at hand, without having to open different browsers at the same time.&lt;/p&gt;

&lt;h3&gt;
  
  
  It’s Not Just Chrome!
&lt;/h3&gt;

&lt;p&gt;Although I’ve mentioned the profiles feature specifically for Chrome, it also exists in other major browsers like Firefox, Edge, and now even Safari. The same techniques and benefits outlined here can be used in any of those browsers as well. &lt;/p&gt;

</description>
      <category>testing</category>
      <category>browser</category>
      <category>productivity</category>
      <category>frontend</category>
    </item>
  </channel>
</rss>
