<?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: Johnny Starr</title>
    <description>The latest articles on Forem by Johnny Starr (@johnnystarr).</description>
    <link>https://forem.com/johnnystarr</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%2F244831%2Fea041499-a282-4ac2-9f46-1382c630085a.jpeg</url>
      <title>Forem: Johnny Starr</title>
      <link>https://forem.com/johnnystarr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/johnnystarr"/>
    <language>en</language>
    <item>
      <title>How to publish a Micronaut project using Gradle</title>
      <dc:creator>Johnny Starr</dc:creator>
      <pubDate>Sat, 17 Apr 2021 03:22:30 +0000</pubDate>
      <link>https://forem.com/johnnystarr/how-to-publish-a-micronaut-project-using-gradle-34j3</link>
      <guid>https://forem.com/johnnystarr/how-to-publish-a-micronaut-project-using-gradle-34j3</guid>
      <description>&lt;p&gt;I have really enjoyed working with Micronaut.  It's a wonderfully fast framework that makes life easier.  This new framework is really easy to get up and running.  Publishing on the other hand isn't well documented, so I wanted to share some tips on how I was able to get this done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;We will specifically be publishing a Micronaut app using Gradle.  You will need to install the Micronaut CLI.  You can grab it here: &lt;a href="https://guides.micronaut.io/micronaut-cli/guide/index.html"&gt;https://guides.micronaut.io/micronaut-cli/guide/index.html&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Micronaut App
&lt;/h2&gt;

&lt;p&gt;Now that you've grabbed the CLI, create a new project.  The Launcher button on the Micronaut site will help you with the CLI commands if you want specific packages.  But for the sake of simplicity, we won't be installing anything special.  Run the following command to build a demo app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mn create-app --build=gradle --jdk=11 --lang=java --test=junit to.dev.publishing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Gradle Steps
&lt;/h2&gt;

&lt;p&gt;If you're like me, you might have IntelliJ with the Gradle plugin installed.  If so, this next part is a no brainer.  By simply opening the project it will run Gradle in the background and generate your project.  You should have a &lt;code&gt;build.gradle&lt;/code&gt; file in the root of your project that looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;plugins&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"com.github.johnrengelman.shadow"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s2"&gt;"6.1.0"&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"io.micronaut.application"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s2"&gt;"1.4.2"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0.1"&lt;/span&gt;
&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"to.dev"&lt;/span&gt;

&lt;span class="n"&gt;repositories&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;mavenCentral&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;micronaut&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"netty"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;testRuntime&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"junit5"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;processing&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;incremental&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;annotations&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"to.dev.*"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"io.micronaut:micronaut-http-client"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"io.micronaut:micronaut-runtime"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"javax.annotation:javax.annotation-api"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"io.micronaut:micronaut-validation"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;runtimeOnly&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"ch.qos.logback:logback-classic"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;application&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;mainClass&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"to.dev.Application"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;java&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;sourceCompatibility&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JavaVersion&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toVersion&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"11"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;targetCompatibility&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JavaVersion&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toVersion&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"11"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running Gradle Build
&lt;/h2&gt;

&lt;p&gt;Run the following command to build your artifacts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./gradlew build

&amp;gt; Task :compileTestJava
Note: Creating bean classes for 1 type elements

BUILD SUCCESSFUL in 11s
15 actionable tasks: 3 executed, 12 up-to-date
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your output may be slightly different depending on your dependencies and workstation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Artifacts
&lt;/h2&gt;

&lt;p&gt;To see the produced artifacts you can run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ls ./build/libs/
publishing-0.1-all.jar
publishing-0.1-runner.jar
publishing-0.1.jar

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hmm... Interesting right?  We have 3, so which one is should we ship to production?  The short answer, is &lt;code&gt;publishing-0.1-all.jar&lt;/code&gt;.  But how do we publish it?  And what are the other two JARs for? (we'll discuss this on another post)&lt;/p&gt;

&lt;h2&gt;
  
  
  Publishing
&lt;/h2&gt;

&lt;p&gt;It took some time to understand the best publishing strategy for my current use-case.  What I wanted were the following 3 things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The primary JAR&lt;/li&gt;
&lt;li&gt;A Zip archive of the distribution&lt;/li&gt;
&lt;li&gt;A Tar archive of the distribution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ok.  Great... So how do we do that?  Add the following lines to your &lt;code&gt;build.gradle&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;publishing {
    publications {
        myDist(MavenPublication) {
            artifact shadowJar
            artifact distZip
            artifact distTar
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it!  &lt;code&gt;shadowJar&lt;/code&gt; produces our large &lt;code&gt;publishing-0.1-all.jar&lt;/code&gt;.  The zip/tar produce the full distribution which includes all the required JAR files for your project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Publishing to Maven Locally "Trick"
&lt;/h2&gt;

&lt;p&gt;So you're not quite ready to publish to Maven Central, Nexus3 or Artifactory.  No biggie.  You can use the following "trick" to publish to your local machine.  This is useful if you want to inspect distribution you can delete your local &lt;code&gt;~/.m2&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ rm -rf ~/.m2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Afterwards, from your project repo run the following Gradle command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./gradlew publishMyDistPublicationToMavenLocal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can inspect your "published" artifacts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ find ~/.m2
/Users/johnny/.m2
/Users/johnny/.m2/repository
/Users/johnny/.m2/repository/to
/Users/johnny/.m2/repository/to/dev
/Users/johnny/.m2/repository/to/dev/publishing
/Users/johnny/.m2/repository/to/dev/publishing/maven-metadata-local.xml
/Users/johnny/.m2/repository/to/dev/publishing/0.1
/Users/johnny/.m2/repository/to/dev/publishing/0.1/publishing-0.1-all.jar
/Users/johnny/.m2/repository/to/dev/publishing/0.1/publishing-0.1.pom
/Users/johnny/.m2/repository/to/dev/publishing/0.1/publishing-0.1.tar
/Users/johnny/.m2/repository/to/dev/publishing/0.1/publishing-0.1.zip

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Micronaut is amazing and I love it.  It's still quite new and documentation is hard.  Getting up and running is fast, but publishing isn't super obvious at first glance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Even without selecting it, our CLI command includes the Netty webserver.  I think its pretty slick coming from a WAR/Tomcat background!  Netty completely sidesteps the Servlet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Gradle is awesome.  As a UNIX expert my heart will always go out to to Make, but come on people its 2021!&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope this helps save you some time.  It took me a little bit to figure this one out.&lt;/p&gt;

</description>
      <category>micronaut</category>
      <category>gradle</category>
      <category>devops</category>
      <category>maven</category>
    </item>
    <item>
      <title>Lessons from UNIX History: Avoiding the "second system effect"</title>
      <dc:creator>Johnny Starr</dc:creator>
      <pubDate>Tue, 07 Apr 2020 18:39:34 +0000</pubDate>
      <link>https://forem.com/johnnystarr/lessons-from-unix-history-avoiding-the-second-system-effect-116b</link>
      <guid>https://forem.com/johnnystarr/lessons-from-unix-history-avoiding-the-second-system-effect-116b</guid>
      <description>&lt;p&gt;This is the first article in my "Lessons from UNIX History" series.  By day I am a DevOps Tech Lead, by night a computing archeologist.  This indulgence allows me to express my findings with practical, modern day applications.  Ideally, we can all benefit at present by taking a moment to reflect on the past.&lt;br&gt;&lt;br&gt;
This is more than history, it is our heritage as engineers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Human Tendencies
&lt;/h3&gt;

&lt;p&gt;As humans, we love upgrades and new features.  In fact, where would we be without them?  The software industry relies on continuous delivery of new content; which is fine generally speaking.  But what happens when we get too attached to a system, product or application?  This results in the "second system effect".&lt;/p&gt;

&lt;h3&gt;
  
  
  Second System Effect
&lt;/h3&gt;

&lt;p&gt;This term is best explained by Brian Kernighan in his 2020 book "UNIX A History and a Memoir":&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"... it's tempting to try to create a new system that fixes all the remaining problems with the original while adding everybody's favorite new features too.  The result is often a system that's too complicated, a consequence of taking on too many different things at the same time ..."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This excerpt explains the challenges we face in today's industry quite well.  But we do well to analyze the history just a bit deeper.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multics to Unics (UNIX)
&lt;/h3&gt;

&lt;p&gt;We owe a surprising amount of features and applications to the UNIX predecessor "Multics".  Such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A shell&lt;/li&gt;
&lt;li&gt;User authentication&lt;/li&gt;
&lt;li&gt;Streams (output, redirection, file descriptors)&lt;/li&gt;
&lt;li&gt;Commands like "ls" and "date"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Multics was a time-sharing operating system that was both feature rich and yet inherently flawed.  So much so that General Electric (GE) withdrew funding during the late stages of development.  Business aside, it was clear that the OS was too bloated (sound familiar?).  This is where Ken Thompson decided to branch off to new territory.  We're very happy he did, because Unics (later renamed UNIX) was born.&lt;/p&gt;

&lt;p&gt;No doubt, he had invested a lot of time in Multics.  Yet, he was emotionally mature enough to "walk away" from a dying system full of bloat.  He would embrace a new way of thinking; albeit at the cost of some very favored features.&lt;/p&gt;

&lt;h3&gt;
  
  
  But that was 50+ years ago!
&lt;/h3&gt;

&lt;p&gt;It amazes me how much history repeats itself.  Really, this is just as much of a problem today.  Computers have gotten smaller.  Our operating systems larger.  Our knowledge vaster, but deep down we have the same tendencies as our predecessors. In fact, it could be argued with continuous delivery, our necessity for favorite features have gone up, not down.&lt;/p&gt;

&lt;h3&gt;
  
  
  What can you do about it?
&lt;/h3&gt;

&lt;p&gt;Ken Thompson was open minded about the future.  He recognized the need to abandon pet-projects when they were overloaded.  We need to leave behind legacy features and not be afraid to go back to the drawing board.  (I realize the irony in stating that lessons from a "legacy" era help us move forward technologically).&lt;/p&gt;

&lt;p&gt;If you are in the DevOps / systems engineering world, be open minded to what is possible today.  At times, I find myself leaning on base knowledge of AWS or Docker.  Due to the saturation of our industry, what was "impossible yesterday" is certainly "possible today".  Don't be afraid to forge your own path.  You just might invent something for the next generation!&lt;/p&gt;

&lt;h4&gt;
  
  
  Next Article "Avoiding Tool Sprawl"
&lt;/h4&gt;

&lt;p&gt;Today, we are bombarded with hundreds of tools, packages and middleware that promises to solve all your problems.  Of course, I think we can all agree, that FoSS is wonderful.  But how many tools do we really need?  Have you noticed how many files go into the root directory of your code repos?  In the next article I will tackle this real world problem, and what we learn from the past yet again.&lt;/p&gt;

</description>
      <category>unix</category>
      <category>devops</category>
      <category>architecture</category>
      <category>design</category>
    </item>
    <item>
      <title>What DevOps Engineers can learn from Extreme Programming (XP)</title>
      <dc:creator>Johnny Starr</dc:creator>
      <pubDate>Tue, 08 Oct 2019 16:34:18 +0000</pubDate>
      <link>https://forem.com/johnnystarr/what-devops-engineers-can-learn-from-extreme-programming-xp-33g</link>
      <guid>https://forem.com/johnnystarr/what-devops-engineers-can-learn-from-extreme-programming-xp-33g</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--K0D6bV6B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/d7p6hn4idzg4l6xlpo0n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K0D6bV6B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/d7p6hn4idzg4l6xlpo0n.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Is it a stretch to say that modern day DevOps folks can learn something from a bygone movement?  Absolutely not.  Although XP has a bad reputation for some specific reasons, it contains some principles that are of value to us today in 2019.&lt;/p&gt;

&lt;h3&gt;
  
  
  YAGNI
&lt;/h3&gt;

&lt;p&gt;This acronym stands for "You aren't gonna need it".  This is one of the pillars of XP.  It makes the assertion that you shouldn't spend time on components or features that you &lt;em&gt;might&lt;/em&gt; need down the line.  In modern development languages like GoLang, we see the application of this principle is inherit in Go's design.  A practical example of this, is &lt;code&gt;Get/Set&lt;/code&gt; methods not being idiomatic.  For purest Java DEVs, this can be troubling at best.  Yet, modern platforms such as Kubernetes and Docker we're written in GoLang.  A cursory review of the source-code on Github reveals that the engineers hardly follow a dogmatic OOP standard.&lt;/p&gt;

&lt;p&gt;I can't say how many times I've tried to implement code for my systems that &lt;em&gt;"I might need later"&lt;/em&gt; only to realize that new technology has lapped us yet again.  This can be frustrating, as you never seem to get to the gold-plating stage.  If we are able to apply YAGNI, we can save valuable time and effort.  But what happens when the time comes to make the changes?  Simple, we refactor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Continuous Refactoring
&lt;/h3&gt;

&lt;p&gt;Refactoring is often neglected in the DevOps realm.  This is because that "Ops" portion of your title is always going to be concerned about stability.  And lets face, we're feature oriented.  At the same time, many engineers fail to understand what "refactoring" truly means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Code refactoring is the process of restructuring existing computer code—changing the factoring—without changing its external behavior. - Wikipedia&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One of the misunderstandings is that "refactoring" gets confused with "gold plating" or new features.  Instead, our efforts should be on the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Optimization&lt;/li&gt;
&lt;li&gt;Increased Readability&lt;/li&gt;
&lt;li&gt;Increased Maintainability&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Test Drive Development (TDD)
&lt;/h3&gt;

&lt;p&gt;So let me be clear: You cannot continuously refactor without continuous testing.  If you change your code often &lt;em&gt;without&lt;/em&gt; tests, it will lead to unmanageable code and defects.  This can be challenging for engineers who "haven't gotten around to writing tests yet."&lt;/p&gt;

&lt;p&gt;TDD ensures that you write your tests, because it requires that you write them first!  TDD works like so:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write a test for a unit of work&lt;/li&gt;
&lt;li&gt;This test will automatically fail because this code does not yet exist&lt;/li&gt;
&lt;li&gt;Write the code to pass the test&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I have found TDD to work well with IaC projects such as Chef, Puppet, Ansible and friends.  &lt;/p&gt;

&lt;p&gt;&lt;em&gt;TDD is not synonymous with XP, but it was largely embraced by it.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Peer Coding
&lt;/h3&gt;

&lt;p&gt;Ok, this one is very controversial.  I've heard many arguments about the practicality of a group of developers attacking a problem all at once.  Like all things, you don't have to follow XP to perfection to get some benefit out of peer coding.  &lt;/p&gt;

&lt;p&gt;Working with distributed teams can be a challenge, but you shouldn't have to wait until you open a PR to have someone else take a look at your code.  I have found that 1-2 hours a week can be well spent in an interactive screenshare session.  Remember that diversity is great for problem solving.  Having more than one mind solving an issue will unlock potential in all involved.  In my opinion, the best possible scenario for team building, is team coding!&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;I'm sure there's still folks out there that love XP and use it daily.  As a DevOps engineer, I'm fascinated with Dev practices and better understanding the platforms that others have built in the past.  I strongly believe that each methodology is built on principles that have their place in the modern world.  There is a lot more to XP than I've covered, and I don't claim to be an expert.  I just wanted to share what has worked for myself and my team.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>xp</category>
      <category>testing</category>
      <category>ci</category>
    </item>
    <item>
      <title>A sensible semantic versioning process for Docker images</title>
      <dc:creator>Johnny Starr</dc:creator>
      <pubDate>Mon, 07 Oct 2019 22:22:30 +0000</pubDate>
      <link>https://forem.com/johnnystarr/a-sensible-semantic-versioning-process-for-docker-images-9po</link>
      <guid>https://forem.com/johnnystarr/a-sensible-semantic-versioning-process-for-docker-images-9po</guid>
      <description>&lt;p&gt;I'm a big fan of Ruby.  I've literally used it almost every day since 2008 when I picked up the "hummingbird" book (v1.8.7).  Naturally, I am likely to have a Rakefile at the root of all my repositories.  GNU Make has its place, but Rake is always the clear winner in my book.&lt;/p&gt;

&lt;p&gt;I've noticed that CI/CD pipelines for Docker images can be challenging for newcomers, so I wanted to share a best practice for bumping versions in a codified manner.  And no, I don't mean the goto &lt;code&gt;VERSION&lt;/code&gt; text file.  This method works of course, but if you want something more sophisticated read on.&lt;/p&gt;

&lt;p&gt;To get started, you'll need Ruby installed and create the following two files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.version.yml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Rakefile&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In our &lt;code&gt;.version.yml&lt;/code&gt; file, let's add some content&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.1&lt;/span&gt;
&lt;span class="na"&gt;promote&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;major&lt;/span&gt;
&lt;span class="na"&gt;phase&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;         &lt;span class="c1"&gt;# optional&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our intention here is to cook up an initial release version &lt;code&gt;1.0.0&lt;/code&gt;.  The value we're passing for the version doesn't really matter, as long as its below major version &lt;code&gt;1&lt;/code&gt;.  The &lt;code&gt;promote:&lt;/code&gt; key is going to determine the semantic bump that we plan to use.  Now let's create our Rakefile:&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="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'semantic'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'yaml'&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;next_version&lt;/span&gt;
  &lt;span class="c1"&gt;# load the current version&lt;/span&gt;
  &lt;span class="n"&gt;version_yml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;YAML&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safe_load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'.version.yml'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="n"&gt;current&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;version_yml&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'version'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;level&lt;/span&gt;       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;version_yml&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'level'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to_sym&lt;/span&gt;
  &lt;span class="n"&gt;phase&lt;/span&gt;       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;version_yml&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'phase'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="c1"&gt;# create a forecast of what our future version will be&lt;/span&gt;
  &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Semantic&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Version&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;current&lt;/span&gt;
  &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increment!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="p"&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;version&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;phase&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ss"&gt;:bump&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s2"&gt;"docker tag myproject:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;next_version&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; myproject:1.0.0&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So what exactly is happening here?  We've defined a method in the &lt;code&gt;#Kernel&lt;/code&gt; namespace that we can employ throughout our Rake tasks.  It reads the YAML file and provides the appropriate bump to our version.  The &lt;code&gt;:bump&lt;/code&gt; task simple runs the &lt;code&gt;docker tag&lt;/code&gt; command, but could be used in conjunction with any other release mechanisms.&lt;/p&gt;

&lt;h3&gt;
  
  
  Putting it to Work
&lt;/h3&gt;

&lt;p&gt;In order for this to be useful to you and your team, you will need to discuss your release strategy.  Ideally, you should only bump your semantic versions on merges to master.  Depending on your CI/CD process, you would only run the &lt;code&gt;:bump&lt;/code&gt; task during the release phase.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;p&gt;Rake is more than it appears at first glance.  It's not simply a Makefile with some Ruby features.  It &lt;em&gt;is&lt;/em&gt; Ruby; or rather a Ruby environment.  By putting Rake to good use, you avoid mangled Makefiles with pseudo-bash scripting baked in.&lt;/p&gt;

&lt;p&gt;I also view this as a great mechanism for peer reviews / pull requests.  It allows the team visibility on what you're planning to release and why your chosen promotion level is appropriate (or inappropriate).  Simply dropping off a number in &lt;code&gt;VERSION&lt;/code&gt; doesn't provide the same stimulus in my experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  The YAML File
&lt;/h3&gt;

&lt;p&gt;Why YAML and not JSON or something else?  Meh.  I say do what you want!  I chose YAML because its human readable and ubiquitous in the DevOps realm lately.  You can also add additional fields at any time without breaking functionality.  In the above example I left &lt;code&gt;phase:&lt;/code&gt; blank.  This is great for any modifiers such as &lt;code&gt;pre-release, alpha, beta, rc1, etc.&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;p&gt;Unlike the &lt;code&gt;yaml&lt;/code&gt; gem, you will need to install &lt;code&gt;semantic&lt;/code&gt; yourself.  Either use &lt;a href="https://bundler.io/"&gt;bundler&lt;/a&gt; or simply run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gem &lt;span class="nb"&gt;install &lt;/span&gt;semantic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally, this gem (and of course Ruby) will have to be installed on your CI/CD agent(s).  I would suggest using Bundler with a Gemfile to accomplish this.  If you're concerned about keeping your agents pristine, read up on installing the gems locally to the &lt;code&gt;vendor/&lt;/code&gt; folder.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;The DevOps frontier is still very "wild west".  I'm sure someone will invent a tool that handles versioning in a more succinct way, but what would the fun be in that? &lt;/p&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>ruby</category>
      <category>yaml</category>
    </item>
  </channel>
</rss>
