<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Steven Hicks</title>
    <description>The latest articles on DEV Community by Steven Hicks (@pepopowitz).</description>
    <link>https://dev.to/pepopowitz</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F138085%2F93ac7b47-979b-4176-b6a9-6618f0bdd195.jpeg</url>
      <title>DEV Community: Steven Hicks</title>
      <link>https://dev.to/pepopowitz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pepopowitz"/>
    <language>en</language>
    <item>
      <title>How To De-index Your Docs From Google (And Then Fix It)</title>
      <dc:creator>Steven Hicks</dc:creator>
      <pubDate>Wed, 15 Nov 2023 16:35:03 +0000</pubDate>
      <link>https://dev.to/pepopowitz/how-to-de-index-your-docs-from-google-and-then-fix-it-4gkp</link>
      <guid>https://dev.to/pepopowitz/how-to-de-index-your-docs-from-google-and-then-fix-it-4gkp</guid>
      <description>&lt;h2&gt;
  
  
  1. The introduction
&lt;/h2&gt;

&lt;p&gt;Software documentation is a critical element of developer experience. I don't believe this is a bold or disagreeable statement.&lt;/p&gt;

&lt;p&gt;Think about how you engage with documentation. Occasionally, you know exactly what you're looking for — you could browse the documentation navigation to find the page you need. More often, you either don't know exactly what you're looking for, or you don't know where it lives in the documentation.&lt;/p&gt;

&lt;p&gt;The majority of time you interact with documentation, you probably search for the help you need. That search might happen within the documentation site itself. It might happen in your favorite search engine. Wherever it happens, you expect to find helpful results to your search terms. If you don't find helpful results, you're likely to get stuck. Get stuck too many times, and you give up entirely. This is the nightmare of every tool built for developers.&lt;/p&gt;

&lt;p&gt;Imagine the docs you most interact with. You know them reasonably well, but you've forgotten a detail that you know the docs can explain. Envision yourself searching for help on this topic. Imagine browsing the results for your query — and seeing....&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;0 results found.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Zero results??!! What??? You &lt;em&gt;know&lt;/em&gt; it's in there somewhere. (╯°□°)╯︵ ┻━┻&lt;/p&gt;

&lt;h3&gt;
  
  
  The real-life story of de-indexing our docs
&lt;/h3&gt;

&lt;p&gt;For months I worked on reviving the search results for &lt;a href="https://docs.camunda.org/"&gt;Camunda's version 7 documentation&lt;/a&gt;. Our docs weren't giving 0 results for most queries, but they weren't far off. Known pertinent results were excluded consistently. Most of the results that came up were slightly related but not helpful.&lt;/p&gt;

&lt;p&gt;Throughout this I learned unexpected details about SEO (Search Engine Optimization) — especially Google's flavor of SEO. In the end we were able to revive the massively deindexed content. I had my doubts that we'd get there.&lt;/p&gt;

&lt;h3&gt;
  
  
  What to expect from this article
&lt;/h3&gt;

&lt;p&gt;This article describes my journey through this problem of vanishing search results in the Camunda 7 (C7) docs. There are 5 parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The introduction. You're almost finished reading that.&lt;/li&gt;
&lt;li&gt;The disappearance.&lt;/li&gt;
&lt;li&gt;The investigation.&lt;/li&gt;
&lt;li&gt;The resolution.&lt;/li&gt;
&lt;li&gt;The recommendation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That final fifth part is the payoff. If you're managing a documentation site, and unsure how to handle versioned documentation from an SEO perspective, and just looking for guidance, jump there. Most guidance for handling duplicate content in regards to SEO ignores one prime use case — versioned documentation. I hope this article fills that gap.&lt;/p&gt;

&lt;p&gt;If you prefer a video format, I've posted &lt;a href="https://www.youtube.com/watch?v=HJGxdj_8oJ8"&gt;a summary of this experience on YouTube&lt;/a&gt;. It's told in a different way, but the message is the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The disappearance
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Setting context
&lt;/h3&gt;

&lt;p&gt;Camunda version 8 (C8) marked a significant change from version 7 (C7). While version 7 was intended for self-managed environments, version 8 is cloud-first. Both versions are still supported, &lt;a href="https://docs.camunda.org/enterprise/announcement/#camunda-extended-support-offering"&gt;at least until 2027&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The large differences in approaches and product features led us to rebuild the documentation for Camunda 8. C7 docs are hosted at &lt;a href="https://docs.camunda.org/"&gt;docs.camunda.org&lt;/a&gt;; the version 8 documentation lives at &lt;a href="https://docs.camunda.io/"&gt;docs.camunda.io&lt;/a&gt;. The two different sites are built with different tooling (&lt;a href="https://gohugo.io/"&gt;Hugo&lt;/a&gt; vs &lt;a href="https://docusaurus.io/"&gt;Docusaurus&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;My team, Developer Experience, manages the C8 docs; we &lt;em&gt;interact&lt;/em&gt; with the C7 docs, but we mostly try to leave them alone. Basically, we try to keep the lights on, and not much more. There are issues I'd love to fix in the C7 docs, like the fact that &lt;a href="https://github.com/camunda/camunda-docs-manual#installing-hugo"&gt;they're tied to a very-specific very-old version of Hugo&lt;/a&gt;. But it's not worth the ROI for us...and there are &lt;a href="https://github.com/camunda/camunda-platform-docs/issues"&gt;so many other issues in the C8 docs&lt;/a&gt; that take priority.&lt;/p&gt;

&lt;p&gt;This story is about the C7 docs. The ones that we try not to touch very much....unless something goes very very wrong with them.&lt;/p&gt;

&lt;h4&gt;
  
  
  Camunda 7 documentation details
&lt;/h4&gt;

&lt;p&gt;The Camunda 7 product is currently on version 7.20. Each released version has its own documentation: &lt;a href="https://docs.camunda.org/manual/7.20/"&gt;7.20&lt;/a&gt;, &lt;a href="https://docs.camunda.org/manual/7.19/"&gt;7.19&lt;/a&gt;, &lt;a href="https://docs.camunda.org/manual/7.18/"&gt;7.18&lt;/a&gt;, all the way down to &lt;a href="https://docs.camunda.org/manual/7.0/"&gt;7.0&lt;/a&gt;. There are also two non-numeric versions of the documentation: &lt;a href="https://docs.camunda.org/manual/latest/"&gt;latest&lt;/a&gt; (a mirror of whichever numeric version is latest), and &lt;a href="https://docs.camunda.org/manual/develop/"&gt;develop&lt;/a&gt; (in-progress documentation for the next release). That's &lt;code&gt;n+2&lt;/code&gt; versions of the documentation, where &lt;code&gt;n&lt;/code&gt; is the number of released numeric versions. The early versions were released over 8 years ago! There are a lot of docs.&lt;/p&gt;

&lt;p&gt;Much of the documentation is duplicated across versions. For example, look how similar the "Introduction" page is between &lt;a href="https://docs.camunda.org/manual/7.4/introduction/"&gt;version 7.4&lt;/a&gt; and &lt;a href="https://docs.camunda.org/manual/7.19/introduction/"&gt;version 7.19&lt;/a&gt;. There are a couple small differences, but probably 95% of the content has not changed, over 15 versions. As my 12yo would say, "foreshadowing...."&lt;/p&gt;

&lt;p&gt;One other important detail — there is an in-site search for the C7 docs. Notably, it is built on a &lt;a href="https://programmablesearchengine.google.com/about/"&gt;programmable Google search engine&lt;/a&gt;. Basically, that means that the search functionality on the site is powered by Google. A search query entered into the C7 docs search box gives basically the same results as entering the query on google.com and filtering by &lt;code&gt;site:docs.camunda.org&lt;/code&gt;. Again, "foreshadowing...."&lt;/p&gt;

&lt;h3&gt;
  
  
  First report
&lt;/h3&gt;

&lt;p&gt;At some point in 2021, the search experience on docs.camunda.org began to degrade. It was not sudden, or obvious. But search results became less helpful, and obvious hits on specific search queries began to disappear. A search for BPMN — a critical and core technology in the Camunda ecosystem, and definitely documented — returned zero helpful results. Since the in-site search is built on Google, the results were degraded both within the site, and directly from google.com.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://forum.camunda.io/t/camunda-docs-repo-search-is-not-working-as-expected/32044"&gt;The community noticed&lt;/a&gt;. I want to say we &lt;em&gt;heard&lt;/em&gt; them....but we didn't realize how bad it was, or how much it was affecting people. We thought these were one-off complaints. We didn't prioritize the work to fix the search experience.&lt;/p&gt;

&lt;p&gt;In mid-2022, we heard from Camunda's support team. They emphasized the problem. Every day, they help Camunda users find the answers to their problems. The degrading search experience was making that much harder. As a result, every member of the support team had effectively built a &lt;a href="https://en.wikipedia.org/wiki/Method_of_loci"&gt;memory palace&lt;/a&gt; of the C7 documentation structure. The only way for them to find content in our docs was &lt;em&gt;to already know where it existed&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Upon this discovery, I wrapped up other on-going projects, and we shifted our focus to fixing the C7 search experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The investigation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Finding answers in Google Search Console
&lt;/h3&gt;

&lt;p&gt;Early in our investigation, we noticed something strange in the Google Search Console. Many of the docs for the current version were not indexed. They were filed under the category of &lt;a href="https://support.google.com/webmasters/answer/7440203#duplicate_page_without_canonical_tag"&gt;"Duplicate without user-selected canonical"&lt;/a&gt; — meaning Google chose a different version of the page as canonical, and we didn't specify one with a &lt;code&gt;&amp;lt;link rel="canonical"&amp;gt;&lt;/code&gt; hint.&lt;/p&gt;

&lt;p&gt;This sounded familiar to me. When I learned to program in Ruby, I spent a lot of time searching &lt;a href="https://ruby-doc.org"&gt;ruby-doc.org&lt;/a&gt; for help. One thing I always found interesting was that I'd never get the latest version of the Ruby docs from my search. Searching Google for &lt;a href="https://www.google.com/search?q=ruby+array+map&amp;amp;oq=ruby+array+map"&gt;"ruby array map"&lt;/a&gt; does return a result from ruby-doc.org relatively near the top. But it's for Ruby version 2.7.0, while latest Ruby is currently somewhere in the 3s.&lt;/p&gt;

&lt;p&gt;Our situation was much worse, though. We weren't getting &lt;em&gt;any&lt;/em&gt; version in the results, let alone an older version.&lt;/p&gt;

&lt;p&gt;Inspecting the older version pages in Google Search Console clearly revealed the reason. Our older version pages were excluded from the Google search index because they were &lt;a href="https://support.google.com/webmasters/answer/7440203#submitted_but_noindex"&gt;"Excluded by ‘noindex’ tag"&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Sure enough — our docs &lt;a href="https://github.com/camunda/camunda-docs-manual/blob/master/themes/camunda/layouts/partials/header.html#L24-L26"&gt;had a check in them for the version being rendered&lt;/a&gt;, and if it wasn't the current version, a &lt;code&gt;&amp;lt;meta name="robots" content="noindex"&amp;gt;&lt;/code&gt; directive was applied to the page. We did this with the intention of convincing Google to index only the current version. Unfortunately, it did not have that effect.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why we had zero search results
&lt;/h4&gt;

&lt;p&gt;It was the combination of these two factors that caused Google to index very little of our content. We were telling Google explicitly not to index our older version pages with &lt;code&gt;noindex&lt;/code&gt; directives; Google was choosing those older version pages as the canonical source, and therefore not indexing the latest version pages. 😅😬&lt;/p&gt;

&lt;p&gt;This set me on a learning adventure. To me, it's obvious that in a documentation site, the latest version page is probably the one I want to find in a search. Why doesn't Google think that? And really, how does Google (and any other search engine) handle duplicate content?&lt;/p&gt;

&lt;h3&gt;
  
  
  How search engines handle duplicate content
&lt;/h3&gt;

&lt;p&gt;Duplicate content happens all the time on the internet. Sometimes it's malicious (cue a generative AI ethics discussion), usually it's not. The usual example is a product page that can live in multiple categories, e.g. &lt;code&gt;/tops/cowboy-shirt&lt;/code&gt; and &lt;code&gt;/western-wear/cowboy-shirt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We don't usually see the same page multiple times in search results, because a search engine chooses one as the canonical source. &lt;a href="https://developers.google.com/search/docs/crawling-indexing/canonicalization#canonical-how"&gt;The canonical is chosen based on many factors&lt;/a&gt; -- which page is linked most by the rest of the internet, which page is referenced in a sitemap, etc. Website owners can even suggest a recommendation for a page's canonical URL, via a &lt;a href="https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls#rel-canonical-link-method"&gt;&lt;code&gt;link rel=canonical&lt;/code&gt; hint&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I say "hint" because that's all &lt;code&gt;link rel=canonical&lt;/code&gt; is. Google might, and in my experience often does, choose a different canonical than you specify. It's interpreting a collective story about your page from many different sources. Sometimes, it just doesn't agree with your suggestion — the rest of the internet convinces it to choose a different canonical.&lt;/p&gt;

&lt;p&gt;Our docs weren't doing much to help Google interpret the collective story. We thought we were giving directives about canonicals by applying &lt;code&gt;noindex&lt;/code&gt; to the old versions, but the &lt;code&gt;noindex&lt;/code&gt; was applied separately from the choice of canonicals. We weren't submitting sitemaps with the true canonicals — in fact, we didn't even have sitemaps that were properly formed. It was basically 100% up to Google to figure out which version was canonical, without any of our input.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The resolution
&lt;/h2&gt;

&lt;p&gt;We tried a lot of different things to convince Google to index the most recent version of our documentation. Not many of our attempts seemed to work!&lt;/p&gt;

&lt;p&gt;And the feedback loop for each of them was horribly long! I'd experiment with something, and then wait weeks to see what happened. Even then, it was hard to tell if something worked. It was more obvious if it definitely &lt;em&gt;didn't&lt;/em&gt; work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Submitting a correct sitemap...unsuccessfully
&lt;/h3&gt;

&lt;p&gt;When we realized Google wasn't working off of a sitemap, we went to submit ours, figuring it would be useful as a signal to Google about canonicals. It was rejected 😜 for being in an improper format. I wondered how long it had been in that state...&lt;/p&gt;

&lt;p&gt;We corrected the sitemap's format. Our docs have a redirect rule set up so that if you visit a page &lt;em&gt;without&lt;/em&gt; a version in the URL, it will redirect to the latest version of that page. We tried submitting &lt;a href="https://github.com/camunda/camunda-docs-manual/pull/1345"&gt;a sitemap with these versionless redirecting URLs&lt;/a&gt; — no significant change was affected.&lt;/p&gt;

&lt;p&gt;We tried a sitemap with the latest version hardcoded in the URLs. This had a more positive effect than the redirecting versionless URLs, but still not very significant.&lt;/p&gt;

&lt;h3&gt;
  
  
  Declaring canonicals...unsuccessfully
&lt;/h3&gt;

&lt;p&gt;We shifted our focus to declaring canonicals via &lt;a href="https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls#rel-canonical-link-method"&gt;&lt;code&gt;link rel=canonical&lt;/code&gt;&lt;/a&gt;. Since we have so many versions of documentation, we ran these experiments on only a few pages at a time, or sometimes an entire version at a time.&lt;/p&gt;

&lt;p&gt;We started with &lt;a href="https://github.com/camunda/camunda-docs-manual/pull/1345"&gt;pointing canonicals of the current version at the redirecting versionless URLs&lt;/a&gt;. We wanted to use the redirecting URLs so that we would never have to go back and change them. Google was unconvinced, and maintained its own opinion, usually canonicalizing an older version of the page. We also got new errors from the redirecting versionless URLs, about &lt;a href="https://support.google.com/webmasters/answer/7440203#page_with_redirect"&gt;containing a redirect&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We tried &lt;a href="https://github.com/camunda/camunda-docs-manual/pull/1361"&gt;using self-referential canonicals in the current version&lt;/a&gt;, and submitting the current sitemap. This had no effect.&lt;/p&gt;

&lt;p&gt;Nothing happened when we applied the current version canonicals &lt;a href="https://github.com/camunda/camunda-docs-manual/pull/1370"&gt;to older version pages&lt;/a&gt;, either. Because our older version pages still said &lt;code&gt;noindex&lt;/code&gt;, Google wouldn't even bother to look at the canonical link. These pages remained canonical, but also de-indexed.&lt;/p&gt;

&lt;p&gt;We also experimented with the sequencing of our submissions to Google's crawler. Would it make a difference if we submitted an older page first, then re-submitted a current version page? If we submitted an entire sitemap vs an individual page?&lt;/p&gt;

&lt;p&gt;Sadly, no. Occasionally something good would happen, but results were not repeatable or predictable. It seemed like there was nothing we could do to convince Google to consistently choose our latest version docs as canonical. Each experiment took weeks to play out, and it was frustrating.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building a comprehensive story...and waiting
&lt;/h3&gt;

&lt;p&gt;We noticed that Google was doing a lot more crawling of our docs when we released a new version, and a flood of new pages came online. Given what we'd learned about how search engines choose a canonical source — by piecing together clues from a website &lt;em&gt;and&lt;/em&gt; the rest of the internet — we decided to take one final half-court shot.&lt;/p&gt;

&lt;p&gt;In a few months, we'd release version 7.19, and Google would crawl it thoroughly. If we could put together a comprehensive story, with correct canonicals on most versions, and an accurate sitemap, maybe this flurry of crawling activity would convince it to canonicalize and index the correct versions.&lt;/p&gt;

&lt;p&gt;We weren't sure what to do about the &lt;code&gt;noindex&lt;/code&gt; situation. My opinion was that the story would be more comprehensive with the older versions &lt;strong&gt;not&lt;/strong&gt; &lt;code&gt;noindex&lt;/code&gt;ed. We ran &lt;a href="https://gist.github.com/pepopowitz/c0c9bc63bd50318f54a17e19fd9788f5"&gt;an experiment&lt;/a&gt; to see if there was a positive effect on canonicals when I removed &lt;code&gt;noindex&lt;/code&gt;. &lt;a href="https://gist.github.com/pepopowitz/f672e1d2bef12ad2adf7cb70e261fe38"&gt;There was no positive effect&lt;/a&gt;. We decided to leave all older versions marked as &lt;code&gt;noindex&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Months later, when we released our next version &lt;a href="https://docs.camunda.org/manual/7.19/"&gt;(7.19)&lt;/a&gt;, we submitted a sitemap containing only the 7.19 pages, and crossed our fingers. And waited again.&lt;/p&gt;

&lt;h3&gt;
  
  
  Success!
&lt;/h3&gt;

&lt;p&gt;Over the next few months, we received a couple notes from our support team that suggested things had improved.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9RnnJN8a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ftwkp6a0a5krkrlrqpkt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9RnnJN8a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ftwkp6a0a5krkrlrqpkt.png" alt='A message in Slack: "I completely forgot that C7 doc search works again. I just tried it and ... IT ACTUALLY WORKS!"' width="800" height="159"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Eventually I logged into Google Search Console to see if things had improved. It not only had improved, but it had improved &lt;strong&gt;remarkably&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/pepopowitz/cbda099efb429a904369398fb107c480"&gt;90% of our 7.19 pages were indexed&lt;/a&gt;! I checked back later, and &lt;a href="https://gist.github.com/pepopowitz/64ff0c6d19c56f328531ec6cfc582807"&gt;all but 4 of 500 pages were indexed&lt;/a&gt;. Google finally agrees with us! Current version pages are canonical. And more importantly, you get accurate results again when searching our docs.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. The recommendation
&lt;/h2&gt;

&lt;p&gt;Whew, the payoff! So you've got a documentation site, with duplicate content across versions. You don't want to re-live our awful experience of de-indexing your docs. What should you do?&lt;/p&gt;

&lt;h3&gt;
  
  
  Build a cohesive picture about the canonical source.
&lt;/h3&gt;

&lt;p&gt;There's no one thing that resolved our situation. Search engines take a wholistic look at your website, and you need to make sure every version is telling a consistent story. &lt;a href="https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls"&gt;Hints and signals compound&lt;/a&gt;. The more comprehensive story you tell, the more likely search engines are to agree with your canonical suggestions.&lt;/p&gt;

&lt;p&gt;More specifically, in our experience that means:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use &lt;code&gt;link rel=canonical&lt;/code&gt; tags on all older version documents, pointed at the current version.&lt;/li&gt;
&lt;li&gt;Submit a sitemap that contains absolute URLs of only the current version of documents.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There's not much there, but any slight deviation can cause havoc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Some other important notes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;link rel=canonical&lt;/code&gt; is only a suggestion!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is definitely important to specify canonicals, but they aren't a guarantee. Search engines might very well choose a different page if the comprehensive story suggests your preferred canonical is incorrect.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Self-referential canonicals are probably not helpful.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=TepFVYrBVg0&amp;amp;t=968s"&gt;The only time they're helpful is when the visited URL doesn't match the canonical URL&lt;/a&gt;. If links to your docs include query-string parameters for tracking, they might be helpful. That's not how we use our docs, so declaring a self-referential canonical would give a search engine as much information as declaring &lt;em&gt;no&lt;/em&gt; canonical.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Probably don't use &lt;code&gt;noindex&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is straying a bit from science, as my experimentation suggested that removing the &lt;code&gt;noindex&lt;/code&gt; tag from our older versions was actually detrimental to our situation. But hear me out.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; All of my results at the time I ran that experiment were confusing, inconsistent, and reliable. I think removing &lt;code&gt;noindex&lt;/code&gt; before The Big Recrawl (when we released a new version) would have also resulted in strong canonicalization success.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Google recommends that &lt;a href="https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls"&gt;you &lt;em&gt;don't&lt;/em&gt; use &lt;code&gt;noindex&lt;/code&gt; to prevent canonicalization&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We don't recommend using noindex to prevent selection of a canonical page within a single site, because it will completely block the page from Search. rel="canonical" link annotations are the preferred solution.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Returning to my &lt;a href="https://ruby-doc.org"&gt;Ruby docs&lt;/a&gt; example from earlier — they have canonicalization problems, but their problems are way less severe than ours were. If we were indexing our old versions, our search wouldn't have broken. It would have served old versions for search results, but that's a much better situation for a stuck user than &lt;em&gt;zero&lt;/em&gt; results.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Epic conclusion
&lt;/h2&gt;

&lt;p&gt;This was the classic ambiguous and confusing software problem. The system was broken, and there was very little help pinpointing why. There are no examples on the internet about how to handle content duplication in versioned documentation. The horribly loose feedback loop when making changes, the unpredictability and inconsistency of canonical selection, it just all felt like I was powerless and guessing. As I got deeper into it, it became something I couldn't &lt;em&gt;not&lt;/em&gt; solve. It haunted me a little. I had basically exhausted our appetite to figure it out when we took our final half-court shot. I'm glad it worked out, because that was probably my last attempt before saying "🤷🏼 sorry it just doesn't work."&lt;/p&gt;

</description>
      <category>seo</category>
      <category>documentation</category>
      <category>webdev</category>
      <category>search</category>
    </item>
    <item>
      <title>Matches, Spoons, And The Relationship Between DX and UX</title>
      <dc:creator>Steven Hicks</dc:creator>
      <pubDate>Thu, 16 Mar 2023 14:01:00 +0000</pubDate>
      <link>https://dev.to/pepopowitz/matches-spoons-and-the-relationship-between-dx-and-ux-2723</link>
      <guid>https://dev.to/pepopowitz/matches-spoons-and-the-relationship-between-dx-and-ux-2723</guid>
      <description>&lt;p&gt;Developer Experience (DX) is kind of having a moment right now — and not necessarily a good one. In recent months there's been a lot of discourse pushing back on the importance of DX — or at least its importance relative to the importance of User Experience (UX). My favorite article to capture the moment is &lt;a href="https://begin.com/blog/posts/2023-02-28-redefining-developer-experience"&gt;Redefining Developer Experience&lt;/a&gt;, by &lt;a href="https://mastodon.online/@colepeters"&gt;Cole Peters&lt;/a&gt;. Start there, if you aren't sure what I'm referring to.&lt;/p&gt;

&lt;p&gt;I recently listened to a podcast episode that talked about the relationship between Developer Experience and User Experience. I don't recall what the episode said about this relationship....I &lt;em&gt;do&lt;/em&gt; recall that I wasn't satisfied with it.&lt;/p&gt;

&lt;p&gt;It's very possible that a part of me wants to write this article to make myself feel better about my life choices. I'm less than a year into my first job with the words "developer experience" in the title. Still, even if it's confirmation bias, I truly think I believe these things.&lt;/p&gt;

&lt;p&gt;Since its conception, the field of Developer Experience has not defined itself clearly. As &lt;a href="https://begin.com/blog/posts/2023-02-28-redefining-developer-experience"&gt;Cole suggests&lt;/a&gt;, this lack of a consistent interpretation or definition is certainly a significant factor in the disagreement on the importance of DX.&lt;/p&gt;

&lt;p&gt;Not long ago I was at a conference, where I met two other people with the exact title as me — "Developer Experience Engineer." All three of us did completely different things. When I meet people and tell them my title, they often ask "what does that mean"; sometimes I introduce myself as "a Developer Experience Engineer...but I'm not sure what that means!" Beyond this specific title, there seems to be an identity crisis for the entire term "developer experience".&lt;/p&gt;

&lt;p&gt;I'm not sure how you think about DX. I've got two metaphors, and one questionable psychological theory, to explain how &lt;em&gt;I&lt;/em&gt; think about DX, why I think it is important, and how I see its relation to UX.&lt;/p&gt;

&lt;h2&gt;
  
  
  Burning matches
&lt;/h2&gt;

&lt;p&gt;If you watch a cycling race, you're likely to hear the broadcasters talk about burning matches. The metaphor is this: at the beginning of a race, each rider starts with a book of matches. Each hard effort a rider gives burns one match from the book. Riders can choose when to burn each match...but once the matches are all burned, they have no hard efforts remaining.&lt;/p&gt;

&lt;p&gt;You may have noticed this in any sport. When you're fresh, you can put in a hard effort. It feels great! But put in a bunch of hard efforts over time, and your body just can't give you anything more.&lt;/p&gt;

&lt;p&gt;In a cycling race, it's a balancing act of burning the matches at the best time. If you can save a match for the sprint at the end of a race, you've got a chance of winning it. Burn them all too early, and you don't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spoon theory
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Spoon_theory"&gt;Spoon theory&lt;/a&gt; is a metaphor originally used in the context of chronic illness, and which has since been used to describe many other scenarios.&lt;/p&gt;

&lt;p&gt;Imagine a person starts their day with a limited number of spoons. Each activity throughout the day exhausts energy, and uses one spoon — even ordinary tasks. By the end of the day, their spoons are all gone, and the person has no remaining energy for even a small task.&lt;/p&gt;

&lt;p&gt;Resting replenishes their spoons, so they can start over again the next day. But spoons need to be managed carefully every day, or the person runs the risk of exhausting their supply long before bedtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ego depletion
&lt;/h2&gt;

&lt;p&gt;Both these metaphors "rhyme" with the concept of &lt;a href="https://en.wikipedia.org/wiki/ego_depletion"&gt;ego depletion&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ego depletion refers to the idea that our willpower is based on our mental energy, which is of limited supply. The more mental energy we have, the more likely we are to show self-control, and make choices that are better for us in the long term. As we use our mental energy throughout the day, our willpower is weakened, and we're more likely to make short-sighted choices for immediate satisfaction. Think food choices — you might find it manageable to avoid unhealthy foods early in the day...but after a long frustrating day of work, cookies are hard to resist. Maybe that's just me.&lt;/p&gt;

&lt;p&gt;The concept of ego depletion is controversial. The validity of studies that demonstrate ego depletion are questioned. This is one of the many ideas that frustratingly falls into the bucket of "seems logical....no proof in either direction....and it might just be in your head."&lt;/p&gt;

&lt;p&gt;But hey, I believe in it! Therefore it is real. For me, at least.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does this have to do with building software?
&lt;/h2&gt;

&lt;p&gt;Ego depletion, or something like it, appears in software development too. When a developer's tooling is working for them, they're able to put more effort toward the details that make a difference for the user. When the tooling fights them, their energy is exhausted, and they're less likely to put effort toward user-facing details.&lt;/p&gt;

&lt;p&gt;If I burn two matches trying to test an asynchronous event handler on a React component, those matches are gone! If I spend three spoons fighting a weird webpack error, I'm becoming exhausted. Neither of these issues directly impact the user, but I've burned my energy on them. When something comes up late in the day that &lt;em&gt;actually&lt;/em&gt; impacts the user — maybe something missing in regards to accessibility, maybe an important edge case in some component that only affects a small amount of users — my energy to deal with it appropriately depends on how much I've fought my tooling all day.&lt;/p&gt;

&lt;p&gt;This is why I think Developer Experience matters. DX that works with you leads to better UX. Energy I must put into &lt;em&gt;how&lt;/em&gt; I'm building a product pulls directly from energy I would have put into &lt;em&gt;what&lt;/em&gt; I'm building.&lt;/p&gt;

&lt;p&gt;I don't care as much about how shiny and hot the tools are that I'm using. I care that the tools I'm using are guiding me to a &lt;a href="https://blog.codinghorror.com/falling-into-the-pit-of-success/"&gt;pit of success&lt;/a&gt;, where I don't need to invest a ton of effort to build a proper experience for the user.&lt;/p&gt;

&lt;p&gt;There's certainly a risk of over-optimizing for DX at the expense of UX, in anything we build. Developers can easily overlook that imbalance because they aren't the actual product user. But even when prioritizing the user's experience, the developer's experience matters. If creating a good UX requires a lot of effort from developers, they just aren't going to do it.&lt;/p&gt;

&lt;p&gt;The answer to the question "DX or UX?", as always, lies somewhere in the middle.&lt;/p&gt;

</description>
      <category>dx</category>
      <category>ux</category>
      <category>product</category>
      <category>psychology</category>
    </item>
    <item>
      <title>Breaking Problems Down: A Case Study</title>
      <dc:creator>Steven Hicks</dc:creator>
      <pubDate>Thu, 01 Dec 2022 20:30:32 +0000</pubDate>
      <link>https://dev.to/pepopowitz/breaking-problems-down-a-case-study-24gk</link>
      <guid>https://dev.to/pepopowitz/breaking-problems-down-a-case-study-24gk</guid>
      <description>&lt;p&gt;I've written in the past about &lt;a href="https://artsy.github.io/blog/2021/03/09/strategies-for-small-focused-pull-requests/" rel="noopener noreferrer"&gt;strategies for breaking Pull Requests (PRs) into smaller pieces&lt;/a&gt;. In that article I give a number of recommendations for reducing the size of a PR....but it's still very abstract.&lt;/p&gt;

&lt;p&gt;Recently I spent a couple months working on a large-ish project for the &lt;a href="https://github.com/camunda/camunda-platform-docs" rel="noopener noreferrer"&gt;Camunda Platform 8 documentation&lt;/a&gt;. The size of the project forced me to practice what I preach. It involved moving &lt;em&gt;a lot&lt;/em&gt; of content, which could easily have turned into massive PRs, and left overwhelmed reviewers with no choice but to rubber stamp "LGTM" (looks good to me) on them. I also worked in relative isolation on this project, and since I was much deeper into the work than my reviewers, easily-digested PRs were an absolute requirement.&lt;/p&gt;

&lt;p&gt;So consider this article almost an addendum to &lt;a href="https://artsy.github.io/blog/2021/03/09/strategies-for-small-focused-pull-requests/" rel="noopener noreferrer"&gt;my previous work&lt;/a&gt;. Instead of describing in the abstract, now I can point you at concrete examples!&lt;/p&gt;

&lt;h2&gt;
  
  
  Background: the project
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://camunda.com/platform/" rel="noopener noreferrer"&gt;Camunda Platform 8&lt;/a&gt; includes a handful of components that work together to facilitate process orchestration. Most of them are always on the same version — but some of them aren't! Our &lt;a href="https://camunda.com/platform/optimize/" rel="noopener noreferrer"&gt;Optimize&lt;/a&gt; component, which you might guess empowers you to optimize a modeled process, is on a completely different version number than the rest of the components. Where most components are currently on version 8.1, the latest Optimize release is 3.9.0.&lt;/p&gt;

&lt;p&gt;Unfortunately, our docs weren't reflecting this. We treated the latest version of &lt;em&gt;all&lt;/em&gt; components as version 8, even if that wasn't correct. And that was this project! Get &lt;a href="https://docs.camunda.io/optimize/components/what-is-optimize/" rel="noopener noreferrer"&gt;the Optimize docs&lt;/a&gt; showing the correct version number.&lt;/p&gt;

&lt;p&gt;As with anything, when you boil it down to a paragraph it sounds like way less work than the actual implementation. 😅&lt;/p&gt;

&lt;h2&gt;
  
  
  The work before the work
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://artsy.github.io/blog/2021/03/09/strategies-for-small-focused-pull-requests/#small-prs-start-long-before-the-work-starts" rel="noopener noreferrer"&gt;Small PRs start long before the PRs are opened&lt;/a&gt;. In this case, some up-front investigation helped identify ways to break the work down.&lt;/p&gt;

&lt;h3&gt;
  
  
  Early exploration to identify and understand the work
&lt;/h3&gt;

&lt;p&gt;I had two goals with the early exploration:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;To understand what work was needed, so that I could break it down.&lt;/li&gt;
&lt;li&gt;To resolve some uncertainty about how the tooling supported solving our problem.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So I built some proofs of concept:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://github.com/camunda/camunda-platform-docs/pull/904" rel="noopener noreferrer"&gt;To explore the Docusaurus feature of "multi-instance versioning"&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/camunda/camunda-platform-docs/pull/906" rel="noopener noreferrer"&gt;To rougly apply "multi-instance versioning" to our Optimize documentation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/camunda/camunda-platform-docs/pull/910" rel="noopener noreferrer"&gt;To make it possible to release incomplete changes&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Along the way, I built a couple more proofs of concept when I ran into problems I wasn't sure how to solve:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://github.com/camunda/camunda-platform-docs/pull/921" rel="noopener noreferrer"&gt;Reducing duplication across documentation instances&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/camunda/camunda-platform-docs/pull/1345" rel="noopener noreferrer"&gt;Navigation issues across documentation instances&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This investigation and prototyping resulted in a much better understanding of the work. It even helped us identify work that, if done up front, would improve our ability to schedule and complete the remaining work in three important ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://artsy.github.io/blog/2021/03/09/strategies-for-small-focused-pull-requests/#integrating-code-a-little-at-a-time" rel="noopener noreferrer"&gt;Changes could be introduced incrementally&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://artsy.github.io/blog/2021/03/09/strategies-for-small-focused-pull-requests/#separate-infrastructural-work-from-implementations" rel="noopener noreferrer"&gt;Infrastructural changes could be introduced separate from routine content movement&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://artsy.github.io/blog/2021/03/09/strategies-for-small-focused-pull-requests/#start-with-small-scope--slice-your-stories-small" rel="noopener noreferrer"&gt;Work could be sliced into smaller deliveries&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The critical output of breaking down work is not only the smaller pieces. It's also the knowledge of which pieces are the scariest, riskiest, and most uncertain, so you can solve those first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Make the change easy before making the easy change
&lt;/h3&gt;

&lt;p&gt;Of the 3 improvements listed above, I want to call out one in particular. Before writing a single line of code for the project, I wanted to make sure we could integrate incomplete changes a little bit at a time, especially at the beginning. As the project went on, and PRs started to resemble other previous PRs, it became less important to be able to integrate incrementally. But at the beginning, this was novel work -- we weren't sure what it would/should look like, and I wanted to feel safe introducing it in incomplete parts.&lt;/p&gt;

&lt;p&gt;The first PR I opened for this project was to &lt;a href="https://github.com/camunda/camunda-platform-docs/pull/1118" rel="noopener noreferrer"&gt;introduce a "next" version of the docs&lt;/a&gt;. With an "under construction" version, I felt free to make as many changes there as I wanted. I could deploy incomplete changes and show them to people for feedback.&lt;/p&gt;

&lt;p&gt;This is not the first time I've referenced the following Kent Beck tweet, nor will it be the last:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-250733358307500032-300" src="https://platform.twitter.com/embed/Tweet.html?id=250733358307500032"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-250733358307500032-300');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=250733358307500032&amp;amp;theme=dark"
  }



 &lt;/p&gt;

&lt;p&gt;Building a seam before introducing changes is always easier than doing those two things simultaneously.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tracking the work
&lt;/h2&gt;

&lt;p&gt;After the initial exploration and proposal, this project sat untouched for a month or two — for no important reason, there are just other things that I worked on. But the exploration gave us enough information to start tracking the work with some level of confidence.&lt;/p&gt;

&lt;p&gt;There are a handful of different tools and artifacts we use to track projects at Camunda — Trello, Jira, GitHub, Google Docs... For the Developer Experience team in particular, we've moved to using GitHub to track almost everything. So for this project, I created &lt;a href="https://github.com/camunda/camunda-platform-docs/issues/1116" rel="noopener noreferrer"&gt;a single issue to list all the things we'd have to do to complete this project&lt;/a&gt;. I initially filled it out from a high level, not too much detail, figuring I'd fill in more details as I learned them.&lt;/p&gt;

&lt;p&gt;I have mixed feelings about this approach. I like that there is one place that tracks all of the work. I believe strongly in the importance of tracking the work publicly (or at least, visible to my team). This accomplishes that.&lt;/p&gt;

&lt;p&gt;But by the end of the project the issue became pretty massive. If you're looking for something specific in that list of completed work, it's hard to find. Part of me thinks this might have been better served as a GitHub project, instead of an issue. I chose the artifact based on what I knew at the time, so I'm not holding it against myself for tracking it this way. I do think I'll be more conscious next time of how big an epic &lt;em&gt;might grow&lt;/em&gt; when I decide how to track it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explaining the work
&lt;/h2&gt;

&lt;p&gt;Knowing that I was working on this project mostly in isolation, it was critical to explain my work to reviewers who had less context. I learned some good habits about &lt;a href="https://artsy.github.io/blog/2020/08/11/improve-pull-requests-by-including-valuable-context/" rel="noopener noreferrer"&gt;adding context to PRs&lt;/a&gt; while at Artsy. On this project I got to put them to good use — especially &lt;a href="https://github.com/camunda/camunda-platform-docs/pull/1170" rel="noopener noreferrer"&gt;adding videos to demonstrate changes&lt;/a&gt; (&lt;a href="https://www.loom.com/share/511730f7dbec41e9ad6fb1d748da0041" rel="noopener noreferrer"&gt;direct link to video&lt;/a&gt;), and &lt;a href="https://github.com/camunda/camunda-platform-docs/pull/1328" rel="noopener noreferrer"&gt;inline comments identifying the interesting changes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A couple other good reasons to explain work at this level:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When anyone comes back to this in the future, there will be plenty of context. They shouldn't have to spend much time spelunking Slack or asking "hey I know this is a long shot, but do you remember why we wrote this line of code 6 months ago?"&lt;/li&gt;
&lt;li&gt;It makes for a nice reference point whenever we want to do something similar in the future, or when someone on the internet asks a question about how to do this thing.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Separating infrastructure from routine work
&lt;/h2&gt;

&lt;p&gt;Pull requests that combine significant infrastructural changes with routine changes &lt;a href="https://artsy.github.io/blog/2021/03/09/strategies-for-small-focused-pull-requests/#separate-riskycontroversial-work-from-routine-work" rel="noopener noreferrer"&gt;are a recipe for losing the signal amidst the noise&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One example of this was mentioned above — &lt;a href="https://github.com/camunda/camunda-platform-docs/pull/1118" rel="noopener noreferrer"&gt;introducing a "next" version of the docs&lt;/a&gt;, which was shipped separately from any content changes. After it was merged, I was free to twiddle with content all I wanted, but I wouldn't have wanted someone to have to review both types of changes in one place.&lt;/p&gt;

&lt;p&gt;Another example — &lt;a href="https://github.com/camunda/camunda-platform-docs/pull/1166" rel="noopener noreferrer"&gt;while I was moving Optimize documentation into its own section for the first time&lt;/a&gt;, I noticed that the multiple sets of versions were going to create a cross-linking mess. We'd end up with hard-coded versions in URLs when linking across the documentation, and have to update them whenever new versions were released. Before completing &lt;a href="https://github.com/camunda/camunda-platform-docs/pull/1166" rel="noopener noreferrer"&gt;the first Optimize docs PR&lt;/a&gt;, I introduced &lt;a href="https://github.com/camunda/camunda-platform-docs/pull/1170" rel="noopener noreferrer"&gt;an infrastructural enhancement for cross-linking&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Sequence in this case probably didn't matter too much; the importance to me was that I had two related but distinct changes, and I wanted to keep the history of them separate. Aside from making it easier to review, this approach prevents &lt;a href="https://artsy.github.io/blog/2021/03/09/strategies-for-small-focused-pull-requests/#what-is-small-and-focused" rel="noopener noreferrer"&gt;one ready feature from being held up by one disputed feature&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Splitting stories
&lt;/h2&gt;

&lt;p&gt;Even though there was a ton of content to move in this project, there presented two natural ways to split the content into smaller pieces: by version, and by content section.&lt;/p&gt;

&lt;p&gt;Splitting by version was something we noticed up front. We could iterate through the different versions, starting with the most recent version and ending with the oldest, and migrate content one version at a time. This also presented itself midway through the project as an opportunity to defer work. As we worked through newer versions, we realized that the oldest versions were less important to us, and we de-prioritized them.&lt;/p&gt;

&lt;p&gt;Splitting by content section was not noticed up front. In fact I only discovered this natural seam on accident — by forgetting to do two of three content sections! 😅😬 When a stakeholder pointed out that I'd only shipped one section of the first version, I decided this was fine, and actually a good way to break things down for the other versions.&lt;/p&gt;

&lt;h2&gt;
  
  
  De-scoping mercilessly
&lt;/h2&gt;

&lt;p&gt;One of my constant struggles in software development is deciding when a bug or edge case should be resolved with the work I'm doing, or if it can be done later. I have a high standard for "done" — sometimes too high. Perfect is the enemy of good, as....someone says. And I fall for perfection just about every time.&lt;/p&gt;

&lt;p&gt;I've gotten better at de-scoping and deferring these kinds of things, and this project was an opportunity for me to demonstrate my progress. Many issues came up as I was working, and you can see &lt;a href="https://github.com/camunda/camunda-platform-docs/issues/1116" rel="noopener noreferrer"&gt;in the epic for this project&lt;/a&gt; that we treated many of them as follow-up work instead of blocking issues.&lt;/p&gt;




&lt;p&gt;Do you have any real life examples of breaking problems down into smaller pieces? &lt;a href="https://dev.to/where"&gt;Let me know!&lt;/a&gt;&lt;/p&gt;

</description>
      <category>problemsolving</category>
      <category>project</category>
      <category>documentation</category>
      <category>pullrequest</category>
    </item>
    <item>
      <title>Automating Pull Requests With Sed And The GitHub CLI</title>
      <dc:creator>Steven Hicks</dc:creator>
      <pubDate>Mon, 21 Nov 2022 18:41:46 +0000</pubDate>
      <link>https://dev.to/pepopowitz/automating-pull-requests-with-sed-and-the-github-cli-27nf</link>
      <guid>https://dev.to/pepopowitz/automating-pull-requests-with-sed-and-the-github-cli-27nf</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4uFpNKSD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgs.xkcd.com/comics/automation.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4uFpNKSD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgs.xkcd.com/comics/automation.png" alt="xkcd comic on automation" width="404" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://xkcd.com/1319/"&gt;Obligatory xkcd comic on automation&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/pepopowitz/125e1102d455d5cdb20c345930c4c9de"&gt;Here's a Bash script to&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Take a branch name&lt;/li&gt;
&lt;li&gt;Create a new branch based on the original&lt;/li&gt;
&lt;li&gt;Call some scripts to update files&lt;/li&gt;
&lt;li&gt;Use sed to update a config setting in another file&lt;/li&gt;
&lt;li&gt;Commit the changes&lt;/li&gt;
&lt;li&gt;Push up a GitHub PR with a lot of information filled in&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On the other hand, if you've got some time...&lt;/p&gt;

&lt;h2&gt;
  
  
  Pull up a chair and listen to me talk about my personality again 🙄
&lt;/h2&gt;

&lt;p&gt;My learning style looks something like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run into a problem&lt;/li&gt;
&lt;li&gt;Discover or remember a tool that could solve that problem&lt;/li&gt;
&lt;li&gt;Learn that tool just enough to solve the problem&lt;/li&gt;
&lt;li&gt;Never go back to that tool until the next problem wants it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's kind of frustrating, actually — I would love to be the kind of person who dives &lt;strong&gt;deep&lt;/strong&gt; into something new. I'd love to stay interested in a topic for more than one problem. Instead, I learn a little about a tool, and lose interest in it until I need it again. This is why this blog seems so scatterbrained, I think — no content strategy here, friends! Just a bunch of mismatched tangentially related solutions to very specific problems.&lt;/p&gt;

&lt;p&gt;Nowhere is this more obvious than in my use of shell scripting. I know enough Bash to be ...very very casually dangerous. Like tortoise-shell-sunglasses-and-fake-leather-bomber-jacket dangerous.&lt;/p&gt;

&lt;p&gt;Recently I got a chance to grow my shell scripting skills! When I recognized the need for &lt;a href="https://www.gnu.org/software/sed/manual/sed.html"&gt;sed&lt;/a&gt;, I was excited that I finally knew enough about it to recognize a case for it.&lt;/p&gt;

&lt;p&gt;And as soon as I'd automated that, I recognized another opportunity — to get a little more knowledge of &lt;a href="https://cli.github.com/"&gt;the GitHub CLI&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The use case, in detail
&lt;/h2&gt;

&lt;p&gt;We have &lt;a href="https://docs.camunda.org/"&gt;some older docs at Camunda&lt;/a&gt; that are built with Hugo. There are about 20 versions that we maintain, and &lt;a href="https://github.com/camunda/camunda-docs-manual/branches/all?query=7."&gt;each version is built from its own branch&lt;/a&gt;. This is a pain when you have to update more than one version. Thankfully, that doesn't happen very often.&lt;/p&gt;

&lt;p&gt;But ooohhhhh, when it does! When it does!&lt;/p&gt;

&lt;p&gt;We recently learned that we had sabotaged our docs by de-indexing most of them from Google search results. Yikes! Very long story short — when providing multiple versions of documentation, make sure you explicitly declare one version as the canonical. If you don't, Google &lt;em&gt;will pick one for you&lt;/em&gt;! And &lt;em&gt;it might pick one that you told it not to index&lt;/em&gt;!!&lt;/p&gt;

&lt;p&gt;To resolve this, I wanted to apply canonical URLs to all versions of our docs. Which meant I had to make nearly identical changes to all ~20 versions/branches. The changes themselves were relatively simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Update the theme to include canonical URLs&lt;/li&gt;
&lt;li&gt;Configure a site-level setting to use the correct base URL&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Feel the pain before addressing it
&lt;/h2&gt;

&lt;p&gt;More personality stuff, sorry. Here's one rule of developer experience that I feel pretty strongly about:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Before you address a problem, you should feel the pain of it as directly as possible.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Otherwise you're just guessing at the solution.&lt;/p&gt;

&lt;p&gt;As a bonus, having a way to experience the problem gives you a nice feedback loop. You'll know you've fixed the problem when you no longer experience it.&lt;/p&gt;

&lt;p&gt;All this to say, I had a hunch I might want to automate these PRs, but I didn't feel comfortable automating them until I knew what the repetition looked like. So I worked through the first few manually.&lt;/p&gt;

&lt;p&gt;Once I'd been through the process a few times, I recognized the repetition. For each version of the docs, I needed to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Manage my branch&lt;/li&gt;
&lt;li&gt;Run a command to update a few files&lt;/li&gt;
&lt;li&gt;Update a configuration file by changing one setting&lt;/li&gt;
&lt;li&gt;Commit it all&lt;/li&gt;
&lt;li&gt;Create a PR based on a template&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;1, 2, and 4 were already in my toolbox. 3 and 5 were not.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Updating a configuration file with sed
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.gnu.org/software/sed/manual/sed.html"&gt;sed is a "stream editor"&lt;/a&gt;; for my needs, that effectively means it's a command line tool for editing the contents of a file. It's not just the editing that makes it the right tool to change a config setting — it also makes it easy to target text, by filtering the stream/file.&lt;/p&gt;

&lt;p&gt;My specific use case was to modify the value of the &lt;code&gt;baseURL&lt;/code&gt; configuration setting, to include a domain. So where the config file previously read:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;baseURL: "/some/value"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I needed it to read:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;baseURL: "https://docs.camunda.org/some/value"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I'd previously heard of sed, and previously used sed, but mostly in the context of copy-pasting things from the internet. This was the first time I'd looked at a problem and thought "oh, I could use sed to do that!"&lt;/p&gt;

&lt;p&gt;And I'll be honest -- my understanding is still not far beyond &lt;a href="https://en.wikipedia.org/wiki/Cargo_cult_programming"&gt;cargo-culting&lt;/a&gt;. What I &lt;em&gt;can&lt;/em&gt; say is that these are the arguments I needed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-i ''&lt;/code&gt; to edit the file inline, rather than create a copy&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-E&lt;/code&gt; to use "extended" regular expressions, because my regular expression used &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Groups_and_Backreferences"&gt;a capture group&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;'s/...matching pattern.../...replacement pattern...'/g&lt;/code&gt; to substitute text for a regular expression match&lt;/li&gt;
&lt;li&gt;the file name to edit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the end, this is what my command looked like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'s/baseURL: \"(.*)\"/baseURL: \"https:\/\/docs.camunda.org\1\"/g'&lt;/span&gt; config.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This searches the &lt;code&gt;config.yaml&lt;/code&gt; file for the &lt;code&gt;baseURL&lt;/code&gt; setting, and injects the domain before its current value.&lt;/p&gt;

&lt;p&gt;The most frustrating part about writing this command was figuring out that I needed the &lt;code&gt;-E&lt;/code&gt; flag in order to use a capture group in my regular expression.&lt;/p&gt;

&lt;p&gt;Yay, computers!&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Creating a PR with the GitHub CLI
&lt;/h2&gt;

&lt;p&gt;I'd previously opened many GitHub PRs, and I even use the GitHub CLI to do it every single time (although I abstract it behind &lt;a href="https://github.com/pepopowitz/dotfiles/blob/main/git.zsh#L8"&gt;an alias I call &lt;code&gt;open-pr&lt;/code&gt;&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;And initially, that's what I scripted. But I found myself going into the GitHub UI to update things repetitively. I figured there had to be some arguments I could use to change those values for me.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cli.github.com/manual/gh_pr_create"&gt;There are&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;In particular, I was interested in these arguments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--web&lt;/code&gt;: to open the web interface, so that I could make some final changes before creating the PR&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-t&lt;/code&gt;: the title of the PR&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-a&lt;/code&gt;: PR assignee, which I could set to myself with the identifier &lt;code&gt;@me&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-B&lt;/code&gt;: the base of the PR, meaning which branch it should be merged into&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-b&lt;/code&gt;: the body of the PR.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also assigned a bunch of variables ahead of time, to make my command easier to read:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gh &lt;span class="nb"&gt;pr &lt;/span&gt;create &lt;span class="nt"&gt;--web&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TITLE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; @me &lt;span class="nt"&gt;-B&lt;/span&gt; &lt;span class="nv"&gt;$VERSION&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BODY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Stitching it all together
&lt;/h2&gt;

&lt;p&gt;You can see my final script &lt;a href="https://gist.github.com/pepopowitz/125e1102d455d5cdb20c345930c4c9de"&gt;in this gist&lt;/a&gt;. I've done my best to comment it well, but feel free to leave comments in there if anything doesn't make sense or could be improved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extra credit: automating the screenshots
&lt;/h2&gt;

&lt;p&gt;When using the GitHub CLI to open a PR, I chose to use the web interface because there was still one repetitive task I didn't want to automate: adding screenshots to the body of the PR.&lt;/p&gt;

&lt;p&gt;I'm certain there are tools to do this, but it seemed like a can of worms I didn't want to open. Getting everything but the screenshots was 95% of the win. I didn't mind having to take two screenshots and then paste them into the GitHub UI.&lt;/p&gt;

&lt;p&gt;If you've automated this step as part of opening a PR, I'd love to hear about it!&lt;/p&gt;

</description>
      <category>bash</category>
      <category>sed</category>
      <category>github</category>
      <category>cli</category>
    </item>
    <item>
      <title>Rule 4: Warm Up To Get Into Flow State Sooner</title>
      <dc:creator>Steven Hicks</dc:creator>
      <pubDate>Thu, 25 Feb 2021 17:21:09 +0000</pubDate>
      <link>https://dev.to/pepopowitz/rule-4-warm-up-to-get-into-flow-state-sooner-5fah</link>
      <guid>https://dev.to/pepopowitz/rule-4-warm-up-to-get-into-flow-state-sooner-5fah</guid>
      <description>&lt;p&gt;Deep coding work is like building a LEGO spaceship in my head. It takes a while to construct the model, and it takes focus to keep the model from collapsing. Once it collapses I have to rebuild the model from scratch. I've explained to my lovely interrupting children that this is why I get mad when I'm interrupted — what seems like a 30 second request to them is a much longer distraction for me. &lt;/p&gt;

&lt;p&gt;I've estimated in my head that I need about 20-30 minutes to get into a state where I'm in the flow, the spaceship is fully constructed, and I can manipulate it like a master builder. I call this my "ease-in time." I think of it like &lt;a href="https://en.wikipedia.org/wiki/Critical_mass_(sociodynamics)"&gt;critical mass&lt;/a&gt;, or a moat that I have to cross to get to an adventure.&lt;/p&gt;

&lt;p&gt;I find writing to be similar — I need that ease-in time to get into a writing mindset before I feel effective. When I've got 45 minutes until bedtime, which is frequently when I write, it's easy to convince myself that it's not even worth starting. I'm only going to get 15 minutes of quality writing time after I ease in. &lt;strong&gt;Why bother?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;During my ease-in time, I have very little self-control or direction. I'm mostly doing my best to keep myself from all the distractions I want to chase. It's sloppy. Sometimes I end up on Twitter. Sometimes I don't. The sessions when I end up on Twitter take longer to feel productive than the sessions when I don't. &lt;/p&gt;

&lt;p&gt;Until recently I treated this as something I couldn't influence. I thought I just had to ride out my ease-in time, it takes what it takes, and I'd get to deep flow eventually. &lt;/p&gt;

&lt;p&gt;But then I thought about athletes. &lt;/p&gt;

&lt;h2&gt;
  
  
  Athletes don't warm up on Twitter
&lt;/h2&gt;

&lt;p&gt;Athletes don't just do whatever they want before a game — they &lt;em&gt;warm up&lt;/em&gt;. Giannis Antetokounmpo doesn't go into a game cold — he warms up by dribbling and shooting jump shots. When I get on a bike, I'm not ready to crush my ride right away — it takes ten minutes of gradually increasing my cadence and power before my body feels ready. I don't just put on my shoes and run five miles — I do stretches and strengthening exercises. (Maybe &lt;em&gt;you&lt;/em&gt; just put your shoes on and run but I am old and I want to avoid injury.)&lt;/p&gt;

&lt;p&gt;Athletes don't warm up by scrolling through Twitter or watching &lt;a href="https://www.youtube.com/watch?v=e1BYAfrUwLk"&gt;incredible marble machine videos on Youtube&lt;/a&gt;. They do drills that warm their body up for the specific movements they'll be making. &lt;/p&gt;

&lt;p&gt;With easing-in I've been allowing myself the &lt;em&gt;time&lt;/em&gt; to warm up, but I wasn't guiding my time properly. I was taking jump shots to warm up for a bike ride. My warm-up time wasn't effective. It didn't actually warm me up for deep coding or writing. &lt;/p&gt;

&lt;h2&gt;
  
  
  What makes a good warm-up activity?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;It's easy to start the activity. You're using it to warm up for the deep work — it shouldn't itself require a warm-up.&lt;/li&gt;
&lt;li&gt;It's easy to stop the activity. It's important to set it aside when it's time to focus on your intention. You don't want to get sucked into your warm-up.&lt;/li&gt;
&lt;li&gt;Specificity. This is the piece I was missing. Warm-ups should exercise the muscles you're going to use during your time block. &lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Writing warm-ups
&lt;/h2&gt;

&lt;p&gt;Intentional writing warm-ups have reframed the amount of time I need to work on my writing practice. Whereas I used to see that 45 minutes before bed as not worth starting, I now see it as an opportunity to make at least a &lt;em&gt;little&lt;/em&gt; bit of meaningful progress. I'm more likely to start a short writing session instead of conceding to Twitter or &lt;a href="https://www.youtube.com/user/globalcyclingnetwork"&gt;GCN&lt;/a&gt; for the night, because I can usually get myself into the writing mindset in ten minutes or less. &lt;/p&gt;

&lt;p&gt;Some things I've done for writing warm-ups: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Time-boxed free-writing&lt;/li&gt;
&lt;li&gt;Re-reading/editing an in-progress article&lt;/li&gt;
&lt;li&gt;Picked a page out of the book &lt;a href="https://www.amazon.com/642-Things-Write-About-Writers/dp/1452127840/"&gt;"642 Things To Write About: Young Writers Edition"&lt;/a&gt;. ("Young Writers Edition" because my partner teaches middle-school English online, and that's the book that's on our shelf 😅)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm sure this is probably a technique that real writers talk about, and there are probably lots of other good resources for this. &lt;/p&gt;

&lt;h2&gt;
  
  
  Coding warm-ups
&lt;/h2&gt;

&lt;p&gt;Warming up to write code feels more natural to me because I've done this unintentionally for years. It feels different now that I'm applying intention, though. It feels like I have self-awareness and I've got agency in how long my warm-up will take.&lt;/p&gt;

&lt;p&gt;Things I've tried for coding warm-ups: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Time-boxed work on a personal project. Emphasis on time-boxed, because I &lt;em&gt;don't&lt;/em&gt; want to get sucked in. If you try this option, make sure this work doesn't require its own warm-up: right now I have a personal project that's always kind of floating around in my brain when I'm not working, so I can usually jump right into it.&lt;/li&gt;
&lt;li&gt;Time-boxed PR Review. Yeah, I know I told you in &lt;a href="//../categorize-by-depth"&gt;the previous article&lt;/a&gt; that you should defer that to your smallest time slots, but I have found it a good way to ease into deep coding too. &lt;/li&gt;
&lt;li&gt;Small learning challenges. My friend &lt;a href="https://twitter.com/bhoggard"&gt;Barry&lt;/a&gt; recently shared &lt;a href="https://www.executeprogram.com/courses/typescript"&gt;an interactive TypeScript course with very small exercises&lt;/a&gt;. The exercises are &lt;em&gt;tiny&lt;/em&gt;, so I can get a taste of coding without getting sucked in. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Things I want to try:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing a couple unit tests. They wouldn't even have to be tests for my focus project — the important thing is that I'd be warming up the "muscles" I'll be using. I haven't done this yet only because I keep forgetting to try it.&lt;/li&gt;
&lt;li&gt;A code kata. The challenge here is to find a kata that's short enough to qualify as a warm-up instead of a full-fledged problem. It'd be easy to get sucked into this. I haven't put the effort into finding a short enough kata yet. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  I'm not a robot
&lt;/h2&gt;

&lt;p&gt;I know it seems like it but I'm not always on. Sometimes I self-indulge. Sometimes I still let my hair down and "warm up" by scrolling through Twitter. &lt;/p&gt;

&lt;p&gt;I have more awareness of when I'm doing it though. And intention — I'm &lt;em&gt;allowing&lt;/em&gt; myself to warm up in a way that I know is less effective as a treat. Like allowing myself to drink a Coke twice a week even though it's bad for me. (This is what I tell myself...and you...to convince us that I have agency and self-control. Is it working? 😬)&lt;/p&gt;

&lt;p&gt;I also don't always &lt;em&gt;need&lt;/em&gt; a warm-up. Sometimes my mind is in the right space from the start, and I'm able to jump right into my deep work. I try to do a check within the first few minutes of my time-block to see if I need an intentional warm-up. I think I need it about 50% of the time for writing, and maybe a little less for coding. &lt;/p&gt;




&lt;p&gt;Warming up for deep work in small blocks of time is pretty new to me but so far it's been really effective. I spend a lot less time getting into the mindset I need to be effective. If you've got suggestions on how to warm up for deep work, I'd love to hear about them! &lt;/p&gt;

</description>
      <category>productivity</category>
      <category>career</category>
    </item>
    <item>
      <title>Rule 3: Categorize Tasks By Depth, And Work Them At The Right Time</title>
      <dc:creator>Steven Hicks</dc:creator>
      <pubDate>Tue, 23 Feb 2021 19:18:57 +0000</pubDate>
      <link>https://dev.to/pepopowitz/rule-3-categorize-tasks-by-depth-and-work-them-at-the-right-time-589n</link>
      <guid>https://dev.to/pepopowitz/rule-3-categorize-tasks-by-depth-and-work-them-at-the-right-time-589n</guid>
      <description>&lt;p&gt;I find an hour to be enough time to get into deep work. I haven't always felt this way, but I do now. &lt;/p&gt;

&lt;p&gt;But what about even shorter blocks of time? What about those half-hour gaps between meetings? Is it worth starting anything meaningful during those, or should I just burn that time on Twitter?&lt;/p&gt;

&lt;p&gt;As with everything, the answer lies somewhere in between. I don't think the ROI is worth getting into deep thought for a half-hour, but I &lt;em&gt;do&lt;/em&gt; find those pesky half-hour blocks to be extremely useful for &lt;em&gt;shallow&lt;/em&gt; work. &lt;/p&gt;

&lt;p&gt;What qualifies as shallow work? &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Making a personal phone call&lt;/li&gt;
&lt;li&gt;Raising a question in Slack&lt;/li&gt;
&lt;li&gt;Scheduling meetings&lt;/li&gt;
&lt;li&gt;Creating tickets for new issues&lt;/li&gt;
&lt;li&gt;PR review (if they're on the smaller side)&lt;/li&gt;
&lt;li&gt;Small updates to docs&lt;/li&gt;
&lt;li&gt;Investigating a tool I learned about recently&lt;/li&gt;
&lt;li&gt;Answering a question I'm curious about&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Many of these things require very little time, but "shallow work" isn't really about the amount of time involved for the task itself. It's about how much time it would take me to get into the level of focus required for the task. Everything in that list requires very little priming. I could drop any one of them immediately and spend very little time getting back into it an hour later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoid shallow work during deep focus blocks
&lt;/h2&gt;

&lt;p&gt;This is probably the most controversial thing I'll say in this series: &lt;strong&gt;procrastinate shallow work when you come across it during times of deep focus&lt;/strong&gt;. Instead, track the task however you track your work, tag it as "shallow," and save it for a later tiny block of time. &lt;/p&gt;

&lt;p&gt;When that next tiny block of time comes up, knock out as many shallow tasks as you can. &lt;/p&gt;

&lt;h2&gt;
  
  
  Ten seconds of distraction is better than five minutes of distraction
&lt;/h2&gt;

&lt;p&gt;I've heard people say something to the effect of "if it takes less than five minutes, do it now." But I don't need &lt;em&gt;any&lt;/em&gt; help getting distracted, especially when I'm trying to do deep work. Every unrelated task that you do during deep work is an opportunity to pull yourself out of your flow.&lt;/p&gt;

&lt;p&gt;Stomp these distractions out. Recognize them as quickly as you can. Say "Not today, Satan," and save them for a time when you don't need your attention, focus, or deep thought. &lt;/p&gt;

&lt;h2&gt;
  
  
  My system for categorizing tasks by depth
&lt;/h2&gt;

&lt;p&gt;What works for you will probably look different than what works for me, but I use &lt;a href="https://todoist.com"&gt;Todoist&lt;/a&gt; to manage my day. I've shared &lt;a href="https://twitter.com/pepopowitz/status/1261323105479331841"&gt;more detail about how I use it&lt;/a&gt;, but I categorize my tasks using &lt;a href="https://todoist.com/help/articles/how-to-best-use-labels"&gt;the labels feature&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;When I'm in the middle of deep work and I come across a distracting task, I'll use Todoist's &lt;a href="https://todoist.com/help/articles/keyboard-shortcuts"&gt;global Quick-Add shortcut&lt;/a&gt; (ctrl-cmd-A on a Mac) and add a task with the appropriate label: &lt;code&gt;@shallow&lt;/code&gt;, &lt;code&gt;@medium&lt;/code&gt;, or &lt;code&gt;@deep&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;And that's it! I'm distracted for less than a minute and I stay focused. &lt;/p&gt;

&lt;p&gt;The rest of the day I'll handle my &lt;code&gt;@shallow&lt;/code&gt; tasks during my extra-short time blocks, my &lt;code&gt;@deep&lt;/code&gt; tasks during my longer time blocks, and...I don't really have a good description of when I handle the &lt;code&gt;@medium&lt;/code&gt; tasks. I use it for tasks between &lt;code&gt;@shallow&lt;/code&gt; and &lt;code&gt;@deep&lt;/code&gt; but I don't have a simple heuristic to describe when I use it. &lt;/p&gt;




&lt;p&gt;Next time we'll answer the question that actually prompted me writing this series: "How can I get into flow state more quickly so that I can accomplish something meaningful in smaller blocks of time?"&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>career</category>
    </item>
    <item>
      <title>Rule 2: Slice Work Smaller</title>
      <dc:creator>Steven Hicks</dc:creator>
      <pubDate>Sat, 20 Feb 2021 04:12:31 +0000</pubDate>
      <link>https://dev.to/pepopowitz/rule-2-slice-work-smaller-lhe</link>
      <guid>https://dev.to/pepopowitz/rule-2-slice-work-smaller-lhe</guid>
      <description>&lt;p&gt;I'm a +7 perfectionist. I don't like to walk away from work until t's are crossed, i's are dotted, tests are written, and the work is &lt;em&gt;done&lt;/em&gt;-done. &lt;/p&gt;

&lt;p&gt;Perfectionism is a double-edged sword. While it produces beautiful work, it also prevents me from shipping things when they're "good enough," or even from collecting helpful feedback before I consider the work "complete." Sometimes it takes a non-trivial effort to convince myself to open a PR when the edges aren't yet polished.&lt;/p&gt;

&lt;p&gt;My perfectionism has another negative effect — it prevents me from feeling accomplished when I have only a short amount of time to put toward the work. "One hour to put toward this feature? Pfffttt that'll only get me 10% done," my brain says. I dread even starting the work because I know I'm not going to "finish" within an hour. &lt;/p&gt;

&lt;p&gt;The trick for me is to break work into smaller slices. &lt;/p&gt;

&lt;h3&gt;
  
  
  Smaller slices shift the meaning of "meaningful progress"
&lt;/h3&gt;

&lt;p&gt;Instead of setting a goal of finishing an entire feature, I treat it like a good agile team treats their work — I break the feature into sub-features, or steps, or layers, or milestones. Anything that breaks the problem down into smaller problems. &lt;/p&gt;

&lt;p&gt;These smaller tasks shift my perspective — a small block of time was not enough to finish an entire feature, but it's usually enough to make progress on at least one or two of the smaller tasks. The reframing lowers the bar for what "meaningful progress" means. An hour of work suddenly feels much more impactful. I don't have to fight the voice in my head that tells me it's not worth starting. My milestones for progress seem more attainable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Smaller slices make me feel good
&lt;/h3&gt;

&lt;p&gt;I've also now got dopamine on my side. &lt;/p&gt;

&lt;p&gt;Large chunks of work feel nebulous. They make me feel bad. Like I'm never going to finish. Like I'm slow. Leaving a non-tasked story in "In Progress" for a week is frustrating. It's embarrassing to give the same update in standup several days in a row — "ummm I'm still working on the edit screen." &lt;/p&gt;

&lt;p&gt;Smaller slices allow me to check things off a list more frequently, or to drag a sub-task from "In Progress" to "Done." This simple act feels like an accomplishment, &lt;a href="https://www.psychologytoday.com/us/blog/the-truisms-wellness/201610/the-science-accomplishing-your-goals"&gt;complete with dopamine rush&lt;/a&gt;. No matter how many tasks remain or how small the task I checked off, it &lt;strong&gt;literally feels good&lt;/strong&gt; when I complete one.&lt;/p&gt;

&lt;p&gt;I actually left you a good heuristic above for knowing when you need to break your work down. If you're repeating your daily update...it might be time to break that work smaller. It will help you feel like you're making progress, and it'll give better visibility to the rest of your team.&lt;/p&gt;

&lt;h3&gt;
  
  
  A couple other good reasons to slice work smaller
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;It's easier for people to review a smaller scope of work. Large PR reviews often end in "LGTM" (looks good to me) because they're just too difficult to digest. &lt;/li&gt;
&lt;li&gt;It tightens the feedback loop of delivery &amp;amp; iteration. Smaller chunks can be shipped faster. We can learn from them more quickly and iterate. &lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;Slicing my work smaller helped me answer a very specific question I was trying to answer: "How can I accomplish something meaningful in a shorter amount of time?"&lt;/p&gt;

&lt;p&gt;In the next article I'll answer another question: "How can I take advantage of those &lt;em&gt;very&lt;/em&gt; short blocks of time?" &lt;/p&gt;

</description>
      <category>productivity</category>
      <category>career</category>
    </item>
    <item>
      <title>Rule 1: Dedicate Time. And Schedule It.</title>
      <dc:creator>Steven Hicks</dc:creator>
      <pubDate>Mon, 15 Feb 2021 17:52:18 +0000</pubDate>
      <link>https://dev.to/pepopowitz/rule-1-dedicate-time-and-schedule-it-5gda</link>
      <guid>https://dev.to/pepopowitz/rule-1-dedicate-time-and-schedule-it-5gda</guid>
      <description>&lt;p&gt;My first rule for maximizing productivity in small blocks of time is to dedicate time to a specific goal, and schedule it on my calendar. &lt;/p&gt;

&lt;p&gt;An hour of time that has no declared focus often turns out to be exactly that — unfocused, scatterbrained, not dedicated to any one thing. In an unfocused hour I might feel like I make a bit of progress on several things, but rarely will I feel like I made significant progress on any &lt;em&gt;one&lt;/em&gt; thing. Spending 10 minutes each on five things feels much less productive than spending 50 minutes on one thing. If I dedicate an hour to a specific task or project, I feel more focused from the start. There's nothing &lt;em&gt;forcing&lt;/em&gt; me to stay dedicated...but having an intention gives me something to return to when distractions beckon. &lt;/p&gt;

&lt;p&gt;The benefits compound when I &lt;strong&gt;schedule this dedicated time on my calendar&lt;/strong&gt;. I feel more in control of my day. Control is a big thing for me — my worst days are the ones where I feel like I have no agency in what I'm doing. Dedicating time on my calendar for work that &lt;strong&gt;I&lt;/strong&gt; want to accomplish satisfies my need for autonomy.&lt;/p&gt;

&lt;p&gt;Recurring dedicated time on your calendar is a great way to build a practice, too. I've been using this strategy to strengthen my writing practice. &lt;/p&gt;

&lt;p&gt;The hardest part about scheduling dedicated time is the discipline required to use it for its intended focus. For my first six months at Artsy, I had a weekly block of time on my calendar for "sharpening my tools" (h/t &lt;a href="https://twitter.com/jonallured"&gt;Jon Allured&lt;/a&gt;). I &lt;strong&gt;never once&lt;/strong&gt; used it for its intended purpose. &lt;/p&gt;

&lt;p&gt;What's the difference between the hour of sharpening I never did and the hour of writing I successfully dedicate every Monday? I think it's about specificity. I never really had an idea of what I'd do when it was time to sharpen. I wanted to keep my dev tools up to date, but that was such a nebulous goal that I never knew where to start.&lt;/p&gt;

&lt;p&gt;With my writing hour, my goal is smaller — work on a blog article. Even if I don't know what the article is about yet, I know where to start...&lt;/p&gt;

&lt;p&gt;And I'll tell you all about that later, in Rule 4. &lt;/p&gt;

</description>
      <category>productivity</category>
      <category>career</category>
    </item>
    <item>
      <title>Maximizing Productivity During Small Blocks Of Time</title>
      <dc:creator>Steven Hicks</dc:creator>
      <pubDate>Fri, 12 Feb 2021 17:44:42 +0000</pubDate>
      <link>https://dev.to/pepopowitz/maximizing-productivity-as-a-maker-with-a-manager-s-schedule-2mb2</link>
      <guid>https://dev.to/pepopowitz/maximizing-productivity-as-a-maker-with-a-manager-s-schedule-2mb2</guid>
      <description>&lt;p&gt;One of my primary self-improvement projects is to use my time more effectively, especially the small blocks in between meetings. I'm not a manager, but I have more of a &lt;a href="http://www.paulgraham.com/makersschedule.html"&gt;manager's schedule than a maker's schedule&lt;/a&gt;. I willingly accept more meetings than most developers. I like the interaction, I like to get others unstuck, and I like to participate in cross-team conversations. The result: most of the empty time on my calendar is in blocks of an hour or less.&lt;/p&gt;

&lt;p&gt;It's hard to do deep work in an hour or less. If I make full use of the hour, sure, I'll probably feel good about my progress...but that assumes I can get into the deep work promptly and that I don't get distracted. In reality, what looked on my calendar like a good hour often ends up feeling like 15 minutes of meaningful progress. If I'm lucky. &lt;/p&gt;

&lt;p&gt;Thankfully, I have a lot of good role models to emulate at &lt;a href="https://twitter.com/artsyopensource"&gt;Artsy&lt;/a&gt;. Some of my most admirable coworkers accomplish meaningful work in the same size blocks of time that I would have previously dismissed as too small for meaningful work. They might not know it, but I've been watching and taking notes.&lt;/p&gt;

&lt;p&gt;Over the next couple weeks, I'll share the rules I've developed for myself to work more effectively with my small blocks of time. I don't always follow them, but the days I do are the days I feel most successful, productive, and effective.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>career</category>
    </item>
    <item>
      <title>Tips For Working With Legacy Code, Courtesy Of My New Kitchen Faucet</title>
      <dc:creator>Steven Hicks</dc:creator>
      <pubDate>Fri, 08 Jan 2021 16:36:19 +0000</pubDate>
      <link>https://dev.to/pepopowitz/tips-for-working-with-legacy-code-courtesy-of-my-new-kitchen-faucet-3l3</link>
      <guid>https://dev.to/pepopowitz/tips-for-working-with-legacy-code-courtesy-of-my-new-kitchen-faucet-3l3</guid>
      <description>&lt;p&gt;This holiday break I replaced our kitchen faucet. The neck was loose and it wiggled, and there was a little bit of water starting to pool at the base. It wasn't leaking yet but it seemed like it might start leaking soon. There's probably a gasket or something I could have replaced but the faucet wasn't great and I had the time off of work. I figured now would be a good time to replace it.&lt;/p&gt;

&lt;p&gt;We own an old&lt;sup&gt;(1)&lt;/sup&gt; home. The main drawback of owning an old home is that every project takes five times longer than you expect it to. Anything that's hidden behind walls is made of old materials and it was introduced when building codes were far less safe. As you start opening things up during a project you find solutions from long ago, many of which are terrifying to work with. Like this electrical box containing 10 old wires with crumbling insulation that killed another of my holiday break projects:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1MiP2OJO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/896xa6vrkrxrav0nqxze.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1MiP2OJO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/896xa6vrkrxrav0nqxze.jpg" alt="An absolute mess of an electrical box containing 10 wires with crumbling insulation" width="880" height="954"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My kitchen faucet project had all the makings of a classic old-home project. The instructions for installing the new faucet were very well-written and easy to follow! But my kitchen is old. Who would win — the new faucet or the old home?&lt;/p&gt;

&lt;p&gt;The old home won. As things went wrong, I noticed parallels between my faucet installation project and working with legacy code. I've distilled the experience into a handful of tips.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 1: Reframe "legacy code"
&lt;/h2&gt;

&lt;p&gt;There doesn't seem to be a canonical definition of "legacy code", but it seems software engineers use the term to mean "code that I inherited that I must support." This definition isn't itself negative, but it has very negative connotations for most engineers. Since the code is inherited, it probably doesn't follow the patterns or style the new owner would like to see. It probably doesn't have enough test coverage to thoroughly describe its intentions or prevent regressions. There's likely technical debt that has been accrued over its lifetime. All of these factors make it difficult to change. But we &lt;em&gt;need&lt;/em&gt; to change it, because it's still in production, and we still need to support it.&lt;/p&gt;

&lt;p&gt;Much of what I've described above is purely factual, but our cognitive biases and past experiences with legacy code apply a layer of emotional distortion. &lt;a href="https://en.wikipedia.org/wiki/Fundamental_attribution_error"&gt;The fundamental attribution error&lt;/a&gt; convinces us that the existing code wasn't well-factored or well-conceived by the original author. The &lt;a href="https://en.wikipedia.org/wiki/Hindsight_bias"&gt;hindsight bias&lt;/a&gt; convinces us that it could have been written much more simply and clearly. We remember the last time we had to support legacy code and how we broke production while trying to make a single small change. We start thinking it would be much easier to rewrite this code from scratch than to support it.&lt;/p&gt;

&lt;p&gt;As I began my faucet project, I noticed similar feelings. The last time I'd tried to do a simple project in this house I found that mess of wiring, closed the box up, and called an electrician. How could the previous owners have been so careless with electricity? They must have had a death wish, I thought.&lt;/p&gt;

&lt;p&gt;I started thinking about how if this were a software project, I'd want to rewrite the entire kitchen. Correction — I'd rewrite the entire &lt;em&gt;house&lt;/em&gt;. The wiring is confusing and nasty all over, not just the kitchen. New construction would be &lt;strong&gt;so much easier&lt;/strong&gt; to work with.&lt;/p&gt;

&lt;p&gt;But I know "the big rewrite" is a trap in software. The hindsight bias and overuse of the phrase "technical debt" discredit the learning and the journey that went into creating legacy code. They overlook all of the features that are working well. If "the big software rewrite" were like building a new house from scratch, you'd move into a house with a beautiful kitchen and overall layout...but cardboard boxes for furniture, bathrooms with buckets for toilets, and nowhere to park your car.&lt;/p&gt;

&lt;p&gt;This thought gave me a lot of appreciation for my old home. I don't want to do a big rewrite on it. The neighborhood is amazing. The house is the perfect size for us. We just finished our basement. Sure, the electrical is terrifying in most of the house and the main bathroom is peach, but just like existing software, it's &lt;strong&gt;a legacy&lt;/strong&gt;. There's too much good stuff here to throw it all away and start over. Incremental improvement is the way to go — just like with a legacy software project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tip 1A: Dealing with integration problems &lt;strong&gt;is the job&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In &lt;a href="https://www.youtube.com/watch?v=3XscuivvUzI"&gt;a talk that I love to give about getting unstuck&lt;/a&gt;, I encourage software developers to reframe our understanding of getting stuck on difficult problems. Getting stuck isn't something that prevents you from doing your job as a software engineer; it &lt;em&gt;&lt;strong&gt;is&lt;/strong&gt;&lt;/em&gt; the job of a software engineer. We get stuck and have the persistence to get unstuck. That's problem solving. We're good at it, and that's why we get paid to do this job.&lt;/p&gt;

&lt;p&gt;This perspective ran through my mind as I thought about the things that typically go wrong with a home improvement project. I've always considered problems with old plumbing or electrical to be a nuisance. Steps that I shouldn't need to take. A distraction from the &lt;em&gt;actual&lt;/em&gt; home improvement project. But I was wrong — integration with the existing plumbing/electrical/construction &lt;em&gt;&lt;strong&gt;is&lt;/strong&gt;&lt;/em&gt; the home improvement project.&lt;/p&gt;

&lt;p&gt;It's similar to working with legacy code. When you introduce new code, you'll always face the challenge of integrating it with the existing system. Somehow you need to make a seam for your changes. Once your changes are introduced you need to verify they don't negatively affect the existing system. These aren't merely tasks that &lt;em&gt;impede you&lt;/em&gt; from supporting legacy code...these tasks &lt;em&gt;are&lt;/em&gt; the legacy code support.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 2: Identify incremental improvement opportunities by de-risking
&lt;/h2&gt;

&lt;p&gt;So now we've all gained appreciation for the existing system, and we're in agreement that the best way to make changes is through incremental improvement. Where do we start? How do we know &lt;em&gt;which parts&lt;/em&gt; of the system to improve first?&lt;/p&gt;

&lt;p&gt;Often the system will present the answer to you. Is there a subsystem that's flaky and needs to be reset often? A section of code that breaks every time you try to make a small change? Is there a service that was written by a departed teammate in a language that no current engineers feel confident supporting? Start there.&lt;/p&gt;

&lt;p&gt;This is de-risking. You can't predict exactly when a line of code will fail, but you &lt;em&gt;can&lt;/em&gt; predict which services or subsystems are likely to fail sooner rather than later. Identify them and improve them before they get a chance to fail.&lt;/p&gt;

&lt;p&gt;With my kitchen sink, I'd been noticing water pooling at the base of the neck for a couple weeks. It was nothing catastrophic, but it was an indication of a weakness. It may not be failing now, but it would likely fail soon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 3: &lt;em&gt;You&lt;/em&gt; own the code
&lt;/h2&gt;

&lt;p&gt;The first integration problem I ran into with my kitchen faucet was that the window sill behind the faucet extends as a shelf, and it was in the way:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LKbMwJQY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/gz8d4k66p7wdjrx614jk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LKbMwJQY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/gz8d4k66p7wdjrx614jk.jpg" alt="A kitchen faucet unable to be installed, due to a shelf obstructing it." width="880" height="957"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we picked out a new faucet, I never considered that the faucet currently installed was probably deliberately chosen because of its short height. This allowed them to install it below the shelf. We chose a taller replacement faucet though, and the shelf prevented us from installing it.&lt;/p&gt;

&lt;p&gt;My initial instinct was to return the faucet and find a shorter one. My partner suggested I cut a notch into the shelf. I resisted and thought "but this shelf is part of the house." She convinced me pretty quickly. It took us a while to pick out this faucet and we both really liked it, and this is &lt;em&gt;our&lt;/em&gt; house, so we can do whatever we want with the shelf. We're planning on renovating the kitchen in the next few years, and it's very unlikely the shelf will survive that remodel. We could survive a few years with a notch in a shelf, especially if it means we enjoy the feature we use the most in our house.&lt;/p&gt;

&lt;p&gt;So I cut a notch in the shelf with a reciprocating saw:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Vddt-o0B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/1iqk43dgrw868ico4juc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Vddt-o0B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/1iqk43dgrw868ico4juc.jpg" alt="The kitchen faucet in place, with a notch cut into the shelf to make space." width="880" height="684"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As I painted the new edges, I thought about how this was classic Steve — amplifying the importance of an &lt;em&gt;existing&lt;/em&gt; feature at the expense of a new feature. I do this when I'm working with code. If code is already there and it's working well, I have an intense aversion to modifying it. I avoid refactoring anything that I don't &lt;em&gt;need&lt;/em&gt; to change. This is extremely noticeable when I'm working with teammates who refactor freely. I get feelings of anxiety and discomfort as they make more changes than I would, and I get visions of a late night fixing an outage caused by a change that didn't need to be made.&lt;/p&gt;

&lt;p&gt;My aversion to refactoring sometimes leads to a feeling of &lt;em&gt;borrowing&lt;/em&gt; code — like the codebase isn't mine, it's the previous owner's, and I'm temporarily passing through. But like the flimsy shelf above my faucet, it's helpful to remind myself that &lt;strong&gt;I own this code&lt;/strong&gt;. If there is code that doesn't look right, &lt;strong&gt;I can change it&lt;/strong&gt;, because it's mine, and I'm the one supporting it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tip 4: Perfect is the enemy of done
&lt;/h2&gt;

&lt;p&gt;My aversion to unnecessary refactoring has one major benefit: it prevents me from blowing a project into something much larger. When modifying a legacy codebase you regularly face decisions about how far to pull a loose thread.&lt;/p&gt;

&lt;p&gt;There were several points in my faucet project where I caught myself in this kind of decision point. From the start, I wasn't sure if I'd be content with replacing only the faucet. It'd be nice to have a new sink too...but it wasn't as critical as the faucet. The entire kitchen needs a remodel...but that would be &lt;strong&gt;way&lt;/strong&gt; too much work for a holiday project. The connectors on the faucet supply lines didn't fit the valves coming out of the wall, and I could have replaced the valves with something more modern...but that would have meant doing real actual plumbing. Even the window sill/shelf could have become a bigger project if I'd decided to remove it entirely.&lt;/p&gt;

&lt;p&gt;Somewhere in between refactoring nothing and refactoring everything is a sweet spot for navigating these types of decision points. Practice helps you find that sweet spot. &lt;a href="https://en.wikipedia.org/wiki/Timeboxing"&gt;Time-boxing&lt;/a&gt; or &lt;a href="https://en.wikipedia.org/wiki/Pomodoro_Technique"&gt;the Pomodoro technique&lt;/a&gt; can be helpful for preventing yourself from pulling a refactoring thread too far. Being transparent and detailed in standup about the changes you're making makes it hard for you to go days with a vague update of "I'm still refactoring." Working with your team lead or product owner to prioritize the exact bits that &lt;em&gt;need&lt;/em&gt; to change will help you avoid nebulous tasks with no end in sight.&lt;/p&gt;

&lt;p&gt;And sometimes it comes down to showing self-restraint and acceptance that something is "good enough". It's okay to ship code that you wouldn't show in an interview. It's okay to not refactor the next code down the call stack. If you need it, I grant you permission to not solve &lt;em&gt;all&lt;/em&gt; the problems today.&lt;/p&gt;

&lt;p&gt;Oh, and here's my new kitchen faucet:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xu1pHL7O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/fut63qautto0n4mmb4h5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xu1pHL7O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/fut63qautto0n4mmb4h5.jpg" alt="A shiny new kitchen faucet installed in an old kitchen." width="880" height="906"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;br&gt;
&lt;sup&gt;1&lt;/sup&gt; as a resident of the Milwaukee area working for a company based in New York, I've discovered that "old home" is a very relative term, depending on where you live. My home was built in the 1930s — around here, that's pretty old. It's certainly not pre-revolutionary...but it's old enough to make home improvement difficult.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>development</category>
      <category>codequality</category>
      <category>code</category>
    </item>
    <item>
      <title>Generating Social Sharing Images In Eleventy</title>
      <dc:creator>Steven Hicks</dc:creator>
      <pubDate>Thu, 17 Dec 2020 04:30:31 +0000</pubDate>
      <link>https://dev.to/pepopowitz/generating-social-sharing-images-in-eleventy-1ikm</link>
      <guid>https://dev.to/pepopowitz/generating-social-sharing-images-in-eleventy-1ikm</guid>
      <description>&lt;p&gt;Inspired by &lt;a href="https://twitter.com/jlengstorf" rel="noopener noreferrer"&gt;Jason Lengstorf&lt;/a&gt;, I added social sharing images to all my blog posts on &lt;a href="https://stevenhicks.me" rel="noopener noreferrer"&gt;stevenhicks.me&lt;/a&gt;. This means that when you share an article from my site to a place like Twitter, you'll get a nice big card like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqbia48nx196dxtvj58qb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqbia48nx196dxtvj58qb.png" alt="Example of sharing an article from my website on Twitter"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sweet! I can't get enough of those 70s shag carpet vibes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prior art
&lt;/h2&gt;

&lt;p&gt;Before I show you how &lt;em&gt;I&lt;/em&gt; hooked this up to my eleventy site, consider &lt;a href="https://dev.to/5t3ph/automated-social-sharing-images-with-puppeteer-11ty-and-netlify-22ln"&gt;this article by Stephanie Eckles about using puppeteer to generate social share images&lt;/a&gt;. If you want to use HTML &amp;amp; CSS to create your social sharing images, that is probably what you're looking for!&lt;/p&gt;

&lt;p&gt;The method I chose&lt;sup&gt;(1)&lt;/sup&gt; uses &lt;a href="https://cloudinary.com/" rel="noopener noreferrer"&gt;Cloudinary&lt;/a&gt; to overlay the article title onto a common social sharing image.&lt;/p&gt;

&lt;p&gt;Most of what I needed was covered by Jason in his articles on &lt;a href="https://www.learnwithjason.dev/blog/add-text-overlay-cloudinary/" rel="noopener noreferrer"&gt;adding text overlays in Cloudinary&lt;/a&gt;, &lt;a href="https://www.learnwithjason.dev/blog/design-social-sharing-card/" rel="noopener noreferrer"&gt;designing a social sharing card&lt;/a&gt;, and &lt;a href="https://www.learnwithjason.dev/blog/auto-generate-social-image/" rel="noopener noreferrer"&gt;auto-generating social share images with &lt;code&gt;get-share-image&lt;/code&gt;&lt;/a&gt;. Thanks, Jason!&lt;/p&gt;

&lt;p&gt;Heads up that the most time-consuming part was tweaking the Cloudinary text overlays. Lots of fiddling with cryptic arguments. It's literally pushing pixels to get text in the right place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Emitting the image URLs in eleventy
&lt;/h2&gt;

&lt;p&gt;Here's my addition to this problem space: &lt;a href="https://github.com/pepopowitz/stevenhicks.me/pull/14" rel="noopener noreferrer"&gt;a PR that shows everything I needed to do to hook up the images in eleventy&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;There's not a lot there, but let's walk through it.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Add the &lt;code&gt;get-share-image&lt;/code&gt; dependency
&lt;/h3&gt;

&lt;p&gt;You'll do this with &lt;code&gt;npm install @jlengstorf/get-share-image&lt;/code&gt; or &lt;code&gt;yarn add @jlengstorf/get-share-image&lt;/code&gt;. &lt;a href="https://github.com/pepopowitz/stevenhicks.me/pull/14/files#diff-7ae45ad102eab3b6d7e7896acd08c427a9b25b346470d7bc6507b6481575d519R20" rel="noopener noreferrer"&gt;I added it to my &lt;code&gt;devDependencies&lt;/code&gt;&lt;/a&gt; because I care about separating dev dependencies from runtime dependencies. Maybe you don't care — I'm not going to arm-wrestle you over it.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Add an eleventy computed data file
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.11ty.dev/docs/data-computed/" rel="noopener noreferrer"&gt;Eleventy's computed data files&lt;/a&gt; inject computed properties into a page template for each page they apply to. Like maybe you want to compute a social sharing image URL that's based on the article title!&lt;/p&gt;

&lt;p&gt;I &lt;a href="https://github.com/pepopowitz/stevenhicks.me/pull/14/files#diff-e45e8998b62ae0dac0f40e46f3db483cf93f3b027a20b4649e8988a78785b371" rel="noopener noreferrer"&gt;added a file named &lt;code&gt;blog.11tydata.js&lt;/code&gt; to the &lt;code&gt;blog/&lt;/code&gt; folder&lt;/a&gt;. I chose to put it in the &lt;code&gt;blog/&lt;/code&gt; folder because I only wanted to generate social images for my blog articles; it seemed silly to me to generate a social image for my about page that said "About". I'm not sure if the file name needs to start with &lt;code&gt;blog&lt;/code&gt;, but that's what the docs did in their example (&lt;code&gt;posts/posts.11tydata.js&lt;/code&gt;), so I just went with it.&lt;/p&gt;

&lt;p&gt;The contents of &lt;code&gt;blog/blog.11tydata.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getShareImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@jlengstorf/get-share-image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;eleventyComputed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;shareImage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;getShareImage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="c1"&gt;// settings for cloudinary overlays&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;We pull in the &lt;code&gt;get-share-image&lt;/code&gt; dependency.&lt;/li&gt;
&lt;li&gt;We export an object with a property named &lt;code&gt;eleventyComputed&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Each property of &lt;code&gt;eleventyComputed&lt;/code&gt; is a computed property that becomes available in your page templates. In our case, we compute a property named &lt;code&gt;shareImage&lt;/code&gt;. The value of it is the result of a call to &lt;code&gt;getShareImage&lt;/code&gt; with a bunch of configuration for the Cloudinary overlay.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This &lt;code&gt;shareImage&lt;/code&gt; property gets computed for each page within &lt;code&gt;blog/&lt;/code&gt;, based on its metadata (that's what the &lt;code&gt;data&lt;/code&gt; argument passed into the function represents).&lt;/p&gt;

&lt;p&gt;The only dynamic data here for my site &lt;a href="https://github.com/pepopowitz/stevenhicks.me/pull/14/files#diff-e45e8998b62ae0dac0f40e46f3db483cf93f3b027a20b4649e8988a78785b371R20" rel="noopener noreferrer"&gt;was the &lt;code&gt;title&lt;/code&gt; property that gets passed to &lt;code&gt;getShareImage&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;eleventyComputed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;shareImage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;getShareImage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Emit the &lt;code&gt;shareImage&lt;/code&gt; property in your template
&lt;/h3&gt;

&lt;p&gt;I have &lt;a href="https://github.com/pepopowitz/stevenhicks.me/blob/5d95aaa4145975ba6abd36f5696442347d7ed7b0/_includes/layout.pug" rel="noopener noreferrer"&gt;one base page template for my site&lt;/a&gt;. It's based on the &lt;a href="https://pugjs.org/" rel="noopener noreferrer"&gt;pug language&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I &lt;a href="https://github.com/pepopowitz/stevenhicks.me/pull/14/files#diff-6bc31447ed9d02b94fbb1d63dd5659e7d2a6d6e7f8ab4c757650c16e189a7316" rel="noopener noreferrer"&gt;updated it to emit a &lt;code&gt;shareImage&lt;/code&gt; in the appropriate meta tags if it exists&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
meta(property='og:image' content=`${shareImage || 'https://www.stevenhicks.me/static/img/avatar.jpg'}`)
meta(property='twitter:image' content=`${shareImage || 'https://www.stevenhicks.me/static/img/avatar.jpg'}`)

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

&lt;/div&gt;



&lt;p&gt;All blog articles will have that &lt;code&gt;shareImage&lt;/code&gt; computed property, so they'll emit their generated images. Pages like Home and About won't have a &lt;code&gt;shareImage&lt;/code&gt; computed because I put the &lt;code&gt;blog.11tydata.js&lt;/code&gt; file in the &lt;code&gt;blog/&lt;/code&gt; folder — so they'll get stuck with an image of my face. MY FACE!&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap it up, Steve
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/pepopowitz/stevenhicks.me/pull/14/files" rel="noopener noreferrer"&gt;The PR&lt;/a&gt; ends up being 39 lines added — and over half of that is configuration settings for the text overlay. JavaScript is neat!&lt;/p&gt;

&lt;p&gt;You likely found this article because you've already got an &lt;a href="https://www.11ty.dev/" rel="noopener noreferrer"&gt;eleventy&lt;/a&gt; site, but if you don't, you should absolutely give it a look. It's a great option for building a blog or any other site where the data doesn't change frequently. I find it more intuitive than other popular options. This example especially demonstrates how well it's designed for generating dynamic content. Every time I come across a new problem I'm delighted to find there's a simple mechanism built into eleventy to solve it.&lt;/p&gt;




&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;br&gt;
&lt;sup&gt;1&lt;/sup&gt; Hahahahaha I act like I had agency in this decision but really I didn't see Stephanie's article until I had invested a lot of time in generating an image template based on Jason's articles. I'm as much a sucker for the sunk-cost fallacy as anyone, and here we are.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>eleventy</category>
      <category>javascript</category>
      <category>social</category>
    </item>
    <item>
      <title>Sandwiches, Froyo, and Delivering A Cohesive Message</title>
      <dc:creator>Steven Hicks</dc:creator>
      <pubDate>Mon, 16 Nov 2020 16:24:21 +0000</pubDate>
      <link>https://dev.to/pepopowitz/sandwiches-froyo-and-delivering-a-cohesive-message-4o60</link>
      <guid>https://dev.to/pepopowitz/sandwiches-froyo-and-delivering-a-cohesive-message-4o60</guid>
      <description>&lt;p&gt;I love sandwiches. I &lt;strong&gt;love&lt;/strong&gt; sandwiches. Yes — I would marry them, if I could.&lt;/p&gt;

&lt;p&gt;This week my friend &lt;a href="https://twitter.com/jonallured"&gt;Jon&lt;/a&gt; asked me what I ate for lunch one day. It was the right day to ask. I spent probably too much time describing to him my current favorite sandwich — the amazing &lt;a href="https://thechocolatefactorywi.com/menu/"&gt;Phoenix sub from local chain The Chocolate Factory&lt;/a&gt;. It's not a complicated sandwich but it's got a specific vibe and it does it well. Jon was really impressed 🙄 and asked me if I was a professional sandwich-eater. I did not dispute.&lt;/p&gt;

&lt;p&gt;It got me thinking about other sandwiches because, well, yummmm. The "Velvet Elvis" - an absolute chef's kiss that a Milwaukee bar stopped selling years ago, but still makes an appearance in my kitchen — peanut butter, banana, and bacon, "grilled" in a buttery frying pan. If you're a fan of salty &amp;amp; sweet it will make you cry. And a sandwich that I've only heard about — &lt;a href="https://www.parisibakery.com/parisi-bakery-deli-sandwiches/"&gt;The Dennis&lt;/a&gt; — mentioned many times in our work Slack by my New York-based coworkers. It looks like a real monster.&lt;/p&gt;

&lt;p&gt;Amazing sandwiches are amazing because they are cohesive. They push you to the edge of your comfort level but they show the restraint and respect to not push you over. They are adventurous...but &lt;em&gt;responsibly&lt;/em&gt; adventurous.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unlike my kids at the frozen yogurt place.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Andes Candies, Boba Balls, Raspberries, and sour Gummi Worms
&lt;/h2&gt;

&lt;p&gt;My kids are currently 9 and 12. It's been a while since we've been to the local frozen yogurt joint (ugh, COVID), but when we've gone their combinations give me shivers. Oooo, they've got strawberry yogurt! And Reese's Pieces! And Andes Candies! Oooo, sour Gummi worms, brownie bites, raspberries, and marshmallows!&lt;/p&gt;

&lt;p&gt;Don't get me wrong - each of things, on its own, is delicious:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reese's Pieces&lt;/li&gt;
&lt;li&gt;Andes Candies&lt;/li&gt;
&lt;li&gt;sour Gummi Worms&lt;/li&gt;
&lt;li&gt;brownie bites&lt;/li&gt;
&lt;li&gt;raspberries&lt;/li&gt;
&lt;li&gt;marshmallows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But together? An absolute nightmare. Barf city. I won't even finish the leftovers when my kids put together these nightmare concoctions.&lt;/p&gt;

&lt;h2&gt;
  
  
  I'm so hungry right now but what does this have to do with delivering a clear message?
&lt;/h2&gt;

&lt;p&gt;When you're communicating a complex idea to someone, it's tempting to tell them everything you know. Every single detail you've learned is valuable to you. There are &lt;em&gt;so many cool things&lt;/em&gt; you've learned about your topic, and you've put a lot of time into it. You just want to share &lt;strong&gt;all of it&lt;/strong&gt; with your audience.&lt;/p&gt;

&lt;p&gt;But is all of it necessary? Is some of it noise? How much of it will distract someone from the main idea you're teaching them?&lt;/p&gt;

&lt;p&gt;Think of it like that trip to the frozen yogurt place. You're not my kids — you know better than to mix &lt;em&gt;all&lt;/em&gt; the toppings you like in one dish. You'd rather craft something cohesive. Even though you &lt;em&gt;love&lt;/em&gt; toasted coconut &lt;em&gt;and&lt;/em&gt; Andes Candies &lt;em&gt;and&lt;/em&gt; raspberries, you wouldn't dare put them all in the same dish.&lt;/p&gt;

&lt;p&gt;Treat your communication and content the same way. Not every concept needs to be shared. Find the ones that fit together and create a cohesive narrative.&lt;/p&gt;

&lt;p&gt;If you're drafting an email to your leader, they don't need to know &lt;em&gt;everything&lt;/em&gt; that happened in the latest incident. Tell them the most important facts.&lt;/p&gt;

&lt;p&gt;If you're writing a blog post, it doesn't have to be 5000 words long. Less is better — if you feel like you've got several stories to tell, tell them in separate blog posts.&lt;/p&gt;

&lt;p&gt;If you're building a presentation, don't add slides just to fill time. Your listeners will learn a lot more from one cohesive story than three stories and 13 unrelated points.&lt;/p&gt;

&lt;p&gt;If you're building a workshop or course, you don't have to teach every feature. Focus on the features that most align with your learning objectives. Give them resources to learn more when there could be side quests, but your workshop doesn't need to include the entire universe.&lt;/p&gt;

&lt;p&gt;Cut out the Gummi Bears if you're going for a mint and white chocolate vibe. Skip the performance analysis if you're going for an intro to building an app. Be the Velvet Elvis. Be the Dennis. Be the Phoenix. Be cohesive. Cut out the noise that doesn't fit your narrative.&lt;/p&gt;

</description>
      <category>writing</category>
      <category>speaking</category>
      <category>blogging</category>
      <category>communication</category>
    </item>
  </channel>
</rss>
