<?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: Mel Kaulfuss</title>
    <description>The latest articles on DEV Community by Mel Kaulfuss (@melissakaulfuss).</description>
    <link>https://dev.to/melissakaulfuss</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%2F451668%2Feb74c90f-34d4-42f6-9ead-37cb6c223f8e.jpeg</url>
      <title>DEV Community: Mel Kaulfuss</title>
      <link>https://dev.to/melissakaulfuss</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/melissakaulfuss"/>
    <language>en</language>
    <item>
      <title>Applying SRE Principles to CI/CD</title>
      <dc:creator>Mel Kaulfuss</dc:creator>
      <pubDate>Mon, 27 Nov 2023 03:36:59 +0000</pubDate>
      <link>https://dev.to/melissakaulfuss/applying-sre-principles-to-cicd-3k3o</link>
      <guid>https://dev.to/melissakaulfuss/applying-sre-principles-to-cicd-3k3o</guid>
      <description>&lt;p&gt;The software landscape in 2023 is complex. Actually, it’s always been, but we’re &lt;em&gt;really&lt;/em&gt; feeling it this year. We work in distributed teams, have monolithic codebases, gargantuan test suites, and microservices that stretch as far as the eye can see. Not to mention our teams are becoming leaner and leaner.&lt;/p&gt;

&lt;p&gt;One thing working in our favor is that we have Continuous Integration and Continuous Deployment (CI/CD) at the heart of our software delivery lifecycles. CI/CD allows us to ship code easily and frequently, with a high level of trust that our end users won’t be impacted by bugs (or at least that’s what CI/CD promises to deliver). Sometimes though, our ability to ship without friction is hampered by flaky unreliable test suites, very slow builds, or even simply waiting around for builds to start running. We end up lacking confidence in the system, whose number one job is to provide it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pain of flaky tests
&lt;/h2&gt;

&lt;p&gt;A flaky test is a test that passes most of the time but sometimes fails for no immediately obvious reason. Flaky tests are caused by many things, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test ordering&lt;/li&gt;
&lt;li&gt;Missing elements in integration specs&lt;/li&gt;
&lt;li&gt;Dates, time, and timezones&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I began digging into the problem more to understand exactly how painful flaky tests were. Turns out that in one month, Buildkite users spent a cumulative 9,413 days retrying failed steps. That’s 59.24 days wasted every day that month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To put those numbers into context, you can get to Mars and back 17 times in 9,413 days.&lt;/strong&gt; Suppose you also factor in time wasted across all CI/CD platforms (and especially ones that make you re-run the entire build for each failure rather than an individual failed step). In that case, we’ve suddenly got time to explore strange new worlds and seek out new life and civilizations at the very edge of our galaxy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpxueft9vodj7u0yry3jk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpxueft9vodj7u0yry3jk.png" alt="GIFS that show a button being repeatedly hit."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Back when I was a junior developer, there was a smoke test in our pipeline that never passed. I recall asking, “Why is this test failing?” The Senior Developer I was pairing with answered, “Ohhh, that one, yeah it hardly ever passes.” From that moment on, every time I saw a CI failure, I wondered: “Is this a flaky test, or a genuine failure?”&lt;/p&gt;

&lt;p&gt;The additional overhead of lost flow (and focus) is real–we become distracted and potentially occupy ourselves with Twitter, X, Bluesky scrolls and Slack messages. According to a &lt;a href="https://hbr.org/2014/04/help-your-employees-find-flow" rel="noopener noreferrer"&gt;Harvard Business Review article&lt;/a&gt;, it takes over 23 minutes to get back on task after an interruption. So if we’re playing whack-a-mole with flaky tests while battling mega-slow builds, we’re in a very bad place.&lt;/p&gt;

&lt;p&gt;Developers need to be able to rely on the systems and tools they use to get the job done–our CI/CD systems need to provide fast, reliable feedback about the software we’re delivering. When that doesn’t happen, we’ve got some problems, and so do the end users of our software.&lt;/p&gt;

&lt;h2&gt;
  
  
  SRE principles to the rescue
&lt;/h2&gt;

&lt;p&gt;Regardless of whether you can &lt;em&gt;literally&lt;/em&gt; deploy on a Friday or not, asking, “Can I deploy on a Friday afternoon?” is an awesome way to gauge a team’s sentiment about how reliable their pipeline-to-production workflows are. We should all be able to say yes when asked the question, and if we can’t, we have some work to do to restore trust.&lt;/p&gt;

&lt;p&gt;It turns out Site Reliability Engineers (SREs) know a thing or two about ensuring our systems are reliable (as the name suggests), so let’s look at some of the principles they use to guide their efforts.&lt;/p&gt;

&lt;p&gt;Google’s book &lt;a href="https://sre.google/sre-book/introduction/" rel="noopener noreferrer"&gt;Site Reliability Engineering - How Google Runs Production Systems&lt;/a&gt; compares DevOps to SRE:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"&lt;em&gt;The term “DevOps” emerged in industry in late 2008…Its core principles—involvement of the IT function in each phase of a system’s design and development, heavy reliance on automation versus human effort, the application of engineering practices and tools to operations tasks—are consistent with many of SRE’s principles and practices.&lt;/em&gt;" &lt;br&gt;
&lt;strong&gt;Site Reliability Engineering – How Google Runs Production Systems&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It goes on to say that DevOps could be viewed as a generalization of several core SRE principles to a wider organizational context. And that SRE could be viewed as a specific implementation of DevOps with some idiosyncratic extensions.&lt;/p&gt;

&lt;p&gt;DevOps thinking encourages us to see accidents as a normal part of software delivery. We saw blameless culture evolve because of this principle, and also that tooling, human systems, and culture are interrelated. DevOps is a way of thinking and working focused on bringing people together (from Development and Operations), improving collaboration, and leveraging automation and tooling to further improve how we deliver software. SRE, on the other hand, is a little more focused on the practical: improving operational practices, efficiency, and as the name suggests, the reliability of our core systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Only as reliable as strictly necessary
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F55622fgk5wk61wcumhw3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F55622fgk5wk61wcumhw3.png" alt="One does not simply 100% reliability"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"&lt;em&gt;...it is difficult to do your job well without clearly defining well. SLOs provide the language we need to define well.&lt;/em&gt;" &lt;br&gt;
– &lt;strong&gt;Theo Schlossnagle Circonus, Seeking SRE&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Common principles in both DevOps and SRE involve measurement, observability, and information about the health of systems and services. Whilst SRE works to ensure systems are reliable, 100% reliability is never the goal. SRE seeks to ensure systems are only as reliable as strictly necessary.&lt;/p&gt;

&lt;p&gt;SRE uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Service Level Objectives (SLO)&lt;/strong&gt; to define what level of reliability is guaranteed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service Level Indicators (SLI)&lt;/strong&gt; to measure how things are tracking against the SLO.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error budgets&lt;/strong&gt; to reflect how much, or for how long, a service can fail to meet the SLO without consequence.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;"&lt;em&gt;SLIs/SLOs shift the mindset from ‘I’m responsible for X service in a very complex, vague backend environment way’ to 'If I don’t meet this SLO my customer is going to be unhappy.&lt;/em&gt;'" &lt;br&gt;
– &lt;strong&gt;Lucia Craciun &amp;amp; Dave Sanders, Putting Customers first with SLIs and SLOs, The Telegraph (Engineering)&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We need solid metrics as an objective foundation for conversations in our teams and with leadership, and we need to agree that these metrics represent an accurate picture of reality. If we rely on data to actualize the cost associated with developer pain, it’s no longer about feelings and is far easier to mitigate. SLOs, SLIs, and Error Budgets provide a framework to prioritize the upkeep and maintenance of key systems, which can often be the hardest thing to make time for. SREs generally apply these to production services and systems, but there’s no reason we can’t apply them to CI/CD.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started with SLOs
&lt;/h2&gt;

&lt;p&gt;First, you start with understanding what everyone involved expects from the system, and then you should focus on building a shared understanding.&lt;/p&gt;

&lt;p&gt;Ask some questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is the system in question?

&lt;ul&gt;
&lt;li&gt;Is it CI, CD, or both?&lt;/li&gt;
&lt;li&gt;Are you limiting the scope to an application’s test suite?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;What about the test suite? Speed? Reliability?

&lt;ul&gt;
&lt;li&gt;Who are the system’s different stakeholders?&lt;/li&gt;
&lt;li&gt;Who relies on the system?&lt;/li&gt;
&lt;li&gt;Who maintains the system?&lt;/li&gt;
&lt;li&gt;What is important to everyone?&lt;/li&gt;
&lt;li&gt;What’s currently working?&lt;/li&gt;
&lt;li&gt;What isn’t working?&lt;/li&gt;
&lt;li&gt;What needs to improve?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Once you have built this shared understanding, it’s time to agree on some SLOs, SLIs, and reasonable error budgets.&lt;/p&gt;

&lt;p&gt;For example, if you want to reduce the time developers need to wait for a build to kick off, a good SLO could be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SLO&lt;/strong&gt;: Builds should start running within one minute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SLI&lt;/strong&gt;: Total wait time for a build to start.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error budget&lt;/strong&gt;: 33 builds that take more than 1 minute to start running in a four-week period.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another example might involve the need to have speedy feedback loops:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SLO&lt;/strong&gt;: Developers have commits tested and notified in five minutes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SLI&lt;/strong&gt;: Total build run time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error budget&lt;/strong&gt;: 33 builds that finish in more than five minutes in a four-week period.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or you might want to mitigate the problems associated with flaky tests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SLO&lt;/strong&gt;: Test suite reliability should be greater than 87%.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SLI&lt;/strong&gt;: Test suite reliability score.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error budget&lt;/strong&gt;: 77 test runs with a reliability score of less than 87% in a four-week period.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your SLOs will naturally evolve from understanding what everyone expects from the system. How you get your SLIs will vary depending on what CI/CD platform you use and what metrics you need to collect. For SLIs like build wait time and total build run time, they should be metrics that are available via your CI/CD platform. Buildkite has &lt;a href="https://buildkite.com/docs/agent/v3/tracing#using-opentelemetry-tracing" rel="noopener noreferrer"&gt;OpenTelelemetry tracing&lt;/a&gt; built into the agent that allows you to send build agent health and performance metrics to an OpenTelemetry collector, a CLI tool to request and build runtime metrics from the API, to be collected and visualized as you need. And for test suite reliability, Buildkite has tooling to detect and manage flaky tests, with a suite reliability percentage score for test suites. &lt;a href="https://www.honeycomb.io/" rel="noopener noreferrer"&gt;Honeycomb&lt;/a&gt; and &lt;a href="https://docs.datadoghq.com/continuous_integration/" rel="noopener noreferrer"&gt;Datadog&lt;/a&gt; also have products to integrate with CI tools to gain valuable metrics and insights.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using error budgets to maintain focus
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjr3zl1vdn8op7kx2q32z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjr3zl1vdn8op7kx2q32z.png" alt="Drake holding up hand refusing to hear that "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"&lt;em&gt;SLOs are a powerful weapon to wield against micromanagers, meddlers and feature-hungry PMs. They are an API for your engineering team.&lt;/em&gt;" &lt;br&gt;
–– &lt;strong&gt;Charity Majors, SLOs Are the API for Your Engineering Team&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s look at the error budget in our test suite reliability-related SLO above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SLO&lt;/strong&gt;: Test suite reliability should be greater than 87%.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SLI&lt;/strong&gt;: Test suite reliability score.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error budget&lt;/strong&gt;: 77 test runs with a reliability score of less than 87% in a four-week period.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While we are “within budget,” we can maintain momentum and focus on the work we’re already doing, ignoring any issues that may distract us. However, once that budget is spent, and we’ve had more than 77 test suite executions with a reliability score of under 87% in four weeks, we’ll need to have brokered an agreement on what happens. Ideally, your teams would shift focus to the work required to get your SLI back to meeting your SLO.&lt;/p&gt;

&lt;p&gt;The perception of grinding to a halt to fix things in a system once an error budget isn’t met can be a huge point of contention when implementing SLOs and SLIs. Since everyone has agreed on what’s important to track, you’ll have SLI metrics in place, so the discussions are centered around hard facts and the visible monetary cost associated with developer pain.&lt;/p&gt;

&lt;p&gt;Besides being able to remain focused on our work, in her blog post &lt;a href="https://www.infoq.com/articles/slos-engineering-team-API/" rel="noopener noreferrer"&gt;SLOs Are the API for Your Engineering Team&lt;/a&gt; Honeycomb CTO Charity Majors says, “SLOs give you the ability to push back when demands from other parties exceed your capacity to deliver what the business has deemed most important.” This sounds like the kind of thing we need to be able to lean on from time to time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;If you’re not sure how to get started, start small! With one SLO. For that SLO, guarantee to maintain the level of reliability the system currently performs at. That’s a great first step, and you can commit to a better percentage when you’re further along in your SRE practices.&lt;/p&gt;

&lt;p&gt;It’s important to remember that SLOs, SLIs, and Error budgets are a journey, there may be dragons, but change is fine, and revising these agreements can happen until they work for everyone. Understand and define expectations, set some SLOs, and prioritize mitigating developer pain to rebuild trust in your system—because everyone should be able to deploy on a Friday afternoon (even if they can’t).&lt;/p&gt;

&lt;p&gt;If you’ve tried this approach, let me know what’s worked for you on &lt;del&gt;Twitter&lt;/del&gt; &lt;a href="https://twitter.com/MelissaKaulfuss" rel="noopener noreferrer"&gt;X&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/gP3VBi8lmcA"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This post is based on my talk of the same title: &lt;em&gt;Applying SRE Principles to CI/CD&lt;/em&gt;, and was originally published on the &lt;a href="https://buildkite.com/blog/applying-sre-principles-to-cicd" rel="noopener noreferrer"&gt;Buildkite blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>sre</category>
      <category>cicd</category>
      <category>metrics</category>
      <category>slo</category>
    </item>
    <item>
      <title>Extending Buildkite with plugins: HashiCorp Vault</title>
      <dc:creator>Mel Kaulfuss</dc:creator>
      <pubDate>Tue, 05 Sep 2023 02:37:06 +0000</pubDate>
      <link>https://dev.to/melissakaulfuss/extending-buildkite-with-plugins-hashicorp-vault-3k5p</link>
      <guid>https://dev.to/melissakaulfuss/extending-buildkite-with-plugins-hashicorp-vault-3k5p</guid>
      <description>&lt;p&gt;In this post, I'll cover what Buildkite plugins are, how they work in &lt;a href="//https:buildkite.com/home"&gt;Buildkite&lt;/a&gt; and use the &lt;a href="https://github.com/buildkite-plugins/vault-secrets-buildkite-plugin" rel="noopener noreferrer"&gt;Vault secrets plugin&lt;/a&gt; as my example. The Vault plugin is the recommended way to integrate with &lt;a href="https://www.vaultproject.io/" rel="noopener noreferrer"&gt;HashiCorp Vault&lt;/a&gt; and is a great option for managing and protecting your secrets.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's a plugin?
&lt;/h2&gt;

&lt;p&gt;Buildkite's CI/CD pipelines support extensibility in so many ways, and plugins play a big role in this composability. &lt;/p&gt;

&lt;p&gt;Plugins are small self-contained pieces of functionality that can be included in pipelines so you can customize Buildkite to your specific requirements. You might already be familiar with Jenkins plugins, or CircleCI Orbs, Buildkite plugins are very similar, but are even more flexible.&lt;/p&gt;

&lt;p&gt;They can be used to modify the behavior of jobs in your pipeline using one or more of Buildkite's ten &lt;a href="https://buildkite.com/docs/agent/v3/hooks" rel="noopener noreferrer"&gt;job lifecycle hooks&lt;/a&gt;. Each hook modifies a different part of the job lifecycle, for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting up the environment.&lt;/li&gt;
&lt;li&gt;Checking out the code.&lt;/li&gt;
&lt;li&gt;Running commands.&lt;/li&gt;
&lt;li&gt;Handling artifacts.&lt;/li&gt;
&lt;li&gt;Cleaning up the environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh1p6pw5xs4w8zbi818iv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh1p6pw5xs4w8zbi818iv.png" alt="A plugin hooking into the job lifecycle"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Plugins can be open source and available for anyone to use or private and kept in a repository only your organization and agents can access. If you keep them private, they can be in a centralized place or the same repository as your source code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using plugins
&lt;/h2&gt;

&lt;p&gt;If you're familiar with Jenkins, you'll be familiar with having a central or web-based plugin management system. Like many other CI/CD tools, Buildkite lets you manage plugins directly in your pipeline definitions. The advantage of this is that they're decentralized, more easily version controlled, and only included in the pipelines that really need them.&lt;/p&gt;

&lt;p&gt;You use plugins in pipeline command steps to access a library of commands or to perform actions. To add a plugin to a command step, use the plugins attribute in your pipeline definition. The plugins attribute accepts an array so you can add multiple plugins to the same step.&lt;/p&gt;

&lt;p&gt;For example, the following pipeline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;runs a &lt;code&gt;test.sh&lt;/code&gt; script&lt;/li&gt;
&lt;li&gt;if needed, it logs into Docker using the &lt;a href="https://github.com/buildkite-plugins/docker-login-buildkite-plugin" rel="noopener noreferrer"&gt;Docker Login plugin&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;It then runs the &lt;code&gt;test.sh&lt;/code&gt; script inside an &lt;code&gt;app&lt;/code&gt; service container using the &lt;a href="https://github.com/buildkite-plugins/docker-compose-buildkite-plugin" rel="noopener noreferrer"&gt;Docker Compose plugin&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;steps:
  - command: test.sh
    plugins:
      - docker-login#v1.2.3:
          username: mello
      - docker-compose#v4.14.0: 
          run: app
          image-repository: index.docker.io
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which is the equivalent to running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose run app test.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Securing your secrets with the Vault secrets plugin
&lt;/h2&gt;

&lt;p&gt;Buildkite is an extremely secure CI/CD tool, you don't store any secrets in Buildkite, we don't have (or want) access to them. Because &lt;a href="https://buildkite.com/docs/agent" rel="noopener noreferrer"&gt;build agents&lt;/a&gt; (aka runners in other CI tools) run on your infrastructure, secrets are only accessed within the boundaries of your environment.&lt;/p&gt;

&lt;p&gt;A secret in CI/CD is anything you want to tightly control access to, such as API keys, passwords, and certificates. &lt;a href="https://www.vaultproject.io/" rel="noopener noreferrer"&gt;HashiCorp Vault&lt;/a&gt; is an identity-based secrets and encryption management system. Along with securely storing secrets and granting access, it also allows you to create ephemeral/short-lived tokens, and because it's centrally managed it makes revoking and rotating tokens extremely simple should the need arise. You can use Vault in your pipelines to manage your secrets securely.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/buildkite-plugins/vault-secrets-buildkite-plugin" rel="noopener noreferrer"&gt;Vault secrets plugin&lt;/a&gt; is developed and maintained by Buildkite as the recommended way to use Vault in your pipelines. It aims to provide a general solution most teams can use to integrate with Vault.&lt;/p&gt;

&lt;p&gt;The plugin allows build agents to authenticate to Vault and acquire pipeline secrets while running a job. It also enables you to create more granular policies for access by agents and pipelines, making it easier to have a better security posture.&lt;/p&gt;

&lt;p&gt;The plugin wraps &lt;a href="https://developer.hashicorp.com/vault/docs/commands" rel="noopener noreferrer"&gt;Vault's CLI&lt;/a&gt; and allows you to configure multiple authentication methods to acquire a token, including AppRole, AWS, and JWT.&lt;/p&gt;

&lt;p&gt;For example, the following pipeline accesses Vault using AppRole authentication:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;steps:
  - command: ./run_build.sh
    plugins:
      - vault-secrets#v1.1.0:
          server: "https://my-vault-server"
          path: secret/buildkite
          auth:
            method: "approle"
            role-id: "my-role-id"
            secret-env: "VAULT_SECRET_ID"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Order of operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authenticate to Vault to get a token that is scoped to the appropriate policies using the configured authentication method.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Check Vault for any secrets using different paths:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;env&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;environment&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;private_ssh_key&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;id_rsa_github&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git-credentials&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Download the secrets and add them to the job environment.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;You can use this &lt;a href="https://github.com/jeremybumsted/vault-buildkite-demo" rel="noopener noreferrer"&gt;demo repository&lt;/a&gt; to quickly get a local Vault environment running.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plug'n play
&lt;/h2&gt;

&lt;p&gt;It's best to extract and wrap commonly used or repeated functionality into a plugin, not only will this simplify its inclusion in pipelines, it reduces repetitive code, and makes updating versions or removing and changing plugins across multiple pipelines much simpler.&lt;/p&gt;

&lt;p&gt;Many common problems or workflows already have dedicated plugins from Buildkite or the Buildkite community. Check the &lt;a href="https://buildkite.com/plugins" rel="noopener noreferrer"&gt;Plugins directory&lt;/a&gt; to look for an existing solution.&lt;/p&gt;

&lt;p&gt;If you can't find an existing plugin that does what you need, check out the &lt;a href="https://buildkite.com/docs/plugins/writing" rel="noopener noreferrer"&gt;documentation to write your own&lt;/a&gt;. Buildkite plugins are written in Bash and loosely coupled with Buildkite, making them more maintainable and less prone to compatibility issues than in other CI/CD tools.&lt;/p&gt;




</description>
      <category>cicd</category>
      <category>buildkite</category>
      <category>devops</category>
      <category>security</category>
    </item>
    <item>
      <title>✨ Rekindling Community ✨</title>
      <dc:creator>Mel Kaulfuss</dc:creator>
      <pubDate>Thu, 25 Feb 2021 01:53:00 +0000</pubDate>
      <link>https://dev.to/melissakaulfuss/rekindling-community-227d</link>
      <guid>https://dev.to/melissakaulfuss/rekindling-community-227d</guid>
      <description>&lt;p&gt;2020 really took its toll on our company culture as I'm sure it did for many of you.&lt;/p&gt;

&lt;p&gt;It's not like we weren't already a flexible, remote friendly delivery team. It's just that being forced 100% remote meant that we suddenly became what felt like, a company full of 1 person silos.&lt;/p&gt;

&lt;p&gt;Collaborating and pairing are my happiest places to be, but as our work pivoted to meet the demands placed on it as an airline during a pandemic, the pressures of delivery meant our team was fractured – people were head down, under pressure, delivering in isolation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where did the fun and laughter go?
&lt;/h2&gt;

&lt;p&gt;Fun is often an overlooked yet valuable ingredient in delivery teams that produce exceptional output (both in terms of quality and speed). As a Dev team, that's always been a very important part of who we are and why people choose to work with us. Moving our Dev team rituals to Google Meet wasn't as successful as we'd have hoped. The silence and lack of frivolity was jarring and instead of fun it was, do the business, get the thing over with and get out of there. These once enjoyable routines fast became chores, and engagement waned. Work no longer felt like the fun place it once was and that meant showing up was hard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bringing back the energy
&lt;/h2&gt;

&lt;p&gt;Last week we had our first Hack week for nearly 2 years. I'm trying to put my finger on why it was the most successful we've ever had, and if I was to choose one reason why, I think it was that everyone was all too familiar with the pain of feeling disconnected.&lt;/p&gt;

&lt;p&gt;I can't speak to how it was organised, because I wasn't involved this time round, but I can talk about how it felt from a participants perspective - a perspective that was shared across the teams.&lt;/p&gt;

&lt;p&gt;Here's how it happened:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Call for ideas/proposals&lt;/li&gt;
&lt;li&gt;Tech Leadership group review&lt;/li&gt;
&lt;li&gt;Choose your own team/idea&lt;/li&gt;
&lt;li&gt;Clear instructions/expectations communicated&lt;/li&gt;
&lt;li&gt;Organic/teams have full autonomy&lt;/li&gt;
&lt;li&gt;No winners/losers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course there's no magic in any of this. It's worth mentioning though that there is a palpable amount of psychological safety and trust in our 30+ delivery team. This is both remarkable, and also key to things running so well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Call for ideas/proposals
&lt;/h3&gt;

&lt;p&gt;A spreadsheet was circulated to gather any and all ideas, along with what types of skills would be needed to get the job done. There were no bounds imposed at this stage. Anything and everything was valid.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tech Leadership group review
&lt;/h3&gt;

&lt;p&gt;The tech leads reviewed the ideas to weed out proposals that were deemed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Too BAU (ideas that were snuck into the list to get business as usual work further along the delivery roadmap)&lt;/li&gt;
&lt;li&gt;Not really achievable (in some way)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These ensured the ideas that remained were exciting, fun and within reach.&lt;/p&gt;

&lt;h3&gt;
  
  
  Choose your own team/idea
&lt;/h3&gt;

&lt;p&gt;Some teams were big, some were small, UX/design peeps were shared across teams. We were free to choose to work on anything that resonated with us personally in some way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Clear instructions/expectations communicated
&lt;/h3&gt;

&lt;p&gt;The tone of all communications were friendly, light, and lacked the usual hackathon competitive tone. I felt informed, knew what to expect, had the opportunity for Q&amp;amp;A. All ducks in row.&lt;/p&gt;

&lt;p&gt;A few days before the event we were given a high level running sheet. There was no anxiety, nothing left to chance, everyone felt included and heard.&lt;/p&gt;

&lt;h3&gt;
  
  
  Organic/teams have full autonomy
&lt;/h3&gt;

&lt;p&gt;Whilst there were a few optional checkpoints during the week, the week's organisation was loose and teams free to self organise.&lt;/p&gt;

&lt;p&gt;The checkins:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monday (AM):&lt;/strong&gt; &lt;br&gt;
Google Meet Kick off - welcome and overview of what to expect during the week.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tuesday (AM):&lt;/strong&gt; &lt;br&gt;
Engineering Manager check-in with participants to see how things are going (informal and via DM), light touch, mainly to collect ammunition for jokes in hindsight. &lt;/p&gt;

&lt;p&gt;Teams presented logo/name and general idea/approach via Google Meet. Loaded with jokes/joviality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Friday (PM):&lt;/strong&gt;&lt;br&gt;
Team presentations/awards&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Throughout the week:&lt;/strong&gt;&lt;br&gt;
Regular updates in #delivery slack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting the band back together
&lt;/h2&gt;

&lt;p&gt;What was remarkable to me, was seeing the energy rekindled. Our enthusiasm for building smart, customer centric, value imbued products! Every team produced amazingly innovative results and in just 5 days.&lt;/p&gt;

&lt;p&gt;I'm not sure exactly how it was possible, but I'm certain, that the open, friendly, trusting, and optimistic energy that was part of the inception of ideas and communication style permeated through the entire event. In stark contrast to our Google Meets of late there was also a focus on being silly, irreverent, and laughing A LOT.&lt;/p&gt;

&lt;p&gt;The challenge of running a remote hack-a-thon unexpectedly didn't seem to be a thing. It all just worked. Slack/google-meet and pairing via VSCode Liveshare meant for a very easy week and our trust in and ability to converse online firmly established.&lt;/p&gt;

&lt;p&gt;Final presentations were given via team slides and live-demos in Google Meet. Remarkably, all of the teams approached their presentation in the same way (without consultation). Each of the team members took their turn to present a component of the work they did. No-one was left behind. The teamwork and collaboration and community was back. &lt;/p&gt;

&lt;p&gt;I'm sure the execs were aware that this wasn't a cheap investment. But the outcomes were far greater than simply the products that were built (or not built). Harmony, cross team collaboration and communication are the backbone of getting shit done efficiently. A 5 day investment in fun and innovation produces something that money just can't buy. &lt;/p&gt;

&lt;p&gt;It feels like we're on track to being back baby!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Enumerable#tally in Ruby 2.7</title>
      <dc:creator>Mel Kaulfuss</dc:creator>
      <pubDate>Tue, 27 Oct 2020 22:43:41 +0000</pubDate>
      <link>https://dev.to/melissakaulfuss/enumerable-tally-in-ruby-2-7-254o</link>
      <guid>https://dev.to/melissakaulfuss/enumerable-tally-in-ruby-2-7-254o</guid>
      <description>&lt;p&gt;The &lt;code&gt;tally&lt;/code&gt; method has been added to Enumerable in Ruby 2.7&lt;/p&gt;

&lt;p&gt;It's a simple little method and as the name suggests, it tallys things for us! It affords us a much simpler way to group items and count their occurrence.&lt;/p&gt;

&lt;p&gt;We could always count the size of an array.&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;fruit_bowl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"apple"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"pear"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"pear"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"banana"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"apple"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;fruit_bowl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt; 
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We could count how many of each item an array contained.&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;fruit_bowl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"apple"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can easily take stock of our array items with &lt;code&gt;tally&lt;/code&gt; which will return us a list of items with their respective counts.&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;fruit_bowl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"apple"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"pear"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"pear"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"banana"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"apple"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;sorted_fruit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fruit_bowl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tally&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"apple"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"pear"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"banana"&lt;/span&gt;&lt;span class="o"&gt;=&amp;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;sorted_fruit&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"apple"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sweet as 🍓😋&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Opening a window on codesmells 💩💨</title>
      <dc:creator>Mel Kaulfuss</dc:creator>
      <pubDate>Tue, 15 Sep 2020 07:46:00 +0000</pubDate>
      <link>https://dev.to/melissakaulfuss/opening-a-window-on-codesmells-part-i-57ea</link>
      <guid>https://dev.to/melissakaulfuss/opening-a-window-on-codesmells-part-i-57ea</guid>
      <description>&lt;p&gt;&lt;a href="https://martinfowler.com/bliki/CodeSmell.html"&gt;Codesmell&lt;/a&gt; sure is a great word, it was first introduced by Kent Beck in the 1990s and later popularised in the book &lt;em&gt;Refactoring: Improving the Design of Existing Code&lt;/em&gt; written by Martin Fowler in 1999. Just like the name suggests it's a pong or stank that emanates from code. Suffice to say, the code in question is less than ideal.&lt;/p&gt;

&lt;p&gt;Over a series of small posts I'm going to a uncover a few of the easier to identify codesmells and give you some tools to fix them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://sandimetz.com/"&gt;Sandi Metz&lt;/a&gt;, my favorite technical speaker and writer, has written and spoken about these topics extensively. I thoroughly recommend checking these resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://youtu.be/PJjHfa5yxlU"&gt;Get a whiff of this, Rails Conf 2016&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=8bZh5LMaSmE"&gt;All the little things, Rails Conf 2014&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Trust your gut
&lt;/h2&gt;

&lt;p&gt;❌ &lt;strong&gt;test fails&lt;/strong&gt;: 🤔 "why &lt;em&gt;isn't&lt;/em&gt; this working?" &lt;br&gt;
✅ &lt;strong&gt;test passes&lt;/strong&gt;: 🤔 "why &lt;em&gt;is&lt;/em&gt; this working?"&lt;/p&gt;

&lt;p&gt;Familiar scenario? As a complete n00b literally &lt;em&gt;everything&lt;/em&gt; was baffling. I considered it a huge win if I even began to understand the code I was reading. If things were tough, I'd hear a little voice:&lt;/p&gt;

&lt;p&gt;"You've read over this class or method a million times, why can't you understand it? Adding a new spec shouldn't be that difficult, what's wrong with you?".&lt;/p&gt;

&lt;p&gt;What if it wasn't you but it was the code (don't bother &lt;code&gt;git blame&lt;/code&gt;ing - it was probably you last month 😅)? Complex code isn't good code. Learning to trust your intuition only comes with confidence but it's important to remember that if something doesn't &lt;em&gt;feel&lt;/em&gt; right, often it isn't!&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Any fool can write code that a computer can understand, good programmers write code that every human can understand. &lt;br&gt;
                      -- Martin Fowler&lt;/strong&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  Start off small 👀
&lt;/h3&gt;

&lt;p&gt;A couple of relatively easy to spot issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Conditional complexity in the form of way too many &lt;code&gt;if&lt;/code&gt;s, &lt;code&gt;elsif&lt;/code&gt;s and &lt;code&gt;else&lt;/code&gt;s&lt;/li&gt;
&lt;li&gt;The length of Classes, Modules and Methods&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's take a look at these things in a little more detail.&lt;/p&gt;
&lt;h3&gt;
  
  
  Conditional complexity
&lt;/h3&gt;

&lt;p&gt;You're working on adding a small feature or fix and need to add a simple expectation or two to an existing unit spec to cover this case. How hard can that be?&lt;/p&gt;

&lt;p&gt;Upon opening that &lt;code&gt;my_thing_spec.rb&lt;/code&gt; file (because you practice TDD of course 😉) you discover that can be pretty hard. The spec is overly complex, the set up alone fills your screen, and scrolling down the file your your enthusiasm starts to wane.&lt;/p&gt;

&lt;p&gt;A tonne of nested contexts is most likely a hint that something isn't quite right.&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;describe&lt;/span&gt; &lt;span class="s1"&gt;'#my_understanding'&lt;/span&gt;
 &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'when i see this'&lt;/span&gt;
  &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'when it looks like this'&lt;/span&gt;
  &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'when it looks like that'&lt;/span&gt;
  &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'when it looks like something unrelated'&lt;/span&gt;
  &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s1"&gt;'when it arrrghhhhhhhh'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your unit spec &lt;em&gt;feels&lt;/em&gt; difficult to work with, it's highly likely you're looking at an opportunity to banish a codesmell 👉🚪.&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;describe&lt;/span&gt; &lt;span class="s1"&gt;'#error_message'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

   &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s2"&gt;"with an 'error' response"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
     &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'has an error describing the error'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

     &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;

   &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s2"&gt;"with a 'refused' response"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;  

    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s2"&gt;"without a response present"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s2"&gt;"and a status code"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
         &lt;span class="o"&gt;...&lt;/span&gt;
       &lt;span class="k"&gt;end&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;

     &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="s2"&gt;"and no status code"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
       &lt;span class="o"&gt;...&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;end&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 the above (semi-fabricated and overly simplified) example we're testing an &lt;code&gt;error_message&lt;/code&gt; method that clearly has a few different return values depending on the context.&lt;/p&gt;

&lt;p&gt;Looking at the corresponding method that we're testing, it's no surprise there are a few conditionals.&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;error_message&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;error_response?&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"errorCode"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&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;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"errorType"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&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;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;payment_result&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'refused'&lt;/span&gt;
    &lt;span class="s2"&gt;"Payment refused: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"refusalReason"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;elsif&lt;/span&gt; &lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; status received"&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="s2"&gt;"Payment connection failed"&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 fact we've got all the &lt;code&gt;if&lt;/code&gt;, &lt;code&gt;else&lt;/code&gt; and &lt;code&gt;elsif&lt;/code&gt; bases covered and more. &lt;/p&gt;

&lt;p&gt;Asking myself - &lt;strong&gt;What does the "error_message" method do?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Wellllll if it's an error response it builds an error message &lt;em&gt;and&lt;/em&gt; if the request was refused we bubble that reason up &lt;em&gt;and&lt;/em&gt; if we don't get an error or a reason but there's a status code we put that in the error &lt;em&gt;and&lt;/em&gt; if all else fails, we just state that the request failed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;11 lines of code&lt;/li&gt;
&lt;li&gt;3 ands.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What would Sandi Metz do? I do know what she'd says: &lt;br&gt;
&lt;strong&gt;Methods should be no longer than 5 lines of code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now I'll admit, I do break that rule (maybe a little more than sometimes). But the &lt;em&gt;and&lt;/em&gt; rule is a good one to stick to. If you have to say &lt;em&gt;AND&lt;/em&gt; when describing it's function it's doing too much! So, we've identified a &lt;em&gt;conditional complexity&lt;/em&gt; codesmell and an opportunity for a refactor.&lt;/p&gt;

&lt;p&gt;Zooming out to the class level now:&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;module&lt;/span&gt; &lt;span class="nn"&gt;Clients&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentResult&lt;/span&gt;
  &lt;span class="nb"&gt;attr_reader&lt;/span&gt; &lt;span class="ss"&gt;:result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:api_version&lt;/span&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="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;api_version&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
      &lt;span class="vi"&gt;@result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
      &lt;span class="vi"&gt;@api_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api_version&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;
      &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:response&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;payment_result&lt;/span&gt;
     &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"resultCode"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;downcase&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;     

   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;success?&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'authorised'&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;failure?&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'error'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'refused'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;error_response?&lt;/span&gt;
   &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has_key?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"errorCode"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;       

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;error_message&lt;/span&gt;
    &lt;span class="c1"&gt;# you've already seen me :)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;insufficient_funds?&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"refusalReasonCode"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&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="nf"&gt;card_expired?&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"refusalReasonCode"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"2"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

 &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;declined?&lt;/span&gt;
   &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"9"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"refusalReasonCode"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;end&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;We can ask ourselves a similar question –– &lt;strong&gt;What is this class doing?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Wellll, it handles/parses the response from the external third party payment provider &lt;em&gt;AND&lt;/em&gt; if there are errors it then formats the error according to the type of &lt;code&gt;resultMessage&lt;/code&gt; received"&lt;/p&gt;

&lt;p&gt;It's a relatively small class (doesn't exceed Sandi's 100 line limit for Classes rule). There is another codesmell lurking in there: &lt;em&gt;primative obsession&lt;/em&gt;. But I'll park that one for another post (or justification) soon. There was an &lt;em&gt;and&lt;/em&gt; in our class explanation – and a sign that the class is doing more than it needs to.&lt;/p&gt;

&lt;p&gt;We have green tests and our existing code works so that means we can refactor with confidence 🙌&lt;/p&gt;

&lt;h2&gt;
  
  
  Just a simple refactor?
&lt;/h2&gt;

&lt;p&gt;Pragmatism and discretion are always an essential ingredients in our work as Software Engineers. There are never absolutes, only trade offs that can be lived with or not. Refactoring isn't &lt;em&gt;always&lt;/em&gt; the answer but it usually is. Investing more time than is necessary isn't always called for, it's up for to you to decide what's appropriate for the given circumstances.&lt;/p&gt;

&lt;p&gt;Ask yourself a couple of things before embarking:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is this code likely to ever need to change?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If it's unlikely to be revisited it's okay to leave it. Honestly, bad code isn't necessarily bad if it was cheap and is robust and does it's thing without needing to change. You don't get paid to be perfect. You get paid to build the right tool for the job at the right time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is a refactor going to help the next person who will inevitably need to touch this code?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the answer is hell yeah - then why not leave things in a better state than what they were when you arrived. Investing in clean code can take time but saves it in the long run.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>refactoring</category>
      <category>codequality</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Accidentally Destructuring in Ruby</title>
      <dc:creator>Mel Kaulfuss</dc:creator>
      <pubDate>Thu, 10 Sep 2020 13:07:17 +0000</pubDate>
      <link>https://dev.to/melissakaulfuss/accidentally-destructuring-in-ruby-37e4</link>
      <guid>https://dev.to/melissakaulfuss/accidentally-destructuring-in-ruby-37e4</guid>
      <description>&lt;p&gt;The funniest thing happened to me yesterday. Funny now I guess. At the time it was distinctly &lt;em&gt;unfunny&lt;/em&gt;. I struggled for hours on the simplest unit spec that was failing.&lt;/p&gt;

&lt;p&gt;The response I was expecting to see in my test:&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="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:response&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="s2"&gt;"pspReference"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"883598242852329J"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
   &lt;span class="s2"&gt;"resultCode"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Authorised"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
   &lt;span class="s2"&gt;"amount"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"currency"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"AUD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"value"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;},&lt;/span&gt;
 &lt;span class="ss"&gt;:header&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"Sandwich"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Eggplant-Parma-Sanga"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
 &lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The failure message, less than helpful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TypeError:
  no implicit conversion of Symbol into Integer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Staring at the code.&lt;br&gt;
Staring at the spec.&lt;br&gt;
Thinking I'd done something stupid in my &lt;code&gt;before&lt;/code&gt; block. &lt;/p&gt;

&lt;p&gt;A &lt;code&gt;binding.pry&lt;/code&gt; in my code shows me the Hash I'm expecting to be able to access with a &lt;code&gt;:response&lt;/code&gt; key is coming back inside an Array?!&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="p"&gt;[{&lt;/span&gt;&lt;span class="ss"&gt;:response&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"pspReference"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"883598242852329J"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"resultCode"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Authorised"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"amount"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"currency"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"AUD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"value"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt;
  &lt;span class="ss"&gt;:header&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"Sandwich"&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"Eggplant-Parma-Sanga"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="n"&gt;api_version&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You wot m8! &lt;/p&gt;

&lt;p&gt;It took a second pair of eyes to help spot the error.&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="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;api_version&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
  &lt;span class="vi"&gt;@result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;# &amp;lt;-------- this stray comma&lt;/span&gt;
  &lt;span class="vi"&gt;@api_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api_version&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As soon as that silly stray comma was pointed out I laughed out loud.&lt;/p&gt;

&lt;p&gt;I was accidentally &lt;strong&gt;Destructuring&lt;/strong&gt; in Ruby and I'm chalking up the fact I knew that's what was happening as my win here! And also that I'd been meaning to write about Destructuring in Ruby earlier, so, win win I guess 🤷‍♀️&lt;/p&gt;

&lt;p&gt;Destructuring is described in &lt;a href="http://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node252.html"&gt;Common Lisp the Language, 2nd Edition&lt;/a&gt; as the ability to "bind a set of variables to a corresponding set of values anywhere that you can normally bind a value to a single variable".&lt;/p&gt;

&lt;p&gt;In Ruby we assign values to a single variable 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="n"&gt;my_choice_sandwich&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"eggplant parma"&lt;/span&gt;

&lt;span class="c1"&gt;# irb(main):001:0&amp;gt; my_choice_sandwich&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; "eggplant parma"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Destructuring allows us to do multiple assignments with a simple comma separating the multiple values like so&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;my_choice_sandwich&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"crumbed eggplant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sugo sauce"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"mozzerella"&lt;/span&gt;

&lt;span class="c1"&gt;# irb(main):001:0&amp;gt; my_choice_sandwich&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; ["crumbed eggplant", "sugo sauce", "mozzerella"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above we see the Object on the left (the "variable") is an Array because we are passing in multiple values on the right.&lt;/p&gt;

&lt;p&gt;You can set multiple variables (on the right) with corresponding values (on the left).&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;sandwich&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pizzas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"salad"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;vege: &lt;/span&gt;&lt;span class="s2"&gt;"mushrooms"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;meat: &lt;/span&gt;&lt;span class="s2"&gt;"aussie"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# irb(main):001:0&amp;gt; sandwich&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; "salad"&lt;/span&gt;

&lt;span class="c1"&gt;# irb(main):001:0&amp;gt; pizzas&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; {:vege=&amp;gt;"mushrooms", :meat=&amp;gt;"aussie"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above you'll see:&lt;br&gt;
&lt;code&gt;sandwich&lt;/code&gt; returns a &lt;strong&gt;String&lt;/strong&gt; because only one value is being assigned on the right.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pizzas&lt;/code&gt; returns a &lt;strong&gt;Hash&lt;/strong&gt; as that was the corresponding value on the right.&lt;/p&gt;

&lt;p&gt;Isn't learning fun!&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>bug</category>
      <category>testing</category>
    </item>
    <item>
      <title>Bogged down by Blogging</title>
      <dc:creator>Mel Kaulfuss</dc:creator>
      <pubDate>Fri, 14 Aug 2020 12:46:25 +0000</pubDate>
      <link>https://dev.to/melissakaulfuss/bogged-down-by-blogging-5a79</link>
      <guid>https://dev.to/melissakaulfuss/bogged-down-by-blogging-5a79</guid>
      <description>&lt;p&gt;What is it about us software engineers? &lt;/p&gt;

&lt;p&gt;On the one hand we're the good kind of lazy, we love optimisation and efficiency. We automate the most menial tasks so we don't need to waste time on them ever again. If you've a product mindset, you're no stranger to the concept of Minimum Viable Product. You're always asking the why's, using TDD to drive design evolution, and only reaching for abstraction when there's a need. Yet, when it comes to our own private projects - we're all in on the over engineering. &lt;/p&gt;

&lt;p&gt;We love a good meaty problem, the curlier the more intriguing. And there's nothing wrong with over engineering if your goal is experimentation, learning and fun right? But what about when that's a form of procrastination?&lt;/p&gt;

&lt;p&gt;For five years my sad old blog has lay in that dusty, dark corner of the internet where all neglected blogs lie, never a visitor, unloved. I have dreamed of resurrecting it. You'd be forgiven for thinking, when I say "resurrect", that I wrote more than a "hello world" post and a follow up. You'd be wrong, it's a vacuous collection of broken CSS, despite being lovingly composed with the best of intentions.&lt;/p&gt;

&lt;p&gt;It was 2015 (the good old days) I hadn't learned to code yet, this was my first foray into learning to code as a hobby. Fresh faced, innocent (some might say naive), eyes glittering, I rolled up my sleeves to get started. Finally I was practicing what I (had myself) preached in many presentations to junior developers on how to break into the industry.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Focus on the three Ps; Practice, Persistence and Presence."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I needed a blog for that last P, I needed Presence and pronto!&lt;/p&gt;

&lt;p&gt;Without hesitation, I reached for &lt;a href="https://jekyllrb.com/"&gt;Jekyll&lt;/a&gt;, it's a fantastic and simple open source static site generator, not complex when you have some experience under your belt.&lt;/p&gt;

&lt;p&gt;I learned a lot as I experimented and broke things. About CSS and the feels associated with cascading down into those pits of despair. About infrastructure and how much I loved understanding how the internet evened. I served my blog from AWS s3, configured route53 for my DNS, and to this day it's up (with minimal costs associated). So it was a success of types.&lt;/p&gt;

&lt;p&gt;What I didn't do was continue to blog and that's because my choice of tool didn't serve my needs. I was hung up on a particular solution rather than asking "what are you trying to achieve?".&lt;/p&gt;

&lt;p&gt;I wanted to practice writing about technical subject matter, to sharpen and hone my skills, to share and help others who were struggling with similar issues.&lt;/p&gt;

&lt;p&gt;After an inspirational conversation with &lt;a href="https://twitter.com/kaslinfields"&gt;@KaslinFields&lt;/a&gt; and laughing at my failure I'm heeding her advice of "Do it!" with a renewed vigor and a more fitting tool for my needs. Use whatever tool is right for you, there's no need to over complicate things. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Always be the right kind of lazy (and smart)!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>blogging</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
