<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Soldier's 5</title><link>https://4lex.nz/</link><description>Recent content on Soldier's 5</description><image><title>Soldier's 5</title><url>https://4lex.nz/img/404-bg.jpg</url><link>https://4lex.nz/img/404-bg.jpg</link></image><generator>Hugo</generator><language>en-us</language><lastBuildDate>Mon, 13 Oct 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://4lex.nz/index.xml" rel="self" type="application/rss+xml"/><item><title>The Helpful Web - How AI will bring the next evolution of accessibility</title><link>https://4lex.nz/posts/the-helpful-web/</link><pubDate>Mon, 13 Oct 2025 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/the-helpful-web/</guid><description>&lt;p&gt;Better buttons, cleaner HTML markup, stricter adherence to WCAG - we&amp;rsquo;ve been investing in digital accessibility for over twenty years, and that investment has opened doors for millions of people worldwide.&lt;/p&gt;
&lt;p&gt;We are nearing a plateau. I believe that the next order-of-magnitude gain in accessibility won&amp;rsquo;t come from ever-tighter contrast ratios or perfection in semantic HTML.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ve reached the point of diminishing returns. We might build current best practice levels of accessibility faster and more effectively with better technology. Still, we are bound in our current approach to accessibility by the interfaces we ship. The next leap in accessibility cannot come from better execution of traditional interfaces; it can only come from changing the interface. Changing the interfaces means making them adaptive. Making them personal and responsive in ways that statically designed software can never achieve.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Better buttons, cleaner HTML markup, stricter adherence to WCAG - we&rsquo;ve been investing in digital accessibility for over twenty years, and that investment has opened doors for millions of people worldwide.</p>
<p>We are nearing a plateau. I believe that the next order-of-magnitude gain in accessibility won&rsquo;t come from ever-tighter contrast ratios or perfection in semantic HTML.</p>
<p>We&rsquo;ve reached the point of diminishing returns. We might build current best practice levels of accessibility faster and more effectively with better technology. Still, we are bound in our current approach to accessibility by the interfaces we ship. The next leap in accessibility cannot come from better execution of traditional interfaces; it can only come from changing the interface. Changing the interfaces means making them adaptive. Making them personal and responsive in ways that statically designed software can never achieve.</p>
<p>That change will come from agents. Agents are the next step in digital accessibility.</p>
<p>By agents, I mean allies that help you and increasingly act on your behalf. These agents will translate, reshape and personalise every interface an individual might need. In some cases, that agent will be your personal agent; in others, it will be an agent provided for you to use by the company you&rsquo;re interacting with. Either way, these agents will be accountable for ensuring that the things you need to interact with, screens, video, forms and more, will be exquisitely tailored for your unique needs.</p>
<p>At one end of the spectrum, that might mean synthesising the information you&rsquo;re reading with relevant insights from elsewhere on the internet.</p>
<p>On the other hand, it might mean actively interpreting for you, turning information that was previously inaccessible into something you can use instantly.</p>
<p>Imagine a world where anyone with macular degeneration can fill in their Lotus Notes timesheets!</p>
<p>Or where your personal agent spends a day learning your unique colour perception profile, then shares it with Netflix&rsquo;s agent, which re-colours every show you watch to give you the most vivid picture possible.</p>
<p>If agents can reshape interfaces in real time, they don&rsquo;t just make things easier; they change the rules of interface development. The most significant rule they will break is the tradeoff between usability and accessibility.</p>
<h2 id="dissolving-the-accessibility-usability-dilemma">Dissolving the accessibility-usability dilemma</h2>
<p>For years, we have wrestled with a false choice: make a product fully accessible and risk adding friction, or make it frictionless and knowingly exclude users. Agents dissolve that tradeoff. They will adapt to interfaces and needs in real time.</p>
<p>With the right tech and design, agents can deliver rigorous accessibility AND effortless usability simultaneously. The same agent that makes Lotus Notes usable for a user with macular degeneration can also streamline the workflow for a power user by skipping the UI and hitting APIs directly - without forcing either of those users to compromise.</p>
<p>To make agents truly helpful, we need to give them the same usage cues we give humans today, but in a form that agents can read and act on. In design, we call these cues &lsquo;affordances&rsquo;.</p>
<h2 id="what-are-affordances">What are affordances?</h2>
<p>Affordances are the characteristics or properties of an object that suggest how we can use it. They show a user that an object can be interacted with.</p>
<p>As such, an affordance is not a &ldquo;property&rdquo; of an object (like a physical object or a User Interface). Instead, an affordance is defined in the relation between the user and the object: A door affords opening if you can reach the handle. For a toddler, the door does not afford opening if she cannot reach the handle.</p>
<p>An affordance is, in essence, an action possibility in the relation between user and an object.</p>
<p>When your software&rsquo;s user is an agent, the affordances you build must be designed for machine interpretation and action, not just human user perception.</p>
<p>Let&rsquo;s look at three areas where agent-friendly affordances unlock capabilities beyond human limits, starting with precision.</p>
<h2 id="beyond-human-precision">Beyond-Human precision</h2>
<p>Today, surgeons use machines in surgery where human hands lack the precision needed to achieve the work safely. Tomorrow, a world awaits us where NOT using a machine will be considered malpractice.</p>
<p>Imagine that conversation between two surgeons, <em>&ldquo;What!? What do you mean you didn&rsquo;t use the robot to suture that vein back together! You just aren&rsquo;t that accurate! No human can be after three hours of surgery!&rdquo;.</em></p>
<figure class="align-center ">
    <img loading="lazy" src="/img/in-post/medical_droid.webp#center"
         alt="Medical droid assisting surgeon"/> <figcaption>
            <p>Hopefully they aren&rsquo;t as aggressive looking as this one.</p>
        </figcaption>
</figure>

<p>What would the world need to look like for this to be possible?</p>
<p>We would need to have built the affordances necessary for an agent to interact with external tools and the patient. We would need safety and security guarantees, and we would need signs of confidence and doubt so that humans can review and intervene as needed.</p>
<p>In short, we would need equal affordances for the robot and for the human. We might need:</p>
<ul>
<li>Some kind of semantic data mapping of the human body that an agent could understand and apply knowledge to (Agent: &ldquo;the patient&rsquo;s blood pressure is stable, if it drops, I need to check for bleeding and respond with the appropriate tool immediately&rdquo;)</li>
<li>Tools that provide telemetry and other feedback via APIs (Agent: &ldquo;Anaesthetic is being consumed at the expected rate given patient physiology, if rates change up or down, I will pause and escalate for human review&rdquo;)</li>
<li>A state machine for agents (Agent: &ldquo;The state of the cardiopulmonary bypass machine is 12, subcomponents are in state 3, 7, 16 - I have mapped out a response plan if these states change.&rdquo;)</li>
<li>Insight into control states for humans (Human in the loop: I can see that the robot is in a planning state for the following surgical task and is anticipating three scenarios. I can see the plans for those scenarios and have pre-approved those actions only)</li>
</ul>
<p>These examples show that the affordances we need for agents are the same as those for a novice surgeon. We need clear insight into the &ldquo;headspace&rdquo; of the novice, we need the novice to understand the capabilities of the tools they have at hand as well as how they can use those tools to achieve their plan (i.e. the theory knowledge and the applied knowledge), and we need someone with experience who can provide feedback in the moment that improves the performance of the novice. None of these affordances are new; we&rsquo;re just applying them to computers that we use to apply only to humans.</p>
<p>If precision is about moment-to-moment execution, diligence is about never missing a detail, no matter how long you look or how deeply you dig.</p>
<h2 id="beyond-human-diligence">Beyond-Human diligence</h2>
<p>My team is currently building payroll software. It may be the first agentic payroll software in Oceania, if not the world. We&rsquo;re planning and actively investing in a world where we can ensure that you are paid correctly by inspecting every single pay packet you have ever received, every time we calculate your next pay packet. A world where we can review 10 years of your tax history, 10 years of superannuation contributions, and 10 years of deductions and bonuses. Every single number that has ever been associated with you and your pay, for 10 years or more.</p>
<p>Payroll is a highly complex, compliance-heavy domain. Mistakes trigger legal penalties that can run into the millions. In Australia, if a company pays an employee incorrectly, the individual payroll staff member involved in that transaction can be jailed!</p>
<p>The pressure to get it right is massive, and the longer your employee is with you, the more data you have to check and re-check every time you pay them. Many of my customers have been with us for over a decade!</p>
<p>Can you imagine a human reviewing that for ten, a hundred, a thousand, or ten thousand employees? Picture this: It&rsquo;s the end of the financial year. If you are in agriculture, this is your equivalent of moving day. If you&rsquo;re in retail, it&rsquo;s Black Friday, and it&rsquo;s flu season in healthcare.</p>
<p>You&rsquo;re the CFO. Your payroll manager walks into the room and says, &ldquo;We&rsquo;ve reviewed 100% of the payroll transactions we&rsquo;ve made for about five thousand staff employed in the last seven years against every change in tax and holiday law in that period. We found three issues and fixed them. You can sign off on the end-of-year accounts.&rdquo;</p>
<p>Previously, the payroll manager would have sampled the data with the risk of issues slipping through the cracks. In most cases today, your end-of-year accounts process is focused on the yearly period in question, and the amount of reports and data needed is enormous. Most companies (at least in New Zealand) can&rsquo;t afford to hit the depth level they would like, which is why we have auditors routinely come through and do things like Holidays Act reviews and remediation.</p>
<p>The affordances we need for this level of automated compliance audit look very different to the affordances required for surgery.</p>
<p>We need:</p>
<ul>
<li>Immutable event logs (time-ordered records that afford the agent insight into the history of the employee they are checking)</li>
<li>Semantic tagging of transactions (to convey intent and interactions around pay configuration)</li>
<li>Rules versioning (to ensure the agent understands how to reconcile changes in the legislative environment against changes in configuration)</li>
<li>Scalable Audit APIs (to simplify data access for the agent and provide interactions with other services)</li>
</ul>
<p>With this level of diligence, when the auditors come knocking, the CFO is ready and can confidently prove every decision and every cent spent.</p>
<p>Now imagine what happens when this diligence is applied globally across industries. That&rsquo;s how supercycles unlock new markets.</p>
<h2 id="beyond-human-eyesight">Beyond-Human eyesight</h2>
<p>Agents can breathe new life into software you can&rsquo;t use just as well as it can solve for software you hate using - without having to rewrite it from scratch.</p>
<figure class="align-center ">
    <img loading="lazy" src="/img/in-post/macular_degen.jpeg#center"
         alt="A child fishing, seen with and without macular degeneration"/> <figcaption>
            <p>A 2014 study estimated ~10% of New Zealanders 45-85 suffer from macular degeneration.</p>
        </figcaption>
</figure>

<p>Macular Degeneration New Zealand estimates that 10% of the 45-85 age bracket suffers from macular degeneration. That&rsquo;s ~3-4% of the total population of New Zealand. What would it be worth to your company if you could increase the available market for your product by ~3-4%? If you&rsquo;re selling a B2B SaaS product and you&rsquo;re stuck with a system whose accessibility is poor, a 4% increase is massive.</p>
<p>Previously, unlocking that new market would be expensive! You either retrofit accessibility into a system that was never designed for it, using technology that was never built for it (anyone remember Adobe Flash? Good luck making that screen-reader compatible), or you rewrite significant portions or the entirety of that product!</p>
<p>With your own personal agent the story is very different. IBM is never going to update your particular version of lotus notes, and yet every fortnight, with an eye condition like macular degeneration, you&rsquo;ve got to fill in your time sheet. You&rsquo;re a skilled analyst, but this clunky, inaccessible user interface turns a ten-minute job into half an hour or more of struggle, every week, every fortnight, until you die or leave the company.</p>
<p>Not only can agents dissolve the accessibility-usability dilemma, they can give you a usable facade over unusable software. Your agent can consume the underlying APIs that Lotus Notes uses, unlocking a natural language interface to your timesheets. Whether it&rsquo;s text or voice, you ask your agent to fill in your timesheet, you&rsquo;ve worked on these three initiatives this week, and the time split is around 20%/40%/40%, and the agent converts that into the API calls needed to fill in your timesheet. If I were making ANY timesheet software today (#startup-idea), I&rsquo;d bin my UI and create MS Teams, Slack and Discord bots that convert your text or voice into valid timesheets. Not Hipchat or Flowdock though. No nice things for those people.</p>
<p>Flowdock aside, we have a moral imperative to make these systems accessible; it is the right thing to do.</p>
<p>This principle works for any legacy software that can make rapid accessibility gains, to say nothing of software built with accessibility at its core. Eyesight is just one example. Every tech supercycle has expanded access or created new markets, and with agents, we&rsquo;re about to see it happen again.</p>
<h2 id="accessibility-remains-a-moral-and-commercial-imperative">Accessibility remains a moral and commercial imperative</h2>
<p>Consider this: I&rsquo;m a company that today sells inaccessible software as a service. I charge $20 monthly, and New Zealand has about 5 million people. Adding 4% of that population to my serviceable addressable market, I can now sell to 200,000 new customers. If I win 5% of those, I can add 10,000 new subscribers and increase my annual recurring revenues by $2.4m. The total addressable market of New Zealand - that five million - is small fry. In June 2024, Sydney had about 5.5 million residents. Accessibility is money. Pure and simple.</p>
<p>These supercycles - PC, e-commerce, Cloud Computing, Mobile Web, and now AI each unlocked new markets with brand new users and unlocked new interactions that expanded existing markets. They made previously inaccessible systems accessible for those who were previously excluded.</p>
<p>The PC created markets, and e-commerce turned local and regional markets global. Cloud computing unlocked new markets by drastically reducing the barrier to entry for brand-new types of companies. AWS had a five- to seven-year head start with startups that used cloud computing to reach new markets faster than the incumbents could keep up. Mobile Web put a device in the pockets of billions of people who previously might have struggled to buy expensive computer hardware!</p>
<p>Enterprises responded to these market changes by either building new products native to the ideas of that supercycle or retrofitting legacy systems with the tech needed to play in that new market, even if the outcomes weren&rsquo;t as technically pure as building from scratch.</p>
<h2 id="how-to-build-affordances-for-agents">How to build affordances for agents</h2>
<p>Affordances in UI/UX include clear and consistent navigation patterns, breadcrumbs, and semantic markup that conveys intent and relationships. In back-end services, affordances include machine-readable APIs with consistent patterns and discoverable endpoints, semantic metadata, explicit error signalling and recovery protocols, data provenance, and separation of commands from queries. All of these things help humans use and build systems today!</p>
<p>All of them are necessary for agents to use and build those same systems tomorrow.</p>
<p>Consider building a significant integration into a product or system never designed for it. If you treat affordances for agents today, you will feel the same pain tomorrow as you felt building that integration. Maybe more so.</p>
<p>We already strive to build clean interfaces with clear documentation and unambiguous behaviours. We do this for humans and third-party developers who want to integrate with us.</p>
<p>The only difference is that tomorrow&rsquo;s third parties will be agents. Those agents will expect the same affordances we build today, and the customers those agents serve will expect that your software works with their agent (or your own).</p>
<p>So, what can you do today to best prepare? If you don&rsquo;t happen to be rewriting your whole product from scratch, what can you do to ensure that your product meets the prerequisites for agentic success?</p>
<h2 id="concrete-steps-teams-can-take-today">Concrete steps teams can take today</h2>
<h4 id="audit-current-affordances-to-identify-gaps-in-context-or-access">Audit current affordances to identify gaps in context or access.</h4>
<p>In surgery, an agent can only act safely if it knows what tools are available, what states the patient is in, and what the patient&rsquo;s vitals mean. If those affordances are missing, the agent is blind.</p>
<ul>
<li>Start by mapping the interaction surfaces of your products. Any place a human or machine can read, write or otherwise interact. Ask: If the user were an agent, would they have all of the information they need to understand what is possible here?</li>
<li>Look for missing cues; this might mean e.g. unclear navigation, inconsistent APIs or documentation or poorly or uncommunicated state changes.</li>
<li>Treat this like a penetration test, but for usability. You&rsquo;re trying to find where an agent would be blind or blocked because of missing affordances.</li>
</ul>
<h4 id="add-semantic-metadata-ensuring-every-entityevent-is-machine-readable">Add semantic metadata, ensuring every entity/event is machine-readable.</h4>
<p>Agents cannot guess intent. They cannot be aware of the conversations that were had in the room back during design time. They only have access to what you grant them at run time. Provide explicit signals for use that convey intent. Add semantic tags to your data models, events, and UI elements to make their meaning unambiguous.</p>
<p>For example, you might return a response to a UI that looks something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="p">{</span><span class="err">...</span><span class="p">},</span> <span class="c1">// omitted for brevity
</span></span></span><span class="line"><span class="cl">  <span class="nt">&#34;metadata&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;workflow_stage&#34;</span><span class="p">:</span> <span class="mi">3</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>In payroll, an agent can&rsquo;t guess that <code>&quot;workflow_stage&quot;: 3</code> means &ldquo;awaiting approval from a payroll manager&rdquo;. Your UI might store an enum that maps 3 to something a human might understand, but your agent won&rsquo;t know what to make of this. You might resolve this in a few different ways. One option might be to ship a representation of the enum class with every response, but I can hear the purists in the back groaning about extra bytes over the wire, so what if we just did this instead:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="p">{</span><span class="err">...</span><span class="p">},</span> <span class="c1">// omitted for brevity
</span></span></span><span class="line"><span class="cl">  <span class="nt">&#34;metadata&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;workflow_stage&#34;</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;workflow_description&#34;</span><span class="p">:</span> <span class="s2">&#34;Awaiting approval from a payroll manager&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This simple change tells us (and our agents) a lot! This is a payroll application, and we expect a manager to approve this task. An agent can then curry that information into whatever other process is needed. A small change can make a big difference.</p>
<h4 id="separate-commands-and-queries-to-clarify-state-changes-vs-information-retrieval">Separate commands and queries to clarify state changes vs information retrieval.</h4>
<p>In a legacy retrofit, an agent filling in a Lotus Notes timesheet should be able to read current entries without risk of accidentally writing changes. In surgery, separating &ldquo;monitor vitals&rdquo; from &ldquo;adjust anaesthetic&rdquo; presents a clear safety improvement.</p>
<p>Splitting reads from writes benefits modularity and encapsulation, scalability, and keeping interfaces small and clean. It can also substantially de-risk agent use of your software!</p>
<p>Separating reads from writes creates structurally clear and governable ways for agents to interact. Want a read-only agent? Scope its token to your read surface. Want your agent to plan before acting? Give it the context that API 1 is for planning and API 2 is for executing.</p>
<p>CQRS makes it easier for agents to plan, simulate, and validate actions before committing. We want agents to be cautious and measured, like human operators.</p>
<h4 id="expose-intent-surfaces-letting-agents-understand-why-actions-are-taken">Expose intent surfaces, letting agents understand why actions are taken.</h4>
<p>Agents are like my 10-year-old. He craves the &ldquo;why&rdquo; of things. Agents need that &ldquo;why&rdquo; as well, not just the &ldquo;what&rdquo;.</p>
<p>In our JSON example above, you saw that we exposed a metadata block in our API response. That metadata block is a crucial intent surface. Intent surfaces are metadata or API endpoints that explain the rationale behind specific actions we might take, are taking, or have taken in the past.</p>
<p>Consider this API response for a payroll adjustment</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;content&#34;</span><span class="p">:</span> <span class="p">{</span><span class="err">...</span><span class="p">},</span> <span class="c1">// omitted for brevity
</span></span></span><span class="line"><span class="cl">  <span class="nt">&#34;metadata&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;reason_code&#34;</span><span class="p">:</span> <span class="s2">&#34;overtime&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;policy_reference&#34;</span><span class="p">:</span> <span class="s2">&#34;HR-OT-2024&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>With a response like this, an agent understands that we&rsquo;ve adjusted pay because a staff member has worked overtime, and to ensure compliance, the agent can cross-reference (via RAG or e.g. an HTTP request to a document store) the overtime payment against the policy. It can do this as a matter of course for all overtime payments or only for anomalous overtime payments (because they are high, or low, or on an unexpected or unusual day of the week or for any other reason). The agent can do this check at audit time, or it can do it asynchronously while the pay run is still being worked on - so we can look back in history and spot issues AND prevent new problems from appearing.</p>
<h4 id="implement-provenance-hooks-to-track-data-origin-and-transformations">Implement provenance hooks to track data origin and transformations.</h4>
<p>Provenance is the audit trail for meaning. Every piece of data should carry its origin, transformation history, and trust level.</p>
<p>When data carries this information, agents can verify accuracy, detect anomalies and explain decisions to humans.</p>
<p>A medical agent deciding on a treatment plan (perhaps after the surgery we discussed earlier is complete) can trace lab results back to a specific machine, calibration date, and technician and flag if some aspect of that calibration might have a material impact on the patient&rsquo;s well-being.</p>
<p>These steps aren&rsquo;t solely about future-proofing in the abstract; they&rsquo;re about making your systems agent-ready now. The teams that start today will be the teams whose products feel inevitable across the next supercycle. Everyone else will be scrambling to retrofit under pressure.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Take yourself back to the year 2000 once more. The iPhone is coming. Android is nearly here. What would you do if you were there again? I believe that we are there today. You might argue about the moral rights and wrongs of AI, or you might think that we are in a massive bubble. You might be right. But the e-commerce supercycle lasted a full decade after the dotcom bubble burst. The bubble was a harbinger of a changing world.</p>
<p>We are at the start of a new supercycle. A supercycle of agents, that I believe will be bigger than cloud computing, bigger than the mobile web, bigger than e-commerce. Maybe even bigger than the PC.</p>
<p>The Helpful Web: humans and AI allies expanding access for everyone.
Accessibility isn&rsquo;t just inclusion — it&rsquo;s growth.
Audit your affordances, design for agents, make AI your company strategy.
In ten years, bypassing your agents will feel like malpractice.</p>
<h4 id="references">References</h4>
<p>[1] <a href="https://www.interaction-design.org/literature/topics/affordances?ep=ug0" target="_blank" rel="noopener noreferrer">What are Affordances?</a>

, IxDF</p>
<p>[2] <a href="https://static1.squarespace.com/static/5dcba244e3fb8952b16c79fd/t/5e9917df060d124f4a403e11/1587091469915/About&#43;Macular&#43;Degeneration&#43;A4.pdf" target="_blank" rel="noopener noreferrer">About Macular Degeneration</a>

, Macular Degeneration New Zealand</p>
]]></content:encoded></item><item><title>Architecture is gardening.</title><link>https://4lex.nz/posts/architecture-practice-1-gardeners/</link><pubDate>Mon, 08 Jul 2024 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/architecture-practice-1-gardeners/</guid><description>&lt;p&gt;Large software projects are living things. They grow, adapt, and sometimes run wild.&lt;/p&gt;
&lt;p&gt;The way we design them - and the way we design the organisations that build them - determines whether they flourish or wither.&lt;/p&gt;
&lt;p&gt;Over the years, I’ve come to see architecture not as a static blueprint or a master plan, but as an act of cultivation.&lt;/p&gt;
&lt;p&gt;This article is the first in a series exploring my personal philosophy of architecture: how we can guide complex systems, human and technical, with the same care and intention as a gardener tending a landscape.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Large software projects are living things. They grow, adapt, and sometimes run wild.</p>
<p>The way we design them - and the way we design the organisations that build them - determines whether they flourish or wither.</p>
<p>Over the years, I’ve come to see architecture not as a static blueprint or a master plan, but as an act of cultivation.</p>
<p>This article is the first in a series exploring my personal philosophy of architecture: how we can guide complex systems, human and technical, with the same care and intention as a gardener tending a landscape.</p>
<p>Here, I’ll start with the metaphor itself - why I believe architects are gardeners - before moving on in future pieces to define what architecture really is, how to build sustainable practices, and the principles and tactics that make them work.</p>
<p>The way we structure our communications with one another is the single biggest determinant of what our architecture will
look like when it&rsquo;s done.</p>
<p>You design a system that looks different to the way you structure your communications.
Over time, without great effort, that design will slowly be subsumed into one that more organically maps the
teams and business units that made it.</p>
<p>We have two choices open to us then:</p>
<ul>
<li>Plan, account for it and attempt to design around it</li>
<li>Let it happen</li>
</ul>
<p>It&rsquo;s not clear to me that either of these approaches are inherently wrong.</p>
<p>Expanding, our choice is whether we fight the change those communication pathways bring or whether we guide that same flow, co-opting its&rsquo;
force and nudging it left or right towards where we wish it. This philosophical question is not unlike a question many
gardeners have faced over the years. Gardening is an excellent metaphor for the activity of architecture.</p>
<h2 id="architects-are-gardeners-that-cultivate-renewable-systems-both-human-and-computer">Architects are gardeners that cultivate renewable systems (both human and computer)</h2>
<p>It is powerful and freeing to consider that we are all gardeners of systems that will outlast us.
Years before publishing his book, <a href="https://www.amazon.com/gp/product/1492077542?camp=1789&amp;creative=9325&amp;creativeASIN=1492077542&amp;linkId=b743cdb232b038666e078965899c8624" target="_blank" rel="noopener noreferrer">The Software Architect Elevator</a>

,
<a href="https://architectelevator.com/" target="_blank" rel="noopener noreferrer">Gregor Hohpe</a>

 describes <a href="https://www.enterpriseintegrationpatterns.com/ramblings/88_moviestararchitects.html" target="_blank" rel="noopener noreferrer">large socio-technical systems as gardens</a>

:</p>
<blockquote>
<p>Large-scale IT is much like a garden: things evolve and grow on their own. Weeds tend to grow the fastest. The role of
the gardener is to trim and prune what does not fit and to establish an overall balance and harmony in the garden,
keeping in mind plants&rsquo; needs, for example by planting shade-loving plants near large trees or bushes. A good gardener
is no dictatorial master planner and certainly does not make all the detailed decisions about which direction a strain
of grass should grow &ndash; Japanese gardens being a possible exception. Rather, the gardener sees him or herself as the care taker of a living ecosystem.</p>
</blockquote>
<p>The gardener is an actor within that system who tends to its&rsquo; health. Pruning, planting and maintaining from within the system itself.</p>
<p>This philosophy contrasts sharply with the French notion of gardeners as kings and gods outside of the garden they curate.
We&rsquo;ll explore the French approach and compare it to a traditional Japanese Zen garden where the mindset of that Zen gardener
is closer to the culture and approach architects should employ.</p>
<p>By acting as gardeners rather than emperor-kings, architects can guide large projects and embrace the flow of the project
rather than fighting the chaos inherent in the group hallucination these projects inevitably are.</p>
<h3 id="the-garden-at-diago-ji-kyoto">The Garden at Diago-ji, Kyoto</h3>
<p>Consider the garden at Diago-ji in Kyoto, pictured below. What do you notice about it?</p>
<p>How is it assembled? What structural elements can you identify and how do they come together to form the garden?</p>
<p>Do you find the garden aesthetically pleasing? Do you find it calming or exciting?</p>
<p>What level of effort do you imagine it took to plan the garden? To plant and to grow, and to maintain it?</p>
<figure class="align-center ">
    <img loading="lazy" src="/img/in-post/japanese-garden.jpg#center"
         alt="Photo by David Emrich on Unsplash"/> <figcaption>
            Daigo-ji, a Buddhist temple in Kyoto, Japan<p>Photo by <a href="https://unsplash.com/@davidemrich?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" target="_blank" rel="noopener noreferrer" class="figure-link">David Emrich</a>

 on <a href="https://unsplash.com/photos/green-leaf-tree-near-house-during-daytime-9a0S_8bU0lo?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" target="_blank" rel="noopener noreferrer" class="figure-link">Unsplash</a></p>
        </figcaption>
</figure>

<p>Fix those answers in your mind and then consider the garden constructed at The Palace of Versailles.</p>
<p>Consider the same questions about the effort taken to plan, plant, grow and maintain.</p>
<p>Consider the aesthetics. Consider the &ldquo;mouth-feel&rdquo; of both gardens. Where do they do the same things? Where are they different?</p>
<figure class="align-center ">
    <img loading="lazy" src="/img/in-post/french-imperial-garden.jpg#center"
         alt="Photo by Hugo Herrera on Unsplash"/> <figcaption>
            The Sun Kings&#39; Garden<p>Photo by <a href="https://unsplash.com/@hugomanito?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" target="_blank" rel="noopener noreferrer" class="figure-link">Hugo Herrera</a>

 on <a href="https://unsplash.com/photos/green-grass-field-with-fountain-during-daytime-9a0S_8bU0lo?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" target="_blank" rel="noopener noreferrer" class="figure-link">Unsplash</a></p>
        </figcaption>
</figure>

<p>This is an entirely different construct to the garden at Diago-ji yet they are both gardens, maintained by gardeners.
Both have plants and engineered constructions. Both are beautiful and satisfying to view, and yet outside, they are almost entirely different!</p>
<p>Why? What can we learn about the gardeners through viewing and considering each choice made in these gardens?</p>
<p><a href="https://philosophynow.org/issues/146/The_Philosophy_of_Japanese_Gardens" target="_blank" rel="noopener noreferrer">Sailee Khurjekar at philosophynow.org</a>

 can help us here.</p>
<p>They describe that</p>
<blockquote>
<p>Garden design in the Japanese tradition presents an alternative approach to the English tradition.
The properties of the natural objects are not altered to suit an aesthetic design, as an ‘English garden’ designer
might cut down a shrub so that it is neat and symmetrical, with the sole aim of pleasing the observer.
Instead, in a Japanese garden the organic properties are manipulated to enhance the naturalness of the objects in the garden.</p>
</blockquote>
<style type="text/css">
     
    .notice {
        --title-color: #fff;
        --title-background-color: #6be;
        --content-color: #444;
        --content-background-color: #e7f2fa;
    }

    .notice.info {
        --title-background-color: #fb7;
        --content-background-color: #fec;
    }

    .notice.tip {
        --title-background-color: #5a5;
        --content-background-color: #efe;
    }

    .notice.warning {
        --title-background-color: #c33;
        --content-background-color: #fee;
    }

     
    @media (prefers-color-scheme:dark) {
        .notice {
            --title-color: #fff;
            --title-background-color: #069;
            --content-color: #ddd;
            --content-background-color: #023;
        }

        .notice.info {
            --title-background-color: #a50;
            --content-background-color: #420;
        }

        .notice.tip {
            --title-background-color: #363;
            --content-background-color: #121;
        }

        .notice.warning {
            --title-background-color: #800;
            --content-background-color: #400;
        }
    }

    body.dark .notice {
        --title-color: #fff;
        --title-background-color: #069;
        --content-color: #ddd;
        --content-background-color: #023;
    }

    body.dark .notice.info {
        --title-background-color: #a50;
        --content-background-color: #420;
    }

    body.dark .notice.tip {
        --title-background-color: #363;
        --content-background-color: #121;
    }

    body.dark .notice.warning {
        --title-background-color: #800;
        --content-background-color: #400;
    }

     
    .notice {
        padding: 18px;
        line-height: 24px;
        margin-bottom: 24px;
        border-radius: 4px;
        color: var(--content-color);
        background: var(--content-background-color);
    }

    .notice p:last-child {
        margin-bottom: 0
    }

     
    .notice-title {
        margin: -18px -18px 12px;
        padding: 4px 18px;
        border-radius: 4px 4px 0 0;
        font-weight: 700;
        color: var(--title-color);
        background: var(--title-background-color);
    }

     
    .icon-notice {
        display: inline-flex;
        align-self: center;
        margin-right: 8px;
    }

    .icon-notice img,
    .icon-notice svg {
        height: 1em;
        width: 1em;
        fill: currentColor;
    }

    .icon-notice img,
    .icon-notice.baseline svg {
        top: .125em;
        position: relative;
    }
</style><div class="notice note" >
    <p class="notice-title">
        <span class="icon-notice baseline">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 128 300 300">
  <path d="M150 128c82.813 0 150 67.188 150 150 0 82.813-67.188 150-150 150C67.187 428 0 360.812 0 278c0-82.813 67.188-150 150-150Zm25 243.555v-37.11c0-3.515-2.734-6.445-6.055-6.445h-37.5c-3.515 0-6.445 2.93-6.445 6.445v37.11c0 3.515 2.93 6.445 6.445 6.445h37.5c3.32 0 6.055-2.93 6.055-6.445Zm-.39-67.188 3.515-121.289c0-1.367-.586-2.734-1.953-3.516-1.172-.976-2.93-1.562-4.688-1.562h-42.968c-1.758 0-3.516.586-4.688 1.563-1.367.78-1.953 2.148-1.953 3.515l3.32 121.29c0 2.734 2.93 4.882 6.64 4.882h36.134c3.515 0 6.445-2.148 6.64-4.883Z"/>
</svg>

        </span> Note </p><p>Note that for this purpose, English gardens are largely interchangeable with French ones - The Normans, victorious at
the Battle of Hastings in 1066 are close enough to King Louis XIV who would come 600 years later stylistically that
the comparison stands.</p></div>

<p>Where a Japanese garden initially presents as wild and untamed, a natural expression of nature run wild,
nothing could be further from the truth as Hohpe identifies.</p>
<p>It&quot;s clear to Khurjekar that a Japanese garden accentuates the natural state of the elements in that garden and in doing so,
imparts meaning into those elements as they are. This concept very in keeping with the Zen philosophy of the gardeners.
The concept of accentuating the natural state and imparting meaning is known as the art of reduction.</p>
<p>What would an architecture aligned to this concept look like?</p>
<p>Flow Architecture embodies many of the same ideas found in the Zen garden. (For more on Flow
architecture see the excellent <a href="https://medium.com/digital-anatomy/why-is-flow-important-909f2d9fc9bf" target="_blank" rel="noopener noreferrer">article</a>

 and <a href="https://www.oreilly.com/library/view/flow-architectures/9781492075882/" target="_blank" rel="noopener noreferrer">book</a>

 by James Urquhart)</p>
<p>Flow architecture advocates we represent our system as a series of nodes in a graph (rather than a stack), where each
node contains its&rsquo; own context and properties separate from the whole. A node is designed (manipulated) to enhance the natural properties
of both that node and the nodes connected to it.</p>
<p>The explicit design goal of a Japanese garden is to enable the viewer to observe all of the elements of the garden at
once. If the individual elements are too big or too small they force the observer out of the scene entirely
or make the observer zoom in so far that they lose sight of the broader picture.</p>
<p>Have you ever had a feeling when viewing an architecture diagram or document, that the level of abstraction is wrong?</p>
<p>How did that make you feel?</p>
<h3 id="the-gardens-of-the-palace-of-versailles">The Gardens of the Palace of Versailles</h3>
<p>At the Palace of Versailles we see that nature has been reshaped in distinctly
un-natural ways, that is to say, ways that are the opposite of what you might expect to see in nature. When I go hiking, I rarely see
shrubs on the trail formed into perfect geometric shapes. Looking at the palace garden we see that nature is formed into shapes pleasing to the
eye - aesthetics are still very important here, but instead of focusing on the natural qualities of the element, the element is shaped
into a way that accentuates the un-natural construction of the garden.</p>
<p>To re-emphasise Khurjekars&rsquo; assertion:</p>
<blockquote>
<p>Garden design in the Japanese tradition presents an alternative approach to the English tradition. The properties of
the natural objects are not altered to suit an aesthetic design, as an ‘English garden’ designer might cut down a
shrub so that it is neat and symmetrical, with the sole aim of pleasing the observer.</p>
</blockquote>
<p><a href="https://rmschwartz.wordpress.com/nature/gardens-in-les-miserables/garden-design-in-france-from-louis-xiv-to-victor-hugo/formal-gardens/" target="_blank" rel="noopener noreferrer">Robert Schwartz</a>


defines the French approach as &ldquo;Order over Nature&rdquo;.</p>
<p>Schwartz states that</p>
<blockquote>
<p>Gardens are reflections of the people who create them. By shaping nature, men can exert control over their surroundings
and create small paradises modeled after their own ideal of beauty.</p>
</blockquote>
<p>Systems too are reflections of their creators and maintainers! Schwartz describes the gardens at Versailles as</p>
<blockquote>
<p>The best representation of human will imposing itself on a natural landscape</p>
</blockquote>
<p>And ties that into the nature of the designer of that garden, King Louis XIV</p>
<blockquote>
<p>Louis XIV became an orchestrator, not only of nature but also of how visitors viewed this landscape.
Even in the King’s absence, visitors would follow a specific path through the gardens, seeing them as he wished them to be seen.
&hellip;
As the Sun King, he imposed absolute control over nature, subjugating wilderness to his own ideal of beauty.</p>
</blockquote>
<p>Bringing the French philosophy back to Architecture and applying this philosophy to the design of human and computer systems,
the French approach is the approach the industry have broadly taken from the birth of the industry through to perhaps the mid 2000s.</p>
<p>The prevailing system design philosophy was one of control. Of planning. The Waterfall way of working emphasises clear boundaries
between activity and a breakdown of process by function in an attempt to tame the chaos inherent in the large group hallucinations that software is.</p>
<p>This philosophical approach is that of The Architect of the Matrix, which Gregor Hohpe <a href="https://www.enterpriseintegrationpatterns.com/ramblings/88_moviestararchitects.html" target="_blank" rel="noopener noreferrer">describes</a>

 as</p>
<blockquote>
<p>the ultimate authority: he designed the Matrix (a computer program to simulate reality to humans being farmed by
machines as an energy source) and knows and controls everything. Enterprise architects are sometimes seen as such
a person - the all-knowing decision maker. Some even wish themselves into such a role,
partly because it is neat to be all-knowing and partly because it gets you a lot of respect.</p>
</blockquote>
<p>Hohpe also warns of the master planned approach,</p>
<blockquote>
<p>Even if the architect is a super smart person, he or she can base decisions only on those facts that are known to them.
In large companies, this inevitably means relying on PowerPoint slides or statements from middle management as it is
impossible to be in touch with all technology that is in place. Such an information channel to the supreme decision
maker tends to be heavily guarded by people who understand its value as an influencing vehicle, which results in the
architect being fed indirect, often biased information, as opposed to reality. Making decisions based on such a model is dangerous.</p>
</blockquote>
<h3 id="what-can-system-designers-learn-from-french-and-japanese-gardens">What can system designers learn from French and Japanese gardens?</h3>
<p>When the <a href="https://agilemanifesto.org/" target="_blank" rel="noopener noreferrer">Agile Manifesto</a>

 took off, the change in how software projects are run became less about controlling
the chaos and more about embracing it and redirecting it in the direction you choose - co-opting nature rather than fighting it,
yet the root philosophy is still one of control.</p>
<p><a href="https://teamtopologies.com/" target="_blank" rel="noopener noreferrer">Team Topologies</a>

 seeks to organise teams and communication pathways such that they mirror
the flow of value through the business - in other words seek the true value in what you&rsquo;re doing and align around that rather
than creating functional business areas that then struggle to work out how to pass parts of that value between each other.</p>
<p>In taking this bottom up rather than top down approach to system design, Team Topologies is closer to Zen then it is to Master-Planned.</p>
<p>In other words, rather then taming the nature of software development and imposing control over that nature,
we now try to follow a more philosophically Japanese approach where we manipulate the organic properties of software development
to enhance the naturalness of it.</p>
<p><a href="https://learnwardleymapping.com/" target="_blank" rel="noopener noreferrer">Wardley Mapping</a>

 also trends towards Zen in that it helps us identify the weeds in our system
(those parts of the system that have already commoditized) Hohpe described earlier as well as Hohpe&rsquo;s shade loving plants (perhaps accelerators towards value).</p>
<p>If the ways in which teams interact (Kanban, Agile, lowercase a-agile) slowly become more Zen-like in nature and less like we are
all very small Sun Kings, the practice of architecture too must become more Zen-like. I see that change finally coming with
concepts like <a href="https://teamtopologies.com/" target="_blank" rel="noopener noreferrer">Team Topologies</a>

, <a href="https://www.oreilly.com/library/view/flow-architectures/9781492075882/" target="_blank" rel="noopener noreferrer">Flow Architectures</a>

 and <a href="https://learnwardleymapping.com/" target="_blank" rel="noopener noreferrer">Wardley Mapping</a>

.</p>
<p>Software development as a group hallucination is best when it is creative, experimental, iterative and guided. Software development
is inherently unconstrained in a way that e.g. hardware development is not. Our processes should manipulate that unbounded-ness
and direct it in ways that enhance that natural beauty rather than trying to subjugate it.</p>
<p>In order for a system designer to build sustainable systems that work,
we have to understand how value flows through businesses and how we should best structure the communication of that value.
Having structured that communication we can design concepts around those flows and gently prune away the things that confuse
or distort that communication.</p>
<p>We must act and design in ways that enhance the innate qualities of the elements of our organisations and systems.
Doing so grants us the ability to place observers into those systems (customers, builders, managers, executives) who can
see the system for what it is without parts of the system overwhelming others in that view.</p>
<p>That is why architects are gardeners and not master planners.</p>
<h2 id="references-and-further-reading">References and further reading</h2>
<p>[1] <a href="https://www.amazon.com/gp/product/1492077542?camp=1789&amp;creative=9325&amp;creativeASIN=1492077542&amp;linkId=b743cdb232b038666e078965899c8624" target="_blank" rel="noopener noreferrer">The Software Architect Elevator Book</a>

 by Gregor Hohpe</p>
<p>[2] <a href="https://www.enterpriseintegrationpatterns.com/ramblings/88_moviestararchitects.html" target="_blank" rel="noopener noreferrer">enterpriseintegrationpatterns.com</a>

 - Hohpes&rsquo; article on movie star architects that precede his book</p>
<p>[3] <a href="https://architectelevator.com/" target="_blank" rel="noopener noreferrer">Gregor Hohpe</a>

 - Gregor Hohpes&rsquo; website</p>
<p>[4] <a href="https://unsplash.com/@davidemrich?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" target="_blank" rel="noopener noreferrer">Japanese Garden Imagery by David Emrich</a>

 - David Emrich on Unsplash</p>
<p>[5] <a href="https://unsplash.com/@hugomanito?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash" target="_blank" rel="noopener noreferrer">French Imperial Garden Imagery by Hugo Ferrera</a>

 - AXP Photography on Unsplash</p>
<p>[6] <a href="https://rmschwartz.wordpress.com/nature/gardens-in-les-miserables/garden-design-in-france-from-louis-xiv-to-victor-hugo/formal-gardens/" target="_blank" rel="noopener noreferrer">Formal Gardens</a>

 - France in the Age of Les Miserables, Robert Schwartz</p>
<p>[7] <a href="https://agilemanifesto.org/" target="_blank" rel="noopener noreferrer">Agile Manifesto</a>

 - Agile Manifesto for Software Development</p>
<p>[8] <a href="https://medium.com/digital-anatomy/why-is-flow-important-909f2d9fc9bf" target="_blank" rel="noopener noreferrer">Why is Flow Important</a>

 - James Urquhart</p>
<p>[9] <a href="https://www.oreilly.com/library/view/flow-architectures/9781492075882/" target="_blank" rel="noopener noreferrer">Flow Architectures</a>

 - James Urquhart</p>
]]></content:encoded></item><item><title>A framework for creating strategy</title><link>https://4lex.nz/posts/strategy-development-framework/</link><pubDate>Wed, 15 Jun 2022 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/strategy-development-framework/</guid><description>&lt;p&gt;Recently, I wrote my very first technology strategy. Overall I&amp;rsquo;d say the process went pretty well. Of course writing the strategy is intimidating to begin with, but once I started I quickly realised that the much more intimidating thing is actually working to implement the strategy after it&amp;rsquo;s written.&lt;/p&gt;
&lt;p&gt;I felt and still at times feel like fleeing to the woods to live as a wolf as a result of this realisation but I&amp;rsquo;m pretty sure my life insurance has exclusions for fleeing to the woods in order to live as a wolf. It&amp;rsquo;s not the family oriented response you think it could be, despite wolves being good family members that live in packs.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Recently, I wrote my very first technology strategy. Overall I&rsquo;d say the process went pretty well. Of course writing the strategy is intimidating to begin with, but once I started I quickly realised that the much more intimidating thing is actually working to implement the strategy after it&rsquo;s written.</p>
<p>I felt and still at times feel like fleeing to the woods to live as a wolf as a result of this realisation but I&rsquo;m pretty sure my life insurance has exclusions for fleeing to the woods in order to live as a wolf. It&rsquo;s not the family oriented response you think it could be, despite wolves being good family members that live in packs.</p>
<p>Anyway, these (broadly) are the steps I took to create my strategy, this article intends to describe systemically and procedurally, how you might create a technology strategy (or any other kind of strategy for that matter).</p>
<ol>
<li>Identify the stakeholders to the strategy</li>
<li>Interview the stakeholders to understand the scope/expectations</li>
<li>Draft terms of reference based on the outcome of 2.</li>
<li>Present the Terms of Reference to the stakeholders and seek endorsement</li>
<li>Based on the agreed terms of reference go forth and gather data</li>
<li>Use Confluence to build out a template starting from the Terms of Reference</li>
<li>Link to relevant frameworks/models through Confluence for reuse via other strategies</li>
</ol>
<style type="text/css">
     
    .notice {
        --title-color: #fff;
        --title-background-color: #6be;
        --content-color: #444;
        --content-background-color: #e7f2fa;
    }

    .notice.info {
        --title-background-color: #fb7;
        --content-background-color: #fec;
    }

    .notice.tip {
        --title-background-color: #5a5;
        --content-background-color: #efe;
    }

    .notice.warning {
        --title-background-color: #c33;
        --content-background-color: #fee;
    }

     
    @media (prefers-color-scheme:dark) {
        .notice {
            --title-color: #fff;
            --title-background-color: #069;
            --content-color: #ddd;
            --content-background-color: #023;
        }

        .notice.info {
            --title-background-color: #a50;
            --content-background-color: #420;
        }

        .notice.tip {
            --title-background-color: #363;
            --content-background-color: #121;
        }

        .notice.warning {
            --title-background-color: #800;
            --content-background-color: #400;
        }
    }

    body.dark .notice {
        --title-color: #fff;
        --title-background-color: #069;
        --content-color: #ddd;
        --content-background-color: #023;
    }

    body.dark .notice.info {
        --title-background-color: #a50;
        --content-background-color: #420;
    }

    body.dark .notice.tip {
        --title-background-color: #363;
        --content-background-color: #121;
    }

    body.dark .notice.warning {
        --title-background-color: #800;
        --content-background-color: #400;
    }

     
    .notice {
        padding: 18px;
        line-height: 24px;
        margin-bottom: 24px;
        border-radius: 4px;
        color: var(--content-color);
        background: var(--content-background-color);
    }

    .notice p:last-child {
        margin-bottom: 0
    }

     
    .notice-title {
        margin: -18px -18px 12px;
        padding: 4px 18px;
        border-radius: 4px 4px 0 0;
        font-weight: 700;
        color: var(--title-color);
        background: var(--title-background-color);
    }

     
    .icon-notice {
        display: inline-flex;
        align-self: center;
        margin-right: 8px;
    }

    .icon-notice img,
    .icon-notice svg {
        height: 1em;
        width: 1em;
        fill: currentColor;
    }

    .icon-notice img,
    .icon-notice.baseline svg {
        top: .125em;
        position: relative;
    }
</style><div class="notice note" >
    <p class="notice-title">
        <span class="icon-notice baseline">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 128 300 300">
  <path d="M150 128c82.813 0 150 67.188 150 150 0 82.813-67.188 150-150 150C67.187 428 0 360.812 0 278c0-82.813 67.188-150 150-150Zm25 243.555v-37.11c0-3.515-2.734-6.445-6.055-6.445h-37.5c-3.515 0-6.445 2.93-6.445 6.445v37.11c0 3.515 2.93 6.445 6.445 6.445h37.5c3.32 0 6.055-2.93 6.055-6.445Zm-.39-67.188 3.515-121.289c0-1.367-.586-2.734-1.953-3.516-1.172-.976-2.93-1.562-4.688-1.562h-42.968c-1.758 0-3.516.586-4.688 1.563-1.367.78-1.953 2.148-1.953 3.515l3.32 121.29c0 2.734 2.93 4.882 6.64 4.882h36.134c3.515 0 6.445-2.148 6.64-4.883Z"/>
</svg>

        </span> Note </p><p>This is all going to be a bit cerebral. The strategy is the cognition; that informational content that provides a bounded context, assumptions and input data about that content.</p>
<p>This page then, is meta-cognition. It’s about the process and procedures with which you might structure your thinking, synthesise large amounts of data, discard data that is immaterial to your cognition and crystalise and reduce the remainder down to guidance that can be given to your audience.</p>
<p>The intent of this page is not to be procedural, if you’re looking for a clickbait article of 9 top tricks to produce a killer strategy (Consultants HATE them!) then you’re going to be disappointed. This is all about developing your own method within a cognition framework that encourages a focus on context, assumptions and implications where the analysis of that context is deep enough and clear-eyed enough that</p></div>

<h2 id="corporate-context">Corporate Context</h2>
<p>Eben Hewitt, in his book <a href="https://www.goodreads.com/book/show/42414767-technology-strategy-patterns" target="_blank" rel="noopener noreferrer">Technology Strategy Patterns</a>

, states:</p>
<blockquote>
<p>In any business, there are patterns of work, at work within the business. These patterns are crucial to your success and their effectiveness may vary. Your job as a strategist is to understand the patterns and processes in your area of concern and use them to achieve your goals. Your job as a strategist trying to influence and create change will be vastly more difficult if you oppose these patterns and processes.</p>
</blockquote>
<p>In other words, don’t fight the plumbing. Understanding and describing the corporate context you operate within significantly increases your chances of success. Your goal is to use the existing patterns to change the patterns. Don’t fight the plumbing, co-opt it.</p>
<p>Hewitt goes on to say:</p>
<blockquote>
<p>The way to be successful in a company is to do something that matters to someone who matters. Projects that don’t matter to the people who matter are misaligned. Eventually, someone will ask about the line item on the budget spreadsheet. If nobody in the room knows what it is or why we’re doing it, odds are that it’ll be sidelined or cancelled. Aligning yourself with that project is a waste of your time and a waste of the company’s time. If you somehow manage to complete your misaligned project under the radar, you will soon find that your project was thriving only because it was under the radar. The impact to the company is once again a waste of time and resources as a result of the misspend, and a delay in the chance to complete the strategic stuff.</p>
</blockquote>
<p>Your first job then is to identify the people who matter. This is nothing more or less than old fashioned stakeholder management. Stakeholder management is crucial to your success in developing a strategy for the reasons described above. If you are not aligned with them, you will struggle to produce value.</p>
<div class="notice note" >
    <p class="notice-title">
        <span class="icon-notice baseline">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 128 300 300">
  <path d="M150 128c82.813 0 150 67.188 150 150 0 82.813-67.188 150-150 150C67.187 428 0 360.812 0 278c0-82.813 67.188-150 150-150Zm25 243.555v-37.11c0-3.515-2.734-6.445-6.055-6.445h-37.5c-3.515 0-6.445 2.93-6.445 6.445v37.11c0 3.515 2.93 6.445 6.445 6.445h37.5c3.32 0 6.055-2.93 6.055-6.445Zm-.39-67.188 3.515-121.289c0-1.367-.586-2.734-1.953-3.516-1.172-.976-2.93-1.562-4.688-1.562h-42.968c-1.758 0-3.516.586-4.688 1.563-1.367.78-1.953 2.148-1.953 3.515l3.32 121.29c0 2.734 2.93 4.882 6.64 4.882h36.134c3.515 0 6.445-2.148 6.64-4.883Z"/>
</svg>

        </span> Note </p><p>As a general rule, I tend to consider strategy work in timeboxes. Of the timebox I allocate, I will typically spend 15%-20% of the available time working with stakeholders.</p>
<p>At the start this might look like meeting to discover their needs and expectations, during the process it might look like giving them updates on progress or drawing out their feedback. At the end of the process it might look like individually briefing your stakeholders in a ‘meeting before the meeting’ style (more on that later).</p>
<p>The amount of influencing and managing you will have to do will depend on how closely aligned the things you are proposing are with the way your stakeholders think and feel. If you’re proposing a radical departure from the status quo, expect to spend more time talking to folks and ensuring they are on board.</p></div>

<div class="notice warning" >
    <p class="notice-title">
        <span class="icon-notice baseline">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="126 76.5 300 300">
  <path d="M297.431 324.397v-34.255c0-3.245-2.344-5.95-5.358-5.95h-32.146c-3.014 0-5.358 2.705-5.358 5.95v34.255c0 3.245 2.344 5.95 5.358 5.95h32.146c3.014 0 5.358-2.705 5.358-5.95Zm-.335-67.428 3.014-82.753c0-1.081-.502-2.524-1.674-3.425-1.005-.902-2.512-1.983-4.019-1.983h-36.834c-1.507 0-3.014 1.081-4.019 1.983-1.172.901-1.674 2.704-1.674 3.786l2.846 82.392c0 2.344 2.512 4.146 5.693 4.146h30.975c3.013 0 5.525-1.803 5.692-4.146Zm-2.344-168.39L423.34 342.425c3.683 7.032 3.516 15.686-.335 22.717-3.85 7.031-10.883 11.358-18.417 11.358H147.413c-7.534 0-14.566-4.327-18.417-11.358-3.85-7.031-4.018-15.685-.335-22.716L257.248 88.578C260.93 81.188 268.13 76.5 276 76.5c7.87 0 15.069 4.688 18.752 12.08Z"/>
</svg>

        </span> Warning </p><p>Not every leader recognises the centrality of strategy. These folks may just prefer to wheel and deal, valuing sensing and response over planning, implementing and measuring. They may be passive in your process, ignoring you as they hope you are a flash in the pan that eventually burns out. They may actively undermine you as your strategy encroaches on their percieved fiefdom, or they may seek to discredit you or your strategy. They may view you as an astronaut, so high up you can&rsquo;t see the daily operation of the business.</p>
<p>Be careful with these people. Include them in your stakeholder analysis. If they oppose you, it might be because they are right! They might know something you don&rsquo;t, so lean in and engage them. If you can convince them, they will be a strong ally, and if you can&rsquo;t, they will make your life difficult. Your stakeholder analysis will show you the approach you need to take with them.</p></div>

<h2 id="determining-stakeholders">Determining Stakeholders</h2>
<p>In order to achieve something meaningful, you must first understand the org chart and start at the top. Start with the CEO and the other members of your executive team. If your strategy does not matter to these people, it will fail. (Note that I&rsquo;m writing this in the context of a smaller business. Fewer than 1000 folks but more than 50. An organisation where you as a strategy-maker would reasonably expect to bump into a random executive team member in the kitchen at lunch time once or twice a week. If your organisation is bigger, replace C-Suite member with a suitable director level role e.g. VP, Head of, GM etc)</p>
<p>There are three groups of people with whom you must be aligned:</p>
<ul>
<li>The folks who will pay for it and stand on a stage and tell others that it’s important (i.e. your leaders and executive team)</li>
<li>The people who will execute it, and need to understand it well enough to execute it properly (the teams and individual contributors doing the work)</li>
<li>The people who will ignore or undermine it if their views, aspirations or concerns aren’t represented (your peers)</li>
</ul>
<p>You must have a 360-degree view of the organisation. That view will help you understand how your organisation works, how things get funded and where the bottlenecks might be.</p>
<h2 id="determining-drivers">Determining Drivers</h2>
<p>Identify which leaders in the business matter in terms of your strategy. These people will reside across the organisation. They won’t be restricted solely to your business unit. It’s not enough to work in the context of a single function of the business. You will have to learn to communicate and collaborate cross-functionally.</p>
<p>Consider the different parts of the value chain your business implements (if you&rsquo;re not familliar with value chain analysis - <a href="https://online.hbs.edu/blog/post/what-is-value-chain-analysis" target="_blank" rel="noopener noreferrer">What is a value chain analysis?</a>

 is a great resource) - Inbound Logistics, Operations, Outbound Logistics, Marketing and Sales and After-Sales Service (consider the supporting functions as well). Your strategy will not be the right strategy, and will not be supported or effective if you do not consult key leaders in these organisations.</p>
<p>It’s not practical to consult with everyone, and you should avoid rule by comittee. If you read anything in your strategy that looks like it could it might have come from a cat poster you saw at the dentist, it’s a sign that you’ve gone too far down that track.</p>
<p>Of the folks, you’re thinking about talking to, who stands out as powerful? Good leaders will try and make it look like they love all their reports equally, in reality, they do not. Reaching out to these people for their feedback and input has the additional benefit of making them aware of your strategy.</p>
<p>A fictional example, provided by Eben Hewitt in his book, <a href="https://www.goodreads.com/book/show/42414767-technology-strategy-patterns" target="_blank" rel="noopener noreferrer">Technology Strategy Patterns</a>

:</p>
<blockquote>
<p>The mergers and acquisitions team might be two people in a room with no budget to buy anything and little of consequence to do. If the view is that building things ourselves is too risky and error prone, the M&amp;A team might be very large and well funded (i.e. powerful) and busy buying another company every week. In such a case it’s important to consult with them as their input matters more. Your strategy should reflect that.</p>
</blockquote>
<h3 id="stakeholder-matrix">Stakeholder Matrix</h3>
<p>Different stakeholders will have different roles in the context of the development of your strategy.</p>
<p>Create a list of all your stakeholders with two columns:</p>
<h4 id="influence">Influence</h4>
<p>Influence refers to how important this person’s support is. What is their level of control over your destiny? Do they control funding or determine timelines or other constraints? How able are they to materially change the detail or direction of your strategy?</p>
<h4 id="impact">Impact</h4>
<p>Impact refers to the degree to which, when realised, your strategy will impact them. If your strategy is to consolidate multiple products into a single product, your strategy will have a large impact on the sales, product and marketing teams. If you’re proposing we outsource software development to a third party, your impact on the members of the technology department will be extreme.</p>
<p>Score each stakeholder you know of on a scale of one to five for each column and generate a 2x2 matrix.</p>
<p><img alt="A classic stakeholder matrix" loading="lazy" src="/img/in-post/stakeholder_matrix.png"></p>
<h3 id="quadrants">Quadrants</h3>
<p>I really love the wording Hewitt uses in <a href="https://www.goodreads.com/book/show/42414767-technology-strategy-patterns" target="_blank" rel="noopener noreferrer">Technology Strategy Patterns</a>

 to describe the different ways in which you should interact with folks depending on the quadrant they are situated within:</p>
<h4 id="monitor">Monitor</h4>
<p>Ask them on a regular basis how their work is going, and what’s coming up for them. Note what might inform your work. Check in occasionally about their work and yours. If these folks have a broader understanding of what you are doing and they understand why it can create support that may come in handy later.</p>
<h4 id="maintain-confidence">Maintain Confidence</h4>
<p>Invite them to steering meetings for your strategy. Send them updates on your progress. Be sure they understand the milestones and know how you are tracking against them. Ensure they understand and approve of your metrics for success. Ask them for insights that might modify your strategy. Follow up on their continued buy-in on your roadmap. Discuss your progress regularly and in detail.</p>
<h4 id="keep-informed">Keep Informed</h4>
<p>Invite them to larger forums for updates, such as town halls. Talk with them about work they may need to prepare, such as communications, training, departmental updates and so on. Go to their meetings to present specifically to their teams.</p>
<h4 id="collaborate">Collaborate</h4>
<p>Actively work with these people on a regular basis in full partnership to co-create the ideas and execution plans within your strategy.</p>
<h2 id="setting-terms">Setting Terms</h2>
<p>Identifying stakeholders makes your life as a strategist easier. You have a list of people who care to different degrees, and you have an idea about what drives them, and the relationships they value.</p>
<p>The next job is to listen deeply to them. I call this process setting terms. The metaphor I use here is an investment one. Term sheets are an investment tool whose intent is to clearly outline the details of a deal. They describe the parties, they describe the agreement and they describe the investment.</p>
<p>You as a strategist are investing a portion of your life into the creation of this strategy. Creating strategy is a time-intensive thing, that time is very valuable in terms of salary, but even more valuable in terms of the opportunity cost. If you might spend six weeks creating a strategy - what else could you do with that time?</p>
<p>Providing a term sheet upfront, before you begin work in earnest will give you the confidence to know that you’re:</p>
<ul>
<li>Asking and answering the right questions</li>
<li>Supporting the right activities</li>
<li>Creating space for the right conversations</li>
</ul>
<p>A term sheet is a powerful statement of support that will help you in the creation of your strategy. It’s a document that clearly articulates what you’re doing and why, and who your backers are. This can really help get traction with folks you need support from - it’s an instant way of helping them understand what you’re doing and create shared goals.</p>
<h3 id="collecting-data-via-stakeholder-interviews">Collecting data via Stakeholder Interviews</h3>
<p>Your next step is to collect the data you need to understand your terms of engagement.</p>
<p>With your stakeholder analysis, you have the information to know who your key stakeholders are. Your next step is to meet with those folks and listen to them.</p>
<p>The size and rough impact of your strategy will dictate how wide you need to go, as a starter, I’d book half-hour sessions with everyone who you have added to the ‘collaborate’ bucket. Time permitting, you should also interview all of the folks you’ve added to the ‘maintain confidence’ bucket. I don’t recommend interviewing either the ‘monitor’ or ‘keep informed’ buckets - there will be too many people in this group to interview and the resulting term sheet will be diluted by too many voices - strategy by committee is ineffective, so too are term sheets by committee.</p>
<h4 id="what-do-i-ask-them">What do I ask them?</h4>
<p>Your goal in these interviews are two:</p>
<ul>
<li>
<p>Create confidence in the interviewee that you are listening, and that you hear and understand their point of view</p>
</li>
<li>
<p>Find out the general and specific areas that your stakeholders want you to address in your work, as well as an idea of outcomes, how they might want your strategy to be implemented as well as their preferred way of interacting with and understanding your strategy</p>
</li>
<li>
<p>Here are the questions I asked my stakeholders:</p>
</li>
<li>
<p>What do you need from the digital strategy?</p>
</li>
<li>
<p>What are the key points you want the strategy to address?</p>
</li>
<li>
<p>What kinds of concrete outcomes or activities should we be able to derive directly from strategy?</p>
</li>
<li>
<p>How do you want to feel after having read, been presented or otherwise consumed it?</p>
</li>
</ul>
<div class="notice warning" >
    <p class="notice-title">
        <span class="icon-notice baseline">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="126 76.5 300 300">
  <path d="M297.431 324.397v-34.255c0-3.245-2.344-5.95-5.358-5.95h-32.146c-3.014 0-5.358 2.705-5.358 5.95v34.255c0 3.245 2.344 5.95 5.358 5.95h32.146c3.014 0 5.358-2.705 5.358-5.95Zm-.335-67.428 3.014-82.753c0-1.081-.502-2.524-1.674-3.425-1.005-.902-2.512-1.983-4.019-1.983h-36.834c-1.507 0-3.014 1.081-4.019 1.983-1.172.901-1.674 2.704-1.674 3.786l2.846 82.392c0 2.344 2.512 4.146 5.693 4.146h30.975c3.013 0 5.525-1.803 5.692-4.146Zm-2.344-168.39L423.34 342.425c3.683 7.032 3.516 15.686-.335 22.717-3.85 7.031-10.883 11.358-18.417 11.358H147.413c-7.534 0-14.566-4.327-18.417-11.358-3.85-7.031-4.018-15.685-.335-22.716L257.248 88.578C260.93 81.188 268.13 76.5 276 76.5c7.87 0 15.069 4.688 18.752 12.08Z"/>
</svg>

        </span> Warning </p><p>It’s possible to produce a good strategy without going through this process, but your chances of</p>
<ul>
<li>Failing to build and maintain confidence with stakeholders</li>
<li>Producing a strategy that cannot be acted on</li>
<li>Producing a strategy that does not address the concerns of the business</li>
</ul>
<p>increase <strong>dramatically</strong> if you do not know well, and are not constantly talking to your stakeholders.</p></div>

<p>With the raw data from these interviews collected, you can begin to boil it down into terms. You may have to go back to clarify several times. You may have ideas from stakeholders that clash or are incompatible. That’s okay! Add them to the terms regardless. One of the benefits of this document is that it will make clear where your stakeholders do not agree, the terms will give you an option to work through and solve that disagreement in a neutral and unbiased manner. At the same time, going through that process with your stakeholders will build trust - trust is a critical resource for you to manage.</p>
<h3 id="closing-on-the-terms">Closing on the terms</h3>
<p>With a draft term sheet in hand, your next step is to get agreement. Your end goal is a meeting in which all of your stakeholders express that there is nothing particularly controversial or bold in the terms and that it all makes complete sense - it’s the natural thing to do!</p>
<p>That’s not to say your terms aren’t radical! The strategy should be strategic! The point is that by the time you get the final meeting to ratify the terms, all of your stakeholders have seen them a few times. They see themselves in the terms as a result of your interviews with them. They see the needs they express are being met and even some needs that they didn’t express (helpfully their colleagues expressed those ones!).</p>
<p>There shouldn’t be much discussion at your final meeting. You should open by reviewing the terms and the process that got you all there, and then ask for ratification. The meeting should feel like a fait accompli.</p>
<p>How is this possible? You should be having the meeting before you have the meeting.</p>
<p>Book a second round of meetings with the stakeholders you initially spoke with, and brief them on the term sheet you’ve developed. Use this time to pay attention to their individual needs and answer any questions they might have. This is a good opportunity to road test your ideas and ensure that you have drawn the right conclusions, and synthesised the many different angles of feedback from your stakeholders appropriately. For more detail on pre-meetings, see <a href="https://hbr.org/2016/10/when-its-worth-having-a-meeting-before-your-meeting" target="_blank" rel="noopener noreferrer">When it's worth having the meeting before the meeting</a>

 fair warning, it’s HBR and very American.</p>
<h2 id="building-your-strategy">Building your strategy</h2>
<p>With a group of folks behind you and an agreed outcome, the next step is the fun part! Now you get to create your strategy and show your vision, knowing that the whole business is behind you and wants you to succeed!</p>
<p>I believe that good strategy has two key jobs:</p>
<ol>
<li>Share Context</li>
<li>Make Assertions</li>
</ol>
<p>The goal of a strategy is to clearly articulate a situation and plot a course through it. The nature of any strategy is that it is actionable. It guides us. In the same vein, every statement you make should be falsifiable. The thing that differentiates strategy from vision is action.</p>
<p>Your strategy should primarily consist of assertions. With the full body of context behind you, you can draw a clear line between the context you observe and your assertions about how to respond to that context.</p>
<div class="notice info" >
    <p class="notice-title">
        <span class="icon-notice baseline">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="92 59.5 300 300">
  <path d="M292 303.25V272c0-3.516-2.734-6.25-6.25-6.25H267v-100c0-3.516-2.734-6.25-6.25-6.25h-62.5c-3.516 0-6.25 2.734-6.25 6.25V197c0 3.516 2.734 6.25 6.25 6.25H217v62.5h-18.75c-3.516 0-6.25 2.734-6.25 6.25v31.25c0 3.516 2.734 6.25 6.25 6.25h87.5c3.516 0 6.25-2.734 6.25-6.25Zm-25-175V97c0-3.516-2.734-6.25-6.25-6.25h-37.5c-3.516 0-6.25 2.734-6.25 6.25v31.25c0 3.516 2.734 6.25 6.25 6.25h37.5c3.516 0 6.25-2.734 6.25-6.25Zm125 81.25c0 82.813-67.188 150-150 150-82.813 0-150-67.188-150-150 0-82.813 67.188-150 150-150 82.813 0 150 67.188 150 150Z"/>
</svg>

        </span> Info </p><p>If your argument cannot be proven correct or incorrect, it cannot be measured and so cannot guide behaviour - strategies are about guiding behaviour.</p></div>

<p>In other words, the contents of your strategy and the points you address are defined by your term sheet. The way that you respond to the term sheet should be informed by the context. That response is your strategy.</p>
<p>The specifics of how you write and communicate your strategy are entirely up to you, and I offer the following advice:</p>
<ul>
<li>Keep talking to stakeholders, be visible in what you are doing and seek feedback throughout the process</li>
<li>Your strategy should be practical. It should be easy for folks to read it and answer their own questions about what they need to do. The reader’s next steps should be clear to them.</li>
<li>You can make those next steps clear, by making clear assertions that are informed by context. These may take the form of “Given X, we should Y, therefore we need to Z.</li>
<li>With your writing, you should be optimising for the reader. Optimise for them, their time, the way they break down and understand information. Optimising for the person reading your strategy is much more difficult then optimising for the writer!</li>
</ul>
<div class="notice tip" >
    <p class="notice-title">
        <span class="icon-notice baseline">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="300.5 134 300 300">
  <path d="M551.281 252.36c0-3.32-1.172-6.641-3.515-8.985l-17.774-17.578c-2.344-2.344-5.469-3.711-8.789-3.711-3.32 0-6.445 1.367-8.789 3.71l-79.687 79.493-44.141-44.14c-2.344-2.344-5.469-3.712-8.79-3.712-3.32 0-6.444 1.368-8.788 3.711l-17.774 17.579c-2.343 2.343-3.515 5.664-3.515 8.984 0 3.32 1.172 6.445 3.515 8.789l70.704 70.703c2.343 2.344 5.664 3.711 8.789 3.711 3.32 0 6.64-1.367 8.984-3.71l106.055-106.056c2.343-2.343 3.515-5.468 3.515-8.789ZM600.5 284c0 82.813-67.188 150-150 150-82.813 0-150-67.188-150-150 0-82.813 67.188-150 150-150 82.813 0 150 67.188 150 150Z"/>
</svg>

        </span> Tip </p><p>When using lists, ensure they are <a href="https://en.wikipedia.org/wiki/MECE_principle" target="_blank" rel="noopener noreferrer">MECE</a></p>
<p>(pronounced “mee-see”)
MECE is an acronym for Mutually exclusive, collectively exhaustive.
MECE dictates the content, not the format of your list. Ensure that you are constructing your content such that it relates to a specific audience.</p>
<p>Here are some examples.
The following two lists are MECE:</p>
<ul>
<li>True, False</li>
<li>Summer, Autumn, Winter, Spring</li>
</ul>
<p>The following two lists are not MECE:</p>
<ul>
<li>Spruce, Cedar, Oak</li>
<li>Light, Dark, Bread</li>
</ul>
<p>Lists that are not MECE invite doubt into the mind of your reader: “Are these all the options? Is there a missing concept here? Are the things being listed comparable?”</p>
<p>Consider another good example from <a href="https://www.goodreads.com/book/show/42414767-technology-strategy-patterns" target="_blank" rel="noopener noreferrer">Technology Strategy Patterns</a></p>
<p>, the following formula:</p>
<p>Revenue - Cost = Profit. Free Cash Flow</p>
<p>Hewitt explains that</p>
<blockquote>
<p>this formula is not MECE because free cash flow is at a different level then the others. It is true that free cash flow is an important financial measurement, but that is unrelated to this equation. Even though they all appear to be related to “stuff about money in a company” the concept is not sufficiently directed to an audience, for a goal.</p>
</blockquote></div>

<p>Documenting your strategy</p>
<p>Regardless of the content of your strategy, it’s going to involve words. Knowledge. Ideas. These things will evolve and grow over time. You will need to store these things somewhere visible and accessible, searchable and editable. You will need to engage in conversation pertaining to specific things you have said. You will need to demonstrate that you are listening and adapting to feedback.</p>
<p>Your company&rsquo;s knowledge management solution is the place for this. Notion, Confluence, find a space or create one, and put your strategy there. Your strategy being words, will be wordy, and few people may want to read all of it. That’s okay. You’re creating a reference.</p>
<p>Once you have strategy published and it’s some degree of complete, you’ll be able to create products from that strategy to suit the audience and the message.</p>
<p>Consider the words in your knowledge managent solution like a big bowl of dough. That&rsquo;s a lot of calories. It&rsquo;s big, carb-heavy. Could take some time to get through in it’s current form. Sweet Jesus that’s a lot of calories. With this documentation you are creating an encyclopaedia for folks to take what they need. We don&rsquo;t expect anyone (except my seven year old becuase he loves it) to take the encyclopaedia from the shelf and read it from start to finish in one sitting.</p>
<div class="notice warning" >
    <p class="notice-title">
        <span class="icon-notice baseline">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="126 76.5 300 300">
  <path d="M297.431 324.397v-34.255c0-3.245-2.344-5.95-5.358-5.95h-32.146c-3.014 0-5.358 2.705-5.358 5.95v34.255c0 3.245 2.344 5.95 5.358 5.95h32.146c3.014 0 5.358-2.705 5.358-5.95Zm-.335-67.428 3.014-82.753c0-1.081-.502-2.524-1.674-3.425-1.005-.902-2.512-1.983-4.019-1.983h-36.834c-1.507 0-3.014 1.081-4.019 1.983-1.172.901-1.674 2.704-1.674 3.786l2.846 82.392c0 2.344 2.512 4.146 5.693 4.146h30.975c3.013 0 5.525-1.803 5.692-4.146Zm-2.344-168.39L423.34 342.425c3.683 7.032 3.516 15.686-.335 22.717-3.85 7.031-10.883 11.358-18.417 11.358H147.413c-7.534 0-14.566-4.327-18.417-11.358-3.85-7.031-4.018-15.685-.335-22.716L257.248 88.578C260.93 81.188 268.13 76.5 276 76.5c7.87 0 15.069 4.688 18.752 12.08Z"/>
</svg>

        </span> Warning </p><p>Many people will tell you “Writing is a waste of time! Nobody will read it!” I disagree. Writing is the way you scale your career. Writing is the way you scale your business. Writing is how you create impact and share ideas. It is timeless and durable.</p>
<p>If you get your term sheet right, people will read it. More then that, they will want to.</p></div>

<h3 id="presenting-your-strategy">Presenting your strategy</h3>
<p>You cannot ask sixty people to sit on a teams call and listen to you read out loud, no matter how velvety smooth your voice is. When it comes time to present your ideas, take a slice. Cut off a piece of dough and shape it and bake it however you need.</p>
<p>In this case, I would recommend a slide deck or shorter written summaries - think flash cards, not short stories. You can create as many slices as you need, that target and speak to as many audiences as you need, to any level of time. Having the full strategy written and held elsewhere makes it infinitely flexible when it comes time to present, and that is a very powerful capability to have in your back pocket.</p>
<p>There are two facets and five cuts you might like to consider as default options. Pick one facet and one cut, and you have a purpose built communication tool specific to your audience and their context.</p>
<p>Facets:</p>
<ul>
<li>Technical</li>
<li>Non-Technical</li>
</ul>
<p>Audience Cuts:</p>
<ul>
<li>Board</li>
<li>Executive Team</li>
<li>Department Managers</li>
<li>Entire Department</li>
<li>Entire Business</li>
</ul>
<p>To produce individual decks addressing all of the combinations of facet and audience is made much easier by having the complete documentation live elsewhere. It becomes a case of pulling the relevant data and message into each deck - and there is no need to do them all at the start, it takes so little time to recast that you can do it as needed, rather than upfront.</p>
<h3 id="implementing-your-strategy">Implementing your strategy</h3>
<p>This section is left intentionally blank, as an exercise for the reader.</p>
<h3 id="summary">Summary</h3>
<p>Creating strategy is hard work. Implementing it is harder!</p>
<p>Your strategy will live within the corporate context of your organisation. It will be dictated by your organisational design and the individuals who fit within that design.</p>
<p>You will need to identify the folks who matter to your strategy, and win them over. Further, you&rsquo;ll need to manage stakeholders with varying goals and varying centrality to your strategy, and you will need to work with them and their processes, not against them.</p>
<p>Consider that in writing your strategy you are creating a desk reference on where we are going. Expect to take that volume and streamline it for the time, place and audience - and optimise for them, not for you.</p>
<h4 id="references-and-further-reading">References and further reading</h4>
<p>[1] <a href="https://www.goodreads.com/book/show/42414767-technology-strategy-patterns" target="_blank" rel="noopener noreferrer">Technology Strategy Patterns</a>

 - Eben Hewitt</p>
<p>[2] <a href="https://online.hbs.edu/blog/post/what-is-value-chain-analysis" target="_blank" rel="noopener noreferrer">What is a value chain analysis?</a>

 - Tim Stobierski, Harvard Business School</p>
<p>[3] <a href="https://hbr.org/2016/10/when-its-worth-having-a-meeting-before-your-meeting" target="_blank" rel="noopener noreferrer">When it's worth having the meeting before the meeting</a>

 - Andy Molinksy, Harvard Business Review</p>
<p>[4] <a href="https://en.wikipedia.org/wiki/MECE_principle" target="_blank" rel="noopener noreferrer">MECE</a>

 - Wikipedia</p>
]]></content:encoded></item><item><title>The Role of a Software Developer</title><link>https://4lex.nz/posts/the-role-of-a-software-developer/</link><pubDate>Wed, 01 Jun 2022 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/the-role-of-a-software-developer/</guid><description>&lt;p&gt;What if the role of the software developer is not actually about writing software?
What if the most important thing I can do today isn&amp;rsquo;t to write a sweet recursive function that delights and confuses the next human that reads it in equal measure?
As our industry has matured, the roles of technical staff in an organisation have changed. Increasingly, companies require that technical folk interact with customers. Some people like this. Some don&amp;rsquo;t.
Let&amp;rsquo;s have a brief chat about the opportunities and costs of this happening and what that might mean for your progression through the industry and your life.&lt;/p&gt;</description><content:encoded><![CDATA[<p>What if the role of the software developer is not actually about writing software?
What if the most important thing I can do today isn&rsquo;t to write a sweet recursive function that delights and confuses the next human that reads it in equal measure?
As our industry has matured, the roles of technical staff in an organisation have changed. Increasingly, companies require that technical folk interact with customers. Some people like this. Some don&rsquo;t.
Let&rsquo;s have a brief chat about the opportunities and costs of this happening and what that might mean for your progression through the industry and your life.</p>
<h3 id="r-e-v-e-n-u-e">R E V E N U E</h3>
<p>Software developers, and technical people of all stripes, for that matter, have the same role that every other employee in the company has. Solve problems for customers that customers will pay for. Do so sustainably, do so responsibly, and do so ethically. All of our roles exist to bring value to the customer and bring value to our teams. That&rsquo;s it. Spoiler, that&rsquo;s the essence of the entire article.</p>
<p>It&rsquo;s important to note that I don&rsquo;t mention writing code. We&rsquo;re not talking about APIs or Microservices. Databases? Design? Don&rsquo;t care. These are tools that we use. The same way that Vic on the social media team uses short-form content to inform folk of the value their company brings. Just like we collaborate to build a marketing website or a CMS. Just like the fine folks over in finance, we make sure we can be sustainable in the value we bring to customers so that we can continue to work away at those problems.</p>
<h3 id="increasingly-customer-focused">Increasingly Customer Focused</h3>
<p>It&rsquo;s no accident that over the past few years, as a collective, we&rsquo;ve focused on bringing back-office functions forward. Even five years ago, sitting in front of a customer to talk about something I&rsquo;ve built would have terrified me. What if they don&rsquo;t like the software? What if they noticed that bug I couldn&rsquo;t fix in time? If you haven&rsquo;t ridden the &ldquo;Oh no, they&rsquo;re on the page with the column that&rsquo;s three pixels out&rdquo; roller coaster, are you even a thrill junkie? They didn&rsquo;t even notice that side of the page because they were focused on the cart.</p>
<p>Getting back-office staff (technical or not) out in front of customers has driven innovation and improved the quality of the products we create. The empathy that we feel for our users has grown. I used to have a CEO that said, &ldquo;I don&rsquo;t need to pay for design staff because we&rsquo;re a monopoly. Our users will use our software anyway, so it doesn&rsquo;t have to be good; it has to work&rdquo;. Inspiring stuff.</p>
<p>Ultimately, with any for-profit enterprise, this comes down to money. These techniques produce better outcomes for the humans that pay for them. Better solutions mean more revenue, better retention, and so on. On an individual level, I like to think that we here care more about the people involved than the dollars. We here are deeply passionate about producing something extraordinary that improves people&rsquo;s lives, but I may just be projecting!</p>
<p>Getting out in front of customers, engaging with other parts of your business and generally getting away from your desk a bit more brings opportunities.</p>
<p>Being close to the customer reduces rework. If you know well what people want, it&rsquo;s easier to identify that we&rsquo;re building the wrong thing or that the thing is being built in the wrong way.</p>
<p>Talking to customers is also a great way of getting early feedback, and the benefits of more rapid input in the software we make are well known. It&rsquo;s easier to stay aligned as a team, and it&rsquo;s easier to work with product and design folks because of that alignment. Being close to the customer is like greasing the wheels that way.</p>
<p>Is career progression important to you? Being close to the customer cultivates trust with the broader business because you&rsquo;re not just those speccy folks that want to talk about RAM and AZURE. You&rsquo;re the people closest to the customer. You demonstrably care about and advocate for your customers. Those above us who make decisions about us and our careers will see and reward that.</p>
<h3 id="afk">AFK</h3>
<p>This brave new world does bring challenges. The big one is time management. Time away from keyboards is valuable - but there are already reasons for us to be away from our desks. Meetings are the most common complaint. Reviews, plannings and refinements. Sitting with stakeholders, sitting with customers, general admin. Sometimes it feels like we already spend more time talking than doing work.</p>
<p>It can be pretty demoralising if you measure your productivity or worth by lines of code or the number of releases. Lifting your head and treating time away as an investment in creating better (if fewer) outcomes can help with that. Better still, judge yourself by the impact you make, not by any technical metric.</p>
<h3 id="parting-thoughts">Parting Thoughts</h3>
<p>The next five years are looking good for us. We are people who produce solutions for customers through code, and in that space, demand will exceed supply for quite some time. With trends like DevSecOps, we&rsquo;ve seen teams of specialists turn into cross-functional teams. With design thinking, we&rsquo;ve seen groups of people who previously thought they had nothing to do with a customer&rsquo;s experience realise that we all have an impact. That impact might be a workflow in an app or the pricing model we sometimes inflict on users. Self-sufficient, sustainable product teams are becoming more common. Business people who in the past didn&rsquo;t have a lot to do with the software are becoming more involved, and groups of technical folk are gaining access to more institutional knowledge, better links into the business and faster feedback loops.</p>
<p>So what do I say in parting? You are not the code you write nor the technical solution you create; that is immensely positive! We&rsquo;re seeing people begin to value us as complete humans, not as mechanical turks running on undersized EC2s that produce software. We&rsquo;re seeing that the IT Crowd is now a comedy show and no longer a manual on what to expect from technical staff and how to treat them. With that comes leverage for us, personally and professionally. Think about how that leverage applies in your role and use it! Use it to get better outcomes for the people you create for and better results for yourself!</p>
]]></content:encoded></item><item><title>Introducing the QUERY HTTP Verb</title><link>https://4lex.nz/posts/the-query-method/</link><pubDate>Thu, 25 Nov 2021 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/the-query-method/</guid><description>&lt;h2 id="introducing-query"&gt;Introducing QUERY&lt;/h2&gt;
&lt;p&gt;The IETF has published a document detailing the QUERY verb. The Query verb neatly solves the problem of asking APIs for LOTS of data or &lt;em&gt;conditional&lt;/em&gt; data.
In other words, QUERY enables API providers to provide a means for clients to ask the server about data, where the client isn&amp;rsquo;t sure what data is available.&lt;/p&gt;
&lt;h2 id="a-worked-example"&gt;A worked example&lt;/h2&gt;
&lt;p&gt;Working in Agri-tech, I often want to find out information about animals.
Unfortunately, that information can live across many systems, with many identifiers (primary keys or other).
In one context I&amp;rsquo;ve experienced, Dairy Animals can be identified and referenced by five or more different identifiers - so what do you do if you don&amp;rsquo;t know all of them?&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="introducing-query">Introducing QUERY</h2>
<p>The IETF has published a document detailing the QUERY verb. The Query verb neatly solves the problem of asking APIs for LOTS of data or <em>conditional</em> data.
In other words, QUERY enables API providers to provide a means for clients to ask the server about data, where the client isn&rsquo;t sure what data is available.</p>
<h2 id="a-worked-example">A worked example</h2>
<p>Working in Agri-tech, I often want to find out information about animals.
Unfortunately, that information can live across many systems, with many identifiers (primary keys or other).
In one context I&rsquo;ve experienced, Dairy Animals can be identified and referenced by five or more different identifiers - so what do you do if you don&rsquo;t know all of them?</p>
<p>In my case, I want to provide an API with the identifiers I DO know and ask for the API to return any identifiers it
has that are linked to the ones I submitted. There are a few ways the API could implement such functionality.</p>
<p>For a complete animal:</p>
<blockquote>
<p><code>HTTP GET /animals/${the_identifier_i_know}?identifierType=${the_identifier_type}</code></p>
</blockquote>
<p>Based on this request URL, I would expect to provide an identifier and receive back the entire animal entity.
Animal entities can be of substantial size! Serialising all this data can be time-consuming and wasteful for a busy API,
particularly when we know we&rsquo;re just exploring and want only a subset of that data. We&rsquo;d be discarding 90% of the data returned.</p>
<p>For an animal&rsquo;s identifiers only:</p>
<blockquote>
<p><code>HTTP GET /animal/identifier?identifierType=${the_identifier_i_know}</code></p>
</blockquote>
<p>I interpret this URL as only providing me with identifying details about the animal. Optionally, I can request a certain type of identifier.
If I only care about a singular animal, this is probably all you need. No <code>QUERY</code> verb is necessary.
What If I want to query a collection of animals? There are potentially many thousands of animals in a group for which I require data.
I&rsquo;d encounter a significant HTTP overhead, making that many requests to this endpoint. What we need is an API for a collection of animals!</p>
<p>A collection of dairy cows is a herd. I don&rsquo;t care about herds; I want identifiers for an arbitrary group of animals for my specific use case.
<img alt="Image of man demanding pictures of Spiderman" loading="lazy" src="/img/in-post/get-me-pictures.jpeg"></p>
<p>REST guidelines ask that you model your APIs as</p>
<blockquote>
<p><code>/{collection_one}/{collection_one_identifier}/{collection_two}/{collection_two_identifier}</code> and so on.</p>
</blockquote>
<p>Under this guidance, we cannot submit multiple identifiers for a single collection. So if we had 3000 animals, for example, the request might be:</p>
<p><code>/animals?identifier=id1,id2,id3 ... etc</code> - it doesn&rsquo;t make sense! Putting many identifiers in a URI comes with its work,
but crucially, if you&rsquo;re asking for lots of data and are forced to encode that in the URI,
you could run into length limits preventing you from encoding all of your request. Most servers will typically limit
the length of your encoded URI to 2048 characters or less. Those servers will decline your request if the URI is too long.</p>
<p>Most folks get around this by defining a <code>POST</code> endpoint that breaks RESTful principles. Here&rsquo;s an example of a <code>POST</code> request:</p>
<p><code>HTTP POST /animals/search</code> with a body:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;knownAnimalIdentifier&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;id1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;id2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;id3&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;id4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;id5&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>You could put all sorts of freaky logic or conditionals in this body; there are far fewer limits and, therefore,
more risk of pushing the concept outside sensible boundaries. For example, this might also be valid:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;knownAnimalIdentifier&#34;</span><span class="p">:[</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;id1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;id3&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;id3&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;where&#34;</span><span class="p">:{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;healthTreatment&#34;</span><span class="p">:{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;condition&#34;</span><span class="p">:</span><span class="s2">&#34;mastitis&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;product&#34;</span><span class="p">:</span><span class="s2">&#34;amyzin&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>You could pull all sorts of stunts here. The above looks suspiciously like GraphQL to me - the key message here is that
<code>POST</code>-ing arbitrary search data to an API imposes very few limits on otherwise conscientious and responsible developers.</p>
<p>Further, using a <code>POST</code> doesn&rsquo;t match the intent of the request. REST guideline defines a <code>POST</code> as NOT idempotent - <code>POST</code>
means you&rsquo;re creating a new resource every time you make the request. With our above requests, the intent is to search for data,
not create resources, but we don&rsquo;t have a good way of modelling that! You could argue that when you submit a search via <code>POST</code>
you are asking the server to create a new resource containing your results - the issue with that, is that you&rsquo;re not creating a new resource.
At best you are creating a new projection of resources that already exist. You&rsquo;re not <code>POST</code>-ing a new letter into the letterbox,
you&rsquo;re asking the mailman to pass you some letters that may or may not exist.</p>
<p>To do a search we need to do the <code>HTTP</code> <code>GET</code> <code>POST</code> <code>GET</code> dance. First <code>GET</code> enough data to formulate your search query, then <code>POST</code> to
create a query resource, then optionally <code>GET</code> the resource you just created.</p>
<p>Some folks will implement an API that directly returns the results to you. You&rsquo;d need to code
for latency, dropped connections and retries. For long-lived queries, you&rsquo;d need a solution for the underlying data
changing as the query progresses (similar to pagination where page 3 changes after you retrieve it but before you complete paging).
If the API indirectly returns your data to you (i.e. immediately returns a link to the newly created search result resource), you have to go and <code>GET</code> that resource.</p>
<p>Using <code>POST</code> requests for search functionality solves one problem - lack of flexibility in the <code>GET</code> verb - but introduces another.
Using <code>POST</code> server cannot cache or re-use results, nor can it deal gracefully with common failure modes (retrying ten times
creates ten resources! All those resources might be different!)</p>
<h2 id="the-query-verb">The Query Verb</h2>
<p>Julian Reschke, Ashok Malhotra and James M Snell has authored a draft RFC that describes the <a href="https://www.ietf.org/archive/id/draft-ietf-httpbis-safe-method-w-body-02.html" target="_blank" rel="noopener noreferrer">QUERY verb</a>

.</p>
<p><code>QUERY</code> solves the problems of your typical search workflow.</p>
<p>The draft spec defines a <code>QUERY</code> as a means of making a safe, idempotent request that contains content.
It&rsquo;s worth your time to read the entire article (it&rsquo;s about a five-minute read).</p>
<p>The important parts for me (i.e. order and emphasis mine) as a system designer are:</p>
<blockquote>
<p><code>QUERY</code> requests are both safe and idempotent with regards to the resource identified by the request&rsquo;s URI</p>
</blockquote>
<blockquote>
<p>The response to a <code>QUERY</code> method is cacheable</p>
</blockquote>
<blockquote>
<p>The body payload of the request defines the query. Implementations MAY use a request body of any content type with the <code>QUERY</code> method, provided that it has appropriate query semantics.</p>
</blockquote>
<blockquote>
<p>The payload returned in response to a <code>QUERY</code> cannot be assumed to be a representation of the resource identified by the effective request URI.</p>
</blockquote>
<p>Before, I would have to make a <code>GET</code> to some URL with an enormous query string OR create arbitrary numbers of un-cacheable
new resources; now, I can use the <code>QUERY</code> request. Furthermore, I can put whatever data I need in the body of my request,
safe in the knowledge that implemented to spec; I&rsquo;m going to get a quick response that I can cache in line with the server&rsquo;s guidance.</p>
<p>It&rsquo;s a small and subtle change. Implementation-wise, you could change your existing <code>POST</code> search endpoint to a <code>QUERY</code> and
leave the same body. Migrate current <code>GET</code> endpoints by shifting query/path parameter content into the body and change the verb.</p>
<p>The most significant improvement relates to intent and clarity. Using a <code>QUERY</code> verb signals my intention to request data
based on conditions that I supply. <code>QUERY</code> marks that request as idempotent and indicates the intent of the caller.</p>
<p><code>POST</code> shows intent to create a new resource. <code>GET</code> indicates intent to retrieve a resource for whom the caller already has
an identifier. Neither <code>POST</code> nor <code>GET</code> indicate an intention to request data about which the caller is uncertain.
The semantics of the verb and the request matter because they clarify intent. Computers will do what we tell them to do. Not what we intend for them to do.</p>
<p>Clarity of intent in an API is crucial to the success of that API. When designing that API, we want to make it as easy
as possible to map the intent of the caller to appropriate functionality in the API. Introducing new verbs to clarify
the caller&rsquo;s desires and better match those needs (i.e. &ldquo;What endpoint should I call to do X?&rdquo;) to the correct part of an API brings developers joy.</p>
<h2 id="conclusion">Conclusion</h2>
<p>The <code>QUERY</code> verb is brand new and in draft with the IETF - you can check it out <a href="https://www.ietf.org/archive/id/draft-ietf-httpbis-safe-method-w-body-02.html" target="_blank" rel="noopener noreferrer">here</a>

.
It clarifies the calling system&rsquo;s intent and enables behaviour more suited to interactions where you don&rsquo;t know what specific resources are available,
but you do know something about those resources (e.g. the first and last names of a person, or their phone number,
email address or some combination thereof). QUERY allows for idempotency, caching and is easy to adopt incrementally.</p>
<h2 id="citations-and-further-reading">Citations and Further Reading</h2>
<ul>
<li><a href="https://www.ietf.org/archive/id/draft-ietf-httpbis-safe-method-w-body-02.html" target="_blank" rel="noopener noreferrer">Julian Reschke, Ashok Malhotra, James M Snell: The HTTP QUERY Method</a>

</li>
</ul>
]]></content:encoded></item><item><title>Trialling One Day Sprints</title><link>https://4lex.nz/posts/1-day-sprints/</link><pubDate>Sun, 17 Oct 2021 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/1-day-sprints/</guid><description>&lt;p&gt;1-day sprints are a fantastic way of turning the traditional time pressures a team might face from negative to positive.
They act as a time constraint on the team and force clarity, agility and feedback into your work system in far more
significant amounts than you might already have. They can act as circuit breakers for teams struggling to meet sprint
goals. They can create momentum in and outside of the team and encourage teams to focus on producing the smallest impact
possible for your users. They carry risk, but some thoughtful conversations upfront can minimise any negative
impact later on - and the rewards are well worth it.&lt;/p&gt;</description><content:encoded><![CDATA[<p>1-day sprints are a fantastic way of turning the traditional time pressures a team might face from negative to positive.
They act as a time constraint on the team and force clarity, agility and feedback into your work system in far more
significant amounts than you might already have. They can act as circuit breakers for teams struggling to meet sprint
goals. They can create momentum in and outside of the team and encourage teams to focus on producing the smallest impact
possible for your users. They carry risk, but some thoughtful conversations upfront can minimise any negative
impact later on - and the rewards are well worth it.</p>
<h2 id="what-is-a-1-day-sprint">What is a 1-day sprint?</h2>
<p>There&rsquo;s nothing fancy about a one day sprint. All it is, is your regular sprint cadence scaled down to eight hours. Of
course, that means your rituals scale down as well. In my case, a one day sprint meant a half an hour planning session
in the morning before we kicked off into work. If your regular standup is meandering, not resulting in a plan for the
day or is otherwise less effective, then you might hope this is an excellent way to fix it. The broad idea here is, as
John Cutler says, to have the conversation and then do the thing. See John&rsquo;s article
<a href="https://medium.com/hackernoon/one-day-sprints-1ce1f53a08b2" target="_blank" rel="noopener noreferrer">here</a>

.</p>
<p>Timeboxes are a forcing function. Forcing functions help you control things you want to control. Setting a timebox of
one day for the sprint acts as a forcing function for the size of the work you bring into the sprint. Setting a timebox
is also a forcing function for the amount of work you pull into a sprint. If you want to deliver a successful sprint at
day&rsquo;s end, you need to be careful about the shape of the work you commit to, as well as the amount of work you pull in.</p>
<p>Small timeboxes lead to chunking down work smaller. Your larger work items won&rsquo;t fit, so they need to get smaller. The
process of breaking down those larger items forces clarity and understanding. Where before you might have a big fuzzy
thing you want to deliver in two or four weeks or longer, you now have to split it up and to split it, you need to
understand it. So, the small timebox also forces a better understanding of the work you are doing while removing all of
the places uncertainty usually likes to hide.</p>
<p>In other words, you never have to worry about whether or not you can get something done - if you don&rsquo;t think you can
achieve the work in a day, keep talking and cut it down smaller. Worst case, you might spend most of the day talking,
that&rsquo;s okay. Planning and breaking down work is a universally valuable thing. Your company pays you to produce solutions
to business problems, right? I&rsquo;m assuming you&rsquo;re not paid per line of code written per hour.</p>
<h2 id="what-made-me-think-1-day-sprints-would-be-useful">What made me think 1-day sprints would be useful?</h2>
<p><strong>(a.k.a. how can you identify when they might be helpful to you?)</strong></p>
<p>I had an excellent experience with 1-day sprints on one team and projects that might not translate to another group with
different constraints.</p>
<p>For context, I had a project that needed to be done by a specific date. We got approval to start the work, but by the
time we could pull a team together, we would only have eight weeks, give or take, to build and deliver the tech (We
delivered in six weeks).</p>
<p>The project was a challenging task mainly because of the available time rather than because the solution was novel or
the technology complex. We had to deliver our solution and enable our partners to integrate with our solution
simultaneously.</p>
<p>I identified early on that I had two important jobs that needed to be done.</p>
<p>I needed to:</p>
<ul>
<li>Hire a process that enabled the team to be highly responsive to the progress we made, and</li>
<li>Do that in a way that provides clarity and transparency for our external stakeholders such as the CEO, CIO,
and sales folks working with partners who want to use the solution.</li>
</ul>
<p>The default process we use is a 2-week sprint. We had eight weeks to deliver the solution and clear instructions that if
at any time we felt we wouldn&rsquo;t hit the timeline, we should stop. Sharing progress with stakeholders four times before
the deadline would not be good enough. Bugging teams about progress every day would negatively affect the team.</p>
<p>Running a 1-day sprint would yield 30 reporting opportunities rather than four. 1-day sprints
constrain us to work that we could reliably achieve before the next reporting boundary, ensuring we would always be able
to report progress. Making that reporting interaction a system feature rather than having an individual do it would also protect
the team from undue interruption and help them maintain flow - increasing our likelihood of success.</p>
<p>I hired 1-day sprints to enable decision-making and reporting agility, both in and outside the team. We saw some other
benefits. In the first week, we created significant momentum. We were better able to onboard new team members since the
work led to making the smallest change possible. We quickly encountered, identified, and resolved bottlenecks because
the moment a 1-day ticket didn&rsquo;t take a day, we knew to stop and inspect.</p>
<p>Using a 1-day sprint created momentum and flow.</p>
<p>Using a 1-day-sprint also created clear intervention points where momentum and flow broke down due to missing tools or processes.</p>
<p>Because we could so rapidly diagnose and resolve issues affecting productivity, we were productive. Further, because we
could respond quickly, a little bit of pre-planning went much further than it might usually have gone. For example, we
identified some third-party dependencies early on and encountered delays with some of them. Our ability to respond and
work around those delays meant that those delays did not become blocking issues where a team running a 2-week sprint may
have become blocked by them.</p>
<h2 id="some-other-unscientific-observations">Some other (unscientific) observations:</h2>
<ul>
<li>The team that ran 1-day sprints had an average cycle time of about three hours.</li>
<li>Our average time to resolve a blocking issue for our integration partners was about 24 hours -
addressing these issues rapidly in a 2-week sprint would be disruptive and affect sprint scope.
We didn&rsquo;t have that issue with 1-day sprints.</li>
<li>The team enjoyed working with 1-day sprints. They rated the experience at 4/5.</li>
</ul>
<h2 id="risks">Risks</h2>
<p>We did highlight risks through our regular retrospective process.
There are things to think about and tradeoffs to make.</p>
<p>Make them deliberately and with forethought:</p>
<ul>
<li>Managing wellbeing can be tricky. Running sprints for a long time can lead to burnout. Folks need mental breaks. We
usually build in time to recuperate (in the form of quarterly plannings, offsites or other regular &ldquo;off-sprint&rdquo; time) -
that time can be harder to lock in with a 1-day sprint.</li>
<li>Managing dependencies, blockers, or long-term plans requires
thought. It can be tricky to map tactical activities up to operational or strategic activities. To address this
tradeoff, you&rsquo;ll need to draw lines between your day-to-day and your strategic efforts more often.</li>
<li>It&rsquo;s weird - 1-day sprints can be novel for many organisations. If you want to run a 1-day sprint,
how does that process interact with other parts of the business? What about the governance and finance folks?
Customer support? Does pushing out small batches of work every day put a strain on other parts of the organisation?
Does it expose and break manual processes we might have in place? Dunno. The impacts are worth thinking through.</li>
</ul>
<h2 id="conclusions">Conclusions</h2>
<p>1-day sprints are a potent tool - they can help you turn around a team struggling to complete sprints and circuit-break
uncertainty in the development process. In addition, 1-day sprints can help you restore confidence with stakeholders and
rapidly build confidence where you don&rsquo;t have a lengthy pedigree to stand on. As with most tools, this carries risk,
and there are questions you&rsquo;ll need to answer thoughtfully to make the most of 1-day sprints. What are your
dependencies, and what impact will such rapid feedback have on other parts of your system? While 1-day sprints may work
well for the team conducting them you should also be mindful of their impact on other teams. Will working in 1-day sprints place extra pressure on your colleagues?
Is that pressure a feature or a bug? It&rsquo;ll be up to you to ensure that pressure is positive!</p>
<h2 id="sources-and-further-reading">Sources and Further Reading</h2>
<a href="https://medium.com/hackernoon/one-day-sprints-1ce1f53a08b2" target="_blank" rel="noopener noreferrer">Have the conversation, do the thing</a>


]]></content:encoded></item><item><title>Implementing a team topology</title><link>https://4lex.nz/posts/implementing-a-team-topology/</link><pubDate>Wed, 24 Feb 2021 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/implementing-a-team-topology/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Any organisation that designs a system (defined broadly) will produce a design whose structure is a copy of the organisation&amp;rsquo;s communication structure.&lt;/p&gt;
&lt;p&gt;— Melvin E. Conway&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Team topologies are a concept created and popularised by Manuel Pais and Matthew Skelton. They describe a way of reforming
your organisation shape in such a way that improves the architecture of your software while reducing cognitive load and directing
more energy and focus towards the streams of value that flow through your organisation.&lt;/p&gt;</description><content:encoded><![CDATA[<blockquote>
<p>Any organisation that designs a system (defined broadly) will produce a design whose structure is a copy of the organisation&rsquo;s communication structure.</p>
<p>— Melvin E. Conway</p>
</blockquote>
<h2 id="introduction">Introduction</h2>
<p>Team topologies are a concept created and popularised by Manuel Pais and Matthew Skelton. They describe a way of reforming
your organisation shape in such a way that improves the architecture of your software while reducing cognitive load and directing
more energy and focus towards the streams of value that flow through your organisation.</p>
<p>Performing an inverse-Conway manoeuvre is a big deal. We&rsquo;re talking about people&rsquo;s livelihoods here. To do it, you need to
collect skills in change management, communication, design, and potentially many more things. Critically you&rsquo;ll need
to figure out the communications pathways and relationships in your business that will get your plan actioned. You&rsquo;ll need
to figure out the logistics behind the action as well. If you can do that, you&rsquo;re golden.</p>
<p>Those top-level streams of value are paramount. Getting them right helps your design resonate. It helps you build shared understanding.
It helps you account for change over time and growth. Streams are flexible by nature, but it&rsquo;ll help if your streams make sense from the start.</p>
<p>To do this, you&rsquo;ll need a team behind you. More than that you&rsquo;ll need friends. Folks who come together to work towards an answer to the questions:</p>
<blockquote>
<p>Why am I spending so much time fighting my architecture? How do we build a sense of ownership? How do we balance short term priorities with long term strategy?</p>
</blockquote>
<p>I&rsquo;m going to talk about how my org did it — an org that is large by New Zealand standards but small globally.</p>
<p>I&rsquo;ll walk you through the symptoms we had and how my org struck at the root cause, how I developed an idea and worked to get it signed off.
I&rsquo;ll finish with how we&rsquo;re rolling out that change and offer some advice for you to takeaway.</p>
<p>One time I cooked two-minute noodles in 112 seconds, and that was pretty cool. This is much cooler.</p>
<h2 id="symptoms">Symptoms</h2>
<p>In our technology department, we used to be extremely busy. We still are, but we used to be, too. We had various streams
of work in progress such that every ten weeks, all of our teams would end up working on something different to what they
worked on in the previous ten weeks. High WIP made things difficult to manage, and it caused us pain.</p>
<p>Teams could never really &ldquo;own&rdquo; a bit of software for longer than ten weeks. We couldn&rsquo;t build tribal knowledge because
we chopped and changed regularly enough that by the time we returned to something (if we returned it at all), the
expertise we gained prior we lost to skill fade.</p>
<p>Mostly we took one pass at solving a problem for someone and never got to iterate on an issue. Our teams were (and still
are but to a far lesser degree) funded on a project basis, and the project already had a tick in the box.</p>
<p>Because we never iterated on our work, we never returned to address technical debt. We never got to enhance successful
features nor swing around to flourish the scythe at poorly performing features.</p>
<p>Knowing we would never return, we would oscillate between gold plating our work (and delivering late) and piling on
technical debt (which would cause late delivery and more down the track instead).</p>
<p>Never returning leads to &rsquo;ninja teams&rsquo; that are typical firefighters who do an excellent job of tackling small urgent work but struggle
to create features of substance. This type of work focuses on short term aims and optics at the cost of long term progress.</p>
<p>Knowing we would never return, our stakeholders would ask us to reduce our estimates and do more work in less time. Or
worse, ask us to cut non-functional work or work that other projects relied on (also, we had dependencies between work
that ought to have been independent).</p>
<p>None of our teams had distinct specialities, focus areas or parts of the architecture they resonated with or cared for.</p>
<p>It was expected that to deliver a unit of valuable work to a customer; an engineer would need to work across 5+
codebases, 3+ languages, queues, streams, containers, lambda functions or services deployed on bare metal.</p>
<p>The cognitive load across these tasks precluded us from developing expertise. The footprint of our architecture relative
to our level of staffing trapped us in a place where we could never gain or retain enough proficiency to deliver to our
partners and stakeholders&rsquo; expectations.</p>
<p>Our engineers did a great job of treading water — but they should never have had to do that! We as leaders put them into
a place where systemically, it was an effort to remain in place.</p>
<p>With continually increasing demand from our partners across the organisation, we couldn&rsquo;t keep doing this. Not only did
we need to staff up. We needed to organise ourselves better. We still do, but we used to, too.</p>
<h2 id="what-is-a-team-topology">What is a Team Topology?</h2>
<p>Briefly, team topologies are the answer to the question, &ldquo;How can I reshape my organisation to be more
focused on the streams of value that flow through my business?&rdquo;. Developed by Matthew Skelton and Manuel Pais, team
topologies gave me the tools to describe what I felt needed to change in my org, be more focused on value, focus on
products, and focus more on teams and streams of business value.</p>
<p>Team topologies are about optimising for flow and reducing cognitive load of all kinds. It provides a ubiquitous
language to talk to anyone in our business about value, interactions, and what it might look like to evolve our
organisation humanly and humanely.</p>
<p>If you&rsquo;re not familiar with the concepts and have some time, I highly recommend you have a quick read of <a href="https://teamtopologies.com/" target="_blank" rel="noopener noreferrer">the website</a>

</p>
<p>Team topologies contain four basic topologies — stream aligned teams, enabling teams, complicated subsystem teams, and
platform teams.</p>
<table>
  <thead>
      <tr>
          <th>Topology</th>
          <th>Description</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Stream Aligned Team</td>
          <td>stream aligned teams cover the streams of value that flow through your business. These streams could be internal processes that differentiate your product or service; we could align them to the product or service itself.</td>
      </tr>
      <tr>
          <td>Enabling Team</td>
          <td>Enabling teams are experienced facilitators. We intend for them to unblock other teams around them.</td>
      </tr>
      <tr>
          <td>Complicated Subsystem Team</td>
          <td>Complicated subsystem teams have the experience, expertise, or business and domain context needed to maintain and improve complex or critical software safely and effectively.</td>
      </tr>
      <tr>
          <td>Platform Team</td>
          <td>Platform teams provide high-quality internal products to other groups in the organisation. Platform teams can cover cross-cutting concerns like authorisation and authentication, billing or internal developer tooling.</td>
      </tr>
  </tbody>
</table>
<p>The fractal nature of these streams allows for growth. They also enable incremental adoption — this is a critically
important factor for my enterprise, and I imagine others will find the value there as well.</p>
<p>As a concrete example, right now, we have one platform team, my team. Our platform team needs to support cross-cutting
concerns over a business of many hundreds of staff and many development teams. We have a backlog that&rsquo;s about four years
long, and we&rsquo;ve existed for four months.</p>
<p>That&rsquo;s okay!</p>
<p>I&rsquo;ll expand on this more later, but as buy-in grows, we can hire more teams to work in the platform space, and we can
start splitting our platform space into streams of the value of their own. I.e. right now, we have one platform team; in
the future, I expect we&rsquo;ll have an animal platform team, a customer platform team, et cetera. They will one day cover
all of the valuable parts of our domain where cross-cutting concerns exist.</p>
<p>Teams interact with other groups; often, it&rsquo;s not clear how or that how is not a considered (even conscious) decision.
Out of the box with team topologies, you get three modes for how your team should interact.</p>
<table>
  <thead>
      <tr>
          <th>Mode</th>
          <th>Description</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Collaboration</td>
          <td>In collaboration mode, teams pair up for a defined period to learn together things they share a mutual interest in.</td>
      </tr>
      <tr>
          <td>X-as-a-Service</td>
          <td>In as-a-service mode, one team provides something as a service. The consuming team treats the thing as a product; the consuming team can choose to buy it off the shelf or choose to do something else.</td>
      </tr>
      <tr>
          <td>Facilitating</td>
          <td>In facilitation mode, one team focuses on helping another team go faster or be better for some value of faster and better. I expect that the vast majority of facilitating teams use this interaction mode.</td>
      </tr>
  </tbody>
</table>
<h2 id="why-team-topologies">Why Team Topologies?</h2>
<p>Team topologies, an extension by the same authors on DevOps topologies (links below), is well-researched, well-cited,
and the adoption is snowballing. It&rsquo;s cutting edge, and it directly answers the pain my organisation felt.</p>
<p>It&rsquo;s not the case with all frameworks or concepts someone will try to sell you, but team topologies directly address the
root issues of that pain as well!</p>
<p>Some of the frameworks or concepts that I&rsquo;ve read about seemed fluffy, difficult to apply to the real world or hard to
implement. This is not the case with team topologies. I found it easy to re-model my organisation on a whiteboard. I
found it easy to grab the nearest available executive (who happened to be the CIO) and talk through what I was thinking.
We both found it easy to talk about what it might mean for us, how we might start it, and how we might measure how we
are doing.</p>
<p>I rolled my whiteboard around the office and shopped it to different parts of my organisation, and each time I was
heartened to see folks intuitively grasp the ideas. Because the model was simple, the ability to give feedback on the
whiteboard was very accessible.</p>
<p>In this way, it was easy to get buy-in on the strength of the idea.</p>
<p>That ease put folks at peace while also enabling them to research for themselves; it cemented the ideas as credible and
actionable. Even better, we had a clear place to start with rolling out a platform team focusing on our API platform
that interacted in a collaborative and sometimes as-a-service manner. The API platform team was a great first team, and
we could use it to build comfort and familiarity with the system. We could use that platform team for prototyping the
wider rollout. We could roll out incrementally, starting with a top-level team for our API platform, and then convert
existing units into top-level teams for their streams. We could later split those streams and add additional humans who
would be more tightly focused on their fractal streams.</p>
<p>The ability to roll out incrementally is a crucial enabler. It means we don&rsquo;t have to invest millions of dollars — we
can change the org like we change software, quickly, adaptively and responsively. We don&rsquo;t need to ask for an infusion
of trust and an investment of money — we only need to ask for the first team and then show what we&rsquo;ve measured and
learned.</p>
<p>Incremental rollout is easier on the rest of our business function as well. It&rsquo;s easier to hire, onboard and retain
small numbers of staff. It&rsquo;s easier to help those staff build relationships across the business, and it&rsquo;s easier to
track how we&rsquo;re going — making it easier to ask for the next team and do it all over again. I can&rsquo;t stress enough how
critical this aspect of team topologies is for our business.</p>
<p>This low-restructure (frankly, you can never guarantee it) approach stands apart from more traditional ways of
defining and creating a system of humans. I love it because of the degree of humanism I see in it. Nowhere in team
topologies or any of the conversations around it do I hear humans referred to as resources.</p>
<h2 id="the-process">The Process</h2>
<p>Most organisation change is long-lived. It can be messy. It can be difficult. Humans are prone to making mistakes. We&rsquo;re
inclined to miss things. Before I asked my colleagues and friends to embark on any change,
I needed a clear, shared understanding of where the  organisation is at.</p>
<p>For my organisation, this meant defining what teams we have, what they are working on, how they act and how they feel.
Do we have the coverage we need to support all of the software running in production? Are teams happy and healthy? Is
there anything that&rsquo;s causing them pain right now?</p>
<p>Defining these things on paper, visually rather than with paragraphs, is a forcing function for clarity and
understanding (if folks can see it on a whiteboard, they&rsquo;ll tell you when you have it wrong).</p>
<p>In my case, I saw teams that wanted to gold-plate solutions; we (systemically) had piled them high with code they would
never have time to maintain. We asked them to context switch on an almost daily basis. Making a single change in a
product could require changes in many supporting systems. This did not spark joy.</p>
<p>We knew the power of teams that cover vertical slices and focus on delivering incremental improvements to our
customers, but our domain is complicated, and our architecture is large. A number of us across the technology department
sensed (and still feel today) that only having vertical teams in a domain as complicated as ours, holds us back.
They needed support.</p>
<p>We burdened teams with too much cognitive load. We asked them to take on so much intrinsic and extraneous cognitive load
that they had very little thinking space left for the germane cognitive load!</p>
<table>
  <thead>
      <tr>
          <th></th>
          <th></th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Intrinsic</td>
          <td>Intrinsic cognitive load is the inherent level of difficulty associated with a certain system or domain.</td>
      </tr>
      <tr>
          <td>Extraneous</td>
          <td>Extraneous cognitive load is generated as a result of poor design by the designers of the system, and negatively affects those who interact with the system.</td>
      </tr>
      <tr>
          <td>Germane</td>
          <td>Germane cognitive load is the processing, construction and automation of knowledge — in other words, it&rsquo;s the stuff we want to spend as much time on as possible.</td>
      </tr>
  </tbody>
</table>
<p>We struggled through our symptoms, and credit to the teams — given what we asked of them, I think they did a great job.
I think we treated them unfairly.</p>
<blockquote>
<p>The Enemy of Art is the Absence of Limitations</p>
<p>Orson Welles</p>
</blockquote>
<p>Our situation felt dire when catalogued this way — but constraints breed creativity. It took some research, but I
eventually discovered the ideas Matthew Skelton and Manuel Pais had packaged up as &ldquo;team topologies&rdquo;.</p>
<p>I spent about a week on my initial drawings. I spent another three refining my ideas with the feedback of anyone who
chanced a walk by my desk and everyone who had five minutes for a chat that inevitably ended an hour and a half later. I
filled up 3.6m x 5.4m of whiteboard area — 20 square metres if you round up. The digital versions are far tidier (full
credit to an excellent UI designer named Luiza) and more compact. I&rsquo;ve inserted them below.</p>
<p><img alt="The front page of the team topologies I designed in April 2020" loading="lazy" src="/img/in-post/tt_whiteboard_front_page.png">
<img alt="How I thought we could roll this out" loading="lazy" src="/img/in-post/tt_rollout_detail.png">
<img alt="Some business rules I thought we needed at the time" loading="lazy" src="/img/in-post/tt_metadata_business_rules.png"></p>
<p>There are a few caveats here — this isn&rsquo;t the final concept we went with. It took further refinement at the exec level,
we had to make it fit into the financial model of my org, and we had to think about the types of skills we needed in each team.</p>
<p>Additionally, while the work the images captured above were all mine, we had an existing strategic plan for technology,
and had spent a lot of time fulfilling that strategy — the work that I had done fed into these plans and offered
language, context and shape to what we already felt we had to do.</p>
<p>At a very high level, I produced an organisational design with both vertical and horizontal teams, where all teams align to a stream of
value. My organisation already has business units aligned around the value that we bring, as you&rsquo;d expect. So each
business unit/stream gets a top-level team initially. I designed these teams to be overwhelmed, and I expect them to
have a long backlog initially. Once they&rsquo;re up and running, the most valuable work or critical systems should naturally
bubble up to the top of the backlog. The key for these teams is to remember they don&rsquo;t need to boil the ocean,
only demonstrate how we all work together.</p>
<p>I thought that once top-level teams are up and running, and work is being done, the next large job is to begin planning for additional
teams. This means doing the work and getting value out to users. It also means using the demand in the business and
bubbling up of work in the backlog as signals to identify fracture planes in each stream of value.</p>
<p>You can align streams of value in several ways. I don&rsquo;t think there is any single way to do it. My org has streams of
value that map to workflows in our products, to user journeys and platforms like mobile and web. I&rsquo;m currently aligned
with the top-level team of the API platform. One day, I&rsquo;ll start cutting that up into streams about animal data,
scientific processes, customer data, authentication and authorisation, or more. Nothing is true; everything is
permitted.</p>
<p>This structure addresses most of the pain I had identified and validated. This structure is by no means perfect or
final. I don&rsquo;t believe there is such a structure. It seems clear that as our organisation grows and our needs change,
we&rsquo;ll need to revisit our design. The good news is that aligning to streams of value means that we can modify the way we
are shaped by applying or removing teams from value streams.</p>
<blockquote>
<p>One key issue that this structure does not address — and it&rsquo;s worth stating explicitly — is product or value stream
funding vs project funding. Arguably, many of the challenges we have had are directly attributable to our organisation
being funded for discrete projects and not long-lived products of value streams. This discussion isn&rsquo;t one I want to
spend much time on because team topologies are distinct and separate concerns. To a degree, we can map incoming
projects to streams of value, so it&rsquo;s not a huge deal now — though it certainly was before we were able to map
stop/start work items onto long-lived bits of software.</p>
</blockquote>
<blockquote>
<p>A critical pivot point for value stream vs project funding is addressed by my model in that I&rsquo;m advocating for a
far greater influence in our system for product people. Each team in my design contains either a product owner, or a
product manager, or both. Product folks should proactively engage with the wider business and collect all of those
would-be projects and map them to products. With time I would expect to see the number of projects coming in from the
wider business reduce as we scoop up increasing amounts of work and drive from within the technology department.</p>
</blockquote>
<p>With some whiteboards to focus conversation onto ideas, the part that I struggle with most is next — communication. I&rsquo;m
not great at packaging what I&rsquo;m saying for different audiences; I&rsquo;m not proud to say that I&rsquo;m responsible for more than
a few phone calls between executives!</p>
<p>Communicating well what your model looks like, what it&rsquo;s optimising for and importantly, what it&rsquo;s not optimising for
is the bread and butter of getting this change across the line. Your organisation will be unique in this area — it
comes down to people. Who are the people you need to show this to? Whom do you need to convince? Who can you recruit as
a signal booster to get your message into parts of the organisation you don&rsquo;t have access to?</p>
<p>I probably spent the better part of 100 hours talking about this to people, taking feedback and refining the message,
format and substance. Once I started hearing people talk about this in reference to other issues they&rsquo;ve encountered in
their work, I knew that they got it.</p>
<p>The next step was to do it. Both intimidating and exciting, it was time to turn the model into something tangible. Again
every organisation will be different and have different levers. For my org, it was a mixture of strategy and finance. I
had to convert this arbitrary concept I found on the internet into something that our leadership team could act on -
that meant turning whiteboards into the same shapes that go into our budgets, with the same language we use in our strategy
documents.</p>
<p>Something that helped a lot was to frame this idea in the same language we use today. Department leaders and I
spent time collaborating on the next version of our plan. This plan was an extension on what was already occurring, and
would help us scale effectively from five teams to more than fifteen! My input in this process was language and shape.
The streams, the teams and the meaning behind them became a part of our strategy and a part of our plan. That input morphed
from being a single data point to being &ldquo;just the way we do things&rdquo;. Importantly, this collaborative process made it
easy for all of us to tie the benefits to current opportunities we had to improve on.</p>
<p>At that point, we had a plan that looked budget-able if you squint at it. We had a plan that we felt would give us room
to grow while addressing pain and history. We published the idea in the relevant documents, and the machinery of the
organisation swung into action to analyse, approve and implement.</p>
<blockquote>
<p>The critical take away here is this: Don&rsquo;t fight the plumbing.
Co-opt it.
Redirect the ship from the bridge, not the bow.</p>
</blockquote>
<h2 id="the-rollout">The Rollout</h2>
<p>My organisation started with just one team, responsible for the shared core. The idea being we start slow and
take the time to get it right. Team interaction modes, the way we embody the agile manifesto, the way we apply software
product management principles, and how we collaborate to create technology all need conscious thought and planning.
Importantly, all of the ways the team come together to deliver value also need road testing. We planned to work in very
different ways compared to what some parts of the business have seen before, and we must speak clearly about why we&rsquo;re
doing that and why we think it&rsquo;s essential.</p>
<p>Consequently, we planned to do the road test, collect some wins, and earn some scars, informing us for when it comes time to scale up.
Starting small is the best way to make these changes stick. The alternative is asking for all the teams upfront, having
to hire 10+ teams while you&rsquo;re trying to build a brand new way of working together. Organised Chaos would be a gentle
description, and in doing this, we would be failing the business in what we promised. We&rsquo;d be acting big and
thinking small when really we want to think big and act small. We want iterative, not big bang.</p>
<p>Boyd Multerer, Xbox&rsquo;s father of innovation, once told me this: Think very carefully about who the first member of the
team is. Who are the founding members? You only get to make your culture once. It&rsquo;s those people who will set it, and
after those founders set it, it&rsquo;ll be tough to change that culture.</p>
<p>I&rsquo;m incredibly proud of the first team. The team is a mixture of internal and external hires, with a broad technical
base and deep knowledge of the technology stack and our domain. We have a culture of writing, of collaboration first,
and of always delivering incremental value. Boyd&rsquo;s words ring true.</p>
<p>With the team in place, the time comes to loudly and explicitly make space for experimentation. There is so much
discovery to do for a new team to move from forming to performing while figuring out the domain, the architecture, the
strategy and everything else they&rsquo;ll need to think big and act small. You might consider that first team to be the
groundbreakers, but every new team you hire that looks and works that same way will need to make discoveries of their
own.</p>
<p>All of the value streams that run through your business will be different, and those teams will need to engage and model
themselves differently. What are the engagement modes, and what team topology will each stream require? How will we hire
and onboard supporting teams? How will we draw the wider org into the conversation?</p>
<p>Most challenging of all, how will we build community? How will we move from being a bunch of people who don&rsquo;t know each
other well to be many people who have strong community links across the organisation? Those relationships will see those
teams through hard times; building community is a top priority.</p>
<p>Every team will be different, that&rsquo;s okay.</p>
<p>We&rsquo;re rolling out right now so a lot of this is planned-but-not-actual, or actual-but-only-since-Tuesday so forgive me that.
These lessons are hot off the press and our journey is incomplete, I hope to perpetually remain with that feeling of
incompleteness at the same time as I watch us grow, deliver, reshape and grow again.</p>
<h2 id="conclusions">Conclusions</h2>
<p>Wrapping up, I have some closing thoughts:</p>
<ul>
<li>Team Topologies can work well, and when you&rsquo;re potentially throwing the whole org into upheaval, you better have done the homework.</li>
<li>Communicating well and getting the logistics right are the two single most significant determinants in delivering. Invest the time and energy to get those things right.</li>
<li>Your top-level streams of value are important; if you can identify those initial streams accurately, you&rsquo;ll have an easier time figuring out how to split them when the time comes.</li>
<li>Humans are social creatures. Get buy-in. Get help. You can&rsquo;t do it on your own.</li>
<li>We&rsquo;re rolling out more teams and more streams faster than we thought we would. The ultimate mark of success in my book is more teams delivering more value to customers.</li>
</ul>
<h2 id="citations-and-further-reading">Citations and Further Reading</h2>
<ul>
<li><a href="http://www.melconway.com/Home/pdf/committees.pdf" target="_blank" rel="noopener noreferrer">Conway, Melvin E — How do committees invent?</a>

</li>
<li><a href="https://teamtopologies.com/book" target="_blank" rel="noopener noreferrer">Skelton, Pais — Team Topologies</a>

</li>
<li><a href="https://web.devopstopologies.com/" target="_blank" rel="noopener noreferrer">Skelton, Pais — DevOps Topologies</a>

</li>
<li><a href="https://en.wikipedia.org/wiki/Conway%27s_law" target="_blank" rel="noopener noreferrer">Wikipedia, Conway's Law</a>

</li>
<li><a href="https://www.thoughtworks.com/insights/blog/applying-conways-law-improve-your-software-development" target="_blank" rel="noopener noreferrer">Applying Conway's Law to improve your software development</a>

</li>
<li><a href="https://en.wikipedia.org/wiki/Cognitive_load#Types" target="_blank" rel="noopener noreferrer">Wikipedia, Cognitive Load</a>

</li>
<li><a href="https://en.wikipedia.org/wiki/Humanism" target="_blank" rel="noopener noreferrer">Wikipedia, Humanism</a>

</li>
<li><a href="https://hbr.org/2019/02/mckinseys-three-horizons-model-defined-innovation-for-years-heres-why-it-no-longer-applies" target="_blank" rel="noopener noreferrer">Steve Blank — Harvard Business Review, McKinsey's Three Horizons Model Defined Innovation for Years. Here's Why It No Longer Applies</a>

</li>
<li><a href="https://web.archive.org/web/20220907052247/https://www.mckinsey.com/business-functions/strategy-and-corporate-finance/our-insights/enduring-ideas-the-three-horizons-of-growth" target="_blank" rel="noopener noreferrer">McKinsey and Company, The Three Horizons of Growth</a>

</li>
<li><a href="https://news.microsoft.com/stories/people/boyd-multerer.html" target="_blank" rel="noopener noreferrer">News, Microsoft — Boyd Multerer</a>

</li>
</ul>
]]></content:encoded></item><item><title>SQL Joins with Sequelize</title><link>https://4lex.nz/posts/sql-joins-with-sequelize/</link><pubDate>Sun, 11 Oct 2020 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/sql-joins-with-sequelize/</guid><description>&lt;p&gt;While working on an API for my day job last week, I needed to do a SQL Inner Join with Sequelize and Typescript in a
web API. Here&amp;rsquo;s how I achieved it.&lt;/p&gt;
&lt;h4 id="environment"&gt;Environment:&lt;/h4&gt;
&lt;p&gt;Things move quickly in the JS ecosystem. Here are the library versions used in the subject API at the time of writing:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;dependencies&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;@types/express&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;~4.0.39&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;@types/sequelize&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;^4.27.21&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;express&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;~4.16.2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;sequelize&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;4.38.0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="context"&gt;Context:&lt;/h4&gt;
&lt;p&gt;I have an identifier in one system that I need to correlate with an identifier in a second system, but I
could only do that via an intermediate identifier, also in the second system. A good case for a SQL Join.&lt;/p&gt;</description><content:encoded><![CDATA[<p>While working on an API for my day job last week, I needed to do a SQL Inner Join with Sequelize and Typescript in a
web API. Here&rsquo;s how I achieved it.</p>
<h4 id="environment">Environment:</h4>
<p>Things move quickly in the JS ecosystem. Here are the library versions used in the subject API at the time of writing:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">&#34;dependencies&#34;</span><span class="err">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;@types/express&#34;</span><span class="p">:</span> <span class="s2">&#34;~4.0.39&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;@types/sequelize&#34;</span><span class="p">:</span> <span class="s2">&#34;^4.27.21&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;express&#34;</span><span class="p">:</span> <span class="s2">&#34;~4.16.2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;sequelize&#34;</span><span class="p">:</span> <span class="s2">&#34;4.38.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span></code></pre></div><h4 id="context">Context:</h4>
<p>I have an identifier in one system that I need to correlate with an identifier in a second system, but I
could only do that via an intermediate identifier, also in the second system. A good case for a SQL Join.</p>
<table>
  <thead>
      <tr>
          <th>System 1</th>
          <th>System 2</th>
          <th>System 2</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Electronic Identifier</td>
          <td>Animal Identifier</td>
          <td>Legacy Animal Identifier</td>
      </tr>
      <tr>
          <td>ABCD</td>
          <td>555 WXYZ</td>
          <td>1234</td>
      </tr>
  </tbody>
</table>
<p>In System 1, I have enough information to send this request:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;items&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;ABC&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;DEF&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;GHI&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>And in response I want:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;links&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;rel&#34;</span><span class="p">:</span> <span class="s2">&#34;self&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;href&#34;</span><span class="p">:</span> <span class="s2">&#34;https://api.com/123-i-am-a-uuid&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;reference&#34;</span><span class="p">:</span> <span class="s2">&#34;123-i-am-a-uuid&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;items&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;legacyId&#34;</span><span class="p">:</span> <span class="mi">123</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;electronicId&#34;</span><span class="p">:</span> <span class="s2">&#34;ABC&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;legacyId&#34;</span><span class="p">:</span> <span class="mi">456</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;electronicId&#34;</span><span class="p">:</span> <span class="s2">&#34;DEF&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;legacyId&#34;</span><span class="p">:</span> <span class="mi">789</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;electronicId&#34;</span><span class="p">:</span> <span class="s2">&#34;GHI&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The initial approach was to make three repository queries to get the data I needed, and then go through three
javascript map operations to splice the data into the above format. Using a series of javascript maps is inefficient, particularly as the data set grows.</p>
<p>A better way is to get SQL to do the grunt work, as it&rsquo;s far more time and memory efficient than the javascript I would have needed to write.</p>
<blockquote>
<p>As an aside, I&rsquo;m not too fond of Javascript on the back end. I don&rsquo;t feel it&rsquo;s performant enough and as your codebase
grows the lack of a robust type system hurts more and more. Typescript helps address the latter (assuming your libraries support it) but does nothing for the former.
A typed language like C# or Java is preferable for web programming in a large enterprise due to better performance, more coherent ecosystem (see: <code>left-pad</code>) and robust type system.</p>
</blockquote>
<p>I need to make a map of identifiers from an <code>electronicIdEvent</code> entity
(that deals with electronic identifiers) and an <code>animal</code> entity that deals with animal information.</p>
<table>
  <thead>
      <tr>
          <th></th>
          <th>electronicId</th>
          <th>animalId</th>
          <th>legacyAnimalId</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>electronicIdEvent</td>
          <td>✓</td>
          <td>✓</td>
          <td></td>
      </tr>
      <tr>
          <td>animal</td>
          <td></td>
          <td>✓</td>
          <td>✓</td>
      </tr>
  </tbody>
</table>
<p>I need the <code>legacyAnimalId</code> field. To find a <code>legacyAnimalId</code> given an <code>electronicIdEvent</code>, I have to:</p>
<ol>
<li>Find all <code>electronicIdEvent</code> rows that match an <code>electronicId</code>.</li>
<li>Find all <code>animal</code> rows that match the <code>animalId</code> of the <code>electronicIdEvent</code> where an <code>electronicId</code> is present.</li>
<li>Return all <code>legacyAnimalId</code> rows that match the <code>animalId</code> of both the <code>animal</code> and the <code>electronicIdEvent</code>.</li>
</ol>
<p>Hopefully, you can see why using javascript maps, and SQL selects for this would be tedious and inefficient.</p>
<p>Here&rsquo;s a SQL query that demonstrates what I want if you&rsquo;re more familiar with SQL then Javascript:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">select</span><span class="w"> </span><span class="n">event</span><span class="p">.</span><span class="n">eid</span><span class="p">,</span><span class="w"> </span><span class="n">event</span><span class="p">.</span><span class="n">animalId</span><span class="p">,</span><span class="w"> </span><span class="n">animal</span><span class="p">.</span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="n">animal</span><span class="p">.</span><span class="n">animalKey</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">from</span><span class="w"> </span><span class="s2">&#34;electronicIdEvent&#34;</span><span class="w"> </span><span class="n">event</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">inner</span><span class="w"> </span><span class="k">join</span><span class="w"> </span><span class="s2">&#34;animal&#34;</span><span class="w"> </span><span class="n">animal</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">on</span><span class="w"> </span><span class="n">event</span><span class="p">.</span><span class="s2">&#34;animalId&#34;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">animal</span><span class="p">.</span><span class="n">id</span><span class="w">
</span></span></span></code></pre></div><h3 id="1-create-associations-for-sequzelize-between-the-data-you-need-to-link">1. Create associations for Sequzelize, between the data you need to link.</h3>
<p>For Sequelize to generate the right SQL, it needs to know about the relationships between your data.</p>
<p>Here&rsquo;s the code that creates associations in Sequelize:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="k">this</span><span class="p">.</span><span class="nx">animal</span><span class="p">.</span><span class="nx">hasMany</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">electronicIdEvent</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">foreignKeyConstraint</span>: <span class="kt">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">foreignKey</span><span class="o">:</span> <span class="s2">&#34;animalId&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="k">this</span><span class="p">.</span><span class="nx">electronicIdEvent</span><span class="p">.</span><span class="nx">belongsTo</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">animal</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">foreignKeyConstraint</span>: <span class="kt">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">foreignKey</span><span class="o">:</span> <span class="s2">&#34;animalId&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>Associations allow the ORM to understand your relations and map <code>One To One</code>, <code>One To Many</code> and <code>Many To Many</code> relationships to appropriate SQL code. The docs describe this <a href="https://sequelize.org/master/manual/assocs.html" target="_blank" rel="noopener noreferrer">here</a>

</p>
<h3 id="2-create-a-sequelize-repository-method-that-uses-this-association-to-form-a-sql-query-that-returns-the-correct-data">2. Create a Sequelize repository method that uses this association to form a SQL query that returns the correct data:</h3>
<p>There is some stuff in here that&rsquo;s not great, but it works well enough. If I had more time, I&rsquo;d figure out
a way of using typescript&rsquo;s not-null override (<code>!</code>), and ideally find a way of making <code>event</code> a typed response.</p>
<p>The pseudocode I want is:</p>
<ul>
<li>First return all the rows for the <code>electronicId</code> and <code>animalId</code> columns in <code>electronicIdEvent</code>, where the <code>electronicId</code> is present in my <code>electronicIdList</code></li>
<li>Next join the rows for those columns with the rows for the <code>legacyAnimalId</code> column, where the <code>animalId</code> is the same in both the <code>electronicIdEvent</code> and <code>animal</code> entities.</li>
<li>When the data comes back, drop the <code>animalId</code> column. I don&rsquo;t need it once it has done the job of being the glue between the other entities and columns.</li>
</ul>
<p>Or, as Javascript:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="c1">// imports omitted for brevity
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">class</span> <span class="nx">AnimalRepository</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">constructor</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="kr">private</span> <span class="nx">db</span>: <span class="kt">AnimalTimelineDbContext</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">getLegacyAnimalIdentifierByElectronicIdentifier</span><span class="p">(</span><span class="nx">electronicIdList</span>: <span class="kt">Array</span><span class="p">&lt;</span><span class="nt">string</span><span class="p">&gt;)</span><span class="o">:</span> <span class="nx">Promise</span><span class="p">&lt;</span><span class="nt">Array</span><span class="p">&lt;</span><span class="nt">IdentifierMap</span><span class="p">&gt;&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">db</span><span class="p">.</span><span class="nx">electronicIdEvent</span>
</span></span><span class="line"><span class="cl">      <span class="p">.</span><span class="nx">findAll</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">        <span class="nx">attributes</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;electronicId&#34;</span><span class="p">,</span> <span class="s2">&#34;animalId&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="nx">where</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">electronicId</span>: <span class="kt">electronicIdList</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="nx">include</span><span class="o">:</span> <span class="p">[</span> 
</span></span><span class="line"><span class="cl">          <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">model</span>: <span class="kt">this.db.animal</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">required</span>: <span class="kt">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nx">attributes</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;legacyAnimalId&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">          <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">],</span>
</span></span><span class="line"><span class="cl">      <span class="p">})</span>
</span></span><span class="line"><span class="cl">      <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">events</span><span class="p">)</span> <span class="o">=&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">events</span><span class="p">.</span><span class="nx">map</span><span class="p">((</span><span class="nx">event</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">({</span>
</span></span><span class="line"><span class="cl">          <span class="nx">eid</span>: <span class="kt">event.electronicId</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">legacyAnimalId</span>: <span class="kt">event.Animal</span><span class="o">!</span><span class="p">.</span><span class="nx">legacyAnimalId</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">}))</span>
</span></span><span class="line"><span class="cl">      <span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><blockquote>
<p>Note: Setting <code>required: true</code> in the <code>include</code> block forces the query to only return records which have an associated model, this converts the query from an OUTER JOIN to an INNER JOIN.</p>
</blockquote>
<blockquote>
<p>Because we only return complete rows (no partials) <code>event.Animal</code> cannot be null, I can override the typescript hint, i.e. <code>event.Animal!.legacyAnimalId</code>.</p>
</blockquote>
<p>I&rsquo;m done at this point. We&rsquo;ve submitted a list of electronic identifiers and gotten back an <code>Array&lt;IdentifierMap&gt;</code>, which is syntactic sugar for <code>Array&lt;[string, integer]&gt;</code>.</p>
<h3 id="3-convert-the-raw-data-into-my-map-object-and-fire-the-response-back">3. Convert the raw data into my map object, and fire the response back.</h3>
<p>Because Sequelize is doing most of the grunt work in formatting the data, all I need in my web layer is to add the UUID
that the user-submitted and save that map. Once I&rsquo;ve saved the map and returned the reference to the calling system,
the calling system can ask for that map and get the data (this is the most RESTful way of handling the data exchange
and I&rsquo;m omitting that setup for a follow-up post).</p>
]]></content:encoded></item><item><title>Running Deployments with Github Actions</title><link>https://4lex.nz/posts/github-actions-heroku-sentry/</link><pubDate>Mon, 13 Jul 2020 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/github-actions-heroku-sentry/</guid><description>&lt;p&gt;In a previous article I wrote about &lt;a href="https://4lex.nz/2020/05/getting-started-with-github-actions" target="_blank" rel="noopener noreferrer"&gt;Getting Started with Github Actions&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;In it, I talk about the basics of Actions, how they work, what the language looks like and how you can get started. If you&amp;rsquo;ve never used Actions before, I highly recommend you have a quick read of that before reading this, as I build on some of the ideas presented there.&lt;/p&gt;
&lt;h2 id="continuous-integration-ci-versus-continues-deployment-cd-versus-continuous-delivery-unhelpfully-also-cd"&gt;Continuous Integration (CI) versus Continues Deployment (CD) versus Continuous Delivery (unhelpfully, also CD)&lt;/h2&gt;
&lt;p&gt;Put simply, &lt;strong&gt;continuous integration&lt;/strong&gt; is the act of continually moving your changeset into the wider codebase, as frequently as it makes sense for your use case. CI is an essential practice because it drives us as engineers to produce smaller changes more often, and these small changes tend to be less risky individually. Those small changes also lead us to deliver value to our users more rapidly, and more often.&lt;/p&gt;</description><content:encoded><![CDATA[<p>In a previous article I wrote about <a href="https://4lex.nz/2020/05/getting-started-with-github-actions" target="_blank" rel="noopener noreferrer">Getting Started with Github Actions</a>

.</p>
<p>In it, I talk about the basics of Actions, how they work, what the language looks like and how you can get started. If you&rsquo;ve never used Actions before, I highly recommend you have a quick read of that before reading this, as I build on some of the ideas presented there.</p>
<h2 id="continuous-integration-ci-versus-continues-deployment-cd-versus-continuous-delivery-unhelpfully-also-cd">Continuous Integration (CI) versus Continues Deployment (CD) versus Continuous Delivery (unhelpfully, also CD)</h2>
<p>Put simply, <strong>continuous integration</strong> is the act of continually moving your changeset into the wider codebase, as frequently as it makes sense for your use case. CI is an essential practice because it drives us as engineers to produce smaller changes more often, and these small changes tend to be less risky individually. Those small changes also lead us to deliver value to our users more rapidly, and more often.</p>
<p><strong>Continuous deployment</strong> is the engineering decision to ship validated or verified changes to production environments as often as practical. The decision to continually deploy tends to be made for the same reasons as we choose to continuously integrate. It&rsquo;s about reducing risk, making improvements available sooner, and building trust with users and with our less technology-focused colleagues.</p>
<p>The natural extension of continuous deployment is <strong>continuous delivery</strong>. While continuous delivery has become an umbrella term to describe all of the bits and pieces that contribute to it, I do see these things as separate concepts. The key difference then is that continuous deployment is an engineering decision to ship code to production. Continuous delivery is the product or broader business decision to make the features that our code provides, available to users.</p>
<p>Each concept requires the predecessor to work, but it can be a valid choice to stop at any of these points and not continue on. My own bias is to adopt continuous delivery as a fundamental practice, but your own operating context will have impacts on what parts of this you can and can&rsquo;t do.</p>
<p>Continuous Delivery is a foundational skill that your organisation needs to be good at if it is to remain responsive and &rsquo;lowercase a&rsquo; agile.</p>
<p>In the previous article, I stated:</p>
<blockquote>
<p>More concretely, a decent CI/CD solution will:</p>
</blockquote>
<ul>
<li>Build your code</li>
<li>Run the tests</li>
<li>Deploy it to an environment</li>
<li>Roll-back if there is an issue</li>
</ul>
<p>My initial implementation of Github Actions saw me using the tool primarily for continuous integration. However, I&rsquo;ve recently expanded that to include continuous deployment (and since I&rsquo;m not using feature flags yet, this technically qualifies as continuous delivery if only as a side effect).</p>
<h2 id="github-actions-now-owns-artefact-build-across-all-environments">Github Actions now owns artefact build across all environments.</h2>
<p>Previously, my <code>create-rc.yml</code> workflow built a container in dev, tested that and then told Heroku that the build is good, and to cut a release.</p>
<p>Heroku then would pull the code, build it and release it to my dev environment. I wasn&rsquo;t actually running the code I tested, I was running code built from the code I tested. This has been the source of subtle but pernicious bugs for me in the past. My rule of thumb is that you want to build your release candidate once and pass it through environments. That way you know for sure, and can prove in an auditable way, that this particular code is running in this specific place.</p>
<blockquote>
<p>Note: The Heroku CLI is bundled as standard on Github Actions runners. I couldn&rsquo;t find this documented anywhere and literally only stumbled on it in a fit of frustration at midnight last night. My new heuristic is that if you need a specific tool, assume it&rsquo;s on the runner but isn&rsquo;t necessarily up to date. If your dependency isn&rsquo;t there, then you should try to work around it.</p>
</blockquote>
<p>I assumed that Heroku CLI wouldn&rsquo;t be there and spent a long time trying to fight shadows. Don&rsquo;t be like me.</p>
<p>Here&rsquo;s a high-level summary of the work I did to make my builds work the way I&rsquo;ve described above, with one RC being deployed to many environments:</p>
<ol>
<li>Have <code>create-rc.yml</code> treat the docker image it makes like the one RC, rather than as a means of delivering integration tests.</li>
<li>Improve the tagging process so that we produce a docker image with the right naming conventions for Heroku.</li>
<li>Push the image to the Heroku docker registry, where we can persist it across builds and environments.</li>
<li>Introduce a new job to deploy the verified image to the right Heroku environment.</li>
<li>Introduce a new workflow with a different trigger, to take the already validated image and redeploy it to production.</li>
</ol>
<p>Overall the changes weren&rsquo;t massive, but I definitely cut myself on some rough edges (documentation being a big part of that).</p>
<p>These steps build my image, tag it and send it off to Heroku. That&rsquo;s step 1, 2 and 3 done in ~20 lines:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Login to Heroku Registry</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">env</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">HEROKU_API_KEY</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.HEROKU_TOKEN }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">heroku container:login</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build and push</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">env</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">HEROKU_API_KEY</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.HEROKU_TOKEN }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">heroku container:push -a dev-api-spring-boot web</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Create tarball from docker image</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c"># Heroku has a naming convention for images, it&#39;s important to match that.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">docker save registry.heroku.com/dev-api-spring-boot/web &gt; spring-boot-api-candidate.tar</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Upload image tarball for next job</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/upload-artifact@v2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">spring-boot-api-candidate.tar</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">spring-boot-api-candidate.tar</span><span class="w">
</span></span></span></code></pre></div><p>These steps close out the only actual building of code that I do. In every other case, I get code from one place to the other by redeploying code I already know to be good.
It&rsquo;s important to note here that I&rsquo;m trying to keep build time low by using capabilities baked into the actions runner, rather than using externally published actions.</p>
<p>It&rsquo;s also important to note that if you use Heroku commands the way I am, you lose visibility of fine-grained build actions because most of that code lives in your docker file. By that, I mean that all I can see in this code is that Heroku is pushing a container. I can&rsquo;t see how it&rsquo;s built. For that, I need to go to my Dockerfile, which in this case looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="l">FROM gradle:6.5.1-jdk14 as build-stage</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">WORKDIR /build</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">COPY src /build/src</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">COPY gradlew /build/gradlew</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">COPY gradle /build/gradle</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">COPY build.gradle /build/build.gradle</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">COPY settings.gradle /build/settings.gradle</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">RUN /build/gradlew clean build -x test</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># Running container build begins</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">FROM adoptopenjdk:14.0.1_7-jre-openj9-0.20.0-bionic</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># copy application JAR</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">COPY --from=build-stage build/build/libs/spring-boot-api-*.jar /spring-boot-api.jar</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># specify default command</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># The command to run this in local mode would be docker run -p 8080:8080 -e JAVA_OPTS=&#34;-Dspring.profiles.active=local -Dserver.port=$PORT&#34; registry.heroku.com/dev-api-spring-boot/web</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="l">CMD [&#34;sh&#34;, &#34;-c&#34;, &#34; java ${JAVA_OPTS} -jar /spring-boot-api.jar&#34;]</span><span class="w">
</span></span></span></code></pre></div><p>Now I have two sets of build logic in two places, which isn&rsquo;t perfect, but given how docker works, it&rsquo;s tolerable. An example of the impact this has is that if I want to stop running tests while I build the image (because I unit test and integration test within the workflow), I need to add <code>-x test</code> to the end of my Gradle command in the Dockerfile as seen here. It adds a little bit of cognitive burden. Okay for now but not ideal.</p>
<p>Speaking of unit tests, since I want to cut down my build time, I can run them in parralel! Hooray for Github&rsquo;s <code>jobs are parralel by default</code> stance!</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">unit-test</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">needs</span><span class="p">:</span><span class="w"> </span><span class="l">build-image</span><span class="w"> </span><span class="c"># build-image is the name of the first job in this workflow, it&#39;s where the Heroku code above lives.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Check out code</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Set up JDK 14</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/setup-java@v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">java-version</span><span class="p">:</span><span class="w"> </span><span class="m">14</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Make gradle wrapper executable</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">chmod +x gradlew</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Run Unit Tests</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">env</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">PORT</span><span class="p">:</span><span class="w"> </span><span class="m">8080</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">./gradlew clean test</span><span class="w">
</span></span></span></code></pre></div><h2 id="releasing-a-verified-image-to-heroku">Releasing a verified image to Heroku</h2>
<p>The great thing is that we only need to add one more step to our <code>create-rc.yml</code> (although this file should probably be renamed to <code>dev-build</code> or something simillar).
What I&rsquo;m saying below is that at the end of my build, only when I&rsquo;ve got green unit tests and green integration tests, I want that image that I&rsquo;ve already pushed, to be activated in Heroku.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">dev-deploy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">needs</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">integration-test, unit-test]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Login to Heroku Registry</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">env</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">HEROKU_API_KEY</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.HEROKU_TOKEN }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">heroku container:login</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Release to Heroku dev</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">env</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">HEROKU_API_KEY</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.HEROKU_TOKEN }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">heroku container:release -a dev-api-spring-boot web</span><span class="w">
</span></span></span></code></pre></div><p>I don&rsquo;t have to care about artefact handling or pass in a tarball. The image is always current, and I just choose whether or not I want to make it active.
One of the drawbacks here though is that I could get an image pushed that fails unit tests or integration tests. This is mitigated by the image remaining inactive until I release it.
There is an edge case here that if I had to rollback, I might roll back to a bad image but that&rsquo;s a problem for improvement later - and I&rsquo;ll never have a broken image that won&rsquo;t start because if the image won&rsquo;t build, the build job will fail.</p>
<p>So with the above changes, we have a dev build process entirely owned by Github Actions. In theory, I could rip out Heroku and point to AWS ECS or somewhere else quickly. I&rsquo;m using my platform provider for its platform, not for the stepladder on to the platform!</p>
<h2 id="releasing-to-production-with-sentry-integration">Releasing to Production with Sentry Integration</h2>
<p>My procedure is that a release to production is triggered on a push to <code>main</code>, and the only way to push to main is via a merged commit.
This procedure needs to do a few things:</p>
<ol>
<li>Take a validated image from an upstream environment and retag it.</li>
<li>Push that retagged image back to Heroku</li>
<li>Release the image</li>
<li>Create a new release version in Sentry</li>
</ol>
<p>I have some intermediate steps here that I want to use to tie things together from an audit perspective, but I&rsquo;m waiting on some feedback from elsewhere before I can do that.
Here are the steps for my release job, note how similar it is to my dev release methodology.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Login to Heroku Registry</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">env</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">HEROKU_API_KEY</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.HEROKU_TOKEN }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">heroku container:login</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Pull dev image</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">docker pull registry.heroku.com/dev-api-spring-boot/web</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Re-tag dev as prod</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c"># Again, Heroku expects a naming convention here, so we just have to comply.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">docker tag registry.heroku.com/dev-api-spring-boot/web registry.heroku.com/api-spring-boot/web</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Push production image</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">docker push registry.heroku.com/api-spring-boot/web</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Push Prod Image</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">env</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">HEROKU_API_KEY</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.HEROKU_TOKEN }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">heroku container:release -a api-spring-boot web</span><span class="w">
</span></span></span></code></pre></div><p>The post-processing I do on a release is much more interesting then docker jiggery-pokery:</p>
<ol>
<li>I want to grab the entire codebase because I want that delicious commit history.</li>
<li>I want to create a new tag for this production release</li>
<li>I want to get all of the commit descriptions between the new tag, and the previous tag, and then filter out some commits that aren&rsquo;t interesting for given values of interesting</li>
<li>Then cut a release for Github using the commits as a changelog</li>
<li>Finally, use Sentry.io for crash reports, and I want to tell Sentry about the new version so that I can track where issues might have been introduced.</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Checkout code</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># Fetch depth grabs the entire history, though I&#39;m sure I could grab the commits since the last tag if I really wanted.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">fetch-depth</span><span class="p">:</span><span class="w"> </span><span class="m">0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># I encountered an issue here so have left this commented out. It&#39;s not a part of my workflow, but hopefully one day it&#39;ll create tags for me</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># Leaving this out for now - to use the output of tag in the below release use ${{ steps.create_tag.outputs.new_tag }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c">#      - name: Bump version and push tag</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c">#        id: create_tag</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c">#        uses: mathieudutour/github-tag-action@v4.5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c">#        with:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c">#          github_token: ${{ secrets.GITHUB_TOKEN }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c">#          release_branches: dev</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Generate release notes</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c"># Important note: I use https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit for commit format. It makes it really easy to filter commits in the way I&#39;ve done below.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">git log $(git describe --tags --abbrev=0)..HEAD --pretty=format:&#34;%s&#34; -i -E --grep=&#34;^(build|ci|docs|feat|fix|perf|refactor|style|test)*feat|perf|fix&#34; &gt; release-notes.txt</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># I don&#39;t want to cut releases with no info in them! That&#39;s why this too is not currently part of my workflow</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c">#      - name: Create Github Release</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c">#        id: create_release</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c">#        uses: actions/create-release@v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c">#        env:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c">#          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c">#        with:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c">#          tag_name: ${{ github.sha }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c">#          release_name: Release ${{ github.sha }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c">#          body_path: release-notes.txt</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c">#          draft: false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c">#          prerelease: false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Create Sentry release</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">tclindner/sentry-releases-action@v1.2.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">env</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">SENTRY_AUTH_TOKEN</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.SENTRY_AUTH_TOKEN }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">SENTRY_ORG</span><span class="p">:</span><span class="w"> </span><span class="l">my-sentry-org</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">SENTRY_PROJECT</span><span class="p">:</span><span class="w"> </span><span class="l">spring-boot-api</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">tagName</span><span class="p">:</span><span class="w"> </span><span class="l">${{ github.sha }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w"> </span><span class="l">heroku-prod</span><span class="w">
</span></span></span></code></pre></div><p>The net effect of the above is that I get to take a previously validated image. When I choose (via merged PR), I can automatically redeploy quality code to prod without rebuilding.
The neat interplay here is that when I open that PR, my project will build, and use that same build to decide whether or not I can merge. As soon as I merge, the code is in prod without a rebuild. It&rsquo;s nice.</p>
<p>With a new release being cut in Sentry each time, I can resolve errors that I&rsquo;ve introduced down to at worst, a PR or at best, an individual commit. Also nice!</p>
<p><img alt="Sentry Releases" loading="lazy" src="/img/in-post/sentry-releases.png"></p>
]]></content:encoded></item><item><title>Getting Started with Github Actions</title><link>https://4lex.nz/posts/getting-started-with-github-actions/</link><pubDate>Thu, 28 May 2020 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/getting-started-with-github-actions/</guid><description>&lt;p&gt;Continuous Delivery is a foundational skill that your organisation needs to be good at if it is to remain responsive and &amp;rsquo;lowercase a&amp;rsquo; agile.&lt;/p&gt;
&lt;p&gt;A full solution for continuous delivery tends to include (but isn&amp;rsquo;t limited to):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The ability to create artefacts that we can release to production, across many products, teams, branches, environments and repositories.&lt;/li&gt;
&lt;li&gt;The ability to quickly validate an artefact as a candidate for release (through applying the testing triangle - unit, integration, acceptance, smoke and so on).&lt;/li&gt;
&lt;li&gt;The ability to release code to production without necessarily activating it for users (typically through feature flags, canary built and graduated rollouts).&lt;/li&gt;
&lt;li&gt;The ability to rapidly respond if a release candidate we later find to be problematic (again through canary builds, graduated rollouts, and by quickly rolling back software found to be problematic).&lt;/li&gt;
&lt;li&gt;The ability to audit or understand what code is where, what state that code is in, and why that code is where it is.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;More concretely, a decent CI/CD solution will:&lt;/p&gt;</description><content:encoded><![CDATA[<p>Continuous Delivery is a foundational skill that your organisation needs to be good at if it is to remain responsive and &rsquo;lowercase a&rsquo; agile.</p>
<p>A full solution for continuous delivery tends to include (but isn&rsquo;t limited to):</p>
<ul>
<li>The ability to create artefacts that we can release to production, across many products, teams, branches, environments and repositories.</li>
<li>The ability to quickly validate an artefact as a candidate for release (through applying the testing triangle - unit, integration, acceptance, smoke and so on).</li>
<li>The ability to release code to production without necessarily activating it for users (typically through feature flags, canary built and graduated rollouts).</li>
<li>The ability to rapidly respond if a release candidate we later find to be problematic (again through canary builds, graduated rollouts, and by quickly rolling back software found to be problematic).</li>
<li>The ability to audit or understand what code is where, what state that code is in, and why that code is where it is.</li>
</ul>
<p>More concretely, a decent CI/CD solution will:</p>
<ul>
<li>Build your code</li>
<li>Run the tests</li>
<li>Deploy it to an environment</li>
<li>Roll-back if there is an issue</li>
</ul>
<p>Building an excellent build tool is no easy task, and because of the variety of use cases out there, you tend to find tools optimised for those use cases. They&rsquo;ll be passable or useful in that niche and poor outside of it.
Use-case variety and the complexity of the problem space is part of the reason why container-based builds have taken off. Where previously I&rsquo;d have precious build agent boxes sitting somewhere that I&rsquo;d need to feed and water and love and sell my children to, now I can have a dumb build box that knows about nothing. The build box just grabs a container off the internet (or I make a custom container) to fill my needs.</p>
<h2 id="github-actions">Github Actions</h2>
<p><a href="https://github.com/features/actions" target="_blank" rel="noopener noreferrer">Github Actions</a>

 is one such container based build offering, though at first they don&rsquo;t tell you that!</p>
<p>The highest order concept in Actions is the Workflow. A workflow consists of several jobs, and each job can have several steps. Here&rsquo;s an example we&rsquo;ll become a bit more familiar with later:</p>
<p><img alt="Github Actions Example Workflow" loading="lazy" src="/img/in-post/example-github-actions-workflow.jpg"></p>
<p>As you can see, A workflow, is a set of jobs (that we run in series or parallel), with each job containing several steps.
Jobs describe collections of steps that when together, perform some action that is useful to the business. Model your jobs in terms of things that are useful for you - it&rsquo;s these aggregates that you&rsquo;ll be watching most.</p>
<p>Steps are concrete actions that we perform, things like building, unit testing, renaming files and so on. A step is the smallest unit of work you can do in a workflow, and they don&rsquo;t need to be useful in and of themselves.</p>
<blockquote>
<p>The reason I think that jobs should be useful units of work for your business, is because if you plan them that way, it becomes trivial to re-use jobs to achieve new functionality quickly</p>
</blockquote>
<h3 id="pipelines-as-code-with-yaml">Pipelines as Code with&hellip; YAML</h3>
<p>Workflows use YAML as the format of choice, and I YAML in Github Actions as much as I hate any other white spaced language in a place it doesn&rsquo;t belong. I&rsquo;m sure Github chose YAML for a good reason, I&rsquo;ve struggled to find out what those reasons were or are. Work continues in that area.</p>
<p><img alt="I WILL RUN INTO THE WOODS DON&rsquo;T TEMPT ME" loading="lazy" src="/img/in-post/YML.png"></p>
<p>As with most things that involve YAML, the workflow file is easy to quickly grok while it&rsquo;s small to medium length but challenging to troubleshoot or do anything with once it gets a bit bigger.
Github Actions is still new, so I can understand why it might be a little rough around the edges. Perhaps there is tooling around the workflow file I&rsquo;m ignorant of, but an equivalent to Bitbucket Pipelines&rsquo;s <a href="https://web.archive.org/web/20240105172732/https://bitbucket-pipelines.prod.public.atl-paas.net/validator" target="_blank" rel="noopener noreferrer">pipeline validator</a>

 would be an excellent addition.</p>
<p>Even better still (and this would be a feature far down the track), give me a fluent API akin to <a href="https://confluence.atlassian.com/bamboo/tutorial-create-a-simple-plan-with-bamboo-specs-894743911.html" target="_blank" rel="noopener noreferrer">Bamboo's Java Spec Concept</a>

.</p>
<p>To save you a click, 90% of my warm fuzzies for Atlassian Bamboo come from being able to do this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">private</span><span class="w"> </span><span class="n">Plan</span><span class="w"> </span><span class="nf">createPlan</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Plan</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">project</span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="s">&#34;Plan Name&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;PLANKEY&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">description</span><span class="p">(</span><span class="s">&#34;Plan created from (enter repository url of your plan)&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">stages</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                    </span><span class="k">new</span><span class="w"> </span><span class="n">Stage</span><span class="p">(</span><span class="s">&#34;Stage 1&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                            </span><span class="p">.</span><span class="na">jobs</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">Job</span><span class="p">(</span><span class="s">&#34;Build &amp; run&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;RUN&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                                    </span><span class="p">.</span><span class="na">tasks</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                                            </span><span class="k">new</span><span class="w"> </span><span class="n">ScriptTask</span><span class="p">().</span><span class="na">inlineBody</span><span class="p">(</span><span class="s">&#34;echo Hello world!&#34;</span><span class="p">))));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>Getting access to this kind of API, and provide auto-completion, intellisense and everything that comes with that ecosystem is war-winning feature for pipeline builders.</p>
<p>I would prefer XML to YAML. There. I said it. We can move on now.</p>
<h2 id="workflows">Workflows</h2>
<p>What does a workflow file look like? Here&rsquo;s the file for a Spring Boot application I&rsquo;m currently working on.
I was very focused on getting a minimal workflow up and running, so there are things to be added and things to be optimised.</p>
<p>Have a big read, and I&rsquo;ll break it down below:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Create Release Candidate</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">push</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">branches</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="l">dev ]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pull_request</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">branches</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="l">dev ]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">create-rc</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Check out code</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build and tag release candidate</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">docker/build-push-action@v1.1.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">username</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.HEROKU_USER }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">password</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.HEROKU_TOKEN }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">registry</span><span class="p">:</span><span class="w"> </span><span class="l">registry.heroku.com</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">repository</span><span class="p">:</span><span class="w"> </span><span class="l">image-repository/heroku-process</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">tags</span><span class="p">:</span><span class="w"> </span><span class="l">latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">push</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Create tarball from docker image</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">docker save registry.heroku.com/image-repository/heroku-process:latest &gt; heroku-process-candidate.tar</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Upload image tarball for next job</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/upload-artifact@v2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">heroku-process-candidate.tar</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">heroku-process-candidate.tar</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">integration-test</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">needs</span><span class="p">:</span><span class="w"> </span><span class="l">create-rc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Check out code</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Download tarball</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/download-artifact@v2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">heroku-process-candidate.tar</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Load image from tarball</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">docker load &lt; heroku-process-candidate.tar</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c"># github doesn&#39;t allow us to gracefully expire artifacts (e.g. keep last 10 build artifacts and no more)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c"># if we don&#39;t delete these, we&#39;ll hit the storage limit for actions quickly.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c"># persisting docker images outside of builds should be done by pushing the image to a registry</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c"># we should rm the tarball as soon as we&#39;ve loaded it as a docker image.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Tidy up artifact to avoid storage limit</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">geekyeggo/delete-artifact@v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">heroku-process-candidate.tar</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Run application server</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">docker run -d -p 8080:8080 -e JAVA_OPTS=-Dspring.profiles.active=dev registry.heroku.com/image-repository/heroku-process:latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Make server watch script executable</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">chmod +x wait-for-heroku-process.sh</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Wait for server to start</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">./wait-for-heroku-process.sh</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Set up JDK 14</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/setup-java@v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">java-version</span><span class="p">:</span><span class="w"> </span><span class="m">14</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Make gradle wrapper executable</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">chmod +x gradlew</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Run Integration Tests</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">./gradlew integrationTest</span><span class="w">
</span></span></span></code></pre></div><p>The above file is just under 100 lines of YAML to describe the first two jobs in the image above (Package for release and Run Integration tests).
Now that I&rsquo;ve finalised everything, it looks pretty clean and tidy, the process to get there was anything but. Again this is something that I&rsquo;d attribute to new-ness. Still, Github Actions does an excellent job at giving you the low-level details about how a thing works, but a lousy job at describing how you can use what Actions provides you to create higher-order pipelines.</p>
<p>For example, the Gradle starter <a href="https://github.com/actions/starter-workflows/blob/master/ci/gradle.yml" target="_blank" rel="noopener noreferrer">here</a>

 does a good job of telling me how to declare jobs, steps and make them dependent on pull requests or branches. It also leaves me with questions. I want to package my application, and then run it, and then only when it&rsquo;s running, run unit tests. How do I do that? I want to publish to my registry, but only if the unit and integration tests have passed, except for in dev, we can publish there with some failing tests. How do I do that?</p>
<p>The basic building blocks are there, but more complex pipeline logic is missing.</p>
<p>Compare this with the <strong>excellent</strong> documentation produced by VMWare and Pivotal for <a href="https://github.com/vmwarepivotallabs/concourse-pipeline-samples" target="_blank" rel="noopener noreferrer">Concourse CI</a>

 that gets much more into higher-order combinations of those basic building blocks.</p>
<p>Compare <a href="https://github.com/vmwarepivotallabs/concourse-pipeline-samples/tree/master/concourse-pipeline-patterns/gated-pipelines" target="_blank" rel="noopener noreferrer">Gated CI Pipelines</a>

 and <a href="https://github.com/vmwarepivotallabs/concourse-pipeline-samples/tree/master/pipelines/appdev/blue-green-app-deployment" target="_blank" rel="noopener noreferrer">Blue-Green Deployments</a>

 with Concourse, to anything in the only workflow template repository I could find, <a href="https://github.com/actions/starter-workflows/tree/master/ci" target="_blank" rel="noopener noreferrer">Starter Workflows</a>

 to see that high level / low-level split.</p>
<p>Concourse is a more mature product and more fully-featured, where perhaps Github Actions is still more of a CI tool rather than a CD tool. I&rsquo;m sure it will get there in time. The portability and reusability that Github Actions provides is a significant advantage over other build tools.</p>
<blockquote>
<p>I love how sturdy the modelling and pipelining features are for Concourse. It&rsquo;s ironic though that at my current workplace we&rsquo;ve just decided to get off Concourse CI in favour of Github Actions.</p>
</blockquote>
<h2 id="jobs-in-detail">Jobs in detail</h2>
<p>Here&rsquo;s the same workflow as above, trimmed for easier reading.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Create Release Candidate</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">push</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">branches</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="l">dev ]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pull_request</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">branches</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="l">dev ]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">create-rc</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Arbitrary Step 1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">echo &#34;Hello Muddah&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Arbitrary Step 2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">echo &#34;Hello Fadduh&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">integration-test</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">needs</span><span class="p">:</span><span class="w"> </span><span class="l">create-rc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Arbitrary Step 1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">echo &#34;Here I am at&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Arbitrary Step 2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">echo &#34;Camp Granada.&#34;</span><span class="w">
</span></span></span></code></pre></div><h3 id="important-concepts-jobs-and-steps">Important Concepts: Jobs and Steps</h3>
<ul>
<li>A workflow has one set of jobs</li>
<li>A job has one set of steps.</li>
<li>Both of these sets can have one to many items in them. You can have ten jobs, each with 20 steps, or one job with 1 step.</li>
<li>To create a sequential pipeline, add a <code>needs</code> statement in every job after the first one, and in that needs statement, refer to the previous job. For example, I have two jobs, <code>create-rc</code> and <code>integration-test</code>. In the second job, I have this statement: <code>needs: create-rc</code>.</li>
<li><strong>Importantly</strong>, Jobs run in parallel by default. A sequential run like the above is something you have to do specifically. Parallel by default creates advantages if you can decouple your build process.</li>
<li>Right now, I have to wait for Actions to build my RC and image before I can integration test. I believe in pushing one artefact through the entire pipeline rather than rebuilding it each from source each time - but if I didn&rsquo;t, I might be able to get super parallelisation on my build pipelines. I.e. rather than Checkout Source &gt; Build &gt; Test &gt; Deploy with one artefact the whole way through, I could Checkout Source &gt; Build, Checkout Source &gt; Test, Checkout Source &gt; Package &gt; Deploy all at the same time. This means the time the build takes is the <code>max()</code> of the longest job, rather than <code>sum(job_1 + job_2 ... job_n)</code>.</li>
<li>I&rsquo;m an absolute savage though and believe that the best approach is to checkout and built once, and then pass an artefact all the way through. The tradeoff for that certainty is a longer build time (about 5 minutes in this case, but for longer build processes, it probably stacks up quick).</li>
<li>There is a whole ecosystem of Actions available on the Github Marketplace, but every action you use will cost you in build time. Actions are distributed as Docker images, and when your build runs, before you can build your build, you have to build all of your actions.</li>
<li>Steps all run sequentially, and an action can be a step, but not all steps have to be actions. For example:</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Arbitrary Step 1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">echo &#34;Hello Mother&#34;</span><span class="w">
</span></span></span></code></pre></div><p>The above is a step that is not an action; the below is a step that is action. All actions are steps, but a step does not have to be an action.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build and tag release candidate</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">docker/build-push-action@v1.1.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">username</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.HEROKU_USER }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">password</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.HEROKU_TOKEN }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">registry</span><span class="p">:</span><span class="w"> </span><span class="l">registry.heroku.com</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">repository</span><span class="p">:</span><span class="w"> </span><span class="l">image-repository/heroku-process</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">tags</span><span class="p">:</span><span class="w"> </span><span class="l">latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">push</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span></code></pre></div><h2 id="sharing-and-storing-artifacts">Sharing and Storing Artifacts</h2>
<p>Each job runs in parallel and runs in a separate container.
If you have a job that builds, and you have a job that tests, you have to figure out a way to get the build output into the testing task.
Github&rsquo;s solution to this seems inefficient, but I can&rsquo;t see any better way of doing it.</p>
<p>Your workflow could happen anywhere, multiple anywhere even. Because jobs are independent, in theory, you could have your build happening over in the US East region and your tests happening in Europe.
You can&rsquo;t rely on them being on the same machine or even in the same DC, so sharing artefacts via cloud storage is the only choice you have.</p>
<p>It gives rise to these two actions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Upload image tarball for next job</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/upload-artifact@v2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">heroku-process-candidate.tar</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">heroku-process-candidate.tar</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Download tarball</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/download-artifact@v2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">heroku-process-candidate.tar</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Load image from tarball</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">docker load &lt; heroku-process-candidate.tar</span><span class="w">
</span></span></span></code></pre></div><p>The way we share artefacts between jobs (or persist them outside of builds) is essentially by uploading and downloading from S3 or an equivalent (I assume they use S3 but haven&rsquo;t done the research to confirm).
I have a small workflow right now, but what this means is that passing my artefact from my first job to the second, accounts for a whopping <strong>21%</strong> of the run time of the build. This behaviour pushes me to a perverse outcome where I&rsquo;m better off starting from scratch in every single job because of the time it takes to trade artefacts! An alternative to that would be to have one enormous job, but that also flies in the face of the whole idea of Github Actions, which is that concept of composable, reusable workflows.</p>
<p>Further, you need to be careful with your usage of artefacts.</p>
<p>On a team or pro plan, you get 2GB of storage for actions. Artefacts associated with builds older than 90 days get deleted. To naturally stay at or under the 2GB limit, your builds need to persist fewer than <strong>22MB per day - not per build, per day</strong> worth of artefacts. Github Packages, and storage for Actions, get very spendy, very quickly.</p>
<p>This behaviour gives rise to actions like this one. I consider this 100% required for anybody who has Workflows with more than one job, where you need to pass an artefact between them.
Remember, every job that needs to share an artefact will incur the wrath of Github Packages.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="w">      </span><span class="c"># github doesn&#39;t allow us to gracefully expire artifacts (e.g. keep last 10 build artifacts and no more)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c"># if we don&#39;t delete these, we&#39;ll hit the storage limit for actions quickly.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c"># persisting docker images outside of builds should be done by pushing the image to a registry</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c"># we should rm the tarball as soon as we&#39;ve loaded it as a docker image.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Tidy up artifact to avoid storage limit</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">geekyeggo/delete-artifact@v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">heroku-process-candidate.tar</span><span class="w">
</span></span></span></code></pre></div><p>As per the comment, I&rsquo;d recommend you persist artefacts during builds and between jobs, delete them afterwards, and for audit, viewing test reports and things like that, find somewhere else to put them. Put containers into your registry, put test reports into S3 or similar. For more detail on how this all gets billed, see <a href="https://help.github.com/en/github/setting-up-and-managing-billing-and-payments-on-github/about-billing-for-github-actions" target="_blank" rel="noopener noreferrer">this</a>

</p>
<h2 id="in-closing-why-use-actions">In closing, why use Actions?</h2>
<p>I think for a lot of people (myself included), the main draw for Github Actions is that it&rsquo;s conceptually quite simple (though as with anything there are gotchas), and tightly woven in with Github itself.
I like that the build lives close to the code and everything that comes with it. Github Actions is &lsquo;pipeline-as-code&rsquo;, that isn&rsquo;t that much of a burden to learn and use, and even having been newly released, it&rsquo;s as capable as any other build tool I&rsquo;ve used.</p>
<p>Where it starts to get powerful is the open-source ecosystem of Actions and the tie into wider Github. Be that the code repository or other apps I might have added to my repo or org. I can get this without having to deal with another tool, another login, or the overhead of another new thing. Thinking on it, maybe that&rsquo;s why they chose YAML.</p>
<p>While I&rsquo;ve spent a lot of time bagging it, I quite like it. It&rsquo;s a good example of delivering just enough capability at the right time, and I&rsquo;m sure Actions will continue to grow, if not via first-party development, certainly through that action ecosystem.</p>
<p>Yes, I&rsquo;ve got gripes about YAML, but I have that most places I go. You can work around the storage issues, and it&rsquo;s not unreasonable for Github to try and make money given everything else they provide for free or for cheap.
Actions is a fully managed service, no servers to feed and water, no breaking updates so far. With versionable pipelines, there are very few moving parts that we the end-users have to care about. That&rsquo;s worth more than it&rsquo;s equivalent weight in gold.</p>
<p>If you&rsquo;ve got a pet project or something new at work, I recommend you give it a try.</p>
]]></content:encoded></item><item><title>Spring Boot - Comparing Liquibase and Flyway</title><link>https://4lex.nz/posts/spring-boot-database-migrations/</link><pubDate>Thu, 23 Apr 2020 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/spring-boot-database-migrations/</guid><description>&lt;h3 id="what-are-spring-boot-liquibase-and-flyway"&gt;What are Spring Boot, Liquibase and Flyway?&lt;/h3&gt;
&lt;h4 id="the-spring-ecosystem"&gt;The Spring Ecosystem&lt;/h4&gt;
&lt;p&gt;When you need to create a web application or an API in Java, be that RESTful, SOAP or GraphQL; whether you&amp;rsquo;re looking at synchronous HTTP, Asynchronous or Reactive, Messages on Queues or Event Sourced with Kafka, it&amp;rsquo;s hard to go past the Spring Ecosystem. If nobody ever got fired for buying IBM, you could probably say that nobody ever got fired for creating a Spring-based application.&lt;/p&gt;</description><content:encoded><![CDATA[<h3 id="what-are-spring-boot-liquibase-and-flyway">What are Spring Boot, Liquibase and Flyway?</h3>
<h4 id="the-spring-ecosystem">The Spring Ecosystem</h4>
<p>When you need to create a web application or an API in Java, be that RESTful, SOAP or GraphQL; whether you&rsquo;re looking at synchronous HTTP, Asynchronous or Reactive, Messages on Queues or Event Sourced with Kafka, it&rsquo;s hard to go past the Spring Ecosystem. If nobody ever got fired for buying IBM, you could probably say that nobody ever got fired for creating a Spring-based application.</p>
<p>Spring is an extremely popular, well maintained and well known Java Web Application Framework. Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can &ldquo;just run&rdquo;.</p>
<p>Spring Boot is to Spring and Java as Ruby on Rails is to Ruby. So if you&rsquo;re working in a Java environment, there&rsquo;s a pretty good chance you&rsquo;re using Spring or Spring Boot. Even if you&rsquo;re using another framework, it&rsquo;s worth learning about these - they&rsquo;re so ubiquitous they&rsquo;ll be around for a long time!</p>
<h4 id="database-migrations">Database Migrations</h4>
<p>If you&rsquo;re using Spring, there&rsquo;s a chance you&rsquo;re using persistence tech like <a href="https://hibernate.org/" target="_blank" rel="noopener noreferrer">Hibernate</a>

, <a href="https://www.jooq.org/" target="_blank" rel="noopener noreferrer">Jooq</a>

 or <a href="https://ebean.io/" target="_blank" rel="noopener noreferrer">Ebean</a>

 to get data out of your database.</p>
<p>Features come and go; data models change; what happens when you need to change your data model? You run a database migration to add or remove columns or make other changes.</p>
<p>It used to be that this would happen manually; someone would log on to a database, run a bunch of SQL and rely on the fact that you&rsquo;ve made the same changes in your application. There is a bunch of room for human error here, though. So we improve on this by trying to make our database changes safer. Version control, repeatability, and testability become applicable to more parts of the stack. Databases included.</p>
<p>Both Liquibase and Flyway help us with these migrations. Spring helps us with Liquibase and Flyway in turn. The result is a database changeset that does what we have specified every single time (of course, we might still specify the wrong thing - computers remain computers). All we need to do is write a migration and dump it into a folder in our Spring project to achieve that result. Spring handles the rest.</p>
<h4 id="liquibase">Liquibase</h4>
<p>Liquibase is offered free and paid. They don&rsquo;t share pricing on their website, though, which makes me suspicious. They don&rsquo;t do an excellent job of describing why I would want to pay either. Liquibase offers migrations both through XML and SQL. The basic concept is that you have a master file that describes your database configuration and the changesets you want to include in your run. Spring comes in at that point, parses your config and your included changesets, and manages them appropriately.</p>
<h4 id="flyway">Flyway</h4>
<p>Flyway is offered free and paid, the same as liquibase. The pricing and feature breakdown is available <a href="https://flywaydb.org/download/" target="_blank" rel="noopener noreferrer">here</a>

 - and is a lot more detailed than anything I could find on liquibase.</p>
<p>Flyway focuses on your migrations as a first-class concept. You write SQL scripts, place them in a folder in your Spring project, add some config to your <code>application.yml</code> files and then Spring runs the migrations as per your config.</p>
<h4 id="links">Links:</h4>
<ul>
<li><a href="https://spring.io/projects/spring-boot" target="_blank" rel="noopener noreferrer">Spring Boot</a>

</li>
<li><a href="https://www.liquibase.org/get_started/index.html" target="_blank" rel="noopener noreferrer">Liquibase</a>


<ul>
<li><a href="https://www.liquibase.org/documentation/spring.html" target="_blank" rel="noopener noreferrer">Using Liquibase with Spring Boot</a>

</li>
</ul>
</li>
<li><a href="https://flywaydb.org/getstarted/" target="_blank" rel="noopener noreferrer">Flyway</a>


<ul>
<li><a href="https://flywaydb.org/documentation/plugins/springboot" target="_blank" rel="noopener noreferrer">Using Flyway with Spring Boot</a>

</li>
</ul>
</li>
</ul>
<h3 id="using-liquibase">Using Liquibase</h3>
<p>Note: I&rsquo;m using Spring Boot 2.2.6 with Gradle here, but you can do the equivalent with a dependency block in Maven.</p>
<p>Step One is adding your dependencies to <code>build.gradle</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-groovy" data-lang="groovy"><span class="line"><span class="cl">    <span class="c1">// Persistence
</span></span></span><span class="line"><span class="cl">    <span class="n">implementation</span><span class="o">(</span><span class="s1">&#39;com.h2database:h2&#39;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">implementation</span> <span class="s2">&#34;org.liquibase:liquibase-core&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">liquibaseRuntime</span> <span class="s2">&#34;org.liquibase:liquibase-core&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">liquibaseRuntime</span> <span class="n">sourceSets</span><span class="o">.</span><span class="na">main</span><span class="o">.</span><span class="na">compileClasspath</span>
</span></span><span class="line"><span class="cl">    <span class="n">liquibaseRuntime</span> <span class="s2">&#34;org.postgresql:postgresql&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">liquibaseRuntime</span> <span class="s2">&#34;com.h2database:h2&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">implementation</span><span class="o">(</span><span class="s1">&#39;org.postgresql:postgresql&#39;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">implementation</span><span class="o">(</span><span class="s1">&#39;org.springframework.boot:spring-boot-starter-jdbc&#39;</span><span class="o">)</span>
</span></span></code></pre></div><p>Next we need to make new folders in our project:</p>
<ul>
<li>In <code>src/main/resources</code> add a <code>liquibase</code> folder</li>
<li>Add two subfolders, <code>changelog</code> and <code>fake-data</code></li>
</ul>
<p>Add some minimal configuration to your <code>application.yml</code> file and you&rsquo;re good to go.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">  </span><span class="nt">liquibase</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">contexts</span><span class="p">:</span><span class="w"> </span><span class="l">dev, faker</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">change-log</span><span class="p">:</span><span class="w"> </span><span class="l">classpath:liquibase/master.xml</span><span class="w">
</span></span></span></code></pre></div><p>At this point, you should be able to start writing some XML.</p>
<p>You&rsquo;ll need a <code>master.xml</code> that describes your environment setup and the migrations that you want to run. Here&rsquo;s an example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="cp">&lt;?xml version=&#34;1.0&#34; encoding=&#34;utf-8&#34;?&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;databaseChangeLog</span>
</span></span><span class="line"><span class="cl">        <span class="na">xmlns:xsi=</span><span class="s">&#34;http://www.w3.org/2001/XMLSchema-instance&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">xmlns=</span><span class="s">&#34;http://www.liquibase.org/xml/ns/dbchangelog&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">xsi:schemaLocation=</span><span class="s">&#34;http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;now&#34;</span> <span class="na">value=</span><span class="s">&#34;now()&#34;</span> <span class="na">dbms=</span><span class="s">&#34;h2&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;now&#34;</span> <span class="na">value=</span><span class="s">&#34;current_timestamp&#34;</span> <span class="na">dbms=</span><span class="s">&#34;postgresql&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;floatType&#34;</span> <span class="na">value=</span><span class="s">&#34;float4&#34;</span> <span class="na">dbms=</span><span class="s">&#34;postgresql, h2&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;clobType&#34;</span> <span class="na">value=</span><span class="s">&#34;longvarchar&#34;</span> <span class="na">dbms=</span><span class="s">&#34;h2&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;clobType&#34;</span> <span class="na">value=</span><span class="s">&#34;clob&#34;</span> <span class="na">dbms=</span><span class="s">&#34;postgresql&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;uuidType&#34;</span> <span class="na">value=</span><span class="s">&#34;uuid&#34;</span> <span class="na">dbms=</span><span class="s">&#34;h2, postgresql&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;changeSet</span> <span class="na">id=</span><span class="s">&#34;00000000000000&#34;</span> <span class="na">author=</span><span class="s">&#34;alex&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;createSequence</span> <span class="na">sequenceName=</span><span class="s">&#34;sequence_generator&#34;</span> <span class="na">startValue=</span><span class="s">&#34;1050&#34;</span> <span class="na">incrementBy=</span><span class="s">&#34;1&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/changeSet&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;include</span> <span class="na">file=</span><span class="s">&#34;liquibase/changelog/20191024203226_added_entity_Company.xml&#34;</span> <span class="na">relativeToChangelogFile=</span><span class="s">&#34;false&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;include</span> <span class="na">file=</span><span class="s">&#34;liquibase/changelog/20191024203227_added_entity_Team.xml&#34;</span> <span class="na">relativeToChangelogFile=</span><span class="s">&#34;false&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;include</span> <span class="na">file=</span><span class="s">&#34;liquibase/changelog/20191024203234_added_entity_Project.xml&#34;</span> <span class="na">relativeToChangelogFile=</span><span class="s">&#34;false&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/databaseChangeLog&gt;</span>
</span></span></code></pre></div><p>It looks a bit busy but let&rsquo;s break it down. First, we need to tell Liquibase about our database setup, and what values it should use for various data types:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl">    <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;now&#34;</span> <span class="na">value=</span><span class="s">&#34;now()&#34;</span> <span class="na">dbms=</span><span class="s">&#34;h2&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;now&#34;</span> <span class="na">value=</span><span class="s">&#34;current_timestamp&#34;</span> <span class="na">dbms=</span><span class="s">&#34;postgresql&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;floatType&#34;</span> <span class="na">value=</span><span class="s">&#34;float4&#34;</span> <span class="na">dbms=</span><span class="s">&#34;postgresql, h2&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;clobType&#34;</span> <span class="na">value=</span><span class="s">&#34;longvarchar&#34;</span> <span class="na">dbms=</span><span class="s">&#34;h2&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;clobType&#34;</span> <span class="na">value=</span><span class="s">&#34;clob&#34;</span> <span class="na">dbms=</span><span class="s">&#34;postgresql&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;property</span> <span class="na">name=</span><span class="s">&#34;uuidType&#34;</span> <span class="na">value=</span><span class="s">&#34;uuid&#34;</span> <span class="na">dbms=</span><span class="s">&#34;h2, postgresql&#34;</span><span class="nt">/&gt;</span>
</span></span></code></pre></div><p>Next, we need to describe the changes we want Liquibase to make; in our case, we want Liquibase to create a sequence and run three migrations. Note that if you write migrations but don&rsquo;t include them in <code>master.xml</code>, they won&rsquo;t run.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl">    <span class="nt">&lt;changeSet</span> <span class="na">id=</span><span class="s">&#34;00000000000000&#34;</span> <span class="na">author=</span><span class="s">&#34;alex&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;createSequence</span> <span class="na">sequenceName=</span><span class="s">&#34;sequence_generator&#34;</span> <span class="na">startValue=</span><span class="s">&#34;1050&#34;</span> <span class="na">incrementBy=</span><span class="s">&#34;1&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/changeSet&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;include</span> <span class="na">file=</span><span class="s">&#34;liquibase/changelog/20191024203226_added_entity_Company.xml&#34;</span> <span class="na">relativeToChangelogFile=</span><span class="s">&#34;false&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;include</span> <span class="na">file=</span><span class="s">&#34;liquibase/changelog/20191024203227_added_entity_Team.xml&#34;</span> <span class="na">relativeToChangelogFile=</span><span class="s">&#34;false&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;include</span> <span class="na">file=</span><span class="s">&#34;liquibase/changelog/20191024203234_added_entity_Project.xml&#34;</span> <span class="na">relativeToChangelogFile=</span><span class="s">&#34;false&#34;</span><span class="nt">/&gt;</span>
</span></span></code></pre></div><p>Here&rsquo;s the company migration:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl"><span class="cp">&lt;?xml version=&#34;1.0&#34; encoding=&#34;utf-8&#34;?&gt;</span>
</span></span><span class="line"><span class="cl"><span class="nt">&lt;databaseChangeLog</span>
</span></span><span class="line"><span class="cl">        <span class="na">xmlns:xsi=</span><span class="s">&#34;http://www.w3.org/2001/XMLSchema-instance&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">xmlns=</span><span class="s">&#34;http://www.liquibase.org/xml/ns/dbchangelog&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">xsi:schemaLocation=</span><span class="s">&#34;http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;changeSet</span> <span class="na">id=</span><span class="s">&#34;20191024203226-1&#34;</span> <span class="na">author=</span><span class="s">&#34;alex&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;createTable</span> <span class="na">tableName=</span><span class="s">&#34;company&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">&#34;id&#34;</span> <span class="na">type=</span><span class="s">&#34;bigint&#34;</span> <span class="na">autoIncrement=</span><span class="s">&#34;${autoIncrement}&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&lt;constraints</span> <span class="na">primaryKey=</span><span class="s">&#34;true&#34;</span> <span class="na">nullable=</span><span class="s">&#34;false&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;/column&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">&#34;name&#34;</span> <span class="na">type=</span><span class="s">&#34;varchar(255)&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&lt;constraints</span> <span class="na">nullable=</span><span class="s">&#34;false&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;/column&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">&#34;web_page_address&#34;</span> <span class="na">type=</span><span class="s">&#34;varchar(255)&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&lt;constraints</span> <span class="na">nullable=</span><span class="s">&#34;true&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;/column&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">&#34;billing_contact_email_address&#34;</span> <span class="na">type=</span><span class="s">&#34;varchar(255)&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&lt;constraints</span> <span class="na">nullable=</span><span class="s">&#34;true&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;/column&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">&#34;primary_contact_email_address&#34;</span> <span class="na">type=</span><span class="s">&#34;varchar(255)&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&lt;constraints</span> <span class="na">nullable=</span><span class="s">&#34;true&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;/column&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;/createTable&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/changeSet&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;changeSet</span> <span class="na">id=</span><span class="s">&#34;20191024203226-1-data&#34;</span> <span class="na">author=</span><span class="s">&#34;alex&#34;</span> <span class="na">context=</span><span class="s">&#34;faker&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;loadData</span>
</span></span><span class="line"><span class="cl">                <span class="na">file=</span><span class="s">&#34;liquibase/fake-data/company.csv&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="na">separator=</span><span class="s">&#34;;&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="na">tableName=</span><span class="s">&#34;company&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">&#34;id&#34;</span> <span class="na">type=</span><span class="s">&#34;numeric&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">&#34;name&#34;</span> <span class="na">type=</span><span class="s">&#34;string&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">&#34;web_page_address&#34;</span> <span class="na">type=</span><span class="s">&#34;string&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">&#34;billing_contact_email_address&#34;</span> <span class="na">type=</span><span class="s">&#34;string&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">&#34;primary_contact_email_address&#34;</span> <span class="na">type=</span><span class="s">&#34;string&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;/loadData&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/changeSet&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/databaseChangeLog&gt;</span>
</span></span></code></pre></div><p>The first thing this migration creates is a table called Company. Since Liquibase uses XML as an abstraction on top of SQL, we don&rsquo;t need to worry too much about compatibility, Liquibase will look at the XML file and translate our request to the appropriate SQL dialect.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl">    <span class="nt">&lt;changeSet</span> <span class="na">id=</span><span class="s">&#34;20191024203226-1&#34;</span> <span class="na">author=</span><span class="s">&#34;alex&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;createTable</span> <span class="na">tableName=</span><span class="s">&#34;company&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">&#34;id&#34;</span> <span class="na">type=</span><span class="s">&#34;bigint&#34;</span> <span class="na">autoIncrement=</span><span class="s">&#34;${autoIncrement}&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&lt;constraints</span> <span class="na">primaryKey=</span><span class="s">&#34;true&#34;</span> <span class="na">nullable=</span><span class="s">&#34;false&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;/column&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">&#34;name&#34;</span> <span class="na">type=</span><span class="s">&#34;varchar(255)&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&lt;constraints</span> <span class="na">nullable=</span><span class="s">&#34;false&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;/column&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">&#34;web_page_address&#34;</span> <span class="na">type=</span><span class="s">&#34;varchar(255)&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&lt;constraints</span> <span class="na">nullable=</span><span class="s">&#34;true&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;/column&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">&#34;billing_contact_email_address&#34;</span> <span class="na">type=</span><span class="s">&#34;varchar(255)&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&lt;constraints</span> <span class="na">nullable=</span><span class="s">&#34;true&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;/column&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">&#34;primary_contact_email_address&#34;</span> <span class="na">type=</span><span class="s">&#34;varchar(255)&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">                <span class="nt">&lt;constraints</span> <span class="na">nullable=</span><span class="s">&#34;true&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;/column&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;/createTable&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/changeSet&gt;</span>
</span></span></code></pre></div><p>Having defined our table, we can take advantage of one of the neat functions Liquibase can give us - Liquibase will load data into our database from CSV:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-xml" data-lang="xml"><span class="line"><span class="cl">    <span class="nt">&lt;changeSet</span> <span class="na">id=</span><span class="s">&#34;20191024203226-1-data&#34;</span> <span class="na">author=</span><span class="s">&#34;alex&#34;</span> <span class="na">context=</span><span class="s">&#34;faker&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;loadData</span>
</span></span><span class="line"><span class="cl">                <span class="na">file=</span><span class="s">&#34;liquibase/fake-data/company.csv&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="na">separator=</span><span class="s">&#34;;&#34;</span>
</span></span><span class="line"><span class="cl">                <span class="na">tableName=</span><span class="s">&#34;company&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">&#34;id&#34;</span> <span class="na">type=</span><span class="s">&#34;numeric&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">&#34;name&#34;</span> <span class="na">type=</span><span class="s">&#34;string&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">&#34;web_page_address&#34;</span> <span class="na">type=</span><span class="s">&#34;string&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">&#34;billing_contact_email_address&#34;</span> <span class="na">type=</span><span class="s">&#34;string&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&lt;column</span> <span class="na">name=</span><span class="s">&#34;primary_contact_email_address&#34;</span> <span class="na">type=</span><span class="s">&#34;string&#34;</span><span class="nt">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&lt;/loadData&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&lt;/changeSet&gt;</span>
</span></span></code></pre></div><p>The loading of data is a changeset in its own right; this means you can flexibly load data when you create the table, or at a later stage. Loading data in this way can be useful if you&rsquo;ve got complex relationships to model. Another trick here is the <code>context=&quot;faker&quot;</code> statement above. If you want data to be loaded, make sure your <code>application.yml</code>&rsquo;s <code>liquibase</code> entry contains that tag. If you remove that tag, the data won&rsquo;t be loaded.</p>
<p>Here&rsquo;s the CSV referenced above with some sample data in it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csv" data-lang="csv"><span class="line"><span class="cl"><span class="s">id</span><span class="p">,</span><span class="s">name</span><span class="p">,</span><span class="s">web_page_address</span><span class="p">,</span><span class="s">billing_contact_email_address</span><span class="p">,</span><span class="s">primary_contact_email_address</span><span class="p">
</span></span></span><span class="line"><span class="cl"><span class="s">1</span><span class="p">,</span><span class="s">mobileFish</span><span class="p">,</span><span class="s">HomeLoanAccountTableComputer</span><span class="p">,</span><span class="s">Trace</span><span class="p">,</span><span class="s">SavingsAccountalarm</span><span class="p">
</span></span></span><span class="line"><span class="cl"><span class="s">2</span><span class="p">,</span><span class="s">TastyMetalBacon</span><span class="p">,</span><span class="s">redChipsSoap</span><span class="p">,</span><span class="s">HomeLoanAccount</span><span class="p">,</span><span class="s">web-readiness</span><span class="p">
</span></span></span></code></pre></div><p>That&rsquo;s it. When you run your spring boot application, during startup Liquibase will apply this migration, and when your app connects to the database, the tables and data will be ready immediately.</p>
<p>How does liquibase know the database credentials? I&rsquo;m not sure! It&rsquo;s magic. I assume it hooks into your spring database config, an example of which might be:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">datasource</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">com.zaxxer.hikari.HikariDataSource</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="l">jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">username</span><span class="p">:</span><span class="w"> </span><span class="l">sa</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">password</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">hikari</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">poolName</span><span class="p">:</span><span class="w"> </span><span class="l">Hikari</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">auto-commit</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span></code></pre></div><h3 id="using-flyway">Using Flyway</h3>
<p>Flyway is a little bit different. The focus on SQL tidies things up, in my opinion.</p>
<p>First, you add the relevant dependencies:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-groovy" data-lang="groovy"><span class="line"><span class="cl"><span class="c1">// Persistence
</span></span></span><span class="line"><span class="cl">    <span class="n">implementation</span><span class="o">(</span><span class="s1">&#39;com.h2database:h2&#39;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">implementation</span><span class="o">(</span><span class="s1">&#39;org.postgresql:postgresql&#39;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">implementation</span><span class="o">(</span><span class="s1">&#39;org.springframework.boot:spring-boot-starter-jdbc&#39;</span><span class="o">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">implementation</span> <span class="s2">&#34;org.flywaydb:flyway-core&#34;</span>
</span></span></code></pre></div><p>Tell Flyway how to connect to your database (in my case, via <code>application.yml</code>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="w">  </span><span class="nt">flyway</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">locations</span><span class="p">:</span><span class="w"> </span><span class="l">classpath:db/migration/dev</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="l">jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">user</span><span class="p">:</span><span class="w"> </span><span class="l">sa</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">password</span><span class="p">:</span><span class="w">
</span></span></span></code></pre></div><p>Make some folders, again in <code>src/main/resources</code>:</p>
<ul>
<li>Create <code>db/migration</code></li>
<li>In my case, I have H2 in dev and PostgreSQL in test and prod, so I have two sibling folders, <code>dev</code> and <code>prod</code> (test and prod scripts both live in prod as I want those to be the same, and they are the same SQL dialect).</li>
</ul>
<p>The next step is to write some migrations in the appropriate SQL dialect.
Where before our Company migration was in XML, now we&rsquo;re in SQL, note that there is a naming convention here that allows us to cut down on boiler-plate. You have to name files like so <code>V1.0__create_company.sql</code> where the VX.x will define the order in which migrations are run. Anyway, here&rsquo;s the file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">create</span><span class="w"> </span><span class="n">sequence</span><span class="w"> </span><span class="n">hibernate_sequence</span><span class="w"> </span><span class="k">start</span><span class="w"> </span><span class="k">with</span><span class="w"> </span><span class="mi">1050</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">create</span><span class="w"> </span><span class="k">table</span><span class="w"> </span><span class="n">company</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">id</span><span class="w">                            </span><span class="n">uuid</span><span class="w">         </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">name</span><span class="w">                          </span><span class="nb">varchar</span><span class="p">(</span><span class="mi">255</span><span class="p">)</span><span class="w"> </span><span class="k">not</span><span class="w"> </span><span class="k">null</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">web_page_address</span><span class="w">              </span><span class="nb">varchar</span><span class="p">(</span><span class="mi">255</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">billing_contact_email_address</span><span class="w"> </span><span class="nb">varchar</span><span class="p">(</span><span class="mi">255</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">primary_contact_email_address</span><span class="w"> </span><span class="nb">varchar</span><span class="p">(</span><span class="mi">255</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">constraint</span><span class="w"> </span><span class="n">pk_company</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">primary</span><span class="w"> </span><span class="k">key</span><span class="w"> </span><span class="p">(</span><span class="n">id</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><p>Dropping the XML leaves things a lot cleaner and readable, in my opinion.
We load data as well, but this is just a SQL script rather than CSV:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="k">PUBLIC</span><span class="p">.</span><span class="n">COMPANY</span><span class="w"> </span><span class="p">(</span><span class="n">ID</span><span class="p">,</span><span class="w"> </span><span class="n">NAME</span><span class="p">,</span><span class="w"> </span><span class="n">WEB_PAGE_ADDRESS</span><span class="p">,</span><span class="w"> </span><span class="n">BILLING_CONTACT_EMAIL_ADDRESS</span><span class="p">,</span><span class="w"> </span><span class="n">PRIMARY_CONTACT_EMAIL_ADDRESS</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="s1">&#39;1a689e52-f35b-4bda-934c-ea4f076bdc2c&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;Blue Fish Software Inc&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;bluefish.io&#39;</span><span class="p">,</span><span class="w"> </span><span class="s1">&#39;bills@bluefish.io&#39;</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="s1">&#39;hi@bluefish.io&#39;</span><span class="p">);</span><span class="w">
</span></span></span></code></pre></div><p>That&rsquo;s it. When the application runs, it&rsquo;ll load those SQL scripts and manage migrations and state for us. The disadvantage here is that we need two sets of scripts, one for H2 and one for PostgreSQL - this could be because my SQL is super weak and I can combine them but don&rsquo;t realise it.</p>
<h3 id="comparing-liquibase-and-flyway">Comparing Liquibase and Flyway</h3>
<p>I found Flyway to be a lot easier to use, despite having used Liquibase for a lot longer.</p>
<p>I like that Flyway is cleaner and less repetitive, and assuming I fix my dialect issue, it&rsquo;ll be a case of 1000+ fewer lines of code with Flyway than with the same setup in Liquibase. I also found the docs Flyway provides to be a lot better, and I encountered far fewer undocumented tricks needed with Flyway. If crucial information needed to get going is easier to find on Stack Overflow than on your docs pages, you&rsquo;ve done something wrong. Liquibase is bad for this.</p>
<p>Moving from <code>bigint</code> to <code>uuid</code> primary keys on all of my entities was impossible with Liquibase due to a bug around mis-detecting specific UUID values as strings.</p>
<p>Flyway had no such issues, and where I did make a mistake with Flyway, I got printed SQL exceptions rather than SQL exceptions that have abstracted by Liquibase. I encountered far less friction with Flyway.</p>
<p><a href="https://www.reddit.com/r/java/comments/g7fge5/i_wrote_about_flyway_and_liquibase_for_database/fohih2m" target="_blank" rel="noopener noreferrer">u/hooba_stank</a>

 via Reddit, makes the point that liquibase&rsquo;s profiles grant it excellent flexibility. Using contexts in different environments, composing profiles for different testing requirements and conditional change sets and rollbacks make the free Liquibase offering compelling. It&rsquo;s a great point.</p>
<p>Flyway&rsquo;s requirement for sequential ordering of SQL files can lead to sprawl if you prefer small changes per file. Encountering failures becuase you&rsquo;ve fat-fingered a SQL script version number is annoying too. <a href="https://www.reddit.com/r/java/comments/g7fge5/i_wrote_about_flyway_and_liquibase_for_database/fohkebl" target="_blank" rel="noopener noreferrer">u/koreth</a>

 adds that this problem can be avoided by configuring a pre-commit hook and CI check to detect conflicting sequence numbers so this issue can be automated to a degree, though ideally it&rsquo;s not something you have to deal with at all.</p>
<h3 id="wrap-up">Wrap Up</h3>
<p>Both Liquibase and Flyway are better than nothing by far.
My personal preference is Flyway. Flyway is cleaner to implement and easier to use on an ongoing basis and more readable. That being said, Liquibase is a powerful tool. Chosing either of these tools to use in your development will give you good gains in productivity.</p>
]]></content:encoded></item><item><title>Remaining Effective in a Pressure Cooker</title><link>https://4lex.nz/posts/pressure-cooker/</link><pubDate>Thu, 26 Mar 2020 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/pressure-cooker/</guid><description>&lt;p&gt;One thing the Army is good at is teaching you how to function under immense pressure, for extended periods. Speaking briefly about my credentials in this area, I&amp;rsquo;ve spent just under five years in the Army, nine months of which were in Afghanistan as part of a NATO mission with the New Zealand Defence Force. As with everything, there are folk out there with more experience than me, who have done it harder, for longer. I still feel it&amp;rsquo;s worth sharing this experience as perhaps the way I do that resonates with you, as the way others share their experience resonates with others.&lt;/p&gt;</description><content:encoded><![CDATA[<p>One thing the Army is good at is teaching you how to function under immense pressure, for extended periods. Speaking briefly about my credentials in this area, I&rsquo;ve spent just under five years in the Army, nine months of which were in Afghanistan as part of a NATO mission with the New Zealand Defence Force. As with everything, there are folk out there with more experience than me, who have done it harder, for longer. I still feel it&rsquo;s worth sharing this experience as perhaps the way I do that resonates with you, as the way others share their experience resonates with others.</p>
<p>There are several things we need to understand about ourselves if we are to remain functional while under pressure. We need to understand what our baseline stress profile. We need to learn about how to work on our stress levels over time. We must deal with spikes.</p>
<p>I believe that Covid-19 is a long-lived global pandemic, that will shift the way the world works. Now, or over the next 12 months, our society will have to make changes to the way we live, together. If we can change, we may endure, after we endure, we might be able to start living again. We are not in a sprint right now. We&rsquo;re in a marathon. Consider the pandemic an endurance sport.</p>
<p>With the recent announcement by the Government of Alert Level 4, it&rsquo;s time to confront uncomfortable truths. Fortunately, doing so is the first step we can all take to adjust to this new reality. Confronting uncomfortable truths is the first step I&rsquo;d like you to consider.</p>
<p><img alt="This is fine - Alex Villegas, Twitter" loading="lazy" src="/img/in-post/this-is-fine-alex-villegas.jpeg">
<em>Credit: <a href="https://twitter.com/llegaspacheco/status/1111783777372639232" target="_blank" rel="noopener noreferrer">Allex Villegas, Twitter</a>

</em></p>
<h1 id="take-stock-of-how-you-are-right-now">Take stock of how you are, right now.</h1>
<p>How are you feeling? Do you feel loose, calm or limber? Do you think you might evaporate into thin air any second, or perhaps you feel like everything around you is vibrating or spinning?</p>
<p>The very first step here is to figure out how you are responding both mentally and physically. Hopefully, you know what your body feels like when you&rsquo;re feeling nervous, it might be trickier to figure out your mental state. Asking your mind to observe itself, is like trying to look rocks at the bottom of a pond without disturbing the surface.</p>
<p>If you&rsquo;re not sure how you feel (or you feel busy but can&rsquo;t quite narrow down why), you might find it useful to do a body scan. A body scan is a valuable mindfulness technique to begin to understand how you feel.</p>
<p>It&rsquo;s important that during a body scan, you don&rsquo;t judge yourself. Don&rsquo;t beat yourself up at how you feel. The way you feel, is. The way you feel must exist free of any value judgement - our goal here isn&rsquo;t to fix or solve anything. Our goal isn&rsquo;t to make you feel better - yet. With a body scan, all we want to do is gain awareness of ourselves, in a way that we might not normally do.</p>
<p>There are several apps out there that can help you practice your body scan. I&rsquo;ve had good results with both Calm and Headspace, and I recommend them both to you.</p>
<p>Often after a long day in Afghanistan, I would find myself exhausted despite having not done anything of note. My shoulders would feel like slabs of iron, and my back would feel brittle and inflexible. I didn&rsquo;t know why. I slowly learned that the more time I spent trying to sense how individual parts of my body were feeling, the easier it was to find ways to accept how I felt. Allowing that I felt a certain way let me choose where to intervene, or where not to.</p>
<p>The effect this had, is that it took me from a place where I felt like things were happening to me that I couldn&rsquo;t control, to a place where I could step back and examine what was happening, make sense of it and respond. I was able to reassert some control over my situation.</p>
<p>Regaining control in this way led me to a core concept that you may have heard before: You cannot always control your environment, but you can control how you respond.</p>
<h1 id="confront-uncomfortable-truths">Confront uncomfortable truths</h1>
<p>Have you heard the phrase &ldquo;Disagree and commit&rdquo; before? Spoken initially by Scott McNealy of Sun Microsystems, Jeff Bezos popularised them as a core leadership principle of Amazon.
You may have used these words before when talking about a decision at work. In this way, &ldquo;Disagree and commit&rdquo; can shape your reality at work.</p>
<p>They can also shape your reality outside of work.</p>
<p>The Stockdale Paradox states:</p>
<blockquote>
<p>Retain absolute faith that you can and will prevail in the end, regardless of the difficulties AND at the same time confront the most brutal facts or your current reality, whatever they might be.</p>
</blockquote>
<p>This concept was vital for me getting through the pressures of being deployed to a war zone. It can help again by giving us a life raft to hold on to during a global pandemic the likes of which we haven&rsquo;t seen for a hundred years and more.</p>
<p>Practice with me now. Take these facts, understand them, and reflect on them. Take another body scan and reflect on how they make you feel. Be kind to yourself and know that this is a normal reaction.</p>
<p>This pandemic is global and will be with us for longer than our self-isolation. For the next year if not longer, you, I, our loved ones and people we&rsquo;ve never met equally will be at risk of contracting, and being killed by this virus. Our careers, our families and our ways of life are at risk. It&rsquo;s going to get worse before it gets better.</p>
<p>My two-year-old son is currently immunocompromised, and so I feel this risk very acutely. With practice, we can accept these facts and let them flow over us like water, and none of us is helpless.</p>
<p>I believe, and the goal here is for you also to believe in the core of your being, that we will get through this. We will endure. Of that, there is no question - as we have done before, so to will we now. We must have unwavering faith that we will prevail in the end. Having accepted entirely, the brutal facts of our reality, and armed with the conviction that we will prevail, positive change can begin.</p>
<h1 id="understand-what-you-can-control">Understand what you can control</h1>
<p>Confronting the brutal truths of your new normal is the first step in reasserting control over your life. Regardless of what your chosen list is, make sure you have one. If your limbs feel like lead getting out of bed in the morning right now, it can feel gratuitous - why focus more on the things that make me feel this way?</p>
<p>Avoiding the truth will make you feel good in the short term, but only the short-term. It&rsquo;s no way to live in anything other than a sprint, and as I said earlier, this is a marathon. If you need to avoid the truth to get through the short term, be kind to yourself, accept it and move on - but make a plan for how you&rsquo;re going to eventually accept your situation in the same way you accept that your behaviour is what you need to do right now.</p>
<p>You will have worries; you&rsquo;ll have confronted the world we share and the events happening around you. It can feel overwhelming, and you&rsquo;re allowed to feel overwhelmed.</p>
<p>To accept what you can and can&rsquo;t control, you need to be able to think about them all separately. Overseas, I struggled with being removed from my family. I struggled initially with the threat of death or injury lurking around every corner. I didn&rsquo;t know the language and relied on others for communication that was vital to doing my job. I found myself bouncing around from small job to small job because it was challenging to think about the big stuff.</p>
<p>I adjusted. I had a scrap of paper in my barrack room that contained all of my worries. Writing them down took away the exoticness of them, but more importantly, it helped me to untangle them all. Rather than walking around as one big ball of stress, I could compartmentalise, and store little bits of well-identified stress equally across my body and mind. Once I figured this out, the process of dealing with it was much more manageable.</p>
<p>Accept what you cannot control
What if someone buries a bomb in the road and my wagon drives over it and we all die?</p>
<p>For a soldier doing peacekeeping operations in Afghanistan, this was a genuine threat. It has happened before, to people you know, within the last six months. Nothing about this threat is remote or mysterious. If this happens, likely, you won&rsquo;t even know about it. You&rsquo;ll just be dead with a snap of your fingers, like that. Done.</p>
<p>How much thought should I have given the possibility I&rsquo;d be killed by a roadside bomb the next time I left base? As it turns out, the correct answer: None at all. Let me explain why.</p>
<p>We had tactics, training, procedures - the Army spends a lot of time thinking about how to minimise this threat, and we have things that we do consciously and with enough training that it becomes subconscious, to mitigate the danger. Mitigate is the best case here. To altogether avoid the threat would require me not to do my job.</p>
<p>After you&rsquo;ve done everything you can to manage your situation, what more can you do? Nothing. Accept that. What a beautiful thing! You&rsquo;ve maxed out! You are now free to live your life!</p>
<p>Paraphrasing a good friend of mine called Epictetus (Discourses, 2.5.4–5):</p>
<blockquote>
<p>&ldquo;The chief task in life is simply this: to identify and separate matters so that I can say clearly to myself which are externals not under my control, and which have to do with the choices I actually control. Where then do I look for good and evil? Not to uncontrollable externals, but within myself to the choices that are my own . . .&rdquo;</p>
</blockquote>
<p>In other words, if you have made all of the choices available to you, if you have prepared as best you can, if you have reflected on what you can and can&rsquo;t control, why should you focus on any choice other then the ones you own?</p>
<p>Acknowledging that your resources are finite and that you have done the best you can with them, you rob the power the uncontrollable has over you. You transform from &ldquo;I don&rsquo;t know!&rdquo; to something as simple as &ldquo;We shall see.&rdquo;</p>
<p>Go through your mental list of worries and commit them to paper if that&rsquo;s your style. Recently, I&rsquo;ve tended to group and catalogue mine, and then speak them aloud, I talk about how they make me feel, what I can do about them and what I can&rsquo;t do about them. In this way, I make them familiar, and I take away the power they have to shut me down. I take them from a place of power to a place of internal observation.</p>
<h1 id="create-your-new-normal">Create your new normal</h1>
<p><img alt="So much time for activities image" loading="lazy" src="/img/in-post/so-much-time-for-activities.jpeg"></p>
<p>By now, our old routines are gone. No more weekly sit-downs for Dungeons and Dragons, we have to go remote. No more dinner and drinks out. Social Fridays at the Pool and Bar are gone. While some folk continue to work in essential businesses, many will be out of work.</p>
<p>The old rules and old routines don&rsquo;t apply now - take this as an opportunity to reforge your days in a way that works for you. I&rsquo;m working from home. The creeping tyranny of meetings that could have been emails are turning into emails. I suddenly have time in my day.</p>
<p>Think about how you operate best. Are you a morning person? A night owl? Schedule yourself some time to exercise every day - staying active is essential, and so is getting fresh air. Stay in your neighbourhood, but do go for a jog. Schedule in some time for doing what you enjoy, schedule time for improving yourself. If you are able, consider scheduling in some time to help others - that may look like assisting a flatmate looking for work, it may be volunteering your time on the phone or making things to help people in the fight against COVID-19. If in doubt, apply this principle:</p>
<p>Spend time on yourself in the now, spend time on the future you, and give time to others; however you are able.</p>
<p>Importantly, build in time to check in with yourself. We started with body scans, why not create some time in now to keep up the practice? One of the best investments I&rsquo;ve ever made in myself has been the scheduling in of mindfulness practice for a couple of hours a week. Other philosophies might speak to you more, but I&rsquo;ve significantly improved my wellbeing by integrating regular mindfulness practices and adopting a philosophy of stoicism.</p>
<h1 id="track-your-progress">Track your progress</h1>
<p>Allocate time (perhaps before or after your mindfulness practice) to have a check-in. Your body scans are an excellent place to start, and there are more practices you might build in then I can mention here (the internet is your friend!). Start with focusing on how you or your body feel now compared to last week. Perhaps spend some time trying to anticipate future stresses before they come along. Catalogue or internalise the things you can and can&rsquo;t control, or otherwise seek to get ahead of things. Try to position yourself so that when the next wave breaks, you&rsquo;re a little more prepared for it than you were last time. Alternatively, spend that time creating a place to retreat, a place where you can simply endure, and get back up again.</p>
<p>In Afghanistan, that place was the mess hall at Combat Outpost Romero. After a long patrol full of tension, knowing that I could come back after cleaning my kit, and sit down with a cup of tea and a hot meal and spend some time with friends off the clock made the difference between coping and thriving.</p>
<p>With your regular check-ins, you should be on your way to coming to terms with external stress. If you&rsquo;re not, that&rsquo;s okay. Remember that it takes time, and everyone is on their own journey. Be kind to yourself and others. If you don&rsquo;t make immediate progress, or you do make progress and later lose that progress, give yourself some grace. Adjusting is complicated and is never truly over. What matters is that you&rsquo;re making progress!</p>
<p>In the words of another influential author, Dr Seuss (Oh the Places You&rsquo;ll Go!):</p>
<blockquote>
<p>&ldquo;I&rsquo;m sorry to say so<br>
but, sadly, it&rsquo;s true<br>
that Bang-ups<br>
and Hang-ups<br>
can happen to you.&rdquo;</p>
</blockquote>
<blockquote>
<p>&ldquo;But on you will go<br>
though the weather be foul.<br>
On you will go<br>
though your enemies prowl.<br>
On you will go<br>
though the Hakken-Kraks howl.<br>
Onward up many<br>
a frightening creek,<br>
though your arms may get sore<br>
and your sneakers may leak.&rdquo;</p>
</blockquote>
<h1 id="stay-connected---but-consider-avoiding-social-media">Stay connected - but consider avoiding social media</h1>
<p>More than ever, it&rsquo;s essential to be part of a community. Humans are social creatures. During stressful times we band together. Some of the tightest tribes and units I have seen have been built through shared adversity. I&rsquo;m well aware that the literature out there says that teams and bonds form through positive reinforcement, but I cannot reconcile that with my own experience.</p>
<p>The tightest, most trusting, highest quality teams I have ever had the honour of being a part of, have been formed as a group of people were put together, and repeatedly flung into the shit. Be that on the sands of Afghanistan or after one dogshit, irredeemable project after another. The most potent teams I&rsquo;ve observed have become like family in the positive sense of the word, not the exploitative &ldquo;work until midnight because the company is your family&rdquo; spirit of the word.</p>
<p>Connect with video calls, voice calls or text-based messages. Maybe it&rsquo;s a long-running game of scrabble on your phone; perhaps it&rsquo;s a Minecraft server you&rsquo;ve set up for the crew. Find a way of staying connected remotely, with those you care for. Even more than usual, make an extra effort to keep in touch with those who might be more isolated than you. The oldies who are still trying to work their new iPad or the neighbour kids whose parents are working 16 hour days at the local supermarket - connecting with others will help your wellbeing and theirs.</p>
<p>Scrolling through Twitter for 4 hours a day will likely make you feel worse. Sifting through your Facebook feed for that one image a friend sent you might not be worth it. I have long-standing issues with facebook&rsquo;s privacy practices, and have disabled my profile (and even now I encourage you to do the same) - but Messenger is a good way of keeping in touch even without a profile.</p>
<p>There is evidence from studies that say at the best of times; social media can harm our wellbeing. These are not the best of times. I&rsquo;m avoiding social media as much as I can, and where I can&rsquo;t, I&rsquo;m changing my habits to reduce the time I spend on social media. I strongly recommend you do the same.</p>
<h1 id="dealing-with-pressure-spikes">Dealing with pressure spikes</h1>
<p>Themes have emerged throughout this article. Some of them have been deliberate actions we can take on an event occurring; others have been more general strategies or ideas to review. Here&rsquo;s a summary of the tactics you can use to help manage your stress levels when the pressure is high:</p>
<ol>
<li>Assess how you&rsquo;re feeling - are you in a good place to face the wave as it comes in or a not-so-good place? Don&rsquo;t forget to observe the body as well as the mind, as best you can. With practice, you&rsquo;ll be able to take stock of how you are feeling, and anticipate how that might change as the wave approaches, breaks and recedes.</li>
<li>Examine the stress as you see it approach, and begin to accept that there will be some aspects of it you can influence, and some you can&rsquo;t. You&rsquo;re not a dummy for being unable to control some things, that&rsquo;s life! Realising that you can only do your best will help you begin to regain control.</li>
<li>Look at your environment, and if you have time, make changes so that when the next stressor arrives, you have choices. If you feel you can withstand it, confront and mitigate it as soon as you can. If you&rsquo;re feeling vulnerable, or you&rsquo;re not sure you can tackle the stress head-on, find the place where you can endure, this too will pass.</li>
</ol>
<p>In creating your new routine, you will have created a new you, a more resilient you. I&rsquo;m hopeful that if you&rsquo;ve got to this point, not only will you have adapted to COVID-19, you will have adapted to stress in general. You&rsquo;ll have new tools you can use to remain active. You&rsquo;ll have novel ways of integrating stress into your life while also allowing it to drain away, receding like the high tide. I believe that practising mindfulness, adopting a more stoic outlook on life, and being kind to yourself and others will improve the quality of your life, now and for the future.</p>
<p>Above all else, as Prime Minister Ardern says, be calm, be kind, look after one another.</p>
<p><img alt="Jacinda Ardern image" loading="lazy" src="/img/in-post/be-kind.jpeg"></p>
]]></content:encoded></item><item><title>When everything is digital, nothing is</title><link>https://4lex.nz/posts/when-everything-is-digital-then-nothing-is/</link><pubDate>Fri, 30 Nov 2018 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/when-everything-is-digital-then-nothing-is/</guid><description>&lt;h2 id="digital-transformation-digital-experience-digital-thought-leader-digital-coffee-table-digital-devops-team-digital-dystopia"&gt;Digital Transformation, Digital Experience, Digital Thought Leader, Digital Coffee Table, Digital DevOps Team, Digital Dystopia&lt;/h2&gt;
&lt;p&gt;Some of these things are not like the others.&lt;/p&gt;
&lt;p&gt;Have you heard the phrase &amp;lsquo;digital&amp;rsquo; so many times it loses all meaning? Try saying it a hundred times and see if the word still sticks in your brain. If you&amp;rsquo;re like me, it&amp;rsquo;ll slide right away like a fried egg off a bald head.&lt;/p&gt;
&lt;p&gt;Now imagine baldy over there is your company. It&amp;rsquo;s risky for a thing to be so pervasive that one cannot escape it, or mentions of it (or articles about it - no the irony is not lost).&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="digital-transformation-digital-experience-digital-thought-leader-digital-coffee-table-digital-devops-team-digital-dystopia">Digital Transformation, Digital Experience, Digital Thought Leader, Digital Coffee Table, Digital DevOps Team, Digital Dystopia</h2>
<p>Some of these things are not like the others.</p>
<p>Have you heard the phrase &lsquo;digital&rsquo; so many times it loses all meaning? Try saying it a hundred times and see if the word still sticks in your brain. If you&rsquo;re like me, it&rsquo;ll slide right away like a fried egg off a bald head.</p>
<p>Now imagine baldy over there is your company. It&rsquo;s risky for a thing to be so pervasive that one cannot escape it, or mentions of it (or articles about it - no the irony is not lost).</p>
<p>The problem is that there is so much work out there, and so many things that could do with a digital transformation (my thoughts on that phrase <a href="https://www.linkedin.com/pulse/why-digital-transformation-should-dead-alex-corkin/">here</a>), it can be challenging to find a place to start.</p>
<p>As Laozi stated, a journey of a thousand miles begins with a single step. How do we identify that first step? More importantly, how do we ensure that the thing we&rsquo;ve identified is in fact, a first step, and not the first mile, the tenth mile or the hundredth mile?</p>
<p>Mark Rosenberg commented on a previous article. He stated that based on his experience, while you can educate the entire org on what becoming digital means, it&rsquo;s often a challenge to digitally transform an extensive portfolio. Mark went on to explain that the most important thing to get right when considering what to paint with the digital brush, is defining the outcomes the business wants to see and then aligning use cases.</p>
<p>These comments paint the picture of a large organization looking at change, as being best served by starting small. By starting with products in verticals relevant to a business&rsquo;s chosen outcomes, and the teams that look after those products, you provide people with a tangible start and endpoint, clear boundaries, and the most important thing of all;</p>
<h2 id="psychological-safety">Psychological Safety.</h2>
<p>No betting the company, no gambling the product, no guessing the market.</p>
<p>Starting without psychological safety can be ruinous for your efforts to change. I&rsquo;ve previously experienced executives lock up and be unable to make decisions. I&rsquo;ve seen relationships between colleagues ruined because what is evident to one incites a fear-based response in another. I&rsquo;ve seen trusted advisors turn in to detractors that actively try to undermine the effort to change - because they don&rsquo;t feel safe! For a staff member reliant on the company for their living, the prospect of profound upheaval starts ringing alarm bells, and they aren&rsquo;t bells that can be un-rung easily.</p>
<p>When you come to make the change, be that making digital your default proposition (what does that even mean?) or just applying a digital lens to the way you currently work, you enable staff to participate fully. You empower them to work without worrying about the blowback of a mistake made while learning.</p>
<p>Starting small in this way can also be kinder on your customers. Rather than presenting as a different company overnight, with diverse offerings or a different business model, we can offer a more compassionate, thoughtful approach.</p>
<p>Recall when the company that makes your favourite product &lsquo;went digital&rsquo; - they turned off the call centre and switched to an online, email-only support model because it&rsquo;s digital and it&rsquo;s cheaper. I&rsquo;m not sure what was more irritating, waiting on hold for half an hour or waiting three days for a job to be done that would usually have taken a five-minute conversation with a human. If you focus your change on the wrong things or are in it for the wrong reasons, your customers will suffer.</p>
<p>Thinking more about selecting that first step, though, it feels like you could approach the question from multiple angles. In heeding Mark&rsquo;s advice from earlier, you need to ensure your company has set those outcomes for you to draw on. Assuming you&rsquo;ve done that, you can take those outcomes and ask yourselves the following:</p>
<h2 id="a-choice-appears">A Choice appears:</h2>
<blockquote>
<p>Of these outcomes, which one is the most valuable to our customers?</p>
</blockquote>
<p>Or</p>
<blockquote>
<p>Of these outcomes, which is the easiest for us to achieve as a first step?</p>
</blockquote>
<h2 id="whats-the-risk">What&rsquo;s the risk?</h2>
<p>Your answer will likely depend on your risk appetite. Personally, working to deliver the most value to customers feels like the best approach. Should you achieve that outcome, you have the best chance of your customer base, rewarding you via the bottom line. On the other hand, should this outcome not be achieved or should you not provide the right solution, you&rsquo;re also much more exposed for the same reason.</p>
<p>Thinking back to when I had to make this choice, my focus and customers weren&rsquo;t the primary revenue stream for the business. For me, in my role, &lsquo;most valuable for Alex&rsquo; was not &lsquo;most valuable for the business&rsquo;. It was a weird time on reflection. I could take on a risk profile that was a bit more aggressive than might fit your context. While it was the most valuable thing for my area, it was a natural step for the business to take.</p>
<p>Where risk is a more significant concern (or perhaps when you only have one essential product that drives company revenue), the safer option is usually to tackle a smaller option. I won&rsquo;t call them low hanging fruit because, in my experience, low hanging fruit tend not to exist. They are tricky problems masked by a lack of understanding or data.
Instead of creating new teams or new ways of working, or creating new products that need love and investment, begin by conducting user research with a focus on that small problem. Please share what you learn and then act on it.</p>
<p>Early on, it&rsquo;s crucial to share what you&rsquo;re learning with the business and your stakeholders. Once you&rsquo;ve shown the knowledge you&rsquo;ve gained, deliver a change to address that knowledge. If you can show that you&rsquo;re learning from stakeholders, learning from customers and then applying that to your product, you build trust with all of those parties. Importantly, you create trust in a way that is difficult to argue. The data tells all.</p>
<p>I chose to look at a particular feature that wasn&rsquo;t working well in a product. We asked some customers and non-customers to come in for interviews, ended up with a bunch of feedback that we then took to the business. We had to challenge some folk as the feature had been designed initially based on gut feel. Engaging them as equals from a position of psychological safety meant we had a fruitful and robust conversation and were able to produce and deliver the product increment.</p>
<h2 id="snowballs-get-bigger-as-they-roll">Snowballs get bigger as they roll</h2>
<p>In the same way that you might snowball your debt repayments, snowball how you deliver software. By demonstrating you can create with a customer-focused approach, and by tying that to the results the business might see, it becomes a lot easier to justify more substantial changes. At the same time, you are building confidence that you can deliver that more substantial change.</p>
<p>None of this requires Agile, Scrum, Kanban or whatever else. It just requires talking to stakeholders, talking to customers and responding to what you learn. If you can design this feedback loop to be short, and become more responsive to the market rather than driven by a project plan, you&rsquo;ll be in the right place regardless of the chosen framework you want to use.</p>
<p>To me, this is pure business agility. There&rsquo;s no rocket science here, but it does require the things you started with, back when the business was young. It requires humility, a willingness to change and grow, and a willingness to get stuck into some problems that may be hard, ambiguous, poorly defined or a combination of those things. Big businesses can lose that mindset as they become more process and governance driven (read: risk-averse) with growth.</p>
<p>I mention business agility because there is an implied link between business agility and speed or pace, and pace is something that a successful digital initiative can give your business.</p>
<p>I believe that the hallmark of a digital business is pace. Not technology or software or design. Pace in all things. Digital transformations are lucrative because of this. Operational and organizational tempo gives corporations the ability to turn on a dime and respond to market forces, legislative changes like GDPR, crises or opportunities. Digital is eating the world. It will keep coming up again and again.</p>
<p>Snowballing improvements to your product or chosen outcomes demonstrate pace. We start small or start with one thing. To start big is too risky for the business. It threatens too many people needlessly. It works against the goals of the transformation.</p>
<h2 id="pace">Pace</h2>
<p>We&rsquo;re not doing digital for digital&rsquo;s sake; it&rsquo;s pace we want. Replacing a paper form with an electronic one doesn&rsquo;t automatically please your customer. Responding to customers in minutes, rather than days certainly does though. Amazon is still selling books. The difference is that now you can access a wide variety of literature online (more then your local bookstore would have in stock), order online and reasonably expect your order to arrive in a day or two. The days of having to wait for a week are gone. That&rsquo;s pace. Digital, and the technology behind digital efforts, are simply an enabler. I&rsquo;m sure that nobody at Amazon will ever regret investing in getting books to customers faster. Likewise, it&rsquo;s unlikely a business that has tasted pace would regret it, even given the cost.</p>
<p>Getting back to our research, and the action we&rsquo;ve taken; start small, get the knowledge and deliver that to a user. Even if it&rsquo;s a limited section of your customers, that&rsquo;s still good. If you&rsquo;ve done it once, do it again. Rinse and repeat. As you become more familiar with the process of learning, building and showing, this feedback loop begins to feel natural. It should start to feel better than the way you used to work. It becomes the default to ask customers about their problems and views on your product before starting a bit of work. These lessons begin to show in other parts of the organisation. The organisation begins to think more about pace and more about how they might use technology to become faster. You encounter a turning point. The default position becomes to use digital methods, channels (and the technology behind them) to solve customer problems.</p>
<p>Your digital transformation has begun.</p>
<p>In reality, it&rsquo;s never as simple as this. Fortunately, by keeping your cycle time low and your work chunked as small as possible, we reduce the chance of significant problems, and hopefully, we reduce the damage any mishap might cause.</p>
<p>Some points to take away:</p>
<ol>
<li>Your context and the outcomes you want must guide your efforts.</li>
<li>Keep the psychological safety of your colleagues in mind.</li>
<li>Make sure that the work you do aligns with the broader business.</li>
<li>Focus on the smallest thing, build trust and then tackle more significant problems.</li>
<li>Build with customers, not just with customers in mind.</li>
</ol>
]]></content:encoded></item><item><title>Why digital transformations should be dead</title><link>https://4lex.nz/posts/becoming-a-digitally-integrated-business/</link><pubDate>Fri, 30 Nov 2018 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/becoming-a-digitally-integrated-business/</guid><description>&lt;h2 id="digital-transformations-dont-work"&gt;Digital transformations don&amp;rsquo;t work.&lt;/h2&gt;
&lt;p&gt;Everyone wants to talk about digital-led businesses. Every business is a software business, even when they aren&amp;rsquo;t, people will tell you. But the focus on big bang, project managed, change agent-driven initiatives tend to leave sour tastes in the mouths of long-standing staff. Naive technologists or agilists or product people for that matter exacerbate this when they perform the business strategy equivalent of the &amp;lsquo;seagull&amp;rsquo;. Discontent frequently morphs into outright opposition, and emotion becomes the dominant decision-making force rather than observation and planning. Eventually, what good will the agent(s) had is exhausted, and they leave the business. Sometimes voluntarily, sometimes less so.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="digital-transformations-dont-work">Digital transformations don&rsquo;t work.</h2>
<p>Everyone wants to talk about digital-led businesses. Every business is a software business, even when they aren&rsquo;t, people will tell you. But the focus on big bang, project managed, change agent-driven initiatives tend to leave sour tastes in the mouths of long-standing staff. Naive technologists or agilists or product people for that matter exacerbate this when they perform the business strategy equivalent of the &lsquo;seagull&rsquo;. Discontent frequently morphs into outright opposition, and emotion becomes the dominant decision-making force rather than observation and planning. Eventually, what good will the agent(s) had is exhausted, and they leave the business. Sometimes voluntarily, sometimes less so.</p>
<p>Thus ends the digital transformation — a practical exercise in how to win friends and influence people, speaking from experience.</p>
<p>The end goal was a digitally-led business, with a focus on experiences and the products that power them. The more I learn from others in the industry, the more I feel like this is still the right outcome. Is there a business out there today that doesn&rsquo;t want the best for their customers? Is there a business that doesn&rsquo;t want to shift their customers from high-cost channels to lower-cost channels, while at the same time improving the experience those customers have? Is there a business that doesn&rsquo;t want to maximise the lifetime value of a customer in a way that is engaging and delightful? Even companies that might not have the words to describe this nirvana surely want that for their customers and their bottom lines regardless?</p>
<h2 id="the-core-assumption">The core assumption.</h2>
<p>The core assumption I&rsquo;m making is this: Businesses want - and have always wanted - highly engaged, happy customers who pay more money for the goods and services that business provides, while at the same time reducing costs as much as they can.</p>
<p>The thing that differs then, is not the goal, but how we get there as a group. The big question businesses have then is not the what, but how. Is it a shift in the customer segment or vertical? Is it the pursuit of new markets in new ways while maintaining old markets and old styles? Or is it a digital transformation? How do you decide when it could be any of these, at any time and in any order?</p>
<p>These are the questions that every digital, agile, product, devops, rockstar ninja consultancy on the planet have solved they will say, breathlessly in the white paper you paid for with your email address. Some of the claims might even be true. I don&rsquo;t believe in any of them. The majority of the movements I see in industry pros look to digital transformation as the next step for any business. The approach: Hire product owners and managers, or shift the ones you have out of the marketing department. Let them be the agents of change as surely they will rally the business around them in pursuit of the customer! Customer Obsession will carry the day! Last time someone took that approach with me, I was made redundant. From what I&rsquo;ve seen such things end one of two ways. Either you force out the existing staff that built the business, or the agents of change don&rsquo;t get the support or cut through they need, and they move on. Losing momentum is the most significant risk with a transformation. By definition, it has a beginning, middle and end. A transformation implies a shifting of the status quo, techies, product people, design and experience folk are hired into the business with a mandate to change the way the company has worked up until that point - because if you don&rsquo;t change you don&rsquo;t transform. Those newbies to the business have new ways of working, and the implication is that those new ways are inherently better or more suitable for the company than the old ways that we built the business on in the first place!</p>
<p>This attitude implies that the newbies are inherently better or more suited than the existing staff that built the company and a collision point arrives. These groups clash until one group wins, and one group loses. I haven&rsquo;t seen a clash like that de-escalated successfully.</p>
<p>Why then is there such a focus on a transformation? Why are we doing to entire businesses what we now try to avoid doing with the software that we tell companies they need to be thinking about first? As an industry, we&rsquo;ve been attempting to move away from big bang releases on a Friday afternoon. Why do we treat whole companies the same way now? If we can figure out how to de-risk deploying and releasing software, surely we can focus on doing something similar for businesses that want to participate in a digital-first world.</p>
<p>Think about integration, rather than transformation. Rather than seeking to supplant, subvert or replace, we should be looking to merge, join and integrate.</p>
<h2 id="in-tech-we-try-to-remove-silos-why-stop-there">In tech we try to remove silos. Why stop there?</h2>
<p>How many times have you seen a technology team working siloed, away from the rest of the business? The predominant practice used to be waterfall, where the teams hide down on another floor for the entire duration of the project, only coming back to show the business what they&rsquo;ve done at the very end of the budgeted time. Agile and Scrum have advanced that, now we talk about what we&rsquo;ve been building with our stakeholders every few weeks (two weeks being most common). That&rsquo;s a great achievement compared with waterfall.</p>
<p>With this way of working being a next step on the journey, what comes after that? Once you&rsquo;ve got mature teams experienced with this way of working, it can happen that the product owners, design team and other team members are doing regular customer research sessions. Staff process that data into insights and product improvements. Next, they build that feedback into their digital products. The new approach works up to a point.</p>
<p>Eventually, it becomes difficult to improve some aspects of the product because the tech team might only own the digital representation of that product. Take a bank for an example close to heart. Suppose you get some feedback in a user session that the fees charged on a product make it unattractive to the user. Your tech team is incentivised to fix problems with the digital product. Still, now you&rsquo;re finding issues with the traditional representation of that product - you can take that feedback back to the bank, but you&rsquo;ve just hit another silo.</p>
<p>We&rsquo;ve come so far in building cross-functional technical teams and cross-functional software product teams. The next logical increment is the cross-functional business team. When you think about the time before &lsquo;digital&rsquo; existed as a thing people wrote white papers about; you&rsquo;re just describing a regular team as we&rsquo;ve seen for most of the history of business.</p>
<p>You might find that this is not a case of digital eating the world, or technology changing the way we do things. Such statements are arrogant and misguided. The way we position the concept is that there is something inherently better about us and the rest of the business world is lacking. It&rsquo;s like the scrawny new kid on the block hit the gym and got monstrously huge over summer and is now flexing nerd rage on the rest of their class.</p>
<p>We should not be seeking to replace the old world order of business; we should be augmenting it. We should not be trying to transform our lessers. We should be using our skills and technology to push the entire company forward.</p>
<h2 id="a-kinder-more-empathetic-way-is-needed">A kinder, more empathetic way is needed.</h2>
<p>Suppose we had a business team that consisted of a domain product owner, digital product owner, a couple of developers and testers, maybe an analyst, devops type person and designer. Or suppose we took an existing technical team and asked that the principal business stakeholder joined that team, co-located and contributed permanently.</p>
<p>That pesky fee that&rsquo;s turning off your customers in digital and retail channels could be solved quickly. Whenever the digital folk were undertaking a significant software initiative, they would likely already have the backing of their crucial stakeholder. That stakeholder would already be up to speed. They would be the most prominent advocate for that team to the rest of the business. Through co-location, you eliminate a host of meetings and sync up sessions. Through a shared context, you ensure that the company is on the same page as the techies. Sprint reviews become about inspecting and adapting the backlog from a whole of business perspective, not just a technical or feature perspective. Is it more valuable to change the way the product works, or change the way we position the product on the website? Such comparisons of work become meaningful because we can find an answer, rather than being thrown out there and not picked up by anyone.</p>
<p>This approach is by no means a panacea. It is still hard work. The nominated business person and the team still need to learn how to work together. What might seem like an unbridgeable gulf in some places could be just a very very large chasm - bridgable but with effort. The rest of the business needs to back such an endeavour. Come from a place of equality, rather than superiority. Acknowledge that your colleagues paid for your ticket here rather than expressing surprise they haven&rsquo;t choked on a peanut before now. You&rsquo;ll have a much better chance of success, without resentment and without forcing anyone out of work.</p>
<p>What do you get for success with this, as opposed to a digital transformation? For one, you can unsubscribe from all of the consultancy mailing lists cluttering your inbox. More importantly, you&rsquo;ve empowered a group of people with experience across the digital divide and set them loose on the toughest problems your business has. By working incrementally (rather than in a big bang project-driven way) you de-risk the effort. You can manage the pace and spend (reducing pressure on staff old and new, and pressure on the bottom line through less staff and hiring costs). You also ensure that all of your team are engaged and working in the same direction, not just the subset of staff who have the wind behind them.</p>
<h2 id="this-is-just-back-to-basics">This is just back to basics.</h2>
<p>What&rsquo;s ironic is that this way is the way all business began. There are no silos in an eight-person business. In a way, we&rsquo;re just getting back to basics. There&rsquo;s poetic symmetry in that somewhere.</p>
<p>If I owned a business, and it was my money and legacy on the line, I&rsquo;d avoid a digital transformation like the plague. I&rsquo;m not in that position though, so I&rsquo;ll leave it to the reader to pick up the torch.</p>
]]></content:encoded></item><item><title>Low Maintenance Home Labs</title><link>https://4lex.nz/posts/low-maintenance-home-lab/</link><pubDate>Fri, 07 Sep 2018 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/low-maintenance-home-lab/</guid><description>&lt;p&gt;How do you keep up to date with changes in technology? How do you get your startup off the ground from a technical standpoint? What can you do to build out your tools and processes in a sustainable, low risk way? Finding space for continual learning at work can be tough. If you, like me have a passion for technology but have transitioned away from a technical role and miss fiddling with technology on a daily basis, you might consider creating a lab at home to keep your skills sharp, and stay up to date on industry trends and best practice.&lt;/p&gt;</description><content:encoded><![CDATA[<p>How do you keep up to date with changes in technology? How do you get your startup off the ground from a technical standpoint? What can you do to build out your tools and processes in a sustainable, low risk way? Finding space for continual learning at work can be tough. If you, like me have a passion for technology but have transitioned away from a technical role and miss fiddling with technology on a daily basis, you might consider creating a lab at home to keep your skills sharp, and stay up to date on industry trends and best practice.</p>
<h3 id="why-have-a-lab">Why have a lab</h3>
<p>Having a lab represents freedom. Freedom to learn about new ideas in a safe space, freedom to use many different tools to make your life at home better, easier or more automated and freedom to build something new that eventually others could be involved in.</p>
<p>A lab is anything you want it to be. In my case it&rsquo;s an Intel NUC and Raspberry Pi running various versions of linux as a base, with many docker containers on top. For you, it could network storage equipment and backup power supplies, it could be a big server running virtualisation tools or even a whole bunch of Internet of Things devices. Part of the joy of having a lab is that it can reflect your current feelings and priorities, and be flexible enough to change in tune with your priorities.</p>
<h4 id="working-at-the-right-layer-of-complexity">Working at the right layer of complexity</h4>
<p>For me, the most important things in a lab are that I&rsquo;m using the right tool for the job, and that I&rsquo;m working at the right level of abstraction. In my case, I&rsquo;m running a lab at home to automate parts of my life, enable the building of my own software product (maybe one day I&rsquo;ll launch it in to a startup!) and that I can make the internet a better place for my family. To that end, I&rsquo;ve got many tools running at home that fit under those categories (and some that don&rsquo;t). Let&rsquo;s first talk a little about what I&rsquo;m <strong>not</strong> doing.</p>
<ul>
<li>I&rsquo;m not currently focused on how networking works</li>
<li>I&rsquo;m not currently looking at configuration management and how that works (I&rsquo;ve got some experience with puppe though)</li>
<li>I&rsquo;m not currently interested in virtualisation, hardware security or hardware in general</li>
</ul>
<p>Another way of saying this is that right now, I care about applications and how they talk to each other, I don&rsquo;t care about the underlying infrastructure (unless that has a material affect on the applications themselves).</p>
<p>So my chosen layer of complexity is the application layer, that means that where possible I should find a way of removing the complexity below that layer. This is not to say I don&rsquo;t care about what hardware I&rsquo;m running, it&rsquo;s to say that I don&rsquo;t care beyond a certain point. My low maintenance lab consists of three key devices right now.</p>
<ul>
<li>Networking: 1x Netgear Nighthawk X10 R9000</li>
<li>Compute: 1x <a href="https://web.archive.org/web/20170709061248/https://www.pbtech.co.nz/product/BPCINT3431469/Intel-NUC-BOXNUC7i5BNK-Slim-Mini-Computer-Kit-i5-7" target="_blank" rel="noopener noreferrer">Intel NUC i5</a>

</li>
<li>Compute: 1x Rasberry Pi 3b</li>
</ul>
<p>It&rsquo;s worth reiterating that my focus here is low maintenance. That means simple hardware, and low running cost. The Pi costs about $8 in power per year, the NUC a little bit more, all of this equipment runs cool and quiet. It&rsquo;s also not overkill. For me it&rsquo;s more important to have the right kit at the right time, than it is to have plenty of room to grow. I could have started with a big network attached storage device and enterprise grade server, but that&rsquo;s a bunch of cash I don&rsquo;t need to spend right now, for a hobby that might not be around in a couple of years time. It&rsquo;s going to be slightly more expensive to scale because of this. For example the RAM on the NUC tops out at 16gb, at the moment I have 8gb with one slot left. If I want 32gb of RAM I either need a new server box, or another NUC. I&rsquo;ll probably get another NUC. I think the tradeoff is worth it.</p>
<h4 id="raspberry-pi---network-services">Raspberry Pi - Network Services</h4>
<p>The Raspberry Pi 3b was actually the first thing I bought, at the time I wanted to make a retro gaming box for my kids but they&rsquo;re still a little young for that. Now I treat it as a tool for network services. It&rsquo;s my DNS server (so I can get gucci names for my services like $SERVICE.$MY_CUSTOM_DOMAIN.co.nz) as well as a network wide ad-blocker (thank you <a href="https://pi-hole.net/" target="_blank" rel="noopener noreferrer">Pi-Hole</a>

!). I also wanted to run docker containers on it, but the ARM chip in it makes that a hassle. In the future I&rsquo;d like to run a VPN from it, so that I can access all of my home tooling while I&rsquo;m away from home but I never seem to get time to sit down and do that.</p>
<h4 id="intel-nuc---general-compute-and-storage">Intel NUC - General Compute and Storage</h4>
<p>The NUC is my most recent addition, I picked it up in a sale last year. I&rsquo;m pleased with how it&rsquo;s performing and will probably scale out by adding NUCs until power management and things like that get annoying. It&rsquo;s a 7th generation i5 processor, which is to say it&rsquo;s not a beastly multitasking machine. It&rsquo;s not and never will be good for things like rendering farms or bitcoin mining. It is however, good for a lot of moderate usages. I&rsquo;m currently running 30+ docker containers (but more on that soon) and CPU usage hovers at around 3% idling, and bursts up to about 30% when it&rsquo;s working hard. With how powerful entry level CPUs are these days, processors generally aren&rsquo;t your bottleneck. It&rsquo;s RAM - this is a very different picture. There is 8gb of RAM available right now, plus another 8gb of swap on the M.2 SSD inside the NUC. RAM usage sits at about 60% when idle, and maxes out fairly regularly. RAM is my next purchase.</p>
<h4 id="bare-metal-virtualisation-and-containers">Bare Metal, Virtualisation and Containers</h4>
<p>For context, there is no right or wrong choice for you here. It really depends on what you&rsquo;re optimising for. In my case, I want the regular maintenace of the systems and applications I use to be minimal and recovery from failure should be easy (reflecting the fact that I am highly likely to break the entire network and that this should be encouraged. It&rsquo;s where learning happens). I don&rsquo;t care so much about speed and efficient use of resources since the cost of scaling isn&rsquo;t prohibitive, and I don&rsquo;t really care about isolating things as a primary effect of the choice I make.</p>
<p>Business tend to have different needs so if you&rsquo;re running a lab to skill up for work, your needs might differ.</p>
<p>For this reason I chose containerisation as my go-to application paradigm. It gives me the ability to store my application config in VCS, making recovery from failure alsmost trivial. With good orchestration tools I can manage resources well enough, and it also means that I can isolate applications from my underlying hardware. Docker is the de-facto standard for containerisation, and I&rsquo;m already familiar with it through the work I did on continuous delivery for Healthlink and Spark.</p>
<p>That isolation from the host is important to me, as it means that I can treat my machines like cattle, not like a pet. With Docker I can take advantage of the underlying OS (in this case, <a href="http://releases.ubuntu.com/18.04/" target="_blank" rel="noopener noreferrer">Ubuntu Server 18.04</a>

) in a way that keeps the OS clean and tidy. All changes sit inside containers and not on the host OS - I can swap out Ubuntu with CentOS as many times as I want, my containers don&rsquo;t care. I can upgrade the version of an application inside a container 300 times and the underlying OS doesn&rsquo;t care. Things stay tidy. This also means that the process for recovering from a critical failure is small - application configuration is in version control, if the NUC grenades over the weekend, my recovery is to re-run that config on a new node. The hardest part of that is buying a new node because I hate spending money. The use of containers also mitigates some of the scaling pain. Rather than manually moving stuff from one machine to a new machine, or having to configure a bunch of stuff on a new machine, it&rsquo;s easy to scale out the software side of the lab - all thanks to a tool called Rancher.</p>
<h4 id="container-management">Container Management</h4>
<p>It&rsquo;s important.</p>
<p><a href="https://rancher.com/" target="_blank" rel="noopener noreferrer">Rancher</a>

 is the tool I use for container management and orchestration. I consider it to be my one stop shop for all things lab management, and is the main view (I do have other views) in to the state of my lab. This isn&rsquo;t a sales pitch for rancher as much as I love it, this is mainly about how I use Rancher to solve the problems I want to solve.</p>
<p>The feature I use most with Rancher is orchestration. I specify the config of an image I want to run (via compose file or via the GUI) and Rancher worries about what host it goes on, what other containers it&rsquo;s grouped with and so on. When the container is running I can inspect it, run a shell to it or modify it&rsquo;s config easily. Rancher also has an awesome concept - the catalogue, that contains many images and config maintained by the community, that you can one click run in the style of Sandstorm, Bitnami and Cloudron, except rather than just deploying the application, you can deploy applications ready to scale, with pub/sub queues, redis and so on already integrated in to the application state.</p>
<p>Rancher comes with a load balancer based on HAproxy that works well out of the box. It has integrations in to Rancher functionality that make it super easy to set up reverse proxies with SSL termination. I simply add my SSL certs to Rancher&rsquo;s cert store, then select them from a drop down menu within the load balancer. I can apply custom HAproxy config to automatically redirect http requests to https, and can configure path or link based routing.</p>
<p>The final part of the Rancher puzzle for me is the config files I mentioned earlier. Rancher maintains vanilla docker compatible <code>docker-compose.yml</code> files for every stack of applications it manages, and updates that compose file when you make changes to the configuration. It also provides a <code>rancher-compose.yml</code> that is very simillar to docker compose, but provides rancher specific config (e.g start with 3 containers or 10 containers, and so on). This for me is crucial. All of that config is held in VCS and so if the machine grenades, my state is stored safely elsewhere. That;s just the container config though. There is also config specific to the applications, that tend to be stored within the file system - in this case, Rancher makes it easy for me to volume out all of the config to the host, I don&rsquo;t back that up currently but am looking at cloud backups for that also. I&rsquo;ll probably have a daily backup to some hardware I&rsquo;m running locally, and maybe a weekly or monthly backup to a cloud backup service provider (the state of my applications changes very infrequently so monthly or fortnightly is probably the best compromise there for cost vs currency).</p>
<h4 id="what-im-running">What I&rsquo;m Running</h4>
<p>Right now I&rsquo;ve got several stacks running within Rancher. I&rsquo;ve grouped applications around themes for those stacks so that it&rsquo;s easier to see what capability I have running and what I&rsquo;m missing. The stacks are called:</p>
<ul>
<li>Home Admin</li>
<li>Network Admin</li>
<li>Media Automation</li>
<li>Media Streaming</li>
<li>Product Development</li>
</ul>
<p>I&rsquo;ll run through each of them at a high level:</p>
<h5 id="home-admin">Home Admin</h5>
<p>Home admin for me is mainly about automating the things that relate to the physical space I live in, aka my house.
I&rsquo;ve got Mayan EDMS running as a paper removal machine. All of my bills, legal documents and other official docs get scanned and added to shelves within Mayan, so that I can search on the text inside the docs instead of just for PDF names and so on. I&rsquo;ve docker-linked to Mayan to it&rsquo;s own postgres container - this is a pattern I repeat and abuse for all of the tools I run. As an aside, the reason that I do a container and database instance per tool is so that I have more flexibility to upgrade or blow away databases. It also means that the databases are clean with only tables and data from one application. I choose postgres because it&rsquo;s what I&rsquo;m familliar with. It can be a pain but it&rsquo;s a pain I know!</p>
<p>I&rsquo;m also running a janitor tool that periodically scans for images that have no active containers, and dangling data volumes. If it finds anything not in use, it deletes it. Since my config is all stored externally, I can be fairly ruthless in terms of space saving this way.</p>
<h5 id="network-admin">Network Admin</h5>
<p>Network admin is a bit of a small stack right now. It currently contains a netdata instance for host health monitoring - Rancher has the ability to manage hosts but the reporting from netdata is much more detailed. The stack also contains an instance of Rancher&rsquo;s load balancer - this is where the reverse proxy and SSL termination functionality comes from.</p>
<h5 id="media-automation--media-streaming">Media Automation + Media Streaming</h5>
<p>My media automation efforts are the most complex stack. I&rsquo;m using SABnzbd, Sonarr, Radarr, Ombi (half heartedly), Tautulli and Plex. My focus here is on making available all the Blurays, DVDs and other media that I&rsquo;ve collected over the past 15 years or so. All the childhood movies whose disks got broken, the numerous copies of the Matrix trilogy that I somehow acquired and the stuff that I generally grew up on, that I&rsquo;d like to share with my kids now that they&rsquo;re getting old enough.</p>
<h5 id="product-development">Product Development</h5>
<p>This is the stack that is most actively worked on. It contains an instance of some software I&rsquo;m developing in my spare time, a postgres database, an SMTP mail server and interceptor (for testing email notifications that come from the software I&rsquo;m building), and an instance of the swagger editor.</p>
<h4 id="wrap-up">Wrap up</h4>
<p>This is a little bit of detail behind the thinking I went through when I decided I wanted to spend some time away from development, so that I could focus on learning about product and wider business things. Your lab might have different needs and look different, that diversity should be celebrated. There is no right or wrong lab!</p>
<p>I went with docker as it fit with my goals, those goals were about making the things I care about less, easier. That frees me up to focus on the stuff that I care about.</p>
<p>Rancher is cool.</p>
]]></content:encoded></item><item><title>SCIENCE! in the workplace</title><link>https://4lex.nz/posts/science-driven-development/</link><pubDate>Wed, 05 Sep 2018 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/science-driven-development/</guid><description>&lt;p&gt;What do when you don’t know what to do? Even worse: What do you do when people are paying you to do things, and expect you to do things, but you don’t know what to do? Are you in a brand new domain? A brand new type of business or a completely new role? What about if you’re in a completely new industry?&lt;/p&gt;
&lt;p&gt;Having just started a new role, the pressure to deliver and deliver quickly can be quite daunting. Using hypothesis-driven processes in your daily life can ease the tension.&lt;/p&gt;</description><content:encoded><![CDATA[<p>What do when you don’t know what to do? Even worse: What do you do when people are paying you to do things, and expect you to do things, but you don’t know what to do? Are you in a brand new domain? A brand new type of business or a completely new role? What about if you’re in a completely new industry?</p>
<p>Having just started a new role, the pressure to deliver and deliver quickly can be quite daunting. Using hypothesis-driven processes in your daily life can ease the tension.</p>
<h4 id="a-few-caveats-before-getting-started">A few caveats before getting started:</h4>
<ul>
<li>This approach works best if your manager supports you. It can you can do it by stealth, or without asking (sometimes it works best if you don’t ask). Often you’ll get the best results if you bring the method to your team - that’s any managers you may have, as well as anyone that reports to you, or colleagues with whom you work.</li>
<li>The stories I tell are from a software context; I believe that these processes can be used in different settings, but remember to be thoughtful about how you do that, don’t forget to test and adjust as you need.</li>
</ul>
<h4 id="definition-hypothesis">Definition: Hypothesis</h4>
<ul>
<li>A supposition or proposed explanation made based on limited evidence as a starting point for further investigation.</li>
<li>A theory before it’s tested. A hypothesis like a theory should have both explanatory and predictive value.</li>
</ul>
<p>In your everyday work, you have some impacts we want to achieve, be they strategic (increasing profit or reducing cost) or tactical (increasing daily active users by some proportion). Traditionally you might approach those goals any number of ways. You think up a solution and execute on that. You call time on the mission and move on to the next thing. And the next thing. And the thing after that. Project managers often get a bad rap for this, but it’s symptomatic of the business rather than the project manager in many cases. Those managers aren’t being incentivised to return to things and assess benefit since, in many cases, the contract has already paid out and to do so would not get them any more value for the business.</p>
<h4 id="enter-stage-left-the-scientific-method">Enter stage left, the scientific method</h4>
<p>For hundreds of years, people who have no idea what to do, have figured out what they need to do by using the scientific method. It looks something like this:</p>
<p><img alt="Scientific Method" loading="lazy" src="/img/in-post/scientific-method.png"></p>
<p>I’ve made some slight tweaks to the technique, the team here use this flow to manage the delivery of one to many features that will result in actionable insight. We’ve found that building a process where we are regularly looking at the outcomes we’ve achieved in the context of our goals, and work remaining helps us keep on track. Being mindful of business objectives reduces context switching - when the business makes requests, we can point them to data if we have it on why the demand may or may not affect the goal, we can also use the findings to help us prioritise. Those findings show financial value to the business and value to the customer. If someone asks us for some work, we’ll be able to look at the cost of that work, the opportunity cost and the likely benefit we might derive and give data back on how that will affect the backlog and the results we might see from the team.</p>
<p><img alt="Scientific Method" loading="lazy" src="/img/in-post/modified-scientific-method.png"></p>
<h4 id="the-method">The Method</h4>
<p>Start with an <strong>impact</strong> that you want to have on someone - it could be a staff member, a customer or some other kind of stakeholder. Once you’ve defined the impact you want to have and formed that into a goal or objective and key result, you can start building context.</p>
<p>Doing <strong>background research</strong> and getting that context can often be the most costly step in terms of time spent. If there is a mountain of existing data to trawl through, it can be quite a tedious process. Persevere! It’s worth it. The more insight into the issue you can get before you form the plan, the better. When I’m going through this process, I’ll skim the available documentation first and ask myself “Will what I’m learning right now make me adjust the plan, or does it answer my question or address my goal some other way?” - If the answer is yes to either of those things, it’s worth the extra time is taken to make those adjustments. In some cases, you can skip the experiment altogether and prove out your goal by combining multiple existing sources of data in a new way. The time and cost of gaining that context can save you the time and cost of the rest of the process. You can skip straight to the analysis and dissemination of that knowledge.</p>
<p><strong>Forming your plan</strong> is where most of the risk in this process sits. If you are missing crucial data, it can skew the method such that the experiments become questionable. If you’re doing an investigation into how to reduce the number of times a boy cries wolf; You should try to understand that humans lie a bunch. You might miss that sometimes wolves appear despite the local shepherd boy having no credibility. Standing in a field cataloguing the presence or absence of Canis lupus won’t get you closer to an impact or answer.</p>
<p>Further, if your plan is missing crucial information on what data needs to be tracked to prove out the experiment, you’ll struggle to show that the desired impact has been affected.</p>
<p>In an ideal world, you should have a strong hypothesis that can be tested independently of other work going on, that hypothesis should relate to the goal, and the experiments you design should provide you with the data you need to validate or refute the hypothesis. Sometimes it’s hard to get the information you need, or it’s hard to run the experiment in isolation from other changes. That’s okay. From the research you’ve done getting to those points, you should have a good feel for how to mitigate those concerns or make a call on whether or not the experiments should proceed.</p>
<p>After you have <strong>designed your experiments</strong>, the next step is to run them as per the plan. It’s essential that during this time you rigidly stick to the program (unless it has become unsafe, unsound or otherwise untenable). As you run those experiments, it helps to note down during the process any points for improvement for next time. If the tests are not running as you expect, those points you’ve noted for troubleshooting or development will help the experiments and the way you design and reason about trials evolve as your understanding grows.</p>
<p>Your experiments have run the course; now it’s time to <strong>analyse the data</strong>. How you might do that is a little out of the scope of this. There are several methods for synthesising that information in both qualitative and quantitative ways. Once that process is complete, the <strong>write-up</strong> can begin! It’s essential that during the write-up you clearly and plainly <strong>state one of three things</strong>:</p>
<ul>
<li>Has our intervention made a positive impact (i.e. are we closer to our goal now?)</li>
<li>Has our intervention made a negative impact (i.e. are we further to our goal now?)</li>
<li>Has our intervention made an impact that we can see? (i.e. have we been tracking the right metric in regards to the impact?)</li>
</ul>
<p>If you can clearly state one of the above and back that up with the raw data that proves out that point, the rest of the write-up falls into place. You can spend more time talking about what <strong>happens next</strong>, and more time s<strong>ocialising those results</strong>. Either way, it’s essential that the research you have created is filed away in a place it can be easily referred to, as <strong>it now forms part of the ecosystem of knowledge</strong> you have to draw on for the next goal, hypothesis and experiment.</p>
<h4 id="the-difference">The Difference</h4>
<p>he difference between these two processes is less about the process and more about the context. It’s relatively safe to assume that if you’re doing science in a scientific domain (at a university, technical institution or somewhere else) you often have a large body of work to draw on that is formally named, has been cited a bunch and published.</p>
<p>Sometimes in a private company, you don’t have that body of knowledge to draw on. Maybe you have an internal wiki, or perhaps documentation is spread across a bunch of shared drives. This process gives you a place to start. Starting with this process will help you generate that body of knowledge (which you should then store in a tool that can be accessed by whoever needs it). Being able to draw on that research takes the ego out of conversations about how to achieve goals. Having a team follow this process can also help you avoid distraction, as it’s clean, simple and ideally short if you’re working down in detail. If you’re not working at that tactical level, you can also use this process to plan work out across the business - for each high-level initiative, you might repeat lower-level versions of this initiative many times with different foci.</p>
<p>It’s important to note that there is no failure case in this process. Should an experiment not yield a result you expect, that’s okay. There is no value judgement or loss of face there since we acknowledged at the start that the hypothesis is only a best guess and that ultimately the data will tell us the answer. Regardless of the outcome that knowledge gets stored and becomes part of the institutional experience that can now be built up - help others in your organisation, use your lessons in their work.</p>
<p>In this way, we can start creating an ecosystem of research that we can use to reinforce our learning and experiments. It means that we can build and extend on our body of knowledge each analysis we run, in a way that relies on empirical data and processes that can’t be argued with or litigated (assuming the methodology behind your research is sound). If your organisation has a problem with pet projects that deliver questionable value (I’ve seen and heard about this problem in many organisations), this process is excellent for having a conversation about whether or not this is the best thing to be doing with the resources available to the business.</p>
<h4 id="speed-to-market">Speed to Market</h4>
<p>Executing work in this way can be as slow or as fast as your business needs it to be. For domains that require high certainty and minimal risk, we can make choices about how long we run experiments for, we can limit the number of users involved in the research, and the defined process can help you include other parts of the business. As part of the context building step of your experiment, you can reach out and involve other teams or people if you have that specialisation available, and if you don’t, that’s okay too because there isn’t a commitment to the experiment long term unless that suits the business.</p>
<p>Should you be in a domain that encourages rapid iteration, short experiments with a large number of users can help you sense and respond rapidly to the market. Borrowing heavily from lean methodologies, you can focus your effort on the successful experiments and quickly discard ideas that do not help you achieve your goals.</p>
<h4 id="you-still-need-to-prioritise">You still need to prioritise</h4>
<p>Applying the scientific method in your role doesn’t replace the traditional process of prioritising work. It remains vital for everyone involved to understand what the most important thing to do is and why it’s crucial. Whether you use the Kano model, Outcome-Driven Innovation or something else entirely, those goals still need to be looked at in comparison with other purposes. This method tells you whether or not you’re moving the dial, and whether or not you should keep trying to move the dial the way you are currently trying to move it, or if you should change or stop.</p>
<h4 id="conclusions-and-key-points">Conclusions and key points</h4>
<p>The scientific method is an established, proven way of learning new things. This method is something that can be used in a business or technical context as much as well as in academia.</p>
<p>Using this process, or basing your process on the method can help you understand new situations and contexts in a way that is safe, rapid and applicable to many domains or industries.</p>
<p>It helps if you’ve got buy-in from your organisation, but ultimately you can use this process to guide your work internally within your team, or on your own, if you don’t work as part of a team.</p>
<p>This process helps you define a goal, and plan interventions to shift a metric on that goal - it then encourages you to show others the results and use that new knowledge as part of your future work.</p>
<h4 id="references-and-further-reading">References and further reading</h4>
<p>[1] <a href="https://web.archive.org/web/20230623073015/https://www.drdobbs.com/architecture-and-design/hypothesis-driven-development/229000656" target="_blank" rel="noopener noreferrer">Hypothesis-driven development</a>

 - Dr Dobb&rsquo;s World of Software Development</p>
<p>[2] <a href="https://www.infoq.com/news/2010/02/edd-post-agile" target="_blank" rel="noopener noreferrer">Experiment driven development</a>

 - Sebastien Auvray, InfoQ</p>
<p>[3] <a href="https://www.thoughtworks.com/insights/blog/how-implement-hypothesis-driven-development" target="_blank" rel="noopener noreferrer">How to implement hypothesis-driven development</a>

 - Barry O&rsquo;Reilly, Thoughtworks</p>
]]></content:encoded></item><item><title>Good Teams, Why Care?</title><link>https://4lex.nz/posts/why-teams-are-important/</link><pubDate>Fri, 13 Jul 2018 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/why-teams-are-important/</guid><description>&lt;p&gt;I build teams. I build teams good. Good teams that do well, and they also do good. Technical teams mainly, but I&amp;rsquo;m branching out. It&amp;rsquo;s good.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s all well and good, but what is the point?&lt;/p&gt;
&lt;p&gt;The point is that good teams are rare. That is to say, a team that you can leave alone, with the trust that they can
self-regulate, self-correct and grow without you is very difficult to find, such that the effort to grow one
(if you can) is a far simpler thing by comparison.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I build teams. I build teams good. Good teams that do well, and they also do good. Technical teams mainly, but I&rsquo;m branching out. It&rsquo;s good.</p>
<p>That&rsquo;s all well and good, but what is the point?</p>
<p>The point is that good teams are rare. That is to say, a team that you can leave alone, with the trust that they can
self-regulate, self-correct and grow without you is very difficult to find, such that the effort to grow one
(if you can) is a far simpler thing by comparison.</p>
<p>In a world where everyone says they put users first, but very few actually walk the talk, you need teams like this.
You need them because there is simply too much stuff going on for anyone to keep track of.
Particularly at a place like BNZ. We&rsquo;ve got work coming out our backsides. BNZ Digital is a small part of the wider Bank.
Dunbar&rsquo;s number is 150. Dunbar’s number describes the amount of stable relationships a person can have - relationships
in which an individual knows who each person is and how each person relates to every other person.</p>
<p>Teams are important because they increase Dunbar&rsquo;s number. I can abstract away up to ten people and hide them behind a
single team. I can align those teams with products or purposes or causes or constructs; it’s natural for organisations
to grow, grouping employees into teams helps us keep it all in our heads at once. It helps us maintain cohesion and coherence.</p>
<p>But getting back to the users, they don&rsquo;t see all that complexity nor the size of the issue you&rsquo;re trying to fix.
They don&rsquo;t care about the complexities of OAuth 2.0 or how difficult it is to scale things without blockchain or the
latest facebook framework. They care that in the year 2018 they still have to log in to internet banking with a number we gave them.</p>
<p>Imagine the cognitive dissonance. On the one hand, the bank has spent time and effort on an ad in between the ruggers
about how important I am to them personally, and on the other, they&rsquo;ve literally set my internet banking login to some
random number instead of my own name or something meaningful to me. Neat.</p>
<p>Good teams show users that the team cares. Good teams put users first. Yes, the Product Manager has given you metrics
to move and the head of department wants a big splashy thing. The salespeople want more conversions and the brand people
want you to speak with one voice and feel with two feet, or something equally as nebulous. Who knows what that other team
wants. They stopped coming to meetings ages ago.</p>
<p>Good teams know. A mature, high-performing team can work across the business and manage that entire lifecycle of work.
They can start with discovery and research, ensuring that they&rsquo;re working on a valuable problem that if solved, will
make a happy user. They can plan and execute the work, and importantly, know when to involve people from other teams
and disciplines as needed. That other team didn&rsquo;t turn up to the meeting because we&rsquo;d spoken to them previously.
Brand is happy since they saw the copy weeks ago, and actively drafted the content alongside the content team.
Of course the software is being written and tested, eventually deployed and importantly, the impact of that feature on the user is measured.</p>
<p>All this is not to say that if you&rsquo;re not doing the above, you&rsquo;re not part of a good team.
Everyone&rsquo;s context is different and that should be celebrated. We all get value to our users in different ways and
that makes for diverse companies that compete, learn and grow.</p>
]]></content:encoded></item><item><title>Building Continuous Delivery for Healthlink</title><link>https://4lex.nz/posts/building-ci-cd-for-healthlink/</link><pubDate>Wed, 05 Oct 2016 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/building-ci-cd-for-healthlink/</guid><description>&lt;p&gt;My team at Healthlink have just finished a proof of concept around Continuous Delivery. Here’s the writeup, sanitised for the internet.&lt;/p&gt;
&lt;h2 id="context"&gt;Context&lt;/h2&gt;
&lt;p&gt;We’re a Health IT company based out of New Zealand, and run an set of products
that provide messaging integrations between Healthcare organisations across New Zealand
and Australia. Our applications are a mixture of desktop and web service based, and we’re
currently undergoing a massive change process from Waterfall to Agile and from a bespoke
services company to a truly product focused company.&lt;/p&gt;</description><content:encoded><![CDATA[<p>My team at Healthlink have just finished a proof of concept around Continuous Delivery. Here’s the writeup, sanitised for the internet.</p>
<h2 id="context">Context</h2>
<p>We’re a Health IT company based out of New Zealand, and run an set of products
that provide messaging integrations between Healthcare organisations across New Zealand
and Australia. Our applications are a mixture of desktop and web service based, and we’re
currently undergoing a massive change process from Waterfall to Agile and from a bespoke
services company to a truly product focused company.</p>
<h2 id="the-problem">The Problem</h2>
<p>Our time to market is poor, we want to shorten our release cycle and provide
and awesome service to our customers (above and beyond the services that basically
enabled us to become a monopoly in New Zealand).</p>
<h2 id="the-solution">The Solution</h2>
<p>To enable rapid iteration, we needed to shorten our feedback loops, and the best way
of doing that is by implementing continuous delivery.</p>
<h2 id="the-principles-of-continuous-delivery">The Principles of Continuous Delivery</h2>
<p>There was quite a but of work to do to enable our solution, continuous delivery has
a bunch of prerequisites and we wanted to abide by a set of core principles that would
guide the technology and process decisions we made.</p>
<h3 id="the-principles-that-guided-us-are">The principles that guided us are:</h3>
<ol>
<li>
<p><strong>Processes MUST be repeatable and reliable.</strong></p>
<ul>
<li>The process by which we deploy our software has been automated, but often full automation of a release can make the business uncomfortable.
To help the business get used to this, we’ve introduced some decision points in our release flow that require manual intervention.</li>
</ul>
</li>
<li>
<p><strong>EVERYTHING has to be automated.</strong></p>
<ul>
<li>The manual decision points we’ve introduced don’t violate this because no process is actually being done manually, we’re just providing business
the opportunity to control the process in more discrete steps. All of our build, test and deployment steps are automated though, that in itself has
reduced our time to any arbitrary environment to about a minute, with full unit, integration and system tests conducted every time we build and deploy.</li>
</ul>
</li>
<li>
<p><strong>You must be able to guarantee quality.</strong></p>
<ul>
<li>The bedrock of any automated process is quality. We guarantee the quality of our code by monitoring our code metrics in Sonarqube - things like
test coverage, technical debt and best practice conformance. Sonarqube breaks builds if they introduce issues or lack test coverage on new code.
Further, in tracking unit test coverage, we also ensure that those tests are run and pass - if you don’t use them, there’s little point spending time
and money creating and maintaining them.</li>
</ul>
</li>
<li>
<p><strong>If something is difficult or painful, it must be done more often. You can’t build muscle without exercise.</strong></p>
<ul>
<li>In forcing ourselves to do painful things often, we worked out the issues in those processes and were able to mitigate or resolve them entirely,
leading to a much more robust process.</li>
</ul>
</li>
<li>
<p><strong>Everything must be expressed as code and versioned.</strong></p>
<ul>
<li>Obviously our application code is all safely in git, versioned and lovely, but it’s important to version and code our build steps and infrastructure as well.
We achieved this using Puppet with Hieradata within a git repository managed with Puppets’ Code Manager, and docker allows us to store our image creation steps in a Dockerfile,
which is stored side by side with the application code itself.</li>
</ul>
</li>
<li>
<p><strong>Things are only considered DONE when they are released to Production.</strong></p>
<ul>
<li>Setting up triggers such that successful deploys off a dev branch trigger builds and deploys on our master branch enabled us to smoothly flow code through our environments in
and automated way. Bamboo gives great deployment functionality that we’ve made good use of.</li>
</ul>
</li>
<li>
<p><strong>Measure quality throughout the process.</strong></p>
<ul>
<li>Tying all of our builds back to Sonarqube enabled us to keep track of quality, we also track metrics around the amount of time it takes to complete the process, ratios of successfull builds
and deploys to failures, and a bunch of other stuff.</li>
</ul>
</li>
<li>
<p><strong>The Continuous Deployment process needs to be continuously improved.</strong></p>
<ul>
<li>We’re still working on the process. It’s important to not forget that there is always another issue we could be improving.</li>
</ul>
</li>
</ol>
<h3 id="the-tools-we-chose">The Tools we chose</h3>
<ul>
<li>
<p><strong>Docker</strong></p>
<ul>
<li>Docker enables us to have superfast builds by building our applications in layers. By building these layers separately, when we build the application, we only have to build the application, rather than
also having to build all of our dependencies and other bits and bobs.</li>
</ul>
</li>
<li>
<p><strong>Rancher</strong></p>
<ul>
<li>We use Rancher for container orchestration and management, it enables us to keep a high level picture of what our applications look like somewhere easy to reach. It enables us to do a whole bunch of really
cool things like load balancing, health checks and the bread and butter of container orchestration. Further, it also has a great repository of applications, stuff like load balancers, CTF stacks, container
discovery tools, even a containerised version of the famous ELK stack. The capability to create your own catalog also exists, so once we’ve got all of our tools and services in to containers, we can spin up
production ready software in no time flat, with all of the quality guarantees built in to our building and deployment pipelines. This is the part of our solution that I’m most excited about and I would recommend
Rancher to anyone interested in container orchestration and management.</li>
</ul>
</li>
<li>
<p><strong>Nexus</strong></p>
<ul>
<li>We use Sonatype Nexus 3 for a bunch of stuff. We have two instances of Nexus, one for integration ready blobs and one for deployment ready blobs. That means that all of our base docker images, sub-images,
and layers we don’t want to send to prod live in our CI Nexus, and the only images that live in our CD instance of Nexus are images that are ready for deployment at a moments notice. We also use
Nexus to mirror Maven Central and the NPM Registry in order to reduce our build times by removing as much network variance as possible. We also keep build dependencies in our CI Nexus, blobs like Servicemix
archives, SoapUI archives and others.</li>
</ul>
</li>
<li>
<p><strong>Bamboo</strong></p>
<ul>
<li>We use Bamboo to wire up our builds, tests and deploys. One thing that irks me about Bamboo is it’s lack of ability to verify a deployment and conduct system and smoke tests. That being said,
it’s got an awesome build system and the a strong deploy system that allow us to tie any image running in production all the way back through the pipe, to the commit that triggered the build.
That provides us with a great deal more visibility as to what code is where when compared with our old process.</li>
</ul>
</li>
<li>
<p><strong>Bitbucket</strong></p>
<ul>
<li>Git and Bitbucket are the solid but silent performers. We use a Feature branching workflow with pull requests - we require that the only change to master is a pull request from dev, and code only gets in to
dev via pull requests. Each request must have a minimum of two reviewers, no issues or tasks against them, and a passing bamboo build.</li>
</ul>
</li>
<li>
<p><strong>SonarQube</strong></p>
<ul>
<li>Sonarqube enables us to track issues with our code, and break builds if quality gates aren’t met. It’s also a great tool to communicate the health of our code base to the rest of the business.</li>
</ul>
</li>
</ul>
<h2 id="system-map">System Map</h2>
<p>Here’s the system we built, end to end. It’s a bit busy but it is quite compact, and looking at it, I can’t see anything I would take away to make it simpler, which I think is a good thing. It’s as simple as it can
be while still being powerful enough to deal with any deployment requirement we might have.</p>
<p><img alt="Healthlink System Map" loading="lazy" src="/img/in-post/cd-system-map.png"></p>
<p>What value has the business derived from this project?</p>
<p>Applications that use our CD Pipeline gain the following benefits:</p>
<ol>
<li>The application can be built and deployed to any arbitrary environment in less then two minutes in most cases, and we’ve yet to see a build and deployment take longer than four.</li>
<li>Batch sizes are smaller, so deploys are safer</li>
<li>Deploys can be rolled back in a single click, in the unlikely event of a failure</li>
</ol>
<p>The business benefits derived from this then are:</p>
<ul>
<li>Vastly cheaper deployments, we’re saving about $800 every deployment on every project that uses this flow.</li>
<li>Our time to production, where customers use our software, is under half an hour. The benefits of being to get feedback and iterate quickly provide us with an unfair advantage over the competition. I like unfair advantages.</li>
<li>Software Quality is way up, which means less issues in production, less time spent supporting that software and less time spent maintaining that software.</li>
</ul>
<p>I feel like that’s some pretty compelling stuff.</p>
]]></content:encoded></item><item><title>DevOps Days NZ 2016</title><link>https://4lex.nz/posts/dev-ops-days-wellington/</link><pubDate>Wed, 05 Oct 2016 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/dev-ops-days-wellington/</guid><description>&lt;p&gt;On Friday 30th September, a colleague and I spoke at Devops Days 2016 in Wellington.&lt;/p&gt;
&lt;p&gt;It was an awesome experience, and the first time I&amp;rsquo;ve spoken on technical and non-technical topics at an industry conference.&lt;/p&gt;
&lt;p&gt;The talk I gave is available on &lt;a href="https://speakerdeck.com/alexandernz/health-is-hard" target="_blank" rel="noopener noreferrer"&gt;Speakerdeck&lt;/a&gt;
. I&amp;rsquo;ve also embedded it below:&lt;/p&gt;
&lt;div class="speakerdeck-embed" style="position: relative; padding-bottom: 56.2500%; height: 0; overflow: hidden;"&gt;
&lt;iframe
src="https://speakerdeck.com/player/bf1d128fcc4741efbe9b2313be9ecfdf"
title="SpeakerDeck presentation bf1d128fcc4741efbe9b2313be9ecfdf"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen
loading="lazy"
referrerpolicy="strict-origin-when-cross-origin"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;"
&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;I think that the one thing that I took away from &lt;a href="https://www.devopsdays.org/events/2016-newzealand/welcome/" target="_blank" rel="noopener noreferrer"&gt;Devops Days NZ 2016&lt;/a&gt;
was this.&lt;/p&gt;</description><content:encoded><![CDATA[<p>On Friday 30th September, a colleague and I spoke at Devops Days 2016 in Wellington.</p>
<p>It was an awesome experience, and the first time I&rsquo;ve spoken on technical and non-technical topics at an industry conference.</p>
<p>The talk I gave is available on <a href="https://speakerdeck.com/alexandernz/health-is-hard" target="_blank" rel="noopener noreferrer">Speakerdeck</a>

. I&rsquo;ve also embedded it below:</p>
<div class="speakerdeck-embed" style="position: relative; padding-bottom: 56.2500%; height: 0; overflow: hidden;">
  <iframe
    src="https://speakerdeck.com/player/bf1d128fcc4741efbe9b2313be9ecfdf"
    title="SpeakerDeck presentation bf1d128fcc4741efbe9b2313be9ecfdf"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    allowfullscreen
    loading="lazy"
    referrerpolicy="strict-origin-when-cross-origin"
    style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;"
  ></iframe>
</div>


<p>I think that the one thing that I took away from <a href="https://www.devopsdays.org/events/2016-newzealand/welcome/" target="_blank" rel="noopener noreferrer">Devops Days NZ 2016</a>

 was this.</p>
<p><em>It&rsquo;s very easy to get wrapped up in your own problems in this industry.
It&rsquo;s very easy to feel like your problems are unique, new or otherwise unreproducible. Unless you&rsquo;re Google, Netflix or of that ilk, this is very unlikely.
You and I, we&rsquo;re not so different. Maybe it&rsquo;s health, maybe it&rsquo;s media, maybe it&rsquo;s any other industry. We&rsquo;re all going through the same things, at different times.
It&rsquo;s important that we continue to work through these diverse issues as best we can, while knowing that someone out there has felt, is now feeling or will feel your pain.</em></p>
<p><em>You are not alone!</em></p>
<p><em>In the spirit of DevOps and all the baggage that that word contains and frees us from, let&rsquo;s keep helping each other, let&rsquo;s keep sharing, we&rsquo;re a community of like minded people.</em></p>
]]></content:encoded></item><item><title>The Gospel of Doubt</title><link>https://4lex.nz/posts/casey-gerald/</link><pubDate>Sun, 27 Mar 2016 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/casey-gerald/</guid><description>&lt;p&gt;Casey Gerald is an amazing speaker. He spoke at Webstock about belief, and doubt.&lt;/p&gt;
&lt;p&gt;He spoke about how each time he became invested in a belief of something greater than himself, he was inevitably
dissapointed. Each time he entered a different phase in his life, he believed in something new, and each time
that belief was broken. He began to believe in doubt, he believed it might not be possible to believe in anything at all.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Casey Gerald is an amazing speaker. He spoke at Webstock about belief, and doubt.</p>
<p>He spoke about how each time he became invested in a belief of something greater than himself, he was inevitably
dissapointed. Each time he entered a different phase in his life, he believed in something new, and each time
that belief was broken. He began to believe in doubt, he believed it might not be possible to believe in anything at all.</p>
<p>He compared his belief with ours.</p>
<p>There is a great hunger in our generation for purpose and meaning. Dan Pink describes it in his studies on human motivation,
<em>mastery, autonomy and purpose</em>. That purpose is more and more, coming to be at odds with the society we share with
other generations.</p>
<p>Casey realised that he was the dialysis for a country that needs a kidney transplant. His voice had stood in for those who
had no voice themselves. This time the false saviour was Casey.</p>
<p>Some of us belief in the bible, or the Harvard Business Review. We hardly realise the human cost of our beliefs. We dare
not question the founding bricks of our belief, lest the cracks at the bottom bring our whole facade crumbling down.</p>
<p><strong>Why, with all the power we hold in our hands, are people suffering so?</strong></p>
<p>He&rsquo;s speaking about capitalism. He references the titans of our capitalist western world, Harvard, the New Yorker,
<em>&lsquo;We see suffering as a necessary act of the capitalism that is our god&rsquo;</em>.</p>
<p>With his closing words, he says <em>&lsquo;And this doubt, it fuels me, it gives me hope that when our troubles overwhelm us,
when the paths laid out for us seem to lead to our demise, when our healers bring no comfort to our wounds,
it will not be our blind faith &ndash; no, it will be our humble doubt that shines a little light into the darkness
of our lives and of our world and lets us raise our voice to whisper or to shout or to say simply, very simply,
&ldquo;There must be another way&rdquo;.&rsquo;</em></p>
<p>And he&rsquo;s right. There must be a better way. There are hard questions we need to ask, that our politicians need to ask,
because we live in a more prosperous time then any other throughout history, and yet deprivation is growing. The amount of
inequality is increasing and more and more, a sense of resentment in those members of our global economy who are left behind
is becoming apparent.</p>
<p>It&rsquo;s that sense of resentment that people like Donald Trump are exploiting for their own benefit. It&rsquo;s the reason why so many
people in New Zealand rabidly hate John Key. People are being left behind, and becoming more desperate. Desperate people are
in New Zealand are changing our political landscape. From attacks on MP electorate offices, to assaults on cabinet ministers.</p>
<p>We don&rsquo;t have good answers for anything of this nature. That little light in the darkness needs to shine a bit brighter.</p>
]]></content:encoded></item><item><title>Overtime is dead, long live Overtime.</title><link>https://4lex.nz/posts/overtime/</link><pubDate>Fri, 11 Mar 2016 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/overtime/</guid><description>&lt;p&gt;We started Overtime last year bemoaning the lack of social eSports being played in New Zealand, not fully understanding the reason behind that absence. Well, now we do. It&amp;rsquo;s really hard to do it properly. It takes a bunch of time, coordination, and passion.&lt;/p&gt;
&lt;p&gt;Fortunately we still have the passion, but unfortunately, our time has dried up.&lt;/p&gt;
&lt;p&gt;We won&amp;rsquo;t be running Season 2 this year. Instead, Troy and I will be spinning off the software we wrote to manage the first season, and working on that to the point where it&amp;rsquo;s a viable product in and of itself. Our new tournament as a service will be called BattleStack - and we&amp;rsquo;re very excited to be working on it.&lt;/p&gt;</description><content:encoded><![CDATA[<p>We started Overtime last year bemoaning the lack of social eSports being played in New Zealand, not fully understanding the reason behind that absence. Well, now we do. It&rsquo;s really hard to do it properly. It takes a bunch of time, coordination, and passion.</p>
<p>Fortunately we still have the passion, but unfortunately, our time has dried up.</p>
<p>We won&rsquo;t be running Season 2 this year. Instead, Troy and I will be spinning off the software we wrote to manage the first season, and working on that to the point where it&rsquo;s a viable product in and of itself. Our new tournament as a service will be called BattleStack - and we&rsquo;re very excited to be working on it.</p>
<p>The Team would like to thank everyone who had input into Season 1, and our pre-season. Thank you to our sponsors, our game admins and the teams, you, our players. In particular, we want to thank the great folk at Bigpipe and Playtech. Without your support, our LAN Final would never have happened. Your support is what made it all work.</p>
<p>Thank you.</p>
<p>The awesomeness that was Season 1 is a perfect example of what all you likeminded people can do when you come together. We aim to make that &lsquo;coming together&rsquo; scale.</p>
<p>So long and thanks for all the fish.</p>
<p>Alex</p>
<p><a href="mailto:team@battlestack.io">team@battlestack.io</a></p>
]]></content:encoded></item><item><title>The Fraud of Authenticity</title><link>https://4lex.nz/posts/heather-armstrong/</link><pubDate>Sat, 13 Feb 2016 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/heather-armstrong/</guid><description>&lt;p&gt;So it struck me that the more I reflect and refine the thoughts I have on the speakers at Webstock 2016, the more I want to write it down somewhere. Alcoholism will eventually take my brain, so it would serve me well to log down my thoughts. It&amp;rsquo;s also good practice for building opinions and learning how to communicate better.&lt;/p&gt;
&lt;p&gt;Heather Armstrong, for those who do not know, is internet famous. I didn&amp;rsquo;t know that. I&amp;rsquo;m not her target market and of course, that&amp;rsquo;s okay. But what is she doing at webstock?&lt;/p&gt;</description><content:encoded><![CDATA[<p>So it struck me that the more I reflect and refine the thoughts I have on the speakers at Webstock 2016, the more I want to write it down somewhere. Alcoholism will eventually take my brain, so it would serve me well to log down my thoughts. It&rsquo;s also good practice for building opinions and learning how to communicate better.</p>
<p>Heather Armstrong, for those who do not know, is internet famous. I didn&rsquo;t know that. I&rsquo;m not her target market and of course, that&rsquo;s okay. But what is she doing at webstock?</p>
<p>She&rsquo;s telling us that she&rsquo;s worried. Sure there&rsquo;s humour, there&rsquo;s some old war stories about pissing off sponsors, something about goats, and hairy vaginas - maybe I&rsquo;m overthinking it, but I don&rsquo;t think she is happy with the way the internet has become homogonised, white-breadified or, in Heather&rsquo;s own words, &ldquo;white privilege gone mad&rdquo;.</p>
<p><img alt="No, Seriously" loading="lazy" src="/img/in-post/hairyvaginas.png"></p>
<h3 id="i-agree">I agree.</h3>
<p>Like it matters what I think. But then, this is designed to be a critical analysis and you&rsquo;ve read down this far, so let&rsquo;s assume that it does matter.</p>
<p>At first I thought that Heather was speaking about how she loves the shit life throws at her. She loves talking about the ugly parts. The vomit filled car and car sick kids, the not having slept for two days on account of goddam slumber parties. At first it seemed that Heather simply wanted to tell us how satisfying it is being a whole human being, complete with all the flaws that humanity entails. I was a simpleton.</p>
<h3 id="theres-a-problem-with-social-media-these-days">There&rsquo;s a problem with social media these days.</h3>
<p>It&rsquo;s fucking fake man. That&rsquo;s the crux of it. How many times have you not posted how awful your day was, because you didn&rsquo;t want to be seen to be fishing for attention or sympathy? Believe me, if I&rsquo;m posting about how crappy my day has been, there&rsquo;s a pretty good chance I&rsquo;m just venting, and don&rsquo;t actually want any of your pity.</p>
<p>How many times on Snapchat or Instagram, Pinterest or Twitter, have you retaken the same photo forty times to get the light &lsquo;just right&rsquo; or to wait for the perfect background tableau? #Instafilter #VSCOCAM #IveOnlyTypedThreeAndIAlreadyThinkLessOfMyself. Is that real? Is that raw? Is the rather mundane picture of your home made spag bol with a filter plasted over it, an accurate representation of your day? It may well be. How about the adorable raccoon account you follow? How about that wedding list? Are those ideas that you&rsquo;re judging the most important day of your life against, a comparable wedding, with a comparable budget and are there people like yours attending?</p>
<h2 id="how-do-you-tell-the-difference">How do you tell the difference?</h2>
<p>That&rsquo;s the point Heather is making. There are so many beautiful, natural and real things out there that we could be seeing, judging, loving. But we&rsquo;re not. We&rsquo;re wrapped up in what the Beebs said on twitter. We&rsquo;re checking out Kim K on whatever godawful service she uses the most (probably all of them, actually?). This isn&rsquo;t a new thing, it&rsquo;s been a thing with magazines for a while now, hence models eating salad and water and starving themselves. Hence men stressing themselves out because they don&rsquo;t look like He-Man (ps most girls think He-Man is too muscly so be cool guys we good. Or my wife is being kind to me. Shit). Kids are covering themselves in makeup because cleo or cosmo or whatever told them it was the right thing to do at the age of 8 (which is when most kids these days first see porn on the internet by the way - but that&rsquo;s a different talk).</p>
<p>The rub is that the world (mostly) knows that these magazines are curated and unreal, that is, they misrepresent fundamental aspects of human nature. They lead us to believe that some highly idealised, highly managed and largely contrived set of circumstances, are real and normal and what we should all aspire to. And it&rsquo;s wrong, dangerously so.</p>
<p>Heather knows this, she took great delight in pointing out the fakeness of it all. And she challenged us. I didn&rsquo;t really realise until afterwards. There was this anecdote she told. She had a contract to write a thing about travelling with her kids. They didn&rsquo;t want to be dragged out away from their lives, they resisted, and it put Heather in a position where she couldn&rsquo;t write the bit. Because doing so required the involvement of her children <em>even against their will</em>. Now the children were part of her contract, before they had only ever been involved willingly.
<strong>It felt like exploitation</strong> Heather said.</p>
<p>That begs the question, how far are we willing to create fake lives for the benefit of others? Surely it would be better for our own wellbeing if we were just honest that, <em>yes, sometimes life is shitty and for no good reason</em>.</p>
<p>Surely we would be better off as a society if we didn&rsquo;t feel the need to &lsquo;clean up&rsquo; the picture on the front page of whatever fitness / fashion magazine we hold so dear.</p>
<p>Of course we should, but money, but sex! But how will I prove that I&rsquo;m doing better then $NEIGHBOR?
<img alt="Dinkleberg" loading="lazy" src="/img/in-post/dinkleberg.png"></p>
<p>I feel like I&rsquo;ve put a lot of words in Heather&rsquo;s mouth, I want to be clear that this is my own interpretation of the message that I think she was sending. Communication is hard man.</p>
<p>It&rsquo;s so hard that sometimes we have to fake it. That was okay in the past, because moderation and because an individual couldn&rsquo;t influence the world back then in the way they can now.</p>
<p>Maybe in this modern age of instant communication and empty social media moments, we need to work a bit harder to be real, to be <em>honest</em>, but above all, to be <strong>human</strong>.</p>
]]></content:encoded></item><item><title>What I learned at Webstock 2016</title><link>https://4lex.nz/posts/webstock-2016/</link><pubDate>Sat, 13 Feb 2016 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/webstock-2016/</guid><description>&lt;p&gt;Over the last few days, I&amp;rsquo;ve spent a lot of time thinking. Thinking about what, and why should you care?
I&amp;rsquo;m not sure I know the answer to those questions any more.&lt;/p&gt;
&lt;p&gt;What I do know, is that I want to go back to webstock next year, regardless of whatever else is going on in my life at the time. These words are coming out like treacle right now, I don&amp;rsquo;t usually have a problem communicating like this.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Over the last few days, I&rsquo;ve spent a lot of time thinking. Thinking about what, and why should you care?
I&rsquo;m not sure I know the answer to those questions any more.</p>
<p>What I do know, is that I want to go back to webstock next year, regardless of whatever else is going on in my life at the time. These words are coming out like treacle right now, I don&rsquo;t usually have a problem communicating like this.</p>
<p>Last night I said something that resonated with the people I was with. In a rare flash of brilliance, I said something that my friends were so affected by that they specifically mentioned it to others - &ldquo;You have to hear this, it was <del>amazing not shit 2deep5me</del> interesting, come on and talk to Alex about it&rdquo;. <em>That&rsquo;s not really something I&rsquo;m used to</em></p>
<p>So what did I say? And more importantly, <strong>What did I learn?</strong></p>
<p>What I said can wait until later, my exact words feel slippery, and at the edge of my vision right now. Rands said that writing helps teach how to order thoughts, and build opinions. He also said &ldquo;Just hit publish&rdquo; and he gave me this pen.</p>
<p><strong>So here are a few things that I learned at Webstock:</strong></p>
<ol>
<li>
<p>The speakers of webstock, are at the top of their game technically. These are the gods of of our industry. People breaking new ground in user interaction, CSS, responsive design and content, the works.
All these people, focused on the user, they focused on people. There were no java devs speaking at webstock this year (though I&rsquo;m sure many of them knew java). <strong>This is important</strong>, I&rsquo;ll address why it&rsquo;s important soon.</p>
</li>
<li>
<p>The attendees, were people from Spark Ventures, Xero, Trademe, Digital Arts Network, and a huge amount of other companies.
If the speakers were gods of the industry, the attendees were all minor deities, everyone who I spoke with was a leader in New Zealand - incredibly skilled developers, designers and others. What could we learn from the gods at webstock, that we couldn&rsquo;t learn more easily through a technical blog? Why bother with it all?</p>
</li>
<li>
<p>The attendees were mainly technical people, but not a single technical talk was given. This initially confused and upset me, before I gained time and context around webstock. This is webstock&rsquo;s 10th birthday, and that&rsquo;s also important.</p>
</li>
<li>
<p>In isolation, none of the talks had anything to do with each other, there were several talks that, at the time, felt like missed opportunities. Harry Roberts talked about making cocktails. Michael Lopp talked about pens and writing. Askew One talked about graffiti and growing up poor in Auckland. I was gutted.</p>
</li>
<li>
<p>When viewed through the lens that I stumbled on during the after party, every talk I had previously been disappointed in, changed. Remarkably so.
Askew didn&rsquo;t talk about growing up in Auckland, he didn&rsquo;t talk about the regional differences in graffiti. He said words about those things, but that&rsquo;s not the <em>meaning</em> that he was communicating. He taught us about community. About a group of people creating something bigger then themselves.</p>
</li>
</ol>
<p><strong>Keavy</strong> avoided all talk of her work at Github, because she was teaching us about identity. About who we are and who we want to be, and how that can and should change at different stages of our lives, in response to different stimuli. She spoke about growth, not of our technical skills, but of our minds as people. She spoke about the messy growth of flawed human beings, who get hurt, get better, get sick and well again, and how we can help ourselves grow in the direction we value the most.</p>
<p><strong>Debbie</strong> spoke on rejection. Here&rsquo;s a giant in her industry, one of the finest design minds out there, and she&rsquo;s telling us about how she got rejected from every postgrad school she applied to, about how she was ousted from industry organisations. But what she taught us was very different. She taught us about determination and doggedness. About refusing to be refused. About every failure in our lives being a defining factor in our eventual success.</p>
<p><strong>Harry</strong> could have spoken about CSS architecture, about his work as a consultant, about literally any technical topic he had on his mind. Instead, he crowd sourced his talk. He showed us how to trust in others, he showed us that there is more to life then a nine to five. He showed us that amazing people are literally everywhere, and all you have to do to see that is to flick a tweet out into the ether.</p>
<p><strong>Casey Gerald</strong>. I think I had began to link up these threads by the point that Casey spoke, that or he was the one who forced me to drag the threads and weave them in to rope. Of all the people I have ever heard speak, he is the most memorable. He was the last speaker at webstock and by far the most impactful. He&rsquo;s better then Petraeus, Obama, he&rsquo;s better then any actor or actress in any movie I&rsquo;ve ever seen. Every word held meaning to him, every moment, considered. It&rsquo;s the most surreal situation I&rsquo;ve ever been in. It was just.. right. I have never been as certain of anything in my life. But what he talked to us about, was doubt.</p>
<p>He talked about his upbringing, his education and his work, he detailed how each stage in his life was renewed by an earth shattering break in his beliefs. He taught us about doubt, about how examining a crack in our beliefs could shake the foundations about who we are as individuals, and who we are as a collection - whether that collection is a community, a nation, race or people. How could we not question everything? We live in a world where there is injustice, inequality and hate - but we equally live in a world where amazingly talented people care. Where people who can do something, do. Casey challenged us all to evaluate what we were doing was helping as many people as we could. He dedicated his professional life to helping people until he had the realisation that he couldn&rsquo;t save everyone, then he gave away everything he had built, so that we could all save each other. He leads by example.</p>
<p><strong>So what gives?</strong></p>
<p>Why should anybody give a shit and what epiphany of yours was it, that affected people the way it did?</p>
<p>The point of webstock this year, wasn&rsquo;t to teach us new technical skills. It wasn&rsquo;t to help us get ahead of the pack or beat the competition. The goals of webstock 2016 felt much loftier then that. Webstock 2016 aimed for nothing less then to fundamentally shift the way that we, the attendees, interacted with the world. We had the best of the best, the gods of our industry; people of such astounding intellectual brilliance that we looked up to them as personal heroes, just.. being human. Being good humans. Good humans talking about developing the traits that we want to see reflected in our world. These people helped us see that as an industry, we are in a position to change our own futures. Whether that&rsquo;s fighting global surveillance, addressing poverty and inequality, challenging and changing rape culture, domestic and sexual violence or just helping people be happier, healthier humans. It&rsquo;s incredibly humbling to think that the most valuable contribution these people made to our lives was to help us think differently. A personality request rather than a pull request.</p>
<p>So what exactly did I say? I don&rsquo;t know, but it went something like this:</p>
<p><strong>What they taught us transcends any one technology, methodology or practice. These speakers are specialists in technology, yes, but more then that, they are world leaders in interaction. They showed us new ways of interacting with people, new ways of thinking about how things interact with people. Any technical subject would have been mundane by comparison amongst talks that taught about no less then a fundamental shift in our own psyches.</strong></p>
<p>That sounds shit, and it doesn&rsquo;t capture what I said or the inebriated eloquence of the thought I put out there. But it&rsquo;s a start. And if there&rsquo;s one thing I&rsquo;ve learned this week, it&rsquo;s that starting is the hardest part. It&rsquo;s also the most worthwhile.</p>
]]></content:encoded></item><item><title>Overtime's Tech Stack</title><link>https://4lex.nz/posts/overtime-tech/</link><pubDate>Thu, 14 Jan 2016 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/overtime-tech/</guid><description>&lt;p&gt;Overtime New Zealand is a social video game league for corporates. Companies get together and play games against each other
for bragging rights, e-peen and other critically important reasons, like proving that you&amp;rsquo;re better at Dota 2 then X_MAIN_RIVAL_FIRM.&lt;/p&gt;
&lt;p&gt;So without Further ado, here&amp;rsquo;s what we&amp;rsquo;re running at the moment.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Technology Overview" loading="lazy" src="https://4lex.nz/img/in-post/overtime-stack.png"&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll go through our components technology by technology:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Most importantly, Docker&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We couldn&amp;rsquo;t do what we wanted to do with Overtime&amp;rsquo;s CI / CD without docker.
Initially I created docker-compose containers for instances of Docker Registry, Nginx, and MongoDB and our Web App. The plan was to containerise
those components because it would allow us to scale up instances of our web app and MongoDB in case our load got crazy high. An ambitious design requirement,
but much better for the long run. In fact nearly every design decision we&amp;rsquo;ve made technologically has been focused on &amp;ldquo;What will make our lives easier in the long term&amp;rdquo;.
We do this voluntarily so making money immediately and as quickly as possible isn&amp;rsquo;t such a huge issue for us! It&amp;rsquo;s nice.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Overtime New Zealand is a social video game league for corporates. Companies get together and play games against each other
for bragging rights, e-peen and other critically important reasons, like proving that you&rsquo;re better at Dota 2 then X_MAIN_RIVAL_FIRM.</p>
<p>So without Further ado, here&rsquo;s what we&rsquo;re running at the moment.</p>
<p><img alt="Technology Overview" loading="lazy" src="/img/in-post/overtime-stack.png"></p>
<p>I&rsquo;ll go through our components technology by technology:</p>
<p><strong>Most importantly, Docker</strong></p>
<p>We couldn&rsquo;t do what we wanted to do with Overtime&rsquo;s CI / CD without docker.
Initially I created docker-compose containers for instances of Docker Registry, Nginx, and MongoDB and our Web App. The plan was to containerise
those components because it would allow us to scale up instances of our web app and MongoDB in case our load got crazy high. An ambitious design requirement,
but much better for the long run. In fact nearly every design decision we&rsquo;ve made technologically has been focused on &ldquo;What will make our lives easier in the long term&rdquo;.
We do this voluntarily so making money immediately and as quickly as possible isn&rsquo;t such a huge issue for us! It&rsquo;s nice.</p>
<p>We had a bunch of technical issues with Nginx not liking being containerised and not linking to our other containers properly.
That resulted in us shifting it to the VPS itself and using it for reverse proxy and routing, it&rsquo;s working pretty nicely so far.</p>
<p>We run separate production and test environments. Local builds are automatically built and dockerised on commit,
are automatically deployed to our test environment with 0 loss of service. When we&rsquo;re ready to go to prod, we just retag
the docker image in question as prod rather than test. Docker picks up that there is a new image available and redeploys
in the same way as our test environment, meaning we also get 0 down time. We still plan our deployment windows for early
in the morning just in case something breaks but the process seems pretty solid so far.</p>
<p><strong>Our Web App</strong></p>
<p>Overtime uses a Java 8 back end with an Angular front end. Once the application is complete, players will be able to
register themselves and their teams, sign up for tournaments, track points, rankings tables identities, and communicate with
organisers and other teams. We have a dedicated slack team for overtime and we&rsquo;re planning on our players being automatically enrolled into slack
on sign up. I also want the enrollment process to add them in to relevant channels (they might get a private team channel
and enrollment into open game and interest channels) - but we&rsquo;re still thinking about how that would work!</p>
<p>We&rsquo;re using MongoDB because we really like it&rsquo;s object paradigm, it&rsquo;ll make it easier for us to manage our data long term.</p>
<p><strong>Speaking of building:</strong></p>
<p>We use Bamboo for our CI/CD work, initially we used Jenkins but found it unreliable, prone to random failures and difficult
to set up, maintain and extend. Bamboo is a much more user friendly solution. We had set up our build server as an additional
docker container on the first VPS but thinking about it, we wanted to ensure that development work could never impact production
environments. Because of the size of our VPS it was possible that our build container could cause production issues by hogging
up resources while building things. An edge case but with the focus on the long term we decided on separating that out on to it&rsquo;s
own VPS with its own resources.</p>
<p><strong>Project Management</strong></p>
<p>We use Taiga.io for project management. It has the option for sprint or kanban set ups, it&rsquo;s a hosted solution and it&rsquo;s free, which fits our budget!
Taiga is also open source which means that we can take it, self-host and extend it should we ever need to provided we comply with the licence, which is another bonus.</p>
<p><strong>Wrap Up</strong></p>
<p>That&rsquo;s all the detail I&rsquo;ll go in to for now. I hope this helped you, if you want any more detail or you&rsquo;d like help
with your own projects, let me know, I&rsquo;d love to see how I can help!</p>
]]></content:encoded></item><item><title>New Year Update</title><link>https://4lex.nz/posts/new-year-update/</link><pubDate>Fri, 08 Jan 2016 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/new-year-update/</guid><description>&lt;p&gt;It&amp;rsquo;s been a while between posts. Here&amp;rsquo;s Why:&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been on holiday for two weeks! It&amp;rsquo;s been pretty good.&lt;/p&gt;
&lt;p&gt;I spent a week in Ohakune, in the central Plateau of the North Island of Naw Zealand and another week in Russell, much
further north. It was awesome to get away from Auckland, but I&amp;rsquo;m ready to go home.&lt;/p&gt;
&lt;p&gt;I have spent my time organising my reading list for the upcoming year (scheduled for reading are gems such as Code Complete,
Continuous Delivery, The Fundamentals of Security and The Mythical Man Month). I&amp;rsquo;m excited, I&amp;rsquo;ve got about 60 books lined
up for the year and I can&amp;rsquo;t wait to get stuck in.&lt;/p&gt;</description><content:encoded><![CDATA[<p>It&rsquo;s been a while between posts. Here&rsquo;s Why:</p>
<p>I&rsquo;ve been on holiday for two weeks! It&rsquo;s been pretty good.</p>
<p>I spent a week in Ohakune, in the central Plateau of the North Island of Naw Zealand and another week in Russell, much
further north. It was awesome to get away from Auckland, but I&rsquo;m ready to go home.</p>
<p>I have spent my time organising my reading list for the upcoming year (scheduled for reading are gems such as Code Complete,
Continuous Delivery, The Fundamentals of Security and The Mythical Man Month). I&rsquo;m excited, I&rsquo;ve got about 60 books lined
up for the year and I can&rsquo;t wait to get stuck in.</p>
<p><strong>The Christie Marceau Charitable Trust</strong></p>
<p>I&rsquo;ve also spent a good deal of time helping out with the trust. The woman who runs it is a good friend of my mother in law.
The trust needed a refresh of their website and to consolidate the digital presence they had.</p>
<p>They were paying $300 a year
for a bunch of services they didn&rsquo;t need (part of the package they had gave them 50 databases! 50 databases and one website</p>
<ul>
<li>what a hilarious way of making it seem like your product represents value for money to those who don&rsquo;t know any better.
Honestly the state of web hosting in New Zealand makes me feel angry. So many people who don&rsquo;t know better are getting fleeced
by these companies who, at the end of the day, provide a poor service simply because competition in NZ is so abysmal).</li>
</ul>
<p>/rant</p>
<p>Anyway, I rewrote the trust website into an angular site with a bunch of content, realised it was as ugly as sin and enlisted
the help of a talented designer named <a href="walterlim.co">Walt</a>. He helped me out with the design, I cancelled the trust&rsquo;s old
web services package and the new setup is super simple. I ditched Angular, hosted the page on github pages (TY GITHUB)
and now the trust only needs to pay for the domain name every 2 years. It&rsquo;s a solution much better suited to the actual needs
of the trust and I&rsquo;m glad to have been able to tailor a solution for them. It&rsquo;s a timely reminder that what is simple for
people in the tech industry often isn&rsquo;t for people outside the industry. A timely reminder for me to stay humble and focus
on how I can help people rather than how I can make as much cash as quickly as possible and screw the needs of the person
I&rsquo;m representing (looking at you Free Parking ಠ_ಠ). The new website is fairly basic at the moment but it&rsquo;s functional and
thanks to Walt, it look good. I&rsquo;ll expand on it content wise throughout the year, I hope to post fairly regular updates
on it though that will depend on the trust having news to share!</p>
<p><strong>Overtime Tech Talk</strong></p>
<p>I&rsquo;ve taken some time out to draft a post and presentation on Overtime and how exactly our technology fits together, it&rsquo;s
still in the works but for those that care, I haven&rsquo;t forgotten! I promise!</p>
<p>That&rsquo;s it for now, time to hit the beach!</p>
<p><img alt="Russell Waterfront" loading="lazy" src="/img/in-post/beach.jpg"></p>
]]></content:encoded></item><item><title>Change</title><link>https://4lex.nz/posts/change/</link><pubDate>Fri, 18 Dec 2015 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/change/</guid><description>&lt;p&gt;Oh the times, they are a changin'&lt;/p&gt;
&lt;p&gt;The past month has been crazy.&lt;/p&gt;
&lt;p&gt;In the past month, I&amp;rsquo;ve started a new role, my son has started crawling, I&amp;rsquo;ve helped launch the second season of
Overtime on a brand new technical stack, they only thing that hasn&amp;rsquo;t changed is this blog, I&amp;rsquo;ve been neglecting it.
Work has slowed down a bit before christmas, so now seems like a good time to post an update.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Oh the times, they are a changin'</p>
<p>The past month has been crazy.</p>
<p>In the past month, I&rsquo;ve started a new role, my son has started crawling, I&rsquo;ve helped launch the second season of
Overtime on a brand new technical stack, they only thing that hasn&rsquo;t changed is this blog, I&rsquo;ve been neglecting it.
Work has slowed down a bit before christmas, so now seems like a good time to post an update.</p>
<p><strong>Leaving Spark Ventures</strong></p>
<p>I left Spark Ventures after working there for a year. They supported me in the first full time role as a software
developer that I&rsquo;ve ever had, I&rsquo;m extremely grateful for the opportunities they gave me, and I&rsquo;ll miss the team and the work.
I left on great terms and (if it&rsquo;s in the cards) I look forward to returning in a more senior role. Before I left, my
team was working on continuous integration and deployment. We successfully transitioned Bigpipe from a completely
manual deployment process to an automatic one. It was a really enjoyable task, and once it was complete, I wanted to
keep doing that kind of work.</p>
<p><strong>Enter Healthlink, stage left.</strong></p>
<p>I was made aware of an opportunity at Healthlink to continue doing work like the work I did for Bigpipe. The situation
here at healthlink makes the Bigpipe automation simple! Healthlink runs a product that enables health practitioners to
send documents - referrals, letters, medical certificates, test results and so on - digitally between providers. It&rsquo;s
the reason that you can get a non-funded drug approved by Pharmac in three days rather than three weeks. It&rsquo;s behind
your rural doctor getting results back from that specialist lab in the big city as soon as the results are done - without
having to wait for the rural post service to deliver the mail.</p>
<p>It&rsquo;s an complex system of java applications, web servers, auth servers, databases and APIs that have got loads of tightly
coupled moving parts. I&rsquo;m helping them work through that system with a view to achieving CI/CD - completing this process
will hopefully reduce the degree of coupling, consolidate technologies and generally refresh the tech too. It&rsquo;s the
biggest job I&rsquo;ve ever attempted, and I&rsquo;m glad that I&rsquo;ll be working alongside some incredibly clever people - all of
whom will be able to teach me a great deal.</p>
<p><strong>Overtime Season 2</strong></p>
<p>Overtime season 2 has launched!</p>
<p>We&rsquo;ve completely rebuilt out tech stack! There&rsquo;ll be another post coming with the specifics on what we did and how we did
it, as well as some challenges we had to overcome. We&rsquo;re looking at returning to the NZ eSports scene bigger and better
then ever before, and I think we&rsquo;ll start to see some traction this season. We&rsquo;re more organised, more experienced and have
better technologies behind us. The team is expanding - more people working on more things mean we can just do.. more!</p>
<p>Watch that space, season 2 is going to be excellent!</p>
<p><strong>Toby has started crawling!</strong></p>
<p>I&rsquo;m pretty proud of that little guy. He&rsquo;s ahead of schedule for all of his developmental milestones, he was born on the wrong
end of the scales for average head size, body size, body weight and a bunch of other stuff. At 8 months old, he&rsquo;s now in the
97th percentile for head size (aka big ol&rsquo; brain) and is above the average for his other growth indicators. My wife is doing
a great job being a mum while I have to leave for work every day. I&rsquo;m very proud of them both.</p>
<p><strong>Things are looking pretty positive.</strong></p>
<p>I&rsquo;m in the proess of writing a whole bunch of posts, and will be hopefully publishing a bit over christmas. TTYL. Beer to drink.</p>
]]></content:encoded></item><item><title>Continuous Delivery</title><link>https://4lex.nz/posts/ci-cd/</link><pubDate>Wed, 28 Oct 2015 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/ci-cd/</guid><description>&lt;p&gt;The work that I&amp;rsquo;ve been doing for the past few months has been pretty interesting. At the moment it takes developers working
on one of our ventures about an hour and a half to two hours to deploy code to a test or prod machine. It used to take
a second venture a similar amount of time to do the same task, but the process we&amp;rsquo;ve implemented for them has reduced that
significantly. Once the process is completed across both ventures, our team will have reduced that deployment time enough,
that the product owners for those ventures will effectively gain 20% more points for work per sprint.&lt;/p&gt;</description><content:encoded><![CDATA[<p>The work that I&rsquo;ve been doing for the past few months has been pretty interesting. At the moment it takes developers working
on one of our ventures about an hour and a half to two hours to deploy code to a test or prod machine. It used to take
a second venture a similar amount of time to do the same task, but the process we&rsquo;ve implemented for them has reduced that
significantly. Once the process is completed across both ventures, our team will have reduced that deployment time enough,
that the product owners for those ventures will effectively gain 20% more points for work per sprint.</p>
<p><em><em>That&rsquo;s a pretty huge productivity increase</em></em></p>
<p>Especially given that it was two juniors and two seniors across three months that did this work.</p>
<p>We&rsquo;re not done yet, we&rsquo;ve still got some UAT and final bits and pieces to deal with, but I&rsquo;m proud to say that we&rsquo;re mostly there.</p>
<p><strong>Here&rsquo;s a basic run down of how we did it:</strong></p>
<p>We chose Atlassian&rsquo;s Bamboo as our general purpose task runner. It&rsquo;s the backbone of our process. We chose it because
it&rsquo;s got a nice interface, awesome integration with Bitbucket (which all of our repos are hosted on) as well as Jira and Confluence.</p>
<p>It can provide us with automatically generated information about the issues addressed by a deployment, link back to repos if we need
to sanity check source code and a bunch of other cool stuff. We&rsquo;ve also added webhooks to our deployment channel within Slack, so
Bamboo posts alerts to that channel when a deployment completes.</p>
<p>We&rsquo;re stuck with a bunch of technologies that aren&rsquo;t being used correctly (Like using Puppet to manage individual machines
rather than types of machines) and Bamboo is going to give us freedom to swap out discrete pieces of the process as and when we need.</p>
<p>We&rsquo;ve got a bunch of scripts and SSH tasks that reach out to our repos, check file versions, run puppet agents (yes, manually running rather than every n minutes),
stop queues, dump databases for backup purposes, start queues and all sorts of other nebulous tasks.</p>
<p>The end result is that to build a venture project, all a developer needs to do is commit code. To cut a release they need
to select a branch to build. To deploy to a dev, test or staging environment they need to select a build result and click deploy, same deal for pre-prod and prod (with a few additional caveats).</p>
<p><strong>The great thing is, the teams are onboard with it.</strong></p>
<p>This is a huge amount of disruption to the way 80% of our team does business. It affects developers, testers, BAs and product owners.
Without exception, everyone that I&rsquo;ve talked to is pumped. It&rsquo;s a credit to the guys here that they aren&rsquo;t stuck in a process but are always looking
to improve. It makes me happy.</p>
]]></content:encoded></item><item><title>Design and Content</title><link>https://4lex.nz/posts/design/</link><pubDate>Mon, 19 Oct 2015 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/design/</guid><description>&lt;p&gt;Thoughts on Design.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m losing faith in the design of my website. It looks dated. I&amp;rsquo;m not really sure what to do with it other then give it an overhaul.
But that requires coming up with a new look and feel. It requires implementing that. But muh effort, Muh time constraints and other such excuses of the weak willed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I&amp;rsquo;m going to learn about design.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Rather then just minimally modifying a template, I&amp;rsquo;m going to look at every piece of this website and give it some love and attention. It&amp;rsquo;s been quite neglected.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Thoughts on Design.</p>
<p>I&rsquo;m losing faith in the design of my website. It looks dated. I&rsquo;m not really sure what to do with it other then give it an overhaul.
But that requires coming up with a new look and feel. It requires implementing that. But muh effort, Muh time constraints and other such excuses of the weak willed.</p>
<p><strong>I&rsquo;m going to learn about design.</strong></p>
<p>Rather then just minimally modifying a template, I&rsquo;m going to look at every piece of this website and give it some love and attention. It&rsquo;s been quite neglected.</p>
<p>I want to focus on creating something that is minimal, contemporary and clean. I like white space, I like strong borders and well defined blocks of content. Turning that into an actual design will be interesting!
Until I know more about what I want to achieve and how to get there, I might have to be content with making a few structural changes and a few CSS tweaks here and there.</p>
<p><strong>Project Updates are important.</strong></p>
<p>I need to go through the groups within my site and update them in terms of content as well as design. I&rsquo;ve done a bit of work, had a bit of a play with different technologies, as well as submitting some interesting university assignments.
I need to throw that work up on to the projects section so it can be consumed!</p>
<p><strong>My skills and Experiences have also changed.. slightly</strong></p>
<p>I should update that section to reflect the work I&rsquo;ve been doing recently at Spark Ventures around continuous integration and deployments.
It&rsquo;s technically interesting, requires technical nouse, involves communicating with dev teams, creating and changing work flows and managing the expectations of the product teams, development teams and bigger fish higher up in the Spark Ventures food chain. It&rsquo;s been fun.</p>
<p><strong>With the publication of this post, I&rsquo;ve changed the font from Junction Regular to San Francisco - because I love the OS X El Capitan font that much.</strong></p>
<p>What a difference that makes:
<img alt="Junction vs San Francisco" loading="lazy" src="/img/in-post/junctionVSsanfran.png"></p>
]]></content:encoded></item><item><title>The Internship Experience Part II</title><link>https://4lex.nz/posts/the-internship-experience-part-ii/</link><pubDate>Sat, 10 Oct 2015 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/the-internship-experience-part-ii/</guid><description>&lt;p&gt;In hindsight I feel like I focussed more on the soft skills needed to get an internship and how to actually go about it, so this time I&amp;rsquo;ll provide a bit of insight into the actual technical skills that I feel you need (or I need, at least I need while I&amp;rsquo;m here at Spark)&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s be clear right at the start, I have used maybe 15% ~ 20% at best, of the knowledge I learned at University. Most surprising of all is that most of what I have used, came from a stage one course. Yeah that course in HTML that everybody turns their noses up at because it&amp;rsquo;s for plebians who can&amp;rsquo;t into C? Take it. You&amp;rsquo;ll earn dollars with that course.&lt;/p&gt;</description><content:encoded><![CDATA[<p>In hindsight I feel like I focussed more on the soft skills needed to get an internship and how to actually go about it, so this time I&rsquo;ll provide a bit of insight into the actual technical skills that I feel you need (or I need, at least I need while I&rsquo;m here at Spark)</p>
<p>Let&rsquo;s be clear right at the start, I have used maybe 15% ~ 20% at best, of the knowledge I learned at University. Most surprising of all is that most of what I have used, came from a stage one course. Yeah that course in HTML that everybody turns their noses up at because it&rsquo;s for plebians who can&rsquo;t into C? Take it. You&rsquo;ll earn dollars with that course.</p>
<p>Speaking of courses, I&rsquo;m in my second year of Uni, when I started with <a href="https://summeroftech.co.nz" target="_blank" rel="noopener noreferrer">Summer of Tech</a>

 I was the second semester of my first year, by the time the program wound down and internship offers were out I had completed my third semester - which brings me up to date as of writing this (I&rsquo;m not taking any summer school papers - I&rsquo;m not smart or beautiful enough to get away with that).</p>
<p>I&rsquo;m doing a double major in Geography and Computer Science, and for those of you at the start of your studies, I would recommend picking up a second major, it gives your more flexibility in your degree, looks better on a CV and to be honest, it lets you avoid the courses in each school that don&rsquo;t interest you or have bad reps for being poorly run. My degree consists of courses that I 100% love and am interested in, which makes it a lot easier to learn the content. Don&rsquo;t hate your study, change it.</p>
<p>For those that want the nitty gritty of how useful each particular course has been, here&rsquo;s a breakdown for you. Please note that I&rsquo;m trying to keep it institution agnostic, so people who aren&rsquo;t at the same place as me can still understand the topics I&rsquo;ve covered and hopefully still get some value out of this post.</p>
<p>On the CompSci side of my degree, I&rsquo;ve studied all the level one papers available, they are pretty generic so they don&rsquo;t get named individually - they contained the basic theories of CompSci, the history of the field, an introduction to Java (though I understand they have now changed from Java to Python) and an introduction to data structures.</p>
<p>At stage two, so far I&rsquo;ve taken courses in Discrete Mathematics (I was tricked into it, the course code said CS but should have been MATH), Algorithms and Data Structures, Software Construction, and Low Level Programming.</p>
<p>Software construction focussed on Java GUIS and threading, which haven&rsquo;t been so useful for me here thus far, there was a small module on testing and a small module on version control though, and both of those were invaluable. Many people turn their noses up at testing, but it&rsquo;s actually one of the most critical aspects of shipping software. If your code doesn&rsquo;t work, you don&rsquo;t (or shouldn&rsquo;t) ship and you don&rsquo;t get paid (Except if you&rsquo;re a Triple A Game Publisher - I&rsquo;m looking at you BF4 / AC:Unity).</p>
<p>Low level programming covered computing from Transistors up through to the C programming language. It&rsquo;s a useful course in that it gave me a background in how computers actually physically compute, and therefore how to make things as efficient as possible, and why that&rsquo;s important.</p>
<p>Algorithms and Data structures is a hard course, but also valuable in it&rsquo;s own way. the knowledge there is the kind of knowledge you forget you need, and then you look at your code one day and think why is this so buggy and horrible and slow? Oh wait it&rsquo;s because I somehow have made a triply nested loop where each iteration of each loop calls a recursive function. That&rsquo;s probably hyperbole. But I am certainly much more aware of the way I structure code and solve problems now. So that&rsquo;s something. I also learned search, I haven&rsquo;t used it for work yet, but in one of the games I&rsquo;m working on in my spare time, I use search, so that stuff is good to know.</p>
<p>As I alluded to earlier, discrete mathmatics, although billed and coded as a CompSci course, was actually</p>
<p>taught by math staff and was a maths course in disguise. It&rsquo;s one of those courses where you don&rsquo;t notice yourself using it until you find yourself trying to prove that some graph has some property and therefore is a better choice as a data structure than some other structure. I&rsquo;m loathe to admit that it was a decent course because I don&rsquo;t like, nor am I good at math. I&rsquo;m probably in the wrong industry, I know.</p>
<p>Learn a version control system and become very familliar with it.</p>
<p>It&rsquo;s an unwritten prerequisite that you know Git or some other version control system before you get into the workforce. If you don&rsquo;t know what I&rsquo;m talking about, at Ventures here we use <a href="https://git-scm.com/" target="_blank" rel="noopener noreferrer">Git</a>

. I also ended up learning <a href="https://www.gnu.org/software/bash/" target="_blank" rel="noopener noreferrer">Bash</a>

. Linux users you should have no issue with this, OS X also has bash. Windows users, it&rsquo;s a bit wierd at first but once you get used to it, it makes life a lot easier (I actually find that I can do things in bash via command line faster then I can do things using a GUI now.)</p>
<p>Take the time to do your own projects, it&rsquo;s a great way to learn something new. It was probably my dabbling in Javascript and html - and the projects I did - rather than my somewhat incomplete knowledge of University level Computer Science that helped me more in getting this position</p>
<p>Don&rsquo;t forget to go outside. I&rsquo;m real bad for that.</p>
]]></content:encoded></item><item><title>The Internship Experience</title><link>https://4lex.nz/posts/the-internship-experience/</link><pubDate>Fri, 09 Oct 2015 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/the-internship-experience/</guid><description>&lt;p&gt;This is an article I wrote for Spark Ventures I&amp;rsquo;m currently Interning for them) on how I got my internship, and how others can break into the IT Industry. My writing style still isn&amp;rsquo;t great, so it was good to be given an excuse to practice!&lt;/p&gt;
&lt;p&gt;=================================&lt;/p&gt;
&lt;p&gt;I have loved technology ever since I was a wee nipper. I confused a lot of people when I joined the Army after college but that decision was key (more on that another time though). I left the Army in time to enroll in Semester Two 2013 at the University of Auckland. I sit here now, on the top floor of Spark City in Auckland, and I feel very, very fortunate.&lt;/p&gt;</description><content:encoded><![CDATA[<p>This is an article I wrote for Spark Ventures I&rsquo;m currently Interning for them) on how I got my internship, and how others can break into the IT Industry. My writing style still isn&rsquo;t great, so it was good to be given an excuse to practice!</p>
<p>=================================</p>
<p>I have loved technology ever since I was a wee nipper. I confused a lot of people when I joined the Army after college but that decision was key (more on that another time though). I left the Army in time to enroll in Semester Two 2013 at the University of Auckland. I sit here now, on the top floor of Spark City in Auckland, and I feel very, very fortunate.</p>
<p>I&rsquo;m Alex, an intern Software Developer for Spark Ventures over the Summer of 2014 (and hopefully after that also). It&rsquo;s a tough job breaking into a new industry, but worry not, I&rsquo;m going to tell you how I did it, and how you can do it too.</p>
<p>The work here at Spark is awesome, just this morning I designed and implemented a web page that leverages social media to increase engagement with customers. I&rsquo;ve dabbled in web stuff before but this is an actual product that will may go live at some stage, it&rsquo;s a cool feeling. A fellow intern is working on automating some work processes we have here, to save time and tears (a lot of tears in my case) and this is only our second week. I&rsquo;m looking forward to the rest of summer and the sweet projects we&rsquo;ll be working on.</p>
<p>I got involved in an excellent program called <a href="https://summeroftech.co.nz" target="_blank" rel="noopener noreferrer">Summer of Tech</a>

, they run boot camps and workshops on all sorts of stuff that industry needs but Uni doesn&rsquo;t teach you. They run meet and greets and interviews with companies, they organised a day long hack-fest where teams of students get together and work on projects to win prizes and impress industry reps, it&rsquo;s a great program. If you are in Auckland or Wellington, get involved ASAP.</p>
<p>It&rsquo;s not the only way, but they certainly took a lot of the leg work out of the equation for me. Summer of Tech made it about my skills and experiences, and how I interacted with companies, rather then how good I am at cold calling or finding opportunities. Summer of Tech put the opportunities in front of me, all I had to to was grab them.</p>
<p>Plenty of big companies offer internships, those not involved in Summer of Tech often have their own programs, I went to a few of those, you should too. Friends of mine have got internships with Deloitte, PwC and the Wynyard Group, they did it through company recruitment events, not Summer of Tech.</p>
<p>Put yourself out there, if you see an opportunity that isn&rsquo;t quite what you were after, who cares? Take it anyway. Learn something new. Apply for everything you can, be greedy, say yes. People remember those positive types who say yes to everything. Managers want to look good right? Well managers will remember you if they think you&rsquo;ll make them look good, and if you make them look good, they&rsquo;ll remember that and repay you the kindness.</p>
<p>Work hard, strive for excellence, again, make your manager look good, or make that potential manager think you&rsquo;ll do such great stuff that you&rsquo;ll both look good.</p>
<p>Know your field. It&rsquo;s hard to make your manager look good if you don&rsquo;t know how to do your job. You want a role in the IT industry, you need to know about the tools and procedures that get used in the industry, it&rsquo;s that simple.</p>
<p>Be a decent human being. Arrogance, Rudeness, Idleness, these traits will make you stand out for the wrong reasons. Be mindful of that. I&rsquo;ve spoken to people who feel that if they have an 8 or 9 point GPA, industry should be seeking them out, not them seeking out industry. Grades are important, but it&rsquo;s more important to be able to function in a team environment. You need not be party central, you need not be a superheroic extrovert and the center of attention (I&rsquo;m certainly none of those things!) but you do need to be able to form meaningful relationships. You need to be able to leave the right impressions on people, for the right reasons. So when you meet people, wear deodorant or anti-perspirant. Shower. Clean your teeth. Dress appropriately. All those good things mum and dad told you.</p>
<p>Your ability to form relationships and communicate, and your ability to learn new skills will get you a job. Taking your waifu pillow (Dakimakura?) to the interview will not. I&rsquo;m not saying don&rsquo;t be into that stuff, what you do at home is neither my business nor an employers&rsquo;. All I&rsquo;m saying is be professional while meeting professionals.</p>
<p>Seriously guys, I can&rsquo;t stress this enough. You can have a ridiculous GPA, but if you can&rsquo;t communicate or form relationships, it&rsquo;s going to make your life a lot harder then it needs to be (<a href="https://summeroftech.co.nz" target="_blank" rel="noopener noreferrer">Summer of Tech</a>

 does workshops on soft skills like interviewing and meeting people by the way). If you take nothing else away from this post, take the above two paragraphs.</p>
<p>TL;DR, How do I into internships<br>
1 Join Summer of Tech or go to Company Recruiting Events<br>
2 Be cool, be good at what you do.<br>
3 Put your hand up. Say yes to every opportunity that comes your way, the worst that can happen is nothing.<br>
4 Follow up with recruiters<br>
5 ????<br>
6 Profit (Literally, they pay you to do what you love)</p>
<p>I got where I am today with hard work, a bit of stubbornness and some luck. I can&rsquo;t implement Djikstra&rsquo;s algorithm in assembly language, but I can interact with people -ish. You can too. The results are always worth it and there is never a better time to start then now. If you have any questions you feel free to hit me up on twitter (<a href="https://twitter.com/4lexNZ" target="_blank" rel="noopener noreferrer">@4lexNZ</a>

) or this website, either by email on the main page, or by dropping me a comment below.</p>
]]></content:encoded></item><item><title>The Inaugural Blog Post</title><link>https://4lex.nz/posts/the-inaugural-blog-post/</link><pubDate>Thu, 08 Oct 2015 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/the-inaugural-blog-post/</guid><description>&lt;p&gt;Hi there,&lt;/p&gt;
&lt;p&gt;My name is Alex. I&amp;rsquo;ve no idea if anybody will read this, it&amp;rsquo;s more for my own sanity anyway so that&amp;rsquo;s fine!&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m a student at the University of Auckland, currently working over summer as an intern for Spark Ventures. I&amp;rsquo;m loving being back in a real job. Don&amp;rsquo;t get me wrong, University is great, but the amount of time I wasted every day sleeping in until 11am because I didn&amp;rsquo;t have class until noon, was ridiculous.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Hi there,</p>
<p>My name is Alex. I&rsquo;ve no idea if anybody will read this, it&rsquo;s more for my own sanity anyway so that&rsquo;s fine!</p>
<p>I&rsquo;m a student at the University of Auckland, currently working over summer as an intern for Spark Ventures. I&rsquo;m loving being back in a real job. Don&rsquo;t get me wrong, University is great, but the amount of time I wasted every day sleeping in until 11am because I didn&rsquo;t have class until noon, was ridiculous.</p>
<p>I hope to populate this blog with revision notes from my classes, once classes start back up, as well as passing comment on anything and everything, as and when I see fit.</p>
<p>This is the first post so I will end it here, with this sweet picture of a a place near and dear to my heart.</p>
]]></content:encoded></item><item><title>Summer of Tech Meet and Greet</title><link>https://4lex.nz/posts/summer-of-tech-meet-and-greet/</link><pubDate>Mon, 07 Sep 2015 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/summer-of-tech-meet-and-greet/</guid><description>&lt;p&gt;Next week on Tuesday, the Summer of Tech Meet and Greet will be going down range. I&amp;rsquo;m looking forward to it!
It&amp;rsquo;s going to be interesting to be attending this year&amp;rsquo;s meet and greet on the other side of the fence. Last year I attended
as a prospective intern, it was a loud, chaotic and hectic environment. I feel like it suited me well!&lt;/p&gt;
&lt;p&gt;This time I&amp;rsquo;m going to be looking at it from the Employer perspective. I&amp;rsquo;ve been handling Spark Venture&amp;rsquo;s Summer of Tech efforts
this year as well as dealing with sponsorship stuff, running bootcamps and of course, running the Hackfest (that we held at the
awesome Spark City Conference center)&lt;/p&gt;</description><content:encoded><![CDATA[<p>Next week on Tuesday, the Summer of Tech Meet and Greet will be going down range. I&rsquo;m looking forward to it!
It&rsquo;s going to be interesting to be attending this year&rsquo;s meet and greet on the other side of the fence. Last year I attended
as a prospective intern, it was a loud, chaotic and hectic environment. I feel like it suited me well!</p>
<p>This time I&rsquo;m going to be looking at it from the Employer perspective. I&rsquo;ve been handling Spark Venture&rsquo;s Summer of Tech efforts
this year as well as dealing with sponsorship stuff, running bootcamps and of course, running the Hackfest (that we held at the
awesome Spark City Conference center)</p>
<p>The Meet and Greet is competitive. Students want to impress the best employers, and likewise, employers want to imprint themselves on the minds
of students as &ldquo;The place to be&rdquo;.</p>
<p>Last year, I would have found it useful to know my way around the process, in the end I just drank beer and talked shit, and that was good enough - depending on who you ask :P</p>
<p>Firstly - Talk to everyone. Talk to every single employer in the room, regardless of whether or not you want a job with them. For one, it&rsquo;s great practice.
Communication sklls are the API that all of your other, more technical skills are exposed via. You can&rsquo;t expect employers to spend money on you (onboarding is expensive!)
without them being confident that you have the skills needed. If they can&rsquo;t see or verify those skills, you may as well not have them. I beat students last year who
were a lot more talented, hardworking and conscientious then me, because I could more readily communicate. It&rsquo;s not fair, but it&rsquo;s how the world works (there are always exceptions mind you).</p>
<p>Secondly - Don&rsquo;t get too drunk. If you&rsquo;re as fond of the gin as I, it might pay to watch your intake. It&rsquo;s exciting, it&rsquo;s fast paced, it&rsquo;s a competition.
It&rsquo;s easy to lose track of the little things (by that I mean, it&rsquo;s easy to lose track of everything that isn&rsquo;t related to talking to employers).
If you&rsquo;re the type of person that can&rsquo;t stretch a drink out over a few hours, consider banning yourself from it until the end of the night. Personally, I thought of it as a reward system,
Every time I did a circuit of the employers present, after having had meaningful 5m+ long conversations with them, I would reward myself with one low alcohol content beer.
Another way to think of it is like this, if you&rsquo;re drinking enough to feel more then a good buzz, your lips are spending too much time on the end of a bottle, and not enough time on the
end of a conversation.</p>
<p>TL:DR - Be cool, manage your intake, don&rsquo;t be that guy. You know the one.</p>
<p>Finally, enjoy it. When dealing directly with people who could give you a job, it&rsquo;s easy to feel the pressure. Try to focus on having great conversations, and enjoy the time with your fellow
students, and employers who are passionate about the industry they are in. Without fail, every single employee in that room is there because they want to be, because they love what they do,
love the industry they work in and love what Summer of Tech does. Enjoy yourself, the best conversations happen when you&rsquo;re happy and comfortable, not when you&rsquo;re forcing yourself to say words
for the sake of somebody else. It&rsquo;s about you. Own it, enjoy it. I can&rsquo;t stress that point enough.</p>
]]></content:encoded></item><item><title>The First Post on a new Blog Stack!</title><link>https://4lex.nz/posts/first-post-jekyll-blog/</link><pubDate>Tue, 25 Aug 2015 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/first-post-jekyll-blog/</guid><description>&lt;p&gt;This now constitutes v3.0 of my personal website and sounding board. It&amp;rsquo;s been a long journey from some
bloated and slow bootstrap template that I picked up and cannibalised, to a bespoke website written in Pure CSS with a
Wordpress Blog component - to the current (as at 25 Aug 15) iteration. Currently the site is hosted off github pages.
It&amp;rsquo;s Jekyll with Foundation 5 at the core, I&amp;rsquo;m using markdown and liquid to handle blog posts.&lt;/p&gt;</description><content:encoded><![CDATA[<p>This now constitutes v3.0 of my personal website and sounding board. It&rsquo;s been a long journey from some
bloated and slow bootstrap template that I picked up and cannibalised, to a bespoke website written in Pure CSS with a
Wordpress Blog component - to the current (as at 25 Aug 15) iteration. Currently the site is hosted off github pages.
It&rsquo;s Jekyll with Foundation 5 at the core, I&rsquo;m using markdown and liquid to handle blog posts.</p>
<p>Gulp is what I&rsquo;m using for builds, the output of gulp&rsquo;s build process is piped into a folder that I&rsquo;ve set up as a
nested github profile. BrowserSync handles all of my testing and content iteration, it&rsquo;s amazing. To get a post onto
the live site or make some other change, all I have to do it write it up, check it looks right using BrowserSync and
then <code>git commit -am &quot;Of course with a sweet commit message&quot;</code> from my <code>prod</code> folder. Yes there are extra steps that I&rsquo;ve
introduced, but that allows me to make big changes with multiple commits, iterate and push to test as much as I like,
without the risk of <code>omnishambling</code> my live site. It&rsquo;s pretty cool.</p>
<p>Technical things aside, my boy Toby is nearly 5 months old. Time flies. I&rsquo;m working full time at Ventures, it&rsquo;s awesome.
Super glad that I did the <a href="https://www.summeroftech.co.nz" target="_blank" rel="noopener noreferrer">Summer of Tech</a>

 programme when I did, else I&rsquo;d still be
working at the liquor store. That would be a grim existence indeed.</p>
<p>I am <em>technically</em> still at Uni. The longer I work in the tech industry though, the less relevant Uni becomes and thus
the more difficult it becomes to stay engaged in my studies - especially when <em>still</em> no course I&rsquo;ve done past CS111G
has actually been useful for work. It&rsquo;s maddening.</p>
<p>It could be a lot worse though, my amazing son and beautiful wife are keeping me honest, in an industry I love.
<strong>First World Problems indeed</strong></p>
]]></content:encoded></item><item><title>Blood Donations</title><link>https://4lex.nz/posts/blood-donations/</link><pubDate>Wed, 27 May 2015 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/blood-donations/</guid><description>&lt;p&gt;I&amp;rsquo;m donating blood today. It&amp;rsquo;ll be my 20th donation. Probably around 4 were whole blood, another 6 were plasma, the remaining 10 being apheresis aka platelet donations. I&amp;rsquo;m O- so my blood is quite useful, it can be given to anyone. Mostly it goes to newborns.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s always been a pretty cool thing for me, but when my own little one was born it made things quite a bit more important. Tobias was born premature due to a rare condition my wife had. He narrowly avoided the neo-natal intensive care unit (a few days earlier or a few tens of grams less and he would have gotten a sweet incubator)&lt;/p&gt;</description><content:encoded><![CDATA[<p>I&rsquo;m donating blood today. It&rsquo;ll be my 20th donation. Probably around 4 were whole blood, another 6 were plasma, the remaining 10 being apheresis aka platelet donations. I&rsquo;m O- so my blood is quite useful, it can be given to anyone. Mostly it goes to newborns.</p>
<p>That&rsquo;s always been a pretty cool thing for me, but when my own little one was born it made things quite a bit more important. Tobias was born premature due to a rare condition my wife had. He narrowly avoided the neo-natal intensive care unit (a few days earlier or a few tens of grams less and he would have gotten a sweet incubator)</p>
<p><img alt="Incubator" loading="lazy" src="https://www.hiwtc.com/photo/products/20/01/16/11621.jpg"></p>
<p>We got results back from his umbilical cord and placenta analysis - turns out there was a blood clot in the cord. I imagine if the docs new that, he would have been whipped out via c-section the second we presented to the hospital. He should have been stillborn, all things considered, Laura could have died if a part of the clot broke off and got into her system. Fucking unreal. We&rsquo;re very lucky. Miraculously so. The odds were super against us.</p>
<p>Which is why now, more then ever, I&rsquo;m motivated to ignore a lot of baggage and more than a little bit of fear, in order to crack on and get stuck with big needles. Because people need blood, my wife and child nearly did, others sure as hell do. During her hospital stay she wasn&rsquo;t the sickest person on the ward, nor was Toby.</p>
<p>Shit&rsquo;s important yo. Go donate. Each donation could save the lives of 3 people. What&rsquo;s a bit of fear and discomfort compared with the lives of 3 people?</p>
]]></content:encoded></item><item><title>Update 2 :: I need sleep edition</title><link>https://4lex.nz/posts/update-2-i-need-sleep-edition/</link><pubDate>Mon, 04 May 2015 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/update-2-i-need-sleep-edition/</guid><description>&lt;p&gt;On Thursday my son, Tobias, will be one month old.&lt;/p&gt;
&lt;p&gt;The days are getting longer but the weeks are getting shorter!&lt;br&gt;
I&amp;rsquo;ve been neglecting this platform, but haven&amp;rsquo;t forgotten about it.&lt;br&gt;
At Uni, exams times are approaching, I&amp;rsquo;ll be posting all of my revision notes on the blog - to keep me honest and keep everything searchable.&lt;/p&gt;
&lt;p&gt;In the time between posts I&amp;rsquo;ve been crazy busy. Juggling Uni with work and having a son, while still doing pet projects for my own learning. A while back a few friends and I started something called overtime, It&amp;rsquo;s a very exciting project - &lt;a href="https://web.archive.org/web/20170406084608/https://overtime.nz/"&gt;www.overtime.nz&lt;/a&gt;. I also joined an ARMA 3 Clan called SOCOMD, I&amp;rsquo;m rewriting the website and blog they use on my own time, and they&amp;rsquo;re very pleased with what I&amp;rsquo;ve done so far. Check it out at &lt;code&gt;www.socomd.azurewebsites.net&lt;/code&gt;. Another very cool project.&lt;/p&gt;</description><content:encoded><![CDATA[<p>On Thursday my son, Tobias, will be one month old.</p>
<p>The days are getting longer but the weeks are getting shorter!<br>
I&rsquo;ve been neglecting this platform, but haven&rsquo;t forgotten about it.<br>
At Uni, exams times are approaching, I&rsquo;ll be posting all of my revision notes on the blog - to keep me honest and keep everything searchable.</p>
<p>In the time between posts I&rsquo;ve been crazy busy. Juggling Uni with work and having a son, while still doing pet projects for my own learning. A while back a few friends and I started something called overtime, It&rsquo;s a very exciting project - <a href="https://web.archive.org/web/20170406084608/https://overtime.nz/">www.overtime.nz</a>. I also joined an ARMA 3 Clan called SOCOMD, I&rsquo;m rewriting the website and blog they use on my own time, and they&rsquo;re very pleased with what I&rsquo;ve done so far. Check it out at <code>www.socomd.azurewebsites.net</code>. Another very cool project.</p>
<p>I&rsquo;ve been doing what I can to get to uni and lectures, but frankly I haven&rsquo;t been very successful in that regard. I&rsquo;m handing in assignments on time and have passed all my tests and stuff so far, so it&rsquo;s not an unsalvageable semester, but it is a very tight wire that i&rsquo;m walking on. With luck and effort I&rsquo;ll do well, but the more I attend uni, the less I feel like it&rsquo;s actually helping me.</p>
<p>I&rsquo;m still working at Spark Ventures, I&rsquo;m also involved with Summer of Tech this year. Both are awesome opportunities for me in regards to career prospects.</p>
<p>The flipside of having a child now is that I&rsquo;ll be seeking full time work in, around or slightly before August. I&rsquo;m undecided if I&rsquo;ll stay with Spark or if I&rsquo;ll throw in with Summer of Tech, either options are really good.</p>
<p>Thats why Uni is such a conundrum for me. I want to get as much done as I can before that August cut off, but by the same token, I&rsquo;ve got a job, every week of full time uni over full time work represents significant loss of earnings for me, at a time where I want to be earning as much for my boy as I can.</p>
<p>Compounding that is the fact that I feel like I&rsquo;ve lost the faith with the CS department here. The stuff I&rsquo;m learning right now just doesn&rsquo;t feel relevant, the most useful paper I&rsquo;ve done so far at Uni is still a stage 1 paper that taught basic html and css. I can&rsquo;t help but feel like the purpose of going to uni was to get a job, and now that I&rsquo;ve done that, why am I still giving these people money for what is essentially learnings that are irrelevant at best and outdated at worst.</p>
<p>I don&rsquo;t know.</p>
<p>I&rsquo;ve got some stuff to work through I think. I&rsquo;ll be glad to see the end of full time study later this year. Of course once I start working full time, I&rsquo;ll study part time and get my degree done eventually. But is there any reason to let it weigh on my mind and my time as heavily as it does right now?</p>
<p>I don&rsquo;t know. I guess time will tell.</p>
]]></content:encoded></item><item><title>Update re lack of posts</title><link>https://4lex.nz/posts/update-re-lack-of-posts/</link><pubDate>Tue, 24 Mar 2015 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/update-re-lack-of-posts/</guid><description>&lt;p&gt;I have been offline for a long long time.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the situation.&lt;/p&gt;
&lt;p&gt;Last Friday, my wife Laura and I found out she is 34 weeks pregnant with a potentially life threatening complication, forcing her to remain in Hospital.&lt;/p&gt;
&lt;p&gt;We have been overwhelmed with the generosity of our family and friends, we are truly grateful for all the love and support we have received thus far.&lt;/p&gt;
&lt;p&gt;I have been meaning to write up all my notes and use this blog for revision. I still intend to do that. I have been attending lectures but unfortunately am so time poor apart from that, that I have loads of text files that need formatting, media added and additional detail before I can upload them here. I still intend to do that, but it will take time I don&amp;rsquo;t have now. Hopefully I can fully catch up during the mid semester break.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I have been offline for a long long time.</p>
<p>Here&rsquo;s the situation.</p>
<p>Last Friday, my wife Laura and I found out she is 34 weeks pregnant with a potentially life threatening complication, forcing her to remain in Hospital.</p>
<p>We have been overwhelmed with the generosity of our family and friends, we are truly grateful for all the love and support we have received thus far.</p>
<p>I have been meaning to write up all my notes and use this blog for revision. I still intend to do that. I have been attending lectures but unfortunately am so time poor apart from that, that I have loads of text files that need formatting, media added and additional detail before I can upload them here. I still intend to do that, but it will take time I don&rsquo;t have now. Hopefully I can fully catch up during the mid semester break.</p>
<p>I&rsquo;ve made notes on my github projects reflecting the current status of the projects. Hopefully I can get back to them at some stage. Funnily enough there is a lot of work preparing to have a first child with three weeks notice. Again, once things settle down I hope to get back to those projects.</p>
<p>I want to thank everybody who reads this. I&rsquo;ve had a pretty steady amount of visitors, almost 600 per month for the past months. It means a lot to me that people are reading, and hopefully enjoying what I have written, even if my original intention was to use this solely for revision purposes.</p>
<p>I will attempt to post whenever I can, time and content permitting.</p>
<p>Cheers</p>
<p>Alex</p>
]]></content:encoded></item><item><title>Scrambler Program Update 2</title><link>https://4lex.nz/posts/scrambler-program-update-2/</link><pubDate>Tue, 03 Feb 2015 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/scrambler-program-update-2/</guid><description>&lt;p&gt;I&amp;rsquo;ve decided that it&amp;rsquo;s not accurate to call the python project a crypto project..yet. Initially it will be a text scrambler of some kind. I&amp;rsquo;m also taking a bit of time off the project to work on making sure my employment docs (CV etc) are all up to date for the coming year. You never know when you might need something like that on short notice.&lt;/p&gt;
&lt;p&gt;I also bought a great book today with a bunch of coding exercises in it. I might again take time off the scrambler in the future to work through those programs, as they are waaay more likely to help me in interviews than that program (at least when it comes to answering technical questions).&lt;/p&gt;</description><content:encoded><![CDATA[<p>I&rsquo;ve decided that it&rsquo;s not accurate to call the python project a crypto project..yet. Initially it will be a text scrambler of some kind. I&rsquo;m also taking a bit of time off the project to work on making sure my employment docs (CV etc) are all up to date for the coming year. You never know when you might need something like that on short notice.</p>
<p>I also bought a great book today with a bunch of coding exercises in it. I might again take time off the scrambler in the future to work through those programs, as they are waaay more likely to help me in interviews than that program (at least when it comes to answering technical questions).</p>
]]></content:encoded></item><item><title>Simple Crypto Program Integrity Call</title><link>https://4lex.nz/posts/simple-crypto-program-integrity-call/</link><pubDate>Mon, 02 Feb 2015 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/simple-crypto-program-integrity-call/</guid><description>&lt;p&gt;Okay, so it&amp;rsquo;s not really crypto. At least not yet. I started writing the GUI in tkinter, but have decided to try a newer tech called Kivy - which allegedly has multitouch support and other such features I don&amp;rsquo;t really need. Neat!&lt;/p&gt;
&lt;p&gt;Also, started reading &amp;lsquo;Cracking the Code Interview&amp;rsquo; - this book makes me appreciate how little in know about interviewing! I&amp;rsquo;ve got a lot of work to do if I want to bum rush the bigger software firms!&lt;/p&gt;</description><content:encoded><![CDATA[<p>Okay, so it&rsquo;s not really crypto. At least not yet. I started writing the GUI in tkinter, but have decided to try a newer tech called Kivy - which allegedly has multitouch support and other such features I don&rsquo;t really need. Neat!</p>
<p>Also, started reading &lsquo;Cracking the Code Interview&rsquo; - this book makes me appreciate how little in know about interviewing! I&rsquo;ve got a lot of work to do if I want to bum rush the bigger software firms!</p>
]]></content:encoded></item><item><title>Why Hash Functions are cool</title><link>https://4lex.nz/posts/why-hash-functions-are-cool/</link><pubDate>Thu, 22 Jan 2015 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/why-hash-functions-are-cool/</guid><description>&lt;p&gt;I had a bit of down time after work today so I thought I would go over some hash functions.&lt;/p&gt;
&lt;p&gt;I never really got hash tables. Like truly &lt;strong&gt;understood&lt;/strong&gt; them. I could see the purpose but where was the justification for them being everywhere?  Fortunately Uni is exactly the time and place to be smashed down for having silly thoughts like that! Hashing is used for a whole lot of really useful, really cool stuff. Or hash functions rather&lt;/p&gt;</description><content:encoded><![CDATA[<p>I had a bit of down time after work today so I thought I would go over some hash functions.</p>
<p>I never really got hash tables. Like truly <strong>understood</strong> them. I could see the purpose but where was the justification for them being everywhere?  Fortunately Uni is exactly the time and place to be smashed down for having silly thoughts like that! Hashing is used for a whole lot of really useful, really cool stuff. Or hash functions rather</p>
<p><img alt="Hash Function Image" loading="lazy" src="https://upload.wikimedia.org/wikipedia/commons/thumb/2/2b/Cryptographic_Hash_Function.svg/2000px-Cryptographic_Hash_Function.svg.png" title="All Hail Wikipedia"></p>
<p><strong>Passwords</strong> - think about login credentials can be stored securely on a server. Your password to neopets.com is a closely guarded secret, but how is it kept secure? Before I go any further, it&rsquo;s important to note that the hash functions I&rsquo;m talking about now, are not the same as the hash functions used in hash tables. A hash table can search, insert or delete keys in  O(1)  in the average case and  O(n)  (meaning that most of the time, the time it takes to find something in a hash table is directly proportional to the amount of things you have in the hash table). A hash function for a table is made for speed, not security.</p>
<p><strong>So please don&rsquo;t use your (x mod y) hash function to store or encrypt passwords.</strong></p>
<p>There are all sorts of different cryptographic hash functions, that but they should all do the same thing. Ideally they:</p>
<ul>
<li>Can easily make a hash value for any given message</li>
<li>Manipulate a message in such a way that it&rsquo;s hard to generate a message just from the hash value</li>
<li>Make it difficult to modify the message without also modifying the hash value</li>
<li>Make it incredibly unlikely that two different messages evaluate to the same hash (for reasons that should hopefully be obvious!)</li>
</ul>
<p>This is all in an ideal world, in reality, sometimes you can&rsquo;t use an ideal function, there are always tradeoffs to be made in terms of speed vs security etc, and there are a bunch of ways that such schemes can be broken.</p>
<p><a href="https://en.wikipedia.org/wiki/Security_of_cryptographic_hash_functions" target="_blank" rel="noopener noreferrer">Wikipedia</a>

 quotes three such angles:</p>
<ul>
<li>Pre Image Resistance</li>
<li>Second Pre Image Resistance and</li>
<li>Collision Resistance</li>
</ul>
<p>Actually I have to leave it there, I don&rsquo;t understand enough about the different angles yet.</p>
<p>Note to self, Don&rsquo;t forget to continue this series.</p>
<p>In the mean time, read a bunch of wikipedia!</p>
]]></content:encoded></item><item><title>On fiddling with DNS</title><link>https://4lex.nz/posts/on-fiddling-with-a-websites-dns/</link><pubDate>Mon, 19 Jan 2015 00:00:00 +0000</pubDate><guid>https://4lex.nz/posts/on-fiddling-with-a-websites-dns/</guid><description>&lt;p&gt;Note to self:&lt;/p&gt;
&lt;p&gt;Be patient when changing domain name. This site was down for most of the day today because I was an impatient prick.&lt;/p&gt;
&lt;p&gt;Be patient!&lt;/p&gt;</description><content:encoded><![CDATA[<p>Note to self:</p>
<p>Be patient when changing domain name. This site was down for most of the day today because I was an impatient prick.</p>
<p>Be patient!</p>
]]></content:encoded></item><item><title>About</title><link>https://4lex.nz/about/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://4lex.nz/about/</guid><description>&lt;p&gt;I am Alex, and less is more.&lt;/p&gt;
&lt;p&gt;Every single day, I get out of bed with a passion for helping people be their best selves. A passion for making life easier,
more inclusive and frictionless. I do that by growing and serving diverse, empowered teams of passionate humans, rallied around a cause.&lt;/p&gt;
&lt;p&gt;Having spent the last 15 years learning how to parse big meaty problems into smaller issues in order to solve them with technology,
I’ve honed in on the type of work I love. I write about technology, product development and other things when I do write.
With two kids + wife, university and full time work, life always seems to get in the way.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I am Alex, and less is more.</p>
<p>Every single day, I get out of bed with a passion for helping people be their best selves. A passion for making life easier,
more inclusive and frictionless. I do that by growing and serving diverse, empowered teams of passionate humans, rallied around a cause.</p>
<p>Having spent the last 15 years learning how to parse big meaty problems into smaller issues in order to solve them with technology,
I’ve honed in on the type of work I love. I write about technology, product development and other things when I do write.
With two kids + wife, university and full time work, life always seems to get in the way.</p>
<h3 id="awards">Awards</h3>
<p><img alt="Alex Corkin on stage at CIO Awards 2023" loading="lazy" src="/img/in-post/alex-cio-awards-winner-13.jpeg"></p>
<ul>
<li><a href="https://www.ciosummit.co.nz/winners#:~:text=Alex%20Corkin,creating%20new%20operating%20model%20concepts." target="_blank" rel="noopener noreferrer">Winner, Emerging Young ICT Leader of the Year (2023)</a>

</li>
</ul>
<p>At LIC I repaired substantial rifts between departments where those relationships later drove substantial technical transformation in areas of substantial risk and complexity.
At FarmIQ I rebuilt the architecture function transitioning from architecture antagonistic to engineers to a practice focused on helping engineers do what they do best.</p>
<p>These significant cultural transformations drove technical success in both businesses.</p>
<p>At LIC I created new operating model concepts and ways of working that enabled business success not previously possible - my teams delivered a brand new product to market (Smart Wearables) in eight weeks,
where previously the business could only ever deliver iterations on existing software in 10 weeks due to being shackled to a 10 week SAFE release train.</p>
<p>At FarmIQ the cultural changes I brought made it safe for engineers to work on the technical health of the product while
introducing supporting capabilities that helped engineers deliver more change in six months then they had the previous year.</p>
<p>The technical success I delivered downstream of cultural success led to significant business success in the form of profit
and revenue growth at LIC. FarmIQ made me redundant soon after my achievements there due to a failed capital raise - I was unable to stay long enough to observe results there.</p>
<p>For this work I was awarded the Emerging Young ICT Leader of the Year at the 2023 CIO Awards.</p>
<h3 id="projects">Projects</h3>
<h4 id="professional">Professional</h4>
<ul>
<li><a href="https://datacom.com/nz/en/products/datapay" target="_blank" rel="noopener noreferrer">Datapay - There is a good chance you've been paid by this software at some point in your life!</a>

</li>
<li><a href="https://www.farmiq.co.nz/" target="_blank" rel="noopener noreferrer">FarmIQ - Software to help convert dry matter into milk, beef, lamb and wool as efficiently as possible</a>

</li>
<li><a href="https://www.lic.co.nz/products-and-services/minda/minda-live/" target="_blank" rel="noopener noreferrer">MINDA Live, Unnamed Digital Platform - Agritech</a>

</li>
<li><a href="https://www.bnz.co.nz/" target="_blank" rel="noopener noreferrer">bnz.co.nz - Banking, Fintech</a>

</li>
<li><a href="https://nz.healthlink.net" target="_blank" rel="noopener noreferrer">Healthlink - Healthcare, Distributed Messaging Systems, Developer Experience</a>

</li>
<li><a href="https://www.morepork.nz/" target="_blank" rel="noopener noreferrer">Morepork.nz - eCommerce, Security</a>

</li>
<li><a href="https://www.bigpipe.co.nz/" target="_blank" rel="noopener noreferrer">Bigpipe - ISP / Telco</a>

</li>
</ul>
<h4 id="personal">Personal</h4>
<ul>
<li><a href="https://app.valuable.ringfence.nz/" target="_blank" rel="noopener noreferrer">Valuable - Decision Making, SaaS Web Applications</a>

</li>
<li><a href="https://live.voluntarily.nz/" target="_blank" rel="noopener noreferrer">Voluntari.ly - Open Source, Social Good</a>

</li>
</ul>
<h3 id="long-talks">Long Talks</h3>
<ul>
<li>
<p>DevOps Days Wellington - <a href="https://www.youtube.com/watch?v=_0d4HnhTIk8" target="_blank" rel="noopener noreferrer">Health is hard</a>

 // <a href="https://speakerdeck.com/alexandernz/health-is-hard" target="_blank" rel="noopener noreferrer">Slides</a>

</p>
</li>
<li>
<p>Summer of Tech Bootcamp - DevOps: <a href="https://speakerdeck.com/alexandernz/summer-of-tech-auckland-2016-devops-bootcamp" target="_blank" rel="noopener noreferrer">The Good Parts</a>

</p>
</li>
<li>
<p>Junior Dev Meetup - <a href="https://speakerdeck.com/alexandernz/deliberate-continuous-career-progression" target="_blank" rel="noopener noreferrer">Deliberate, Continuous Career Progression</a>

</p>
</li>
</ul>
<h3 id="lightning-talks">Lightning Talks</h3>
<ul>
<li>Full Stack Day Auckland - <a href="https://docs.google.com/document/d/1lvxeUlmlkaQA65-sNpf1XP2r_dUcI3zAemBrxuwL8Lo/edit?usp=sharing" target="_blank" rel="noopener noreferrer">The Role of a Software Developer</a>

</li>
</ul>
]]></content:encoded></item><item><title>cv</title><link>https://4lex.nz/cv/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://4lex.nz/cv/</guid><description>alex corkin · technology and product leadership</description><content:encoded><![CDATA[<p><strong>Full stack business from idea to profit.</strong> I build teams and strategies that convert investor goals and customer research into viable products. I&rsquo;ve built new teams in hypergrowth (15 to 40 in six months), I&rsquo;ve rebuilt teams and culture after restructures and have been trusted with products across health, agriculture, banking and payroll, including the one that pays one in seven Kiwis. Lately the work is agent-native: local models, agent context and infrastructure, and months of engineering work done in a day, repeatedly. One of them cost about $150 in tokens.</p>
<p>I&rsquo;ve delivered cultural and technological change both ways: big bang greenfield builds and gradual, continuous improvement. I know when a business needs which.</p>
<p>I have found through practice that the happiness of your users rarely diverges from the happiness of your technical staff.</p>
]]></content:encoded></item></channel></rss>