<?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:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[The Disco Blog]]></title><description><![CDATA[It's the Disco Blog, baby]]></description><link>https://www.thediscoblog.com/</link><image><url>https://www.thediscoblog.com/favicon.png</url><title>The Disco Blog</title><link>https://www.thediscoblog.com/</link></image><generator>Ghost 6.44</generator><lastBuildDate>Mon, 08 Jun 2026 10:54:27 GMT</lastBuildDate><atom:link href="https://www.thediscoblog.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Solve the hard problem first]]></title><description><![CDATA[Successful leaders attack the hard problems first, because those are the problems when solved, unlock the entire equation. ]]></description><link>https://www.thediscoblog.com/solve-the-hard-problem-first/</link><guid isPermaLink="false">661c3e13621cef0001858cf4</guid><category><![CDATA[leadership]]></category><category><![CDATA[stategy]]></category><dc:creator><![CDATA[Andrew Glover]]></dc:creator><pubDate>Tue, 16 Apr 2024 19:24:07 GMT</pubDate><media:content url="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2024/04/Close-up-red-dart-arrow-hitting-target-center-dartboard-on-sunset-background.-Business-targeting-and-focus-concept.-1211578175_7820x4399.jpeg" medium="image"/><content:encoded><![CDATA[<img src="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2024/04/Close-up-red-dart-arrow-hitting-target-center-dartboard-on-sunset-background.-Business-targeting-and-focus-concept.-1211578175_7820x4399.jpeg" alt="Solve the hard problem first"><p>Copasetic goals can be broken down into steps. Steps can be thought of as <em>problems to solve</em>. For example, if a goal is to launch a product, then there are indubitably a number of problems to solve in building the product. Invariably, examining the various problems in need of solving will highlight that there are at least two groups. </p><p>One group is made up of easy problems. These are the steps we&apos;ve done before; they are familiar problems we&apos;ve solved previously.  There&apos;s also a group of complicated problems; they are <a href="https://en.wikipedia.org/wiki/There_are_unknown_unknowns?ref=thediscoblog.com" rel="noreferrer">known unknowns</a>. These types of problems are new challenges; they are hard. It&apos;s critical to differentiate the two types of problems and prioritize them appropriately. </p><h3 id="its-human-nature">It&apos;s human nature</h3><p>Unfortunately, while leaders can categorize steps correctly, they oftentimes prioritize them incorrectly. There&#x2019;s a natural tendency for people to inadvertently prioritize easy work ahead of harder work. Afterall, solving hard problems usually takes a lot longer and oftentimes it&#x2019;s quite satisfying to accomplish <em>something</em> (even if it&#x2019;s easy). Nevertheless, when we go after the easy work before doing the hard work, we do ourselves a disservice. </p><p>Hard problems are usually the ones that when solved, open up doors and create new and hip opportunities. What&apos;s more, the hard problems are usually the ones that throw a wrench in things, resulting in a project that&apos;s perpetually 80% complete. Occasionally, hard problems can&apos;t be solved either! So it&apos;s prudent to attack this class of problems <em>first</em>. The reluctance to go after the hard problem is oftentimes the difference between success and failure.</p><h2 id="monkeys-and-pedestals">Monkeys and pedestals </h2><p>There&#x2019;s a notion from Google&#x2019;s moonshot company, <a href="https://x.company/?ref=thediscoblog.com" rel="noreferrer">X</a> regarding <a href="https://www.theptdc.com/articles/monkeys-and-pedestals?ref=thediscoblog.com"><u>monkeys and pedestals</u></a> that captures the tension of easy problems versus hard problems quite nicely. As <a href="https://www.annieduke.com/?ref=thediscoblog.com"><u>Annie Duke</u></a>, a world champion poker player and author <a href="https://www.annieduke.com/newsletter-monkeys-and-pedestals-find-the-bottleneck-and-solve-for-that-first/?ref=thediscoblog.com"><u>notes in her blog</u></a>: </p><blockquote>
<p>&#x201C;imagine that you&#x2019;re trying to teach a monkey to juggle flaming torches while it stands on a pedestal in the town square. Two tasks are competing for your money, time, and attention: training the monkey and building the pedestal.&#x201D;</p>
</blockquote>
<p>Of course, one task is easy and the other is hard. It&#x2019;s tempting to solve the easy problem and build the pedestal first. It&apos;s a quick win and in doing so, you can show positive progres! But as she aptly states: </p><blockquote>
<p>&#x201C;there is no point in building pedestals if you can&#x2019;t solve for the monkey.&#x201D;</p>
</blockquote>
<p>Solving the hard problem unlocks the entire equation! Get the monkey to juggle flaming torches and the world is your oyster. </p><h3 id="one-thing-at-a-time">One thing at a time</h3><p>When thinking about monkeys and pedestals, it&#x2019;s additionally important to recognize that in any set of choices, there&#x2019;s usually a monkey and quite a few pedestals; moreover, once you solve for the monkey, there will undoubtedly be future choices that present a tension between the two. That is, there are always monkeys in the equation but you only have to solve one of them at a time. </p><h2 id="putting-monkeys-into-perspective">Putting monkeys into perspective</h2><p>Throughout my career, I&apos;ve certainly prioritized pedestals at the expense of monkeys. Unfortunately, I&apos;ve usually come to this conclusion after the fact. </p><p>Monkeys present themselves in various forms! They can be incredibly hard technical problems, but in practice, they&apos;re more often found in seemingly mundane activities such as finding customers or getting people to <em>actually</em> use your product. </p><p>Indeed, building a product is oftentimes the pedestal and getting people to use it is the monkey. The software industry is awash with tomes describing how to find product-market-fit, the essence of building minimal viable products, and avoiding a solution that&apos;s looking for a problem; however, time and time again, leaders overlook this class of hard problems and solve for the pedestal. The technology startup graveyard is littered with headstones etched with &quot;they failed to solve for the monkey.&quot;  </p><p>Leadership is the art of influencing people to achieve a goal. Moreover, an effective leader is capable of breaking a goal into steps. Those steps must be grouped into easy problems and hard problems. Successful leaders <em>attack the hard problems first</em>, because those are the problems when solved, unlock the entire equation. Don&apos;t fall into the trap of solving for the pedestal because it could mean the difference between success and failure. </p><p>Can you dig it? </p><div class="kg-card kg-signup-card kg-width-regular " data-lexical-signup-form style="background-color: #F0F0F0; display: none;">
            
            <div class="kg-signup-card-content">
                
                <div class="kg-signup-card-text ">
                    <h2 class="kg-signup-card-heading" style="color: #000000;"><span style="white-space: pre-wrap;">Sign up for The Disco Blog</span></h2>
                    <p class="kg-signup-card-subheading" style="color: #000000;"><span style="white-space: pre-wrap;">Signing up is the easy problem! </span></p>
                    
        <form class="kg-signup-card-form" data-members-form="signup">
            
            <div class="kg-signup-card-fields">
                <input class="kg-signup-card-input" id="email" data-members-email type="email" required="true" placeholder="Your email">
                <button class="kg-signup-card-button kg-style-accent" style="color: #FFFFFF;" type="submit">
                    <span class="kg-signup-card-button-default">Subscribe</span>
                    <span class="kg-signup-card-button-loading"><svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewbox="0 0 24 24">
        <g stroke-linecap="round" stroke-width="2" fill="currentColor" stroke="none" stroke-linejoin="round" class="nc-icon-wrapper">
            <g class="nc-loop-dots-4-24-icon-o">
                <circle cx="4" cy="12" r="3"/>
                <circle cx="12" cy="12" r="3"/>
                <circle cx="20" cy="12" r="3"/>
            </g>
            <style data-cap="butt">
                .nc-loop-dots-4-24-icon-o{--animation-duration:0.8s}
                .nc-loop-dots-4-24-icon-o *{opacity:.4;transform:scale(.75);animation:nc-loop-dots-4-anim var(--animation-duration) infinite}
                .nc-loop-dots-4-24-icon-o :nth-child(1){transform-origin:4px 12px;animation-delay:-.3s;animation-delay:calc(var(--animation-duration)/-2.666)}
                .nc-loop-dots-4-24-icon-o :nth-child(2){transform-origin:12px 12px;animation-delay:-.15s;animation-delay:calc(var(--animation-duration)/-5.333)}
                .nc-loop-dots-4-24-icon-o :nth-child(3){transform-origin:20px 12px}
                @keyframes nc-loop-dots-4-anim{0%,100%{opacity:.4;transform:scale(.75)}50%{opacity:1;transform:scale(1)}}
            </style>
        </g>
    </svg></span>
                </button>
            </div>
            <div class="kg-signup-card-success" style="color: #000000;">
                Email sent! Check your inbox to complete your signup.
            </div>
            <div class="kg-signup-card-error" style="color: #000000;" data-members-error></div>
        </form>
        
                    <p class="kg-signup-card-disclaimer" style="color: #000000;"><span style="white-space: pre-wrap;">No spam. Unsubscribe anytime.</span></p>
                </div>
            </div>
        </div>]]></content:encoded></item><item><title><![CDATA[Two truths and a lie about software engineering]]></title><description><![CDATA[The carrying costs of production software are recurring. Sadly, we rarely realize it, much to the dismay of stakeholders. Having a healthy appreciation for the carrying costs of code will make you a more effective leader.  ]]></description><link>https://www.thediscoblog.com/two-truths-and-a-lie-about-software-engineering/</link><guid isPermaLink="false">660c9ea9c500300001726f3e</guid><category><![CDATA[leadership]]></category><category><![CDATA[productivity]]></category><category><![CDATA[management]]></category><dc:creator><![CDATA[Andrew Glover]]></dc:creator><pubDate>Wed, 03 Apr 2024 23:10:25 GMT</pubDate><media:content url="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2024/04/Young-afro-american-woman-over-isolated-background-peeking-in-shock-covering-face-and-eyes-with-hand--looking-through-fingers-with-embarrassed-expression.-1124236398_1847x1627.jpeg" medium="image"/><content:encoded><![CDATA[<img src="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2024/04/Young-afro-american-woman-over-isolated-background-peeking-in-shock-covering-face-and-eyes-with-hand--looking-through-fingers-with-embarrassed-expression.-1124236398_1847x1627.jpeg" alt="Two truths and a lie about software engineering"><p>&quot;Two Truths and a Lie&quot; is a groovy icebreaker game that encourages players to share interesting details about themselves where two statements are true and one is a fib. In the game, the group guesses which of the remarks are true and which one of them is a lie. I love the game; unfortunately, I&#x2019;m pretty bad at it as people quickly pick up on my fabrication. Nevertheless, I&#x2019;m going to play with you and let&#x2019;s see if you can find the lie:&#xA0;&#xA0;</p><ol><li>Developers spend more time reading code than writing code.&#xA0;</li><li>More time is spent maintaining code than in initially writing it.&#xA0;</li><li>Management accounts for the carrying cost of existing code when planning.</li></ol><p>Which one of these statements is the falsehood? I imagine the lie is obvious, but strangely, as an industry, we consistently overlook a fundamental truth of software development and proceed to mislead ourselves, our customers, and stakeholders. We routinely <em>overlook the costs of maintaining code</em>.&#xA0;I categorize software maintenance as a carrying cost and if you do not appreciate its effect on a development team&apos;s capacity, you will routinely overpromise and underdeliver.&#xA0;</p><h2 id="carrying-costs-are-recurring">Carrying costs are recurring</h2><p>We rarely account for how much time and effort is required to keep existing code running healthily. Healthy running code is not a point in time but rather a <em>span of time</em>. It&apos;s a recurring cost that starts once software is in the field and used by people and continues until that code is retired.  Thus, because we don&#x2019;t think in terms of carrying costs and fool ourselves into thinking maintenance-like efforts are one-time events, we overpromise. Once code is in the wild, time is required for the care and feeding of it. What&#x2019;s more, the time required is <em>proportional to the surface area of the software</em>. The more code that exists, the more time will be spent keeping it running.&#xA0;</p><p>I say this with confidence because I&#x2019;ve fallen into this trap! I&#x2019;m guilty of underestimating how much work is required to keep the lights on. In doing so, I&#x2019;ve overpromised and underdelivered to stakeholders. It&#x2019;s an easy trap to fall into too: it&#x2019;s far more fun to write new code and add new features. Delivering new functionality to stakeholders intuitively feels more exciting than telling them you fixed a series of bugs. Code maintenance has never been a fashionable activity one brags about.  &#xA0;</p><h2 id="accounting-for-carrying-costs">Accounting for carrying costs</h2><p>If planning and resource allocation were copasetic and easy in software development, projects wouldn&apos;t be late and stakeholders would never be disappointed. Figuring out how much time to allocate to software care and feeding is a mixture of science and art. In my experience, the time required to keep the lights on is far more than you initially think. Accordingly, my advice is to err on the side of conservativity. </p><p>The art aspect of this process is in recognizing a team&apos;s capacity encompasses more than new feature development. Their capacity must include the care and feeding of running software. From a scientific standpoint, there&#x2019;s years of knowledge around the various techniques for ascertaining a software team&#x2019;s velocity; suffice it to say, as a leader, you absolutely must get a feel for how much work a team can reasonably accommodate in a span of time. Once you realize that the vast majority of stakeholders believe work means new features, then you can address the problem by clarifying additional time is required for maintenance.  </p><p>One way to reason about capacity planning and feature work is to think in terms of surface area. I tend to logically think of a software project as made up of <a href="https://martinfowler.com/bliki/DomainDrivenDesign.html?ref=thediscoblog.com"><u>domains</u></a>. The more domains an initiative has, the larger the surface area. Surface area is at tension with capacity. Capacity is the maximum amount of work your team can do. If you have a lot of surface area, you need a lot of capacity. Capacity is almost always your limiting factor; accordingly, carrying costs tend to accumulate as domains proliferate.&#xA0;</p><h3 id="focus-focus-and-more-focus">Focus, focus, and more focus</h3><p>Anyone who&#x2019;s successfully built a software product from scratch intuitively appreciates the value of focus. A laser focus on creating value for specific users is another way of limiting surface area. As a product&#x2019;s focus widens to accommodate more features, users, and use cases so too does the surface area of a product. You can hire to keep the pace of surface area, but this relationship is rarely in lock step. Hence, lean towards conservativity when accounting for your capacity to maintain what&apos;s going into the field. </p><p>There are certainly times when it pays to lean into racing out ahead (i.e. building this new thing <em>right now</em> will yield more users, etc). A bias to action can increase a product&apos;s surface area. In those instances, by all means, do it. Recognize that you&apos;ll have to pay down that veritable debt in some way either by temporarily slowing down, hiring, or cutting back on promises. Taking the initiative to pull head of the competition or capture a market in need is definitely part of the game! Understanding how and when to race ahead or pull back is how the game is won. </p><h2 id="acknowledge-carrying-costs-and-plan-accordingly">Acknowledge carrying costs and plan accordingly</h2><p>I hope you enjoyed playing two truths and a lie! Indeed, the fib is that management properly accounts for carrying costs. Frequently this cost goes accounted in favor of new feature development. Sadly, by ignoring carrying costs, we continually overpromise and underdeliver. Acknowledging carrying costs will immediately make you a more effective planner. </p><p>An essential aspect of leadership is influencing and inspiring a group of people to achieve challenging goals. Tactically, you will absolutely need to understand your team&apos;s capacity and master stakeholder expectations. Software needs care and feeding. It needs maintenance to properly function. A healthy appreciation for the carrying costs of code in production will make you a more realistic and effective leader.   </p><p>Dig it? </p><div class="kg-card kg-signup-card kg-width-regular " data-lexical-signup-form style="background-color: #F0F0F0; display: none;">
            
            <div class="kg-signup-card-content">
                
                <div class="kg-signup-card-text ">
                    <h2 class="kg-signup-card-heading" style="color: #000000;"><span style="white-space: pre-wrap;">Sign up for The Disco Blog</span></h2>
                    <p class="kg-signup-card-subheading" style="color: #000000;"><span style="white-space: pre-wrap;">This is a one-time cost for recurring Disco themed content! </span></p>
                    
        <form class="kg-signup-card-form" data-members-form="signup">
            
            <div class="kg-signup-card-fields">
                <input class="kg-signup-card-input" id="email" data-members-email type="email" required="true" placeholder="Your email">
                <button class="kg-signup-card-button kg-style-accent" style="color: #FFFFFF;" type="submit">
                    <span class="kg-signup-card-button-default">Subscribe</span>
                    <span class="kg-signup-card-button-loading"><svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewbox="0 0 24 24">
        <g stroke-linecap="round" stroke-width="2" fill="currentColor" stroke="none" stroke-linejoin="round" class="nc-icon-wrapper">
            <g class="nc-loop-dots-4-24-icon-o">
                <circle cx="4" cy="12" r="3"/>
                <circle cx="12" cy="12" r="3"/>
                <circle cx="20" cy="12" r="3"/>
            </g>
            <style data-cap="butt">
                .nc-loop-dots-4-24-icon-o{--animation-duration:0.8s}
                .nc-loop-dots-4-24-icon-o *{opacity:.4;transform:scale(.75);animation:nc-loop-dots-4-anim var(--animation-duration) infinite}
                .nc-loop-dots-4-24-icon-o :nth-child(1){transform-origin:4px 12px;animation-delay:-.3s;animation-delay:calc(var(--animation-duration)/-2.666)}
                .nc-loop-dots-4-24-icon-o :nth-child(2){transform-origin:12px 12px;animation-delay:-.15s;animation-delay:calc(var(--animation-duration)/-5.333)}
                .nc-loop-dots-4-24-icon-o :nth-child(3){transform-origin:20px 12px}
                @keyframes nc-loop-dots-4-anim{0%,100%{opacity:.4;transform:scale(.75)}50%{opacity:1;transform:scale(1)}}
            </style>
        </g>
    </svg></span>
                </button>
            </div>
            <div class="kg-signup-card-success" style="color: #000000;">
                Email sent! Check your inbox to complete your signup.
            </div>
            <div class="kg-signup-card-error" style="color: #000000;" data-members-error></div>
        </form>
        
                    <p class="kg-signup-card-disclaimer" style="color: #000000;"><span style="white-space: pre-wrap;">No spam. Unsubscribe anytime.</span></p>
                </div>
            </div>
        </div>]]></content:encoded></item><item><title><![CDATA[Platform is a leverage play]]></title><description><![CDATA[I was recently a guest on the Ship It! podcast where the hosts and I discussed all things Platform and Productivity Engineering, lessons learned, technology evolution, values that matter for leaders, and much more! ]]></description><link>https://www.thediscoblog.com/platform-is-a-leverage-play/</link><guid isPermaLink="false">6603219cab3ba60001f9a683</guid><category><![CDATA[podcast]]></category><category><![CDATA[News]]></category><category><![CDATA[leadership]]></category><category><![CDATA[platform]]></category><dc:creator><![CDATA[Andrew Glover]]></dc:creator><pubDate>Tue, 26 Mar 2024 21:50:37 GMT</pubDate><media:content url="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2024/03/Cup-of-coffee-and-the-newspapers-173733825_2120x1419.jpeg" medium="image"/><content:encoded><![CDATA[<img src="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2024/03/Cup-of-coffee-and-the-newspapers-173733825_2120x1419.jpeg" alt="Platform is a leverage play"><p>I was <a href="https://changelog.com/shipit/96?ref=thediscoblog.com"><u>recently a guest</u></a> on the copasetic <a href="https://changelog.com/shipit?ref=thediscoblog.com"><u>Ship It!</u></a> podcast, which is one of my favorite shows from the amazingly prolific folks at <a href="https://changelog.com/?ref=thediscoblog.com" rel="noreferrer">Changelog</a>! <a href="https://changelog.com/person/autumn808?ref=thediscoblog.com" rel="noreferrer">Autumn</a>, <a href="https://twitter.com/rothgar?ref=thediscoblog.com" rel="noreferrer">Justin</a>, and I discussed the value of Platform Engineering, various lessons learned from my experience at Netflix ranging from the challenge of technology evolution, infrastructure migrations, saying no, and the importance of empathy when working with stakeholders.&#xA0;Plus, we talked about Productivity Engineering, how one measures productivity, and much, much more. Give it a listen! </p>
<!--kg-card-begin: html-->
<audio data-theme="day" data-src="https://changelog.com/shipit/96/embed" src="https://op3.dev/e/https://cdn.changelog.com/uploads/shipit/96/ship-it-96.mp3" preload="none" class="changelog-episode" controls></audio><p><a href="https://changelog.com/shipit/96?ref=thediscoblog.com">Ship It! 96: Productivity engineering at Netflix</a> &#x2013;&#xA0;Listen on <a href="https://changelog.com/?ref=thediscoblog.com">Changelog.com</a></p><script async src="//cdn.changelog.com/embed.js"></script>
<!--kg-card-end: html-->
<p>It was groovy chatting with Justin and Autumn; what&#x2019;s more, I&#x2019;m super excited that the Ship It! podcast has spun back up. Autumn and Justin bring fresh new perspectives and a renewed energy to the discussion about all the things that happen after <code>git push</code>.&#xA0;Be sure to subscribe to Ship It! along with Changelog&apos;s other podcasts like <a href="https://changelog.com/practicalai?ref=thediscoblog.com" rel="noreferrer">Practical AI</a> and, of course, the stalwart podcast that started it all: <a href="https://changelog.com/podcast?ref=thediscoblog.com" rel="noreferrer">The Changelog</a>. </p><p>I&apos;d love for <a href="https://shipit.show/96?ref=thediscoblog.com" rel="noreferrer">you to listen to the show</a> and, of course, if you have any feedback, please let me know! Dig it? </p><div class="kg-card kg-signup-card kg-width-regular " data-lexical-signup-form style="background-color: #F0F0F0; display: none;">
            
            <div class="kg-signup-card-content">
                
                <div class="kg-signup-card-text ">
                    <h2 class="kg-signup-card-heading" style="color: #000000;"><span style="white-space: pre-wrap;">Sign up for The Disco Blog! </span></h2>
                    <p class="kg-signup-card-subheading" style="color: #000000;"><span style="white-space: pre-wrap;">It&apos;s the hippest blog on the internet since 1973. </span></p>
                    
        <form class="kg-signup-card-form" data-members-form="signup">
            
            <div class="kg-signup-card-fields">
                <input class="kg-signup-card-input" id="email" data-members-email type="email" required="true" placeholder="Your email">
                <button class="kg-signup-card-button kg-style-accent" style="color: #FFFFFF;" type="submit">
                    <span class="kg-signup-card-button-default">Subscribe</span>
                    <span class="kg-signup-card-button-loading"><svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewbox="0 0 24 24">
        <g stroke-linecap="round" stroke-width="2" fill="currentColor" stroke="none" stroke-linejoin="round" class="nc-icon-wrapper">
            <g class="nc-loop-dots-4-24-icon-o">
                <circle cx="4" cy="12" r="3"/>
                <circle cx="12" cy="12" r="3"/>
                <circle cx="20" cy="12" r="3"/>
            </g>
            <style data-cap="butt">
                .nc-loop-dots-4-24-icon-o{--animation-duration:0.8s}
                .nc-loop-dots-4-24-icon-o *{opacity:.4;transform:scale(.75);animation:nc-loop-dots-4-anim var(--animation-duration) infinite}
                .nc-loop-dots-4-24-icon-o :nth-child(1){transform-origin:4px 12px;animation-delay:-.3s;animation-delay:calc(var(--animation-duration)/-2.666)}
                .nc-loop-dots-4-24-icon-o :nth-child(2){transform-origin:12px 12px;animation-delay:-.15s;animation-delay:calc(var(--animation-duration)/-5.333)}
                .nc-loop-dots-4-24-icon-o :nth-child(3){transform-origin:20px 12px}
                @keyframes nc-loop-dots-4-anim{0%,100%{opacity:.4;transform:scale(.75)}50%{opacity:1;transform:scale(1)}}
            </style>
        </g>
    </svg></span>
                </button>
            </div>
            <div class="kg-signup-card-success" style="color: #000000;">
                Email sent! Check your inbox to complete your signup.
            </div>
            <div class="kg-signup-card-error" style="color: #000000;" data-members-error></div>
        </form>
        
                    <p class="kg-signup-card-disclaimer" style="color: #000000;"><span style="white-space: pre-wrap;">No spam. Unsubscribe anytime.</span></p>
                </div>
            </div>
        </div>]]></content:encoded></item><item><title><![CDATA[The first rule of decision-making]]></title><description><![CDATA[Leadership requires making decisions and I've found it helpful to apply three principles. ]]></description><link>https://www.thediscoblog.com/the-first-rule-of-decision-making/</link><guid isPermaLink="false">65ee61cdf820810001119c87</guid><category><![CDATA[leadership]]></category><dc:creator><![CDATA[Andrew Glover]]></dc:creator><pubDate>Mon, 11 Mar 2024 21:07:05 GMT</pubDate><media:content url="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2024/03/Confused-Man-Looking-Up-000063649367_XXXLarge_sm.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2024/03/Confused-Man-Looking-Up-000063649367_XXXLarge_sm.jpg" alt="The first rule of decision-making"><p>As a leader, your job is to make decisions. There are a few copasetic principles I&#x2019;ve used over the years that help me with decision-making. Two particular principles help in determining how long you should spend contemplating a decision and who should make the decision. Not all decisions are created equally; what&apos;s more, leaders don&apos;t need to make all the decisions. These two principles are essential and as you&apos;ll see, there&apos;s an equally important third principle that&apos;s frequently overlooked. </p><h3 id="what-are-the-consequences-of-the-decision">What are the consequences of the decision? </h3><p>Some decisions carry more weight than other decisions. Accordingly, there&#x2019;s the notion of Type 1 and Type 2 decisions, famously embraced by Amazon&apos;s Jeff Bezos. In this framework, Type 1 decisions are one-way doors requiring thorough analysis. You cannot undo these decisions, so it&#x2019;s important not to rush into them. Type 2 decisions, on the other hand, are two-way doors. These decisions can be undone. The risk of making a bad Type 2 decision isn&#x2019;t catastrophic thus the diligence required shouldn&#x2019;t be arduous. You can make Type 2 decisions faster than Type 1 decisions.&#xA0;</p><p>Differentiating between the two types of decisions is key to Amazon&#x2019;s ability to move fast. As <a href="https://www.sec.gov/Archives/edgar/data/1018724/000119312516530910/d168744dex991.htm?ref=thediscoblog.com" rel="noreferrer">Jeff Bezos wrote in a letter to shareholders in 1997</a>, he felt it  important for Amazon leadership to appreciate Type 2 decisions for what they are and not mistake them for crucial Type 1 decisions. Confusing the two means:&#xA0;&#xA0;&#xA0;</p><blockquote>&#x201C;The end result of this is slowness, unthoughtful risk aversion, failure to experiment sufficiently, and consequently diminished invention. We&apos;ll have to figure out how to fight that tendency.&#x201D;</blockquote><p>Clearly, this groovy framework works for Amazon and has certainly influenced me in my decision-making. Understanding a decision&apos;s consequence underscores how much time I should spend on making it. Higher risk Type 1 decisions demand more of your attention and careful thought.   </p><h3 id="who-should-make-the-decision">Who should make the decision? </h3><p>Determining the consequence of a decision is important, but it&apos;s equally imperative to determine <em>who</em> should make the decision. A leader who has built an amazing organization and who provides excellent context shouldn&#x2019;t necessarily need to make <em>a lot </em>of decisions. If you have a capable team, there are a bevy of decisions you can effectively delegate. For example, as <a href="https://www.gsb.stanford.edu/insights/netflix-founder-reed-hastings-make-few-decisions-possible?ref=thediscoblog.com#:~:text=%E2%80%9CI%20take%20pride%20in%20making,as%20possible%2C%E2%80%9D%20Hastings%20says" rel="noreferrer">Reed Hastings once opined</a>:&#xA0;</p><blockquote>&#x201C;I take pride in making as few decisions as possible, as opposed to making as many as possible.&#x201D;</blockquote><p>Make no mistake, Reed certainly made decisions in his role as CEO of Netflix and those decisions were most definitely critical to the business. But many other decisions were made by his team of trusted lieutenants. And many of their decisions were delegated to their trusted teams and so forth on down. The idea espoused by Netflix leadership is to ensure everyone has amazing context and thus push decision-making to the appropriate level. </p><p>I certainly began my leadership career thinking I should make all the decisions; however, I&apos;ve learned to embrace Reed&apos;s philosophy. I&apos;ve come to realize that I can empower my teams to make far<em> </em>more<em> informed</em> decisions if I&apos;ve provided great context. </p><h3 id="the-more-important-question-to-figure-out">The more important question to figure out</h3><p>Distinguishing who should make a decision and the consequences of that decision are certainly important principles in decision-making. But for me, the first rule of decision-making isn&#x2019;t either of these. Instead, the first rule is to determine <em>when a particular decision needs to be made.&#xA0;&#xA0;</em></p><p>There&#x2019;s a tendency for leaders to demonstrate confidence and poise under pressure. The need to prove oneself as a component leader manifests itself as <em>fast decision-making</em>. But I&#x2019;ve come to realize that many seemingly important decisions don&#x2019;t, in fact, need to be made in the moment. In my experience, determining <em>when</em> to make a decision often helps uncover the <em>consequences</em> of it and accordingly <em>who</em> should properly make the decision.&#xA0;</p><p>Deciding when a decision needs to be made doesn&#x2019;t mean putting off decision-making! Situational awareness is key; bias to action is important. Additionally, analysis paralysis or waiting too long is obviously an ineffective practice. Consequently, deciding when a decision needs to be made may yield an answer of <em>right now</em>! Obviously, if that&#x2019;s the case, then make sure you&#x2019;ve gathered the skinny and make a call.&#xA0;</p><p>By giving yourself a few moments to consider when a particular decision needs deciding, you reduce the chances of rushing into it. If you determine that an answer isn&#x2019;t immediately required, you certainly need to figure out when it must be decided. In the course of determining the answer to that equation, you have time to reason through the consequences of the decision and can furthermore figure out&#xA0;who should make the decision.&#xA0;&#xA0;</p><h3 id="decision-making-principles">Decision-making principles</h3><p>Leadership requires making decisions and I&apos;ve found it helpful to apply principles. It&apos;s important to first figure out <em>when</em> a decision needs to be made. Moreover, determining the consequence of a decision will point out <em>how long</em> to spend making it. Finally, many decisions can be made by people <em>closer to the problem</em> as they&apos;re likely more informed than you.  </p><p>You don&apos;t need to make snap decisions all that often. You also don&apos;t need to make every decision. Take the time to apply principled decision-making and you&apos;ll become a more effective leader. </p><p>Can you dig it? </p><div class="kg-card kg-signup-card kg-width-regular " data-lexical-signup-form style="background-color: #F0F0F0; display: none;">
            
            <div class="kg-signup-card-content">
                
                <div class="kg-signup-card-text ">
                    <h2 class="kg-signup-card-heading" style="color: #000000;"><span style="white-space: pre-wrap;">Sign up for The Disco Blog</span></h2>
                    <p class="kg-signup-card-subheading" style="color: #000000;"><span style="white-space: pre-wrap;">Make a fabulous decision and do it, baby! </span></p>
                    
        <form class="kg-signup-card-form" data-members-form="signup">
            
            <div class="kg-signup-card-fields">
                <input class="kg-signup-card-input" id="email" data-members-email type="email" required="true" placeholder="Your email">
                <button class="kg-signup-card-button kg-style-accent" style="color: #FFFFFF;" type="submit">
                    <span class="kg-signup-card-button-default">Subscribe</span>
                    <span class="kg-signup-card-button-loading"><svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewbox="0 0 24 24">
        <g stroke-linecap="round" stroke-width="2" fill="currentColor" stroke="none" stroke-linejoin="round" class="nc-icon-wrapper">
            <g class="nc-loop-dots-4-24-icon-o">
                <circle cx="4" cy="12" r="3"/>
                <circle cx="12" cy="12" r="3"/>
                <circle cx="20" cy="12" r="3"/>
            </g>
            <style data-cap="butt">
                .nc-loop-dots-4-24-icon-o{--animation-duration:0.8s}
                .nc-loop-dots-4-24-icon-o *{opacity:.4;transform:scale(.75);animation:nc-loop-dots-4-anim var(--animation-duration) infinite}
                .nc-loop-dots-4-24-icon-o :nth-child(1){transform-origin:4px 12px;animation-delay:-.3s;animation-delay:calc(var(--animation-duration)/-2.666)}
                .nc-loop-dots-4-24-icon-o :nth-child(2){transform-origin:12px 12px;animation-delay:-.15s;animation-delay:calc(var(--animation-duration)/-5.333)}
                .nc-loop-dots-4-24-icon-o :nth-child(3){transform-origin:20px 12px}
                @keyframes nc-loop-dots-4-anim{0%,100%{opacity:.4;transform:scale(.75)}50%{opacity:1;transform:scale(1)}}
            </style>
        </g>
    </svg></span>
                </button>
            </div>
            <div class="kg-signup-card-success" style="color: #000000;">
                Email sent! Check your inbox to complete your signup.
            </div>
            <div class="kg-signup-card-error" style="color: #000000;" data-members-error></div>
        </form>
        
                    <p class="kg-signup-card-disclaimer" style="color: #000000;"><span style="white-space: pre-wrap;">No spam. Unsubscribe anytime.</span></p>
                </div>
            </div>
        </div>]]></content:encoded></item><item><title><![CDATA[Extending Code Llama]]></title><description><![CDATA[Retrieval-augmented generation (or RAG) boosts the usefulness of AI by combining specific knowledge with an LLM. In this case, pointing Code Llama at specific code yields a highly productive learning experience.]]></description><link>https://www.thediscoblog.com/extending-code-llama/</link><guid isPermaLink="false">65e52d48943fe30001dfe00f</guid><category><![CDATA[productivity]]></category><category><![CDATA[python]]></category><category><![CDATA[ruby]]></category><category><![CDATA[AI]]></category><category><![CDATA[RAG]]></category><category><![CDATA[LLM]]></category><category><![CDATA[Code Llama]]></category><dc:creator><![CDATA[Andrew Glover]]></dc:creator><pubDate>Mon, 04 Mar 2024 19:30:58 GMT</pubDate><media:content url="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2024/03/Easy---Inscription-on-Blue-Keyboard-Key.-3D-687479784_5000x2875.jpeg" medium="image"/><content:encoded><![CDATA[<img src="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2024/03/Easy---Inscription-on-Blue-Keyboard-Key.-3D-687479784_5000x2875.jpeg" alt="Extending Code Llama"><p>I&#x2019;ve been <a href="https://www.thediscoblog.com/pairing-with-my-friend-codelama/" rel="noreferrer">diggin&apos; Code Llama lately</a>. It&apos;s free, easy to set up, and using Code Llama via <a href="https://github.com/ollama/ollama?ref=thediscoblog.com" rel="noreferrer">Ollama</a> can be a lot of fun. Code Llama has already proved itself useful out-of-the-box; nevertheless, I was curious if I could boost my productivity by using <a href="https://research.ibm.com/blog/retrieval-augmented-generation-RAG?ref=thediscoblog.com" rel="noreferrer">retrieval-augmented generation</a> (or RAG for short). I decided on pointing Code Llama directly at specific code for question answering. I wanted to see if Code Llama could help me understand an underlying code base and help me develop new features. </p><p>If you&apos;re willing to roll up your sleeves, this is a fairly easy process! All you have to do is write a little Python code. </p><h3 id="whats-rag">What&apos;s RAG?</h3><p>Briefly, <a href="https://medium.com/@datadrifters/fully-local-rag-with-qdrant-ollama-langchain-and-langserve-10d4dd1facbc?ref=thediscoblog.com" rel="noreferrer">RAG</a> is technique that combines something specific (like code, documents, images, etc) with a more general LLM. In this way, <a href="https://research.ibm.com/people/kim-martineau?ref=thediscoblog.com" rel="noreferrer">as Kim Martineau explains</a>, RAG improves the: </p><blockquote>quality of&#xA0;<a href="https://www.youtube.com/watch?v=hfIUstzHs9A&amp;ref=thediscoblog.com">LLM-generated</a>&#xA0;responses by grounding the model on external sources of knowledge to supplement the LLM&#x2019;s internal representation of information. </blockquote><p>For instance, if I asked an LLM, such as OpenAI&apos;s ChatGPT, to summarize everything <a href="https://www.thediscoblog.com/tag/leadership/" rel="noreferrer">I&apos;ve recently written about leadership</a>, I&apos;d be out of luck. ChatGPT has no knowledge of my recent blog entries as the model was trained <em>before</em> I wrote my articles. Nevertheless, I could provide the model with some relevant information, such as everything amazing I&apos;ve recently written <em>and then ask </em>the LLM to summarize my leadership articles. Depending on the quality and quantity of the specific information provided, the LLM could surprise you!   </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2024/03/rag-description.png" class="kg-image" alt="Extending Code Llama" loading="lazy" width="998" height="474" srcset="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/size/w600/2024/03/rag-description.png 600w, https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2024/03/rag-description.png 998w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">A high-level overview of RAG.</span></figcaption></figure><p>In essence, via RAG, you provide the LLM with some specific information, and then the corresponding answers to your questions are <em>more informed </em>through the use of that specific data. </p><h3 id="code-llama-and-rag">Code Llama and RAG</h3><p>Leveraging RAG with Code Llama takes about 60 lines of Python code. What&apos;s more, I leveraged <a href="https://ollama.com/?ref=thediscoblog.com" rel="noreferrer">Ollama</a> along with <a href="https://www.langchain.com/?ref=thediscoblog.com" rel="noreferrer">LangChain</a>. You&apos;ll need to ensure that you&apos;ve both pulled and run the Code Llava LLM via Ollama beforehand. The steps to create a RAG with Code Llama are:</p><ol>
<li>Create an instance of an LLM.</li>
<li>Create a prompt (i.e. context for the LLM).</li>
<li>Load a directory full of code.</li>
<li>Split the various files from step 3 into chunks.</li>
<li>Store those chunks in a vector database.</li>
<li>Create a chain from all the above which generates answers to questions.</li>
</ol>
<p>I created a simple class named <code>ChatCode</code> that has three methods: an initialization one (i.e. Python&apos;s<code>init</code>), <code>ingest</code>, and an <code>ask</code>. The <code>init</code> method creates an instance of <code>ChatOllama</code> using the Code Llama LLM and creates a <a href="https://www.comet.com/site/blog/introduction-to-prompt-templates-in-langchain/?ref=thediscoblog.com" rel="noreferrer">prompt context</a>. </p><figure class="kg-card kg-code-card"><pre><code class="language-python">def __init__(self):
    self.model = ChatOllama(model=&quot;codellama&quot;)
    self.prompt = PromptTemplate.from_template(
      &quot;&quot;&quot;
      &lt;s&gt; [INST] You are an expert programmer for 
      question-answering tasks. Use the following pieces of retrieved
      context to answer the question. If you don&apos;t know the answer, 
      just say that you don&apos;t know. Use three sentences maximum and 
      keep the answer concise. [/INST] &lt;/s&gt; 
      [INST] Question: {question} 
      Context: {context} 
      Answer: [/INST]
      &quot;&quot;&quot;
    )</code></pre><figcaption><p><code spellcheck="false" style="white-space: pre-wrap;"><span>ChatCode</span></code><span style="white-space: pre-wrap;">&apos;s initialize method.</span></p></figcaption></figure><p>The key aspect of <code>init</code> is the creation of a <code>PromptTemplate</code>, which seeds the LLM with specific context (i.e the LLM should act as an expert programmer). The context provided connects the interaction between the LLM and a user. As <a href="https://www.comet.com/site/blog/introduction-to-prompt-templates-in-langchain/?ref=thediscoblog.com" rel="noreferrer">Harpreet Sahota eloquently states</a>, LangChain&apos;s <code>PromptTemplate</code> class enables</p><blockquote>the refined language through which we converse with sophisticated AI systems, ensuring precision, clarity, and adaptability in every interaction.</blockquote><p>The magic of RAG happens in the <code>ingest</code> method where a number of important operations occur. Firstly, a file system path is provided and all Ruby files are loaded into memory via the <code>GenericLoader</code> class. Those Ruby files are then spit into chunks for efficient indexing into a vector store. The logic here is obviously specific to Ruby and you can easily do the same for Python, Go, Java, or a <a href="https://python.langchain.com/docs/integrations/document_loaders/source_code?ref=thediscoblog.com" rel="noreferrer">handful of other languages</a>. </p><figure class="kg-card kg-code-card"><pre><code class="language-Python">def ingest(self, path: str):
        
    loader = GenericLoader.from_filesystem(
        path, glob=&quot;**/[!.]*&quot;, suffixes=[&quot;.rb&quot;],
        parser=LanguageParser(),
    )

    text_splitter = RecursiveCharacterTextSplitter.from_language(
        language=Language.RUBY, chunk_size=1024, chunk_overlap=100)

    chunks = text_splitter.split_documents(loader.load())
    chunks = filter_complex_metadata(chunks)

    vector_store = Chroma.from_documents(
      documents=chunks, embedding=FastEmbedEmbeddings())
    
    retriever = vector_store.as_retriever(
        search_type=&quot;similarity_score_threshold&quot;,
        search_kwargs={&quot;k&quot;: 3,&quot;score_threshold&quot;: 0.5}
    )

    self.chain = ({&quot;context&quot;: retriever, 
                   &quot;question&quot;: RunnablePassthrough()}
                  | self.prompt
                  | self.model
                  | StrOutputParser())</code></pre><figcaption><p><code spellcheck="false" style="white-space: pre-wrap;"><span>ChatCode</span></code><span style="white-space: pre-wrap;">&apos;s </span><code spellcheck="false" style="white-space: pre-wrap;"><span>ingest</span></code><span style="white-space: pre-wrap;"> method.</span></p></figcaption></figure><p>The vector store in this case is <a href="https://docs.trychroma.com/?ref=thediscoblog.com" rel="noreferrer">Chroma</a>, which by default stores vectors (i.e. documents) in memory. A <a href="https://python.langchain.com/docs/modules/data_connection/retrievers/?ref=thediscoblog.com" rel="noreferrer">LangChain Retriever</a> instance is created that returns results from Chroma given a specific query. In this case, a <a href="https://python.langchain.com/docs/modules/data_connection/retrievers/vectorstore?ref=thediscoblog.com#similarity-score-threshold-retrieval" rel="noreferrer">similarity score threshold retrieval</a> is used, which returns a maximum of three documents above a threshold of 0.5. Finally, a <a href="https://python.langchain.com/docs/modules/chains/?ref=thediscoblog.com" rel="noreferrer">LangChain <code>chain</code></a> is created that links everything together. This final step is where RAG&apos;s power is leveraged as an existing model, like Code Llama, is combined with specific context, such as Ruby code. </p><p>With RAG set up, asking a question of a code base couldn&apos;t be any easier! The <code>ask</code> method simply invokes the aforementioned <code>chain</code> like so:   </p><figure class="kg-card kg-code-card"><pre><code class="language-Python">def ask(self, query: str):
    if not self.chain:
        return &quot;Please, add a directory first.&quot;

    return self.chain.invoke(query)</code></pre><figcaption><p><span style="white-space: pre-wrap;">CodeChat&apos;s ask method. </span></p></figcaption></figure><p>As you can see, there&apos;s not a lot of complex code. It&apos;s quite simple! Specific context is loaded into memory and combined with an LLM for a more precise, context-aware answer. In this case, asked questions will be answered by what&apos;s found in the underlying indexed code.</p><h3 id="show-me-the-money-baby">Show me the money, baby!</h3><p>As I have a few different Rails application on hand, I fired up the <a href="https://docs.python.org/3/tutorial/interpreter.html?ref=thediscoblog.com" rel="noreferrer">Python interpreter</a> on my local machine and ingested a simple <a href="https://github.com/aglover/locution?ref=thediscoblog.com" rel="noreferrer">dictionary application named Locution</a>.  </p><pre><code class="language-bash">&gt;&gt;&gt; from chat_code import ChatCode
&gt;&gt;&gt; code = ChatCode()
&gt;&gt;&gt; code.ingest(&quot;/Users/ajglover/Development/projects/github/locution&quot;)</code></pre><p>The <code>ingest</code> method might take a few moments depending on how many files are loaded. Once <code>ingest</code> returns, you can then invoke the <code>ask</code> method. I decided to start simple and give <code>CodeChat</code> a softball question: </p><figure class="kg-card kg-code-card"><pre><code class="language-bash">&gt;&gt;&gt; code.ask(&quot;what language was this code base written in?&quot;)</code></pre><figcaption><p><span style="white-space: pre-wrap;">Starting with an easy question!</span></p></figcaption></figure><p>While the question is an easy one, it also demonstrates that CodeChat is focused on my Locution code base! </p><figure class="kg-card kg-code-card"><pre><code class="language-bash">&apos;The code base was written in Ruby.&apos;</code></pre><figcaption><p><span style="white-space: pre-wrap;">A softball question yields the correct response!</span></p></figcaption></figure><p>With <code>CodeChat</code> providing a specific answer, I&apos;m confident that my RAG setup with Code Llama is working correctly. Next, I&apos;ll ask a more relevant question for what would be a typical use case for a question-answer style application pointed at a code base. In my case, I want to know about the underlying database. </p><figure class="kg-card kg-code-card"><pre><code class="language-bash">&gt;&gt;&gt; code.ask(&quot;what database tables does this application work with?&quot;)
</code></pre><figcaption><p><span style="white-space: pre-wrap;">I could have also looked at the models.</span></p></figcaption></figure><p><code>CodeChat</code> dutifully tells me that two tables are defined (did I mention this is a simple Rails application?).  </p><figure class="kg-card kg-code-card"><pre><code>&apos;The application works with two database tables, &quot;definitions&quot; and &quot;words&quot;. The &quot;definitions&quot; table has columns for the definition and example sentence, as well as a foreign key referencing the &quot;words&quot; table. The &quot;words&quot; table has columns for the word and part of speech, as well as timestamps for creation and update.&apos;</code></pre><figcaption><p><span style="white-space: pre-wrap;">A detailed response describing the two tables defined in the application.</span></p></figcaption></figure><p>The generated response is quite helpful as it describes the details of the two tables <code>words</code> and <code>definitions</code>. But I&apos;m not quite satisfied as I&apos;d like to see the <a href="https://www.techtarget.com/whatis/definition/Data-Definition-Language-DDL?ref=thediscoblog.com#:~:text=Data%20Definition%20Language%20(DDL)%20is,commands%20and%20a%20specific%20syntax." rel="noreferrer">DDL</a>. It&apos;s no problem though &#x2013;  all I have to do is ask! </p><figure class="kg-card kg-code-card"><pre><code class="language-bash">&gt;&gt;&gt; code.ask(&quot;can you show me the DDL for the two tables defined?&quot;)</code></pre><figcaption><p><span style="white-space: pre-wrap;">Gettin&apos; specific with CodeChat!</span></p></figcaption></figure><p><code>CodeChat</code> gives me what appears as a cogent answer: </p><figure class="kg-card kg-code-card"><pre><code class="language-bash">Sure, here are the DDL statements for the two tables defined in the provided context:

1. `definitions` table:
    
    CREATE TABLE definitions (
    definition VARCHAR(255),
    example_sentence VARCHAR(255),
    word_id BIGINT NOT NULL,
    created_at DATETIME NOT NULL,
    updated_at DATETIME NOT NULL,
    PRIMARY KEY (word_id));
    
2. `words` table:
    
    CREATE TABLE words (
    word VARCHAR(255),
    part_of_speech VARCHAR(255),
    created_at DATETIME NOT NULL,
    updated_at DATETIME NOT NULL,
    PRIMARY KEY (word));
    
Note that the `definitions` table has a foreign key relationship with the `words` table on the `word_id` column.</code></pre><figcaption><p><span style="white-space: pre-wrap;">Is this the DDL for the underlying database? </span></p></figcaption></figure><p>I was immediately suspicious of a few odd aspects of the answer, however. While there are indeed two tables, <code>word_id</code> isn&apos;t the primary key of the <code>definitions</code> table nor is <code>word</code> the primary key of the <code>words</code> table. Skepticism towards what an LLM will confidently tell you is a key skill! The good news is that with code, you can usually spot any issues quickly.  </p><p>Perhaps asking low level database questions isn&apos;t necessarily the right level of abstraction required at this point. Afterall, it&apos;s a Rails application, so let me see if I can figure out how to use application code to find a specific word: </p><figure class="kg-card kg-code-card"><pre><code class="language-bash">&gt;&gt;&gt; code.ask(&quot;is there an API call to find a specific word in the application?&quot;)</code></pre><figcaption><p><span style="white-space: pre-wrap;">Can I find a specific word?</span></p></figcaption></figure><p>My RAG-ified Code Llama application produced a detailed answer to the above question, containing some great examples and explanations of the corresponding code. </p><figure class="kg-card kg-code-card"><pre><code class="language-bash">There is no specific API call to find a specific word in 
the application, but there are several ways to retrieve 
words based on their attributes or associations. 

For example, you can use the `Word.find_by` method to find a 
word by its ID, or use the `Word.where` method to find words 
that match certain criteria.
 
You can also use the `include` method to eager load associations, 
such as definitions, for a specific word. For example:

@word = Word.find_by(id: 1).includes(:definitions)

This will retrieve the word with an ID of 1 and include its 
associated definitions in the response.
 
If you need to find a specific word based on multiple 
attributes, such as the word itself and the part of speech, 
you can use the `where` method to create a more complex query. 
 
For example:

@word = Word.where(word: &apos;hello&apos;, part_of_speech: &apos;noun&apos;).first

This will retrieve the first word that matches the word &quot;hello&quot; and 
the part of speech &quot;noun&quot;.
 
You can also use the `order` method to sort the results based 
on a specific attribute, such as the ID. 

For example:

@words = Word.order(:id).page params[:page]
 
This will retrieve the words in ascending order by their IDs 
and paginate the results based on the value of 
the `params[:page]` parameter.
 
Overall, there are several ways to find specific words in an 
application based on various attributes or associations. The best 
approach will depend on your specific use case and requirements.</code></pre><figcaption><p><span style="white-space: pre-wrap;">A detailed response for how to use the </span><code spellcheck="false" style="white-space: pre-wrap;"><span>Word</span></code><span style="white-space: pre-wrap;"> model. </span></p></figcaption></figure><p>This type of detailed response with code examples tailored to my specific code base is incredibly helpful. It&apos;s akin to reading a tutorial specific to an application. I&apos;ve easily spent a few hours asking <code>ChatCode</code> various questions about my simple Rails application and I&apos;d say that 80% of the generated answers are helpful. I suspect that I can extract better answers by providing better questions!  </p><p>I found that asking questions like &quot;how do I create a controller method that removes a word&quot; and &quot;how do I add a new attribute to the word model&quot; yielded fairly competent responses with detailed elaborate steps. In this way, I can see how an AI coding assistant could be helpful when becoming acclimated with a new code base. Rather than spending a lot of context switching time between my IDE and Googling for answers, this workflow is intuitively more productive. </p><h3 id="the-legacy-of-code-as-a-use-case">The legacy of code as a use case</h3><p>Throughout much of my career, I&apos;ve had to work on <em>ancient</em> code. This sort of code is often labeled as <a href="https://en.wikipedia.org/wiki/Legacy_system?ref=thediscoblog.com#:~:text=Legacy%20code%20is%20old%20computer,obsolete%20or%20supporting%20something%20obsolete." rel="noreferrer">legacy code</a>. It&apos;s frequently mystifying and definitely frustrating when getting started. The above use case of leveraging a RAG that combines a specific code base with an LLM, like Code Llama, could certainly make the experience of familiarizing yourself with legacy code <em>way more easy.</em> What&apos;s more, what I&apos;ve demonstrated here is a command line interface. This experience is less optimal than a snazzy UI or even an integrated IDE experience (like <a href="https://github.com/features/copilot?ref=thediscoblog.com" rel="noreferrer">Github&apos;s Copilot</a>). Both UI and IDE integration options are entirely possible nevertheless! </p><h3 id="ai-enhanced-productivity-is-here">AI enhanced productivity is here</h3><p>Throughout my career, I&apos;ve often heard complaints about new technologies. Java was too slow in 1997. Mobile devices weren&apos;t powerful enough to do anything interesting in the early 2000s. And AI isn&apos;t too sophisticated &#x2013; it hallucinates, frequently making mistakes (as I&apos;ve shown above). But the history of technology has clearly shown that <em>useful</em> technologies improve. Today&apos;s LLMs are the worst they&apos;ll be. LLMs can only get better as we learn how to both leverage them and importantly, improve them. </p><p>The skeptics of today are right to point out deficiencies! But remember that the deficiencies of today will be solved tomorrow provided the underlying technology is promising. Don&apos;t overlook AI because of its shortcomings. It&apos;s here to stay and will only become more pervasive and pragmatic! </p><h3 id="want-to-learn-more">Want to learn more? </h3><p>I created a Github repository, aptly named <a href="https://github.com/aglover/windbag?ref=thediscoblog.com" rel="noreferrer">windbag</a>, which has the above code. Moreover, <a href="https://hackernoon.com/a-tutorial-on-how-to-build-your-own-rag-and-how-to-run-it-locally-langchain-ollama-streamlit?ref=thediscoblog.com" rel="noreferrer">Duy Huynh&apos;s article on Hackernoon</a> was extremely helpful in demonstrating the various aspects of RAG. It certainly helped me get started. Furthermore, while I used Code Llama, you can just as easily use <a href="https://www.infoworld.com/article/3712860/retrieval-augmented-generation-step-by-step.amp.html?ref=thediscoblog.com" rel="noreferrer">OpenAI&apos;s ChatGPT LLM</a> using <a href="https://python.langchain.com/docs/integrations/llms/openai?ref=thediscoblog.com" rel="noreferrer">ChainLang&apos;s API</a>. It would be interesting to compare and contrast the two! </p><p>Can you dig it? </p><div class="kg-card kg-signup-card kg-width-regular " data-lexical-signup-form style="background-color: #F0F0F0; display: none;">
            
            <div class="kg-signup-card-content">
                
                <div class="kg-signup-card-text ">
                    <h2 class="kg-signup-card-heading" style="color: #000000;"><span style="white-space: pre-wrap;">Sign up for The Disco Blog!</span></h2>
                    <p class="kg-signup-card-subheading" style="color: #000000;"><span style="white-space: pre-wrap;">It&apos;s all the rage since the 1970s. </span></p>
                    
        <form class="kg-signup-card-form" data-members-form="signup">
            
            <div class="kg-signup-card-fields">
                <input class="kg-signup-card-input" id="email" data-members-email type="email" required="true" placeholder="Your email">
                <button class="kg-signup-card-button kg-style-accent" style="color: #FFFFFF;" type="submit">
                    <span class="kg-signup-card-button-default">Subscribe</span>
                    <span class="kg-signup-card-button-loading"><svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewbox="0 0 24 24">
        <g stroke-linecap="round" stroke-width="2" fill="currentColor" stroke="none" stroke-linejoin="round" class="nc-icon-wrapper">
            <g class="nc-loop-dots-4-24-icon-o">
                <circle cx="4" cy="12" r="3"/>
                <circle cx="12" cy="12" r="3"/>
                <circle cx="20" cy="12" r="3"/>
            </g>
            <style data-cap="butt">
                .nc-loop-dots-4-24-icon-o{--animation-duration:0.8s}
                .nc-loop-dots-4-24-icon-o *{opacity:.4;transform:scale(.75);animation:nc-loop-dots-4-anim var(--animation-duration) infinite}
                .nc-loop-dots-4-24-icon-o :nth-child(1){transform-origin:4px 12px;animation-delay:-.3s;animation-delay:calc(var(--animation-duration)/-2.666)}
                .nc-loop-dots-4-24-icon-o :nth-child(2){transform-origin:12px 12px;animation-delay:-.15s;animation-delay:calc(var(--animation-duration)/-5.333)}
                .nc-loop-dots-4-24-icon-o :nth-child(3){transform-origin:20px 12px}
                @keyframes nc-loop-dots-4-anim{0%,100%{opacity:.4;transform:scale(.75)}50%{opacity:1;transform:scale(1)}}
            </style>
        </g>
    </svg></span>
                </button>
            </div>
            <div class="kg-signup-card-success" style="color: #000000;">
                Email sent! Check your inbox to complete your signup.
            </div>
            <div class="kg-signup-card-error" style="color: #000000;" data-members-error></div>
        </form>
        
                    <p class="kg-signup-card-disclaimer" style="color: #000000;"><span style="white-space: pre-wrap;">No spam. Unsubscribe anytime.</span></p>
                </div>
            </div>
        </div>]]></content:encoded></item><item><title><![CDATA[Pairing with my good friend Code Llama]]></title><description><![CDATA[AI won't take your job, but someone who's leveraging AI will. Besides, it's easy getting started with AI, especially if you're looking for a coding buddy. ]]></description><link>https://www.thediscoblog.com/pairing-with-my-friend-codelama/</link><guid isPermaLink="false">65d39c7436686c000162c777</guid><category><![CDATA[AI]]></category><category><![CDATA[python]]></category><category><![CDATA[pairing]]></category><dc:creator><![CDATA[Andrew Glover]]></dc:creator><pubDate>Mon, 26 Feb 2024 23:07:38 GMT</pubDate><media:content url="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2024/02/Coding-519161626_5000x3337.jpeg" medium="image"/><content:encoded><![CDATA[<img src="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2024/02/Coding-519161626_5000x3337.jpeg" alt="Pairing with my good friend Code Llama"><p>Because it&#x2019;s my bag, I&#x2019;ve been diving deep into the incredibly exciting and rapidly evolving world of AI. The innovations in the open source arena are astonishing; indeed, you can now run a diverse set of LLMs from the convenience of your laptop. If you are willing to roll up your sleeves, there are plenty of OSS models and frameworks that cost you only the time it takes to set things up.&#xA0;</p><p>One popular tool for running open source LLMs locally is <a href="https://github.com/ollama/ollama?ref=thediscoblog.com" rel="noreferrer">Ollama</a>. I started using it recently and I&#x2019;ve been blown away with how useful, productive, and enlightening common workflows can become if you take the time to explore, understand, and ultimately leverage AI.&#xA0;With Ollama, you can run various LLMs such as Meta&apos;s <a href="https://llama.meta.com/?ref=thediscoblog.com" rel="noreferrer">Llama2</a>, <a href="https://docs.mistral.ai/?ref=thediscoblog.com" rel="noreferrer">Mistral</a>, <a href="https://llava-vl.github.io/?ref=thediscoblog.com" rel="noreferrer">LLaVA</a>, and my new favorite, Meta&apos;s <a href="https://about.fb.com/news/2023/08/code-llama-ai-for-coding/?ref=thediscoblog.com" rel="noreferrer">Code Llama</a>. </p><h3 id="metas-code-llama">Meta&apos;s Code Llama</h3><p>Code Llama is an LLM that can use text prompts to generate and discuss code. With Code Llama, you can generate Python, Java, Typescript, and more. As Meta&apos;s documentation for Code Llama states: </p><blockquote>It has the potential to make workflows faster and more efficient for developers and lower the barrier to entry for people who are learning to code.&#xA0;</blockquote><p>Indeed, the evolution of AI enhanced productivity will come in the form of <em>boosted</em> workflows. These workflows will have fewer speed bumps resulting from context switching and accordingly increase productive capacity. </p><p>I saw AI&apos;s boosting of productivity potential firsthand recently. It&#x2019;s been <a href="https://cheetahtemplate.sourceforge.net/learn.html?ref=thediscoblog.com" rel="noreferrer">quite a long time since I&#x2019;ve coded in Python</a> (circa 2005!) and in an effort to refresh my memory on the finer aspects of Python, I decided to <a href="https://web.archive.org/web/20151231140631/http://thediscoblog.com/blog/2014/04/01/schooled-in-ping-pong/" rel="noreferrer">play some ping-pong</a>. But in this case, I decided that I&apos;d use Code Llama as my coding partner. </p><p>After <a href="https://ollama.com/download?ref=thediscoblog.com" rel="noreferrer">installing Ollama</a>, you need to download the Code Llama LLM like so:</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">$ ollama run codellama</code></pre><figcaption><p><span style="white-space: pre-wrap;">Installing the Code Llama LLM is simple!</span></p></figcaption></figure><p>After downloading the LLM, which could take a bit, you&#x2019;ll be presented with an in-terminal chat-like interface. You prompt Code Llama and it&apos;ll magically respond with (mostly) cogent answers. </p><h3 id="ping-pong-pair-programming">Ping-pong pair programming</h3><p>In <a href="https://web.archive.org/web/20151231140631/http://thediscoblog.com/blog/2014/04/01/schooled-in-ping-pong/" rel="noreferrer">ping-pong</a>, your partner writes a failing test case. Your job is to write<em> the least amount of code</em> that makes that failing test case pass. Once you&#x2019;ve done that, you then write a failing test case and your partner in turn writes the least amount of code to make your test case pass. You go back and forth and in the process of playing ping-pong, you&apos;ll undoubtedly learn new techniques, styles, and ways of approaching problem solving.&#xA0;Over the years, I&apos;ve learned valuable lessons when pairing with folks! Ping pong has certainly made me a better programmer. I&apos;ve even employed ping-pong in the context of interviewing. </p><p>I figured I&#x2019;d get right into AI pair programming by building a simple stack implementation in Python. Stacks commonly have <code>push</code> and <code>pop</code> methods which follow a <em>last in, first out </em>(or LIFO) algorithm. <code>push</code> adds items onto the stack and <code>pop</code> removes the last item added.&#xA0;I was also aware that out-of-the-box, Python comes with a unit testing framework dubbed <a href="https://docs.python.org/3/library/unittest.html?ref=thediscoblog.com" rel="noreferrer">unittest</a> (akin to Java&apos;s <a href="https://junit.org/junit5/?ref=thediscoblog.com" rel="noreferrer">JUnit</a>). </p><p>Kicking things off, I began with the following prompt:</p><figure class="kg-card kg-code-card"><pre><code>&gt;&gt;&gt;&gt; write me a test using python unittest for a pop method of a stack</code></pre><figcaption><p><span style="white-space: pre-wrap;">Prompting Code Lama via Ollama.</span></p></figcaption></figure><p>As requested, Code Llama promptly returned some code using Python&#x2019;s unittest framework: </p><figure class="kg-card kg-code-card"><pre><code class="language-Python">import unittest
from collections import deque

class TestStack(unittest.TestCase):
    def setUp(self) -&gt; None:
        self.stack = deque()

    def test_pop(self) -&gt; None:
        self.assertEqual(self.stack.pop(), None)
        for i in range(5):
            self.stack.append(i)
        self.assertEqual(self.stack.pop(), 4)
        self.assertEqual(self.stack.pop(), 3)
        self.assertEqual(self.stack.pop(), 2)
        self.assertEqual(self.stack.pop(), 1)
        with self.assertRaises(IndexError):
            self.stack.pop()
</code></pre><figcaption><p><span style="white-space: pre-wrap;">Code Llama&apos;s answer to my &quot;create a test for me in Python&quot; prompt.</span></p></figcaption></figure><p>Code Llama additionally described the usage of unittest&#x2019;s <code>setup</code> method as well as the usage of <code>assertEqual</code> and <code>assertRaises</code>.&#xA0;</p><p>Of course, I was provided with way more implementation code than what I really wanted. Nevertheless, the example provided was enough to get me started as the example code quickly got me up to speed on unittest and also informed me that Python has an implementation of a stack data structure via the <code>deque</code> object.&#xA0;</p><p>I copied the code into a file dubbed <code>test_stack.py</code> and then ran it. Unfortunately, nothing happened. Then I remembered from my Python days of yore that I needed the equivalent of a<code>main</code> method. But I had forgotten the syntax. No problem, I just asked Code Llama and it told me to insert this:</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">if __name__ == &apos;__main__&apos;:
   unittest.main()
</code></pre><figcaption><p><span style="white-space: pre-wrap;">A </span><code spellcheck="false" style="white-space: pre-wrap;"><span>main</span></code><span style="white-space: pre-wrap;"> method in Python.</span></p></figcaption></figure><p>I added the above code and reran the test. Unsurprisingly, the test errors out as the first assert of <code>self.stack.pop()</code> raises an <code>IndexError</code> because the underlying list is initially empty.  You can see that the test case, in fact, verifies this case correctly in the last part of the <code>test_pop</code> method.&#xA0;</p><h3 id="testing-testing-1-2-3">Testing, testing, 1, 2, 3</h3><p>As it turns out, AI (inadvertently) wrote me a failing test! Now all I needed to do was make the test pass with as little code as possible. I figured that I would trim down the test case and simply test for an empty <code>pop</code> yielding <code>None</code>.&#xA0;</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">import unittest
from collections import deque

class TestStack(unittest.TestCase):
    def setUp(self) -&gt; None:
        self.stack = deque()

    def test_pop(self) -&gt; None:
        self.assertEqual(self.stack.pop(), None)

if __name__ == &apos;__main__&apos;:
    unittest.main()
</code></pre><figcaption><p><span style="white-space: pre-wrap;">Trimmed down test case.</span></p></figcaption></figure><p>Next, I wanted to write my own stack implementation and remove <code>deque</code>. Looking at the <code>TestStack</code> class, I remembered some important aspects of coding in Python: methods are defined with <code>def</code>, whitespace is a delimiter, and don&apos;t forget about colons. But I couldn&apos;t remember exactly how imports work. No problem! All I had to do was ask Code Llama:</p><figure class="kg-card kg-code-card"><pre><code class="language-Bash">&gt;&gt;&gt; how do I import a python class in my local directory?</code></pre><figcaption><p><span style="white-space: pre-wrap;">Prompting my buddy for some answers.</span></p></figcaption></figure><p>I was informed via a nice description that I could create a file, using snake case, and inside of the file define a class. Then in another file, I can import that class using the <code>from</code> file <code>import</code> class syntax. Excellent! Next, I needed to define a minimal stack &#x2013; ideally, with only a <code>pop</code> method. Might as well ask my groovy fried Code Llama how to define a custom stack in Python: </p><figure class="kg-card kg-code-card"><pre><code class="language-Bash">&gt;&gt;&gt; how do I implement a custom stack class in python?</code></pre><figcaption><p><span style="white-space: pre-wrap;">I could use this prompt as a Google search, but then I&apos;d have to click on the webpage I think is appropriate. </span></p></figcaption></figure><p>Code Llama dutifully spat out the following code: </p><figure class="kg-card kg-code-card"><pre><code class="language-Python">class CustomStack:
    def __init__(self):
        self.stack = []

    def push(self, item):
        self.stack.append(item)

    def pop(self):
        if len(self.stack) == 0:
            raise IndexError(&quot;Stack is empty&quot;)
        return self.stack.pop()

    def peek(self):
        if len(self.stack) == 0:
            raise IndexError(&quot;Stack is empty&quot;)
        return self.stack[-1]

    def size(self):
        return len(self.stack)
</code></pre><figcaption><p><span style="white-space: pre-wrap;">A handy stack generated by Code Llama.</span></p></figcaption></figure><p>Not only did I get what appears to be working <code>pop</code> and <code>push</code> methods, but I also got a <code>peek</code> and <code>size</code> method to boot! This is obviously more code than I needed; accordingly, I simplified the code with the intent of making the aforementioned test pass:</p><figure class="kg-card kg-code-card"><pre><code class="language-Python">class Stack:
    def __init__(self):
        self.stack = []

    def pop(self):
        None

</code></pre><figcaption><p><span style="white-space: pre-wrap;">The least amount of code required to get my failing test to pass.</span></p></figcaption></figure><p>When I rerun the test case, things pass! Of course, I&apos;ve merely written the <em>least amount of code necessary</em> to make the test pass. It&apos;s now my friend Code Llama&apos;s turn to write a failing test case. This wasn&apos;t too hard of a prompt to fashion: </p><figure class="kg-card kg-code-card"><pre><code class="language- bash">&gt;&gt;&gt; write me a test that fails for the pop method in this class: class Stack:
...     def pop(self):
...         return None</code></pre><figcaption><p><span style="white-space: pre-wrap;">Prompting Code Llama to create a failing test case.</span></p></figcaption></figure><p>Impressively, Code Llama did exactly what I wanted. It generated a simple test case that will force a failure: </p><figure class="kg-card kg-code-card"><pre><code class="language-Python">import unittest
from my_stack import Stack

class TestStack(unittest.TestCase):
    def test_pop(self):
        stack = Stack()
        self.assertEqual(stack.pop(), 1)</code></pre><figcaption><p><span style="white-space: pre-wrap;">Thank you Code Llama for an easy test case to expose my simple code!</span></p></figcaption></figure><p>I added another test case to my existing <code>TestStack</code>, ran it, and sure enough, I&apos;ve got a failing test. It&apos;s time to make it pass. My brutally simple <code>pop</code> method needs to do some real logic as opposed to returning <code>None</code>. </p><h3 id="cant-pop-without-a-push">Can&apos;t pop without a push</h3><p>Satisfying my friend Code Llama&apos;s new test case means I need to flesh out a <code>push</code> method so I can test popping a value. </p><figure class="kg-card kg-code-card"><pre><code class="language-Python">class Stack:
    def __init__(self):
        self.stack = []

    def pop(self):
        if len(self.stack) &gt; 0:
            return self.stack[-1]
        else:
            return None

    def push(self, value):
        self.stack.append(value)</code></pre><figcaption><p><span style="white-space: pre-wrap;">A refactored </span><code spellcheck="false" style="white-space: pre-wrap;"><span>Stack</span></code><span style="white-space: pre-wrap;"> class with a newly defined </span><code spellcheck="false" style="white-space: pre-wrap;"><span>push</span></code><span style="white-space: pre-wrap;"> method.</span></p></figcaption></figure><p>My <code>Stack</code> now has a <code>push</code> method that adds an item to an internal list (<code>self.stack</code>) and the <code>pop</code> method simply returns the top item or <code>None</code> if the list is empty. </p><p>I&apos;ve refactored my test case slightly and have two passing test cases now. </p><figure class="kg-card kg-code-card"><pre><code>import unittest
from stack import Stack

class TestStack(unittest.TestCase):
    def setUp(self):
        self.stack = Stack()

    def test_pop(self):
        self.assertEqual(self.stack.pop(), None)

    def test_pop_again(self):
        self.stack.push(1)
        self.assertEqual(self.stack.pop(), 1)

if __name__ == &apos;__main__&apos;:
    unittest.main()
</code></pre><figcaption><p><span style="white-space: pre-wrap;">A valid test case fashioned with the help of my good friend Code Llama.</span></p></figcaption></figure><p>After a few back and forth interactions with Code Llama and my Python confidence is increased. I can see how this technique would work equally well with a language I wasn&apos;t familiar with too. </p><h3 id="what-a-match">What a match!</h3><p>The game of ping-pong need not go on forever. In fact, a few rounds is often enough to get into the flow of things and learn a little from your partner. In my case, I had a lot of fun getting to know how Code Llama approaches problem solving and along the way, it helped re-acquainted me with Python.</p><p>I dig ping-pong pairing with an AI and will continue to do it going forward. As with all things AI related, prompting is a crucial skill as well as having a keen eye for code correctness. LLMs will confidently give you an incorrect answer (i.e. code that doesn&apos;t work). </p><p>In my case, I was using a terminal; nevertheless, there are other in-IDE options ranging from <a href="https://github.com/features/copilot?ref=thediscoblog.com" rel="noreferrer">GitHub&apos;s Copilot</a> to other innovative tools. You can also take advantage of <a href="https://docs.codegpt.co/docs/tutorial-ai-providers/ollama?ref=thediscoblog.com" rel="noreferrer">Ollama and Code Llama via CodeGPT</a> and have yourself some sweet VSCode integration. </p><p>Can you dig it, man? </p><div class="kg-card kg-signup-card kg-width-regular " data-lexical-signup-form style="background-color: #F0F0F0; display: none;">
            
            <div class="kg-signup-card-content">
                
                <div class="kg-signup-card-text ">
                    <h2 class="kg-signup-card-heading" style="color: #000000;"><span style="white-space: pre-wrap;">Sign up for The Disco Blog! </span></h2>
                    <p class="kg-signup-card-subheading" style="color: #000000;"><span style="white-space: pre-wrap;">My friend Code Llama thinks it&apos;s a groovy idea. </span></p>
                    
        <form class="kg-signup-card-form" data-members-form="signup">
            
            <div class="kg-signup-card-fields">
                <input class="kg-signup-card-input" id="email" data-members-email type="email" required="true" placeholder="Your email">
                <button class="kg-signup-card-button kg-style-accent" style="color: #FFFFFF;" type="submit">
                    <span class="kg-signup-card-button-default">Subscribe</span>
                    <span class="kg-signup-card-button-loading"><svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewbox="0 0 24 24">
        <g stroke-linecap="round" stroke-width="2" fill="currentColor" stroke="none" stroke-linejoin="round" class="nc-icon-wrapper">
            <g class="nc-loop-dots-4-24-icon-o">
                <circle cx="4" cy="12" r="3"/>
                <circle cx="12" cy="12" r="3"/>
                <circle cx="20" cy="12" r="3"/>
            </g>
            <style data-cap="butt">
                .nc-loop-dots-4-24-icon-o{--animation-duration:0.8s}
                .nc-loop-dots-4-24-icon-o *{opacity:.4;transform:scale(.75);animation:nc-loop-dots-4-anim var(--animation-duration) infinite}
                .nc-loop-dots-4-24-icon-o :nth-child(1){transform-origin:4px 12px;animation-delay:-.3s;animation-delay:calc(var(--animation-duration)/-2.666)}
                .nc-loop-dots-4-24-icon-o :nth-child(2){transform-origin:12px 12px;animation-delay:-.15s;animation-delay:calc(var(--animation-duration)/-5.333)}
                .nc-loop-dots-4-24-icon-o :nth-child(3){transform-origin:20px 12px}
                @keyframes nc-loop-dots-4-anim{0%,100%{opacity:.4;transform:scale(.75)}50%{opacity:1;transform:scale(1)}}
            </style>
        </g>
    </svg></span>
                </button>
            </div>
            <div class="kg-signup-card-success" style="color: #000000;">
                Email sent! Check your inbox to complete your signup.
            </div>
            <div class="kg-signup-card-error" style="color: #000000;" data-members-error></div>
        </form>
        
                    <p class="kg-signup-card-disclaimer" style="color: #000000;"><span style="white-space: pre-wrap;">No spam. Unsubscribe anytime.</span></p>
                </div>
            </div>
        </div>]]></content:encoded></item><item><title><![CDATA[Perverse incentives and DORA metrics]]></title><description><![CDATA[“Move fast and break things” isn’t what anyone wants. Plan for complex systems to break. Reframe the rallying cry to “move fast with reliability” and stop wasting time measuring failure.  ]]></description><link>https://www.thediscoblog.com/perverse-incentives-and-dora-metrics/</link><guid isPermaLink="false">65d16d6a36686c000162c636</guid><category><![CDATA[leadership]]></category><category><![CDATA[KPIs]]></category><category><![CDATA[productivity]]></category><category><![CDATA[metrics]]></category><dc:creator><![CDATA[Andrew Glover]]></dc:creator><pubDate>Mon, 19 Feb 2024 16:29:49 GMT</pubDate><media:content url="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2024/02/iStock_000005197015Small.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2024/02/iStock_000005197015Small.jpg" alt="Perverse incentives and DORA metrics"><p>I&#x2019;ve never been a fan of <a href="https://cloud.google.com/blog/products/devops-sre/dora-2022-accelerate-state-of-devops-report-now-out?ref=thediscoblog.com" rel="noreferrer">DORA metrics</a>. I appreciate the copasetic thought leadership behind them and their intended purpose of <a href="https://medium.com/booking-com-development/dora-metrics-at-work-46c835a86a89?ref=thediscoblog.com" rel="noreferrer">tracking a company&apos;s ability</a> to <em>reliably move fast</em>. But it&#x2019;s always struck me as odd that one of the metrics is <a href="https://www.jit.io/blog/calculate-change-failure-rate?ref=thediscoblog.com" rel="noreferrer">Change Failure Rate</a>. This is an unnecessary metric that works against the other metrics. </p><p>If you track Change Failure Rate, it really means that you&#x2019;re afraid of change. Moreover, if you&#x2019;re afraid of change, you can&#x2019;t move fast.&#xA0;If you can&apos;t move fast, your business will lose to one which can. </p><h3 id="what-are-dora-metrics">What are DORA metrics?</h3><p>Briefly, <a href="https://dora.dev/?ref=thediscoblog.com" rel="noreferrer">DORA metrics</a> are four key performance indicators (KPIs) that attempt to measure the effectiveness of a software delivery process. Those KPIs are:</p><ol><li>Deployment Frequency: how often an organization deploys code into production.</li><li>Lead Time for Changes: the average time it takes for a code change to go from commit into production.</li><li>Change Failure Rate: the percentage of deployments that fail.</li><li>Mean Time to Restore: the average time it takes to recover from a failed deployment. This is often abbreviated as MTTR. </li></ol><p>The folks behind DORA metrics are respected luminaries and their recommendations come from an incredibly large set of studies and surveys from a diverse audience of global companies. Sadly, while their recommendations are quite popular, it&apos;s my experience that only one of the four metrics really matters. What&apos;s more, Change Failure Rate creates a tension with Deployment Frequency and results in a perverse incentive that ultimately can reduce Lead Time for Changes.&#xA0;</p><h3 id="competitive-advantages">Competitive advantages</h3><p>Speed is the primary <a href="https://www.entrepreneur.com/growing-a-business/3-companies-using-speed-as-a-competitive-advantage/253372?ref=thediscoblog.com" rel="noreferrer">competitive advantage</a> a company has in any market. Therefore, the only metric that matters to all parties in a business is Lead Time for Changes (commonly shortened to Lead Time). This metric is broadly the time it takes for a business initiative to reach customers. </p><p>Lead Time for Changes can be reduced by making it fast and safe to deploy (Deployment Frequency) along with having the safeguards in place to rapidly detect and fix any issues that arise (MTTR). When Deployment Frequency is high and MTTR is low, the business is happy. </p><p>MTTR, however, is easier said than done. Indeed, if you want to move fast with some semblance of reliability, you&#x2019;ll need to invest in three disciplines: observability, failure domains, and fast fixes via rollbacks or via rolling forward. If you can detect issues quickly (often called Mean Time to Detect), have thought through how to limit the blast radius of errors (like avoiding global changes), and can rapidly fix those issues by either reverting the change or deploying a fix, you&#x2019;ll have agency with MTTR.&#xA0;If you want to be groovy and move fast, you need confidence that you can rapidly resolve issues. Consequently, you have to know when an issue arises, have the ability to limit its scope, and quickly rectify it. </p><h3 id="fear-based-metrics">Fear based metrics</h3><p>If you&apos;re tracking Change Failure Rate, you&#x2019;re afraid. Tracking this metric means your MTTD and MTTR are high. It means you have poor observability. It probably also means you lack automation and the necessary machinery to fix things without a lot of fire fighting. It means your business panics when things break because there&apos;s a lack of confidence engineering can fix the issue quickly. It means the business doesn&apos;t trust engineering.&#xA0;</p><p>Here&#x2019;s the thing: complex systems break. They break all the time too. All high speed companies with massive scale (like Google, Netflix, Meta) have figured out a way to find and fix those issues quickly. Indeed, failure is inevitable in complex distributed systems; therefore, you can&#x2019;t reduce all failures. It&#x2019;s far more efficient to accept failures as an outcome of moving fast and put resources towards figuring out how to deal with them <em>quickly</em>.&#xA0;Tracking Change Failure Rate is a distraction. </p><h3 id="when-the-measure-becomes-a-target">When the measure becomes a target</h3><p>Tracking Change Failure Rate is a perverse incentive. What&apos;s more, the word &#x201C;failure&#x201D; is massively negative. There are few outcomes in the context of software processes that end well when the word failure is used. No one wants to have their name associated with a metric that uses the word failure.&#xA0;</p><p>If you judge people for failing, it&#x2019;s a certainty that they&#x2019;ll game the system to avoid the stigma associated with the metric. In this case, they&#x2019;ll hide failures or slow  deployment velocity so as to limit the growth of this nefarious metric. This is <a href="https://en.wikipedia.org/wiki/Goodhart%27s_law?ref=thediscoblog.com" rel="noreferrer">Goodhart&apos;s law</a> in action, baby.  The metric ceases to be a good measure because as soon as folks are afraid to report failures, you don&#x2019;t learn. And you don&apos;t move fast. </p><p>Failures happen. Great engineering cultures celebrate and learn from failures while awful cultures punish for failures. Focus on what matters: moving fast with reliability. </p><h3 id="measure-what-really-matters">Measure what really matters</h3><p>DORA metrics represent a framework for optimizing a company&apos;s software delivery process and boosting its overall performance. Every business wants to move fast as it&apos;s the only way to win. Accordingly, the only metric that matters is Lead Time. </p><p>If you want to understand this metric, ask the business if it feels Engineering is moving fast enough. It&apos;s every business leader&apos;s bag to want to move faster, so listen carefully to the answer. If the answer is a frustrating &quot;no&quot; then you have a lot of work to do. You&apos;ll need to improve things in short order if want to survive. If the answer is a &quot;yes&quot; then you might want to also worry. Complacency leads to stagnation. Anything in between is the sweet spot for increasing overall engineering transparency and working to ensure that your company can reliably move fast.</p><p>Measure what matters by thinking through incentives. Moreover, acknowledge <em>and accept</em> that failures will occur. Invest in detecting failures and rapidly rectifying them. What&apos;s more, invest in learning from failures. Get behind the rallying cry of &#x201C;move fast with reliability&#x201D; and stop wasting time measuring failure.</p><p>Can you dig it? </p><div class="kg-card kg-signup-card kg-width-regular " data-lexical-signup-form style="background-color: #F0F0F0; display: none;">
            
            <div class="kg-signup-card-content">
                
                <div class="kg-signup-card-text ">
                    <h2 class="kg-signup-card-heading" style="color: #000000;"><span style="white-space: pre-wrap;">Be hip and sign up for The Disco Blog. </span></h2>
                    <p class="kg-signup-card-subheading" style="color: #000000;"><span style="white-space: pre-wrap;">It&apos;s still cool to dress in leisure suits. </span></p>
                    
        <form class="kg-signup-card-form" data-members-form="signup">
            
            <div class="kg-signup-card-fields">
                <input class="kg-signup-card-input" id="email" data-members-email type="email" required="true" placeholder="Your email">
                <button class="kg-signup-card-button kg-style-accent" style="color: #FFFFFF;" type="submit">
                    <span class="kg-signup-card-button-default">Subscribe</span>
                    <span class="kg-signup-card-button-loading"><svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewbox="0 0 24 24">
        <g stroke-linecap="round" stroke-width="2" fill="currentColor" stroke="none" stroke-linejoin="round" class="nc-icon-wrapper">
            <g class="nc-loop-dots-4-24-icon-o">
                <circle cx="4" cy="12" r="3"/>
                <circle cx="12" cy="12" r="3"/>
                <circle cx="20" cy="12" r="3"/>
            </g>
            <style data-cap="butt">
                .nc-loop-dots-4-24-icon-o{--animation-duration:0.8s}
                .nc-loop-dots-4-24-icon-o *{opacity:.4;transform:scale(.75);animation:nc-loop-dots-4-anim var(--animation-duration) infinite}
                .nc-loop-dots-4-24-icon-o :nth-child(1){transform-origin:4px 12px;animation-delay:-.3s;animation-delay:calc(var(--animation-duration)/-2.666)}
                .nc-loop-dots-4-24-icon-o :nth-child(2){transform-origin:12px 12px;animation-delay:-.15s;animation-delay:calc(var(--animation-duration)/-5.333)}
                .nc-loop-dots-4-24-icon-o :nth-child(3){transform-origin:20px 12px}
                @keyframes nc-loop-dots-4-anim{0%,100%{opacity:.4;transform:scale(.75)}50%{opacity:1;transform:scale(1)}}
            </style>
        </g>
    </svg></span>
                </button>
            </div>
            <div class="kg-signup-card-success" style="color: #000000;">
                Email sent! Check your inbox to complete your signup.
            </div>
            <div class="kg-signup-card-error" style="color: #000000;" data-members-error></div>
        </form>
        
                    <p class="kg-signup-card-disclaimer" style="color: #000000;"><span style="white-space: pre-wrap;">No spam. Unsubscribe anytime.</span></p>
                </div>
            </div>
        </div>]]></content:encoded></item><item><title><![CDATA[The value of values]]></title><description><![CDATA[Values represent what you consider important and desirable in life. If you want to find meaning and purpose, then know your values and be true to them.  ]]></description><link>https://www.thediscoblog.com/the-value-of-values/</link><guid isPermaLink="false">65c4517061ffbb000143b403</guid><category><![CDATA[leadership]]></category><dc:creator><![CDATA[Andrew Glover]]></dc:creator><pubDate>Thu, 08 Feb 2024 04:23:21 GMT</pubDate><media:content url="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2024/02/Business-competition-concept-511852007_4765x2091--1-.jpeg" medium="image"/><content:encoded><![CDATA[<img src="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2024/02/Business-competition-concept-511852007_4765x2091--1-.jpeg" alt="The value of values"><p>I never really thought about values for much of my career. I probably assumed that everyone more or less shared my values. It wasn&#x2019;t until a few years ago that I started to reflect on my values and in doing so, I&#x2019;ve learned some important lessons. First, most people share the same values, but we often disagree on how values manifest themselves. We also tend to disagree on their relative priority. Second, it&#x2019;s crucial to recognize that a disagreement arising from conflicting values is practically impossible to rectify.&#xA0;</p><h2 id="what-do-you-mean-by-values">What do you mean by values?&#xA0;</h2><p>Values are your guiding principles. They are the core beliefs that shape your decisions and actions. Values represent what you consider important and desirable in life. Indeed, values give your life meaning and purpose.&#xA0;</p><p>When faced with choices, big or small, values serve as internal compasses. They help you navigate complex situations by suggesting paths that align with what you believe is right, copasetic, or important. Aligning your decisions with your values often leads to a greater sense of satisfaction, fulfillment, and general grooviness.&#xA0;</p><p>As a leader, it&#x2019;s important to take stock of your values from time-to-time as they influence the decisions you make and the consequent actions you take. If someone has ever asked you the question &#x201C;what kind of leader are you?&#x201D; your answer is likely in terms of values. Your values shape your identity and understanding them helps you better understand you.</p><h2 id="words-aren%E2%80%99t-the-same-as-actions">Words aren&#x2019;t the same as actions</h2><p>If you spend time reflecting on the values that you value, you&#x2019;ll likely come up with a series of nouns such as <em>accountability</em>, <em>love</em>, <em>loyalty</em>, or <em>empathy</em>, for instance. These words are meaningful, but it&#x2019;s important to recognize they aren&#x2019;t unique to you. Most people value love. </p><p>What&#x2019;s more, the context of a particular value can be nuanced: loyalty to family could be different than loyalty to a company. Consequently, when you think through your values, it&#x2019;s helpful to articulate <em>how</em> those values manifest themselves through your actions or how you judge those values in other people&#x2019;s actions. Moreover, it&#x2019;s helpful to prioritize your values, especially if some values are in tension with each other. Empathy and accountability are a few of my values. Those two can be at tension with each other; accordingly, I place a higher priority on accountability.&#xA0;</p><p>If you haven&apos;t done so before, take some time and list your values. Describe what they mean to you. Order your values by importance. Periodically revisit your values too. My values have certainly evolved over time, especially their relative order.&#xA0;</p><h2 id="a-values-clash">A values clash</h2><p>Most companies espouse a set of values as part of their corporate culture. Ideally, you&#x2019;ve aligned with your particular company&#x2019;s values; indeed, it&#x2019;s probably a driving force to work for the company. When you and your company share values, things are copasetic. Nevertheless, there are occasions where values clash. Specifically, the values of your company, or more realistically, your boss, occasionally will fall into conflict with your values. This is why it&#x2019;s important to know your values and your expectations of how those values manifest themselves.&#xA0;</p><p>If you find yourself unhappy or frustrated with corporate decisions, direction, or leadership, take an accounting of the values you see reflected. For instance, if you value transparency, but struggle with the lack of it from your company, this is likely a cause of tension and unhappiness. Unfortunately, as I&#x2019;ve learned over time, this tension is rarely solvable. That is, when your values clash with the corporation, the corporation usually wins.&#xA0;</p><p>Values define <em>you</em>. If you value curiosity and find yourself working for a manager who&apos;s a know-it-all, chances are you won&#x2019;t be happy. If you value fairness but feel the decisions corporate leadership makes are explicitly unfair, you&#x2019;re going to feel conflicted.&#xA0;You can certainly try to change the values of those around you, but recognize that it&apos;s an uphill battle. Afterall, their values define <em>them</em>. </p><p>Values represent what you consider important and desirable in life. If you want to find meaning and purpose, then know your values and be true to them.&#xA0;&#xA0;</p><div class="kg-card kg-signup-card kg-width-regular " data-lexical-signup-form style="background-color: #F0F0F0; display: none;">
            
            <div class="kg-signup-card-content">
                
                <div class="kg-signup-card-text ">
                    <h2 class="kg-signup-card-heading" style="color: #000000;"><span style="white-space: pre-wrap;">Be hip. Sign up for The Disco Blog!</span></h2>
                    <p class="kg-signup-card-subheading" style="color: #000000;"><span style="white-space: pre-wrap;">It was cool then and still is now, baby. </span></p>
                    
        <form class="kg-signup-card-form" data-members-form="signup">
            
            <div class="kg-signup-card-fields">
                <input class="kg-signup-card-input" id="email" data-members-email type="email" required="true" placeholder="Your email">
                <button class="kg-signup-card-button kg-style-accent" style="color: #FFFFFF;" type="submit">
                    <span class="kg-signup-card-button-default">Subscribe</span>
                    <span class="kg-signup-card-button-loading"><svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewbox="0 0 24 24">
        <g stroke-linecap="round" stroke-width="2" fill="currentColor" stroke="none" stroke-linejoin="round" class="nc-icon-wrapper">
            <g class="nc-loop-dots-4-24-icon-o">
                <circle cx="4" cy="12" r="3"/>
                <circle cx="12" cy="12" r="3"/>
                <circle cx="20" cy="12" r="3"/>
            </g>
            <style data-cap="butt">
                .nc-loop-dots-4-24-icon-o{--animation-duration:0.8s}
                .nc-loop-dots-4-24-icon-o *{opacity:.4;transform:scale(.75);animation:nc-loop-dots-4-anim var(--animation-duration) infinite}
                .nc-loop-dots-4-24-icon-o :nth-child(1){transform-origin:4px 12px;animation-delay:-.3s;animation-delay:calc(var(--animation-duration)/-2.666)}
                .nc-loop-dots-4-24-icon-o :nth-child(2){transform-origin:12px 12px;animation-delay:-.15s;animation-delay:calc(var(--animation-duration)/-5.333)}
                .nc-loop-dots-4-24-icon-o :nth-child(3){transform-origin:20px 12px}
                @keyframes nc-loop-dots-4-anim{0%,100%{opacity:.4;transform:scale(.75)}50%{opacity:1;transform:scale(1)}}
            </style>
        </g>
    </svg></span>
                </button>
            </div>
            <div class="kg-signup-card-success" style="color: #000000;">
                Email sent! Check your inbox to complete your signup.
            </div>
            <div class="kg-signup-card-error" style="color: #000000;" data-members-error></div>
        </form>
        
                    <p class="kg-signup-card-disclaimer" style="color: #000000;"><span style="white-space: pre-wrap;">No spam. Unsubscribe anytime.</span></p>
                </div>
            </div>
        </div>]]></content:encoded></item><item><title><![CDATA[Supercharging PostgreSQL with PgBouncer]]></title><description><![CDATA[PgBouncer enables you to both vertically scale Postgres and more efficiently take advantage of Postgres horizontal scaling.]]></description><link>https://www.thediscoblog.com/supercharging-postgres-with-pgbouncer/</link><guid isPermaLink="false">65aca06c302ab9000148bbee</guid><category><![CDATA[postgres]]></category><category><![CDATA[pgbouncer]]></category><category><![CDATA[scaling]]></category><category><![CDATA[read replica]]></category><dc:creator><![CDATA[Andrew Glover]]></dc:creator><pubDate>Thu, 25 Jan 2024 17:11:39 GMT</pubDate><media:content url="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2024/01/361621_A-dancing-elephant-_xl-1024-v1-0.png" medium="image"/><content:encoded><![CDATA[<img src="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2024/01/361621_A-dancing-elephant-_xl-1024-v1-0.png" alt="Supercharging PostgreSQL with PgBouncer"><p>As <a href="https://www.thediscoblog.com/horizontally-scaling-postgres-with-read-replicas/" rel="noreferrer">I&#x2019;ve written about previously</a>, Postgres is an incredibly rock solid database. What&#x2019;s more, when the good vibes go your way and you need to scale Postgres, its august ecosystem answers the call quite nicely. In particular, <a href="https://www.pgbouncer.org/?ref=thediscoblog.com" rel="noreferrer">PgBouncer</a> enables you to both vertically scale Postgres and more efficiently take advantage of Postgres horizontal scaling.&#xA0;</p><p>PgBouncer is primarily a connection pooler and router for Postgres. It acts as middleware between applications and one or more Postgres instances, managing both routes to individual databases as well as a pool of connections. Leveraging PgBouncer can significantly improve the performance of an application by reducing the overhead of opening and closing new connections to the database as well as enabling you to easily route traffic to multiple Postgres instances (such as read replicas, for instance).&#xA0;</p><p>With PgBouncer managing database connections, you free up valuable resources for your database instances thereby enabling you to more efficiently vertically scale. Moreover, PgBouncer&apos;s routing features make it copacetically easily to route queries to multiple instances. In this way, PgBouncer is conceptually akin to a load balancer enabling efficient horizontal scaling. </p><h3 id="getting-started-with-pgbouncer">Getting started with PgBouncer</h3><p>Getting started with PgBouncer couldn&apos;t be any easier; in fact, I&apos;ve set up a Github repository, dubbed <a href="https://github.com/aglover/recoil?ref=thediscoblog.com" rel="noreferrer">Recoil</a>, that&apos;ll enable you to familiarize yourself with both PgBouncer&apos;s connection pooling and routing features from the comfort of your laptop.  All you need is <a href="https://docs.docker.com/engine/?ref=thediscoblog.com" rel="noreferrer">Docker Engine</a> and you&apos;ll be good to go, baby! </p><h3 id="the-connection-pooling-boogie">The connection pooling boogie</h3><p>Connections in Postgres are expensive because Postgres creates a new backend process for each connection, which involves memory allocation and initialization tasks. A connection pooler manages a set of connections thereby freeing up a database&apos;s resources to focus on other aspects, such as rapidly returning data.</p><p>To see how PgBouncer works as a connection pool, you can use use&#xA0;<a href="https://www.postgresql.org/docs/current/pgbench.html?ref=thediscoblog.com" rel="nofollow">pgbench</a>, which is a simple command line utility that runs benchmark tests on a Postgres instance. It&apos;s an efficient way to exhaust database resources, which is easy do if you set a high value for <code>pgbench</code>&apos;s client connections (i.e. via the <code>-c</code> flag) . You can, of course, <a href="https://stackoverflow.com/questions/30778015/how-to-increase-the-max-connections-in-postgres?ref=thediscoblog.com" rel="noreferrer">increase Postgres&apos;s <code>max_connections</code></a>; however, you&apos;ll potentially do this at the cost of other important resources. </p><p>Using the handy <code>docker-compose.yml</code> file found in the <a href="https://github.com/aglover/recoil?ref=thediscoblog.com" rel="noreferrer">Recoil repository</a> and following the detailed directions in the <code>README</code>, you can fire up a single instance of Postgres and do two things: initialize the database for benchmarking and then running a benchmark test. </p><p>Initializing the database for benchmarking is easy. Run the following command in your favorite terminal:</p><figure class="kg-card kg-code-card"><pre><code class="language- zsh">$ pgbench -i -s 5 -h localhost -p 49149 -U postgres recoil</code></pre><figcaption><p><span style="white-space: pre-wrap;">Using pgbench to initialize the database before benchmarking.</span></p></figcaption></figure><p>The <code>-i</code> flag initializes a benchmark with a scale factor of 5 via the <code>-s</code> flag. This will create 500,000 rows in a <code>ppgbench_accounts</code> table. </p><p>By default, Postgres&apos;s <code>max_connections</code> is set at 100. You can quickly exhaust this resource by running a benchmark test that exceeds this limit like so:</p><figure class="kg-card kg-code-card"><pre><code class="language-zsh">$ pgbench -c 101 -j 2 -t 100000 -h localhost -p 49149 -U postgres -S recoil</code></pre><figcaption><p><span style="white-space: pre-wrap;">Exhausting Postgres with 101 client connections.</span></p></figcaption></figure><p>In this case, 101 clients, configured via the <code>-c</code> flag were thrown at the local Postgres via 2 threads (i.e. <code>-j</code>). Each client attempted to issue 100,000 transactions via the <code>-t</code> flag. Unfortunately, this test fails as Postgres quickly exhausts valuable connections. The database will error out with a message along the lines of <code>FATAL: sorry, too many clients already</code>. </p><p>This is a great use case for employing a connection pool like PgBouncer! Following the <a href="https://github.com/aglover/recoil?tab=readme-ov-file&amp;ref=thediscoblog.com#directions-to-see-pgbouncer-pooling-in-action" rel="noreferrer">groovy directions I outlined in Recoil&apos;s <code>README</code></a> you can stand up PgBouncer in front of Postgres and let it manage database connections; what&apos;s more, if you set PgBouncer&apos;s connection pool to a large number, such as 200, you can get through the above <code>pgbench</code> test without failure. </p><p>Accordingly, you can reissue the <code>pgbench</code> benchmark test pointing to a different port, since you&apos;re going directly to PgBouncer (instead of Postgres) like so:</p><figure class="kg-card kg-code-card"><pre><code class="language-zsh">$ pgbench -c 101 -j 2 -t 100000 -h localhost -p 5432 -U postgres -S recoil</code></pre><figcaption><p><span style="white-space: pre-wrap;">A successful pgbench test using PgBouncer.</span></p></figcaption></figure><p>Connection pooling isn&apos;t a panacea; indeed, you can play around with <code>pgbench</code> and quickly exhaust PgBouncer as well. What&apos;s important is that you&apos;ve essentially offloaded connection management to a specialized process which is more efficient. Moreover, PgBouncer adds a few other handy features for your scaling needs including routing. </p><h3 id="routin-sql-requests">Routin&apos; SQL requests</h3><p>Horizontally scaling Postgres is fairly simple, especially if you read <a href="https://www.thediscoblog.com/horizontally-scaling-postgres-with-read-replicas/" rel="noreferrer">my blog post on read replicas</a>. Of course, when you stand up more than one database, you&apos;ll want an easy way to route SQL requests. A simple load balancer would do this automatically for you; however, with PgBouncer, you can easily route via a <em>name.</em> In the case of read replicas, you&apos;ll want to route writes to the primary instance and reads to one or more replicas. Conceptually, the architecture of using PgBouncer as a combination pooler <em>and router</em> looks like so:    </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2024/01/pgbouncer-1.png" class="kg-image" alt="Supercharging PostgreSQL with PgBouncer" loading="lazy" width="440" height="600"><figcaption><span style="white-space: pre-wrap;">PgBouncer as a router, with SQL writes going to the primary and reads to a replica.</span></figcaption></figure><p>With PgBouncer, routes are configured like connection strings, where the database name signifies a specific database. For instance, in my Recoil example, the database is named <code>recoil</code>. In a normal read replica environment, read replicas  would have the same name; nevertheless, with PgBouncer, you can give each read replica a unique identifier, which clients can leverage for specific routing. </p><p>You can see routing in action <a href="https://github.com/aglover/recoil?tab=readme-ov-file&amp;ref=thediscoblog.com#directions-to-see-pgbouncer-routing-in-action" rel="noreferrer">following the directions in the Recoil Github repository</a>. If you examine the <code>docker-compose.yml</code> file, you&apos;ll note that the PgBouncer instance, <code>pgbouncer</code>, defines two databases. Defining the first database is done like so: </p><figure class="kg-card kg-code-card"><pre><code class="language-yml">- POSTGRESQL_HOST=postgres-primary
- POSTGRESQL_PASSWORD=recoil
- POSTGRESQL_DATABASE=recoil
- PGBOUNCER_DATABASE=recoil</code></pre><figcaption><p><span style="white-space: pre-wrap;">PgBouncer environmental variables defining one database with a route named recoil.</span></p></figcaption></figure><p>Subsequent databases, however, are defined using a somewhat esoteric string like so:</p><figure class="kg-card kg-code-card"><pre><code class="language-yml">- PGBOUNCER_DSN_0=recoil-ro=host=postgres-replica port=5432 dbname=recoil</code></pre><figcaption><p><span style="white-space: pre-wrap;">Defining a named route in PgBouncer. </span></p></figcaption></figure><p>The crucial part of this string is the <code>DSN_0</code> aspect, which gives this route the name <code>recoil-ro</code>. This logical name points to the <code>postgres-replica</code> service listening on port <code>5432</code>. </p><p>You can see routing (and replication too!) in action by using <code>psql</code> to log into the primary and issuing two SQL queries. First, shell into the primary using the following command:</p><figure class="kg-card kg-code-card"><pre><code class="language-zsh">  $ psql postgresql://postgres:recoil@localhost:5432/recoil</code></pre><figcaption><p><span style="white-space: pre-wrap;">Using psql to shell into the primary instance.</span></p></figcaption></figure><p>If you&apos;re following the directions in Recoil&apos;s repository and ran <code>bunzip2</code> on the  <code>employee_data.sql.bz2</code> file and then started things up with <code>docker compose up -d</code>, then when you run this SQL command, you should see a count of <code>300024</code>. </p><figure class="kg-card kg-code-card"><pre><code class="language-SQL">select count(*) from employees.employee;</code></pre><figcaption><p><span style="white-space: pre-wrap;">A SQL count of all employee rows.</span></p></figcaption></figure><p>Next, insert an additional <code>employee</code> record like so:</p><figure class="kg-card kg-code-card"><pre><code class="language-SQL">insert into employees.employee values (9999999999, &apos;1987-03-21&apos;, &apos;Joe&apos;, &apos;Smith&apos;, &apos;M&apos;, &apos;2022-01-22&apos;);</code></pre><figcaption><p><span style="white-space: pre-wrap;">Inserting a new employee.</span></p></figcaption></figure><p>With Postgres replication, behind the scenes this record is automatically added to the configured read replica (i.e. the <code>postgres-replica</code> service found in the Docker compose file). You can verify this new row was added by using <code>psql</code> to shell into the replica instance by referencing the route name elaborated earlier, which is <code>recoil-ro</code>. </p><figure class="kg-card kg-code-card"><pre><code class="language-SQL">$ psql postgresql://postgres:recoil@localhost:5432/recoil-ro</code></pre><figcaption><p><span style="white-space: pre-wrap;">Using psql to shell into the read replica instance using its logical name recoil-ro.</span></p></figcaption></figure><p>Run the aforementioned <code>count</code> query and you should see the answer is <code>300025</code>, which reflects the newly added <code>employee</code> record inserted into the primary instance and replicated to the replica. </p><p>As you can see from the code above, PgBouncer is acting like a proxy, routing queries to appropriate database instances via name. In fact, conceptually, PgBouncer is akin to a load balancer. It does represent a single point of failure, but if you zoom out slightly, you&apos;ll recall that PgBouncer also is a connection pool.</p><h3 id="keep-on-truckin-with-pgbouncer">Keep on truckin&apos; with PgBouncer</h3><p>If you&apos;re using Postgres and are looking for a way to improve the performance and stability of your application, then PgBouncer is a groovy option. It&apos;s easy to set up and use, and it can provide a significant improvement in performance. Can you dig it? </p><div class="kg-card kg-signup-card kg-width-regular " data-lexical-signup-form style="background-color: #F0F0F0; display: none;">
            
            <div class="kg-signup-card-content">
                
                <div class="kg-signup-card-text ">
                    <h2 class="kg-signup-card-heading" style="color: #000000;"><span style="white-space: pre-wrap;">Be one of the hip ones! Sign up for The Disco Blog and feel groovy. </span></h2>
                    <p class="kg-signup-card-subheading" style="color: #000000;"><span style="white-space: pre-wrap;">Disco. It&apos;s the all the rage, baby. </span></p>
                    
        <form class="kg-signup-card-form" data-members-form="signup">
            
            <div class="kg-signup-card-fields">
                <input class="kg-signup-card-input" id="email" data-members-email type="email" required="true" placeholder="Your email">
                <button class="kg-signup-card-button kg-style-accent" style="color: #FFFFFF;" type="submit">
                    <span class="kg-signup-card-button-default">Subscribe!</span>
                    <span class="kg-signup-card-button-loading"><svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewbox="0 0 24 24">
        <g stroke-linecap="round" stroke-width="2" fill="currentColor" stroke="none" stroke-linejoin="round" class="nc-icon-wrapper">
            <g class="nc-loop-dots-4-24-icon-o">
                <circle cx="4" cy="12" r="3"/>
                <circle cx="12" cy="12" r="3"/>
                <circle cx="20" cy="12" r="3"/>
            </g>
            <style data-cap="butt">
                .nc-loop-dots-4-24-icon-o{--animation-duration:0.8s}
                .nc-loop-dots-4-24-icon-o *{opacity:.4;transform:scale(.75);animation:nc-loop-dots-4-anim var(--animation-duration) infinite}
                .nc-loop-dots-4-24-icon-o :nth-child(1){transform-origin:4px 12px;animation-delay:-.3s;animation-delay:calc(var(--animation-duration)/-2.666)}
                .nc-loop-dots-4-24-icon-o :nth-child(2){transform-origin:12px 12px;animation-delay:-.15s;animation-delay:calc(var(--animation-duration)/-5.333)}
                .nc-loop-dots-4-24-icon-o :nth-child(3){transform-origin:20px 12px}
                @keyframes nc-loop-dots-4-anim{0%,100%{opacity:.4;transform:scale(.75)}50%{opacity:1;transform:scale(1)}}
            </style>
        </g>
    </svg></span>
                </button>
            </div>
            <div class="kg-signup-card-success" style="color: #000000;">
                Email sent! Check your inbox to complete your signup.
            </div>
            <div class="kg-signup-card-error" style="color: #000000;" data-members-error></div>
        </form>
        
                    <p class="kg-signup-card-disclaimer" style="color: #000000;"><span style="white-space: pre-wrap;">No spam. Unsubscribe anytime.</span></p>
                </div>
            </div>
        </div>]]></content:encoded></item><item><title><![CDATA[Mining for dissent]]></title><description><![CDATA[It feels good to be in a room of yes-people, but it’s a trap. Be brave and seek out if and why people disagree with you. It'll make you a more effective leader. ]]></description><link>https://www.thediscoblog.com/mining-for-dissent/</link><guid isPermaLink="false">65a60be6302ab9000148bb49</guid><category><![CDATA[leadership]]></category><dc:creator><![CDATA[Andrew Glover]]></dc:creator><pubDate>Fri, 19 Jan 2024 21:52:19 GMT</pubDate><media:content url="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2024/01/Coach-Drawing-American-Football-Game-Playbook-1366672302_2125x1416.jpeg" medium="image"/><content:encoded><![CDATA[<img src="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2024/01/Coach-Drawing-American-Football-Game-Playbook-1366672302_2125x1416.jpeg" alt="Mining for dissent"><p>As a leader it&#x2019;s tempting to think your ideas and decisions are fantastic. Most likely, your track record of previous ideas and decisions got you to where you are. Moreover, it&#x2019;s human nature to avoid the person who tends to disagree with you. After all, you&#x2019;re the leader. You&#x2019;re the one who&#x2019;s accountable.&#xA0;</p><p>When I was a new leader, I disliked it when individuals would disrupt the harmony of meetings by disagreeing with my ideas, decisions, or game plans. My instincts were to avoid including them or ignoring their opinion. Oh how I wish I could reach back in time and shake some sense into that version of me! My instincts were awfully incorrect. Dissenting opinions are a veritable gold mine of alternative paths, variables, and, importantly, clarity. Mining for dissent, rather than seeking a lazy harmony, always makes decisions <em>even better</em>.&#xA0;</p><h3 id="make-it-safe">Make it safe</h3><p>Naturally, you have to make it safe for disagreement. You have to put your guard down and let your ego go. It&#x2019;s hard at first.&#xA0;But if you want folks to critically think through and challenge ideas, you need to make it easy. Be welcoming of different opinions; what&apos;s more, when folks do speak up and dissent, celebrate it. It&apos;ll become a virtuous cycle as people will see it&apos;s safe and try it for themselves. </p><p>When someone disagrees with you, it&#x2019;s important to take the position that they are disagreeing with <em>your idea</em>, not you. Disagreement isn&#x2019;t a personal attack of your intelligence, but simply a <em>different</em> point of view. Your job as a leader is to understand why there&#x2019;s a disagreement. Understanding how someone arrived at a different position is vital. This process takes practice and a lot of patience. Nevertheless, in these disagreeing situations, encourage dissenters to elaborate their reasons. Make sure you understand their reasons. Did this person uncover a blind spot in your analysis? Did you overlook something?&#xA0;Don&apos;t dismiss this dissent no matter how tempting it is! </p><h3 id="avoid-the-laziness-trap">Avoid the laziness trap</h3><p>Mining for dissent is an effective means to avoid groupthink. Sometimes people get lazy &#x2013; it&#x2019;s been a long day and they aren&#x2019;t in the mood to think critically or engage in some sort of disagreement. This definitely happens to me! Zoom fatigue is real. Don&#x2019;t take the easy path though! You have to encourage debate and disagreement. You have to pull it out of people sometimes. Mining for dissent is as easy as asking &quot;what am I missing?&quot; </p><p>It no doubt feels good to be in a room of yes-people, but it&#x2019;s a trap!  Those harmoniously pleasant interactions without any deep thoughtful debate are weak. Chances are there&apos;s a hole somewhere. Be brave and seek if people disagree with you and then find out why. If you have a high powered team that has a foundation of trust, you can put away your ego! </p><h3 id="great-leaders-embrace-dissent">Great leaders embrace dissent</h3><p>Allow folks to poke holes in your ideas and through that process, you&#x2019;ll end up with a better idea. I guarantee it. An idea which has gone through dissent mining will be one that&#x2019;s been vetted and critically analyzed. And in the process, you&#x2019;ve built buy-in and ownership from your team. By actively mining for dissent, your initially weak idea, decision, or position becomes your team&#x2019;s well throughout strategy or plan. Moreover, when surprises inevitably pop up, you&#x2019;ll all be in the trenches <em>together</em> solving whatever problems arose.&#xA0;By actively engaging a debate, you&apos;ve built commitment. </p><p>It took me some hard lessons to realize that when people disagreed with me, <em>they cared.</em> If someone disagrees with your idea, it means they took the time to <em>think about your idea</em>! Embrace dissent. Mine for it. It&apos;ll result in better decisions, buy-in from your team, and make you a better leader. </p><div class="kg-card kg-signup-card kg-width-regular " data-lexical-signup-form style="background-color: #F0F0F0; display: none;">
            
            <div class="kg-signup-card-content">
                
                <div class="kg-signup-card-text ">
                    <h2 class="kg-signup-card-heading" style="color: #000000;"><span style="white-space: pre-wrap;">Sign up for The Disco Blog and be groovy, baby! </span></h2>
                    <p class="kg-signup-card-subheading" style="color: #000000;"><span style="white-space: pre-wrap;">70s slang doesn&apos;t get used enough anymore. </span></p>
                    
        <form class="kg-signup-card-form" data-members-form="signup">
            
            <div class="kg-signup-card-fields">
                <input class="kg-signup-card-input" id="email" data-members-email type="email" required="true" placeholder="Your email">
                <button class="kg-signup-card-button kg-style-accent" style="color: #FFFFFF;" type="submit">
                    <span class="kg-signup-card-button-default">Subscribe</span>
                    <span class="kg-signup-card-button-loading"><svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewbox="0 0 24 24">
        <g stroke-linecap="round" stroke-width="2" fill="currentColor" stroke="none" stroke-linejoin="round" class="nc-icon-wrapper">
            <g class="nc-loop-dots-4-24-icon-o">
                <circle cx="4" cy="12" r="3"/>
                <circle cx="12" cy="12" r="3"/>
                <circle cx="20" cy="12" r="3"/>
            </g>
            <style data-cap="butt">
                .nc-loop-dots-4-24-icon-o{--animation-duration:0.8s}
                .nc-loop-dots-4-24-icon-o *{opacity:.4;transform:scale(.75);animation:nc-loop-dots-4-anim var(--animation-duration) infinite}
                .nc-loop-dots-4-24-icon-o :nth-child(1){transform-origin:4px 12px;animation-delay:-.3s;animation-delay:calc(var(--animation-duration)/-2.666)}
                .nc-loop-dots-4-24-icon-o :nth-child(2){transform-origin:12px 12px;animation-delay:-.15s;animation-delay:calc(var(--animation-duration)/-5.333)}
                .nc-loop-dots-4-24-icon-o :nth-child(3){transform-origin:20px 12px}
                @keyframes nc-loop-dots-4-anim{0%,100%{opacity:.4;transform:scale(.75)}50%{opacity:1;transform:scale(1)}}
            </style>
        </g>
    </svg></span>
                </button>
            </div>
            <div class="kg-signup-card-success" style="color: #000000;">
                Email sent! Check your inbox to complete your signup.
            </div>
            <div class="kg-signup-card-error" style="color: #000000;" data-members-error></div>
        </form>
        
                    <p class="kg-signup-card-disclaimer" style="color: #000000;"><span style="white-space: pre-wrap;">No spam. Unsubscribe anytime.</span></p>
                </div>
            </div>
        </div>]]></content:encoded></item><item><title><![CDATA[Horizontally scaling PostgreSQL with read replicas]]></title><description><![CDATA[If your PostgreSQL database is reaching the limits of vertical scaling and you’re looking to increase reliability, a read replica strategy can be beneficial. They are easy to set up; however, you should be aware of the tradeoffs. ]]></description><link>https://www.thediscoblog.com/horizontally-scaling-postgres-with-read-replicas/</link><guid isPermaLink="false">65a205ee302ab9000148ba6a</guid><category><![CDATA[postgres]]></category><category><![CDATA[read replica]]></category><dc:creator><![CDATA[Andrew Glover]]></dc:creator><pubDate>Mon, 15 Jan 2024 21:25:49 GMT</pubDate><media:content url="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2024/01/27927_A-disco-dancer-lifting-a-database-hands_xl-1024-v1-0.png" medium="image"/><content:encoded><![CDATA[<img src="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2024/01/27927_A-disco-dancer-lifting-a-database-hands_xl-1024-v1-0.png" alt="Horizontally scaling PostgreSQL with read replicas"><p><a href="https://www.postgresql.org/?ref=thediscoblog.com" rel="noreferrer">Postgres</a> is an incredibly mature and battle hardened database that powers many of the applications <a href="https://learnsql.com/blog/companies-that-use-postgresql-in-business/?ref=thediscoblog.com"><u>you rely on</u></a> daily. From <a href="https://www.instacart.com/company/how-its-made/adopting-pgcat-a-nextgen-postgres-proxy/?ref=thediscoblog.com#:~:text=At%20Instacart%2C%20we%20use%20Postgresql,optimization%20and%20vertically%20scaling%20instances." rel="noreferrer">Instacart</a>, <a href="https://instagram-engineering.com/handling-growth-with-postgres-5-tips-from-instagram-d5d7e7ffdfcb?ref=thediscoblog.com" rel="noreferrer">Instagram</a>, various <a href="https://www.networkworld.com/article/737832/opensource-subnet-apple-ditches-mysql-in-favor-of-postgresql.html?ref=thediscoblog.com" rel="noreferrer">Apple</a> services, and <a href="https://github.com/spotify/postgresql-metrics?ref=thediscoblog.com" rel="noreferrer">Spotify</a>, chances are that data <em>related to you</em> resides in a handful of well scaled and highly tuned Postgres instances across the globe.&#xA0;</p><p>Out-of-the-box, Postgres works well enough. If you&#x2019;re lucky, and you find yourself with a popular application (which generates a lot of load through reads and/or writes), you&#x2019;ll likely find yourself needing to scale Postgres. Fortunately, you have a few options, starting with throwing more hardware at the problem by adding faster disks, additional CPUs, and more memory. You can think of these options as <em>vertically scaling </em>Postgres. Vertically scaling Postgres can increase throughput and reduce latencies; however, these options don&#x2019;t address resilience.&#xA0;&#xA0;</p><p>Addressing resilience ultimately requires making copies of the underlying data, which can be achieved through <a href="https://wiki.postgresql.org/wiki/Shared_Storage?ref=thediscoblog.com"><u>shared storage</u></a>, <a href="https://www.postgresql.org/docs/current/high-availability.html?ref=thediscoblog.com" rel="noreferrer">read replicas</a>, or via distributed versions of Postgres like AWS&#x2019;s <a href="https://aws.amazon.com/rds/aurora/?ref=thediscoblog.com" rel="noreferrer">Aurora</a>, <a href="https://www.yugabyte.com/?ref=thediscoblog.com" rel="noreferrer">Yugabyte</a>, and <a href="https://cloud.google.com/alloydb?ref=thediscoblog.com" rel="noreferrer">Google&#x2019;s AlloyDB</a> (just to name a few!). These tactics enable you to <em>horizontally scale</em> Postgres and of these options, one of the easiest to spin up are read replicas. </p><p>There are certainly trade offs when using read replicas. First and foremost, read replicas are <em>asynchronous</em> copies of your primary Postgres database. Changes made on the primary are streamed (via the <a href="https://www.postgresql.org/docs/current/wal-intro.html?ref=thediscoblog.com" rel="noreferrer">WAL</a> or write ahead log) to the replicas with a slight delay. What&#x2019;s more, read replicas are read-only, meaning you can&apos;t write data directly to them. You can only read from them, which reduces resource demands and avoids data conflicts. It&#x2019;s also easy enough to have multiple read replicas spread across different servers or regions, distributing read traffic and improving responsiveness for user queries.&#xA0;</p><p>In essence, read replicas offer a backup of your database and in a worst case scenario, you can turn a read replica into the primary should your primary database go down. Of course, there&#x2019;s some probable downtime if this were to happen because, at a minimum, you&#x2019;ll need to reroute writes to the newly christened primary.&#xA0;</p><h3 id="show-me-the-code-man">Show me the code, man</h3><p>Setting up a read replica doesn&apos;t take a lot of effort! For instance, if you want to see how they work locally and you&apos;re using Docker, <a href="https://hub.docker.com/r/bitnami/postgresql?ref=thediscoblog.com" rel="noreferrer">Bitnami&apos;s Postgres Docker image</a> couldn&apos;t make it any easier. I created a Github repository, named <a href="https://github.com/aglover/hoodoo?ref=thediscoblog.com" rel="noreferrer">Hoodoo</a>, which contains a Docker Compose file demonstrating read replicas in action. </p><p>If you clone the repository and run <code>docker compose up -d</code>, you&apos;ll find that two Postgres instances have started: one primary and a replica. What&apos;s more, you can decompress a provided SQL script and import this data into the primary, enabling you to work with an <code>employees</code> database. You&apos;ll notice after you import the <code>employees</code> database into the primary, the replica will reflect the same (i.e. replication in action, baby!).  </p><p>As I mentioned, Bitnami has packaged Postgres making creating a replica effortless. The key to configuring the primary and one or more replicas is via Docker Compose individual <code>services</code> <code>environment</code> settings. For instance, the primary is configured with the following <code>environment</code> variables:</p><figure class="kg-card kg-code-card"><pre><code class="language-docker-compose.yml"> environment:
    - POSTGRES_PASSWORD=hoodoo
    - POSTGRESQL_REPLICATION_MODE=master
    - POSTGRESQL_REPLICATION_USER=repl_user
    - POSTGRESQL_REPLICATION_PASSWORD=repl_user
    - POSTGRESQL_DATABASE=hoodoo</code></pre><figcaption><p><span style="white-space: pre-wrap;">The primary&apos;s environment settings </span></p></figcaption></figure><p>As you can see above, the primary is configured as <code>master</code> and a special username and password are configured. The replica&apos;s <code>environment</code> is configured as follows:</p><figure class="kg-card kg-code-card"><pre><code class="language-docker-compose.yml">environment:
   - POSTGRES_PASSWORD=hoodoo
   - POSTGRESQL_MASTER_HOST=postgres-primary
   - POSTGRESQL_MASTER_PORT_NUMBER=5432
   - POSTGRESQL_REPLICATION_MODE=slave
   - POSTGRESQL_REPLICATION_USER=repl_user
   - POSTGRESQL_REPLICATION_PASSWORD=repl_user</code></pre><figcaption><p><span style="white-space: pre-wrap;">The replica&apos;s environment settings </span></p></figcaption></figure><p>Note how the replica is configured as a <code>slave</code>; moreover, this service is configured to point at the primary via a host and port number.  </p><p>You can connect to either database via the <code>psql</code> command - the primary is exposed on port 5432 and the replica on 5434. To get a true feel for how things work in a read replica environment, you can issue a write to the primary and then in another terminal window, issue a read on a replica (i.e. a <code>select</code>) and the data will be there. </p><p>For instance, first verify the count of <code>employee</code> records in the database via a <code>select</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-sql">select count(*) from employees.employee;</code></pre><figcaption><p><span style="white-space: pre-wrap;">The count of employee rows</span></p></figcaption></figure><p>The answer should be 300,024. Verify this on <em>both the primary and the replica</em>. Next, insert a new employee into the primary via the below SQL statement: </p><figure class="kg-card kg-code-card"><pre><code class="language-sql">insert into employees.employee values (99999999999, &apos;1957-03-20&apos;, &apos;Robert&apos;, &apos;Smith&apos;, &apos;M&apos;, &apos;2024-01-02&apos;);</code></pre><figcaption><p><span style="white-space: pre-wrap;">A simple SQL insert</span></p></figcaption></figure><p>Finally, you can run the same <code>select count(*)</code> command on the replica and you&apos;ll see the count has increased by 1. Magic! My hope is that the Hoodoo repository makes it super easy to see Postgres read replicas in action and enable you to get a feel for how to employ this strategy for horizontally scaling Postgres. </p><h3 id="horizontal-scaling-tradeoffs">Horizontal scaling tradeoffs </h3><p>For a resilient backup of your database via read replicas, you do make a few tradeoffs. There&apos;s a slight delay in replication between a primary and a replica. This delay is a function of the speed of light and processing power. You can use read replicas to offload traffic from a primary, but it&#x2019;s only reads. Depending on your load, you&#x2019;ll also need to pay special attention to the primary&#x2019;s WAL settings as if they aren&#x2019;t tuned correctly, your primary&#x2019;s performance could suffer. Finally, spinning up one or more read replicas will naturally cost you more money, most likely above and beyond what you&#x2019;ve already spent on vertically scaling your database (although you&#x2019;ll most likely reap <em>some</em> savings by lowering the primary&#x2019;s load). Read replica instances are an additional asset that&#x2019;ll need monitoring and ongoing maintenance (i.e. upgrades, etc) too.&#xA0;</p><p>If your Postgres database is reaching the limits of vertical scaling and you&#x2019;re looking to increase reliability, a read replica strategy can be beneficial. Read replicas can increase read performance because<strong> </strong>you can offload read-heavy workloads from the primary server. Moreover, read replicas improve scalability: you can add more of them to handle increasing read traffic without impacting the primary server. You can also geo-distribute read replicas to reduce latencies. What&#x2019;s more, read replicas enhance availability. If needed, you can promote a replica to become the primary if the primary fails.</p><p>Can you dig it? </p><div class="kg-card kg-signup-card kg-width-regular " data-lexical-signup-form style="background-color: #F0F0F0; display: none;">
            
            <div class="kg-signup-card-content">
                
                <div class="kg-signup-card-text ">
                    <h2 class="kg-signup-card-heading" style="color: #000000;"><span style="white-space: pre-wrap;">Sign up for 70s slang and technology drivel</span></h2>
                    <p class="kg-signup-card-subheading" style="color: #000000;"><span style="white-space: pre-wrap;">It&apos;s the Disco Blog, baby</span></p>
                    
        <form class="kg-signup-card-form" data-members-form="signup">
            
            <div class="kg-signup-card-fields">
                <input class="kg-signup-card-input" id="email" data-members-email type="email" required="true" placeholder="Your email">
                <button class="kg-signup-card-button kg-style-accent" style="color: #FFFFFF;" type="submit">
                    <span class="kg-signup-card-button-default">Subscribe</span>
                    <span class="kg-signup-card-button-loading"><svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewbox="0 0 24 24">
        <g stroke-linecap="round" stroke-width="2" fill="currentColor" stroke="none" stroke-linejoin="round" class="nc-icon-wrapper">
            <g class="nc-loop-dots-4-24-icon-o">
                <circle cx="4" cy="12" r="3"/>
                <circle cx="12" cy="12" r="3"/>
                <circle cx="20" cy="12" r="3"/>
            </g>
            <style data-cap="butt">
                .nc-loop-dots-4-24-icon-o{--animation-duration:0.8s}
                .nc-loop-dots-4-24-icon-o *{opacity:.4;transform:scale(.75);animation:nc-loop-dots-4-anim var(--animation-duration) infinite}
                .nc-loop-dots-4-24-icon-o :nth-child(1){transform-origin:4px 12px;animation-delay:-.3s;animation-delay:calc(var(--animation-duration)/-2.666)}
                .nc-loop-dots-4-24-icon-o :nth-child(2){transform-origin:12px 12px;animation-delay:-.15s;animation-delay:calc(var(--animation-duration)/-5.333)}
                .nc-loop-dots-4-24-icon-o :nth-child(3){transform-origin:20px 12px}
                @keyframes nc-loop-dots-4-anim{0%,100%{opacity:.4;transform:scale(.75)}50%{opacity:1;transform:scale(1)}}
            </style>
        </g>
    </svg></span>
                </button>
            </div>
            <div class="kg-signup-card-success" style="color: #000000;">
                Email sent! Check your inbox to complete your signup.
            </div>
            <div class="kg-signup-card-error" style="color: #000000;" data-members-error></div>
        </form>
        
                    <p class="kg-signup-card-disclaimer" style="color: #000000;"><span style="white-space: pre-wrap;">No spam. Unsubscribe anytime.</span></p>
                </div>
            </div>
        </div>]]></content:encoded></item><item><title><![CDATA[Context not control]]></title><description><![CDATA[Effective leaders facilitate great outcomes by setting appropriate context as opposed to trying to control their teams.]]></description><link>https://www.thediscoblog.com/context-not-control/</link><guid isPermaLink="false">6588b15a4fd81e0008eb8ca0</guid><category><![CDATA[leadership]]></category><dc:creator><![CDATA[Andrew Glover]]></dc:creator><pubDate>Sun, 24 Dec 2023 22:31:54 GMT</pubDate><media:content url="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2023/12/Group-of-People-Connection-Digital-Device-Concept-000087540079_Full.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://storage.ghost.io/c/cf/de/cfde43e0-9e32-4474-898e-05943cb69043/content/images/2023/12/Group-of-People-Connection-Digital-Device-Concept-000087540079_Full.jpg" alt="Context not control"><p>Before joining Netflix 10 years ago, I had held various leadership positions. In all cases, I was very much a command-and-control style leader. I made what I thought were cogent decisions and expected my teams to unquestionably execute on them. When things didn&#x2019;t go as I&#x2019;d planned, I naturally became frustrated and blamed the people on my teams. It never occurred to me that there was a better way to lead.&#xA0;</p><p>Then I joined Netflix as a leader and I learned about the notion of <em>context over control</em>. Instead of trying to control people, leaders were encouraged to provide teams with <em>context</em>. I learned that great leaders provide the insight and understanding that enables sound decisions<em> </em>through context.</p><h3 id="inspire-through-context">Inspire through context</h3><p>Context is about articulating goals and the corresponding strategy to achieve them. Context is generically the <em>what</em> and not the <em>how.</em> The folks closest to the problem oftentimes have a much better understanding of the challenges and opportunities in front of them and can determine a much better solution (i.e. the how). Importantly, their solution aligns with the goals set forth by effective leaders who have articulated the what.&#xA0;</p><p>Accordingly, encouraging context over control means that leadership must clearly articulate important concepts like business goals and the strategy to achieve those goals. Importantly, leaders must explain why those goals are important and what they mean to the business. Moreover, leaders should strive to provide transparent decision making, eschewing bureaucratic concepts like committees, management approvals, complex processes that become more valued than goals, and the notion of top-down decision making.&#xA0;</p><h3 id="failure-is-on-you">Failure is on you</h3><p>Once I embraced the notion of context not control, my viewpoint on failure flipped as well. When teams fail to execute according to all the context you&#x2019;ve provided, rather than blaming them (like I used to do) instead, try to determine what context you failed to provide. In my case, I almost always have been able to determine my ability to convey context needed improving. That is, my teams were acting with the information they had. As I invested in transparency of decision making, I noticed my teams became more effective. They made better decisions.&#xA0;</p><p>If you&apos;re hiring amazing people, they will do better work if they understand the context behind decisions, goals, and strategy. It&#x2019;s easier said than done and requires a large investment from leadership! Communication is hard; leaders have to deliberately invest in it. Recognize that some context may make folks feel uncomfortable. But that&#x2019;s ok if you&#x2019;ve built a team of adults. Transparent context is not a panacea either: there are certainly times when control is appropriate. Emergencies and a lack of mature team members are examples of those times.&#xA0;&#xA0;&#xA0;</p><h3 id="relinquish-control">Relinquish control</h3><p>The best leaders facilitate great outcomes by setting appropriate <em>context</em> for their teams as opposed to trying to <em>control</em> them. For years, I drummed up developers giving orders to do the work I felt was important. Once I switched to inspiring developers to yearn for lofty, but achievable goals, I saw new levels of impact and success.&#xA0;If only I had learned this lesson earlier in my career.</p><div class="kg-card kg-signup-card kg-width-regular " data-lexical-signup-form style="background-color: #F0F0F0; display: none;">
            
            <div class="kg-signup-card-content">
                
                <div class="kg-signup-card-text ">
                    <h2 class="kg-signup-card-heading" style="color: #000000;"><span style="white-space: pre-wrap;">Sign up for the most copasetic blog since the 1970s. </span></h2>
                    <p class="kg-signup-card-subheading" style="color: #000000;"><span style="white-space: pre-wrap;">It&apos;s the Disco Blog, baby</span></p>
                    
        <form class="kg-signup-card-form" data-members-form="signup">
            
            <div class="kg-signup-card-fields">
                <input class="kg-signup-card-input" id="email" data-members-email type="email" required="true" placeholder="Your email">
                <button class="kg-signup-card-button kg-style-accent" style="color: #FFFFFF;" type="submit">
                    <span class="kg-signup-card-button-default">Subscribe</span>
                    <span class="kg-signup-card-button-loading"><svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewbox="0 0 24 24">
        <g stroke-linecap="round" stroke-width="2" fill="currentColor" stroke="none" stroke-linejoin="round" class="nc-icon-wrapper">
            <g class="nc-loop-dots-4-24-icon-o">
                <circle cx="4" cy="12" r="3"/>
                <circle cx="12" cy="12" r="3"/>
                <circle cx="20" cy="12" r="3"/>
            </g>
            <style data-cap="butt">
                .nc-loop-dots-4-24-icon-o{--animation-duration:0.8s}
                .nc-loop-dots-4-24-icon-o *{opacity:.4;transform:scale(.75);animation:nc-loop-dots-4-anim var(--animation-duration) infinite}
                .nc-loop-dots-4-24-icon-o :nth-child(1){transform-origin:4px 12px;animation-delay:-.3s;animation-delay:calc(var(--animation-duration)/-2.666)}
                .nc-loop-dots-4-24-icon-o :nth-child(2){transform-origin:12px 12px;animation-delay:-.15s;animation-delay:calc(var(--animation-duration)/-5.333)}
                .nc-loop-dots-4-24-icon-o :nth-child(3){transform-origin:20px 12px}
                @keyframes nc-loop-dots-4-anim{0%,100%{opacity:.4;transform:scale(.75)}50%{opacity:1;transform:scale(1)}}
            </style>
        </g>
    </svg></span>
                </button>
            </div>
            <div class="kg-signup-card-success" style="color: #000000;">
                Email sent! Check your inbox to complete your signup.
            </div>
            <div class="kg-signup-card-error" style="color: #000000;" data-members-error></div>
        </form>
        
                    <p class="kg-signup-card-disclaimer" style="color: #000000;"><span style="white-space: pre-wrap;">No spam. Unsubscribe anytime.</span></p>
                </div>
            </div>
        </div>]]></content:encoded></item></channel></rss>