<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Patrick Lee Scott on Medium]]></title>
        <description><![CDATA[Stories by Patrick Lee Scott on Medium]]></description>
        <link>https://medium.com/@patrickleet?source=rss-8f48bb19ea99------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*xMFGjdW55-OXBoxJ5yYt9w.jpeg</url>
            <title>Stories by Patrick Lee Scott on Medium</title>
            <link>https://medium.com/@patrickleet?source=rss-8f48bb19ea99------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 21 Jun 2026 00:28:33 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@patrickleet/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Releasing New Versions Should Be Boring. Really Boring.]]></title>
            <link>https://codeburst.io/releasing-new-versions-should-be-boring-really-boring-55e29e4d9765?source=rss-8f48bb19ea99------2</link>
            <guid isPermaLink="false">https://medium.com/p/55e29e4d9765</guid>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[coding]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[github-actions]]></category>
            <category><![CDATA[gitops]]></category>
            <dc:creator><![CDATA[Patrick Lee Scott]]></dc:creator>
            <pubDate>Fri, 05 Sep 2025 14:24:58 GMT</pubDate>
            <atom:updated>2025-09-05T18:57:37.413Z</atom:updated>
            <content:encoded><![CDATA[<h4>Quick and easy CI/CD pipelines with semantic versioning for any language or tool.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/720/1*Rj4zVDTJlLKdraCHui3guA.jpeg" /></figure><p>Releasing a new version of a tool should be boring. No ceremony, no planning meetings, not even a discussion. It should be transparent, effortless, reliable, and even informative – more on that later.</p><p>However, as a software consultant who helps teams implement DevOps solutions, I often find that this is not the case. What I do find varies wildly in quality, so instead of telling you what I don’t like, let’s focus on how I like to structure my pipelines in order to onboard new repositories quickly and easily.</p><p>To understand my perspective, I&#39;d like to share how my views on the topic have evolved over the years. Currently, I apply a process that involves arranging “LEGO blocks” of modular workflows with minor tweaks, such as parameter changes. Ideally, I aim for defaults that allow me to simply copy and paste the same building blocks from one project to another when they share the same type. For example, the build process for Rust library X is probably about the same as Rust library Y, and Node server foo is probably just about the same as Node server bar. Etc.</p><p>Over the years, I attempted to assemble open-source components to achieve this, but I never found quite the right combination. I built my own solutions finally, and I’ll share those with you soon.</p><p>But first…</p><p>I realize versioning and releasing aren’t the most exciting topics; however, it’s a problem that every project faces, even when vibe-coded (probably especially!). So let’s talk about how to do it right. In my opinion, of course.</p><p>Doing it right will make your project look much more professional and communicate changes to its users. This establishes more trust in your projects by helping developers understand and adopt new changes in a clear and consistent way.</p><h3>In the beginning…</h3><p>Back at the beginning of my software engineering career, releases were quite the event. They were the byproduct of milestones, achieved through a series of sprints, planned, to the best of everyone’s ability, in a series of planning meetings where the backlog was scoured through, and tasks that were worthy were chosen. We’d do our best to write out a spec of a plan, and estimate a Fibonacci number or T-Shirt size, or in worst cases, a number of days. Of course, there were always some bug fixes to be included to fix the previous releases as well.</p><p>The release was the culmination of this process. The deliverable of deliverables, shipped to the real users. As the culmination of this process, and high stakes for getting it right, the release was an emotional moment.</p><p>Exciting?</p><p>Scary?</p><p>Maybe a few you’ve triggered have got your heart racing as they set the machines in motion.</p><p>There’s also the detail and drama of deciding a version number.</p><p>Is this a major release? A whole new version?? Minor? Patch?</p><p>Sometimes marketing dictated the version… 2.0!</p><p>At this point in my career, it was largely not my opinion that mattered on this topic of version numbers, and looking back, it probably should have been no one&#39;s (you’ll see what I mean later).</p><p>I was not responsible for releases in the beginning… However… that would not last forever.</p><h3>Into Open Source</h3><p>At some point in my career, I became interested in contributing to open source. Generally, in the form of npm modules, as I was very into Node at the time.</p><p>It was my turn to be in charge of releases.</p><p>I can’t remember how, maybe through some newsletter or maybe part of the NYC NodeJS meetup that Matt Walters and I used to run together – I came across the library <a href="https://github.com/semantic-release/semantic-release">semantic-release</a>. Semantic Release implemented a principle, which I vibed with as a web developer, of a separation of concerns based on semantics.</p><p>Before you go run off and start using semantic-release, there are some limitations I encountered with it… And I’ll get to those in a moment.</p><p>First, at a high level, the basic idea that semantic-release preached was that you’d follow a specific format for your commit messages, and based on the messages, the version could be calculated.</p><p>It spoke of removing the emotion from releases, making them robotic, based on what has actually changed. This had the benefit of allowing the version number change to communicate what had changed.</p><p>If there was a new major version release – v2 to v3 for example, that meant there was a breaking change.</p><p>To signify this in your commit message you’d include BREAKING CHANGE: in the body of the commit.</p><pre>feat: revamp user auth flow<br><br>BREAKING CHANGE: Per Chad&#39;s &quot;game-changer&quot; vision in the 3am Slack rant, we&#39;ve ditched the old auth system for a blockchain-based solution because &quot;passwords are so 2024.&quot; Update your clients or enjoy the 500 errors!</pre><p>Minor version bumps, like v2.0.0, to v2.1.0 signified there was a new feature added.</p><pre>feat: add dark mode toggle<br><br>Per the 47-comment thread in the &quot;urgent&quot; ticket, users can now save their retinas. Dark mode added, but brace for the inevitable &quot;make it darker&quot; feedback.</pre><p>Patch versions signaled fixes, or refactors, or basically most anything else that should trigger a new release.</p><pre>fix: revert &quot;fix&quot; by AI that skipped the breaking tests to avoid the failure</pre><p>And finally, some commits should not trigger a release at all. “Chores” as they were introduced to me. No-ops, as I call them.</p><pre>chore: update release pipeline version from v3.1.0 to v3.1.1</pre><p>This specific format of commit messages was known as <a href="https://www.conventionalcommits.org/">Conventional Commits</a>, though there are others. You can check our their page for the full spec, but I wanted to share the gist of it.</p><p>Furthermore, this information contained in the commits could also be surfaced as a change log — such as highlighting a breaking change. For example:</p><pre>What&#39;s new in v2.0.0<br><br>* feat: remove deprecated API<br><br>  BREAKING CHANGE: the FooBar API that was deprecated. To upgrade, you can use the new BazBar API. </pre><p>So, for a while, semantic-release was great for me. For Node projects, it still is… However…</p><h3>Into DevOps</h3><p>I was always interested in the idea of how to build scalable distributed systems, and so it was something I pursued for quite some time in my career. Long story short, I eventually learned how to do it well, but had a new problem: how to deploy all the pieces.</p><p>These weren’t npm modules, they were applications, services, databases, and queues!</p><p>I loved the simplicity of semantic-release, but it only worked well for Node.js projects. Sure, you could, and I did a few times, add a package.json file to non-node projects, but that was messy and hacky at best.</p><p>Around this time, I became deeply involved in DevOps, during the Jenkins era, as Docker was starting to gain traction. I wrote about <a href="https://medium.com/hackernoon/my-journey-to-achieving-devops-bliss-without-useless-aws-certifications-a7cbf7c539d1">my early Devops Journey and discovery of Docker for deployment.</a> I learned to write pipelines with Jenkins, and deploy everything with Docker Swarm!</p><blockquote>I was also embracing trunk based development at that time, and <a href="https://medium.com/hackernoon/i-have-a-confession-to-make-i-commit-to-master-6a804f334beb">committing to master</a>. My more recent and still relevant and accurate <a href="https://patrickleet.medium.com/a-guide-to-git-with-trunk-based-development-1e6345c65c78">Guide to Git with Trunk Based Development</a> article explains the why and how of that if you’re interested.</blockquote><p>I always wanted these projects that I worked with to be versioned and released like semantic-release projects were, but I never found a solution that worked as well. Many wouldn’t be able to handle generating changelogs correctly, and some would sporadically bump the version wrong.</p><p>It wasn’t the most important thing on my list, so I generally called it good enough and lived with the flaws.</p><p>Over time, as a DevOps consultant, I have encountered and released various projects in multiple languages and frameworks, and every single one has required versioning, releases, and changelogs.</p><p>Another headache I often encountered was large, monolithic pipelines. Coming from the Jenkins era, I get it, this is how I used to write pipelines too. I‘d try to have the whole release flow in a big series of steps starting with a push to main. If you pushed to main, various quality checks would run, and a new version would be built, published, tagged and released.</p><p>This isn’t terrible per-say and also probably “good enough” in most cases, however, as a DevOps consultant, I have to do it repeatedly. Breaking things down to smaller more modular pieces, allows those pieces individually to serve more projects. I first heard this described by the infamous James Halliday, AKA substack, as <a href="https://www.youtube.com/watch?v=faxfLmChjVQ">the coverage of ideas</a>, an idea that comes from embracing UNIX philosophies.</p><p>For example, a Node Quality pipeline can be the same in every Node project, assuming you follow the standard npm script conventions, and so a generic pipeline for running linting, building, and unit testing can be shared across most, if not all, Node.js projects. The pipeline steps are simply calling npm run lint --if-present and etc, and the scripts section of the package.json file determines what happens when that lint command is run.</p><h3>High-Level Pipelines For Versioning and Releasing</h3><p>At a high level, every project that needs to be released will first be tagged with a new version number. Then, a release can be created that is linked to that version number, and artifacts will be produced as part of its associated pipelines.</p><p>I use GitHub Actions most frequently, as I think pipelines are commodities, and it’s a simple one to get started with. I’ve used many, but they are all essentially the same — perform a series of steps with a shared volume. That shared volume generally contains the checked-out source code.</p><p>Versioning itself, if we are following the standard of conventional commits being tied to a semantic version increase, is not a language-specific thing. It has no dependencies on the contents of the code; instead, it’s concerned with the history of versions and commits.</p><p>For a long time, I tied releases to versions, thinking they were part of the same thing, when in reality they are two distinct entities joined together. A tag, and a release.</p><p>Splitting them apart made the versioning part of the pipeline its own modular piece, which then triggered a build of that version and subsequent release.</p><p>Let me break it down… For each project, in Github Actions, I have two pipelines:</p><ol><li><strong>On commit to the trunk branch, version and tag<br></strong>When a commit occurs in the trunk branch (usually main or master), trigger a pipeline that runs quality checks, and, if they pass, calculates a new version number. It then creates and pushes a tag with that new version number. We can also use this opportunity to update that version number in the code base if necessary, and generate a change log from the commit data that was used to calculate the new version number.</li><li><strong>When a tag is created, build new versions and release them. </strong><br>This step is simplified by the previous step doing the version bump. Everything’s already been bumped to the new version. We can just run build and release using the tag as the base.</li></ol><p>As I mentioned earlier, after trying many times to piece together open-source components from the GitHub Actions Marketplace, I eventually gave in and wrote my own tool that handles step one. I also wrote some workflows, which I’ll share with you as well.</p><h3>Introducing vnext</h3><p>vnext is a fast Rust CLI tool that uses conventional commit messages to compute your next semantic version, automating major, minor, or patch bumps for streamlined releases.</p><p>Here’s how to use it.</p><pre>NEXT_VERSION=v`vnext`<br><br>vnext --changelog &gt; CHANGELOG.md</pre><p>I think it’s pretty straightforward! Let me know what you think.</p><p>It is entirely based on the project’s git history.</p><p>It does not matter what language or tool you are releasing; the versioning process is basically the same. Get a new version, update it in some files, create a changelog, tag, and push. The only difference is which files need to be updated — this is accounted for in the workflow I’ll share in a moment.</p><p>vnext is quick and easy to install via ubi —<a href="https://github.com/houseabsolute/ubi"> the “Universal Binary Installer”</a>. Once configured, you can run:</p><pre>ubi --project unbounded-tech/vnext</pre><p>I’ve also created a shared GitHub Actions workflow that you can call, which handles all those details.</p><p>Here’s how to use the shared workflow:</p><pre>name: On Push Main, Version and Tag<br><br>on:<br>  push:<br>    branches:<br>      - main<br>      - master<br><br>permissions:<br>  packages: write<br>  contents: write<br><br>jobs:<br><br>  version-and-tag:<br>    uses: unbounded-tech/workflow-vnext-tag/.github/workflows/workflow.yaml@v1<br>    with:<br>      useDeployKey: true<br>      changelog: true<br></pre><p>Most projects will also need to configure how and which files to update with the new version number.</p><p>Currently, vnext supports two generic methods (yqPatches and regexPatches) and a few language-specific options.</p><p>The language-specific options are the simplest to use. For example, setting with.node to true uses npm to update the project’s package files.</p><p>yqPatches uses the tool <a href="https://github.com/mikefarah/yq">yq</a> to patch specific fields in a YAML file. I often use this for updating version numbers in Helm charts:</p><pre>  version-and-tag:<br>    uses: unbounded-tech/workflow-vnext-tag/.github/workflows/workflow.yaml@v1.13.0<br>    secrets:<br>      GH_PAT: ${{ secrets.YOUR_ORG_SECRET_PAT }}<br>    with:<br>      usePAT: true<br>      changelog: true<br>      yqPatches: |<br>        patches:<br>          - filePath: helm/values.yaml<br>            selector: .image.tag<br>            valuePrefix: &quot;v&quot;<br>          - filePath: helm/Chart.yaml<br>            selector: .version<br>            valuePrefix: &quot;v&quot;</pre><blockquote>I also took this opportunity to share how to use a Personal Access Token instead of a Deploy Key.</blockquote><p>regexPatches uses sed to find and replace a string with the new version number inserted. For example:</p><pre>regexPatches: |<br>  patches:<br>    - filePath: package/composition.yaml<br>      regex: /ghcr.io/org-name/package-name:(.*)/g<br>      valuePrefix: ghcr.io/org-name/package-name:v<br>    - filePath: README.md<br>      regex: /Current version: v[0-9]+\.[0-9]+\.[0-9]+/g<br>      valuePrefix: Current version: v</pre><p>You can use a combination of options as well.</p><h4>A nuance about GitHub Actions</h4><p>When you run an action on GitHub Actions, by default, the worker generates a temporary GitHub authentication token. This token has permission to perform a few basic tasks; however, it is never allowed to trigger other pipelines.</p><p>But our next step after tagging a new version was to trigger another pipeline with that tag!</p><p>Do not fret, this is by design — simply a measure by GitHub to prevent unintentionally spinning up a bunch of runners. You are allowed to do so, but you need to be intentional about it.</p><p>There are a few ways to be intentional about it — I’ve already shown an example of two:</p><ol><li>Using a Deploy Key and corresponding GitHub Actions Secret</li><li>Using a Personal Access Token as a GitHub Actions Secret</li><li>Creating a GitHub Application (theoretically — I haven’t had a need for this option)</li></ol><p>Using a Personal Access Token is convenient if you are a paying customer in the GitHub ecosystem, as organization-wide secrets are available.</p><p>On the free tier, that’s not an option, so I’ll show you how to set up a deploy key instead.</p><p>In fact, I made it very simple by building it into the vnext CLI. It is a great option, as when configured this way, no human needs to know it, and it can easily be rotated by rerunning the same command.</p><p>For each repository you want to set up vnext to release with a deploy key, in the directory of the project…</p><p>First, get an auth token using gh with permission to manage keys:</p><pre>gh auth refresh -h github.com -s admin:public_key -s admin:ssh_signing_key<br><br>export GITHUB_TOKEN=$(gh auth token)</pre><p>And then run:</p><pre>vnext generate-deploy-key</pre><blockquote>You may be wondering, can’t this go into a pipeline itself? Unfortunately, I can’t find a way to automate this step in Github Actions without creating a Personal Access Token, as the default token can not modify Secrets. This defeats the pupose of using a Deploy Key instead in the free tier, as you’d need to create it in every repo. This requires some sort of secret management to make it easy, and thus makes it more work than using a deploy key. I have thought of it. :)</blockquote><p>With your Deploy Key, or Personal Access Token (PAT), in place, and the version and tag workflow running on pushes to your trunk branch, the next step is to release!</p><p>This will vary per project, but they are all triggered by the version being tagged from the previous step. These pipelines will generally look something like this:</p><pre>name: On Version Tag, Trigger GitHub Release<br><br>on:<br>  push:<br>    tags:<br>      - &#39;v*.*.*&#39;<br><br>permissions:<br>  contents: write<br><br>jobs:<br>  release:<br>    uses: unbounded-tech/workflow-simple-release/.github/workflows/workflow.yaml@v1<br>    with:<br>      tag: ${{ github.ref_name }}<br>      name: ${{ github.ref_name }}</pre><p>Or, if it were a Rust project, maybe something like this:</p><pre>name: On Version Tagged, Build and Publish Rust Binaries<br>on:<br>  push:<br>    tags:<br>    - &quot;v*.*.*&quot;<br><br>permissions:<br>  contents: write<br><br>jobs:<br>  build-and-release:<br>    uses: unbounded-tech/workflows-rust/.github/workflows/release.yaml@v1<br>    with:<br>      binary_name: ${{ github.event.repository.name }}<br>      build_args: &quot;--release --features vendored&quot;</pre><p>Or if it were an application with a Dockerfile, and k8s resource definitions for Gitops deployments, maybe something like this:</p><pre>name: promote<br>on:<br>  push:<br>    tags:<br>    - v*.*.*<br><br>permissions:<br>  contents: write<br>  packages: write<br>  issues: write<br>  pull-requests: write<br><br>jobs:<br><br>  publish:<br>    uses: unbounded-tech/workflows-containers/.github/workflows/publish.yaml@v1.1.1<br><br>  release:<br>    needs: publish<br>    uses: unbounded-tech/workflow-simple-release/.github/workflows/workflow.yaml@v1<br>    with:<br>      tag: ${{ github.ref_name }}<br>      name: ${{ github.ref_name }}<br><br>  promote:<br>    needs: release<br>    uses: unbounded-tech/workflows-gitops/.github/workflows/helm-promote.yaml@v1<br>    secrets:<br>      GH_PAT: ${{ secrets.GH_PAT }}<br>    with:<br>      environment_repository: your-org/staging-env<br>      path: .gitops/deploy<br>      project: staging</pre><blockquote>Note: A deploy key can’t push to other repos, such as in the gitops promote step, so you might consider using a PAT everywhere for this repository. If you have lots of workloads like this, upgrading to a paid org might be worth it for organizational wide secrets rather than configuring every repo separately.</blockquote><p>Generally, every release is a combination of some pieces of these similar modules, which do pretty much the same things but for their specific tools or languages.</p><p>Usually, organizations at least try to follow many of the same patterns across applications and services they build, and so breaking down the workflows into shared modular pieces allows devs to pretty much just copy and paste their flavor of these shared workflow calls to each project of that type.</p><h3>Consistent Releases with Changelogs</h3><p>You may have noticed I kind of glanced over the with.changelog flag in the Version step. I wanted to circle back to that, as it’s a significant part of a mature release process. The changelog is how you can communicate the ways in which your project has changed to other developers.</p><p>Along with bumping versions, vnext also uses the commit messages to construct this changelog and saves it into a file named CHANGELOG.md, which is committed along with the version tag and version bumps.</p><p>Hidden in those release workflows I shared above, this file is used as the body of the GitHub Release. For example, from the vnext project itself:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UEN5eYhV_yN4q8KHS6YS4Q.png" /><figcaption>v1.8.1 of vnext in the Github Releases UI</figcaption></figure><p>This release was a patch version, as it contained a fix, in the form of a dependency version bump, and a number of other chores.</p><p>These release notes are then surfaced in other tools, such as <a href="https://docs.renovatebot.com/">Renovate</a>, a tool that makes PRs to keep your dependencies up to date.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*iSXdu1xu9YDieGOZrRV6aQ.png" /><figcaption>Example Renovate PR Showing Change Log / Release Notes</figcaption></figure><p>When merging Pull Requests, I like to use the Squash and Merge feature of GitHub in order to have the opportunity to write a nice final commit message title and body that will be used in the changelog.</p><p>For example:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Rcysh5Zt6nUMJuAKEuorbw.png" /><figcaption>Squash and Merge UI for Github Pull Requests</figcaption></figure><p>As shown in the example, you can put full-fledged Markdown as the commit body. It will be rendered appropriately in the Release Notes!</p><h3>Conclusion</h3><p>I know versioning and releasing isn’t the sexiest topic; it’s rather boring — or rather, it should be!</p><p>However, this isn’t always the case. That’s why I created <a href="https://github.com/unbounded-tech/vnext">vnext</a> and a bunch of <a href="https://github.com/unbounded-tech/workflows">shared workflows</a>! Now I can easily onboard most projects very quickly!</p><p>Hopefully it’s helpful for you too!</p><p>Please let me know if you give vnext and some shared workflows a try! I’d appreciate some stars on GitHub and a share if you find them useful!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=55e29e4d9765" width="1" height="1" alt=""><hr><p><a href="https://codeburst.io/releasing-new-versions-should-be-boring-really-boring-55e29e4d9765">Releasing New Versions Should Be Boring. Really Boring.</a> was originally published in <a href="https://codeburst.io">codeburst</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What’s wrong, honey? You’ve hardly touched your electrons…]]></title>
            <link>https://patrickleet.medium.com/whats-wrong-honey-you-ve-hardly-touched-your-electrons-c4b5718fc01e?source=rss-8f48bb19ea99------2</link>
            <guid isPermaLink="false">https://medium.com/p/c4b5718fc01e</guid>
            <category><![CDATA[health]]></category>
            <category><![CDATA[local-food]]></category>
            <category><![CDATA[biophoton]]></category>
            <category><![CDATA[mitochondria]]></category>
            <category><![CDATA[sun]]></category>
            <dc:creator><![CDATA[Patrick Lee Scott]]></dc:creator>
            <pubDate>Sat, 09 Aug 2025 19:20:02 GMT</pubDate>
            <atom:updated>2025-08-09T19:20:02.136Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/720/1*6nTKc1MaAy9f1mvPWBGWQQ.jpeg" /></figure><p>I went to a party a couple of weeks ago, and out of an entire table of food, not a single thing was from Texas.</p><p>The next time you walk through a grocery store, take a look — what’s local? Even in a high end market like Central Market, or Wheatsville, a co-op store in the area, or a Sprouts, a natural food health based grocery chain… Almost nothing is from where you live. Around here you’ll maybe find some local watermelon or peaches this time of year.</p><p>We are so disconnected from our food systems, that when a group of people get together and bring food, it almost entirely comes from many hundreds to thousands of miles away.</p><p>Food contains light information — biophotons. Animals, and us, create biophotons as well. Infrared light as a byproduct of Oxidative Phosphorylation, which is the primary process our body uses to create energy. We also produce UV biophotons in response to cold exposure.</p><p>When we eat food that is local to us, we are getting light information about the environment we live in which helps us adapt to it. When we eat food that was shipped from the tropics when we live in the north, we are sending our bodies the wrong signals. Also consider that these biophotons decay over time.</p><p>There’s a Native American tribe’s story about how fire is stored sunlight inside of plants, and the process of making a coal through friction is releasing that stored sunlight energy. In addition to getting energy from food through Oxidative Phosphorylation, we also store electrons from sunlight, as well as collect them from the Earth when grounded, and by generating piezoelectric energy through movement.</p><p>Electrons are funneled through the cell’s Electron Transport Chain, a series of protein complexes and electron carriers embedded in the inner mitochondrial membrane. Electrons are passed along the chain through a series of reduction or oxidation reactions (redox), moving from higher to lower energy levels. Each complex in the chain has different affinities for electrons, causing them to be transferred from one molecule to another in a specific sequence of oxidation and reduction. The energy released during these transfers is used to pump protons across a membrane (inner mitochondrial membrane in eukaryotes), creating a higher concentration of protons on one side. This electrochemical gradient represents potential energy. ATP synthase, embedded in the membrane, acts like a channel for protons to flow down their concentration gradient back across the membrane. This flow of protons provides the energy to rotate a part of the ATP synthase molecule, which then converts ADP and inorganic phosphate into ATP. ATP is the so called “energy molecule” used throughout our bodies.</p><p>We essentially have a store of electrons that we collect from being grounded on the Earth, swimming in it’s waters, bathing in it’s sunlight, climbing it’s mountains, and eating it’s fresh electron rich food, and arguably the aether, you know, if that exists. We use these electrons to power trillions of cells, which collectively produce more energy every second than a bolt of lightning. They are conducted through our coherent water based liquid crystal fascia to every part of our body.</p><p>My truck about a year ago had an issue with a component that was responsible for distributing energy to the different electronics of the vehicles. This started as issues with the touchscreen no longer responding to touch, and eventually led to the vehicle not starting, and the piece needing to be replaced. A vehicle is a system, and the failure of a single part can cause the failure of the whole system.</p><p>We are also systems — ones much more complex than a truck. If we have issues getting energy to the various subsystems of our bodies, whether through conductivity issues, or collection issues — not having enough, or not being able to get it where it needs to go, then we run into trouble. After time this can lead to cascading issues. Cells throughout the body panic and enter the cell danger response, shutting down essential health time duties in exchange for batoning down the hatches and survival mode. This manifests as many types of disease and issues throughout the body, such as the inability to detox properly, or evict parasites. Yes, that’s right, I’m saying parasites and heavy metals and nutrient imbalances, defeciencies, and dependencies are symptoms, not root causes. Therefore, detoxing before or without addressing redox potential, or the bodies ability to conduct electrons throughout the body, are very large missing pieces of the puzzle for many people.</p><p>Hell, most people didn’t even realize they were electric.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c4b5718fc01e" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to find the fastest path to success without wasting years exploring the wrong ones!]]></title>
            <link>https://patrickleet.medium.com/how-to-find-the-fastest-path-to-success-without-wasting-years-exploring-the-wrong-ones-b124bb706c37?source=rss-8f48bb19ea99------2</link>
            <guid isPermaLink="false">https://medium.com/p/b124bb706c37</guid>
            <category><![CDATA[personal-development]]></category>
            <category><![CDATA[success]]></category>
            <category><![CDATA[entrepreneurship]]></category>
            <dc:creator><![CDATA[Patrick Lee Scott]]></dc:creator>
            <pubDate>Thu, 19 Oct 2023 20:50:09 GMT</pubDate>
            <atom:updated>2023-10-20T03:38:09.630Z</atom:updated>
            <content:encoded><![CDATA[<h4>This seemingly obvious advice can save you years of time!</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NAnqNcgf_pOTZV0lRnZ_XQ.jpeg" /></figure><p>It’s a hard thing to generalize what exactly makes someone successful, but, over many years, I’ve landed on a framework that works well for me.</p><p>I’ll get to that in a moment, but first, let me share a quick story that illuminates the point I am trying to make.</p><p>In 2019, one of my best friends died. This was due to a congenital heart defect, and a resulting build-up of scar tissue over the years. As this was something he was born with, knew about, and saw a specialist to monitor yearly about, I’m not suggesting there was a lot that could have been done differently, yet, regardless of that, it certainly made me consider my own mortality.</p><p>“You can just die at 30 years old…” I thought to myself.</p><p>Up until this point, I have to admit, I felt pretty invincible, but the sudden death of a close friend brought that belief crashing down around me.</p><p>I took a good long look at myself, and my health, and had to admit that I was far from where I needed to be…</p><p>It was time to make some drastic changes… but what changes, exactly?</p><p>There are a plethora of differing opinions on the internet about what to do to be healthy. Luckily, I had learned some very poignant advice from one of my mentors, Russell Brunson…</p><blockquote>“If you want results, find someone who has already achieved those results, and do what they say! Don’t take advice from someone who hasn’t achieved those results themselves!”</blockquote><p>So that’s what I did. I found a dude who was jacked to be my mentor in getting in shape and did what they said. This person was Dr. John Jaquish.</p><p>Dr. Jaquish had previously spent years in the gym with very lackluster results but recently had figured out a formula that worked for him, and suddenly he was unstoppable. He clearly had the results I wanted, so, I figured I should listen to his advice.</p><p>This advice consisted of a few key pieces:</p><ol><li>Variable resistance strength training</li><li>Carnivore nutrition</li><li>Fasting</li></ol><p>All I had to do was be disciplined enough to follow the advice consistently, and it should work for me too!</p><p>So I did.</p><p>In the first three months, I went from 210 lbs to 180 lbs. In the 9 months following, I leaned out all the way down to 160 lbs.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PIt67ETHIwNDoISCC9NmWA.jpeg" /><figcaption>One year of following advice from someone who’s done it – down 50 lbs!</figcaption></figure><p>In just one year, by following the advice of Russell, to follow the advice of someone who had the results I wanted, Dr. Jaquish, I was healthier than I had been in years! Not only that, but the results came quickly and easily.</p><p>This wasn’t the first time I’d tried to reach these goals, but in the past I just went off on my own, reading conflicting opinions from different people, and trying to figure out what worked or did not on my own.</p><p>If someone has already been down all of the paths and has seen what worked, and what did not, you really don’t need to do that as well!</p><p>And so, that leads to my key point – the fastest path to success is the one that has already been proven to get the results that you are looking for! Instead of wandering down many paths yourself, find someone who’s done that already and ask them “What is the best path to take?”</p><p>Yet, this seemingly obvious path is not one that many people consider. We’ve been trained from a young age to “figure it out” through our schooling system — to seek the “how?” of a solution when all along there’s been a better question to ask. “Who?”</p><p>This simple advice, “who, not how”, is something that the highest achieving entrepreneurs are intimately aware of.</p><p>The first time I became aware of this concept was when I read the famous book “Think and Grow Rich” by Napoleon Hill. The premise of this book is simple — think about what you are going to do before you do it and you’ll get much better results.</p><p>For those who are not aware of this cult-classic book, let me explain a bit…</p><p>Napoleon Hill was Andrew Carnegie’s right-hand man. He was tasked by Carnegie to go out and research — how does one become “successful”? He leveraged Carnegie’s network of successful people by interviewing them all and compiling everything he learned into a book. This book became “Think and Grow Rich”. Napoleon became fascinated with the subject and went on to write many other extremely influential books as well, such as “Outwitting the Devil”.</p><p>In “Think and Grow Rich”, one of the key concepts that Napoleon Hill outlines is something he called a “mastermind”. A mastermind is a meeting of the minds of the various masters of a subject. This concept is extremely popular nowadays, with every Guru leading a mastermind of their own, and for good reason! Getting the best and brightest together to share what paths they’ve taken, which ones paid off, and which ones did not, is an ENORMOUS time saver.</p><p>Napoleon Hill’s <em>Think and Grow Rich</em> is a highly recommended read. It’s widely revered for a reason. Mr. Hill was a legend of his time. A first edition of a rare book set of his <a href="https://secretsofsuccess.com/bibliomania?afmc=eej">was recently sold for $1.5M</a> to give you some idea.</p><p>So there you have it, my secret framework for success — if you want to be successful, find someone who’s gotten the results you’re looking for, and do what they say!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AkxX7SNo17tYtBsDOGUT8w.jpeg" /><figcaption>My business partner Phillip and I with the founders of Clickfunnels: Russell Brunson and Todd Dickerson. Thanks for the advice Russell!</figcaption></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b124bb706c37" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A Guide to Git with Trunk Based Development]]></title>
            <link>https://patrickleet.medium.com/a-guide-to-git-with-trunk-based-development-1e6345c65c78?source=rss-8f48bb19ea99------2</link>
            <guid isPermaLink="false">https://medium.com/p/1e6345c65c78</guid>
            <category><![CDATA[trunk-based-development]]></category>
            <category><![CDATA[version-control]]></category>
            <category><![CDATA[git]]></category>
            <category><![CDATA[gitflow]]></category>
            <category><![CDATA[devops]]></category>
            <dc:creator><![CDATA[Patrick Lee Scott]]></dc:creator>
            <pubDate>Sun, 21 Mar 2021 19:37:03 GMT</pubDate>
            <atom:updated>2021-03-25T15:34:13.492Z</atom:updated>
            <content:encoded><![CDATA[<h3>Learn the mindset and process behind Trunk Based Development</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wyZKiuhSFvrravF81CfOww.jpeg" /></figure><p>Through my consulting business, I equip technology organizations with the latest and greatest in DevOps tooling to increase the velocity of their development teams.</p><p>Part of this means abandoning outdated workflows like “GitFlow” and replacing them with more streamlined processes — Trunk Based Development and Continuous Deployment.</p><p>We’ll get more into “Trunk Based Development” soon — but it’s important for this conversation to define what “trunk” is, as I’ve seen this misused as well.</p><p>The “trunk” is the main or master branch of your repository. Trunk is a constant. In trunk based development, you either commit to the trunk branch, or make branches and pull requests against the trunk branch. There are no long lived alternative branches to merge against such as development.</p><p>I’ve seen teams create a new branch and call it the new “trunk” every few weeks. Although I applaud the effort, it was missing the mark by quite a lot.</p><p>We’ll get more into what this process looks like end-to-end later on… First, let me spend a minute backing up my claims of GitFlow being outdated, and explain the goals of moving to a different approach.</p><p>Some of the best programming advice you’ll ever receive is from Eric Evan’s required reading, <em>Domain Driven Design</em>. Domain Driven Design, for the uninitiated, is a methodology for modeling business domains in code. The advice is the following:</p><blockquote><em>“Make implicit concepts explicit” — Eric Evans, Domain Driven Design</em></blockquote><p>What exactly does this mean? Also, how does this apply to Git or GitFlow?</p><p>First, let’s define what an “implicit concept” means. An implicit concept is something that happens as a side effect of something else. For example, a side effect of committing to the development using GitFlow is often to tie that to deploying a version of that service to an environment such as staging.</p><p>The explicit action taken is committing to a branch. The implicit action caused by that action is running a deployment process. We didn’t explicitly change our staging environment — we changed our application’s codebase on a specific branch and it updated the environment as a side effect.</p><p>We then often see the same thing committing against the “trunk” (main/master) branch. The explicit action is committing to master - the implicit action is tying some sort of deployment process to this explicit action. Expand that to several repositories and now you’ve got yourself a real headache.</p><p>So, ask yourself — how can we make the currently implicit concepts of releasing new versions of our software, explicit?</p><p>The answer: by making those concepts a part of our model. The reason for the awkwardness we are feeling is because the model doesn’t actually model our environments. Our environments in this model are entirely implicit — and as you can imagine, things like production environments are quite important pieces — one that should be modeled explicitly, for sure.</p><p>To do so, we can create repositories that explicitly model each of our environments. Meaning instead of tying the implicit concept of releasing and deploying code to our commits, we can instead explicitly define the requirements of such a release and deployment in a codebase.</p><p>For example, our staging environment’s repository might look something like this:</p><pre>dependencies:<br>- name: content<br>  repository: http://bucketrepo/bucketrepo/charts/<br>  version: 0.0.75<br>- name: mongodb-replicaset<br>  repository: https://kubernetes-charts.storage.googleapis.com/<br>  version: 3.15.1<br>- name: person-model<br>  repository: http://bucketrepo/bucketrepo/charts/<br>  version: 0.0.20<br>- name: www<br>  repository: http://bucketrepo/bucketrepo/charts/<br>  version: 0.0.55</pre><p>What version of each application is in staging?</p><p>The answer is no longer “whatever is in the development branch for each project”. It’s 0.0.75, 3.15.1, 0.0.20, and 0.0.55. It’s explicitly defined in <a href="https://github.com/CloudNativeEntrepreneur/environment-cldntventr-staging/">an environment repository</a>.</p><p>Meanwhile your production environment might be on www version 0.0.50 and content on 0.0.74. Also explicitly defined in code.</p><p>Much better than “whatever is in trunk of each project? 🤷‍♂️”</p><p>To accomplish this we’ll need to approach things a bit differently than you’re probably used to.</p><p>We’ll still want to tie some actions to commits, but not so many different concepts — we only want to do things that are related to the codebase we are working on. Instead we can limit that action to releasing a new versioned artifact of the project we are working on.</p><p>More explicitly, this means the first commit to the trunk branch will release version 0.0.1 of that project, bundled and ready to go – finalized and immutable. The second commit to trunk will result in release 0.0.2, and the third 0.0.3, etc. We can also run some automated tests as part of that release process, so if commit four failed those tests for example, 0.0.4 would not be released until the 5th commit which makes the tests pass again.</p><p>What exactly those versioned artifacts are will vary by the type of project you are working on, but are usually Docker images, chart repositories, or various language specific packages like NPM or Maven releases.</p><p>Next, we can hook into the release process and with each new release, caused by each verified commit to Trunk, to explicitly <a href="https://github.com/CloudNativeEntrepreneur/environment-cldntventr-staging/pulls?q=is%3Apr+is%3Aclosed">update the staging environment</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*uXFezzQTwdXRSMg6.png" /></figure><p>Then, once the explicit combination of versioned artifacts are verified in the running staging an environment, they can all be moved into production in <a href="https://github.com/CloudNativeEntrepreneur/environment-cldntventr-production/pull/4/files">a single explicit commit</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*etmWq9jx0HRWRsbB.png" /></figure><p>So that’s pretty much modern Trunk Based Development in a nutshell.</p><p>Not so bad, right?</p><p>You can use a tool like Jenkins X to get this set up for you out of the box. Using JX can be a bit overwhelming as it is based on Kubernetes and several tools in the Kubernetes ecosystem. In my course, <a href="https://www.cloudnativeentrepreneur.com/products/DB">DevOps Bliss</a>, I walk through setting up a system like this end to end, as well as get into other topics like SSO, load testing, and more! Check it out if you’re interested!</p><h3>What exactly is in git history?</h3><p>With our Trunk Based Development workflow defined, it’s worth taking a moment to explain how to think about what your project’s git history really is.</p><p>The history of your repository is a series of commits in an ordered list. To move to different spots in that history, pointers are used. These pointers are called branches and tags.</p><p>Check out this image pulled from the <a href="https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell">Git — Branches in a Nutshell</a> documentation.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/0*pYJxUoCjlExZn6el.png" /></figure><p>It’s helpful to remember this as you are working with Git.</p><p>This whole structure, the commits, the pointers, etc, are all copied to your machine when you clone a repo. They are not kept up to date automatically though.</p><p>You are probably used to using git pull at this point to get the changes from the origin.</p><p>Instead, I want to show you a few alternative commands to help you with your Trunk Based Development workflow.</p><h3>General Workflow</h3><p>In this next section I want to capture the “day in the life” decision making and git workflows you’ll need to employ when working on a project that follows Trunk Based Development.</p><p>First of all, whether you are working directly against the Trunk branch (yes, this is ok), or on a branch that will be merged to the trunk branch, you’ll need to know what’s happening back on Github — information like, have other developers changed the code since I’ve last checked?</p><p>I’m assuming you are familiar with the basic git workflow of add, commit, push, and pull, and here is where you might think pull. I want to give you a more granular way of thinking about it instead.</p><p>To do that, we’ll start with a git fetch.</p><h3>git fetch</h3><p>As you are working away on your next fix or feature, either directly on the Trunk branch, or on a short-lived branch that will eventually be merged into the trunk branch, so are other developers.</p><p>They are pushing and pulling from the same remote on Github named origin as you are. Adding new commits to the log, and new pointers in the form of branches and tags.</p><p>The command git fetch simply fetches the information about these changes. It does not update your local copy with the remote changes, it simply makes git aware that those changes exist.</p><p>So, as you’re busy working, every once in awhile, run:</p><pre>git fetch --all -p</pre><blockquote><em>The </em><em>--all flag tells git to fetch from any </em><em>remote you’ve added, </em><em>origin or otherwise. The </em><em>-p flag stands for </em><em>prune which tells git it can cleanup deleted branch pointers and etc.</em></blockquote><p>Then you’ll know if there are any updates you need to bring into your local copy.</p><p>Here is an example of what that looks like:</p><pre>&gt; git fetch --all -p</pre><pre>Fetching origin<br>From github.com:servicebus/kafkabus<br> - [deleted]         (none)     -&gt; origin/renovate/mocha-7.x<br>remote: Enumerating objects: 150, done.<br>remote: Counting objects: 100% (150/150), done.<br>remote: Compressing objects: 100% (58/58), done.<br>remote: Total 198 (delta 125), reused 97 (delta 92), pack-reused 48<br>Receiving objects: 100% (198/198), 362.90 KiB | 1.31 MiB/s, done.<br>Resolving deltas: 100% (144/144), completed with 6 local objects.<br>   98169c3..6b4ca35  master                  -&gt; origin/master<br> * [new branch]      renovate/commitizen-4.x -&gt; origin/renovate/commitizen-4.x<br> * [new branch]      renovate/kafkajs-1.x    -&gt; origin/renovate/kafkajs-1.x<br> * [new tag]         v2.0.1                  -&gt; v2.0.1<br> * [new tag]         v2.0.2                  -&gt; v2.0.2<br> * [new tag]         v2.0.3                  -&gt; v2.0.3<br> * [new tag]         v2.0.4                  -&gt; v2.0.4</pre><p>In response to the git fetch call, I can see that master has been updated on origin compared to my local version of git history: 98169c3..6b4ca35. There are also a few new branches and tags.</p><p>Running git log will verify I am still on the 98169c3 commit locally.</p><pre>&gt; git log</pre><pre>commit 98169c3fabf3052dd89fe0c6900bc3c11a0252a4 (HEAD -&gt; master, tag: v2.0.3)<br>Author: Patrick Lee Scott &lt;pat@patscott.io&gt;<br>Date:   Sat May 23 13:48:00 2020 -0400</pre><pre>    fix: high throughput test already uses no transactions</pre><pre><em># ... more commits ...</em></pre><p>In the parenthesis we can see a few pointers for this commit: HEAD, master, and tag: v2.0.3.</p><p>With that information in mind, we’ll want to rebase our changes on to the version of history pointed to by origin/master, which was the latter, 6b4ca35, from the info about the fetch (98169c3..6b4ca35). That is to say, the changes in Github made it to Github first, before our changes, so our changes should be moved on top of it.</p><p>Depending on our situation, we’ll either want to use a regular, or an interactive rebase.</p><h3>Working on a fix directly against the Trunk branch</h3><p>If you are working on a fix that can be done easily, there is no issue, in my opinion, with committing directly against the trunk branch. I’ve worked on teams with 20 engineers doing this, and I promise you, it’s fine, so long as you have a solid CI/CD process. If this process fails, the worse that happens is nothing. The build is marked as failing, nothing is released or promoted.</p><p>The only rule is if you break the build it’s now your job to fix it, and that’s the top priority!</p><p>If you’re afraid of this, it’s ok to use a branch, and I’ll cover that workflow next.</p><p>So, when you are working directly on the trunk, you’ve fetched info from Github and there are new commits to incorporate, it’s now time to rebase.</p><h3>git rebase</h3><p>To rebase our changes on top of origin/master we run the command:</p><pre>git rebase origin/master</pre><p>This only works AFTER a fetch, otherwise, git doesn’t know the information about new commits and branches from the remote named origin. Make sense? Use fetch to get info, and rebase to then use that info.</p><p>All of the commits you’ve made after the initial commit, which is 98169c3 in this case, will be picked up, placed to the side, and then your history will be updated to match origin/master. This means your log of history will match the origin version of history - ending in 6b4ca35 in our example. Then your commits that were placed aside, will be placed on the end of the log.</p><pre>&gt; git rebase origin/master</pre><pre>First, rewinding head to replay your work on top of it...<br>Fast-forwarded master to origin/master.</pre><p>Now, when I run git log you’ll see the latest commit has been updated to the latter commit identified by fetch earlier (98169c3..6b4ca35).</p><pre>&gt; git log</pre><pre><em># ... your commits ...</em></pre><pre>commit 6b4ca353091a7d6a9eeba8ee5b1978112a81cabf (HEAD -&gt; master, origin/master, origin/HEAD)<br>Author: Renovate Bot &lt;bot@renovateapp.com&gt;<br>Date:   Wed Nov 4 06:34:00 2020 +0000</pre><pre>    chore(deps): update dependency jest to v26.6.3</pre><p>If there are no conflicts, this will be a pretty simple command.</p><p>If there are conflicts, they must be resolved first. To resolve conflicts, in your IDE, fix the conflicts, and when you are ready, save the changes, add them to git’s staging area, and then run git rebase --continue.</p><p>To learn more about rebase, make sure you read the Git’s docs - <a href="https://git-scm.com/book/en/v2/Git-Branching-Rebasing">Git - Rebasing</a>.</p><p>To learn more about resolving conflicts, I recommend learning the VSCode Merge tool: <a href="https://code.visualstudio.com/docs/editor/versioncontrol#_merge-conflicts">Version Control in Visual Studio Code</a></p><h3>Working on a branch that will be merged into Trunk via a Pull Request</h3><p>If you are doing something more than a quick fix, such as working on a new feature, you’ll likely want a branch to work on.</p><h3>git rebase -i origin/master</h3><p>The above git rebase works well when we are on master working on a single commit for a fix. When we are on a branch, preparing for a change to be merged into master via a Pull Request, it’s often expected that we make things nice and clean. Maintainers of open source projects will ask you to “squash and rebase” your commits into a single commit to be merged.</p><p>This keeps the history of the master branch clean and rollback’s easy to perform.</p><p>People will debate on the merits of doing this vs. not doing so, but let’s put all those reasons aside for now, and just focus on how to do it.</p><p>If you specify the -i flag or —interactive when you run the rebase command, instead of a regular rebase, you will be performing an “interactive rebase”.</p><p>This allows you to reword commits in your little segment of history, or squash multiple commits into a single commit.</p><p>Doing so drops you into an editor in your Terminal called vim, and for this reason, knowing the basics of VIM are required in order to perform the interactive rebase.</p><p>Here’s an example from the git documentation (<a href="https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History">Git — Rewriting History</a>):</p><pre>pick f7f3f6d Change my name a bit<br>pick 310154e Update README formatting and add blame<br>pick a5f4a0d Add cat-file</pre><pre><em># Rebase 710f0f8..a5f4a0d onto 710f0f8</em><br><em>#</em><br><em># Commands:</em><br><em># p, pick &lt;commit&gt; = use commit</em><br><em># r, reword &lt;commit&gt; = use commit, but edit the commit message</em><br><em># e, edit &lt;commit&gt; = use commit, but stop for amending</em><br><em># s, squash &lt;commit&gt; = use commit, but meld into previous commit</em><br><em># f, fixup &lt;commit&gt; = like &quot;squash&quot;, but discard this commit&#39;s log message</em><br><em># x, exec &lt;command&gt; = run command (the rest of the line) using shell</em><br><em># b, break = stop here (continue rebase later with &#39;git rebase --continue&#39;)</em><br><em># d, drop &lt;commit&gt; = remove commit</em><br><em># l, label &lt;label&gt; = label current HEAD with a name</em><br><em># t, reset &lt;label&gt; = reset HEAD to a label</em><br><em># m, merge [-C &lt;commit&gt; | -c &lt;commit&gt;] &lt;label&gt; [# &lt;oneline&gt;]</em><br><em># .       create a merge commit using the original merge commit&#39;s</em><br><em># .       message (or the oneline, if no original merge commit was</em><br><em># .       specified). Use -c &lt;commit&gt; to reword the commit message.</em><br><em>#</em><br><em># These lines can be re-ordered; they are executed from top to bottom.</em><br><em>#</em><br><em># If you remove a line here THAT COMMIT WILL BE LOST.</em><br><em>#</em><br><em># However, if you remove everything, the rebase will be aborted.</em><br><em>#</em><br><em># Note that empty commits are commented out</em></pre><p>In order to squash all of the commits, use VIM to edit the messages that say pick to say s or squash for the 2nd and 3rd commits:</p><pre>pick f7f3f6d Change my name a bit<br>squash 310154e Update README formatting and add blame<br>s a5f4a0d Add cat-file</pre><p>This will result in a single commit, Change my name a bit, that consists of the changes from all three commits.</p><p>Say you wanted to change the commit message as well, instead you might edit the commits like this:</p><pre>r f7f3f6d Change my name a bit<br>s 310154e Update README formatting and add blame<br>s a5f4a0d Add cat-file</pre><p>Saving this change would then prompt you with another VIM window that allows you to type a new commit message for the r or reword flag.</p><h3>git push origin/&lt;branch&gt; —force</h3><p>After a rebase, you’ve essentially modified the history of commits in the log. If you try to push these changes to origin you will get an error! This is expected.</p><p>You need to tell git that you have intended to change the history by using the —force flag. This will replace origin’s log of commits with your new rebased log of commits.</p><blockquote><em>WARNING: You should not be force pushing to master, just branches!</em></blockquote><h3>Rebasing when you have unsaved changes</h3><p>Need to rebase but have uncommitted changes?</p><p>If you try to do this, you will receive an error saying you must commit or stash your changes in order to rebase!</p><pre>&gt; git rebase origin/master<br>error: cannot rebase: You have unstaged changes.<br>error: Please commit or stash them.</pre><p>If you are ready to commit them go ahead and do so. If not, you can stash them, perform the rebase, and then retrieve your changes from the stash again.</p><h3>git stash</h3><p>To put all of your changes aside in a temporary space, run:</p><pre>git stash</pre><p>Then, you can complete the rebase:</p><pre>git fetch --all -p<br>git rebase origin/master</pre><p>After the rebase is complete, you can bring back all of the changes you were working on from your stash:</p><pre>git stash pop</pre><h3>--autostash</h3><p>The workflow above is common enough that rebase actually supports a flag, --autostash that combines all the steps into one!</p><pre>git rebase origin/master --autostash</pre><h3>Conclusion</h3><p>Although it can be more steps than git pull, sometimes pulling results in unexpected merge commits - using fetch, stash and rebase gives you more explicit control when working with git, which is very helpful when working with Trunk Based Development!</p><p>Hopefully you understand the benefits of making our previously implicit operations explicit and how you can use a few new git commands to ease that process!</p><p>If you’re looking to implement Continuous Deployment in your organization and are looking for help, reach out to me through my website <a href="http://patscott.io/">patscott.io</a>. If you’re a DIY-type of engineer like me, then check out my course <a href="https://www.cloudnativeentrepreneur.com/products/DB">DevOps Bliss</a> instead!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1e6345c65c78" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Bring In The Bots, And Let Them Maintain Our Code!]]></title>
            <link>https://patrickleet.medium.com/bring-in-the-bots-and-let-them-maintain-our-code-7dc1261da9a7?source=rss-8f48bb19ea99------2</link>
            <guid isPermaLink="false">https://medium.com/p/7dc1261da9a7</guid>
            <category><![CDATA[automation]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[nodejs]]></category>
            <category><![CDATA[testing]]></category>
            <category><![CDATA[code-coverage]]></category>
            <dc:creator><![CDATA[Patrick Lee Scott]]></dc:creator>
            <pubDate>Thu, 23 Jan 2020 00:00:00 GMT</pubDate>
            <atom:updated>2020-02-01T04:02:37.218Z</atom:updated>
            <content:encoded><![CDATA[<h4>Learn how to automate away as much maintenance as possible using Renovate, Travis CI, and CodeCov</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DAN7A53-2gKoTtPrrZiLEQ.jpeg" /></figure><p>Last year I wrote a five part series of articles that built <a href="https://github.com/patrickleet/streaming-ssr-react-styled-components">a React boilerplate using Parcel</a>. It demonstrated how to use <a href="https://hackernoon.com/move-over-next-js-and-webpack-ba367f07545">streaming server side rendering</a>, automatically <a href="https://hackernoon.com/enforcing-code-quality-for-node-js-c3b837d7ae17">enforce code quality</a>, achieve <a href="https://hackernoon.com/the-100-code-coverage-myth-900b83d20d3d">100% code coverage</a>, explored using <a href="https://hackernoon.com/a-better-way-to-develop-node-js-with-docker-cd29d3a0093">Docker for development</a>, and covered <a href="https://hackernoon.com/a-tale-of-two-docker-multi-stage-build-layers-85348a409c84">multi-stage Dockerfiles for production</a>.</p><p>Unfortunately, as time has passed, the boilerplate has begun to get stale. Some of the libraries I chose have changed their APIs, and as a result people following the tutorials as written have encountered some errors as they are getting later versions when following along and typing npm i.</p><p>It’s come time to update this boilerplate, but in my typical fashion, I’ve decided to turn it into a learning experience by showing how to let bots do the heavy lifting for me.</p><p>In this article we will use a particularly useful bot called “ <strong>Renovate</strong>” which will keep track of the project’s dependencies and make Pull Requests every time an update needs to be made. Renovate was recently acquired by WhiteSource and was previously named RenovateBot. Because we have a bunch of automated tests and code quality enforcement at each step of the way we should be able to quickly see if everything is still passing or if a particular update causes any breaks.</p><p>At the end, we will also explore what extra steps could be taken to give us even more confidence in each change.</p><p>Let’s get started.</p><h3>Step 1: Enable Renovate</h3><p>My repository is on Github, and therefore we will head over to the Github Marketplace where we can enable the <a href="https://github.com/marketplace/renovate">Renovate Application</a>. According to their website, there is also support for GitLab, as well as a tool that can be run with some manual set up in other situations such as On Premises. I’ve been using Renovate Bot for over a year now, with great success, but I’ve only tried the GitHub version.</p><blockquote><em>Developer time is expensive, best to let bots do as much as possible!</em></blockquote><p>The setup is pretty straightforward, just press “Install it for free” down at the bottom. If you need more help, I defer to checking <a href="https://renovate.whitesourcesoftware.com/">their website</a>, as the process may change after this article is written.</p><h3>Step 2: Configure Renovate</h3><p>Once Renovate has been installed, you can configure it from the “Settings” page of your account or organization in the “Applications” tab.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DnbpcglMvjAddjDMgWwEtw.png" /></figure><p>You’ll have the choice to enable it for all of your repositories, or select repositories. Seems how I have a lot of repos, I decided to only enable it for certain ones. This choice is up to you.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bXLMXExKmQou6T54mxhUWg.png" /></figure><p>Shortly after giving Renovate access to your repository you should receive a Pull Request titled “Configure Renovate” which will add a file named renovate.json to your project.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*R58lvCp96SoLqCiHoCvR7g.png" /></figure><p>If we take a look inside we can see that the config is pretty simple, but allows for <a href="https://docs.renovatebot.com/configuration-options/">lots of customization</a>.</p><pre>{<br>  &quot;extends&quot;: [<br>    &quot;config:base&quot;<br>  ]<br>}</pre><p>Some customizations you may want to make are things like enabling automerge if you have a high enough confidence in your automated test coverage and code quality enforcement. This will allow renovate to automatically update packages for you if all checks pass. It’s also possible to only enable automerge for minor versions, just devDependencies and much more. I’ll leave it to you to look through all of the configuration options and decide what is best for your team and project.</p><p>In this <a href="https://github.com/patrickleet/streaming-ssr-react-styled-components/pull/7">initial Pull Request</a>, Renovate will detail which package files it has detected and what Pull Requests with what updates you can expect to see once it’s been merged.</p><p>One of the reason’s I switched from using Greenkeeper to Renovate is it’s support for much more than Node.js packages. For our purposes, it will also keep Dockerfiles, <em>docker-compose</em>, and Helm chart files up to date, but also supports much more such as Gradle. The full list can be found in the project’s documentation.</p><p>To finish the configuring your repository for Renovate simply merge the Pull Request.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rs7fRanacOV2aSlcNC60Zg.png" /></figure><h3>Step 3: Automatically Run Tests For Every Pull Request with Travis CI</h3><p>In the previous articles we set up a bunch of Code Quality enforcement as well as configured automated tests with 100% code coverage. Before we start merging changes that could potentially break our project, we’ll want to make sure that all of these checks are run with every Pull Request.</p><p>There are many options to do this, but the quickest and easiest for open source projects is probably Travis CI.</p><p>Enabling Travis CI is just as easy as Renovate, just head on over to the Application in the Github Marketplace, select the free “Open Source” version, and press Install.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uPB9Kafc-tDy28pJZKSw3Q.png" /></figure><p>Again, you’ll have the option to enable it for all repositories, or select repositories. I’m again choosing to limit it to just my selected repos.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*N_4X2LBv1GNtxhKa3wD58Q.png" /></figure><p>In order for Travis to kick in, we’ll need to add a .travis.yml file to the project with the appropriate settings. The minimum we will need is to set the language of the project. Supported languages can be found <a href="https://docs.travis-ci.com/user/tutorial/">in their docs</a>.</p><pre>language: node_js</pre><p>Here’s the PR adding the file to the project: <a href="https://github.com/patrickleet/streaming-ssr-react-styled-components/pull/10">https://github.com/patrickleet/streaming-ssr-react-styled-components/pull/10</a></p><p>Travis notices right away and starts running our builds.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*IbGSWVmG1WF9Gywi6JQnhw.png" /></figure><p>By default, Travis CI does not know what version of Node we are supposed to be running, however, and uses the default version of 0.10.48 which is probably about a decade old by now. We will need to make a small update to our config file to tell it what we want instead. At the time of writing this article Node 11 was the latest version, and was chosen.</p><p>Let’s go ahead and tell Travis to run the build in Node 11, as well as the latest version 12.</p><pre>language: node_js<br>node_js:<br>  - 11<br>  - 12</pre><p>Travis knows to run tests as part of its default Node.js script, and for many libraries or applications, this will be enough. This particular application, however, runs some tests against the built version that is outputted by Parcel, and for that reason, we will need to customize the script to make sure our Parcel build runs before the tests.</p><p>Let’s update our .travis.yml file again to account for this build step before the script runs.</p><pre>language: node_js<br>node_js:<br>  - 11<br>  - 12</pre><pre>before_script:<br>  - npm run build</pre><p>And with that, we have passing builds!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hqpjNnVI1FTNneiMuY7CMQ.png" /></figure><h3>Step 4: Enable Code Coverage Reporting</h3><p>Alright, we have passing builds, and our tests are outputting code coverage, but it’s a bit hard to get at. We want to ensure that we are not losing coverage with a new PR, so it’d be really helpful if we had that information as part of the Pull Request. For this, we can use a tool called <a href="https://github.com/marketplace/codecov">CodeCov</a> from the Github Marketplace.</p><p>Just as before, select the “Open Source” plan and press Install.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eRTrdDI0IO-HftqZ_mHWSA.png" /></figure><p>Again, I’ve chosen to only give permission for specific repositories, as I have a lot.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mZfGsudgB5wNhYyAyRtR-A.png" /></figure><p>In order to finish setting up CodeCov we can add a couple of steps to our .travis.yml file. We can do this in theafter_successlifecycle step.</p><p>Here’s our new .travis.yml file:</p><pre>language: node_js<br>node_js:<br>  - 11<br>  - 12</pre><pre>before_script:<br>  - npm run build</pre><pre>after_success:<br>  - npm i -g codecov<br>  - codecov</pre><p>Now with each PR, a bot will comment with changes in coverage. Here’s <a href="https://github.com/patrickleet/streaming-ssr-react-styled-components/pull/13">the PR where CodeCov was enabled</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fyC86sKiPDBqTFG2RLiEBQ.png" /></figure><p>The question marks are simply because we didn’t have coverage data in the master branch yet. Once this PR is merged, future report will include the difference between the PR and the master branch.</p><h3>Step 5: Pin Dependencies</h3><p>While we were setting up Travis and CodeCov, Renovate made some Pull Requests. The first of which was setting all of the dependencies to a fixed version. This eliminates the reliance on SemVer, or semantic versioning. SemVer allows a range of versions and can be useful to get minor and patch updates each time you runnpm install. Seems how we have a bot monitoring our packages for us, we will rely on it to do this work instead of SemVer. This results in more reproducible builds and less possible variance between each developer’s machine. A more detailed discussion of the reasoning behind this can be found <a href="https://docs.renovatebot.com/dependency-pinning/">in Renovate’s blog</a>.</p><p>We’d like to see how each of the PRs affect test coverage and ensure they are still passing, though. Renovate is pretty smart, and can easily rebase the PR’s it has made. Given enough time it will usually do so automatically, but we can help it along by clicking the checkbox in the PR’s description that says “If you want to rebase/retry this PR, check this box”.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pIus4hujK2A6oWLTnAlLdQ.png" /></figure><p>Within a few moments <a href="https://github.com/patrickleet/streaming-ssr-react-styled-components/pull/8">a rebase has been performed</a> at the Travis build kicks off, and a couple of minutes later we have passing builds and tests, as well as a code coverage report showing no change in coverage — still at 100%.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HksCYEgTKYS3Qf0xigSQYw.png" /></figure><p>Pinning dependencies is a rather safe operation, as all that is happening is the explicit versions that are already installed in the package-lock.json file are being reflected in package.json. We can also see all of the tests are still passing, and code coverage has not been affected.</p><h3>Step 6: Testing our Dockerfiles</h3><p>If you’re paying close attention, you’ve noticed that we are testing our Node application, but not our Dockerfiles. In the previous article we created a process to run and test our Dockerfiles but we are not taking advantage of that process in Travis. The problem is that Renovate will make PRs to keep our Dockerfile’s up to date, and if we don’t automatically run them we may miss something that works on Travis but not in the container.</p><p>Let’s fix that. To do so we can add a new section to our .travis.yml file called jobs that define the docker build jobs.</p><pre>language: node_js<br>node_js:<br>  - 11<br>  - 12</pre><pre>before_script:<br>  - npm run build</pre><pre>after_success:<br>  - npm i -g codecov<br>  - codecov</pre><pre>jobs:<br>  include:<br>    - services:<br>        - docker<br>      script:<br>        - docker build . -t ssr<br>    - services:<br>        - docker<br>      script:<br>        - docker build . -f nginx/Dockerfile -t nginx</pre><p>We have two Dockerfiles, because this project specifically demonstrates two different alternatives — one with SSR, and one with Nginx. Therefore, we have two new jobs defined.</p><p>In the Travis UI we can see that we now have four jobs — the original Node 11 and Node 12 builds, as well as two more that run thedocker buildcommands.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*25yfjh_G5A7XGruKOAtN8w.png" /></figure><p>Clicking inside of “ <a href="https://travis-ci.com/patrickleet/streaming-ssr-react-styled-components/jobs/278519691">25.3</a>” and “ <a href="https://travis-ci.com/patrickleet/streaming-ssr-react-styled-components/jobs/278519692">25.4</a>” we can see the output for each of the docker build commands indicating their success.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/834/1*Yf4-Z13tBnDc5ljx75No8w.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wFafgUl3kBJoxQ0MGCEuHw.png" /></figure><h3>Step 7: Package updates</h3><p>With the pinned dependencies out of the way, we will start receiving Pull Requests for our other packages that need updating. The updates will either be a minor version update, which are generally pretty safe so long as the package author is using semver appropriately, or major version updates, which should receive a bit more scrutiny. Make sure if you had any open before the Travis and CodeCov changes you trigger a rebase of those branches. We’ll also get updates to our Dockerfiles, and thanks to the last step, we know those are being built as well.</p><p>Renovate will only make two Pull Requests per hour, and not open more than twenty total pull requests at a time.</p><p>Let’s take a look at the PRs I’ve received so far, and talk about how to handle each of them.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*841nEoAIA60K67Ud5KISaQ.png" /></figure><p>There are five of them so far, and a range of different types of examples. <a href="https://github.com/patrickleet/streaming-ssr-react-styled-components/pull/9">#9</a> and <a href="https://github.com/patrickleet/streaming-ssr-react-styled-components/pull/11">#11</a> are both update to Dockerfiles, and they are both passing, including actually running the builds so we know that the updates still result in a working Dockerfile. It’d be nice to run them as well and have a preview environment created automatically for each environment, but we’ll need more tools and a more complicated set up to get that. It’s totally possible, and something I usually set up for my clients, but this article is getting long already.</p><p><a href="https://github.com/patrickleet/streaming-ssr-react-styled-components/pull/12">#12</a> is a major version update. In version 1 of react-helmet-async there is a breaking change, and our 100% code coverage correctly detected that something went wrong. If automerge were enabled, this would not have made it through, exactly as planned.</p><p><a href="https://github.com/patrickleet/streaming-ssr-react-styled-components/pull/14">#14</a> is a major version update as well, but our build and tests are working, so we can be pretty confident we are all good here. Again, a preview environment that actually runs this build in the cloud for us would get us to that next level of confidence. We can click the down arrow next to “Release Notes” in the PRs description to take a deeper look.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/584/1*YTg2dx8nBP-MBvmjn7GHbw.png" /></figure><p>If an author is diligent they should detail what breaking change warranted the major version bump. Unfortunately in this case, the author did not provide this information. Not knowing what the reason was increases the risk, and decreases developer confidence. If you’re a library author, I strongly suggest using tools like commitizen and semantic-release to make sure users of your package are kept informed. I wrote up a process of how to do this in a previous article: <a href="https://hackernoon.com/these-6-essential-tools-will-maintain-your-npm-modules-for-you-4cbbee88e0cb">These 6 essential tools will release, version, and maintain your NPM modules for you</a>.</p><p>Lastly, <a href="https://github.com/patrickleet/streaming-ssr-react-styled-components/pull/16">#16</a> is a minor version update, but demonstrates Renovate’s ability to group related packages. Some libraries choose to version all components of the library together. Renovate contains some preset, popular libraries in this manner, such as babel as we are seeing here.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xKBUknsT0BrR8RqoetLFaA.png" /></figure><p>I like living on the wild side, and hate clicking buttons that a bot could click for me, so instead of going through and merging all of these, I’m just gonna go ahead and enable automerge for Renovate. I’m pretty confident that the 100% coverage of the tests do a good job in catching breaking changes as they have demonstrated with PR #12.</p><p>To enable automerge, update yourrenovate.jsonfile like so:</p><pre>{<br>  &quot;extends&quot;: [<br>    &quot;config:base&quot;<br>  ],<br>  &quot;automerge&quot;: true<br>}</pre><p>Here’s <a href="https://github.com/patrickleet/streaming-ssr-react-styled-components/pull/17">the PR that enables automerge</a>.</p><p>However, without preview environments, there’s too much that could potentially go wrong with major versions. Let’s make one more update so major versions will require a manual review and merge:</p><pre>{<br>  &quot;extends&quot;: [<br>    &quot;config:base&quot;<br>  ],<br>  &quot;automerge&quot;: true,<br>  &quot;major&quot;: {<br>    &quot;automerge&quot;: false<br>  }<br>}</pre><p>Automerge will take some time to kick in, so I’ll let it do it’s thing for awhile.</p><p>While my bots get to work, let’s take a look at the failing PR and see if we can find out what went wrong there.</p><h3>Step 8: Investigating failing builds</h3><p>The failing build is caused by updating the library “react-helmet-async” to from version 0.2.0 to 1.0.0. Unfortunately, when we expand the release notes, the library author did not give us any information about what the breaking change may have been.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xu0srfP76EX6vWphYBxbPg.png" /></figure><p>Renovate does allow us to click through via the “Compare Source” link to see what commits and code changes have occurred though, and of course, looking at the failing tests may give show us what went wrong.</p><p>From clicking <a href="https://github.com/staylor/react-helmet-async/compare/0.2.0...9a43be9a8e8d4037fba8b8806eb836cc6d745426">Compare Source for V1.0.0</a> we can see that the breaking change is moving the default export to a named export.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*G5X8N40_EjNFBuqeHkXm5Q.png" /></figure><p>The error message in the tests was pretty unclear, so thankfully looking at the “Compare Source” link helped us find the change pretty quickly.</p><p>To fix it, I’m going to go ahead and check out the branch that Renovate made and simply add a new commit to it which changes the import to use the new named import. Here’s <a href="https://github.com/patrickleet/streaming-ssr-react-styled-components/pull/12/commits/227de41228d0e9086f66d6cd47ad08c4989deba2">the commit containing the fix</a>.</p><p>And with that, our tests are now passing again.</p><h3>Step 9: Testing major version changes</h3><p><a href="https://github.com/patrickleet/streaming-ssr-react-styled-components/pull/14">PR #14</a> is a great example of why we need to test major version changes. If we had a preview environment we’d find that although the tests are all passing, there are run time errors related toreact-imported-componentAPI changes. Seems how we do not, I had to discover this by checking out the branch that renovate created and running it manually.</p><p>The library has changed in order to be inline with React Suspense and React.lazy. Looking through the docs shows us how to refactor to use the new API. If you’re interested, <a href="https://github.com/patrickleet/streaming-ssr-react-styled-components/pull/14/commits/6a6d0cbeb458004a5b0e5628ba3dabe91cdf5948">the diff can be seen here</a>.</p><h3>Conclusion</h3><p>Developer time is expensive, and maintenance is important as package updates often contain security fixes. Staying up to date will make sure any known bugs in the packages you are using get fixed as the author makes updates. Due to the time required to keep everything up to date, these small updates often fall by the wayside and over the months and years your projects can get painfully out of date. By employing a set of bots to do the heavy lifting for you, it’s much easier to keep everything up to date.</p><p>That’s all for now! Happy coding!</p><p>Check out the other articles in this series! This was part 6.</p><ul><li><a href="https://hackernoon.com/move-over-next-js-and-webpack-ba367f07545"><strong>Part 1: Move over Next.js and Webpack</strong></a></li><li><a href="https://hackernoon.com/a-better-way-to-develop-node-js-with-docker-cd29d3a0093"><strong>Part 2: A Better Way to Develop Node.js with Docker</strong></a></li><li><a href="https://hackernoon.com/enforcing-code-quality-for-node-js-c3b837d7ae17"><strong>Part 3: Enforcing Code Quality for Node.js</strong></a></li><li><a href="https://hackernoon.com/the-100-code-coverage-myth-900b83d20d3d"><strong>Part 4: The 100% Code Coverage Myth</strong></a></li><li><a href="https://hackernoon.com/a-tale-of-two-docker-multi-stage-build-layers-85348a409c84"><strong>Part 5: A Tale of Two (Docker Multi-Stage Build) Layers</strong></a></li></ul><p><em>Interested in going deeper, and getting things like Preview Environments on every Pull Request? In my new masterclass, Cloud Native Entrepreneur, you’ll do just that by engineering end-to-end marketing systems with microservice backends and running them in production with Kubernetes! All while learning marketing and entrepreneurship along the way! Check </em><a href="https://hackernoon.com/@patrickleet"><em>my Hackernoon profile</em></a><em> to find how to register for a free info session!</em></p><p>Best,<br>Patrick Lee Scott</p><p><em>Originally published at </em><a href="https://hackernoon.com/bring-in-the-bots-and-let-them-maintain-our-code-gh3s33n9"><em>https://hackernoon.com</em></a><em> on January 23, 2020.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7dc1261da9a7" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A note to my Medium subscribers]]></title>
            <link>https://patrickleet.medium.com/a-note-to-my-medium-subscribers-8dfbf6ec1e2f?source=rss-8f48bb19ea99------2</link>
            <guid isPermaLink="false">https://medium.com/p/8dfbf6ec1e2f</guid>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[entrepreneurship]]></category>
            <category><![CDATA[marketing]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Patrick Lee Scott]]></dc:creator>
            <pubDate>Fri, 29 Nov 2019 15:58:14 GMT</pubDate>
            <atom:updated>2019-11-29T15:58:14.079Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*iSHvcpMQkDD_l3QOzsE8PA.jpeg" /></figure><p>Hey everyone!</p><p>Thanks for all the support I’ve gotten on Medium over the years!</p><p>1600 followers, 100’s of thousands of views, literally over a year of time spent reading my articles about DevOps, Microservices, Full Stack Engineering, and Entrepreneurship.</p><p>And this year, voted HackerNoon’s Contributing Writer of the Year!?</p><p>Amazing.</p><p>Thank you.</p><p>I’ve really enjoyed making content, and helping engineers like you succeed.</p><p>Now, I’m taking that to the next level.</p><p>I want you to not only be a Cloud Native Engineer, but I also want to help you become a Cloud Native Entrepreneur.</p><p>Which is why I’m excited to announce I’m launching my first masterclass, Cloud Native Entrepreneur, where you will learn marketing and business systems through engineering them!</p><p>But first, you know I’m one for telling a good story.</p><p><a href="https://cloudnativeentrepreneur.com/black-friday-beta">Check out my story of becoming a Cloud Native Entrepreneur here.</a></p><p>Best,<br>Patrick Lee Scott</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8dfbf6ec1e2f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Have You Ever Been Told Your Business Idea Sucks?]]></title>
            <link>https://patrickleet.medium.com/have-you-ever-been-told-your-business-idea-sucks-3fb3dd02195a?source=rss-8f48bb19ea99------2</link>
            <guid isPermaLink="false">https://medium.com/p/3fb3dd02195a</guid>
            <category><![CDATA[startup]]></category>
            <category><![CDATA[entrepreneur]]></category>
            <category><![CDATA[startup-lessons]]></category>
            <category><![CDATA[entrepreneurship]]></category>
            <category><![CDATA[business]]></category>
            <dc:creator><![CDATA[Patrick Lee Scott]]></dc:creator>
            <pubDate>Thu, 25 Jul 2019 00:00:00 GMT</pubDate>
            <atom:updated>2019-07-31T18:19:46.868Z</atom:updated>
            <content:encoded><![CDATA[<h3><strong>How to know which feedback to listen to for your startup.</strong></h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1020/0*RnXna6BMfZHe8vl3.png" /></figure><p>If you’re starting a new business, launching a new idea, or doing anything at all really, you’re going to be getting a lot of feedback. People are going to tell you if they love it. And you’re inevitably going to hear some verison of:</p><p>“That’s not a good idea.”</p><p>The fact is, you’re probably going to hear that one a lot, and from a lot of different people. Some of them perfectly well-meaning.</p><p>So, how do we determine which feedback matters the most? When we’re overwhelmed with messages about our ideas being too risky or not good enough, how do we filter out the noise and get to the feedback that really matters?</p><p><strong>How do we know whether to listen to the people saying yes, or the people saying no?</strong></p><p>I’ve dealt with this problem a lot. Over the course of my entrepreneurial journey, I’ve had more than a few ideas.</p><p>Some of them were winners.</p><p>Many, not so much.</p><p>Some I never really got the chance to find out if they would be successful until it was too late.</p><p>Some I left behind because I listened to the <em>wrong</em> feedback.</p><p>This is the story of an idea that I wish I’d done more with, and how it taught me which feedback to listen to, and which to ignore.</p><p>Looking back at this, I realize what I should have done differently (and that I potentially could have made a lot of money!) had I only known then what I’m telling you now.</p><p>One weekend, my buddy Krish managed to get us a spot in a lean startup machine workshop. Normally, this would have cost us a few thousand dollars. Somehow though, he managed to get us in for free. For three days, we’d be attending this workshop and working on our own ideas for a startup.</p><p>I was pretty excited for this opportunity. I’d recently read The Lean Startup, and was interested in getting more hands on with it.</p><p>Inside the workshop, we divided up into groups of three to five people. Each group was tasked with coming up with a potential startup idea. Once we had one, it was time to hit the streets.</p><p>Having the idea wasn’t enough. We needed proof that our idea had value, and we needed to get that by getting people to sign onto it in one form or another.</p><p>We needed to hear from our potential customers.</p><p>Now, a lot of the people in my group lived in the upper east side. This workshop? It was in downtown Manhattan.</p><p>This was before the days of Uber. We had two options for getting there. Either we cough up the money for a <em>very </em>expensive cab ride, or we use the subway for a much cheaper, but also much slower, commute.</p><p>This was the problem that we tackled with our startup idea.</p><p>We called our solution “CabPool.” A <em>very</em> clever twist on the phrase “carpool.” People would use an app to find others going to the same place as them and share a cab, splitting the cost.</p><p>If you’re drawing parallels between what we were thinking about and rideshare apps? Trust me, I’ve been doing that for a while, too.</p><p>When it came time to test our idea, we planted ourselves in front of a Whole Foods market. The plan was simple. We’d wait to see someone hailing a cab and ask them if they’d be interested in a program like ours. We even had a little application.</p><p>When someone said they were interested, we’d ask them where they were going so we could find a match for them.</p><p>For the purposes of this test, I was the person waiting for the ride, and trust me, I just so happened to be going the same direction. They’d be matched up with me, I’d hop in the cab, and off we’d go. We made small talk along the way, and then, at the end of the ride, we split the cost.</p><p>The people I talked to seemed to genuinely enjoy this idea. Not surprising, right? Who doesn’t want cheaper transportation?</p><p>Whether that was the idea itself or just my winning personality, you be the judge.</p><p>As we did this, we got people to give us money. So, when it came time for the judges to determine which group had the best idea? We were pretty sure that we were going to win.</p><p><strong>We did not, in fact, win.</strong></p><p>The judges told us that, despite the fact that we’d made some money, our plan wasn’t a good idea. It simply wasn’t going to work.</p><p>The winner of the competition had proposed an app that would let people know when a parking spot opened up and where it was. I’m sure it’s a complete surprise that a lot of people in NYC were thinking about traffic, commuting, and parking.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*tEKmQUyh5nhgb0e9" /></figure><p>Their group hadn’t gotten anyone to give them money, like ours had. Instead, they had gotten people to sign a letter of intent to use the app. The judges decided this was the better approach.</p><p>Now, I don’t want to harp on their idea, but I do feel like it is worth pointing out that a parking spot in NYC is probably only open for about 60 seconds on a good day — according to the team whom presented their findings… By the time you check an app, find an open spot, and drive to it? That spot isn’t empty anymore.</p><p>That’s probably the reason we’re not seeing this kind of app around much.</p><p>Nevertheless, we took the advice of the judges.</p><p>After all, they were experts on these things, right?</p><p>This had only been a weekend idea for us, not a serious venture. And the judges had told us it hadn’t been good enough. Why continue on with it?</p><p>Obviously, given the success of rideshare apps, we probably should have ignored them.</p><p>The judges, the experts on the issue? They were the feedback we chose to listen to. They weren’t the feedback we <em>should </em>have been listening to, though.</p><p>The customers that we had over the course of the trial had been giving us feedback too. They’d been doing it with their wallets, and they’d been doing it in a positive way.</p><p><em>That</em> was the feedback we should have been listening to.</p><p>Our customers voted with their wallets, and we decided to listen to the people who had no financial interest in the matter.</p><p>When you launch a new idea, that idea is always going to have some detractors. Those detractors might even be the experts. They might tell you that your idea isn’t a good one. That there isn’t a market for what you’re thinking of doing.</p><p>That data? It really isn’t all that useful.</p><p>The useful data comes from your customers, and how they choose to vote with their wallets. We had people voting yes, putting their dollars down on our idea. We didn’t listen to them. We listened to the experts instead.</p><p><strong>That was our mistake. Don’t let it be yours.</strong></p><p>When you’re starting something new, the main feedback you need to listen to is the one that comes from the customers. Are they willing to spend money on what you’re proposing? That answer matters more than any other, and is going to really tell you if your idea can work or not.</p><p>When people vote with their wallets, listen to them.</p><p><em>Do you have an idea or product that you want to turn into a full-fledged business? </em><a href="https://patscott.io/home"><em>Click here</em></a><em> to learn more about me and my agency, Unbounded, and how I can put my experience to work for you.</em></p><p><em>Originally published at </em><a href="https://hackernoon.com/have-you-ever-been-told-thats-not-a-good-idea-fpdb38o8"><em>https://hackernoon.com</em></a><em> on July 25, 2019.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3fb3dd02195a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What’s Holding You Back From Success?]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/swlh/whats-holding-you-back-from-success-35d49a78c54d?source=rss-8f48bb19ea99------2"><img src="https://cdn-images-1.medium.com/max/2600/1*qutC_Z5ceW-ZBDlD6KuKYg.jpeg" width="3917"></a></p><p class="medium-feed-snippet">Is success just a small decision away?</p><p class="medium-feed-link"><a href="https://medium.com/swlh/whats-holding-you-back-from-success-35d49a78c54d?source=rss-8f48bb19ea99------2">Continue reading on The Startup »</a></p></div>]]></description>
            <link>https://medium.com/swlh/whats-holding-you-back-from-success-35d49a78c54d?source=rss-8f48bb19ea99------2</link>
            <guid isPermaLink="false">https://medium.com/p/35d49a78c54d</guid>
            <category><![CDATA[entrepreneurship]]></category>
            <category><![CDATA[live-streaming]]></category>
            <category><![CDATA[success]]></category>
            <category><![CDATA[startup]]></category>
            <category><![CDATA[motivation]]></category>
            <dc:creator><![CDATA[Patrick Lee Scott]]></dc:creator>
            <pubDate>Mon, 01 Jul 2019 18:01:03 GMT</pubDate>
            <atom:updated>2019-07-01T18:01:03.007Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[One Fundamental Business Decision That Many Founders Forget]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/swlh/one-fundamental-business-decision-that-many-founders-forget-e7030bf7d966?source=rss-8f48bb19ea99------2"><img src="https://cdn-images-1.medium.com/max/2600/1*50XnawcyP0YfewXuwxzhyg.jpeg" width="5092"></a></p><p class="medium-feed-snippet">Learn the fundamental question every business owner needs to answer before they can succeed</p><p class="medium-feed-link"><a href="https://medium.com/swlh/one-fundamental-business-decision-that-many-founders-forget-e7030bf7d966?source=rss-8f48bb19ea99------2">Continue reading on The Startup »</a></p></div>]]></description>
            <link>https://medium.com/swlh/one-fundamental-business-decision-that-many-founders-forget-e7030bf7d966?source=rss-8f48bb19ea99------2</link>
            <guid isPermaLink="false">https://medium.com/p/e7030bf7d966</guid>
            <category><![CDATA[founders]]></category>
            <category><![CDATA[startup]]></category>
            <category><![CDATA[marketing]]></category>
            <category><![CDATA[founder-advice]]></category>
            <category><![CDATA[entrepreneurship]]></category>
            <dc:creator><![CDATA[Patrick Lee Scott]]></dc:creator>
            <pubDate>Mon, 01 Jul 2019 17:01:02 GMT</pubDate>
            <atom:updated>2019-07-01T17:01:02.106Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[What Does Peeling An Egg Have To Do With DevOps?]]></title>
            <link>https://medium.com/hackernoon/what-does-peeling-an-egg-have-to-do-with-devops-e55108a4e83c?source=rss-8f48bb19ea99------2</link>
            <guid isPermaLink="false">https://medium.com/p/e55108a4e83c</guid>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[hackernoon-top-story]]></category>
            <category><![CDATA[devops]]></category>
            <dc:creator><![CDATA[Patrick Lee Scott]]></dc:creator>
            <pubDate>Mon, 17 Jun 2019 06:51:01 GMT</pubDate>
            <atom:updated>2019-06-17T06:51:01.216Z</atom:updated>
            <content:encoded><![CDATA[<h4>Learn These Guiding Principles To Help You Level-Up Your DevOps Chops</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3ite-HqIIYApCh_99APINA.jpeg" /><figcaption>Licensed from Adobe Stock Photos, By <a href="https://stock.adobe.com/contributor/293313/africa-studio?load_type=author&amp;prev_url=detail">Africa Studio</a></figcaption></figure><p>A few days ago I was peeling an egg, like an idiot. My girlfriend, Angelly, was like — “why are you peeling the egg like that?”</p><p>She picked up another hard-boiled egg, smacked it on the cutting board, and then rolled it while pressing down firmly. The egg’s shell was off in about 3 seconds total.</p><p>Here I was chipping away tiny piece by tiny piece.</p><p>My point is: <strong>Working hard is overrated.</strong></p><p>The best solutions are the simple, effective solutions.</p><p>Is it hard to take rust off of a screw? Probably, right? Unless you know that coca-cola removes rust from screws.</p><p>Now, is it hard? No. All you need to do is drop it in a cup and wait.</p><p>If you do not know the simple techniques, you cannot use them. You get experimentation instead of implementation. Exploration instead of replication.</p><p>When a technique in software engineering is used over and over again because it makes the problem you are solving simpler, it becomes a pattern, or “best practice”.</p><p>Sometimes, they have complex, scary-sounding names — like Command Query Responsibility Segregation (CQRS), or Event Sourcing (ES) — but these patterns exist to solve problems. Specifically, problems found in building sane, distributed systems.</p><p>When we look at the coding context as a whole there are more universal patterns: “Keep it Simple, Stupid” (KISS), and “Don’t Repeat Yourself” (DRY), for example.</p><p>I want to talk about these same sorts of patterns and principles as applied to DevOps. DevOps is often represented as the promised land where the birds are singing and the sun is shining. Without the right techniques, though, it will be a pointed spiky hell, like my tiny eggshell shards.</p><p>On top of the universal patterns like KISS and DRY, there are other “principles” that I’ve found building DevOps Systems that aren’t quite patterns, yet. These principles can help you move from picking away eggshells piece by piece to having them off in no time flat. They are, in no specific order:</p><ol><li>Don’t repeat others.</li><li>Empower developers to be productive as possible.</li><li>There is no production.</li><li>Put everything in the cluster and backup the whole thing.</li><li>VPNs are not simple, and there are simple solutions to solve the same things.</li><li>Codify and Automate Everything.</li></ol><h3>Lesson 1 — Don’t repeat others</h3><p>If you can buy it off the shelf or there is an easy to use open source version of a tool you need, use it.</p><p>Don’t reinvent the wheel. Buy the wheel.</p><p>Did you know you can use the same mail server that craigslist uses? Did you know you can use it for free? Need a mail server? Use that! Don’t build it, build around that!</p><p>I love awesome-self-hosted for finding things, as well as searching for it for use with Helm on their hub.</p><blockquote>Although Helm is a pretty new tool for finding and sharing software, a v3 website is now up: <a href="https://v3.helm.sh/">https://v3.helm.sh/</a></blockquote><p>Helm is really great for getting wheels.</p><p>Let me take you back to when I started programming. The first “real” language I learned was QBasic in 9th grade. I’d been making websites with HTML and CSS for a few years already. Back then, the internet was new. While people could create packages, they weren’t really sharing them around like we are nowadays. My standard mechanism for storage was a floppy disk — which I wish I could find so I could extract my 11th-grade brick break game I made in Java Swing…</p><p>When I programmed, I used standard libraries, baby. That was all there really was. That I knew of at least. I was like 13. I’m sure some of you more experienced Java Pros (Hi Vlad and Nick!) were killin’ it. ;)</p><p>Not today. Hell, today, you can get entire functioning UI components all bundled up nicely and ready to go. You can get a library for dealing elegantly and easily with dates with a single command.</p><p>Wouldn’t it be great if there were something that allowed you to use and install entire functioning subsystems? Need a database? install database</p><p>Good news! There is! You can now <em>also </em>get entire functioning subsystems all bundled up and ready to go, using Helm.</p><p>It’s the same concept we’ve embraced via NPM and Gradle, but the packages it manages are known as Charts.</p><p>A chart maps containers to run in different ways inside of Kubernetes.</p><p>Using a chart is kinda like buying a wheel. Except the wheels are containers mapped to run inside of Kubernetes.</p><p>The cool part about Charts is that you can easily package up your own services to run easily in any Kubernetes cluster, and even share it with others if you’d like.</p><p>This means that you could describe entire environments in a codified manner:</p><pre>dependencies:<br>- name: backup<br>  repository: <a href="http://jenkins-x-chartmuseum:8080">http://jenkins-x-chartmuseum:8080</a><br>  version: 0.0.2<br>- name: monitor<br>  repository: <a href="http://jenkins-x-chartmuseum:8080">http://jenkins-x-chartmuseum:8080</a><br>  version: 0.0.3<br>- name: marketing-site<br>  repository: <a href="http://jenkins-x-chartmuseum:8080">http://jenkins-x-chartmuseum:8080</a><br>  version: 1.1.10<br>- name: denormalizer-service<br>  repository: <a href="http://jenkins-x-chartmuseum:8080">http://jenkins-x-chartmuseum:8080</a><br>  version: 1.0.0<br>- name: mongo<br>  repository: <a href="https://kubernetes-charts.storage.googleapis.com/">https://kubernetes-charts.storage.googleapis.com/</a><br>  version: 1.0.0</pre><p>Need to update the marketing site to 1.2.0? Just change it, and commit. Which brings us to Lesson Two.</p><h3>Lesson 2 — Empower developers to be as productive as possible</h3><p>So there I was, sitting at my desk, face in my code, tracking down a bug. Users had been complaining about this for weeks and I found some free time and decided to be a hero.</p><p>Badda bing, badda boom! Found! Fixed!</p><p>I shouted over my half-height cubicle in the sun-lit room — “I fixed the bug!”</p><p>Next Tuesday, when the release went out, users were gonna love it! That is, after all of us got together in a room and pushed the boulder of the release up and over the top of another small mountain… err, if we were able to push it over, that was… if nothing goes wrong along the way…</p><p>Okay, next Tuesday, <em>if</em> the release went out — users were gonna love it!</p><p>This was my experience deploying when I was a junior software engineer at my first programming job out of college.</p><p>Things have come a long way since then.</p><p>Now, I practice trunk based development and deploy many times a day. When I make pull requests, a little bot posts a comment with an ephemeral pull request preview environment after a successful build and passing tests.</p><p>In today’s environment, you don’t need to shout over the half-height cubicle.</p><p>The more you can empower engineers to be able to control the parts of the infrastructure that they need, the simpler your job becomes as a DevOps engineer becomes.</p><p>In lesson one, we saw it was possible to update a production environment by literally changing a number and committing.</p><p>In the ideal world, where we are also packaging all of our applications into Charts, we are also empowering each engineer on the team to modify how that service runs in production. A chart is literally a mapping of containers to Kubernetes, and for that purpose, it exposes things like resource requirements.</p><p>Seems how I’m not great at guessing how much memory or CPU settings a new service will need — and I’m guessing you aren’t either — I also like to deploy monitoring and alerting with some rules that will inform me and my team via Slack when those settings need adjusting. This means it will guide you to correct settings once you deploy it. I used to spend hours at a time running Prometheus queries and adjusting, just like I used to spend too much time peeling my eggs. Now, I’ve learned to do it the smart way.</p><p>I can already hear you saying: “That sounds complicated.” Nope. Just install the chart.</p><p>Anything that can be automated or simplified should be. For example, what if simply deploying a service with a label that said “expose: true” would map it to a DNS path? This is where operators come in. This is a more advanced Kubernetes helper tool, and something worth understanding, but let’s not get lost in those details right now.</p><p>And that brings us to Lesson Three.</p><h3>Lesson 3 — There is no production</h3><p>This was one of those epiphany moments for me. It took me looking at things from a new perspective, so try to follow me for a minute.</p><p>For over a decade, I’d thought there were only a handful of environments. At it’s simplest, there was a Staging Environment and a Production environment. First, you deployed to staging, tested things, and then deployed the next thing, tested it, etc. Once everything was deployed together — “integrated” — then we could continue the process and repeat the process to production.</p><p>This is how I did things for years and thought nothing of it. It’s the way it was.</p><p>This is another one of those peeling apart eggshells piece by piece type of moments.</p><p>A chart is a dependency graph. Use the chart to represent the environment, and then the process of deploying to production is just deploying a single chart!</p><p>If each team or project or bounded context had its own chart, you could have many production environments that allowed you to group and update sets of services together in a single transaction.</p><p>So instead of thinking of production as this big, all-encompassing environment, realize there are actually many little productions.</p><p>Big changes are scary and dangerous. By keeping many small production environments you isolate changes and make the rest of the system more resilient.</p><p>Also, because everything is just in Charts all the way down, every setting is exposed and tweakable in a uniform manner. Meaning, things like running a less powerful Kafka cluster in staging where it’s not needed is just a simple config change.</p><h3>Lesson 4 — Put everything in the cluster and back up the whole thing</h3><p>Ok, so we can run things easily in production, but what about databases? What about Kafka? Is that safe?</p><p>Well, if you’ve been paying attention, you’ll remember I mentioned databases as a thing that can be packaged into a chart.</p><p>In Kubernetes, there is now an API specifically for running stateful applications like databases in highly available setups, called StatefulSets.</p><p>Pretty much the entire point of Kubernetes is to run containers reliably.</p><p>Paired with a tool called <em>Velero</em> that backs up an entire Kubernetes cluster, installed via Helm Chart, you can now backup the entire state of a Kubernetes cluster, as well as all of the attached volumes, like the ones created by the StatefulSet, and restore everything with a single command. Backups are also easy to configure on a schedule.</p><p>With backups and restores just a single command away, coupled with managed Kubernetes, spinning up an entirely new cluster and restoring it to your backed up version can now be done in just two commands. Three if you want a brand new backup first.</p><p>Instead of thinking of just servers as cattle, entire clusters start to become disposable.</p><p>Is staging being annoying? Throw it away. Just a simple backup, restore, and DNS change away.</p><h3>Lesson 5 — VPNs are not simple, and there are simple solutions to solve the same things</h3><p>Have you ever <em>enjoyed</em> using a VPN?</p><p>I mean, honestly.</p><p>Has <em>anyone</em>?</p><p>Google tried. Then, a couple of years back, announced that their company would no longer use them. I guess they didn’t enjoy using them, either.</p><p>Instead, they rely on trustless networks.</p><p>Instead of a master key that lets you access all the networks, a key is placed in front of each service via a Single Sign On screen. Need to access the monitoring service? Just log in with your authorized company credentials.</p><p>A small shift is all that’s required. Instead of using a VPN for authentication, use a proxy.</p><p>A VPN also places the Kubernetes management URL on a private network where an SSO gateway does not. However, they also provide an alternative authentication mechanism. For Google Cloud and AWS that is IAM authentication, and you can also whitelist IP addresses.</p><p>If you can maintain a lot less infrastructure with the same benefits, do it. Be like Google: ditch VPNs for trustless networks.</p><h3>Lesson 6 — Codify and Automate Everything</h3><p>Something takes a bit more effort to codify? I don’t care! It will save hours, days, or even weeks of effort later on. Codify it.</p><p>It’s the only reliably repeatable way to recreate infrastructure.</p><p>If every setting can be modified by changing and committing something to git, then your technology organization as a whole is essentially declarative. Any dev with git access can easily maintain any system.</p><p>To do so, use lots of little repositories, then tie them together later. Monorepos lead to people taking shortcuts and a dependency on an artificially important structure. Instead, use lots of little repositories, and tie them together later.</p><p>My friend Matt and I make a tool for doing this in development, it’s called <a href="https://github.com/mateodelnorte/meta">meta</a>. Helm is a tool for doing this in production: everything is just a dependency graph!</p><h3>Conclusion</h3><p>Don’t peel an egg piece by piece. Smack and roll.</p><blockquote>Interested in having me automate your DevOps pains away? I’ve got the smack and roll on lock.</blockquote><blockquote>Check out my website to <a href="https://patscott.io/home">learn more about me</a>, and my agency, <a href="https://www.unbounded.tech/">Unbounded</a> to schedule a free 15 minute Discovery call. I’m also <a href="https://www.advisorycloud.com/profile/Patrick-Scott">available as an advisor on AdvisoryCloud</a>.</blockquote><blockquote>Just Getting Started with DevOps? Check out <a href="https://hackernoon.com/my-journey-to-achieving-devops-bliss-without-useless-aws-certifications-a7cbf7c539d1">My Journey to Acheiving DevOps Bliss, Without Useless AWS Certifications</a> on HackerNoon and find a free 21 day email course at the bottom!</blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e55108a4e83c" width="1" height="1" alt=""><hr><p><a href="https://medium.com/hackernoon/what-does-peeling-an-egg-have-to-do-with-devops-e55108a4e83c">What Does Peeling An Egg Have To Do With DevOps?</a> was originally published in <a href="https://medium.com/hackernoon">HackerNoon.com</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>