<?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>Forem: Darragh O'Riordan</title>
    <description>The latest articles on Forem by Darragh O'Riordan (@darraghor).</description>
    <link>https://forem.com/darraghor</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%2F129214%2F9eb22178-42e0-4df3-8ee7-c920087d5bbf.jpg</url>
      <title>Forem: Darragh O'Riordan</title>
      <link>https://forem.com/darraghor</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/darraghor"/>
    <language>en</language>
    <item>
      <title>Running NextJS in a Docker Container</title>
      <dc:creator>Darragh O'Riordan</dc:creator>
      <pubDate>Thu, 13 Apr 2023 17:12:33 +0000</pubDate>
      <link>https://forem.com/darraghor/running-nextjs-in-a-docker-container-42co</link>
      <guid>https://forem.com/darraghor/running-nextjs-in-a-docker-container-42co</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This is a guide on how to run a NextJS application in a Docker container.&lt;/p&gt;

&lt;p&gt;NextJS is created and maintained by Vercel so it works great on their hosting service. Vercel is an amazing service that allows you to deploy your NextJS application with ease.&lt;/p&gt;

&lt;p&gt;However, there are times when you may want to run your application in a Docker container. Using a docker container will allow you to host a NextJS application on Azure, AWS, DigitalOcean, Fly.io or any modern cloud provider.&lt;/p&gt;

&lt;p&gt;This guide will show you how to dockerize your NextJS app in minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You will need the following installed on your machine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker Desktop&lt;/li&gt;
&lt;li&gt;NodeJS&lt;/li&gt;
&lt;li&gt;A package manager. I use pnpm as my package manager, but you can use npm or yarn. They have similar commands.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;You must configure your NextJS application to build a standalone application. This is required for running NextJS in a Docker container.&lt;/p&gt;

&lt;p&gt;In your &lt;code&gt;next.config.js&lt;/code&gt; file, add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const nextConfig = {
  output: 'standalone',
  // ... other config
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuring Docker
&lt;/h2&gt;

&lt;h3&gt;
  
  
  .dockerignore
&lt;/h3&gt;

&lt;p&gt;Add a &lt;code&gt;.dockerignore&lt;/code&gt; file to the root of your project. This file will contain the files and folders that you do not want to copy into the Docker container.&lt;/p&gt;

&lt;p&gt;I say root of the project here because I build in a monorepo. But you can put this file in the root of your NextJS application if you’re not using a monorepo.&lt;/p&gt;

&lt;p&gt;I use this &lt;code&gt;.dockerignore&lt;/code&gt; file for my NextJS projects. You can use it as a starting point.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dockerfile*
.dockerignore
node_modules
npm-debug.log
README.md
.next
.git
.jest_cache
.docker-compose
.vscode
.terraform
.husky
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dockerfile
&lt;/h3&gt;

&lt;p&gt;Add a &lt;code&gt;Dockerfile&lt;/code&gt; to the root of your project. This file will contain the instructions for building your Docker image.&lt;/p&gt;

&lt;p&gt;This is a multi-stage Dockerfile. This means that we will build the NextJS application in one stage and then copy the built files into a new stage. It results in a smaller final image size for deployment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;STAGE 1: A container with pnpm and python3 is required&lt;/li&gt;
&lt;li&gt;STAGE 2: Fetch deps into the pnpm store&lt;/li&gt;
&lt;li&gt;STAGE 3: Copy the application code and install all deps from cache into the application&lt;/li&gt;
&lt;li&gt;STAGE 4: Build the NextJS app&lt;/li&gt;
&lt;li&gt;STAGE 5: Create a clean production image - only take pruned assets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each stage is stored on the build machine’s cache. This means that if you make a change to your code, only the last 3 stages will be rebuilt. This is a huge time saver.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# STAGE 1: A container with pnpm and python3 is required
FROM node:18-alpine as pnpm_base

WORKDIR /app
# install pnpm
RUN npm i --global --no-update-notifier --no-fund pnpm@7
# install python3 and other deps
RUN apk add --no-cache g++ make py3-pip libc6-compat

# STAGE 2: fetch deps into the pnpm store
# We run pnpm fetch in a separate step to avoid re-fetching deps on every code change
# fetch is a pnpm command that downloads all dependencies to the local store
# You could remove or skip this step if using npm or yarn (but make sure to copy your lock file)
FROM pnpm_base as fetched_deps
WORKDIR /app
# setting production env usually speeds up install for your package manager
ENV NODE_ENV production
# copy the lock file that you use
COPY pnpm-lock.yaml ./
# set the store dir to a folder that is not in the project
RUN pnpm config set store-dir /workdir/.pnpm-store
RUN pnpm fetch

# STAGE 3: Copy the application code and install all deps from cache into the application
FROM fetched_deps as with_all_deps
# I use mono repo so I copy the whole project code (except for ignored things)
COPY . ./
# finally, install all the deps
RUN pnpm install --offline

# STAGE 4: Build the NextJS app
# Here we use pnpm filtering to only build the frontend app
# Then we use pnpm deploy command to prune the dependencies
FROM with_all_deps as builder
RUN pnpm --filter='*frontend' build
RUN pnpm --filter='*frontend' deploy pruned --prod

# STAGE 5: Create a clean production image - only take pruned assets
FROM node:18-alpine AS runner
WORKDIR /app
# We set the NODE_ENV to production to make sure that the NextJS app runs in production mode
ENV NODE_ENV=production
# We add a non-root user to run the app for security reasons
RUN addgroup --system --gid 1001 app
RUN adduser --system --uid 1001 app
USER app

# We copy the built NextJS app assets from the builder stage
# NextJS produces a backend server and a frontend app
COPY --chown=app:app --from=builder /app/apps/frontend/.next/standalone src/
COPY --chown=app:app --from=builder /app/apps/frontend/public src/apps/frontend/public
COPY --chown=app:app --from=builder /app/apps/frontend/.next/static src/apps/frontend/.next/static

# Set the port that the NextJS app will run on
# You should choose a port that is supported by your cloud provider
ENV PORT 5000
# Expose the port to the outside world
EXPOSE 5000

# Finally, we run the NextJS app
CMD ["node", "src/apps/frontend/server.js"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Building the Docker Image
&lt;/h2&gt;

&lt;p&gt;Now that you have your Dockerfile, you can build the Docker image.&lt;/p&gt;

&lt;p&gt;Here we build with a tag of &lt;code&gt;my-frontend-app&lt;/code&gt;. You can name it whatever you want. But a named tag makes it easier to run the image later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker build -t my-frontend-app .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running the Docker Image
&lt;/h2&gt;

&lt;p&gt;Now that you have your NextJS Docker image, you can run it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -p 5000:5000 my-frontend-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will run the NextJS app on port 5000. You can now access the NextJS app at &lt;code&gt;http://localhost:5000&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That’s it! You now have a NextJS app running in a Docker container.&lt;/p&gt;

&lt;p&gt;You can now deploy this Docker image to any cloud provider that supports Containers (All of them do).&lt;/p&gt;

&lt;p&gt;I describe how to run a node container of Dokku in this &lt;a href="https://www.darraghoriordan.com/2021/12/29/run-node-app-postgres-dokku-digital-ocean/"&gt;blog post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have any questions, please leave a comment below.&lt;/p&gt;

&lt;p&gt;If you want to see a real world example of NextJs in a Container, check out my &lt;a href="https://usemiller.dev/docs/miller-start/reference/miller-web/L0RvY2tlcmZpbGUtZmU="&gt;NextJS Starter&lt;/a&gt; project. It uses Docker to deploy NextJs to Dokku on &lt;a href="https://m.do.co/c/1ee4e460bc81"&gt;Digital Ocean&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building an AI generated game with Stable Diffusion and data from Wikipedia</title>
      <dc:creator>Darragh O'Riordan</dc:creator>
      <pubDate>Sun, 27 Nov 2022 17:12:33 +0000</pubDate>
      <link>https://forem.com/darraghor/building-an-ai-generated-game-with-stable-diffusion-and-data-from-wikipedia-26h1</link>
      <guid>https://forem.com/darraghor/building-an-ai-generated-game-with-stable-diffusion-and-data-from-wikipedia-26h1</guid>
      <description>&lt;p&gt;Last week I released a game called &lt;a href="https://doodleai.darraghoriordan.com"&gt;Doodle:ai&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the game you’re shown AI generated images and you have to guess the Wikipedia topic it used to create the game.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/ff0f65e1e1bf2d4afe2574e84a5d9133/3d68f/doodle-ai.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lfkUrr1y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/ff0f65e1e1bf2d4afe2574e84a5d9133/acf8f/doodle-ai.png" alt="Doodle:ai game interface" title="Doodle:ai game interface" width="880" height="607"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Doodle:ai game interface&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;DoodleAI has had thousands of players since launch and I thought I would share how I built it.&lt;/p&gt;
&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;There has been incredible progress in generative AI models over the last 3 months and I wanted to build something with it.&lt;/p&gt;

&lt;p&gt;I had this idea to automatically create Google Doodles based on Wikipedia topics.&lt;/p&gt;

&lt;p&gt;As I was building this Google Doodle generator tool, I thought it might be fun to see if I could randomise the output a little and guess what the AI had chosen for a day.&lt;/p&gt;

&lt;p&gt;It worked as a game so I took that direction.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0f8LOD_v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/dd4dc67a82bc746ab91bd1a7c850a690/architecture.drawio.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0f8LOD_v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/dd4dc67a82bc746ab91bd1a7c850a690/architecture.drawio.svg" alt="Architecture" width="880" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The game runs off pre-generated puzzle data.&lt;/p&gt;

&lt;p&gt;The backend is a NestJS server which exposes endpoints to queue up puzzle generation jobs and store the output in a Digital Ocean Spaces blob store.&lt;/p&gt;

&lt;p&gt;The frontend is a React app which uses the Digital Ocean Spaces store to get the daily puzzle.&lt;/p&gt;
&lt;h3&gt;
  
  
  The high level process for generating a puzzle
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;An administrator (Me) queues up a puzzle generation job for a date range. These jobs get put in a Bull queue by a NestJS controller.&lt;/li&gt;
&lt;li&gt;The Bull queue workers retrieve and process daily topics from Wikipedia.&lt;/li&gt;
&lt;li&gt;The topics are filtered and cleaned up to remove topics that are not suitable for a game.&lt;/li&gt;
&lt;li&gt;A random topic is chosen and the worker requests the summaries for Wikipedia links and articles relevant to the topic.&lt;/li&gt;
&lt;li&gt;The summaries are combined into an essay and sent to Azure NLP for analysis.&lt;/li&gt;
&lt;li&gt;The NLP analysis is filtered and mapped to useful combinations for prompt generation.&lt;/li&gt;
&lt;li&gt;Lots of prompts are generated and 4 random prompts are chosen.&lt;/li&gt;
&lt;li&gt;The prompts are sent to stable diffusion to generate images.&lt;/li&gt;
&lt;li&gt;The images and puzzle data are packaged and sent to Digital Ocean Spaces.&lt;/li&gt;
&lt;li&gt;A customer opens the game and the puzzle data is retrieved from Digital Ocean Spaces.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Topic selection
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BxoESidd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/a000fde9a0a4ef4e74d59a77aed7b941/topicSelection.drawio.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BxoESidd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/a000fde9a0a4ef4e74d59a77aed7b941/topicSelection.drawio.svg" alt="Topic selection" width="880" height="143"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first step is to select a topic for the game. Wikipedia is a relatively high quality source of well organised trivia.&lt;/p&gt;

&lt;p&gt;I use the Wikipedia API to get a list of topics for a given day. I then filter the topics to remove topics that are not suitable for a game.&lt;/p&gt;

&lt;p&gt;I originally accessed the wikimedia api directly. The first few games used this data.&lt;/p&gt;

&lt;p&gt;But Wikitext is extremely difficult to parse so I have changed to use the &lt;a href="https://github.com/spencermountain/wtf_wikipedia"&gt;wtfWikipedia&lt;/a&gt; node library. This returns a structured javascript object and is much easier to work with.&lt;/p&gt;

&lt;p&gt;See an example of wikitext below. It’s so hard to parse, thankfully we have open source parsers like wtfWikipedia.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{{Short description|Capital city of Ontario, Canada}}
{{About|the city in Ontario}}
{{Redirect|City of Toronto|the city's government|Municipal government of Toronto}}
{{Pp-move-indef}}
{{Pp-vandalism|small=yes}}
{{Use Canadian English|date=July 2014}}
{{Use mdy dates|date=April 2021}}
{{Infobox settlement
| name = Toronto
| official_name = City of Toronto
| settlement_type = [[List of cities in Ontario|City]] ([[List of municipalities in Ontario#Single-tier municipalities|single-tier]])&amp;lt;!-- Consensus see: [[Wikipedia talk:WikiProject Ontario/Archive 1#City infoboxes: "tier" or "conventional" municipal statuses (or both)?]] --&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the Wikipedia pages are parsed, I filter out topics that are not suitable for a game. This is generally topics that I feel will result in images that are too visceral or offensive like mass shootings or terrorist attacks.&lt;/p&gt;

&lt;p&gt;The final step is simply choosing a random topic as the correct answer for the game.&lt;/p&gt;

&lt;h2&gt;
  
  
  Entity Categorisation
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bkBzy6-E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/91520b7a0a849b59af24f5dc89333526/entityCategorisation.drawio.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bkBzy6-E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/91520b7a0a849b59af24f5dc89333526/entityCategorisation.drawio.svg" alt="Entity Categorisation" width="880" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The game requires keywords for guesses and keywords for image generation prompts. To create these keywords I build an “essay” from the topic content and any adjacent Wikipedia topics.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  public async getSummariesOfPages(pageNames: string[]): Promise&amp;lt;string[]&amp;gt; {
        const summaries = (await wtf.fetch(pageNames, {
            lang: "en",
            // eslint-disable-next-line @typescript-eslint/naming-convention
            follow_redirects: true,
        })) as WtfWikiDocument[];

        const parsedWikiPages = summaries &amp;amp;&amp;amp; summaries.map((x) =&amp;gt; x.json());

        this.logger.log(
            "summaries",
            util.inspect(parsedWikiPages[0], false, 8, true)
        );
  // continue to parse and filter the data
  // ...
  // ...
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This essay is sent to azure NLP which returns a list of detected entities. This entity list is passed to the next stage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const client = new TextAnalyticsClient(
  this.config.url,
  new AzureKeyCredential(this.config.apiKey)
)

const results = await client.recognizeEntities([text])
this.logger.log('Text analysis results', results)

// map the results to a list of entities
// ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Prompt Creation
&lt;/h2&gt;

&lt;p&gt;The difficult part of using AI generation tools is creating prompts.&lt;/p&gt;

&lt;p&gt;I have gone through four major refactors of my prompt generation process. I have to provide spoilers for these games to explain the prompts. Sorry about that!&lt;/p&gt;

&lt;p&gt;Play all the games first before reading this if you prefer.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Simply add all categorised entity keywords to 4 fixed prompts
&lt;/h3&gt;

&lt;p&gt;This method resulted in some very interesting images but maybe 1/3 were usable for the game and they were all quite similar.&lt;/p&gt;

&lt;p&gt;Example: &lt;em&gt;"watercolor, 1995 gulf of aqaba earthquake, mercalli intensity scale, gulf, of aqaba earthquake, sinai peninsula, saudi arabia, tsunami"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Play this game here: &lt;a href="https://doodleai.darraghoriordan.com/?gamedate=2022-11-22T12:00:00.000Z"&gt;https://doodleai.darraghoriordan.com/?gamedate=2022-11-22T12:00:00.000Z&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yR_p5r1V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/f88cde02ebad3b75e581895287b70ff8/01e7c/aqiba.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yR_p5r1V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/f88cde02ebad3b75e581895287b70ff8/01e7c/aqiba.png" alt="game image" width="512" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Split entities randomly across a few random prompts
&lt;/h3&gt;

&lt;p&gt;I tried randomising the keywords and selecting the first half for one prompt and the second half for another prompt and so on. This was actually OK but there were still issues where prompts that were suitable for portraits were used for landscapes and vice versa.&lt;/p&gt;

&lt;p&gt;Example: &lt;em&gt;"denny party, seattle,by Josef Thomas, matte painting, trending on artstation HQ"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Play this game here: &lt;a href="https://doodleai.darraghoriordan.com/?gamedate=2022-11-13T12:00:00.000Z"&gt;https://doodleai.darraghoriordan.com/?gamedate=2022-11-13T12:00:00.000Z&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UEelM1h2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/a8a5fd8780adeb28b28eddc51917f1da/01e7c/denny-seattle.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UEelM1h2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/a8a5fd8780adeb28b28eddc51917f1da/01e7c/denny-seattle.png" alt="game image" width="512" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Select the entities with best confidence scores and randomly join them
&lt;/h3&gt;

&lt;p&gt;I tried only selecting the top 4 entity confidence scores. These were OK but categorisation confidence is not relevance so this often missed good keywords.&lt;/p&gt;

&lt;p&gt;Example: &lt;em&gt;"Donald Johanson, Australopithecus afarensis, Lucy (Australopithecus), The Beatles, depth of field, photograph, Polaroid 600 Instant Film"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Play this game here: &lt;a href="https://doodleai.darraghoriordan.com/?gamedate=2022-11-24T12:00:00.000Z"&gt;https://doodleai.darraghoriordan.com/?gamedate=2022-11-24T12:00:00.000Z&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XlcqEl_f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/4d8797000ab2178c736b89038ce9be92/01e7c/lucy2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XlcqEl_f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/4d8797000ab2178c736b89038ce9be92/01e7c/lucy2.png" alt="game image" width="512" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Split entities in set patterns, use specific lists of prompts suitable for the category
&lt;/h3&gt;

&lt;p&gt;This is the current method I use. It results in mostly relevant images and a good variety of images.&lt;/p&gt;

&lt;p&gt;Example: &lt;em&gt;"a hyper realistic professional photographic view picture of Richard Seddon, New Zealand photographic filter unreal engine 5 realistic hyperdetailed 8k ultradetail cinematic concept art volumetric lighting, fantasy artwork, very beautiful scenery, very realistic painting effect, hd, hdr, cinematic 4k wallpaper, 8k, ultra detailed, high resolution, artstation trending on artstation in the style of Albert Dros glowing rich colors powerful imagery nasa footage drone footage drone photography"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Play this game here: &lt;a href="https://doodleai.darraghoriordan.com/?gamedate=2022-11-28T12:00:00.000Z"&gt;https://doodleai.darraghoriordan.com/?gamedate=2022-11-28T12:00:00.000Z&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ET7QuSYO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/742cece7405439f70c72a73069a324ce/01e7c/new-zealand.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ET7QuSYO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/742cece7405439f70c72a73069a324ce/01e7c/new-zealand.png" alt="game image" width="512" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this prompt generation method I split the entities into some custom groupings and then select a random prompt from a list of prompts suitable for my pseudo-categories. e.g. I make sure Persons are used for portraits and Locations are used for landscapes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lyLuqIQA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/f2a599b1f7b2f923d3479113211ab6cf/promptGeneration.drawio.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lyLuqIQA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/f2a599b1f7b2f923d3479113211ab6cf/promptGeneration.drawio.svg" alt="Prompt generation" width="880" height="162"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Azure NLP categorisation is pretty good. There is a confidence and often a sub category included for a single entity.&lt;/p&gt;

&lt;p&gt;These are the main categories doodle:ai uses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Person - Names of people.
PersonType - Job types or roles held by a person
Skill - Skills or abilities
Event -Natural - Naturally occurring events.
Event -Sports - Sporting events.
Location - GPE - Geographic and natural features such as rivers, oceans
Location - Structural - Manmade structures. e.g. ships
Organization - Medical
Organization - Stock exchange
Organization - Sports
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I link these together to make prompts so things like&lt;/p&gt;

&lt;p&gt;Person + Location - GPE Event - Sports + Organisation - Sports&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const removeUnwantedCategories = nlpEntities.filter(
  (x) =&amp;gt; !unwantedCategories.has(x.category)
)

const geoLocationEntities = removeUnwantedCategories.filter(
  (x) =&amp;gt; x.category === 'Location' &amp;amp;&amp;amp; x.subCategory === 'GPE'
)
const manMadeEntities = removeUnwantedCategories.filter(
  (x) =&amp;gt; x.category === 'Location' &amp;amp;&amp;amp; x.subCategory === 'Structural'
)
// more of these
// ...
// then map them to entity combinations and random prompts
const manMadeAtLocations = this.createPrimaryWithRandomSecondaryPrompts(
  manMadeEntities,
  geoLocationEntities,
  locationPromptTemplates
)

//
const generatedPrompts: string[] = [
  ...manMadeAtLocations,
  // and many more of these
  // ...
]
  .sort(() =&amp;gt; Math.random() - 0.5)
  .slice(0, requiredNumberOfPrompts) // then grab just 4 and return

return generatedPrompts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Linking entities seems to result in better output so I will likely stick with this method and focus on improving the actual prompts.&lt;/p&gt;

&lt;p&gt;Unfortunately, there is no way for me to link things like “person” and “location” accurately in the context of the topic so sometimes there are very weird results due to my random process. e.g. I might end up with “Ghandi, England” where instead “Ghandi, India” would result in a more coherent image.&lt;/p&gt;

&lt;p&gt;Another issue is that sometimes the adjacent articles are too far removed from the main topic to be useful but I have no way to tell relevancy or adjacency as a score at the moment. So I randomise the prompts and hope for the best.&lt;/p&gt;

&lt;p&gt;I’ll keep tweaking this generation process. It’s fun to see what works and what doesn’t.&lt;/p&gt;

&lt;p&gt;There is an incredible amount of randomness in this process.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The topic is chosen at random for a given date&lt;/li&gt;
&lt;li&gt;Entities for my pseudo-categories are chosen at random&lt;/li&gt;
&lt;li&gt;The order of the combined entities in prompts are random&lt;/li&gt;
&lt;li&gt;I create 30-40 prompts per game and choose only 4 of them&lt;/li&gt;
&lt;li&gt;Stable diffusion is passed a random seed for each prompt&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I have no idea what the output will be until I see it, and I can never recreate a game.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stable Diffusion
&lt;/h2&gt;

&lt;p&gt;I’m not doing anything very special with Stable Diffusion for Doodle:ai. There are heaps of great articles on how to setup and run stable diffusion on a PC.&lt;/p&gt;

&lt;p&gt;One thing I did do is removing the nsfw filter because it’s too eager.&lt;/p&gt;

&lt;p&gt;One difficult thing was running the conda environment required for python from a web server.&lt;/p&gt;

&lt;p&gt;First I had to wrap the nodeJS spawn call in a Promise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { spawn } from 'child_process'

export const spawnPromise = (
  command: string,
  commandArguments: string[],
  cwd: string,
  shell?: boolean
): Promise&amp;lt;string&amp;gt; =&amp;gt; {
  return new Promise((resolve, reject) =&amp;gt; {
    try {
      // console.log('spawnPromise', cmd, args, cwd, shell)
      const runCommand = spawn(command, commandArguments, {
        cwd,
        shell,
        timeout: 500_000,
        stdio: 'inherit',
        // stdio: ["pipe", "pipe", "inherit"],
      })
      console.log('runcmd connected state:', runCommand.connected)
      console.log('runcmd pid state:', runCommand.pid)
      runCommand?.stdout?.on('data', (data) =&amp;gt;
        // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
        console.log(data?.toString() || '')
      )
      runCommand.on('exit', (code) =&amp;gt; {
        console.log('spawnPromise exit', code)
        if (code !== 0) {
          reject(new Error(`Command failed with code ${code || 'UNKNOWN'}`))
        }
        resolve('')
      })
      runCommand.on('error', (error) =&amp;gt; {
        console.log('spawn error', error)
        throw new Error(error.message)
      })
    } catch (error) {
      reject(error)
    }
  })
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I call the &lt;code&gt;txt2img&lt;/code&gt; script combined with &lt;code&gt;conda&lt;/code&gt; rather than launching conda as an environment first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { spawnPromise } from './SpawnPromise'
import { CoreLoggerService } from '@darraghor/nest-backend-libs'
import { Injectable } from '@nestjs/common'
import os from 'os'
import path from 'path'
import { execSDPromise } from './ExecPromise'
@Injectable()
class StableDiffusionRunner {
  constructor(private readonly logger: CoreLoggerService) {}
  // eslint-disable-next-line @typescript-eslint/require-await
  async run(prompt: string, puzzleDate: Date) {
    this.logger.log('running sdiff', prompt)
    // create random integer seed as string
    const seed = Math.floor(Math.random() * 1_000_000_000).toString()
    return execSDPromise(
      'conda',
      [
        'run',
        '--no-capture-output',
        '-n',
        'ldo',
        'python',
        '-u',
        'optimizedSD/optimized_txt2img.py',
        '--prompt',
        `"${prompt}"`,
        '--n_iter',
        '1',
        '--n_samples',
        '1',
        '--H',
        '512',
        '--W',
        '512',
        '--turbo',
        '--seed',
        seed,

        '--ddim_steps',
        '50',
        '--skip_grid',
        '--outdir',
        `./output/${puzzleDate.getUTCFullYear()}/${
          puzzleDate.getUTCMonth() + 1
        }/${puzzleDate.getUTCDate()}`,
      ],
      path.join(
        os.homedir(),
        'projects',
        'personal-projects',
        'stable-diffusion'
      ),
      '/bin/bash'
    )
  }
}

export default StableDiffusionRunner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I do occasionally run out of memory when generating images if I’m doing other work on the PC - not VRAM, just regular RAM.&lt;/p&gt;

&lt;p&gt;I “only” have 16GB. I guess I should have gone for 32GB! If I run generation while I’m not using the PC it works for hours no problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The game engine / Frontend Stack
&lt;/h2&gt;

&lt;p&gt;The frontend is based on a stack I use for all my projects.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;li&gt;Tailwind CSS&lt;/li&gt;
&lt;li&gt;React Query&lt;/li&gt;
&lt;li&gt;React Router&lt;/li&gt;
&lt;li&gt;Vite&lt;/li&gt;
&lt;li&gt;Vitest&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All data for the current game is stored in a &lt;a href="https://tanstack.com/query/v4"&gt;React Query&lt;/a&gt; cache and Local Storage. This means that the game state is persisted across sessions.&lt;/p&gt;

&lt;p&gt;There isn’t much technical complexity here. React Query takes most of that away. What a champ of a library!&lt;/p&gt;

&lt;p&gt;The frontend for doodle:ai is hosted on Netlify.&lt;/p&gt;

&lt;p&gt;I also have an instance of Vercel’s og-image-generator running on vercel for this project.&lt;/p&gt;

&lt;p&gt;This allows me to create an og-image using the first image for a puzzle for a given day.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Backend Server
&lt;/h2&gt;

&lt;p&gt;The backend host is simply a &lt;a href="https://m.do.co/c/1ee4e460bc81"&gt;Digital Ocean Spaces&lt;/a&gt; instance. Spaces are Digital Ocean’s blob store that matches AWS S3 APIs (you can use AWS clients to work with Spaces).&lt;/p&gt;

&lt;p&gt;I have a CDN in front of the game files to increase performance and reduce cost.&lt;/p&gt;

&lt;p&gt;The backend for generating puzzles is actually a NestJS web application. It might seem strange to run this from a web app instead of a CLI tool but I have a bunch of prebuilt libraries for NestJS that make it very, very fast for me to develop new projects with.&lt;/p&gt;

&lt;p&gt;Because stable diffusion requires an NVidia GPU, I run the puzzle generation on my gaming PC with 16GB RAM, an SSD and an Nvidia 2080ti 11GB VRAM.&lt;/p&gt;

&lt;p&gt;It takes 2-3 minutes to create a single puzzle. This means around 18 hours for a year of puzzles. Because I’m tweaking the algorithms I only generate a few weeks in advance at the moment. I’ll probably generate a year in advance once I’m happy with the algorithms.&lt;/p&gt;

&lt;p&gt;Optimised stable diffusion models could probably do a puzzle in 30 seconds and this is something I’m investigating. It would be easy to move the workers (NestJS application) to a cloud GPU instance but these GPU-enabled Cloud VMs are quite pricey.&lt;/p&gt;

&lt;p&gt;Having a server is also useful for cron jobs associated with the game’s Twitter account - &lt;a href="https://twitter.com/PuzzleAI"&gt;@PuzzleAI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For this role the server is running on the same $5 &lt;a href="https://m.do.co/c/1ee4e460bc81"&gt;Digital Ocean&lt;/a&gt; droplet that I use for all my projects.&lt;/p&gt;

&lt;p&gt;The generation APIs are not exposed on this server (no endpoints are public). So it just sits there running cron.&lt;/p&gt;

&lt;p&gt;There is a standard &lt;code&gt;conda&lt;/code&gt; environment on my machine which has all the dependencies for image generation using stable diffusion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;I’m really happy with how Doodle:ai has turned out. It’s been a lot of fun to work on and I’ve learned a lot.&lt;/p&gt;

&lt;p&gt;The game stayed on the front page of Hacker News for a full day and I’ve had a lot of positive feedback from people who have played it.&lt;/p&gt;

&lt;p&gt;The game provides a nice platform, and a reason for me to keep learning and improving my knowledge of tools like Stable Diffusion.&lt;/p&gt;

&lt;p&gt;Play the game at &lt;a href="https://doodleai.darraghoriordan.com"&gt;doodleai.darraghoriordan.com&lt;/a&gt; and let me know if you like it!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>Easiest way to optimise images for web</title>
      <dc:creator>Darragh O'Riordan</dc:creator>
      <pubDate>Fri, 25 Nov 2022 17:12:33 +0000</pubDate>
      <link>https://forem.com/darraghor/easiest-way-to-optimise-images-for-web-3l9n</link>
      <guid>https://forem.com/darraghor/easiest-way-to-optimise-images-for-web-3l9n</guid>
      <description>&lt;p&gt;Here is how I optimise all pngs and jpgs in a folder for publishing to the web.&lt;/p&gt;

&lt;p&gt;To get the tools install with brew or whatever you use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew install imagemagick optipng
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run the following commands to produce optimised images for web sites. This will often reduce the size of the image by 50% or more with no obvious loss of quality for web.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# for jpgs
mogrify -strip -interlace Plane -sampling-factor 4:2:0 -quality 85% *.jpg

# For pngs and bitmaps
optipng *
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>DORA metric monitoring for your team in just 15 minutes with Apache Dev Lake</title>
      <dc:creator>Darragh O'Riordan</dc:creator>
      <pubDate>Sun, 16 Oct 2022 17:12:33 +0000</pubDate>
      <link>https://forem.com/darraghor/dora-metric-monitoring-for-your-team-in-just-15-minutes-with-apache-dev-lake-3oi9</link>
      <guid>https://forem.com/darraghor/dora-metric-monitoring-for-your-team-in-just-15-minutes-with-apache-dev-lake-3oi9</guid>
      <description>&lt;p&gt;DORA (DevOps Research and Assessment) metrics are an excellent way for engineering organisations to measure and improve their performance.&lt;/p&gt;

&lt;p&gt;Up until now, monitoring the DORA metrics across Github, Jira, Azure Devops etc required custom tooling or a tedious manual process.&lt;/p&gt;

&lt;p&gt;With Apache Dev Lake you can get beautiful reporting for DORA metrics on your local machine in as little as 15 minutes (honestly!).&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/8c01179a7caa6efd47c6ba2c3267ca15/4b538/headerJourney.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--msM7k_R4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/8c01179a7caa6efd47c6ba2c3267ca15/acf8f/headerJourney.png" alt="From Google Sheets to Grafana" title="From Google Sheets to Grafana" width="880" height="381"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;From Google Sheets to Grafana&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  DORA Metrics
&lt;/h2&gt;

&lt;p&gt;DORA metrics are 4 dimensions for improving your engineering team. The metrics came out of a report by Google in 2020. They’re designed so they balance velocity and stability.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deployment Frequency&lt;/li&gt;
&lt;li&gt;Lead time for changes&lt;/li&gt;
&lt;li&gt;Median Time to Restore Service&lt;/li&gt;
&lt;li&gt;Change Failure Rate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These metrics are explained in detail in books (&lt;a href="https://www.amazon.com.au/Accelerate-Software-Performing-Technology-Organizations/dp/1942788339"&gt;here&lt;/a&gt;) and blogs (&lt;a href="https://cloud.google.com/blog/products/devops-sre/using-the-four-keys-to-measure-your-devops-performance"&gt;here&lt;/a&gt;) so I won’t go in to any detail on what they are here.&lt;/p&gt;

&lt;p&gt;I have a note at the end of this article on updates to DORA since 2020 and the newer Microsoft SPACE framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  The old way of tracking DORA metrics
&lt;/h2&gt;

&lt;p&gt;Most teams will have various support systems like Github, Azure DevOps, Jira etc.&lt;/p&gt;

&lt;p&gt;Extracting the metrics required for DORA across so many developer tools is difficult to automate without custom software. The time cost is too much, especially for small teams.&lt;/p&gt;

&lt;p&gt;When I first started tracking DORA I used a spreadsheet and did everything manually.&lt;/p&gt;

&lt;p&gt;It wasn’t pretty and it was a lot of work but I was able to see a noticeable increase in deployed work when deployment frequency increased.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/ce4389ab9ddacdd6fd5afeabc820c0a2/e3f06/manualDora.jpg"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4j9LO_bN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/ce4389ab9ddacdd6fd5afeabc820c0a2/e3f06/manualDora.jpg" alt="Custom spreadsheet of DORA metrics" title="Custom spreadsheet of DORA metrics" width="643" height="507"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Custom spreadsheet of DORA metrics&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tracking DORA metrics in 2022: Apache Dev Lake
&lt;/h2&gt;

&lt;p&gt;Apache Dev Lake is a project from the Apache Foundation specifically for tracking engineering performance metrics.&lt;/p&gt;

&lt;p&gt;The project is new and it is in the incubation stage at the Apache Foundation.&lt;/p&gt;

&lt;p&gt;Dev Lake is a set of services in containers - a database, a data pipeline, a grafana instance with pre-built reports and a configuration web application.&lt;/p&gt;

&lt;p&gt;The dev lake team provide helm charts and docker compose files for easy setup of the infrastructure.&lt;/p&gt;

&lt;p&gt;The benefits of tracking your DORA metrics in Dev Lake are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It connects to most major platforms out of the box (Jira, Github, DevOps)&lt;/li&gt;
&lt;li&gt;Comes with pre built Grafana charts for DORA and many other metrics&lt;/li&gt;
&lt;li&gt;Free and you can easily run it locally to get started&lt;/li&gt;
&lt;li&gt;Your data or your client’s data never leaves your organisation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Having the data processed and stored locally is a huge benefit for many organisations. It means you don’t have to add an additional supplier to get started with DORA metrics.&lt;/p&gt;

&lt;p&gt;Visit the &lt;a href="https://devlake.apache.org/"&gt;dev lake site&lt;/a&gt; for more details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up a local instance of Apache Dev Lake
&lt;/h2&gt;

&lt;p&gt;You should have docker desktop installed before continuing.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get the docker compose and .env files from the releases page - &lt;a href="https://github.com/apache/incubator-devlake/releases/latest"&gt;https://github.com/apache/incubator-devlake/releases/latest&lt;/a&gt; and place them in a folder&lt;/li&gt;
&lt;li&gt;Open a terminal in that folder and &lt;code&gt;mv .env.example .env&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker compose up&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;That’s it! Wait for the 4 services to start&lt;/li&gt;
&lt;li&gt;You should be able to access the configuration site at &lt;a href="https://localhost:4000"&gt;https://localhost:4000&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="///static/d4793f39291bd42e3d916bb044ed2dc1/1d94f/blueprints.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vvdPtleQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/d4793f39291bd42e3d916bb044ed2dc1/acf8f/blueprints.png" alt="The Dev Lake homepage" title="The Dev Lake homepage" width="880" height="414"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The Dev Lake homepage&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There are some sites already configured on the instance in the image above. Yours will be empty for now.&lt;/p&gt;

&lt;p&gt;Now you can add any connections to your engineering tools like Jira or Github.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding connections to common dev tools
&lt;/h2&gt;

&lt;p&gt;Github is very popular so let’s use that to demonstrate setting up a tool connection.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on Data Connections &amp;gt; Github&lt;/li&gt;
&lt;li&gt;Give the connection a name you’ll remember&lt;/li&gt;
&lt;li&gt;The endpoint is &lt;code&gt;https://api.github.com/&lt;/code&gt; unless you have a special enterprise account - then you’ll need to use your own endpoint&lt;/li&gt;
&lt;li&gt;Get a personal access token from Github and paste it in the token field (&lt;a href="https://github.com/settings/tokens"&gt;https://github.com/settings/tokens&lt;/a&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The PAT permissions you need are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;repo:status&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;repo_deployment&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;public_repo&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;repo:invite&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;read:user&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;read:org&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I set the github rate limit to 5000 requests per hour but you can leave that as default if you like.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/4aae45c7e2e23b79d2173cc331fd5674/1d94f/addGithubconnection.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0o1mYJbY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/4aae45c7e2e23b79d2173cc331fd5674/acf8f/addGithubconnection.png" alt="Adding a Github connection" title="Adding a Github connection" width="880" height="565"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Adding a Github connection&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring projects in Dev Lake
&lt;/h2&gt;

&lt;p&gt;Now that you have a connection to your dev tools you can configure projects. In Dev Lake a project is called a “Blueprint”.&lt;/p&gt;

&lt;p&gt;I’ll add one of my open source projects (&lt;a href="https://github.com/darraghoriordan/eslint-plugin-nestjs-typed"&gt;https://github.com/darraghoriordan/eslint-plugin-nestjs-typed&lt;/a&gt;) to show how it works.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on Blueprints &amp;gt; Add Blueprint in the main left-hand menu&lt;/li&gt;
&lt;li&gt;Give it a name and select the Github connection you created earlier&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="///static/b06b0a7bd07946f804e0fe1cb143e29f/36405/addingBlueprintScreen1.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qpNb_zBu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/b06b0a7bd07946f804e0fe1cb143e29f/acf8f/addingBlueprintScreen1.png" alt="Add blueprint screen - summary" title="Add blueprint screen - summary" width="880" height="511"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Add blueprint screen - summary&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click next and enter the name of the repo(s) associated with your project&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="///static/1c70ca6cd571d66801fe415f484307d2/9e9da/addingBlueprintScreen2.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6cT9Os7p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/1c70ca6cd571d66801fe415f484307d2/acf8f/addingBlueprintScreen2.png" alt="Add blueprint screen - repository" title="Add blueprint screen - repository" width="880" height="479"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Add blueprint screen - repository&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click next and on the summary screen click on the “add transformation” link. You have to tell Dev Lake how to identify deploys and incidents for your setup.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="///static/6d73ee73b695c7a3efc04374bcdf1a93/5c4aa/AddingBlueprintScreen3.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--B2CPqrtJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/6d73ee73b695c7a3efc04374bcdf1a93/acf8f/AddingBlueprintScreen3.png" alt="Add blueprint screen - optional transformation" title="Add blueprint screen - optional transformation" width="880" height="452"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Add blueprint screen - optional transformation&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;On the add transformation screen you have to configure the tag you use on a Github issue that identifies an incident and the Github actions &lt;code&gt;Job&lt;/code&gt; that identifies a deploy.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="///static/e16997929c85b3b07dc8315fec72d236/f349c/addingTransformation.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2QQmZzRi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/e16997929c85b3b07dc8315fec72d236/acf8f/addingTransformation.png" alt="Add blueprint screen - adding transformation" title="Add blueprint screen - adding transformation" width="880" height="711"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Add blueprint screen - adding transformation&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For the deployment section make sure that you’re getting the Job name and not the workflow name.&lt;/p&gt;

&lt;p&gt;To get the job name on Github, click into an old run of your workflow and you’ll see the job name on the left hand side.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/ebd095240afa06471df5bb60cd0a0036/f36fd/githubJob.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--geVRTGvj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/ebd095240afa06471df5bb60cd0a0036/acf8f/githubJob.png" alt="Github actions settings for transformation" title="Github actions settings for transformation" width="880" height="400"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Github actions settings for transformation&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on “save and run now” and you should be done configuring Dev Lake. Wait for the blueprint data collection run to complete.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can open the Blueprints page anytime and click on the graph “squiggle” icon to open the blueprint overview for your project.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/34ac926bdceb095da93cd4050735a363/99e81/projectBlueprint.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m1PkbAsc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/34ac926bdceb095da93cd4050735a363/acf8f/projectBlueprint.png" alt="Eslint for NestJs Blueprint overview" title="Eslint for NestJs Blueprint overview" width="880" height="663"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Eslint for NestJs Blueprint overview&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The status overview allows you to see the status of the data pipeline for your project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring Sync Settings for Local
&lt;/h2&gt;

&lt;p&gt;Because this is a local instance you can go to settings and change the sync frequency to “Manual”.&lt;/p&gt;

&lt;p&gt;This isn’t required but makes sense for local instances.&lt;/p&gt;

&lt;p&gt;Then you can use the blueprint status page and the “Run Now” button to manually get the latest data when you need to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Viewing the DORA metrics
&lt;/h2&gt;

&lt;p&gt;Ok so now that you have all the data downloaded, filtered and transformed into the DORA metrics you can view them in Grafana.&lt;/p&gt;

&lt;p&gt;Click on the “Dashboard” menu button to open Grafana.&lt;/p&gt;

&lt;p&gt;Then click on the magnifying glass icon in the left-hand menu to open the search bar.&lt;/p&gt;

&lt;p&gt;Search for “DORA” and you should see a dashboard called “DORA”.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/41afddff21de43306c633e79cbd2234d/669cd/doraDisplay.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WQwajib6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/41afddff21de43306c633e79cbd2234d/acf8f/doraDisplay.png" alt="DORA dashboard on Grafana" title="DORA dashboard on Grafana" width="880" height="639"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;DORA dashboard on Grafana&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The image is the metrics for my small open source project. I update it once a month maybe. The users do report issues occasionally so I guess now I’ll have to fix them in a bit quicker!&lt;/p&gt;

&lt;p&gt;You can filter to a specific Github Repo if you added more than one.&lt;/p&gt;

&lt;p&gt;Along with DORA there are 10-20 other pre-built reports you can access. Here’s one for Github stats.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/34fdcf68ee788982943d6d3a9e395181/669cd/githubReporting.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k13Rs6Rb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/34fdcf68ee788982943d6d3a9e395181/acf8f/githubReporting.png" alt="Github engineering report on Grafana" title="Github engineering report on Grafana" width="880" height="1724"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Github engineering report on Grafana&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  SaaS Platforms for DORA Metrics
&lt;/h2&gt;

&lt;p&gt;It’s important to mention that there are some excellent platforms for tracking DORA and more metrics if you want more mature, managed applications with support.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multitudes
&lt;/h3&gt;

&lt;p&gt;Multitudes tracks DORA but supplements engineering metrics with around well-being and collaboration.&lt;/p&gt;

&lt;p&gt;More details on the &lt;a href="https://www.multitudes.co/our-product"&gt;multitudes site&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/8ed22e3ce7403d61fed2b41a7167a0ad/84bf8/multitudes.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YhJ54c0S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/8ed22e3ce7403d61fed2b41a7167a0ad/84bf8/multitudes.png" alt="Multitudes marketing page" title="Multitudes marketing page" width="880" height="516"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Multitudes marketing page&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Linear B
&lt;/h3&gt;

&lt;p&gt;LinearB tracks DORA metrics and helps you fix workflow issues.&lt;/p&gt;

&lt;p&gt;More details on the &lt;a href="https://linearb.io/dora-metrics/"&gt;LinearB site&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/4921441c6aa4e2338591d96b91290115/6acbf/linearb.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--36q47dzX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/4921441c6aa4e2338591d96b91290115/6acbf/linearb.png" alt="LinearB marketing page" title="LinearB marketing page" width="880" height="430"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;LinearB marketing page&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Developments in Engineering and DevOps Metrics since DORA 2020
&lt;/h2&gt;

&lt;p&gt;There have been developments since these metrics were first published in 2020 and it’s a rapidly changing, exciting space.&lt;/p&gt;

&lt;p&gt;The DevOps report of 2022 changed the categorisation to “Low, Medium, High”, removing the “elite” name.&lt;/p&gt;

&lt;p&gt;They also added a 5th dimension to DORA for reliability.&lt;/p&gt;

&lt;p&gt;Microsoft released their SPACE metrics paper in 2021 and arguably they are much more human focused which is great.&lt;/p&gt;

&lt;p&gt;It’s worth reading the microsoft docs for SPACE and the Future of Work Report - &lt;a href="https://www.microsoft.com/en-us/research/publication/the-space-of-developer-productivity-theres-more-to-it-than-you-think/"&gt;https://www.microsoft.com/en-us/research/publication/the-space-of-developer-productivity-theres-more-to-it-than-you-think/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;It’s easier than ever to track your DORA metrics. You can use a local instance of Apache Dev Lake or a SaaS platform like Multitudes or LinearB.&lt;/p&gt;

&lt;p&gt;If you’re interested in tracking DORA metrics in your organisation, I’d recommend starting with a local instance of Dev Lake. It’s free and you can get started in minutes.&lt;/p&gt;

&lt;p&gt;Like anything, don’t over do it with these metrics. They’re just a tool to help you improve your engineering practices. They shouldn’t be used as a KPI.&lt;/p&gt;

</description>
      <category>github</category>
      <category>development</category>
      <category>productivity</category>
    </item>
    <item>
      <title>A summary of NDC Sydney 2022 Developer Conference</title>
      <dc:creator>Darragh O'Riordan</dc:creator>
      <pubDate>Sat, 15 Oct 2022 17:12:33 +0000</pubDate>
      <link>https://forem.com/darraghor/a-summary-of-ndc-sydney-2022-developer-conference-1c1i</link>
      <guid>https://forem.com/darraghor/a-summary-of-ndc-sydney-2022-developer-conference-1c1i</guid>
      <description>&lt;p&gt;NDC Sydney is a developer conference in Sydney. This was my first in-person conference in 3 years because of Covid!&lt;/p&gt;

&lt;p&gt;I try to take notes for each of the talks and here they are! I’ll try to add the relevant videos to talks if they’re released.&lt;/p&gt;

&lt;p&gt;Remember that these notes are just my notes. They’re paraphrased and summarised, the speakers would have provided must more clarity and went into more detail!&lt;/p&gt;

&lt;h1&gt;
  
  
  Day 1
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Securing the future
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Laura Bell - CEO of Safestack&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The tools and supply chain that we’re building into all our products are getting very complex. The worst future attacks are likely to be through supply chain rather than direct.&lt;/li&gt;
&lt;li&gt;OWASP hasn’t changed too much since 2001 considering how our tooling and processes have changed&lt;/li&gt;
&lt;li&gt;Mstic - Data relations and connection analysis software - &lt;a href="https://github.com/microsoft/mstic"&gt;https://github.com/microsoft/mstic&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://Safestack.io/free-plan"&gt;https://Safestack.io/free-plan&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Mayday - Lessons from aviation disasters
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Adele Carpenter - Engineer at Trifork Amsterdam&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Aviation can’t afford mistakes especially repeated mistakes&lt;/li&gt;
&lt;li&gt;The root cause analysis they do is insane&lt;/li&gt;
&lt;li&gt;Open communication for the entire software development process is critical&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Building inclusion into orgs
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Sammy Herbert - Head of Dev Exp at SixPivot&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I didn’t take too many notes here because I was listening! Possibly the best talk of the first day!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AFR “best places to work” is the best award to do if you’re going to do one&lt;/li&gt;
&lt;li&gt;18% of Australian sw engineers are women&lt;/li&gt;
&lt;li&gt;50% of women leave engineering by 35&lt;/li&gt;
&lt;li&gt;Consider having readmes for people, list the important questions first!&lt;/li&gt;
&lt;li&gt;A big issue is glue work, Management (and teams)need to value glue work&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Day 2
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Rocking as an azure architect
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Adam Cogan - Chief Architect SSW&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Heavy use of diagrams - use the architecture center as a starting point - &lt;a href="https://learn.microsoft.com/en-us/azure/architecture/"&gt;https://learn.microsoft.com/en-us/azure/architecture/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;There is a developer cli for setting up local env. Use it to save time.&lt;/li&gt;
&lt;li&gt;Use IaC - bicep - there’s an extension for vs code and visual studio now.&lt;/li&gt;
&lt;li&gt;Be concious of cloud cost from the start - watch out for the login on the pricing calculator on azure, it’s the weird link in the middle of the page&lt;/li&gt;
&lt;li&gt;Prepaying for a year saves 30%, 2 years could save 50%&lt;/li&gt;
&lt;li&gt;Setup cost analysis and alerts - billing menu &amp;gt; cost analysis - avoid “denial of wallet” attack. You must create budgets.&lt;/li&gt;
&lt;li&gt;Review the client’s existing on prem cost using the TCO calculator - &lt;a href="https://azure.microsoft.com/en-au/pricing/tco/calculator/"&gt;https://azure.microsoft.com/en-au/pricing/tco/calculator/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Learning for anyone - Good pdf here: &lt;a href="https://azure.microsoft.com/en-us/resources/whitepapers/developer-guide-to-azure/"&gt;https://azure.microsoft.com/en-us/resources/whitepapers/developer-guide-to-azure/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Must have someone actually responsible for the cloud architecture (not all the team, have someone accountable)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Delivering the worlds first flying car race
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Dom Raniszewski - Principal Consultant at Telstra Purple&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lesson 1 - you can’t force your excitement on others&lt;/li&gt;
&lt;li&gt;Lesson 2 - you should give people space to deal with uncertainty&lt;/li&gt;
&lt;li&gt;Fixed and growth mindsets - &lt;a href="https://fs.blog/carol-dweck-mindset/"&gt;https://fs.blog/carol-dweck-mindset/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;To deal with a very uncertain project you should at least instil and encourage these three traits in the team - courage, commitment and passion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Overdue: When Agile and DevOps meet real world deadlines
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Brenton Race - CTO at Blueshift&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Steve Mconnell - more effective agile book - &lt;a href="https://www.amazon.com/More-Effective-Agile-Roadmap-Software/dp/1733518215"&gt;https://www.amazon.com/More-Effective-Agile-Roadmap-Software/dp/1733518215&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;There are agile boundaries in your org - Agile likely stops at product development teams. VPs etc are not working in agile.&lt;/li&gt;
&lt;li&gt;“understand the context that you’re in, and only apply principals that make sense in that context”&lt;/li&gt;
&lt;li&gt;Structure team(s) to maximise focus - reduce communication overhead - isolate issue types e.g. you can create shield teams (support team that protect the teams doing feature work) - Dar note: Seen this before, can lead to resentment&lt;/li&gt;
&lt;li&gt;Expect change, use it to readjust to maximise business value.&lt;/li&gt;
&lt;li&gt;Track and communicate progress to trigger change conversations as early as possible&lt;/li&gt;
&lt;li&gt;Clarify what you’re being asked for - is the question “can you estimate this please?” or is it “can you come up with a plan to deliver an outcome by a certain date?”&lt;/li&gt;
&lt;li&gt;“set aside time for team and &lt;strong&gt;INDIVIDUALS&lt;/strong&gt; to improve”&lt;/li&gt;
&lt;li&gt;software estimation book - steve mconnell - &lt;a href="https://www.amazon.com.au/Software-Estimation-Demystifying-Developer-Practices-ebook/dp/B00JDMPOVQ"&gt;https://www.amazon.com.au/Software-Estimation-Demystifying-Developer-Practices-ebook/dp/B00JDMPOVQ&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://brentonrace.com"&gt;https://brentonrace.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next decade of software
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Richard Campbell - Consultant, podcaster, author&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We’re literally running out of physical atoms (physical space) to improve processing power , so we’re going to have to get more efficient.&lt;/li&gt;
&lt;li&gt;It hasn’t been an issue for developers for a while, but with the way the economy is going developers should make sure that they understand how their company makes money, and if the role they are doing is helping that. If you’re on a project that doesn’t affect revenue, change your job or your company.&lt;/li&gt;
&lt;li&gt;You can probably bet on the browser lasting as a secure runtime environment. Most people trust it, the tools are getting better. WASM opens the browser as a runtime to all languages.&lt;/li&gt;
&lt;li&gt;Rapid application development tools (Nocode and low-code) will enable subject matter experts to create software. This is a fantastic outcome. We will have to write software to allow these types of apps to integrate with our and other apps.&lt;/li&gt;
&lt;li&gt;AI will become more ubiquitous and will enhance our tooling. It probably wont replace us, but will allow us to provide more value to customers&lt;/li&gt;
&lt;li&gt;The always with you compute device (phone) has clearly won, we are cyborgs but it’s an external augmentation. it’s likely that AR will enhance or replace this. There are devices out there but they’re not in consumer space yet. Hololens costs USD $3500 and around $1000 / month in IoT costs. If you compare this to blackberry’s in 1999 it’s a similar trajectory where blackberrys and their portable email were “industrial” or business-only devices, not consumer.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Visible Developer: Why You Shouldn’t Blend In
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Heather Downing - Principal Dev Advocate at Okta&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Figure out what you want from the career&lt;/li&gt;
&lt;li&gt;Do things and tell people - help people outside of your team or job - write the things you do down somewhere so that you remember at the performance review type thing you do in your company.&lt;/li&gt;
&lt;li&gt;Working hard and doing a good a job in your current job is &lt;strong&gt;insufficient&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Externalise you thought leadership - You should share your knowledge outside of you organisation. Leading by example externally demonstrates your ability (internally also!).&lt;/li&gt;
&lt;li&gt;You should network! - Give first to as many people as you can. We’re a big-small industry.&lt;/li&gt;
&lt;li&gt;Network in person, “your network is your net-worth”.&lt;/li&gt;
&lt;li&gt;Be your honest self on social media - it’s ok to share some frustrations and things - it makes you relatable. “Post with purpose” - before posting social media think “what purpose does this provide” - “what will this do for my personal brand” e.g. if you’re super interested in a topic you should post about it!&lt;/li&gt;
&lt;li&gt;Speak like a human :D - e.g. don’t always shit-post - “is this a better topic for your therapist or the internet?”&lt;/li&gt;
&lt;li&gt;Be moderately responsive - to grow a personal brand you do need to respond occasionally&lt;/li&gt;
&lt;li&gt;If you’re not online, you’re out of sight&lt;/li&gt;
&lt;li&gt;Visibility is good but consistency is great - post regularly, schedule posts if you can&lt;/li&gt;
&lt;li&gt;make sure you have a dedicated website dedicated to your works - if you don’t like designing hire someone who can! Wix or squarespace is better because you can just pay and let a good designer work directly on it.&lt;/li&gt;
&lt;li&gt;Attitude matters - skills can be taught but attitude can’t. Life isn’t “fair”. Some things are out of your control.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Habits of well known developers …
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;They pay attention to what others value, identify the goals of the people around you in the organisation. Show interest, learn about that, go above your paygrade, ask questions above your paygrade, how does your company make money! Take action on an important thing without being asked first.&lt;/li&gt;
&lt;li&gt;Choose the right place for YOUR values. “If your friend / child / anyone you care about were looking for a job, would you recommend where you currently work to them?”. if not, why are you there?&lt;/li&gt;
&lt;li&gt;Well known devs focus on the big picture - is what you’re working on important?&lt;/li&gt;
&lt;li&gt;They are comfortable being uncomfortable - you have to be able to work on things that are new to you or stretching your capabilities. if you’re not deliberately stretching your capabilities you might get stuck in a rut.&lt;/li&gt;
&lt;li&gt;They like their colleagues. Even the jerks in the company. They go out of their way to put the human first, if you don’t like someone build trust anyway. Everyone needs to feel valued, even the jerks. “Empathy is being concerned about the human, not just their output”. Figure out why the jerk says what they say and does what they do&lt;/li&gt;
&lt;li&gt;The know how to become a trusted voice? Questions and listen. speak little, listen much and apply what you hear. Be positive and respectful.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Day 3
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Why the web is this way
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Steve Sanderson - Chief Architect at Microsoft&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This was a great history of web dev langs, browsers, frameworks. I didn’t take notes because I’m old enough to have used everything he showed 👴. It was cool that he actually ran all the old bits of software!&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Credibility with crowd natives
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Shane Baldacchino - Chief Architect at Microsoft&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What are cloud native orgs? - they move fast, they fail fast, they know their stuff (hence the high salaries)&lt;/li&gt;
&lt;li&gt;They use data and output to feed innovation - Challenger Vs Champion for models - &lt;a href="https://www.ibm.com/docs/es/sc-and-ds/8.0.0?topic=steps-champion-challenger-overview"&gt;https://www.ibm.com/docs/es/sc-and-ds/8.0.0?topic=steps-champion-challenger-overview&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Architectures are always changing - At the moment the landscape is changing faster than it ever has before. Being able to iterate and change is the only way to stay competitive.&lt;/li&gt;
&lt;li&gt;(&lt;a href="https://survey.stackoverflow.co/2022/"&gt;Stack overflow dev survey 2022&lt;/a&gt;) 62% of developers are in the industry 9 years or less. They don’t care about VMs. They don’t care about DNS, memory etc. They just want an endpoint and they want the cloud to handle the rest.&lt;/li&gt;
&lt;li&gt;Everything is becoming full stack&lt;/li&gt;
&lt;li&gt;Open source is the present and the future.&lt;/li&gt;
&lt;li&gt;Architectures you have to be across: search indexes (lucerne), event streaming, event driven architecture&lt;/li&gt;
&lt;li&gt;Use a workflow management platform for stateful micro services - e.g. apache airflow&lt;/li&gt;
&lt;li&gt;Machine learning - Know it, or at least understand the difference between training and inference&lt;/li&gt;
&lt;li&gt;L7 routing is the new norm - short lived compute, ephemeral ports, we can’t have traditional networking. envoy, istio, sidecars, service discovery.&lt;/li&gt;
&lt;li&gt;“Culture eats tech choices for breakfast” - conway’s law, crazy company = crazy code. centralized command and control wont work any more - centralised operations is not conducive to speed. centralised operations increases business risk. centralised operations encourages a hero mentality.&lt;/li&gt;
&lt;li&gt;if you have complex systems things will fail and they will fail differently all the time. Decentralise the operations of technology and bring it closer to the creators (and make them accountable for running their environment, and the cost of their environment)&lt;/li&gt;
&lt;li&gt;no change freezes - it’s a sign of immaturity, deploying more frequently reduces risk. Your best competitors do not stop releasing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to say no (and still influence people)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Sasha Hilton - National Head of Digital at Telstra Purple&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The context - you get an unwelcome request&lt;/p&gt;

&lt;p&gt;Common concerns about saying no&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;people think I’m lazy, people think I’m incompetent, someone gets upset, the requester has more power than me, could i get fired?&lt;/li&gt;
&lt;li&gt;conflict avoidance&lt;/li&gt;
&lt;li&gt;perfectionism&lt;/li&gt;
&lt;li&gt;power imbalances - there are real and imagined power imbalances, but if you feel like there is one, it will affect you anyway.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Benefits of saying no&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;your time is precious, learning is a gift you can give to other people (by not doing the work yourself), you’re reducing the risk of burnout&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How to get better&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read Never Split the Difference - Chris Voss for expert tactics - &lt;a href="https://www.amazon.com/Never-Split-Difference-Negotiating-Depended/dp/0062407805"&gt;https://www.amazon.com/Never-Split-Difference-Negotiating-Depended/dp/0062407805&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Build trust, ask questions, build rapport, negotiate.&lt;/li&gt;
&lt;li&gt;be pre-emptive about saying no e.g. use a contract.&lt;/li&gt;
&lt;li&gt;set boundaries - have a mantra (“turning the ring” ) , keep a resentment channel for yourself - write down what makes you resentful.&lt;/li&gt;
&lt;li&gt;practice saying no, get used to saying it.&lt;/li&gt;
&lt;li&gt;Fawning - fight, flight, freeze and … fawn - the please and appease response&lt;/li&gt;
&lt;li&gt;Ways to create calm - (mindfulness helps) notice the feeling, label it&lt;/li&gt;
&lt;li&gt;engage your physical sense “3 things you can see, 2 things you can touch, 1 thing you can smell”&lt;/li&gt;
&lt;li&gt;engage you pre-frontal cortex, count backwards from 1013 * 12 or something that makes you think&lt;/li&gt;
&lt;li&gt;engage your motor cortex - wiggle your toes, write some thing down&lt;/li&gt;
&lt;li&gt;parents have some creative ways of saying no - park things in the future, blame it on a higher power, empathise but ignore request&lt;/li&gt;
&lt;li&gt;“that’s just not possible”&lt;/li&gt;
&lt;li&gt;“let me know if i should reshuffle priorities”&lt;/li&gt;
&lt;li&gt;“i’m not comfortable with that sorry”&lt;/li&gt;
&lt;li&gt;“as a rule, i don’t…”&lt;/li&gt;
&lt;li&gt;“sorry ths isn’t the answer you’re looking for”&lt;/li&gt;
&lt;li&gt;“i can’t right now but i could next month/after the project”&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Empowering Minds of All Kinds: Neurodiversity in Tech
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Ryan Oakly&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The proposed stats around things like autism and aspergers should be thought of as a minimum because there is so much under-diagnosis, especially among older folks that wouldn’t even think about different being a condition (or don’ ask, don’t tell)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ally is a verb not a noun&lt;/li&gt;
&lt;li&gt;some people will never want to tell you their condition, people are are afraid of judgement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Environment&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have areas of low light&lt;/li&gt;
&lt;li&gt;Have areas in co-working spaces that are slightly separated or blocked off&lt;/li&gt;
&lt;li&gt;have quieter spaces&lt;/li&gt;
&lt;li&gt;allow use of headphones or ear muffs&lt;/li&gt;
&lt;li&gt;be flexible around preferred work areas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Job design&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;give the option to provide written or verbal instructions&lt;/li&gt;
&lt;li&gt;give detailed instructions if desired&lt;/li&gt;
&lt;li&gt;adjust job design to remove tasks people struggle with&lt;/li&gt;
&lt;li&gt;adjust recruitment e.g. “do you want me to leave you alone for 5 mins so you can do this task”&lt;/li&gt;
&lt;li&gt;have policies and discussions&lt;/li&gt;
&lt;li&gt;microsoft had specific recruitment strategy since 2016&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;you can’t decide who innovation comes from&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Art of Effective Feedback
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Marcus Bristol - Engineering Manager at PushPay&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Book - “Thanks for the feedback” - Douglas Stone and Sheila Heen - &lt;a href="https://www.amazon.com/Thanks-Feedback-Science-Receiving-Well/dp/0143127136"&gt;https://www.amazon.com/Thanks-Feedback-Science-Receiving-Well/dp/0143127136&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;feedback is a core ingredient for satisfaction in life&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Three common types&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tell and sell - “sports and coaching”&lt;/li&gt;
&lt;li&gt;Tell and listen - “this is what i’ve been seeing, why it’s not , tell me about it”&lt;/li&gt;
&lt;li&gt;Problem solving - “coaching, steer the convo but let the person figure it out”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Giving feedback&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have to care, and show it!&lt;/li&gt;
&lt;li&gt;Evaluate behaviours, not the person&lt;/li&gt;
&lt;li&gt;Everyone is different so adjust feedback and mechanism to the person&lt;/li&gt;
&lt;li&gt;Understand motivations&lt;/li&gt;
&lt;li&gt;Listen and empathise&lt;/li&gt;
&lt;li&gt;Give positive feedback / positive reinforcement regularly too - feedback isn’t just for bad behaviour&lt;/li&gt;
&lt;li&gt;Provide examples of good behaviour&lt;/li&gt;
&lt;li&gt;toastmasters is a great place to learn feedback&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;It was great being at a conference again. A nice mix of talks, and a good mix of people. I’m looking forward to next year already.&lt;/p&gt;

</description>
      <category>engineering</category>
      <category>career</category>
    </item>
    <item>
      <title>4 Surprising uses for GitHub as a cloud datastore</title>
      <dc:creator>Darragh O'Riordan</dc:creator>
      <pubDate>Fri, 14 Oct 2022 17:12:33 +0000</pubDate>
      <link>https://forem.com/darraghor/4-surprising-uses-for-github-as-a-cloud-datastore-43op</link>
      <guid>https://forem.com/darraghor/4-surprising-uses-for-github-as-a-cloud-datastore-43op</guid>
      <description>&lt;p&gt;GitHub is a great place to store code. But it’s also a great place to store any data that you need backed up, versioned, accessible from anywhere and easy to use.&lt;/p&gt;

&lt;p&gt;In this post I’ll show you some surprising uses for GitHub as a datastore like…&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;as a comments system for a website&lt;/li&gt;
&lt;li&gt;as an operating system configuration management tool&lt;/li&gt;
&lt;li&gt;as a datastore for personal data management&lt;/li&gt;
&lt;li&gt;as a software update delivery platform
&amp;lt;!-- end excerpt --&amp;gt;
## Is it a good idea to use GitHub as a datastore?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tools I’m going to show you are all free and are built in a way to use Guthub as their primary datastore.&lt;/p&gt;

&lt;p&gt;This is a “parasitic” use of Github and the usage could be banned in the future but they seem to be supportive of these kinds of uses as long as you don’t abuse them too much.&lt;/p&gt;

&lt;p&gt;There are examples of accounts that have heavily used the github apis but because they provide utility to the tech community they are generally left alone.&lt;/p&gt;

&lt;p&gt;Check out this post about how Github worked with CocoaPods when they were maxing out 5 CPUs on a server and using terabytes of Github’s data: &lt;a href="https://github.com/CocoaPods/CocoaPods/issues/4989#issuecomment-193772935"&gt;https://github.com/CocoaPods/CocoaPods/issues/4989#issuecomment-193772935&lt;/a&gt; - the Github team went out of their way to help them!&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations of GitHub as a datastore
&lt;/h2&gt;

&lt;p&gt;One of the only limitations in using GitHub as a datastore is the file size limitation. The maximum file size is 100MB. This is fine for most use cases. But if you need to store large files you can use a service like S3 or Azure Blob Storage.&lt;/p&gt;

&lt;p&gt;The seconds major limitation is the api request limit. On Github you’re limited to 1,000 requests per hour per repository. Personal tools are unlikely to hit this limit. But if you’re building a tool that is used by thousands of people Github may not be suitable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Github as a comments system for a website
&lt;/h2&gt;

&lt;p&gt;This blog uses utterences as a comments system. You can see it if you scroll right down to the bottom of the page (I prefer if people use twitter so I hide it).&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/b076001dce9c85a381b625d3daa49cbb/ec3e2/comments-on-blog.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ywGmbmvH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/b076001dce9c85a381b625d3daa49cbb/ec3e2/comments-on-blog.png" alt="Showing comments on my blog" title="Showing comments on my blog" width="880" height="281"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Showing comments on my blog&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://utteranc.es/"&gt;Utterences&lt;/a&gt; is a free and open source comments system that uses GitHub issues as its backend. That’s right, Github issues! Quite clever use of that api and you don’t have to pay for a service like Disqus.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/ce31d7e06b27e73c6490267884b86601/d2c2a/comments-on-github.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n9UAsDkA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/ce31d7e06b27e73c6490267884b86601/d2c2a/comments-on-github.png" alt="Showing comments on github" title="Showing comments on github" width="880" height="458"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Showing comments on github&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can see exactly how I set utterences and github up on my Gatsby blog in this post: &lt;a href="https://www.darraghoriordan.com/2022/10/01/how-to-add-utterances-comments-gatsby-typescript/"&gt;How to add utterences to your gatsby blog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the time since utterences was built, Github added a community product called “Discussions” with a full api. So someone took utterences and made it work on the discussions API.&lt;/p&gt;

&lt;p&gt;This is probably a much better api to use for comments on a blog. So if you’re thinking about using Github as a blog comments system today, use Giscus.&lt;/p&gt;

&lt;p&gt;Get Giscus &lt;a href="https://github.com/giscus/giscus"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Github as an operating system configuration management tool
&lt;/h2&gt;

&lt;p&gt;If you’re a mac or linux user you have “dotfiles” that are used to configure how your operating system works for you. The dotfiles live in your home directory and they’re called dotfiles because they usually start with a &lt;code&gt;.&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A file or folder that starts with a &lt;code&gt;.&lt;/code&gt; is usually hidden by default in apps like Finder on Mac or &lt;code&gt;ls&lt;/code&gt; in a terminal. This is handy because you don’t want to see all the files that are used to configure your operating system. They don’t change very often and would clutter up your view.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;Command + Shift + .&lt;/code&gt; to show hidden files in Finder on Mac or &lt;code&gt;ls -a&lt;/code&gt; on cli.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/e463939c623492e013d96e9108d2314e/bde6a/dotfiles-cli.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MjR6Xtt_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/e463939c623492e013d96e9108d2314e/bde6a/dotfiles-cli.png" alt="Showing hidden files" title="Showing hidden files" width="283" height="436"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Showing hidden files&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now if all of your configuration for how the computer works, looks and feels to use are stored in files - what happens if you need to reinstall your operating system? Or if you have 2 computers and you want to sync your configuration between them? You’ll lose all your configuration and have to start from scratch. &lt;/p&gt;

&lt;p&gt;This is where dotfiles come in. You can store your configuration in a git repository and then you can sync it between computers. You can also use it to backup your configuration and restore it if you need to reinstall your operating system.&lt;/p&gt;

&lt;p&gt;Homeshick is a tool that uses Github as a datastore for your dotfiles. You can make a change to dotfiles on one system, commit it to Github and then homeshick will periodically check for changes and sync them to your other systems.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/26028c10dd2d3483278e357aabf66368/fa5c1/homeshick.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mDXq07bT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/26028c10dd2d3483278e357aabf66368/fa5c1/homeshick.png" alt="Showing homeshick in action" title="Showing homeshick in action" width="422" height="99"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Showing homeshick in action&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can see how I configure homeshick and github in this post: &lt;a href="https://www.darraghoriordan.com/2022/01/28/developer-shell-modern-bat-fzf-antigen-zsh-wsl-mac/"&gt;Consistent modern shell tooling on MacOS and Windows WSL&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Github as a datastore for personal data management
&lt;/h2&gt;

&lt;p&gt;If you’re a developer and you take notes you might be interested in Obsidian - It’s like OneNote or Evernote but it’s based on markdown files. Obsidian has really nice tagging and linking between notes built in.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/3e2378304315896e5dbdd0f4f57eca11/0012b/obsidian-md.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8sEHI8nE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/3e2378304315896e5dbdd0f4f57eca11/0012b/obsidian-md.png" alt="Showing obsidian" title="Showing obsidian" width="661" height="421"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Showing obsidian&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Obsidian has a healthy plugin ecosystem and heaps of functionality like automatic importing of kindle notes, a trello board replacement, daily journaling helpers and more.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/ee79983428b4e7604efd8ec18e45114f/58bb7/obsidian-trello.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EA-keko8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/ee79983428b4e7604efd8ec18e45114f/58bb7/obsidian-trello.png" alt="Showing obsidian" title="Showing obsidian" width="880" height="256"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Showing obsidian&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Obsidian is available for Mac, iOS, Windows and Android. To keep your markdown notes synchronised across all of your devices you need a service. Obsidian themselves provide a paid synch-service but there is an Obsidian Git plugin that uses Github as a datastore!&lt;/p&gt;

&lt;p&gt;You can read more details about how I configure and use Obsidian on Github in this post: &lt;a href="https://www.darraghoriordan.com/2022/10/13/how-to-use-obsidian-as-personal-database-mobile-pc/"&gt;Obsidian - A free personal data management system on windows, mac and mobile&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Github as a software update delivery platform
&lt;/h2&gt;

&lt;p&gt;I’ve recently created a tool called Local Dev Tools. The application is a toolkit for developers so they don’t have to paste things like JWTs or JSON into random websites to convert or decode them.&lt;/p&gt;

&lt;p&gt;Local Dev Tools my first native application in maybe 10 years and expectations for applications have changed quite a bit since the last time I compiled a native app.&lt;/p&gt;

&lt;p&gt;Customers expect automatic updates to be applied when they run the app. The infrastructure required to host and deliver updates is a lot of work!&lt;/p&gt;

&lt;p&gt;Update functionality is provided by the app stores for android, windows and mac but if you’re in the stores then you’re limited in monetization strategies, you have to adhere to their policies and have to use their store fronts.&lt;/p&gt;

&lt;p&gt;While I’m also deploying the apps to the various app stores, I decided to use Github as a datastore for my updates for anyone who purchases the application from &lt;a href="https://devshell.darraghoriordan.com/l/localtools"&gt;Gumroad&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you use Github actions you can create a workflow that will build your application and upload it to Github as a release. Then you can use the Github API to check for new releases and download them.&lt;/p&gt;

&lt;p&gt;Nuts is a simple tool that monitors a Github release stream for updates and turns it into an RSS file that’s compatible with electron updater (and &lt;code&gt;squirrel&lt;/code&gt; delivery).&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/eb16095102540f7b5bb5c662be573d4a/cecac/nuts.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MzcM4gVc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/eb16095102540f7b5bb5c662be573d4a/cecac/nuts.png" alt="Showing nuts in action" title="Showing nuts in action" width="728" height="280"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Showing nuts in action&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So once you have semantic releases publishing your assets on Github you can run a nuts server and use it to deliver updates to your application.&lt;/p&gt;

&lt;p&gt;You can get nuts at &lt;a href="https://github.com/GitbookIO/nuts"&gt;https://github.com/GitbookIO/nuts&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Not surprising but worth mentioning - Github as a host for your personal website
&lt;/h2&gt;

&lt;p&gt;Just in case you haven’t heard of it. Github pages is a product provided by Github that allows you to host a static website for free as an alternative to Netlify. You can use it to host a blog, a portfolio or a landing page for your business.&lt;/p&gt;

&lt;p&gt;The benefits are a very close integration with Github and the ability to use Github actions to build and deploy your website.&lt;/p&gt;

&lt;p&gt;Check out their product page here: &lt;a href="https://pages.github.com/"&gt;https://pages.github.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can deploy Gatsby sites to github pages&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.gatsbyjs.com/docs/how-to/previews-deploys-hosting/how-gatsby-works-with-github-pages/"&gt;https://www.gatsbyjs.com/docs/how-to/previews-deploys-hosting/how-gatsby-works-with-github-pages/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>github</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Obsidian - A free personal data management system on windows, mac and mobile</title>
      <dc:creator>Darragh O'Riordan</dc:creator>
      <pubDate>Thu, 13 Oct 2022 17:12:33 +0000</pubDate>
      <link>https://forem.com/darraghor/obsidian-a-free-personal-data-management-system-on-windows-mac-and-mobile-2cek</link>
      <guid>https://forem.com/darraghor/obsidian-a-free-personal-data-management-system-on-windows-mac-and-mobile-2cek</guid>
      <description>&lt;p&gt;Obsidian is a free personal data management system based on markdown notes. Because it’s based on markdown it’s perfect for developers or other power users.&lt;/p&gt;

&lt;p&gt;Obsidian has a healthy plugin ecosystem for extending the base functionality. There are plugins for mind mapping, kanban boards, importing amazon kindle highlights, daily notes and using git as a backend.&lt;/p&gt;

&lt;p&gt;Obsidian has a powerful search engine that allows you to search for notes by their content, tags, and even the content of the notes they link to. This linking technology is what makes Obsidian so powerful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing obsidian
&lt;/h2&gt;

&lt;p&gt;You can grab obsidian mobile versions from the relevant app store for your device.&lt;/p&gt;

&lt;p&gt;On PC or Mac you can download the desktop version from the &lt;a href="https://obsidian.md/download"&gt;obsidian website&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up a GitHub Repo Backend
&lt;/h2&gt;

&lt;p&gt;The first thing you should do is set up a GitHub repo as a backend for your notes. This will allow you to sync your notes across devices and also back them up to the cloud (Github).&lt;/p&gt;

&lt;p&gt;Create a github repo on github itself. You will likely want this to be private. You can do this by clicking the &lt;code&gt;+&lt;/code&gt; button in the top right of the github website and selecting &lt;code&gt;New repository&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Obsidian desktop git plugin can use all the features of a GitHub client. But the mobile version is limited to http only. So you will need to create a personal access token to use with the mobile version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a personal access token
&lt;/h2&gt;

&lt;p&gt;To create a personal access token go to your github settings and select &lt;code&gt;Developer settings&lt;/code&gt; from the left hand menu. Then select &lt;code&gt;Personal access tokens&lt;/code&gt; from the left hand menu. Then click &lt;code&gt;Generate new token&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Copy the token and save it somewhere safe. You will need it later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding a .gitignore file
&lt;/h2&gt;

&lt;p&gt;Use the following &lt;code&gt;.gitignore&lt;/code&gt; file to ignore the files that obsidian creates. Add this to the repo using your favorite git client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Dependency directories

node_modules/
jspm_packages/

# This file is used to keep track of last auto backup/pull

.obsidian-git-data

# exclude workspace cache

.obsidian/workspace

# plugins - we use different plugins on mobile and desktop so we don't want to sync them

.obsidian/plugins
.obsidian/community-plugins.json

# Exclude "bad" names

null\*

# Add below lines to exclude Mac stuff

.trash/
.DS_Store

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting up the desktop version of Obsidian
&lt;/h2&gt;

&lt;p&gt;Clone the repo you created earlier to your local machine using your usual git tool.&lt;/p&gt;

&lt;p&gt;When you start Obsidian it will prompt you to set up the vault location. Set it to the repo you created.&lt;/p&gt;

&lt;p&gt;Install the git plugin from the community plugins menu and configure it to use &lt;code&gt;.&lt;/code&gt; as the repo location.&lt;/p&gt;

&lt;p&gt;I use the following plugins on desktop&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Advanced Tables&lt;/li&gt;
&lt;li&gt;Calendar&lt;/li&gt;
&lt;li&gt;Kanban&lt;/li&gt;
&lt;li&gt;Git&lt;/li&gt;
&lt;li&gt;Periodic Notes&lt;/li&gt;
&lt;li&gt;Tasks&lt;/li&gt;
&lt;li&gt;Mindmap&lt;/li&gt;
&lt;li&gt;Kindle Highlights&lt;/li&gt;
&lt;li&gt;Diagrams&lt;/li&gt;
&lt;li&gt;Advanced Slides&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting up the mobile version of Obsidian
&lt;/h2&gt;

&lt;p&gt;Remember that it’s better to not check in the plugins folder into obsidian if you’re going to use it on mobile. Some plugins just don’t make any sense on a small screen device.&lt;/p&gt;

&lt;p&gt;To configure Obsidian Git for a mobile device requires a bit of extra work. You need to create a personal access token on GitHub and then use that token to authenticate Obsidian Git.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install obsidian from the app store of your mobile device&lt;/li&gt;
&lt;li&gt;install the Obsidian Git plugin from the community plugin page&lt;/li&gt;
&lt;li&gt;Configure the Obsidian Git plugin - Add your username and Github secret access key&lt;/li&gt;
&lt;li&gt;Set a “Custom Base Path” folder for your git repo - remember this is the folder that will be synced to github&lt;/li&gt;
&lt;li&gt;Now clone the repo - go to the actions menu &amp;gt; choose “clone”&lt;/li&gt;
&lt;li&gt;Set the full GH url for repo (remember it must be the https address)&lt;/li&gt;
&lt;li&gt;Set the sub directory you added as the Custom Base Path&lt;/li&gt;
&lt;li&gt;Now change the settings for Obsidian Git plugin - set auto pull every 5 mins, add the device name (e.g. “Mobile”)to the “Commit message auto message” and “hostname” placeholders, turn ON “pull on startup”, you must add an author name and an author email.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Because my mobile Obsidian is primarily used for reading and taking notes I use the following plugins&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Periodic notes&lt;/li&gt;
&lt;li&gt;calendar&lt;/li&gt;
&lt;li&gt;Ttasks&lt;/li&gt;
&lt;li&gt;Kanban.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;There are often merge conflicts on the mobile version of Obsidian. I’ve often had to delete and rebuild the whole data set because resolving merge conflicts on mobile is a pain. But it’s a small price to pay for the power (and cost) of Obsidian.&lt;/p&gt;

&lt;p&gt;If you really enjoy the tool you can pay Obsidian for a first-class built in synchronization platform that they support and build directly in to the application.&lt;/p&gt;

&lt;p&gt;It’s worth reading the docs for Obsidian to get the most out of it. Especially around linking. And take the time to explore all the commands in the command palette. It’s a powerful tool!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How add comments to your Gatsby site with utterances</title>
      <dc:creator>Darragh O'Riordan</dc:creator>
      <pubDate>Sat, 01 Oct 2022 17:12:33 +0000</pubDate>
      <link>https://forem.com/darraghor/how-add-comments-to-your-gatsby-site-with-utterances-59d</link>
      <guid>https://forem.com/darraghor/how-add-comments-to-your-gatsby-site-with-utterances-59d</guid>
      <description>&lt;p&gt;If you want to add comments to your blog quickly there is a nice tool called utterances that uses a GitHub Repo as the api.&lt;/p&gt;

&lt;p&gt;Here is how I setup utterances for my gatsby blog in typescript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Utterances
&lt;/h2&gt;

&lt;p&gt;Utterances is a javascript tool that uses a GitHub repo as the store and api. This makes it ideal for static sites.&lt;/p&gt;

&lt;p&gt;People will have to log in to their GitHub accounts to post a comment so spam shouldn’t be too much of an issue. This does mean that Utterances is mostly suited to a site with a developer audience.&lt;/p&gt;

&lt;p&gt;Because data is stored in a GitHub repo, you actually own it! This is much nicer than tools like Disqus.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup on Github
&lt;/h2&gt;

&lt;p&gt;You have to have a public GitHub repo. My blog content is private so I set up a specific repo for blog comments.&lt;/p&gt;

&lt;p&gt;The you install the app on that repo by visiting&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/apps/utterances"&gt;https://github.com/apps/utterances&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click “install” and then select your comments GitHub repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding the UI to your site
&lt;/h2&gt;

&lt;p&gt;You have to inject a script where ever you want the control. I created a component for this. You can see how each of the settings work below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useEffect } from 'react'
export const Comments = () =&amp;gt; {
  const commentsInjectionRoot: React.RefObject&amp;lt;HTMLDivElement&amp;gt; =
    React.createRef()

  useEffect(() =&amp;gt; {
    if (commentsInjectionRoot.current?.children.length === 0) {
      const scriptEl = document.createElement('script')
      scriptEl.setAttribute('src', 'https://utteranc.es/client.js')
      scriptEl.setAttribute('crossorigin', 'anonymous')
      scriptEl.setAttribute('async', 'true')
      scriptEl.setAttribute(
        'repo',
        'darraghoriordan/darragh-oriordan-com-comments'
      )
      scriptEl.setAttribute('issue-term', 'pathname')
      scriptEl.setAttribute('theme', 'github-light')
      commentsInjectionRoot.current?.appendChild(scriptEl)
    }
  }, [])

  return (
    &amp;lt;div className="container pt-8"&amp;gt;
      &amp;lt;h1 className="mt-0 mb-0 text-3xl font-normal leading-normal"&amp;gt;
        Comments
      &amp;lt;/h1&amp;gt;
      &amp;lt;hr className="my-0" /&amp;gt;
      &amp;lt;div ref={commentsInjectionRoot} /&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then where ever you want the comments add the component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Comments /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="///static/c4c54fa3bdef72623401eeb674d69e91/7e318/comments.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6W3Yv7rj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/c4c54fa3bdef72623401eeb674d69e91/acf8f/comments.png" alt="comments ui" title="comments ui" width="880" height="374"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;comments ui&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Utterances is an easy way to add comments to a developer site and you own all the data so it’s low risk.&lt;/p&gt;

&lt;p&gt;Give it a try today! - &lt;a href="https://utteranc.es/"&gt;https://utteranc.es/&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to use SSH with Git and ssh-agent on Windows</title>
      <dc:creator>Darragh O'Riordan</dc:creator>
      <pubDate>Sat, 24 Sep 2022 17:12:33 +0000</pubDate>
      <link>https://forem.com/darraghor/how-to-use-ssh-with-git-and-ssh-agent-on-windows-e1f</link>
      <guid>https://forem.com/darraghor/how-to-use-ssh-with-git-and-ssh-agent-on-windows-e1f</guid>
      <description>&lt;p&gt;I needed to run git natively in windows (no wsl) for a recent project. I use ssh certificates with passphrases to authenticate with my git provider.&lt;/p&gt;

&lt;p&gt;Ssh requires the certificate passphrase every time you use a connection. It's annoying typing this passphrase in to terminal when using a &lt;code&gt;git&lt;/code&gt; command.  &lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;On most *nix systems there is an ssh-agent installed that will store your pass phrases so you don't have to enter them when using Git with ssh. &lt;/p&gt;

&lt;p&gt;Ssh-agent is harder to configure on windows because some of the default settings and paths are different to *nix systems.&lt;/p&gt;

&lt;p&gt;I didn’t want to use Git for Windows because it uses GitBash. I couldn’t use WSL because I wanted git to work on any terminal in windows.&lt;/p&gt;

&lt;p&gt;These are the steps I had to research to use Git on Windows with the built in Windows ssh-agent.&lt;/p&gt;

&lt;p&gt;Note: You must be an administrator to perform the required actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open ssl on Windows
&lt;/h2&gt;

&lt;p&gt;If you use Windows 10 or higher there is a built-in openssl instance. You can turn it on in the Optional Features settings pane.&lt;/p&gt;

&lt;p&gt;Microsoft provide more instructions here: &lt;a href="https://learn.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse?tabs=gui"&gt;https://learn.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse?tabs=gui&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Follow the instructions to install it if you don’t have it.&lt;/p&gt;

&lt;h2&gt;
  
  
  A note on certificates
&lt;/h2&gt;

&lt;p&gt;I’ll assume that you have ssh certificates available and any ssh aliases are set in the config file&lt;/p&gt;

&lt;p&gt;The default location for the config file on windows is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$HOME\.ssh\config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should create that file if you need ssh aliases. You can read more about this in my article on ssh for git accounts - &lt;a href="https://www.darraghoriordan.com/2021/05/04/configure-multiple-github-accounts-one-computer/"&gt;https://www.darraghoriordan.com/2021/05/04/configure-multiple-github-accounts-one-computer/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Enabling Ssh agent
&lt;/h2&gt;

&lt;p&gt;Open a powershell terminal as administrator and run the following to have ssh-agent available.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Have ssh agent start automatically
Get-Service ssh-agent | Set-Service -StartupType Automatic

# Start ssh agent now
Start-Service ssh-agent

# Should work successfully
Get-Service ssh-agent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configure git to use Windows ssh
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# tell git to use ssh.exe
git config --global core.sshCommand "'C:\Windows\System32\OpenSSH\ssh.exe'"

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Load keys into ssh agent
&lt;/h2&gt;

&lt;p&gt;Copy your keys into a folder that ssh-agent can access. Anywhere in the &lt;code&gt;$HOME/.ssh&lt;/code&gt; should be ok.&lt;/p&gt;

&lt;p&gt;Then add the key to ssh-agent. You will be prompted for a password and ssh agent will remember it for you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh-add "C:\Users\darragh\.ssh\authorized_keys\darraghPersonalGithub"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>windows</category>
      <category>ssh</category>
    </item>
    <item>
      <title>Should you work for a consultancy or a product company?</title>
      <dc:creator>Darragh O'Riordan</dc:creator>
      <pubDate>Sun, 11 Sep 2022 17:12:33 +0000</pubDate>
      <link>https://forem.com/darraghor/should-you-work-for-a-consultancy-or-a-product-company-5bdc</link>
      <guid>https://forem.com/darraghor/should-you-work-for-a-consultancy-or-a-product-company-5bdc</guid>
      <description>&lt;p&gt;I've recently crossed over 2 years in a software consultancy. Previously I spent around 10 years working in product companies.&lt;/p&gt;

&lt;p&gt;As you can imagine there are some differences between them and I wanted to write down some of the things I've noticed.&lt;/p&gt;

&lt;p&gt;Note: These are 100% my thoughts, and don't represent any of my employer's, past or present! I'll hopefully update this with more thoughts in a couple of years as I learn more and my views inevitably change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Types of Consulting
&lt;/h2&gt;

&lt;p&gt;First it's important to mention that there are a few different types of software consulting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Freelancers - who often work alone and find their own work.&lt;/li&gt;
&lt;li&gt;Augmentation - where a person or team might join an existing team for a short time to help accelerate some development&lt;/li&gt;
&lt;li&gt;Bespoke product development - where a team will identify product-market fit or problem-solution fit and build the solution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article I'm talking about a bespoke product development consultancy because that's what I do!&lt;/p&gt;

&lt;p&gt;In a product development consultancy you usually work on a team with designers, product experts and engineers of various specialities. Team size is typically 5–10. You might sometimes do work by yourself but there are better outcomes from teams so that's what we prefer.&lt;/p&gt;

&lt;p&gt;You will usually work on projects, i.e. a specific goal and end date. The projects tend to be one of two main categories&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;iterating on new problems quickly to find product-market fit or problem-solution fit&lt;/li&gt;
&lt;li&gt;upgrading an existing system because it can't be changed quickly and safely in its current state.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Using New Tech
&lt;/h2&gt;

&lt;p&gt;In my consultancy work I've used leading-edge tech, especially the greenfield projects. Even if you're working with legacy code in consulting, it's usually because you're transforming the legacy solution to a more modern stack.&lt;/p&gt;

&lt;p&gt;Notice that I'm deliberately not saying "bleeding-edge" because we usually don't want to use unstable, un-tested technology in client solutions.&lt;/p&gt;

&lt;p&gt;I don't want to name any specific tech examples because they will be out of date in 5 minutes but we're often already using items in the "Adopt" ring of the thoughtworks tech radars - &lt;a href="https://www.thoughtworks.com/radar"&gt;https://www.thoughtworks.com/radar&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In consulting work I've found that there's more appetite to select the best language or tool for the job even if it's not exactly what all the existing stack is built in.&lt;/p&gt;

&lt;p&gt;New tools are sometimes slower to get adopted in product organisations because of the inertia in a large existing code base. However the advantage of working in a product org with a legacy product is that the decisions are often already made for you so you can just get started on solving problems.&lt;/p&gt;

&lt;p&gt;All new features in an existing product will probably be built in the existing stack. Many companies have templates or "golden samples" for new services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rates of learning
&lt;/h2&gt;

&lt;p&gt;As a consultant you get very comfortable with learning quickly. Not only learning technologies but every project is a new team, a new industry and a new business strategy to internalise. &lt;/p&gt;

&lt;p&gt;I'm not saying you don't have to learn a lot in a product company, but the breadth of "new" in every project when consulting is definitely more pronounced.&lt;/p&gt;

&lt;p&gt;One of the primary benefits of always working on new things is that you learn to over come imposter syndrome very quickly. You know that you know nothing about this new business, but you've been in this situation many times before. You're used to the ambiguity, you've solved problems for other clients when starting from scratch previously and you know you can learn quickly.&lt;/p&gt;

&lt;p&gt;To support the learning required consultancies will spend heavily on education and training&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cloud certifications&lt;/li&gt;
&lt;li&gt;MOOCs&lt;/li&gt;
&lt;li&gt;access to conferences&lt;/li&gt;
&lt;li&gt;hiring external experts to quickly train&lt;/li&gt;
&lt;li&gt;investing in new tools like AI code generation to accelerate learning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A disadvantage to consulting on shorter engagements is that you don't get the depth of expertise in whatever industry segment and context that a product company might operate in.&lt;/p&gt;

&lt;p&gt;Spending 4-5 years in a product org, solving a specific problem in a single industry gives you the time to try many approaches and produce comprehensive solutions across the whole problem space compared to a typical consulting engagement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Variety in your work
&lt;/h2&gt;

&lt;p&gt;There's so much variety in consulting! I've worked on maybe 8 projects in the last 2 years. The projects were in government, developer experience tools, construction, sporting tech, financial services, property tech, crypto and healthcare industries. My team has worked on many other projects in different industries that I've also had to have a passing knowledge of.&lt;/p&gt;

&lt;p&gt;All of those industries have different user bases, markets, compliance standards and business models. You will have the opportunity to work on apps with millions of users and brand new apps looking for product market fit. Local companies and global companies. The possibilities in consulting are endless really.&lt;/p&gt;

&lt;p&gt;I love learning about how different businesses work so this suits me well. I can't think of an advantage working at a product company might have here. (But ask me again in another 2 years time and I might be sick of jumping around so much!)&lt;/p&gt;

&lt;p&gt;Similar to the rate of learning, working at a product company for a few years means that you'll have much more depth in whatever context your product is in compared to consulting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Company Culture
&lt;/h2&gt;

&lt;p&gt;Strong daily culture is an area where I think working at a product company is more advantageous.&lt;/p&gt;

&lt;p&gt;In a product company you typically have long-lived teams working on products that they have long-term ownership over. The work they're doing is directly driving the goals of the organisation that they're part of, and so they feel closer to the mission and culture of that company.&lt;/p&gt;

&lt;p&gt;In consulting you will spend a lot of your day working in your client's culture which takes you away from your org. In consulting your work directly drives the goals of the &lt;em&gt;client's&lt;/em&gt; organisation. Of course your work does indirectly make the consultancy successful, but there is a gap there between the two.&lt;/p&gt;

&lt;p&gt;This disjointed feeling and ambiguity as you move between the cultures on projects is noticeable to me. Missions and principles are difficult to maintain when most of your working day is in an organisation that might be quite different.&lt;/p&gt;

&lt;p&gt;Consultancy companies are well aware of this and do the extra work compensate. Consultancies invest heavily in culture building. It has to be more deliberate and consistent compared to a product company.&lt;/p&gt;

&lt;p&gt;For example, we do weekly catchups, weekly games in-office and online, team lunches, regular project check-ins, surveys, monthly events - you name it we probably do it to help retain our culture. Every consultancy works hard at this problem.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Gnar have a nice article about their efforts here: &lt;a href="https://dev.to/thegnarco/exploding-consulting-myths-culture-1n56"&gt;https://dev.to/thegnarco/exploding-consulting-myths-culture-1n56&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Thoughtworks are the content masters and have a whole blog here: &lt;a href="https://www.thoughtworks.com/en-au/insights/culture"&gt;https://www.thoughtworks.com/en-au/insights/culture&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A few different leaders give their thoughts here: &lt;a href="https://dynamicbusiness.com/featured/lets-talk-culture-2.html"&gt;https://dynamicbusiness.com/featured/lets-talk-culture-2.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The final point here is that for shorter consulting projects you miss out on the benefits of long-lived teams and the trust that forms when working together on problems for a long time. Not all consulting work is short term like this, but likely more than in a product organisation.&lt;/p&gt;

&lt;p&gt;On the flip side there are huge benefits from observing other cultures as an outsider, you can be very objective and learn from the good aspects of another org's culture. But you also have to make sure you protect yourself from anything that's too divergent from your own.&lt;/p&gt;

&lt;p&gt;If you're into missions then sometimes the client mission is more attractive than your consulting company! I've loved working on projects where I've helped people start new businesses or helped people get fitter. The client's devotion to solving these kinds of problems is infectious!&lt;/p&gt;

&lt;h2&gt;
  
  
  Ownership
&lt;/h2&gt;

&lt;p&gt;I think most people who work in software feel a certain level of ownership in the work they do. Building software products is a craft rather than an assembly line. This sense of ownership and pride in my work is something I don't think I will ever be able to shake.&lt;/p&gt;

&lt;p&gt;Whenever I built something in a product organisation I knew had to maintain it for as long as I stayed at the company. It was rewarding knowing that I would have ownership on the work for a long time.&lt;/p&gt;

&lt;p&gt;Ownership is far more fleeting in consulting. While you're working on something you're 100% committed to it, but if you're successful you have to hand the work over to the client and that's the last you'll see it.&lt;/p&gt;

&lt;p&gt;So I feel sad now when I have to hand the work back to the client after building something for 6 months. This is one of the things I miss the most about working in a product organisation!&lt;/p&gt;

&lt;h2&gt;
  
  
  Delivery focus
&lt;/h2&gt;

&lt;p&gt;Software is expensive to build, it doesn't matter if you're consulting or in a product company.&lt;/p&gt;

&lt;p&gt;Most good consulting companies will work closely with clients to prioritise and reduce scope up-front to avoid missing deadlines. But the client is usually paying directly for your time so there's more attention when things get delayed.&lt;/p&gt;

&lt;p&gt;Because delivery time pressure is higher than in a product organisation you have to monitor mental health and burnout for yourself and everyone around you as a result. No software company can afford burnout in their teams so there is emphasis on well-being in any good software company.&lt;/p&gt;

&lt;p&gt;I'm not saying that there are no deadlines in product companies, there definitely are! But there is usually revenue that the business generates that is not explicitly the time you spend on the work. Consulting is a different model and a different mentality.&lt;/p&gt;

&lt;p&gt;A benefit of such delivery focus is that you get really great at identifying project risks. You also reduce waste wherever you can like subjective coding arguments. You develop strong pragmatism and you have an extreme focus on solving only the client's problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Responsibility and Self-reliance
&lt;/h2&gt;

&lt;p&gt;Ok so this point will be very organisation dependent. But I feel that in the consulting work there is more responsibility and self-reliance expected of everyone, from a brand new developer to a senior architect.&lt;/p&gt;

&lt;p&gt;You're always representing the consultancy to the customer so you can never ignore issues and you have to follow up on what ever you promise to do. The client is paying a high fee for your expertise so they have high expectations.&lt;/p&gt;

&lt;p&gt;As a consultant there is an expectation that you're an expert at a given role and will produce high quality outcomes, there is little room for mistakes and rework. You'll often be in discussions with the client by yourself and you regularly have to advocate for outcomes that client's might not initially agree with.&lt;/p&gt;

&lt;p&gt;Advocacy skills like this are priceless in your career and the sooner you learn them and the more you practice the better.&lt;/p&gt;

&lt;p&gt;The equivalent at a product company might be regularity working with customers directly, fixing their problems and presenting it back to them for critique. There's definitely product company teams that do this, but I would say that in most cases it's not part of the job for a developer, at least in my experience.&lt;/p&gt;

&lt;p&gt;The benefit in product companies is that you don't have to do this so much, it is stressful at times!&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;If you're a new developer the breadth of experience you get at a consultancy can really accelerate your career.&lt;/p&gt;

&lt;p&gt;The exposure to multiple industries, tech stacks, market sizes and everything else will make you an excellent contributor to projects. You'll never have to worry if you can "hit the ground running" like you see in so many job adverts.&lt;/p&gt;

&lt;p&gt;If you're an experienced product developer then working at a consultancy will give you a degree of freedom and responsibility that might surprise you. You'll never know what problem will be coming next and how exactly you'll lead a team to solve it - but it is up to you to get it done!&lt;/p&gt;

&lt;p&gt;In a product company the ability to target a specific industry, a culture you admire or an interesting tech stack and work on that with focus for a few years is unique. It's not something you're likely to get at a consultancy. A product company is the best place to find stability, depth and focus like that.&lt;/p&gt;

&lt;p&gt;I've enjoyed my time at product companies and my time in consulting. They are quite different as you'd expect. I'd happily do either again.&lt;/p&gt;

&lt;p&gt;I hope some of the points in this article give you some insight into trying one or the other!&lt;/p&gt;

&lt;p&gt;What are you're thoughts on consulting Vs product companies? - let me know on twitter - &lt;a href="https://twitter.com/darraghor"&gt;@darraghor&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>career</category>
      <category>consulting</category>
    </item>
    <item>
      <title>Caching</title>
      <dc:creator>Darragh O'Riordan</dc:creator>
      <pubDate>Thu, 08 Sep 2022 00:00:00 +0000</pubDate>
      <link>https://forem.com/darraghor/postgresql-and-typeorm-caching-df1</link>
      <guid>https://forem.com/darraghor/postgresql-and-typeorm-caching-df1</guid>
      <description>&lt;p&gt;With most web applications you can drastically increase performance by using caching for data that’s frequently read across network boundaries. This lesson will explore some common caching techniques, you’ll learn how some common tools and libraries provide caching for us.&lt;/p&gt;

&lt;p&gt;While caching helps with performance it can also cause some surprises and bugs in applications and i’ll discuss some of those too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Database course index
&lt;/h2&gt;

&lt;p&gt;This is part of a full course on persistence in postgres with typeorm and sql!&lt;/p&gt;

&lt;p&gt;There is a github repo to go with this course. See part 2 for instructions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caching
&lt;/h2&gt;

&lt;p&gt;If you look at data flow in an app you can see how latency is reduced when a cache is used. The example below shows how a read might be cached.&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%2Fwww.darraghoriordan.com%2F7a7cacd52bf6f7d860a430186fd00467%2Fcaching-overview.drawio.svg" 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%2Fwww.darraghoriordan.com%2F7a7cacd52bf6f7d860a430186fd00467%2Fcaching-overview.drawio.svg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Caching is used outside of networked applications also. Computer processors make heavy use of caching for example. Its orders of magnitude faster to read from RAM Vs a hard drive, same improvement reading from a processor cache Vs computer RAM.&lt;/p&gt;

&lt;p&gt;Caching will usually make the cost of your application infrastructure cheaper. You can keep most data on a cheap storage system and only put current data on a faster storage system.&lt;/p&gt;

&lt;p&gt;Caching helps to smooth out unpredictable access patterns. The effect of even very short caching can be significant.&lt;/p&gt;

&lt;p&gt;If you have something that is read heavy but changes often and you cache it for just one second, you can guarantee that any downstream infrastructure can only get 60 requests/minute. Any hardware these days could easily handle 60rpm. Most people that use the system wont notice a delay less than 1 second for new data.&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%2Fwww.darraghoriordan.com%2F8b366d1ff76501dc2cdd2e189c5c81ae%2Fcache-smoothing.drawio.svg" 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%2Fwww.darraghoriordan.com%2F8b366d1ff76501dc2cdd2e189c5c81ae%2Fcache-smoothing.drawio.svg" alt="Cache smoothing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the diagram above there are 500k r/pm to some infrastructure without a cache. This would be pretty tough for a decent RDBMS to handle but Redis could handle that cheaply. In the second diagram you can see the effect of putting a short cache in front of the RDBMS - RDBMS requests are flat and predictable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common caching scenarios
&lt;/h2&gt;

&lt;p&gt;Here are some tools you probably use every day that use caching&lt;/p&gt;

&lt;h3&gt;
  
  
  DNS
&lt;/h3&gt;

&lt;p&gt;The domain name system that runs the internet is based on local caches. Records propagate around the internet with Time to Live set. Your browser first asks the cache in your computer how to find google.com, if that record is stale then the browser goes to your ISP for the DNS record. If your ISP doesn’t have a record then the browser goes to one of the root nameservers for the internet.&lt;/p&gt;

&lt;p&gt;Having a local cache for common DNS records like google.com results in much faster browser experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  React Query
&lt;/h3&gt;

&lt;p&gt;The react query library is an excellent data retrieval library for client applications. React query has built in read-through caching. Each request your application makes is stored in a local cache. React query will reuse the data in the local cache to quickly display data to the user. Then it will refresh the data in the background to reduce the appearance of spinners.&lt;/p&gt;

&lt;h3&gt;
  
  
  Apps with data stores
&lt;/h3&gt;

&lt;p&gt;Most applications store data somewhere, often a database. Most applications are read heavy so we can increase performance and reduce the cost of the data store by utilising a cache between the application and the data store.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common caching patterns
&lt;/h2&gt;

&lt;p&gt;There are a bunch of very common caching patterns. These three are the main ones I see when building web applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cache aside
&lt;/h3&gt;

&lt;p&gt;Cache aside is very common. The application is aware of the cache and acts as the coordinator.&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%2Fwww.darraghoriordan.com%2F7c2232629219450ad3c21875ff34cbb8%2Fcache-aside.drawio.svg" 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%2Fwww.darraghoriordan.com%2F7c2232629219450ad3c21875ff34cbb8%2Fcache-aside.drawio.svg" alt="Cache aside caching"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Assuming a cache miss scenario:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The application checks the cache for data.&lt;/li&gt;
&lt;li&gt;Cache returns empty response&lt;/li&gt;
&lt;li&gt;Application gets the data from the database&lt;/li&gt;
&lt;li&gt;Application stores the data for the next cache retrieval&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Read through caching and Write behind caching
&lt;/h3&gt;

&lt;p&gt;In read through and write behind caching the application only interacts with the cache. The cache layer then communicates with the database layer directly. The application is never aware of the database layer.&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%2Fwww.darraghoriordan.com%2F835ac667ce81f9372bb419c28f0aa58d%2Fcache-read-write-behind.drawio.svg" 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%2Fwww.darraghoriordan.com%2F835ac667ce81f9372bb419c28f0aa58d%2Fcache-read-write-behind.drawio.svg" alt="Read through write behind"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Assuming a cache read miss:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The application queries the cache&lt;/li&gt;
&lt;li&gt;The cache has no data so it queries the datastore&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The cache stores the result and returns it to the client application&lt;/p&gt;

&lt;p&gt;Assuming a cache write&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The application writes data to the cache&lt;/li&gt;
&lt;li&gt;The cache writes the data to the database&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These two patterns are not always used together. e.g. you might always read through the cache but write directly to the database. It’s just easier to talk about them in one section because the principal is so similar.&lt;/p&gt;

&lt;p&gt;Slonik Postgres client can provide an example of read through caching. It allows you to configure interceptors. The interceptor are middleware that run on your query pipeline. &lt;/p&gt;

&lt;p&gt;You can then apply hints like &lt;code&gt;@cache-ttl 10&lt;/code&gt; to the interceptors in your sql query. See the documentation at &lt;a href="https://github.com/stockholmux/slonik-redis-cache" rel="noopener noreferrer"&gt;https://github.com/stockholmux/slonik-redis-cache&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createPool&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;slonik&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;redis&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;redisCache&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;slonik-redis-cache&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="cm"&gt;/* connection options */&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createPool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;postgres://localhost:5432/applicationDb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;interceptors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;redisCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;connection&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="s2"&gt;`SELECT * FROM aTable -- @cache-ttl 10`&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&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;p&gt;An example of write through pattern can be described using Redis Enterprise with Redis Gears (&lt;a href="https://docs.redis.com/latest/modules/redisgears/" rel="noopener noreferrer"&gt;https://docs.redis.com/latest/modules/redisgears/&lt;/a&gt;). &lt;/p&gt;

&lt;p&gt;Redis Gears allows you to set up Redis to run Python and Java with hooks into the Redis runtime. The hook for Write Through Caching will propagate Redis writes to your datastore.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cache Invalidation
&lt;/h2&gt;

&lt;p&gt;If your datastore has data that isn’t in the cache yet then you have a stale cache. Whether this is an issue is highly dependent on the data and your application. At some stage you will want to invalidate this data so that the cached record gets replaced with the new record.&lt;/p&gt;

&lt;p&gt;There are excellent articles online describing how to solve this issue so i'll just give a brief overview here. Check out &lt;a href="https://redis.com/blog/three-ways-to-maintain-cache-consistency/" rel="noopener noreferrer"&gt;https://redis.com/blog/three-ways-to-maintain-cache-consistency/&lt;/a&gt; for a deep dive into cache consistency and cache invalidation.&lt;/p&gt;

&lt;p&gt;The following are the common methods used to invalidate cached data. One important thing to note is that if you use write-through then your cache should always be up to date.&lt;/p&gt;

&lt;h3&gt;
  
  
  Time to live
&lt;/h3&gt;

&lt;p&gt;Time to live invalidation is extremely common. You ask the cache to automatically expire an item after a set time period, meaning your data layer will have to go to the source to refresh the item after some set time period.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cache on write
&lt;/h3&gt;

&lt;p&gt;For data with a high read rate and that changes infrequently, you can simply update the cache each time you write to your datastore. This invalidation strategy becomes expensive as write frequency increases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cache Eviction
&lt;/h3&gt;

&lt;p&gt;Least recently used is an eviction method. This focuses on freeing resources, rather than keeping data fresh, by deleting from the cache data that isn’t getting read very often.&lt;/p&gt;

&lt;p&gt;There are other common eviction mechanisms&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Most Recently Used&lt;/li&gt;
&lt;li&gt;Least Recently User&lt;/li&gt;
&lt;li&gt;First in, First out&lt;/li&gt;
&lt;li&gt;Last in, first out&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Their usage will be highly dependent on your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Issues to watch our for with caching
&lt;/h2&gt;

&lt;p&gt;Caching is notorious in software development for causing difficult to debug issues. For example if you're debugging an issue and the data doesn't make sense, check for caching somewhere. Caching is a form of state in these scenarios.&lt;/p&gt;

&lt;p&gt;These are some of the things I look for when caching is present in a system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inappropriate load for caching
&lt;/h3&gt;

&lt;p&gt;If your database is not under significant load adding a database can have a negative effect because the latency between &lt;code&gt;app -&amp;gt; Cache -&amp;gt; DB&lt;/code&gt; can be more than a more traditional &lt;code&gt;app -&amp;gt; DB&lt;/code&gt; not under load&lt;/p&gt;

&lt;h3&gt;
  
  
  Caching too eagerly
&lt;/h3&gt;

&lt;p&gt;If you cache many things that are not read very often then you will fill up your cache. This will make it more expensive to run and having so many records will slow down the cache.&lt;/p&gt;

&lt;h3&gt;
  
  
  Incorrect configuration
&lt;/h3&gt;

&lt;p&gt;If you cache data for too long or you don’t clear the cached data at the right time then you might return stale data to a customer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;You’ll come across caching on most apps. Tweaking the caching strategies and invalidation strategies that are used will help you to improve user experience and cost of your infrastructure.&lt;/p&gt;

&lt;p&gt;Caching introduces complexity that has to be managed carefully.&lt;/p&gt;

</description>
      <category>database</category>
      <category>postgres</category>
    </item>
    <item>
      <title>How to run Monica personal CRM on Dokku</title>
      <dc:creator>Darragh O'Riordan</dc:creator>
      <pubDate>Mon, 29 Aug 2022 00:00:00 +0000</pubDate>
      <link>https://forem.com/darraghor/how-to-run-monica-personal-crm-on-dokku-3c32</link>
      <guid>https://forem.com/darraghor/how-to-run-monica-personal-crm-on-dokku-3c32</guid>
      <description>&lt;p&gt;I left my home country right after university and I worked and lived in a few countries since then. I’ve met lots of amazing people but I’ve always struggled to remember contact details and important dates for everyone.&lt;/p&gt;

&lt;p&gt;As we all get busier with life time moves faster and faster and sometimes I realise that it’s months since I checked in on people. I have forgotten the last things we talked about and what’s important in everyone’s lives.&lt;/p&gt;

&lt;p&gt;I needed a personal CRM.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monica personal CRM
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/monicahq/monica#purpose"&gt;Monica&lt;/a&gt; is a privacy focused relationship management tool. You run it on your own server so no-one has access to the data but you. There are no social networking features and it doesn’t parse or interpret your data in any way.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/4cec6b288ae6064ca16ed5b8c9ac881b/281a6/monica-interface-small.jpg"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gFzSFR8u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/4cec6b288ae6064ca16ed5b8c9ac881b/281a6/monica-interface-small.jpg" alt="Monica CRM (from Monica HQ)" title="Monica CRM (from Monica HQ)" width="246" height="310"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Monica CRM (from Monica HQ)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;(&lt;a href="//monica-interface.png"&gt;Click for larger screenshot&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;If you’re old like me you might have used Facebook for things like birthdays in the decade 2008-2018ish but most of us have deleted their data on there, or are ready to.&lt;/p&gt;

&lt;p&gt;Monica replaces that functionality in a privacy-focused way. It removes that final blocker and allows you to delete your social media accounts.&lt;/p&gt;
&lt;h2&gt;
  
  
  Dokku self hosting
&lt;/h2&gt;

&lt;p&gt;Dokku is a heroku-like PaaS platform that you can run on your own server. It’s perfect for a solo developer to run multiple applications on a VM and it makes handling security, networking, 0-downtime deploying, let’s encrypt https etc. a breeze!&lt;/p&gt;

&lt;p&gt;Heroku has just announced that they’re removing their free plan so now is a fantastic time to try Dokku on a $5 &lt;a href="https://m.do.co/c/1ee4e460bc81"&gt;Digital Ocean droplet&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I host 3 apps on my Dokku instance on digital ocean, they all have different databases and urls. The only infrastructure work I have to do is log in once a quarter and update apt packages and Dokku itself.&lt;/p&gt;

&lt;p&gt;I have an article at &lt;a href="https://www.darraghoriordan.com/2021/12/29/run-node-app-postgres-dokku-digital-ocean/"&gt;https://www.darraghoriordan.com/2021/12/29/run-node-app-postgres-dokku-digital-ocean/&lt;/a&gt; that describes how to setup a Dokku instance. You can follow that to setup Dokku and then come back here. There is also excellent documentation on digital ocean describing how to setup Dokku.&lt;/p&gt;

&lt;p&gt;You will need a url configured if you want to set up tls for the Monica app (you should do this).&lt;/p&gt;

&lt;p&gt;So for the next steps I assume you have an instance of Dokku running on a server and you’re ssh’d into it.&lt;/p&gt;

&lt;p&gt;The following steps describe how to set up Monica on your Dokku instance.&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Add a swap file to your Dokku host
&lt;/h2&gt;

&lt;p&gt;Note: These commands are run on your Dokku server&lt;/p&gt;

&lt;p&gt;The Monica build process takes a bit of memory. If you’re on a cheap server like a $4 digital ocean droplet you might get errors that the build failed with no details. This is likely an out of memory issue and digital ocean doesn’t configure a swap for you by default.&lt;/p&gt;

&lt;p&gt;First you should check if you have a swap file set. If the swapon show command returns nothing then you don’t have a swap and you should add one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# check if a swap file is set
sudo swapon --show

# check how much free space you have
free -h
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run these commands to setup your swap file on ubuntu. I set a 1GB swap file on my droplet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# create the allocation
sudo fallocate -l 1G /swapfile

# change permissions for swap
sudo chmod 600 /swapfile

# make it a swap file
sudo mkswap /swapfile

# turn it on
sudo swapon /swapfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now add the configuration to fstab so there is still a swap on your server after any reboots.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo cp /etc/fstab /etc/fstab.bak
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
sudo sysctl vm.swappiness=10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The digital ocean article recommends turning down the default swappiness because this is a server not a desktop. Sounds like a great idea so let’s do that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo nano /etc/sysctl.conf
ADD: vm.swappiness=10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Digital ocean have a details article on setting a swap file &lt;a href="https://www.digitalocean.com/community/tutorials/how-to-add-swap-space-on-ubuntu-22-04"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Add Monica as a Dokku application
&lt;/h2&gt;

&lt;p&gt;Note: These commands are run on your Dokku server&lt;/p&gt;

&lt;p&gt;First thing we need is a Dokku application&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dokku apps:create monica-crm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Add a database for Monica
&lt;/h2&gt;

&lt;p&gt;Next we need a database. Monica is built around MySQL but I prefer to use MariaDB instead of MySQL. First we install the MariaDB plugin and then create and link a new database instance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# install the MariaDB plugin
dokku plugin:install https://github.com/dokku/dokku-mariadb.git mariadb

# create the database instance
dokku mariadb:create monica-crm-db

# link the Dokku database to the Dokku application
dokku mariadb:link monica-crm-db monica-crm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Add networking for Dokku app
&lt;/h2&gt;

&lt;p&gt;Next we need to setup some networking for our application. I’m assuming you setup a domain when configuring your Dokku instance, and that you’ve already configured letsencrypt plugin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# set a domain for the app (the default will be app-name.yourdomain.com e.g. monica-crm.yourdomain.com)
dokku domains:set monica-crm monica.yourdomain.com

# set a config variable for email address. This is required by lets encrypt
dokku config:set --no-restart monica-crm DOKKU_LETSENCRYPT_EMAIL=your.email@somedomain.com

# now register a new domain with letsencrypt and install the certificate
dokku letsencrypt:enable monica-crm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Add Monica configuration settings
&lt;/h2&gt;

&lt;p&gt;Before you run Monica in a production-like environment you will have to configure some settings for the application. These are set with environment variables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# APP_URL is used in emails sent by monica so that links work as expected
# APP_DISABLE_SIGNUP blocks anyone from signing up to your instance. We will set this to true later!
# APP_KEY is a security key and you should generate a random set of 32 alphanumeric characters
# APP_ENV tells the application to use tls if set to production - we want this because we have tls with lets encrypt
dokku config:set --no-restart monica-crm APP_URL=https://monica.yourdomain.com APP_DISABLE_SIGNUP=false APP_KEY=MUvCHANGETORANDOMALPHAmbG APP_ENV=production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Configure email sending for Monica
&lt;/h2&gt;

&lt;p&gt;One of the nice features of Monica are reminder emails for important events or any regular catchups you want to have. Monica needs some email provider so that it can send your emails.&lt;/p&gt;

&lt;p&gt;This is optional but very worthwhile.&lt;/p&gt;

&lt;p&gt;I have an SMTP email service that I pay for, so that’s what I use. You can use other providers like mailgun or AWS SES. There are more details on the Monica &lt;a href="https://github.com/monicahq/monica/blob/main/docs/installation/mail.md"&gt;documentation site&lt;/a&gt; on how to configure other email sending providers.&lt;/p&gt;

&lt;p&gt;Email settings are also environment variables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Set the various environment variables for email. These are fairly self-explanatory.
dokku config:set --no-restart monica-crm MAIL_MAILER=smtp MAIL_HOST=smtp.mysmtpprovider.com MAIL_PORT=587 MAIL_USERNAME=myusername@mysmtpprovider.com MAIL_PASSWORD=mysmtppassword MAIL_ENCRYPTION=tls

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. Fork and clone the Monica application
&lt;/h2&gt;

&lt;p&gt;NOTE: These commands are run on your local machine!&lt;/p&gt;

&lt;p&gt;We fork Monica on Github or using gh cli, then clone our copy Monica to a local folder. This is so that we can make some changes to the code for Dokku.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# if you have gh cli you can fork (and clone - it will ask you) from your terminal
gh repo fork monicahq/monica

# clone Monica - change the username (skip if you already cloned)
git clone git@github.com:mygithubusername/monica.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Monica is built to run directly from the repository on Heroku but we must make minor edits for Dokku.&lt;/p&gt;

&lt;p&gt;First file is a new file &lt;code&gt;.buildpacks&lt;/code&gt; in the root with this content list the buildpacks to use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://github.com/heroku/heroku-buildpack-php
https://github.com/heroku/heroku-buildpack-nodejs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next is an edit to the existing &lt;code&gt;app.json&lt;/code&gt; file where we add a cron.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"cron": [
     {
         "command": "php artisan schedule:run",
         "schedule": "*/10 * * * *"
     }
 ],
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now commit and push those 2 changes to your forked Monica instance.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Build Monica on Dokku for the first time!
&lt;/h2&gt;

&lt;p&gt;Note: These commands are run on your local machine&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# you have to add a remote to the local repository that points to your dokku server
git remote add dokku dokku@the_ip_or_hostname_of_your_dokku_server:monica-crm

# and push the code! This will take a while because it kicks off a build of the application
git push dokku main:master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If that worked as expected it will print lines at the end like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;======&amp;gt; Application deployed:
        http://monica.yourdomain.com
        https://monica.yourdomain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go to the url! Monica will ask you to set up your account.&lt;/p&gt;

&lt;p&gt;&lt;a href="///static/9633ad1e44994a8a730befcf092e83a9/78a22/monica-login.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Sx_2ISb4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.darraghoriordan.com/static/9633ad1e44994a8a730befcf092e83a9/78a22/monica-login.png" alt="Monica login screen" title="Monica login screen" width="585" height="527"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Monica login screen&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  9. A final security clean up for Monica
&lt;/h2&gt;

&lt;p&gt;Remember the &lt;code&gt;APP_DISABLE_SIGNUP&lt;/code&gt; variable from before. Now that you have set up your account you probably don’t want anyone else signing up for an account on your server. So let’s shut off that possibility.&lt;/p&gt;

&lt;p&gt;On your Dokku server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dokku config:set monica-crm APP_DISABLE_SIGNUP=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;I’ve just started using Monica and enjoying it so far. It has a simple interface which is just perfect. Time will tell if it helps me as a personal CRM!&lt;/p&gt;

&lt;p&gt;Dokku provides a nice alternative to Heroku’s free tier. Even though it’s not free, it can be run on a very cheap host like a $5 &lt;a href="https://m.do.co/c/1ee4e460bc81"&gt;Digital Ocean droplet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you have setup your Dokku instance you can add new apps very quickly and Dokku makes it easy for a solo-developer to manage multiple applications. I recommend checking it out!&lt;/p&gt;

</description>
      <category>relationships</category>
      <category>socialmedia</category>
      <category>heroku</category>
    </item>
  </channel>
</rss>
