<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Adam Ross: Working in Software</title><link>https://adamross.dev/</link><description>Recent content on Adam Ross: Working in Software</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><atom:link href="https://adamross.dev/index.xml" rel="self" type="application/rss+xml"/><item><title>How My Team Aligns on Prompting for Production</title><link>https://adamross.dev/p/prompting-for-production/</link><pubDate>Tue, 17 Feb 2026 16:39:34 +0000</pubDate><guid>https://adamross.dev/p/prompting-for-production/</guid><description>&amp;lt;img src="https://adamross.dev/p/prompting-for-production/cover.jpg" alt="Featured image of post How My Team Aligns on Prompting for Production" /&amp;gt;
&amp;lt;p&amp;gt;My team at Google is &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://cloud.google.com/blog/topics/developers-practitioners/7-technical-takeaways-from-using-gemini-to-generate-code-samples-at-scale&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;automating sample code generation and maintenance&amp;lt;/a&amp;gt;. Part of that is using Generative AI to produce and assess instructional code. This introduces a challenge: How do we trust the system to meet our specific standards, when core components are non-deterministic?&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;Establishing trust requires isolating and understanding each &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://cloud.google.com/ai/llms&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;large language model (LLM)&amp;lt;/a&amp;gt; request. We need to know exactly what goes into the model, and a guarantee of what comes out.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;This challenge isn&amp;amp;rsquo;t different from other feature development. To succeed, we realized we had to stop treating prompting like chatting or guessing and start treating it like coding.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;h2 id=&amp;#34;natural-language-is-a-fuzzy-programming-language&amp;#34;&amp;gt;Natural Language is a Fuzzy Programming Language&amp;#xA;&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;Prompting an LLM is effectively &amp;amp;ldquo;&amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://ai.google.dev/gemini-api/docs/prompting-strategies&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;natural language programming&amp;lt;/a&amp;gt;&amp;amp;rdquo;: we are programming in English. The problem is that English is not the greatest language for programming. It is ambiguous. It is subjective. It is open to interpretation.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;In C++, a missing semicolon breaks the build. In English, a missing comma changes the objective entirely: &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://en.wikipedia.org/wiki/Vocative_case&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;&amp;lt;em&amp;gt;&amp;amp;ldquo;I don&amp;amp;rsquo;t know, John&amp;amp;rdquo;&amp;lt;/em&amp;gt; becomes &amp;lt;em&amp;gt;&amp;amp;ldquo;I don&amp;amp;rsquo;t know John&amp;amp;rdquo;&amp;lt;/em&amp;gt;&amp;lt;/a&amp;gt;. In a programming language, syntax is binary; it works or it doesn&amp;amp;rsquo;t. In English, the difference between &amp;amp;ldquo;Ensure variables are immutable&amp;amp;rdquo; and &amp;amp;ldquo;Make sure variables never change&amp;amp;rdquo; might yield different results based on the model’s training data.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;When you combine the fuzziness of human language with the &amp;amp;ldquo;black box&amp;amp;rdquo; probabilistic processing of an LLM, you face a difficult question: &amp;lt;em&amp;gt;What is the weather going to be today in the land of AI?&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;To answer that, you have to make the intentions behind your prompts explicit.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;h2 id=&amp;#34;be-efficient-with-brains-pair--review&amp;#34;&amp;gt;Be Efficient with Brains (Pair &amp;amp;amp; Review)&amp;#xA;&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;Writing a prompt is an exploratory process of finding words that trigger the best response. However, a single writer is limited by their own understanding. This is risky with LLMs, which are suited for ambiguous problems but require strict guardrails.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;Relying on one person to do this creates blind spots. We found that prompt quality benefits from &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://martinfowler.com/articles/on-pair-programming.html&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;pairing&amp;lt;/a&amp;gt;. A diversity of thought helps create a more complete definition of any problem. What one engineer considers a clear instruction, another might see as a loophole. Pairing covers the gaps that a single brain might miss.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;Furthermore, you should also review every prompt. This isn&amp;amp;rsquo;t just checking for typos; it’s a logic check. Does this prompt align with business requirements? &amp;lt;strong&amp;gt;We’ve found that prompt reviews often uncover disagreements about the requirements themselves, forcing us to align as a team before we ship.&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;h2 id=&amp;#34;document-the-why-and-manage-change&amp;#34;&amp;gt;Document &amp;amp;ldquo;The Why&amp;amp;rdquo; and manage change&amp;#xA;&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;Because English is fuzzy, the intent behind a specific word choice isn&amp;amp;rsquo;t always obvious. Why did we use the passive voice here? Why did we specify &amp;amp;ldquo;immutable&amp;amp;rdquo;?&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;Even well-structured prompts can eventually obscure the original business requirements. As optimizations blur into the general text, we must take every opportunity to document &amp;amp;ldquo;the why&amp;amp;rdquo;:&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;ul&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Documentation:&amp;lt;/strong&amp;gt; Avoid relying on prompts as canonical business requirements. Our LLM requests are a combination of system instructions, user input, context, and deterministic post-processing; the prompt is not complete for onboarding a developer.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Comments:&amp;lt;/strong&amp;gt; Comment on complex prompts just as you would complex code. Spotlight specific constraints or even punctuation to explain the problem they solve. The model is a moving target, so any unintentional changes can make troubleshooting hard.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Commit Messages:&amp;lt;/strong&amp;gt; Use commit messages as an opportunity to explain what was wrong with the prompt. (for example, &amp;lt;code&amp;gt;fixed: Missing comma lost John&amp;lt;/code&amp;gt;)&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;/ul&amp;gt;&amp;#xA;&amp;lt;h2 id=&amp;#34;separation-of-concerns-use-dedicated-files&amp;#34;&amp;gt;Separation of Concerns (Use Dedicated Files)&amp;#xA;&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;Writing code and writing prompts require distinct mindsets. One focuses on syntax and execution flow; the other on semantics and intent. &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://medium.com/@rexnino/what-is-separation-of-concerns-in-coding-and-why-is-it-important-731aa8cfa898&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;Embedding long English instructions inside code creates a distraction&amp;lt;/a&amp;gt;.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;We keep prompts in dedicated files to disentangle application logic from the LLM interaction configuration, which requires frequent tuning.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;By treating the prompt as a standalone component, we can prototype and iterate on the LLM behavior independent of the application&amp;amp;rsquo;s control flow. &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://google.github.io/dotprompt/&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;Tools like dotprompt&amp;lt;/a&amp;gt; allow us to treat these files as first-class artifacts containing text, model parameters, and schema definitions. This highlights that invoking a model isn&amp;amp;rsquo;t just a function call; it’s an integration with a distinct system that requires its own configuration.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;h2 id=&amp;#34;use-structured-output&amp;#34;&amp;gt;Use Structured Output&amp;#xA;&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;To build a reliable tool, you need a bridge between unpredictable LLM output and deterministic computers.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;We rely on &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://genkit.dev/docs/models/#structured-output&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;structured output&amp;lt;/a&amp;gt;&amp;lt;sup id=&amp;#34;fnref:1&amp;#34;&amp;gt;&amp;lt;a href=&amp;#34;#fn:1&amp;#34; class=&amp;#34;footnote-ref&amp;#34; role=&amp;#34;doc-noteref&amp;#34;&amp;gt;1&amp;lt;/a&amp;gt;&amp;lt;/sup&amp;gt; to guide the model to emit JSON according to a schema. Even if we only need a single field, defining a schema provides a guardrail that helps the model output conform to a shape we can validate programmatically. This is critical for code generation, where models often add unwanted preambles, conversational filler, or inconsistent markdown fences.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;If the output doesn&amp;amp;rsquo;t match the schema, we fail fast or retry. This allows us to integrate the LLM output into our process with the same confidence we have in detecting a bad API response.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;h2 id=&amp;#34;from-magic-to-engineering&amp;#34;&amp;gt;From Magic to Engineering&amp;#xA;&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;Moving from one successful prompting to a reliable system requires acknowledging that prompts are code. You need to manage, review, and test them with the same rigor applied to the rest of your stack. While we are still working on better ways to &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://www.statsig.com/perspectives/what-are-non-deterministic-ai-outputs-&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;benchmark quality&amp;lt;/a&amp;gt;, treating our prompts as first-class codebase assets is our first step towards building confidence in our AI assisted automation.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;h2 id=&amp;#34;join-the-conversation&amp;#34;&amp;gt;Join the conversation&amp;#xA;&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;I&amp;amp;rsquo;m curious how you are handling the fuzzy nature of LLMs in production.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;ul&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;How does your team review and test prompts?&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;Do you treat prompts as configuration, code, or something else entirely?&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;Share your workflow in the comments.&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;/ul&amp;gt;&amp;#xA;&amp;lt;h2 id=&amp;#34;read-more&amp;#34;&amp;gt;Read More&amp;#xA;&amp;lt;/h2&amp;gt;&amp;lt;ul&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;&amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://dev.to/googlecloud/the-lumberjack-paradox-from-theory-to-practice-2lb5&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;The lumberjack paradox: From theory to practice&amp;lt;/a&amp;gt; by Jennifer Davis&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;&amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://medium.com/google-cloud/google-sandwich-manager-and-the-hallucinated-sdk-6bed653e6318&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;Google Sandwich Manager, and the hallucinated SDK&amp;lt;/a&amp;gt; by Katie McLaughlin and Brian Dorsey&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;/ul&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;Thanks to &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://dev.to/sigje&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;Jennifer Davis&amp;lt;/a&amp;gt; &amp;amp;amp; &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://shawnmjones.org/&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;Shawn Jones&amp;lt;/a&amp;gt; for review and contributions.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;div class=&amp;#34;footnotes&amp;#34; role=&amp;#34;doc-endnotes&amp;#34;&amp;gt;&amp;#xA;&amp;lt;hr&amp;gt;&amp;#xA;&amp;lt;ol&amp;gt;&amp;#xA;&amp;lt;li id=&amp;#34;fn:1&amp;#34;&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;This links to how structured output works in the Genkit framework, which my team is using. A succinct example.&amp;amp;#160;&amp;lt;a href=&amp;#34;#fnref:1&amp;#34; class=&amp;#34;footnote-backref&amp;#34; role=&amp;#34;doc-backlink&amp;#34;&amp;gt;&amp;amp;#x21a9;&amp;amp;#xfe0e;&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;/ol&amp;gt;&amp;#xA;&amp;lt;/div&amp;gt;&amp;#xA;</description></item><item><title>Write to Google Sheets from a local script via gcloud CLI authentication</title><link>https://adamross.dev/p/local-gcloud-auth-to-sheets/</link><pubDate>Wed, 25 Sep 2024 17:48:17 +0000</pubDate><guid>https://adamross.dev/p/local-gcloud-auth-to-sheets/</guid><description>&amp;lt;img src="https://adamross.dev/p/local-gcloud-auth-to-sheets/cover.jpg" alt="Featured image of post Write to Google Sheets from a local script via gcloud CLI authentication" /&amp;gt;
&amp;lt;p&amp;gt;Recently I needed to pull data from the GitHub API and publish to a Google Sheet&amp;#xA;so I could share some charts about code review workload. This post is about how&amp;#xA;I got authentication working.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;I wrote a Node.js script because my use case was too complex for BASH and&amp;#xA;seemed too temporary for Go. Initially, the script generated ad hoc CSV data&amp;#xA;that I could manually copy into Google Sheets using the Paste-as-CSV feature.&amp;#xA;After a few rounds of manually copying, I wanted to use my time wisely: I&amp;#xA;estimated that I could get a Sheets integration working within a couple hours&amp;#xA;and if so that would&amp;#xA;&amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://dev.to/grayside/why-automate-1fac&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;probably pay off within a few months&amp;lt;/a&amp;gt;.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;section class=&amp;#34;context callout callout-important&amp;#34;&amp;gt;&amp;#xA; &amp;lt;p role=&amp;#34;note&amp;#34;&amp;gt;&amp;lt;strong&amp;gt;Aside:&amp;lt;/strong&amp;gt; Sheets isn&amp;amp;rsquo;t my database. It&amp;amp;rsquo;s my data reporting UI.&amp;#xA;&amp;lt;em&amp;gt;Don&amp;amp;rsquo;t use Sheets as your database.&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;/section&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;It took me closer to 4 hours to get this working, because authentication is&amp;#xA;hard. This post shows the more direct path to a working solution.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;It&amp;amp;rsquo;s easy to be distracted with the complexity of&amp;#xA;&amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://developers.google.com/workspace/guides/configure-oauth-consent&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;creating an oAuth application&amp;lt;/a&amp;gt;,&amp;#xA;and while I especially like&amp;#xA;&amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://developers.google.com/workspace/guides/create-credentials#service-account&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;using a service account&amp;lt;/a&amp;gt;&amp;#xA;from my Cloud services, that&amp;amp;rsquo;s&amp;#xA;&amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://cloud.google.com/sdk/docs/authorizing#service-account&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;less convenient when running locally&amp;lt;/a&amp;gt;.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;The trick I learned is that the gcloud CLI&amp;amp;rsquo;s setup of&amp;#xA;&amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://cloud.google.com/docs/authentication/application-default-credentials&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;application default credentials&amp;lt;/a&amp;gt; can&amp;#xA;operate as a sort of OAuth proxy for Google Workspace code, by expanding how it&amp;#xA;authenticates your account with Google to include some more permissions&amp;#xA;(OAuth Scopes).&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;h3 id=&amp;#34;-local-authentication-with-gcloud-cli&amp;#34;&amp;gt;🔐 Local authentication with gcloud CLI&amp;#xA;&amp;lt;/h3&amp;gt;&amp;lt;p&amp;gt;To make API requests to Google Sheets, enable the Sheets API in your Cloud project:&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;div class=&amp;#34;highlight&amp;#34;&amp;gt;&amp;lt;pre tabindex=&amp;#34;0&amp;#34; class=&amp;#34;chroma&amp;#34;&amp;gt;&amp;lt;code class=&amp;#34;language-sh&amp;#34; data-lang=&amp;#34;sh&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;$&amp;amp;gt; gcloud services &amp;lt;span class=&amp;#34;nb&amp;#34;&amp;gt;enable&amp;lt;/span&amp;gt; sheets.googleapis.com&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;Operation &amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;amp;#34;operations/acat.p2-480745230567-02564c8d-c6ba-4f60-90bd-13f33e41f0fe&amp;amp;#34;&amp;lt;/span&amp;gt; finished successfully.&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;p&amp;gt;Set your &amp;lt;strong&amp;gt;application default credentials&amp;lt;/strong&amp;gt;, also claiming some non-default OAuth scopes so the credential can be used with sheets:&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;div class=&amp;#34;highlight&amp;#34;&amp;gt;&amp;lt;pre tabindex=&amp;#34;0&amp;#34; class=&amp;#34;chroma&amp;#34;&amp;gt;&amp;lt;code class=&amp;#34;language-sh&amp;#34; data-lang=&amp;#34;sh&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;$&amp;amp;gt; gcloud auth application-default login --scopes &amp;lt;span class=&amp;#34;se&amp;#34;&amp;gt;\&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/drive,https://www.googleapis.com/auth/spreadsheets&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;p&amp;gt;This will trigger an OAuth flow that involves visiting a webpage in your browser. Depending on the terminal configuration this may display a URL or even open the page.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;section class=&amp;#34;context callout callout-info&amp;#34;&amp;gt;&amp;#xA; &amp;lt;p role=&amp;#34;note&amp;#34;&amp;gt;When gcloud is granted this scope, code that can access your local default application credentials will have read/write access to all your Google Sheets data. You can re-run this command without the customized scopes to toggle that access on and off as needed.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;/section&amp;gt;&amp;#xA;&amp;lt;h3 id=&amp;#34;-initialize-a-node-client-for-google-sheets&amp;#34;&amp;gt;🗝️ Initialize a Node client for Google Sheets&amp;#xA;&amp;lt;/h3&amp;gt;&amp;lt;div class=&amp;#34;highlight&amp;#34;&amp;gt;&amp;lt;pre tabindex=&amp;#34;0&amp;#34; class=&amp;#34;chroma&amp;#34;&amp;gt;&amp;lt;code class=&amp;#34;language-js&amp;#34; data-lang=&amp;#34;js&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;kr&amp;#34;&amp;gt;import&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;{&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;google&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;}&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;from&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;googleapis&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;;&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;kd&amp;#34;&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;sheetsClient&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;()&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;{&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;kr&amp;#34;&amp;gt;const&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;authConfig&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;k&amp;#34;&amp;gt;new&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;google&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;auth&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;GoogleAuth&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;({&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;    &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;scopes&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;[&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;        &amp;lt;span class=&amp;#34;c1&amp;#34;&amp;gt;// Only &amp;amp;#39;spreadsheets&amp;amp;#39; scope is needed in the code.&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;            &amp;lt;span class=&amp;#34;c1&amp;#34;&amp;gt;// gcloud CLI also needs &amp;amp;#39;cloud-platform&amp;amp;#39; and &amp;amp;#39;drive&amp;amp;#39;.&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;            &amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;https://www.googleapis.com/auth/spreadsheets&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;        &amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;],&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;    &amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;});&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;    &amp;lt;span class=&amp;#34;kr&amp;#34;&amp;gt;const&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;auth&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;kr&amp;#34;&amp;gt;await&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;authConfig&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;getClient&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;();&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;    &amp;lt;span class=&amp;#34;k&amp;#34;&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;google&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;sheets&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;({&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;version&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;v4&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;auth&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;});&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;}&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;h3 id=&amp;#34;-append-data-to-the-sheet&amp;#34;&amp;gt;🖇 Append data to the sheet&amp;#xA;&amp;lt;/h3&amp;gt;&amp;lt;p&amp;gt;The sample from the docs on how to&amp;#xA;&amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://developers.google.com/sheets/api/guides/values#append_values&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;append data to the sheet&amp;lt;/a&amp;gt;&amp;#xA;(click on Node.js tab) worked well. However, I couldn&amp;amp;rsquo;t understand how to get&amp;#xA;authentication working from there. The sample worked once I understood the&amp;#xA;trick above to add the missing OAuth scopes to my dev environment credentials.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;I made a few changes to enable easier reuse of the client, parameterize the&amp;#xA;request differently, and emphasize how I wanted the data handled by Sheets.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;My code to append data to the sheet, leveraging the client initialization code above:&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;div class=&amp;#34;highlight&amp;#34;&amp;gt;&amp;lt;pre tabindex=&amp;#34;0&amp;#34; class=&amp;#34;chroma&amp;#34;&amp;gt;&amp;lt;code class=&amp;#34;language-js&amp;#34; data-lang=&amp;#34;js&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;kd&amp;#34;&amp;gt;let&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;client&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;;&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;kr&amp;#34;&amp;gt;async&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;kd&amp;#34;&amp;gt;function&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;appendDataToSheet&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;spreadsheetId&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;tab&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;values&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;)&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;{&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;k&amp;#34;&amp;gt;if&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;!&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;client&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;)&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;{&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;client&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;sheetsClient&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;();&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;}&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;k&amp;#34;&amp;gt;try&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;{&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;kr&amp;#34;&amp;gt;const&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;result&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;kr&amp;#34;&amp;gt;await&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;client&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;spreadsheets&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;values&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;append&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;({&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;spreadsheetId&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;range&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;sb&amp;#34;&amp;gt;`&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;si&amp;#34;&amp;gt;${&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;tab&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;si&amp;#34;&amp;gt;}&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;sb&amp;#34;&amp;gt;!A2:AG`&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;c1&amp;#34;&amp;gt;// Use my data as provided.&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;valueInputOption&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;RAW&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;c1&amp;#34;&amp;gt;// Inserts rows as part of appending to reduce overwrites.&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;insertDataOption&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;INSERT_ROWS&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;requestBody&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;{&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;values&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;},&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;});&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;console&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;log&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;sb&amp;#34;&amp;gt;`&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;si&amp;#34;&amp;gt;${&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;result&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;data&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;updates&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;updatedCells&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;si&amp;#34;&amp;gt;}&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;sb&amp;#34;&amp;gt; cells appended.`&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;);&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;}&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;k&amp;#34;&amp;gt;catch&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;e&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;)&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;{&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;c1&amp;#34;&amp;gt;// Show the error, do not stop. Cross-reference the error with terminal output&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;c1&amp;#34;&amp;gt;// and decide case-by-case to re-run the script or manually copy data.&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;console&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;error&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;e&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;);&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;}&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;}&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;p&amp;gt;I&amp;amp;rsquo;m using &amp;amp;ldquo;append&amp;amp;rdquo; because my script collects monthly metrics, and append allows me to add new rows without removing earlier rows.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;Here&amp;amp;rsquo;s an example of how to call the &amp;lt;code&amp;gt;appendDataToSheet()&amp;lt;/code&amp;gt; function:&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;div class=&amp;#34;highlight&amp;#34;&amp;gt;&amp;lt;pre tabindex=&amp;#34;0&amp;#34; class=&amp;#34;chroma&amp;#34;&amp;gt;&amp;lt;code class=&amp;#34;language-js&amp;#34; data-lang=&amp;#34;js&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;kr&amp;#34;&amp;gt;const&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;values&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;o&amp;#34;&amp;gt;=&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;[&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;c1&amp;#34;&amp;gt;// Each nested array is a spreadsheet row.&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;[&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;mi&amp;#34;&amp;gt;1&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;mi&amp;#34;&amp;gt;2&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;mi&amp;#34;&amp;gt;3&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;mi&amp;#34;&amp;gt;4&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;luggage&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;],&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;[&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;mi&amp;#34;&amp;gt;4&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;mi&amp;#34;&amp;gt;5&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;mi&amp;#34;&amp;gt;6&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;N/A&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;sticks&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;],&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;];&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;appendDataToSheet&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;HPDkfqdu6rfIq5-4uTGDqz2tvmPxDZMul27JFexample&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;Exported Data Tab&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;values&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;);&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;p&amp;gt;There are some good tips about working with the&amp;#xA;&amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://developers.google.com/sheets/api/troubleshoot-api-errors&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;sheets API in the docs&amp;lt;/a&amp;gt;&amp;#xA;such as the suggestion not to send more than one API request per second per&amp;#xA;sheet. I found out the hard way: overwriting data from a first request with&amp;#xA;data from a second.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;h3 id=&amp;#34;-ship-it&amp;#34;&amp;gt;🚀 Ship it!&amp;#xA;&amp;lt;/h3&amp;gt;&amp;lt;p&amp;gt;If I move forward to productionize this, I might switch to using&amp;#xA;&amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://cloud.google.com/scheduler/docs&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;Cloud Scheduler&amp;lt;/a&amp;gt; and&amp;#xA;&amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://cloud.google.com/run/docs/quickstarts/jobs/build-create-nodejs&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;Cloud Run Jobs&amp;lt;/a&amp;gt;.&amp;#xA;Let me know if you&amp;amp;rsquo;d like to read about that.&amp;lt;/p&amp;gt;&amp;#xA;</description></item><item><title>Where to Host My Static Tech Blog</title><link>https://adamross.dev/p/where-to-host-my-static-tech-blog/</link><pubDate>Tue, 13 Aug 2024 12:00:00 +0000</pubDate><guid>https://adamross.dev/p/where-to-host-my-static-tech-blog/</guid><description>&amp;lt;img src="https://adamross.dev/p/where-to-host-my-static-tech-blog/cover.jpg" alt="Featured image of post Where to Host My Static Tech Blog" /&amp;gt;
&amp;lt;ul&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Status:&amp;lt;/strong&amp;gt; [&amp;lt;del&amp;gt;thinking&amp;lt;/del&amp;gt; | &amp;lt;del&amp;gt;nope&amp;lt;/del&amp;gt; | &amp;lt;strong&amp;gt;current&amp;lt;/strong&amp;gt; | &amp;lt;del&amp;gt;superceded by &amp;lt;record&amp;gt;&amp;lt;/del&amp;gt;]&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Last Updated:&amp;lt;/strong&amp;gt; Mar 9, 2024&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Objective:&amp;lt;/strong&amp;gt; Choose a web host for my static site&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;/ul&amp;gt;&amp;#xA;&amp;lt;h3 id=&amp;#34;context--problem-statement&amp;#34;&amp;gt;Context &amp;amp;amp; Problem Statement&amp;#xA;&amp;lt;/h3&amp;gt;&amp;lt;p&amp;gt;To have a website, there must be a web host. A website is the visual artifact that everyone sees, the pages, images and videos. The web host is a service provides the compute, storage, networking, and all the bits that make it usable. For my personal website, I need to choose a web host that adheres to the following priorities and constraints.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;h3 id=&amp;#34;priorities--constraints&amp;#34;&amp;gt;Priorities &amp;amp;amp; Constraints &amp;lt;!-- optional --&amp;gt;&amp;#xA;&amp;lt;/h3&amp;gt;&amp;lt;p&amp;gt;Every project has requirements, here are a few of mine:&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;ul&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Be low cost:&amp;lt;/strong&amp;gt; My personal website is a personal expense I&amp;amp;rsquo;d rather keep minimal. My goal is to fit within a free tier, but not to exceed a &amp;amp;ldquo;dollars per month&amp;amp;rdquo; cost.&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Be focused on publishing over development:&amp;lt;/strong&amp;gt; My time is immensely valuable. I want to publish content with some flexibility to tailor for my way of working. However, I do not want to generate (too many) new side projects in web development instead of writing.&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Be low attention:&amp;lt;/strong&amp;gt; I want to ignore this site for months at a time without incurring security risks or a backlog of infrastructure projects.&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Minimal Service Providers:&amp;lt;/strong&amp;gt; I prefer to use the fewest number of service providers for my personal IT. I have GitHub, DNS, and Google Cloud already in the mix. Each additional company is another credit card profile to update and set of corporate customer policies to track.&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Learn from Everything:&amp;lt;/strong&amp;gt; Balance existing skills with learning opportunities&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Sourcing from GitHub:&amp;lt;/strong&amp;gt; I&amp;amp;rsquo;ve decided to keep my site source and content in a public GitHub repository. I want to see merges to my &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; branch deploy to a live site.&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Constraint:&amp;lt;/strong&amp;gt; I&amp;amp;rsquo;ve previously chosen Hugo and Go with code hosted on GitHub as tools for building the site. Hugo is a static site generator flexible enough to be used across many (all?) hosting services.&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;/ul&amp;gt;&amp;#xA;&amp;lt;h3 id=&amp;#34;considered-options&amp;#34;&amp;gt;Considered Options&amp;#xA;&amp;lt;/h3&amp;gt;&amp;lt;ul&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Option 1:&amp;lt;/strong&amp;gt; Use &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://firebase.google.com/docs/hosting&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;Firebase Hosting&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Option 2:&amp;lt;/strong&amp;gt; Use &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://pages.github.com/&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;GitHub Pages&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Option 3:&amp;lt;/strong&amp;gt; Use &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://cloud.google.com/run/docs/&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;Google Cloud Run&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Option 4:&amp;lt;/strong&amp;gt; Use &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://www.netlify.com/&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;Netlify&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;/ul&amp;gt;&amp;#xA;&amp;lt;h2 id=&amp;#34;decision&amp;#34;&amp;gt;Decision&amp;#xA;&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;Chosen option &amp;lt;strong&amp;gt;Option 1: Firebase Hosting&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;Firebase Hosting is a specialized tool for static site hosting, providing ease of deployment, a global CDN, and a good free tier. I have a lot of familiarity with Google Cloud, but less familiarity with the Firebase products.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;section class=&amp;#34;context callout callout-info&amp;#34;&amp;gt;&amp;#xA; &amp;lt;p role=&amp;#34;note&amp;#34;&amp;gt;Working for Google means my day job pays me to be familiar with Google Cloud, which may lead me to use Google Cloud for more personal projects. This gives Firebase Hosting some advantage in the decision.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;/section&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;I&amp;amp;rsquo;m choosing Firebase Hosting because it meets all my requirements and broadens my knowledge of Google Cloud.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;h3 id=&amp;#34;alternatives-considered&amp;#34;&amp;gt;Alternatives Considered&amp;#xA;&amp;lt;/h3&amp;gt;&amp;lt;p&amp;gt;I have experience hosting sites on &amp;lt;strong&amp;gt;Option 2: GitHub Pages&amp;lt;/strong&amp;gt;. However, I have been frustrated by the lack of performant redirects (redirects via &amp;lt;code&amp;gt;HTML meta refresh&amp;lt;/code&amp;gt; lack wildcard configuration options and can&amp;amp;rsquo;t be cached compared to server-side options). Other solutions have server-side redirect features (&amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://firebase.google.com/docs/hosting/full-config#redirects&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;firebase hosting&amp;lt;/a&amp;gt;, &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://docs.netlify.com/routing/redirects/&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;netlify&amp;lt;/a&amp;gt;)&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;I&amp;amp;rsquo;ve found GitHub Pages to be a bit complex for PR Previews, with various advice around multiple repositories, random &amp;amp;amp; niche GitHub Actions, or building your own &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://github.com/grayside/gh-page-previews&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;custom preview solution&amp;lt;/a&amp;gt;.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;I&amp;amp;rsquo;m not choosing GitHub Pages because it&amp;amp;rsquo;s missing features I want.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Option 3: Use Google Cloud Run.&amp;lt;/strong&amp;gt; With 6 years of experience working on the developer experience of Google Cloud Run, using it would be straightforward. I don&amp;amp;rsquo;t need (or want) the control over the serving details like which web server is running. The other options are better aligned with my preference for minimal maintenance.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;section class=&amp;#34;context callout callout-info&amp;#34;&amp;gt;&amp;#xA; &amp;lt;p role=&amp;#34;note&amp;#34;&amp;gt;💡 My teammate Brian Dorsey has a great post on &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://til.cafe/blog/2024/hosting-static-web-site-using-google-cloud-run/&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;hosting a static website using Google Cloud Run&amp;lt;/a&amp;gt; if you&amp;amp;rsquo;d like a recipe.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;/section&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;I&amp;amp;rsquo;m not choosing Cloud Run because I want more abstraction away from the technical details. I don&amp;amp;rsquo;t want to pick up a new infrastructure hobby.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Option 4: Use Netlify.&amp;lt;/strong&amp;gt; Several folks recommended Netlify as their preferred static site host, with strong GitHub integration. Since I am already using Google Cloud for other projects, Netlify has a disadvantage as a new service provider.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;If Firebase Hosting doesn&amp;amp;rsquo;t work out, Netlify would be my next choice. It was a close decision.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;h3 id=&amp;#34;expected-consequences&amp;#34;&amp;gt;Expected Consequences&amp;#xA;&amp;lt;/h3&amp;gt;&amp;lt;p&amp;gt;Any of these options will meet my goal of serving a few megabytes of data. I expect problems to come from pet peeves or curiousity about the alternatives. Either way, this is a &amp;lt;strong&amp;gt;&amp;lt;em&amp;gt;low stakes decision not worth a deeper analysis&amp;lt;/em&amp;gt;&amp;lt;/strong&amp;gt;. If I had to change my decision, I expect the switching cost to move to another static site host to be an evening of work.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;h2 id=&amp;#34;revisiting-this-decision&amp;#34;&amp;gt;Revisiting this Decision&amp;#xA;&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;I&amp;amp;rsquo;ve scheduled a reminder for March 2025 to reflect on this decision, and decide if I&amp;amp;rsquo;d like to change hosting. With the low expected switching cost, it might be worth trying another host just for the learning experience.&amp;lt;/p&amp;gt;&amp;#xA;</description></item><item><title>Dependency Updates: More to Do Than Review</title><link>https://adamross.dev/p/dependency-updates-more-to-do-than-review/</link><pubDate>Tue, 25 Jun 2024 05:31:53 +0000</pubDate><guid>https://adamross.dev/p/dependency-updates-more-to-do-than-review/</guid><description>&amp;lt;img src="https://adamross.dev/p/dependency-updates-more-to-do-than-review/cover.png" alt="Featured image of post Dependency Updates: More to Do Than Review" /&amp;gt;
&amp;lt;p&amp;gt;I enjoy tools that automate dependency updates in my projects (looking at you, Renovate and Dependabot). They save my team a lot of time: with a quick glance and the push of a couple buttons, I can update dozens of dependencies in a repository. Even better, the work came to me, I didn&amp;amp;rsquo;t need to go looking for release schedules or subscribe to a newsletter. This is a big change to how I see teams handle 3rd party updates.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;Merging an update just because the tests pass creates a mess.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;h2 id=&amp;#34;context-what-do-these-dependency-automation-tools-do&amp;#34;&amp;gt;Context: What do these dependency automation tools do?&amp;#xA;&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;They help in the following ways:&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;ul&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;Notify project maintainers that a dependency has an update&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;Modify the project&amp;amp;rsquo;s package manifest to reference the updated code&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;Open a Pull Request and highlight some context on the change&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;and because you have CI set up, they trigger your automated tests&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;/ul&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;When I start my day and look at a Pull Request created by a tool like this, I may see a lot of encouraging green checkmarks✅ declaring merge-worthiness.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;h2 id=&amp;#34;the-tools-did-all-the-work-i-can-review--merge-right&amp;#34;&amp;gt;The tools did all the work, I can review &amp;amp;amp; merge, right?&amp;#xA;&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;Seeing green checkmarks is necessary but not sufficient to merge. I trust automated checks to tell me where to prioritize my review time. As the human in the loop, I should verify:&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;ol&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;Did the automated tests pass?&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;Does the library have a history of causing trouble? Maybe I should manually test the change.&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;Does the package follow &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://semver.org/&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;semantic versioning&amp;lt;/a&amp;gt;? Is their a compatibility break from a major update? What if the break doesn&amp;amp;rsquo;t change the API specification, but critical details in the payload data?&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;/ol&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;With answers &amp;amp;ldquo;Yes&amp;amp;rdquo;, &amp;amp;ldquo;No Problem&amp;amp;rdquo;, and &amp;amp;ldquo;No&amp;amp;rdquo;, I could just merge&amp;amp;hellip;&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;section class=&amp;#34;context callout callout-important&amp;#34;&amp;gt;&amp;#xA; &amp;lt;p role=&amp;#34;note&amp;#34;&amp;gt;🛑 WAIT. I&amp;amp;rsquo;ve left something out: &amp;lt;em&amp;gt;I know that I don&amp;amp;rsquo;t have 100% test coverage&amp;lt;/em&amp;gt;. Are the tests passing because the change is good, or because there is a test coverage gap and I have no idea what will happen next?&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;/section&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;The question &amp;lt;em&amp;gt;&amp;amp;ldquo;Did the automated tests pass?&amp;amp;rdquo;&amp;lt;/em&amp;gt; becomes &amp;lt;em&amp;gt;&amp;amp;ldquo;Is there automated test coverage in my project and does it cover the use of this dependency? Did those tests pass?&amp;amp;rdquo;&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;h2 id=&amp;#34;lets-see-an-example-of-untested-dependencies-slipping-through&amp;#34;&amp;gt;Let&amp;amp;rsquo;s see an example of untested dependencies slipping through&amp;#xA;&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;In this example, suppose a dependency called &amp;lt;em&amp;gt;ThirdPartyAuth&amp;lt;/em&amp;gt; is an important part of the business logic. Below I&amp;amp;rsquo;ll show some of the potential misunderstandings from different testing strategies.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;div class=&amp;#34;highlight&amp;#34;&amp;gt;&amp;lt;pre tabindex=&amp;#34;0&amp;#34; class=&amp;#34;chroma&amp;#34;&amp;gt;&amp;lt;code class=&amp;#34;language-js&amp;#34; data-lang=&amp;#34;js&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;c1&amp;#34;&amp;gt;// Important business logic that takes a callback/function parameter.&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;doGoodThings&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;maybeAMockedRequestFunction&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;)&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;{&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;maybeAMockedRequestFunction&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;()&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;}&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;RealRequest&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;()&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;{&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;k&amp;#34;&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;http_request&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;(&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;https://example.com&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;ThirdPartyAuth&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;coolLibraryAuthV2&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;()&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;)&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;}&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;doGoodThings&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;RealRequest&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;)&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;h3 id=&amp;#34;test-example-1-no-test&amp;#34;&amp;gt;Test Example #1: No Test&amp;#xA;&amp;lt;/h3&amp;gt;&amp;lt;p&amp;gt;Test success reported with no tests run. Do I remember during code review?&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;div class=&amp;#34;highlight&amp;#34;&amp;gt;&amp;lt;pre tabindex=&amp;#34;0&amp;#34; class=&amp;#34;chroma&amp;#34;&amp;gt;&amp;lt;code class=&amp;#34;language-js&amp;#34; data-lang=&amp;#34;js&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;c1&amp;#34;&amp;gt;// A commented out test stub.&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;c1&amp;#34;&amp;gt;// Test_doGoodThings()&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;err&amp;#34;&amp;gt;✔️&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;Success&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;c1&amp;#34;&amp;gt;// No tests were run&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;h3 id=&amp;#34;test-example-2-mocked-test&amp;#34;&amp;gt;Test Example #2: Mocked Test&amp;#xA;&amp;lt;/h3&amp;gt;&amp;lt;p&amp;gt;End-to-end testing can be expensive to write and much more expensive to maintain. Instead, we could mock the responses from external APIs and scope the tests to local business logic.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;This test does not cover the use of the &amp;lt;em&amp;gt;ThirdPartyAuth&amp;lt;/em&amp;gt; library.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;div class=&amp;#34;highlight&amp;#34;&amp;gt;&amp;lt;pre tabindex=&amp;#34;0&amp;#34; class=&amp;#34;chroma&amp;#34;&amp;gt;&amp;lt;code class=&amp;#34;language-js&amp;#34; data-lang=&amp;#34;js&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;MockRequest&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;()&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;{&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;k&amp;#34;&amp;gt;return&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;amp;#34;OK&amp;amp;#34;&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;}&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;Test_doGoodThings&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;MockRequest&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;)&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;err&amp;#34;&amp;gt;✔️&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;Success&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;c1&amp;#34;&amp;gt;// What does that mean?&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;h3 id=&amp;#34;test-example-3-end-to-end-test&amp;#34;&amp;gt;Test Example #3: End-to-End Test&amp;#xA;&amp;lt;/h3&amp;gt;&amp;lt;p&amp;gt;The integration of &amp;lt;code&amp;gt;ThirdPartyAuth.coolLibraryAuthV2()&amp;lt;/code&amp;gt; with the codebase is verified.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;If the test fails it may be a breaking change in the library or perhaps &amp;lt;code&amp;gt;https://example.com&amp;lt;/code&amp;gt; is down.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;div class=&amp;#34;highlight&amp;#34;&amp;gt;&amp;lt;pre tabindex=&amp;#34;0&amp;#34; class=&amp;#34;chroma&amp;#34;&amp;gt;&amp;lt;code class=&amp;#34;language-js&amp;#34; data-lang=&amp;#34;js&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;Test_doGoodThings&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;RealRequest&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;)&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;err&amp;#34;&amp;gt;✅&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;nx&amp;#34;&amp;gt;Success&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;c1&amp;#34;&amp;gt;// Probability of false negative &amp;amp;gt; false positive&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;p&amp;gt;The value of the ✅ passing test is limited by the depth of test coverage and my knowledge of the limitation. No only me: since my team handles maintenance on a rotating basis, the value of the tests depends on how well each teammate understands those limitations.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;h2 id=&amp;#34;functionality-not-broken-but-update-not-complete&amp;#34;&amp;gt;Functionality not broken, but update not complete&amp;#xA;&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;Just because a library works doesn&amp;amp;rsquo;t mean it is used effectively.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;When a dependency has significant changes over time, it likely gains new methods, recommended usage patterns, and planned deprecations on old ways of working. Upgrading the dependency without understanding the new, happy paths sets up a future with a broken update.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Today&amp;amp;rsquo;s outdated practice is tomorrow&amp;amp;rsquo;s deprecated design.&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;h2 id=&amp;#34;call-to-action&amp;#34;&amp;gt;Call to Action&amp;#xA;&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;Passing tests can be deceptive, and package manifests aren&amp;amp;rsquo;t the only spot in your codebase to need changes when a big update happens.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;section class=&amp;#34;context callout callout-important&amp;#34;&amp;gt;&amp;#xA; &amp;lt;p role=&amp;#34;note&amp;#34;&amp;gt;&amp;lt;strong&amp;gt;💡 The robots cannot do all the necessary work to update a dependency safely.&amp;lt;/strong&amp;gt; You &amp;lt;em&amp;gt;do not need&amp;lt;/em&amp;gt; deeper test coverage. You and your collaborators &amp;lt;em&amp;gt;do need&amp;lt;/em&amp;gt; a common understanding of the limits of what a passing test means, and what needs to happen to keep shipping quality software after automated testing has done what it could.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;/section&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;In terms of Renovate configuration, maybe add some &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://docs.renovatebot.com/configuration-options/#prbodynotes&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;extra guidance to those Pull Requests&amp;lt;/a&amp;gt;:&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;div class=&amp;#34;highlight&amp;#34;&amp;gt;&amp;lt;pre tabindex=&amp;#34;0&amp;#34; class=&amp;#34;chroma&amp;#34;&amp;gt;&amp;lt;code class=&amp;#34;language-json&amp;#34; data-lang=&amp;#34;json&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;{&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt; &amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;&amp;amp;#34;prBodyNotes&amp;amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt; &amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;amp;#34;Do the tests cover this dependency? You may have more work to do.&amp;amp;#34;&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;}&amp;lt;/span&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;</description></item><item><title>Why Automate?</title><link>https://adamross.dev/p/why-automate/</link><pubDate>Tue, 30 Apr 2024 12:00:55 +0000</pubDate><guid>https://adamross.dev/p/why-automate/</guid><description>&amp;lt;img src="https://adamross.dev/p/why-automate/cover.webp" alt="Featured image of post Why Automate?" /&amp;gt;
&amp;lt;p&amp;gt;Automation saves time and toil when performing a task, and we all appreciate that. This is one frame for justifying automation but it limits view of other opportunities.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;I love how automation transforms the tedium of work into puzzle-solving. Early in my career, I learned that management would value puzzle time (automation work) by pointing out a greater benefit for the effort than the time spent. (Aim for minimal time investment without sacrificing opportunities or incurring hidden costs like a high maintenance burden.)&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;Folks enjoy citing &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://xkcd.com/1205/&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;XKCD 1205&amp;lt;/a&amp;gt; in automation discussions because it does a great job illustrating the opportunity time cost of automation. The time spent to code a new solution for an already solved problem doesn&amp;amp;rsquo;t help solve business goals until after the investment pays off.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;&amp;lt;figure&amp;gt;&amp;#xA;&amp;lt;img src=&amp;#34;https://imgs.xkcd.com/comics/is_it_worth_the_time.png&amp;#34;&amp;#xA;&amp;#x9;&amp;#xA;&amp;#x9;&amp;#xA;&amp;#x9;&amp;#xA;&amp;#x9;loading=&amp;#34;lazy&amp;#34;&amp;#xA;&amp;#x9;&amp;#xA;&amp;#x9;&amp;#x9;alt=&amp;#34;A grid with the header ‘How long can you work on making a routine task more efficient before you’re spending more time than you save’ the x-axis is the frequency of the task, the y-axis is the time it takes using prospective automation. It implies a narrow path where automating has value.&amp;#34;&amp;#xA;&amp;#x9; title=&amp;#34;XKCD #1205: Is it worth the time?&amp;#34;&amp;#xA;&amp;gt;&amp;#xA;&amp;lt;figcaption&amp;gt;&amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://xkcd.com/1205/&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;XKCD #1205: Is it worth the time?&amp;lt;/a&amp;gt;&amp;lt;/figcaption&amp;gt;&amp;#xA;&amp;lt;/figure&amp;gt;&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;section class=&amp;#34;context callout callout-info&amp;#34;&amp;gt;&amp;#xA; &amp;lt;p role=&amp;#34;note&amp;#34;&amp;gt;What&amp;amp;rsquo;s automation anyway? I&amp;amp;rsquo;d call it &amp;amp;ldquo;&amp;lt;em&amp;gt;software that reduces the amount of effort humans need to put in to get a job done&amp;lt;/em&amp;gt;&amp;amp;rdquo;. It replaces people work with CPU work.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;/section&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;As I continued my journey as a developer, I found that the cost/benefit time ratio around automation didn&amp;amp;rsquo;t line up with my thinking. Time trade-offs are essential, but there are more variables in the trade-off equation than time&amp;amp;ndash;other ways of &amp;lt;strong&amp;gt;&amp;lt;em&amp;gt;recouping effort&amp;lt;/em&amp;gt;&amp;lt;/strong&amp;gt;. A particular automation may have other goals than time-savings.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;h2 id=&amp;#34;planning-for-a-big-push&amp;#34;&amp;gt;Planning for a big push&amp;#xA;&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;Perhaps the usual routine doesn&amp;amp;rsquo;t deserve automation. What if your team plans a sprint next month, focusing everyone&amp;amp;rsquo;s effort on a big change? The trade-offs are different: Increasing the frequency of a task and the number of people to onboard to a task both change the math, raising the value of automation. A bit of scripting here also improves the chances the team meets a time-bound goal.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;&amp;lt;figure&amp;gt;&amp;#xA;&amp;lt;img src=&amp;#34;https://imgs.xkcd.com/comics/2045.png&amp;#34;&amp;#xA;&amp;#x9;&amp;#xA;&amp;#x9;&amp;#xA;&amp;#x9;&amp;#xA;&amp;#x9;loading=&amp;#34;lazy&amp;#34;&amp;#xA;&amp;#x9;&amp;#xA;&amp;#x9;&amp;#x9;alt=&amp;#34;(Cueball, a friend also drawn as Cueball, Danish, and Black Hat are standing together. Danish is looking at her phone.)&amp;#xA;Cueball: …And then after the one in 2024, there’s another on August 12, 2045.&amp;#xA;Friend: We’re in! We can invite our kids, assuming we have any.&amp;#xA;Danish: I’ll create an event. Do you think we’ll still be using Google Calendar in 2045?&amp;#xA;Black Hat: Sorry, I’d love to make it, but I have a thing that day.&amp;#xA;(Caption below the panel:)&amp;#xA;It’s weird making plans for eclipses.&amp;#34;&amp;#xA;&amp;#x9; title=&amp;#34;XKCD 2685: 2045&amp;#34;&amp;#xA;&amp;gt;&amp;#xA;&amp;lt;figcaption&amp;gt;&amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://xkcd.com/2685/&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;XKCD 2685: 2045&amp;lt;/a&amp;gt;&amp;lt;/figcaption&amp;gt;&amp;#xA;&amp;lt;/figure&amp;gt;&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;This kind of automation makes the most sense when swarming on a specific task, such as updating code to use a new logging library or migrating data between systems. This may represent a one-time need, so build with minimal flexibility and worry more about good error messages than over-all maintainability. (&amp;lt;em&amp;gt;This is a good use for a shell script shared, maybe not even committed to source control.&amp;lt;/em&amp;gt;)&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;h2 id=&amp;#34;hiding-complexity&amp;#34;&amp;gt;Hiding complexity&amp;#xA;&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;Folks need to do perform complex tasks but workflow automation can simplify the details. You don&amp;amp;rsquo;t need to ask everyone to carry the full cognitive load when tools can do more of the lifting. It also provides paths to onboard folks to the team in stages. How fast can folks be productive in your environment? For &amp;amp;ldquo;new person job satisfaction&amp;amp;rdquo;, it is best to measure this in hours or days instead of weeks.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;More persistent automation like this is an abstraction layer to hide a mess.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;&amp;lt;figure&amp;gt;&amp;#xA;&amp;lt;img src=&amp;#34;https://imgs.xkcd.com/comics/tech_loops.png&amp;#34;&amp;#xA;&amp;#x9;&amp;#xA;&amp;#x9;&amp;#xA;&amp;#x9;&amp;#xA;&amp;#x9;loading=&amp;#34;lazy&amp;#34;&amp;#xA;&amp;#x9;&amp;#xA;&amp;#x9;&amp;#x9;alt=&amp;#34;A flow chart showing the tooling dependencies you need to be productive, with loops back showing the chaos and the way tooling becomes it’s own goal&amp;#34;&amp;#xA;&amp;#x9; title=&amp;#34;XKCD 1579: Tech Loops&amp;#34;&amp;#xA;&amp;gt;&amp;#xA;&amp;lt;figcaption&amp;gt;&amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://xkcd.com/1579/&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;XKCD 1579: Tech Loops&amp;lt;/a&amp;gt;&amp;lt;/figcaption&amp;gt;&amp;#xA;&amp;lt;/figure&amp;gt;&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;Reducing cognitive load helps makes a complex system more approachable. More than a simplification in performing the task, it provides the vocabulary for non-experts to discuss the process. When folks have the ability to communicate about a task, new opportunities for collaboration open.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;&amp;lt;figure&amp;gt;&amp;#xA;&amp;lt;img src=&amp;#34;https://imgs.xkcd.com/comics/average_familiarity.png&amp;#34;&amp;#xA;&amp;#x9;&amp;#xA;&amp;#x9;&amp;#xA;&amp;#x9;&amp;#xA;&amp;#x9;loading=&amp;#34;lazy&amp;#34;&amp;#xA;&amp;#x9;&amp;#xA;&amp;#x9;&amp;#x9;alt=&amp;#34;Two people talking. Person 1 says “Silicate chemistry is second nature to us geochemists, so it’s easy to forget that the average person probably only knows the formulas for olivine and one or two feldspars.” Person 2 says “And quartz, of course.” Person 1 says “Of course.” Below the panel “Even when they’re trying to compensate for it, experts in anything wildly overestimate the average person’s familiarity with their field.”&amp;#34;&amp;#xA;&amp;#x9; title=&amp;#34;XKCD 2501: Average Familiarity&amp;#34;&amp;#xA;&amp;gt;&amp;#xA;&amp;lt;figcaption&amp;gt;&amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://xkcd.com/2501/&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;XKCD 2501: Average Familiarity&amp;lt;/a&amp;gt;&amp;lt;/figcaption&amp;gt;&amp;#xA;&amp;lt;/figure&amp;gt;&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;h2 id=&amp;#34;what-do-you-do-every-day&amp;#34;&amp;gt;What do you do every day?&amp;#xA;&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;In the last month, I&amp;amp;rsquo;ve given thought to the developer superpower of &amp;amp;ldquo;defining the job you want&amp;amp;rdquo;. Automation is often thought of as a way to create more space to do the job you already have, but it is also a way to &amp;lt;strong&amp;gt;redefine the job&amp;lt;/strong&amp;gt;. You can reinvest the time gained from automation on improving the automation, perhaps even expanding the scope of what the task achieves for your team. As a software developer, maintaining evolving software is more satisfying than turning a crank every 5 minutes!&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;&amp;lt;figure&amp;gt;&amp;#xA;&amp;lt;img src=&amp;#34;https://imgs.xkcd.com/comics/automation.png&amp;#34;&amp;#xA;&amp;#x9;&amp;#xA;&amp;#x9;&amp;#xA;&amp;#x9;&amp;#xA;&amp;#x9;loading=&amp;#34;lazy&amp;#34;&amp;#xA;&amp;#x9;&amp;#xA;&amp;#x9;&amp;#x9;alt=&amp;#34;Top caption says “I spend a lot of time on this task. I should write a program automating it! Below are two graphs, the first showing the theoretical spike in workload to write code followed by a significant reduction in effort. The second shows the reality of a spike in workload followed by a runaway increase in work developing the new system instead of performing the original task at all&amp;#34;&amp;#xA;&amp;#x9; title=&amp;#34;XKCD 1319: Automation&amp;#34;&amp;#xA;&amp;gt;&amp;#xA;&amp;lt;figcaption&amp;gt;&amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://xkcd.com/1319/&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;XKCD 1319: Automation&amp;lt;/a&amp;gt;&amp;lt;/figcaption&amp;gt;&amp;#xA;&amp;lt;/figure&amp;gt;&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;h2 id=&amp;#34;the-checklist&amp;#34;&amp;gt;The Checklist&amp;#xA;&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;When I think of automation now, I consider these factors:&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;ul&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;Will it save time? When will the up-front investment pay off?&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;Will it help others contribute in a complex process?&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;Will it make discussing a big, complex system easier, leading to better high-level decisions?&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;Will it create space for more interesting activities?&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;Will it replace existing activities with a style of work I prefer?&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;/ul&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Don&amp;amp;rsquo;t limit your thinking about the time payoff of automating something, but do consider all the relevant trade-offs.&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;Do you have a favorite XKCD comic that helps you think about automation? &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://hachyderm.io/@grayside&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;Let&amp;amp;rsquo;s talk about it!&amp;lt;/a&amp;gt;.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;Additional Credits: Thanks to &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://www.explainxkcd.com/&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;explain xkcd&amp;lt;/a&amp;gt; for XKCD alt text (not to be confused with XKCD hover text, which you get by visiting xkcd.com).&amp;lt;/p&amp;gt;&amp;#xA;</description></item><item><title>Modernizing cloudbuild.yaml Container Builds</title><link>https://adamross.dev/p/modernizing-cloudbuild.yaml-container-builds/</link><pubDate>Thu, 21 Mar 2024 21:05:17 +0000</pubDate><guid>https://adamross.dev/p/modernizing-cloudbuild.yaml-container-builds/</guid><description>
&amp;lt;section class=&amp;#34;context callout callout-info&amp;#34;&amp;gt;&amp;#xA; &amp;lt;p role=&amp;#34;note&amp;#34;&amp;gt;tl;dr: &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://cloud.google.com/build/docs/configuring-builds/run-bash-scripts&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;Running bash scripts&amp;lt;/a&amp;gt; in the Cloud Build documentation tells you use the &amp;lt;code&amp;gt;script&amp;lt;/code&amp;gt; property with the &amp;lt;code&amp;gt;automapSubstitutions&amp;lt;/code&amp;gt; option and I give this recommendation a 💯.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;/section&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;One of my favorite things to do in writing YAML is minimizing the square brackets and hyphens. The frequency of these things really impacts readability for me, especially when I&amp;amp;rsquo;m trying to glance at a whole block of markup and assess what it&amp;amp;rsquo;s trying to do.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;Over the last couple years, Cloud Build has made improvements in reducing square brackets and enabling more concise configuration for running shell scripts.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;I&amp;amp;rsquo;m going to step through a couple &amp;amp;ldquo;generations&amp;amp;rdquo; of simplification I walked through today in overhauling a container image build pipeline.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;This is what a lot of minimal Cloud Build configurations look like:&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;div class=&amp;#34;highlight&amp;#34;&amp;gt;&amp;lt;pre tabindex=&amp;#34;0&amp;#34; class=&amp;#34;chroma&amp;#34;&amp;gt;&amp;lt;code class=&amp;#34;language-yaml&amp;#34; data-lang=&amp;#34;yaml&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;steps&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;- &amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;id&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;Build Container Image&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;gcr.io/cloud-builders/docker:latest&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;args&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;[&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;build&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;--tag&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;gcr.io/${PROJECT_ID}/${_IMAGE}:latest&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;.&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;]&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;- &amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;id&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;Push Container Image to Container Registry&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;gcr.io/cloud-builders/docker:latest&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;args&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;[&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;push&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;,&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;gcr.io/${PROJECT_ID}/${_IMAGE}:latest&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;]&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;substitutions&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;_IMAGE&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l&amp;#34;&amp;gt;service&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;ul&amp;gt;&amp;#xA;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Starting character count:&amp;lt;/strong&amp;gt; 335&amp;lt;/li&amp;gt;&amp;#xA;&amp;lt;/ul&amp;gt;&amp;#xA;&amp;lt;h2 id=&amp;#34;step-1-remove-square-bracketed-distractions&amp;#34;&amp;gt;Step 1: Remove square-bracketed [distractions]&amp;#xA;&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;Use the &amp;lt;code&amp;gt;script&amp;lt;/code&amp;gt; property instead of &amp;lt;code&amp;gt;args&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;div class=&amp;#34;highlight&amp;#34;&amp;gt;&amp;lt;pre tabindex=&amp;#34;0&amp;#34; class=&amp;#34;chroma&amp;#34;&amp;gt;&amp;lt;code class=&amp;#34;language-yaml&amp;#34; data-lang=&amp;#34;yaml&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;steps&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;- &amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;id&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;Build Container Image&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;gcr.io/cloud-builders/docker:latest&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;script&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l&amp;#34;&amp;gt;docker build --tag gcr.io/${PROJECT_ID}/${_IMAGE}:latest .&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;- &amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;id&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;Push Container Image to Container Registry&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;gcr.io/cloud-builders/docker:latest&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;script&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l&amp;#34;&amp;gt;docker push gcr.io/${PROJECT_ID}/${_IMAGE}:latest&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;substitutions&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;_IMAGE&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l&amp;#34;&amp;gt;service&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;p&amp;gt;🛑 If you try to use this it won&amp;amp;rsquo;t work.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;It turns out switching from &amp;lt;code&amp;gt;args&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;script&amp;lt;/code&amp;gt; means the code &amp;lt;strong&amp;gt;no longer has substitutions, only environment variables&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;A year ago, the next step would be to manually map the substitutions to environment variables:&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;div class=&amp;#34;highlight&amp;#34;&amp;gt;&amp;lt;pre tabindex=&amp;#34;0&amp;#34; class=&amp;#34;chroma&amp;#34;&amp;gt;&amp;lt;code class=&amp;#34;language-yaml&amp;#34; data-lang=&amp;#34;yaml&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;steps&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;- &amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;id&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;Build Container Image&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;gcr.io/cloud-builders/docker:latest&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;script&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l&amp;#34;&amp;gt;docker build --tag gcr.io/${PROJECT_ID}/${_IMAGE}:latest .&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;- &amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;id&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;Push Container Image to Container Registry&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;gcr.io/cloud-builders/docker:latest&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;script&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l&amp;#34;&amp;gt;docker push gcr.io/${PROJECT_ID}/${_IMAGE}:latest&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;options&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;env&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;- &amp;lt;span class=&amp;#34;l&amp;#34;&amp;gt;PROJECT_ID=$PROJECT&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;- &amp;lt;span class=&amp;#34;l&amp;#34;&amp;gt;_IMAGE=$_IMAGE&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;substitutions&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;_IMAGE&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l&amp;#34;&amp;gt;service&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Updated character count&amp;lt;/strong&amp;gt;: 390 (sometimes shorter isn&amp;amp;rsquo;t clearer)&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;h2 id=&amp;#34;step-2-remove-excess-hyphenation&amp;#34;&amp;gt;Step 2: Remove excess hyphenation&amp;#xA;&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;Use the &amp;lt;code&amp;gt;automapSubstitutions&amp;lt;/code&amp;gt; global option and the substitutions are injected as environment variables to all step scripts.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;div class=&amp;#34;highlight&amp;#34;&amp;gt;&amp;lt;pre tabindex=&amp;#34;0&amp;#34; class=&amp;#34;chroma&amp;#34;&amp;gt;&amp;lt;code class=&amp;#34;language-yaml&amp;#34; data-lang=&amp;#34;yaml&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;steps&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;- &amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;id&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;Build Container Image&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;gcr.io/cloud-builders/docker:latest&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;script&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l&amp;#34;&amp;gt;docker build . --tag gcr.io/${PROJECT_ID}/${_IMAGE}:latest&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;- &amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;id&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;Push Container Image to Container Registry&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;gcr.io/cloud-builders/docker:latest&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;script&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l&amp;#34;&amp;gt;docker push gcr.io/${PROJECT_ID}/${_IMAGE}:latest&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;options&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;automapSubstitutions&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;kc&amp;#34;&amp;gt;true&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;substitutions&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;_IMAGE&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l&amp;#34;&amp;gt;service&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Updated character count&amp;lt;/strong&amp;gt;: 373 (progress, but I&amp;amp;rsquo;d be more satisfied if we got back to 335 somehow)&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;h2 id=&amp;#34;step-3-simplify-the-image-push&amp;#34;&amp;gt;Step 3: Simplify the Image Push&amp;#xA;&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;Since this Cloud Build configuration doesn&amp;amp;rsquo;t have any follow-up steps that need to use the image outside the build, I can simplify further, by replacing the step &amp;lt;em&amp;gt;Push Container Image to Container Registry&amp;lt;/em&amp;gt; with the &amp;lt;code&amp;gt;images&amp;lt;/code&amp;gt; property. (Read more about that in &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://dev.to/googlecloud/remember-images-in-your-cloudbuild-yaml-o3l&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;Remember images in your cloudbuild.yaml!&amp;lt;/a&amp;gt; by @glasnt).&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;div class=&amp;#34;highlight&amp;#34;&amp;gt;&amp;lt;pre tabindex=&amp;#34;0&amp;#34; class=&amp;#34;chroma&amp;#34;&amp;gt;&amp;lt;code class=&amp;#34;language-yaml&amp;#34; data-lang=&amp;#34;yaml&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;steps&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;- &amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;id&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;Build Container Image&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;gcr.io/cloud-builders/docker:latest&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;script&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l&amp;#34;&amp;gt;docker build . --tag gcr.io/${PROJECT_ID}/${_IMAGE}:latest&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;images&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;- &amp;lt;span class=&amp;#34;l&amp;#34;&amp;gt;gcr.io/${PROJECT_ID}/${_IMAGE}:latest&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;options&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;automapSubstitutions&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;kc&amp;#34;&amp;gt;true&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;substitutions&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;_IMAGE&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l&amp;#34;&amp;gt;service&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;p&amp;gt;This doesn&amp;amp;rsquo;t reduce hyphens any further, but it does reduce two lines of configuration.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Updated character count&amp;lt;/strong&amp;gt;: 263 (Success!)&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;hr&amp;gt;&amp;#xA;&amp;lt;h2 id=&amp;#34;bonus-round-migrating-to-artifact-registry&amp;#34;&amp;gt;Bonus Round: Migrating to Artifact Registry&amp;#xA;&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;You may have noticed this configuration ships the container image to a Google Container Registry URL! There&amp;amp;rsquo;s a good chance if you are modernizing your Cloud Build YAML this way you&amp;amp;rsquo;ve got some GCR in place, if so, you need to migrate soon.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;blockquote&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;Check out &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://cloud.google.com/artifact-registry/docs/transition/transition-from-gcr&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;Transition from Container Registry&amp;lt;/a&amp;gt;. There are options to migrate while keeping the gcr.io URL.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;/blockquote&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;Say I&amp;amp;rsquo;ve already created a new Artifact Registry instance for my container, what does that YAML look like?&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;(Let&amp;amp;rsquo;s say it&amp;amp;rsquo;s a multi-regional repository called &amp;lt;code&amp;gt;container&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;us&amp;lt;/code&amp;gt; location.)&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;div class=&amp;#34;highlight&amp;#34;&amp;gt;&amp;lt;pre tabindex=&amp;#34;0&amp;#34; class=&amp;#34;chroma&amp;#34;&amp;gt;&amp;lt;code class=&amp;#34;language-yaml&amp;#34; data-lang=&amp;#34;yaml&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;steps&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;- &amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;id&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;Build Container Image&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;gcr.io/cloud-builders/docker:latest&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;script&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l&amp;#34;&amp;gt;docker build . --tag &amp;amp;#34;us-docker.pkg.dev/${PROJECT_ID}/container/${_IMAGE}:latest&amp;amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;images&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;- &amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;amp;#34;us-docker.pkg.dev/${PROJECT_ID}/container/${_IMAGE}:latest&amp;amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;options&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;automapSubstitutions&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;kc&amp;#34;&amp;gt;true&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;substitutions&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;_IMAGE&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l&amp;#34;&amp;gt;service&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;p&amp;gt;Since an Artifact Repository is a Cloud resource, a project might have more than one. This encourages me to parameterize the location and repository name, so that development &amp;amp;amp; testing are easier.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;div class=&amp;#34;highlight&amp;#34;&amp;gt;&amp;lt;pre tabindex=&amp;#34;0&amp;#34; class=&amp;#34;chroma&amp;#34;&amp;gt;&amp;lt;code class=&amp;#34;language-yaml&amp;#34; data-lang=&amp;#34;yaml&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;steps&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;- &amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;id&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;Build Container Image&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;s1&amp;#34;&amp;gt;&amp;amp;#39;gcr.io/cloud-builders/docker:latest&amp;amp;#39;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;script&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l&amp;#34;&amp;gt;docker build . --tag &amp;amp;#34;${_LOCATION}-docker.pkg.dev/${PROJECT_ID}/${_REPO}/${_IMAGE}:latest&amp;amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;images&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;- &amp;lt;span class=&amp;#34;s2&amp;#34;&amp;gt;&amp;amp;#34;${_LOCATION}-docker.pkg.dev/${PROJECT_ID}/${_REPO}/${_IMAGE}:latest&amp;amp;#34;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;options&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;automapSubstitutions&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;kc&amp;#34;&amp;gt;true&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;substitutions&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;_IMAGE&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l&amp;#34;&amp;gt;service&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;_LOCATION&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l&amp;#34;&amp;gt;us&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;line&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;cl&amp;#34;&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;nt&amp;#34;&amp;gt;_REPO&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;p&amp;#34;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;l&amp;#34;&amp;gt;container&amp;lt;/span&amp;gt;&amp;lt;span class=&amp;#34;w&amp;#34;&amp;gt;&amp;#xA;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Updated character count&amp;lt;/strong&amp;gt;: 358 (A little longer than 335, but Artifact Registry is more verbose than Container Registry, not much we can do about that.)&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;In a more complicated YAML block, &amp;lt;code&amp;gt;automapSubstitutions&amp;lt;/code&amp;gt; would make a much bigger difference. I go out of my way to avoid using &amp;lt;code&amp;gt;args&amp;lt;/code&amp;gt;, and this option helps me do that without needing to add a lot of boilerplate config.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;section class=&amp;#34;context callout callout-info&amp;#34;&amp;gt;&amp;#xA; &amp;lt;p role=&amp;#34;note&amp;#34;&amp;gt;Aside Info: Reader, maybe you&amp;amp;rsquo;ll suggest I delete all those pesky curly braces around my variables, getting me a win at 342 characters. This &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://stackoverflow.com/a/8748880&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;stackoverflow answer&amp;lt;/a&amp;gt; explains the value of the curly braces, and I&amp;amp;rsquo;m one of the people that makes curly braced variables part of my &amp;amp;ldquo;shell variables in strings&amp;amp;rdquo; practice.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;/section&amp;gt;&amp;#xA;&amp;lt;blockquote&amp;gt;&amp;#xA;&amp;lt;p&amp;gt;Check out a continuation of this post by &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://dev.to/glasnt&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;Katie McLaughlin&amp;lt;/a&amp;gt; at &amp;lt;a class=&amp;#34;link&amp;#34; href=&amp;#34;https://dev.to/googlecloud/codegolf-build-a-container-in-cloud-build-4okn&amp;#34; target=&amp;#34;_blank&amp;#34; rel=&amp;#34;noopener&amp;#34;&amp;#xA; &amp;gt;Codegolf: Build a container in Cloud Build&amp;lt;/a&amp;gt;.&amp;lt;/p&amp;gt;&amp;#xA;&amp;lt;/blockquote&amp;gt;&amp;#xA;</description></item></channel></rss>