<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jonathan Harrison</title>
    <description>The latest articles on DEV Community by Jonathan Harrison (@jonjam).</description>
    <link>https://dev.to/jonjam</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%2F546415%2Fbca805b2-6c7a-435a-9e9a-27afeb5a9941.jpeg</url>
      <title>DEV Community: Jonathan Harrison</title>
      <link>https://dev.to/jonjam</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jonjam"/>
    <language>en</language>
    <item>
      <title>Agentic development in practice: Building a weather MCP server with Spring AI and Cursor</title>
      <dc:creator>Jonathan Harrison</dc:creator>
      <pubDate>Tue, 31 Mar 2026 07:28:27 +0000</pubDate>
      <link>https://dev.to/jonjam/weather-mcp-1idj</link>
      <guid>https://dev.to/jonjam/weather-mcp-1idj</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;At the beginning of 2026, the UK was experiencing an &lt;a href="https://www.metoffice.gov.uk/blog/2026/how-much-rain-have-we-had-in-february-and-winter" rel="noopener noreferrer"&gt;unusually wet start&lt;/a&gt; — something that seemed to dominate everyday conversation. That got me thinking:&lt;/p&gt;

&lt;p&gt;Why shouldn’t AI agents be able to join in too?&lt;/p&gt;

&lt;p&gt;So I set out to build a weather-powered &lt;a href="https://modelcontextprotocol.io/docs/getting-started/intro" rel="noopener noreferrer"&gt;Model Context Protocol (MCP) server&lt;/a&gt; using &lt;a href="https://spring.io/projects/spring-ai" rel="noopener noreferrer"&gt;Spring AI&lt;/a&gt;, developed with &lt;a href="https://cursor.com/home" rel="noopener noreferrer"&gt;Cursor agents&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Along the way, I got to explore agentic development, learn new tooling, and solve a few unexpected challenges.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I built
&lt;/h2&gt;

&lt;p&gt;I built an &lt;a href="https://www.accuweather.com/" rel="noopener noreferrer"&gt;Accuweather&lt;/a&gt;-powered MCP server that enables AI agents to query:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Current weather&lt;/li&gt;
&lt;li&gt;Hourly forecasts&lt;/li&gt;
&lt;li&gt;Daily forecasts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7g31dhj768s2bqxcxk22.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7g31dhj768s2bqxcxk22.png" alt="A screenshot of the weather forecast for Sheffield, United Kingdom within Claude" width="792" height="582"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The server is published on the &lt;a href="https://registry.modelcontextprotocol.io/?q=io.github.JonJam%2Faccuweather-mcp" rel="noopener noreferrer"&gt;official MCP Registry&lt;/a&gt; and can be integrated into tools like Claude.&lt;/p&gt;

&lt;p&gt;This allows LLMs to access structured, real-time weather data via tools—rather than relying on potentially outdated or hallucinated responses.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;The MCP server exposes weather functionality as tools that AI agents can call via the Model Context Protocol.&lt;/p&gt;

&lt;p&gt;At a high level:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spring AI handles MCP integration and tool definitions&lt;/li&gt;
&lt;li&gt;The server communicates with the AccuWeather API&lt;/li&gt;
&lt;li&gt;MCP tools are exposed using annotations&lt;/li&gt;
&lt;li&gt;AI agents (e.g. Claude) invoke these tools via structured requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This architecture allows the model to fetch live data when needed, making responses more accurate and reliable.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I got up to speed
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Cursor
&lt;/h3&gt;

&lt;p&gt;Cursor provides excellent resources for getting started with agentic development that includes plenty of guidance, recommendations and best practises.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://cursor.com/docs" rel="noopener noreferrer"&gt;Docs&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://cursor.com/learn" rel="noopener noreferrer"&gt;Courses&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cursor.com/workshops" rel="noopener noreferrer"&gt;Workshops&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Model Context Protocol
&lt;/h3&gt;

&lt;p&gt;If you’re new to MCP, the &lt;a href="https://modelcontextprotocol.io/docs/getting-started/intro" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; is a great starting point.&lt;/p&gt;

&lt;p&gt;I’d also strongly recommend reading the specification itself — it covers important details like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Error handling&lt;/li&gt;
&lt;li&gt;Security considerations&lt;/li&gt;
&lt;li&gt;Implementation patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Spring AI
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://docs.spring.io/spring-ai/reference/index.html" rel="noopener noreferrer"&gt;Spring AI reference documentation&lt;/a&gt; provides solid guidance on MCP and broader Generative AI concepts such as chat clients and RAG.&lt;/p&gt;

&lt;p&gt;One of the most useful resources was the &lt;a href="https://github.com/spring-projects/spring-ai-examples" rel="noopener noreferrer"&gt;examples repository&lt;/a&gt;, which demonstrates how everything fits together—particularly how to use &lt;a href="https://docs.spring.io/spring-ai/reference/api/mcp/mcp-annotations-overview.html" rel="noopener noreferrer"&gt;MCP annotations&lt;/a&gt; to define tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotchas
&lt;/h2&gt;

&lt;p&gt;These were the issues that slowed me down the most and are likely to help others building something similar.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cursor
&lt;/h3&gt;

&lt;p&gt;My goal for this project was to lean into agentic development and minimise hand-written code.&lt;/p&gt;

&lt;p&gt;Initially, I tried a one-shot approach: generating a full plan and letting the agent implement the feature. It worked—but produced far more code than I wanted, and used patterns I wouldn’t normally choose.&lt;/p&gt;

&lt;p&gt;To improve this, I made a few changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Added guard rails&lt;/strong&gt;: Unit and integration tests, code formatting with &lt;a href="https://github.com/diffplug/spotless" rel="noopener noreferrer"&gt;spotless&lt;/a&gt;, code analysis using &lt;a href="https://checkstyle.org/" rel="noopener noreferrer"&gt;checkstyle&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Incremental rule creation&lt;/strong&gt;: Whenever the agent made a mistake (e.g. using Maven instead of Gradle, or missing &lt;a href="https://jspecify.dev/" rel="noopener noreferrer"&gt;JSpecify&lt;/a&gt; nullability annotations), I added rules to &lt;a href="https://github.com/JonJam/accuweather-mcp/blob/main/AGENTS.md" rel="noopener noreferrer"&gt;AGENTS.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Used Plan mode and auto model selection&lt;/strong&gt;: This helped optimise usage and extend the limits of the Hobby plan.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Develop in increments&lt;/strong&gt;: Instead of building full features in one go, I broke them into layers: HTTP client, Gateway and MCP tool. This made it easier to review and course-correct.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Referenced existing code&lt;/strong&gt;: Once patterns were established, I used them as examples to guide the agent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the time I implemented the hourly forecast feature, I was able to generate a &lt;a href="https://github.com/JonJam/accuweather-mcp/blob/main/.cursor/plans/hourly_forecast_feature_4e364946.plan.md" rel="noopener noreferrer"&gt;plan&lt;/a&gt; and delegate execution to a Cloud Agent—only needing to review the result.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjr7w9oh6i3odvfx1zouh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjr7w9oh6i3odvfx1zouh.png" alt="A screenshot of the output produced by a Cursor Cloud Agent when building the hourly forecast feature" width="800" height="633"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Spring AI
&lt;/h3&gt;

&lt;p&gt;A few things weren’t immediately obvious from the docs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Turn off console logging&lt;/strong&gt;: MCP servers using stdio must only output JSON-RPC messages. Any logging to stdout will &lt;a href="https://modelcontextprotocol.io/docs/develop/build-server#logging-in-mcp-servers-3" rel="noopener noreferrer"&gt;break the integration with an MCP client&lt;/a&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;console&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;MCP Server annotations&lt;/strong&gt;: To use &lt;a href="https://docs.spring.io/spring-ai/reference/api/mcp/mcp-annotations-server.html" rel="noopener noreferrer"&gt;MCP Server Annnotatons&lt;/a&gt; which simplifies the creation of MCP server functionality like tools, you need to include the &lt;code&gt;org.springframework.ai:spring-ai-mcp-annotations&lt;/code&gt; dependency and import them from &lt;code&gt;org.springaicommunity.mcp.annotation.*&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Debugging with MCP Inspector&lt;/strong&gt;: If you want to use the &lt;a href="https://modelcontextprotocol.io/docs/tools/inspector" rel="noopener noreferrer"&gt;MCP Inspector&lt;/a&gt; to debug your MCP server, use the following command:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @modelcontextprotocol/inspector &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'JAVA_TOOL_OPTIONS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005'&lt;/span&gt; java &lt;span class="nt"&gt;-jar&lt;/span&gt; &lt;span class="s2"&gt;"build/libs/accuweather-mcp-local-snapshot.jar"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Testing with Claude&lt;/strong&gt;: If you want to test your MCP server using Claude, refer to the &lt;a href="https://modelcontextprotocol.io/docs/develop/build-server#testing-your-server-with-claude-for-desktop-3" rel="noopener noreferrer"&gt;MCP docs&lt;/a&gt;. Since I use &lt;a href="https://sdkman.io/" rel="noopener noreferrer"&gt;sdkman&lt;/a&gt;, the &lt;code&gt;command&lt;/code&gt; value needed to be &lt;code&gt;~/.sdkman/candidates/java/current/bin/java&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Publishing to the MCP registry
&lt;/h3&gt;

&lt;p&gt;Publishing to the &lt;a href="https://registry.modelcontextprotocol.io/" rel="noopener noreferrer"&gt;Official MCP registry&lt;/a&gt; introduced a few additional challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Docker multi-platform builds&lt;/strong&gt;: &lt;a href="https://semantic-release.gitbook.io/semantic-release/extending/plugins-list#community-plugins" rel="noopener noreferrer"&gt;semenatic-release docker plugins &lt;/a&gt; only support &lt;code&gt;amd64&lt;/code&gt;. Since I was on an ARM-based Mac I needed &lt;code&gt;arm64&lt;/code&gt; support and because I was already using &lt;a href="https://github.com/GoogleContainerTools/jib" rel="noopener noreferrer"&gt;jib&lt;/a&gt; to containerize my application, I opted to use &lt;a href="https://github.com/semantic-release/exec" rel="noopener noreferrer"&gt;@semantic-release/exec&lt;/a&gt; and jib directly to publish to Docker Hub.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validating server.json&lt;/strong&gt;: The &lt;a href="https://modelcontextprotocol.io/registry/quickstart#step-3-install-mcp-publisher" rel="noopener noreferrer"&gt;mcp-publisher CLI&lt;/a&gt; includes a &lt;code&gt;validate&lt;/code&gt; command - use it early to verify your MCP server definition (&lt;code&gt;server.json&lt;/code&gt;). I added this step to CI after encountering issues with description length during publishing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;This project was a great way to explore agentic development in practice — moving beyond simple prompts to structured, tool-driven systems. As well as getting familiar with the Model Context Protocol and Spring AI.&lt;/p&gt;

&lt;p&gt;And if nothing else, at least your AI will finally be able to complain about the weather too.&lt;/p&gt;

</description>
      <category>spring</category>
      <category>mcp</category>
      <category>ai</category>
      <category>java</category>
    </item>
    <item>
      <title>Part 4: DigitalOcean App Platform Hackathon Submission</title>
      <dc:creator>Jonathan Harrison</dc:creator>
      <pubDate>Sat, 26 Dec 2020 13:42:09 +0000</pubDate>
      <link>https://dev.to/jonjam/part-4-digitalocean-app-platform-hackathon-submission-2-445</link>
      <guid>https://dev.to/jonjam/part-4-digitalocean-app-platform-hackathon-submission-2-445</guid>
      <description>&lt;p&gt;This post is part of a series detailing my journey with &lt;a href="https://golang.org/" rel="noopener noreferrer"&gt;&lt;code&gt;golang&lt;/code&gt;&lt;/a&gt; from learning the language to entering the &lt;a href="https://dev.to/devteam/announcing-the-digitalocean-app-platform-hackathon-on-dev-2i1k"&gt;DigitalOcean App Platform Hackathon&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The app I built can be found on &lt;a href="https://github.com/JonJam/stock-checker" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Part 4 details the DigitalOcean App Platform Hackathon submission and how it is deployed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I built
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Category Submission
&lt;/h3&gt;

&lt;p&gt;Random Roulette&lt;/p&gt;

&lt;h3&gt;
  
  
  App Link
&lt;/h3&gt;

&lt;p&gt;N/A&lt;/p&gt;

&lt;h3&gt;
  
  
  Screenshots
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzexeolf7l0hksklih0gm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzexeolf7l0hksklih0gm.png" alt="Alt Text" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Description
&lt;/h3&gt;

&lt;p&gt;stock-checker is a &lt;code&gt;golang&lt;/code&gt; app that spawns a task every hour to check whether any of the following UK retailers have an Xbox Series X console in stock:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://argos.co.uk/" rel="noopener noreferrer"&gt;Argos&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.co.uk/" rel="noopener noreferrer"&gt;Amazon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.currys.co.uk" rel="noopener noreferrer"&gt;Currys&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://game.co.uk" rel="noopener noreferrer"&gt;Game&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.johnlewis.com/" rel="noopener noreferrer"&gt;John Lewis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.shopto.net/" rel="noopener noreferrer"&gt;Shopto&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.smythstoys.com" rel="noopener noreferrer"&gt;Smyths&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is done using a web automation library that navigates each site in a headless chromium browser. &lt;/p&gt;

&lt;p&gt;If the app determines stock is available, a text message is sent using &lt;a href="https://www.twilio.com" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt; to the configured mobile number.&lt;/p&gt;

&lt;p&gt;This is deployed to a &lt;a href="https://www.digitalocean.com/docs/app-platform/how-to/manage-workers/" rel="noopener noreferrer"&gt;DigitalOcean App Platform Worker&lt;/a&gt; using a &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Link to Source Code
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/JonJam/stock-checker" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Permissive License
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/JonJam/stock-checker/blob/main/LICENSE" rel="noopener noreferrer"&gt;MIT&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;See &lt;a href="https://dev.to/jonjam/my-golang-journey-part-1-background-and-learning-golang-23ni"&gt;Part 1&lt;/a&gt; of this series.&lt;/p&gt;

&lt;h3&gt;
  
  
  How I built it
&lt;/h3&gt;

&lt;p&gt;See &lt;a href="https://dev.to/jonjam/part-3-developing-stock-checker-app-4bnc"&gt;Part 3&lt;/a&gt; of this series.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hosting and deployment
&lt;/h3&gt;

&lt;p&gt;This &lt;code&gt;golang&lt;/code&gt; app is deployed using &lt;code&gt;docker&lt;/code&gt; to a &lt;a href="https://www.digitalocean.com/docs/app-platform/how-to/manage-workers/" rel="noopener noreferrer"&gt;Worker component&lt;/a&gt; on &lt;a href="https://www.digitalocean.com/docs/app-platform/" rel="noopener noreferrer"&gt;DigitalOcean's App Platform&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The DO App is defined using an &lt;a href="https://www.digitalocean.com/docs/app-platform/references/app-specification-reference/" rel="noopener noreferrer"&gt;Application Reference specification&lt;/a&gt; which is shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: stock-checker-app
region: fra
workers:
- name: bg-worker-stock-checker
  github:
    branch: main
    deploy_on_push: true
    repo: JonJam/stock-checker
  dockerfile_path: Dockerfile
  instance_count: 1
  instance_size_slug: basic-xs
  envs:
  - key: SC_TWILIO_ENABLED
    scope: RUN_AND_BUILD_TIME
    value: "true"
  - key: SC_TWILIO_ACCOUNTSID
    scope: RUN_AND_BUILD_TIME
    type: SECRET
    value: ""
  - key: SC_TWILIO_AUTHTOKEN
    scope: RUN_AND_BUILD_TIME
    type: SECRET
    value: ""
  - key: SC_TWILIO_NUMBERTO
    scope: RUN_AND_BUILD_TIME
    type: SECRET
    value: ""
  - key: SC_TWILIO_NUMBERFROM
    scope: RUN_AND_BUILD_TIME
    type: SECRET
    value: ""
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This App was then deployed using &lt;a href="https://www.digitalocean.com/docs/apis-clis/doctl/" rel="noopener noreferrer"&gt;doctl&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Originally I was going to use a &lt;a href="https://www.digitalocean.com/docs/app-platform/how-to/add-deploy-do-button/" rel="noopener noreferrer"&gt;Deploy to DigitalOcean button&lt;/a&gt;, however worker components are not currently supported.&lt;/p&gt;

&lt;p&gt;Prior to this project, I wasn't familiar with Digital Ocean so using the App Platform was new to me. The resources I found useful are listed below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/docs/app-platform/how-to/manage-workers/" rel="noopener noreferrer"&gt;App Platform - Workers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/docs/app-platform/how-to/use-environment-variables/" rel="noopener noreferrer"&gt;App Platform - Environment variables&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/docs/app-platform/references/app-specification-reference/" rel="noopener noreferrer"&gt;App Platform - App Specification Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/community/tech_talks/defining-your-app-specification-on-digitalocean-app-platform" rel="noopener noreferrer"&gt;App Platform - Tech Talk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/docs/apis-clis/doctl/" rel="noopener noreferrer"&gt;doctl - Reference&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Additional Resources / Info
&lt;/h3&gt;

&lt;p&gt;-&lt;/p&gt;

</description>
      <category>dohackathon</category>
      <category>go</category>
      <category>docker</category>
      <category>digitalocean</category>
    </item>
    <item>
      <title>Part 3: Developing stock-checker app</title>
      <dc:creator>Jonathan Harrison</dc:creator>
      <pubDate>Sat, 26 Dec 2020 13:15:06 +0000</pubDate>
      <link>https://dev.to/jonjam/part-3-developing-stock-checker-app-4bnc</link>
      <guid>https://dev.to/jonjam/part-3-developing-stock-checker-app-4bnc</guid>
      <description>&lt;p&gt;This post is part of a series detailing my journey with &lt;a href="https://golang.org/" rel="noopener noreferrer"&gt;&lt;code&gt;golang&lt;/code&gt;&lt;/a&gt; from learning the language to entering the &lt;a href="https://dev.to/devteam/announcing-the-digitalocean-app-platform-hackathon-on-dev-2i1k"&gt;DigitalOcean App Platform Hackathon&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The app I built can be found on &lt;a href="https://github.com/JonJam/stock-checker" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Part 3 details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;golang&lt;/code&gt; packages used to build stock-checker&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker&lt;/code&gt; setup for both development and production&lt;/li&gt;
&lt;li&gt;Integrating with Twilio SMS API&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Overview
&lt;/h1&gt;

&lt;p&gt;stock-checker is a &lt;code&gt;golang&lt;/code&gt; app that spawns a task every hour to check whether any of the following UK retailers have an Xbox Series X console in stock:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://argos.co.uk/" rel="noopener noreferrer"&gt;Argos&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.amazon.co.uk/" rel="noopener noreferrer"&gt;Amazon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.currys.co.uk" rel="noopener noreferrer"&gt;Currys&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://game.co.uk" rel="noopener noreferrer"&gt;Game&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.johnlewis.com/" rel="noopener noreferrer"&gt;John Lewis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.shopto.net/" rel="noopener noreferrer"&gt;Shopto&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.smythstoys.com" rel="noopener noreferrer"&gt;Smyths&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is done using a web automation library that navigates each site in a headless chromium browser. &lt;/p&gt;

&lt;p&gt;If the app determines stock is available, a text message is sent using &lt;a href="https://www.twilio.com" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt; to the configured mobile number. &lt;/p&gt;

&lt;h1&gt;
  
  
  golang packages
&lt;/h1&gt;

&lt;p&gt;A number of different packages were used to build this app which are detailed in this section.&lt;/p&gt;

&lt;p&gt;These packages were discovered on &lt;a href="https://awesome-go.com" rel="noopener noreferrer"&gt;Awesome Go&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Web Automation - go-rod/rod
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/go-rod/rod" rel="noopener noreferrer"&gt;&lt;code&gt;rod&lt;/code&gt;&lt;/a&gt; is a high-level driver for the &lt;a href="https://chromedevtools.github.io/devtools-protocol/" rel="noopener noreferrer"&gt;DevTools Protocol&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This package is used to navigate the various retailer's sites to find the Xbox Series X console listing. Once found, it checks whether the console is in or out of stock.&lt;/p&gt;

&lt;p&gt;Notable features include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://go-rod.github.io/#/emulation?id=bypass-bot-detection" rel="noopener noreferrer"&gt;Bypass bot detection&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://go-rod.github.io/#/custom-launch" rel="noopener noreferrer"&gt;Custom browser launch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://go-rod.github.io/#/page-pool" rel="noopener noreferrer"&gt;Concurrent execution using PagePool&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Configuration - spf13/viper
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/spf13/viper" rel="noopener noreferrer"&gt;&lt;code&gt;Viper&lt;/code&gt;&lt;/a&gt; is a configuration package that supports reading configuration files in various formats, as well as from environment variables.&lt;/p&gt;

&lt;p&gt;This is being used to control:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The scheduler interval&lt;/li&gt;
&lt;li&gt;Debug settings for &lt;code&gt;rod&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Enables/disables sending notifications and the secrets used to communicate with Twilio.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I used an &lt;code&gt;envfile&lt;/code&gt; to define the app's configuration. Originally, I tried using a &lt;code&gt;YAML&lt;/code&gt; file but due to a couple of issues, I had to swap the file format. &lt;/p&gt;

&lt;p&gt;To help others, here are the issues I ran into when using &lt;code&gt;YAML&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/spf13/viper/issues/1029" rel="noopener noreferrer"&gt;GitHub Issue&lt;/a&gt;: If you want to override a &lt;code&gt;YAML&lt;/code&gt; subobject property with an environment variable, you have to name the variable using the following format: &lt;code&gt;PREFIX_SECURE.KEY&lt;/code&gt;. &lt;a href="https://www.digitalocean.com/docs/app-platform/how-to/use-environment-variables/" rel="noopener noreferrer"&gt;DigitalOcean App Platform environment variables&lt;/a&gt; do not support &lt;code&gt;.&lt;/code&gt; in the name.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/spf13/viper/issues/1012" rel="noopener noreferrer"&gt;GitHub Issue&lt;/a&gt;: If you want to override a &lt;code&gt;YAML&lt;/code&gt; subobject property with an environment variable and unmarshal that object to a struct, this isn't currently possible. This is because &lt;code&gt;viper.UnmarshalKey&lt;/code&gt; doesn't take account of environment variables.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Scheduler - go-co-op/gocron
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/go-co-op/gocron" rel="noopener noreferrer"&gt;&lt;code&gt;goCron&lt;/code&gt;&lt;/a&gt; is a scheduling package that lets you run functions periodically at a pre-determined interval.&lt;/p&gt;

&lt;p&gt;This is being used to run a task once an hour that checks the various retailers and if applicable, sends a notification.&lt;/p&gt;

&lt;h1&gt;
  
  
  docker support
&lt;/h1&gt;

&lt;p&gt;To simplify running the app on &lt;a href="https://www.digitalocean.com/" rel="noopener noreferrer"&gt;DigitalOcean&lt;/a&gt;, I added &lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;&lt;code&gt;docker&lt;/code&gt;&lt;/a&gt; support which this section covers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dockerfile
&lt;/h2&gt;

&lt;p&gt;This amazing &lt;a href="https://blog.jetbrains.com/go/2020/05/04/go-development-with-docker-containers/" rel="noopener noreferrer"&gt;JetBrains blog post series&lt;/a&gt; greatly inspired the &lt;code&gt;Dockerfile&lt;/code&gt; created for this project.&lt;/p&gt;

&lt;p&gt;Using &lt;a href="https://docs.docker.com/develop/develop-images/multistage-build/" rel="noopener noreferrer"&gt;multi-stage builds&lt;/a&gt; it provides both a development image with debugging support and a production image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Builder
FROM golang:1.15.6-buster AS builder
COPY . /src
WORKDIR /src
RUN go get github.com/go-delve/delve/cmd/dlv
RUN go build -gcflags="all=-N -l" -o app-dev
RUN go build -o app

# Base runner
FROM debian:10.7 AS base-runner
RUN apt-get update &amp;amp;&amp;amp; apt-get install -y \
    ca-certificates \
    chromium \
    &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/*

# Dev runner
FROM base-runner AS dev-runner
WORKDIR /server
COPY config.env /server
COPY --from=builder /src/app-dev /server/app
COPY --from=builder /go/bin/dlv /server
EXPOSE 40000
CMD ["/server/dlv", "--listen=:40000", "--headless=true", "--api-version=2", "--accept-multiclient", "exec", "/server/app"]

# Prod runner
FROM base-runner AS prod-runner
WORKDIR /server
COPY config.env /server
COPY --from=builder /src/app /server
CMD ["./app"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  docker-compose
&lt;/h2&gt;

&lt;p&gt;To assist with development, this &lt;code&gt;docker-compose&lt;/code&gt; file starts a development container with debugging enabled and enables a few &lt;code&gt;rod&lt;/code&gt; debug options via 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;version: "3.9"

services:
  app:
    build:
      context: ./..
      target: dev-runner 
    security_opt:
      - seccomp:unconfined
    cap_add:
      - SYS_PTRACE
    container_name: stock-checker-$USER

    ports:
      - "40000:40000" # DEBUG

    environment:
      - SC_ROD_TRACE=true
      - SC_ROD_PAGEPOOLSIZE=1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Remote debugging using VS Code
&lt;/h2&gt;

&lt;p&gt;To remote debug the app within &lt;code&gt;docker&lt;/code&gt;, we need to create a launch configuration by following this &lt;a href="https://github.com/golang/vscode-go/blob/master/docs/debugging.md#remote-debugging" rel="noopener noreferrer"&gt;guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The resulting config looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Launch and debug in Docker",
            "type": "go",
            "request": "attach",
            "mode": "remote",
            "cwd": "${workspaceFolder}",
            "port": 40000,
            "host": "127.0.0.1",
            // This corresponds to the directory the app is built in
            "remotePath": "/src"
        },
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Integrating with Twilio
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://www.twilio.com" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt; is being used to send a SMS when the app detects a Xbox Series X console is in stock.&lt;/p&gt;

&lt;p&gt;Originally, I was going to use &lt;a href="https://github.com/saintpete/twilio-go" rel="noopener noreferrer"&gt;&lt;code&gt;saintpete/twilio-go&lt;/code&gt;&lt;/a&gt; which I discovered &lt;a href="https://www.twilio.com/docs/libraries/community-supported-libraries" rel="noopener noreferrer"&gt;here&lt;/a&gt;. However I ran into issues with this package due to incompatible package versions.&lt;/p&gt;

&lt;p&gt;In the end, I went with a simpler approach and ended up following this &lt;a href="https://www.twilio.com/blog/2017/09/send-text-messages-golang.html" rel="noopener noreferrer"&gt;blog post&lt;/a&gt; to integrate with their SMS API directly.&lt;/p&gt;

&lt;h1&gt;
  
  
  Next
&lt;/h1&gt;

&lt;p&gt;That's it for this post.&lt;/p&gt;

&lt;p&gt;In the final part, I will detail my &lt;a href="https://dev.to/devteam/announcing-the-digitalocean-app-platform-hackathon-on-dev-2i1k"&gt;DigitalOcean App Platform Hackathon&lt;/a&gt; submission as well as deploying to DigitalOcean.&lt;/p&gt;

</description>
      <category>dohackathon</category>
      <category>go</category>
      <category>docker</category>
      <category>digitalocean</category>
    </item>
    <item>
      <title>Part 2: Development environment</title>
      <dc:creator>Jonathan Harrison</dc:creator>
      <pubDate>Sat, 26 Dec 2020 12:37:11 +0000</pubDate>
      <link>https://dev.to/jonjam/my-golang-journey-part-2-development-environment-2edg</link>
      <guid>https://dev.to/jonjam/my-golang-journey-part-2-development-environment-2edg</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;The cover image is from &lt;a href="https://github.com/MariaLetta/free-gophers-pack" rel="noopener noreferrer"&gt;MariaLetta/free-gophers-pack&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This post is part of a series detailing my journey with &lt;a href="https://golang.org/" rel="noopener noreferrer"&gt;&lt;code&gt;golang&lt;/code&gt;&lt;/a&gt; from learning the language to entering the &lt;a href="https://dev.to/devteam/announcing-the-digitalocean-app-platform-hackathon-on-dev-2i1k"&gt;DigitalOcean App Platform Hackathon&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The app I built can be found on &lt;a href="https://github.com/JonJam/stock-checker" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Part 2 details the setup of my &lt;code&gt;golang&lt;/code&gt; development environment.&lt;/p&gt;

&lt;h1&gt;
  
  
  Setup &lt;code&gt;golang&lt;/code&gt;
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Install &lt;code&gt;golang&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;First step is to install &lt;code&gt;golang&lt;/code&gt; which can be done by following this &lt;a href="https://golang.org/doc/install" rel="noopener noreferrer"&gt;page&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create workspace
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;This step is not required when using &lt;a href="https://blog.golang.org/using-go-modules" rel="noopener noreferrer"&gt;modules&lt;/a&gt;. See the note on this &lt;a href="https://golang.org/doc/code.html" rel="noopener noreferrer"&gt;page&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Create a &lt;a href="https://golang.org/doc/gopath_code.html#Workspaces" rel="noopener noreferrer"&gt;workspace&lt;/a&gt; which results in the directory structure shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$HOME
    └── go
        ├── bin
        └── src
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Set environment variables
&lt;/h2&gt;

&lt;p&gt;Next I added the following environment variables to my &lt;code&gt;.zshrc&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export GOPATH=/Users/jonjam/go
export GOBIN=/Users/jonjam/go/bin
export PATH=$GOPATH/bin:$GOROOT/bin:$PATH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More information about these variables can be found &lt;a href="https://golang.org/doc/install/source#environment" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Setup Visual Studio Code
&lt;/h1&gt;

&lt;p&gt;There are plenty of &lt;a href="https://github.com/golang/go/wiki/IDEsAndTextEditorPlugins" rel="noopener noreferrer"&gt;choices&lt;/a&gt; when it comes to IDEs. I went with &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt;, since I was already familiar with it.&lt;/p&gt;

&lt;p&gt;This section details configuring it for &lt;code&gt;golang&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install and activate Go extension
&lt;/h2&gt;

&lt;p&gt;First to add &lt;code&gt;golang&lt;/code&gt; support to VS Code, install the &lt;a href="https://marketplace.visualstudio.com/items?itemName=golang.Go" rel="noopener noreferrer"&gt;Go extension&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After installing the extension, there are some additional command line tools that also need to be installed to support the extension. This can be done by following this &lt;a href="https://github.com/golang/vscode-go#activate-the-go-extension" rel="noopener noreferrer"&gt;page&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure formatting tool
&lt;/h2&gt;

&lt;p&gt;The default formatting tool in VS Code is &lt;a href="https://github.com/sqs/goreturns" rel="noopener noreferrer"&gt;&lt;code&gt;goreturns&lt;/code&gt;&lt;/a&gt; which doesn't work with modules (see &lt;a href="https://github.com/golang/vscode-go/blob/master/docs/modules.md" rel="noopener noreferrer"&gt;here&lt;/a&gt; for more information).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pkg.go.dev/golang.org/x/tools/cmd/goimports" rel="noopener noreferrer"&gt;&lt;code&gt;goimports&lt;/code&gt;&lt;/a&gt; on the other hand does support modules. &lt;/p&gt;

&lt;p&gt;Change the &lt;code&gt;go.formatTool&lt;/code&gt; setting to &lt;code&gt;goimports&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enable the Go language server
&lt;/h2&gt;

&lt;p&gt;Besides formatting, some of the other helper tools do not support modules (see &lt;a href="https://github.com/golang/vscode-go/blob/master/docs/modules.md" rel="noopener noreferrer"&gt;here&lt;/a&gt; for more information).&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/golang/tools/tree/master/gopls" rel="noopener noreferrer"&gt;Go language server&lt;/a&gt; should be used; this can be enabled by following these &lt;a href="https://github.com/golang/vscode-go/blob/master/docs/gopls.md#enable-the-language-server" rel="noopener noreferrer"&gt;steps&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Setup debugging
&lt;/h2&gt;

&lt;p&gt;Debugging &lt;code&gt;golang&lt;/code&gt; apps in VS Code is provided by &lt;a href="https://github.com/go-delve/delve" rel="noopener noreferrer"&gt;Delve&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Follow these &lt;a href="https://github.com/golang/vscode-go/blob/master/docs/debugging.md" rel="noopener noreferrer"&gt;steps&lt;/a&gt; to install and configure Delve.&lt;/p&gt;

&lt;h1&gt;
  
  
  Setup linter
&lt;/h1&gt;

&lt;p&gt;Now that VS Code is setup, the final step is to choose a linter to assist with development.&lt;/p&gt;

&lt;p&gt;I chose &lt;a href="https://github.com/golangci/golangci-lint" rel="noopener noreferrer"&gt;&lt;code&gt;golangci-lint&lt;/code&gt;&lt;/a&gt;. It is a fast go linters runner that is capable of running multiple linters in parralel. &lt;/p&gt;

&lt;p&gt;I discovered this on &lt;a href="https://github.com/golang/go/wiki/CodeTools" rel="noopener noreferrer"&gt;golang wiki&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;golangci-lint&lt;/code&gt; can be installed locally by following these &lt;a href="https://golangci-lint.run/usage/install/#local-installation" rel="noopener noreferrer"&gt;steps&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure linting tool
&lt;/h2&gt;

&lt;p&gt;To integrate &lt;code&gt;golangci-lint&lt;/code&gt; with VS Code, it is just a matter of changing a couple of &lt;a href="https://golangci-lint.run/usage/integrations/" rel="noopener noreferrer"&gt;settings&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create configuration file
&lt;/h2&gt;

&lt;p&gt;The final step is to create a &lt;code&gt;.golangci.yml&lt;/code&gt; for the project which defines what linters are enabled. This can also be used to enable/disable specific rules.&lt;/p&gt;

&lt;p&gt;Below is the &lt;code&gt;.golangci.yml&lt;/code&gt; file from stock-checker app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;linters:
  disable-all: true
  enable:
    # Default enabled linters
    - deadcode
    - errcheck
    - gosimple
    - govet
    - ineffassign
    - staticcheck
    - structcheck
    - typecheck
    - varcheck

    # Added
    # goimports aligns with VS Code setup and includes gofmt
    - goimports
    - golint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Next
&lt;/h1&gt;

&lt;p&gt;That's it for this post. &lt;/p&gt;

&lt;p&gt;In part 3, I will talk about developing the project.&lt;/p&gt;

</description>
      <category>dohackathon</category>
      <category>go</category>
      <category>docker</category>
      <category>digitalocean</category>
    </item>
    <item>
      <title>Part 1: Background and learning golang</title>
      <dc:creator>Jonathan Harrison</dc:creator>
      <pubDate>Sat, 26 Dec 2020 11:50:13 +0000</pubDate>
      <link>https://dev.to/jonjam/my-golang-journey-part-1-background-and-learning-golang-23ni</link>
      <guid>https://dev.to/jonjam/my-golang-journey-part-1-background-and-learning-golang-23ni</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;The cover image is from &lt;a href="https://github.com/MariaLetta/free-gophers-pack" rel="noopener noreferrer"&gt;MariaLetta/free-gophers-pack&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This post is part of a series detailing my journey with &lt;a href="https://golang.org/" rel="noopener noreferrer"&gt;&lt;code&gt;golang&lt;/code&gt;&lt;/a&gt; from learning the language to entering the &lt;a href="https://dev.to/devteam/announcing-the-digitalocean-app-platform-hackathon-on-dev-2i1k"&gt;DigitalOcean App Platform Hackathon&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The app I built can be found on &lt;a href="https://github.com/JonJam/stock-checker" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Part 1 details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How I came up with the project idea&lt;/li&gt;
&lt;li&gt;How I learnt golang&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Background
&lt;/h1&gt;

&lt;p&gt;In August 2020, I wanted to start a new side project and learn something new. &lt;/p&gt;

&lt;p&gt;At the top of my backlog was &lt;a href="https://golang.org/" rel="noopener noreferrer"&gt;&lt;code&gt;golang&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the following months, I completed online courses and read blogs, guides, language specification etc to get familiar with the language.&lt;/p&gt;

&lt;p&gt;At the beginning of December, I thought it was about time I built something. &lt;/p&gt;

&lt;p&gt;At the same time, my youngest brother who had missed getting a &lt;a href="https://www.xbox.com/en-GB/consoles/xbox-series-x" rel="noopener noreferrer"&gt;Xbox Series X&lt;/a&gt; on the UK launch day decided that he wanted one. What transpired was every time a UK retailer potentially had a re-stock, my two brothers and I would be checking their website throughout the day to try buy one; only to fail every time.&lt;/p&gt;

&lt;p&gt;Then I saw this &lt;a href="https://dev.to/marisayou/how-to-get-a-playstation-5-when-it-s-always-out-of-stock-5d4i"&gt;DEV post&lt;/a&gt;. This inspired me to build an app in &lt;code&gt;golang&lt;/code&gt; that would check various UK retailers for a Xbox Series X and notify my brothers and I when available.&lt;/p&gt;

&lt;p&gt;Lastly I saw that &lt;a href="https://www.digitalocean.com/" rel="noopener noreferrer"&gt;DigitalOcean&lt;/a&gt; were hosting a &lt;a href="https://dev.to/devteam/announcing-the-digitalocean-app-platform-hackathon-on-dev-2i1k"&gt;Hackathon&lt;/a&gt;. This provided an opportunity to also get experience working with a cloud platform I hadn't used before and more importantly, gave me a deadline to work to.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This series details the journey of building the stock-checker app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Part 1 - Learning &lt;code&gt;golang&lt;/code&gt; (this one)&lt;/li&gt;
&lt;li&gt;Part 2 - Development environment&lt;/li&gt;
&lt;li&gt;Part 3 - Developing the stock-checker app&lt;/li&gt;
&lt;li&gt;Part 4 - Deploying to DigitalOcean&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Learning golang
&lt;/h1&gt;

&lt;p&gt;How I tend to learn something new is by trying to expose myself to as much information as possible from a variety of sources, even if I can't remember it all. &lt;/p&gt;

&lt;p&gt;This is so that when I start building something I do it the right way and also, it gives me mental "hints" to further lookup things during development.&lt;/p&gt;

&lt;p&gt;With that, here are the resources I used to learn &lt;code&gt;golang&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Online courses
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.udemy.com/share/1013gw/" rel="noopener noreferrer"&gt;Learn How To Code: Google's Go (golang) Programming Language&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.udemy.com/share/102aeq/" rel="noopener noreferrer"&gt;Master Go (Golang) Programming:The Complete Go Bootcamp 2021&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.udemy.com/share/101YFa/" rel="noopener noreferrer"&gt;Go Bootcamp: Master Golang with 1000+ Exercises and Projects &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.udemy.com/share/101XnU/" rel="noopener noreferrer"&gt;Go: The Complete Developer's Guide (Golang)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.udemy.com/share/101Xnq/" rel="noopener noreferrer"&gt;Web Development w/ Google’s Go (golang) Programming Language&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Documentation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  golang.org
&lt;/h3&gt;

&lt;p&gt;Particular areas of &lt;a href="https://golang.org/" rel="noopener noreferrer"&gt;golang.org&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tour.golang.org/welcome/1" rel="noopener noreferrer"&gt;A Tour of Go&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://golang.org/doc/code.html" rel="noopener noreferrer"&gt;How to write Go Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://golang.org/ref/spec" rel="noopener noreferrer"&gt;Language Specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://golang.org/doc/effective_go.html" rel="noopener noreferrer"&gt;Effective Go&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Go Blog
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://blog.golang.org/" rel="noopener noreferrer"&gt;Go Blog&lt;/a&gt; has many posts going into depth about aspects of the language.&lt;/p&gt;

&lt;h3&gt;
  
  
  golang/go GitHub
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://github.com/golang/go" rel="noopener noreferrer"&gt;GitHub Wiki&lt;/a&gt; contains many resources for every aspect of programming with &lt;code&gt;golang&lt;/code&gt; from learning, tools and libraries.&lt;/p&gt;

&lt;h3&gt;
  
  
  go.dev
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://go.dev/" rel="noopener noreferrer"&gt;go.dev&lt;/a&gt; details the different use cases of &lt;code&gt;golang&lt;/code&gt; such as cloud and network services as well as companies using it for that purpose and libraries/frameworks to consider.&lt;/p&gt;

&lt;h1&gt;
  
  
  Next
&lt;/h1&gt;

&lt;p&gt;That's it for this post. &lt;/p&gt;

&lt;p&gt;In part 2, I will talk about my development environment.&lt;/p&gt;

</description>
      <category>dohackathon</category>
      <category>go</category>
      <category>docker</category>
      <category>digitalocean</category>
    </item>
  </channel>
</rss>
