<?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: Doug Bridgens</title>
    <description>The latest articles on DEV Community by Doug Bridgens (@daunderworks).</description>
    <link>https://dev.to/daunderworks</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%2F1282789%2F9489a7e3-f198-4845-a18b-a666922ffba2.jpeg</url>
      <title>DEV Community: Doug Bridgens</title>
      <link>https://dev.to/daunderworks</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/daunderworks"/>
    <language>en</language>
    <item>
      <title>How I use git on multiple devices</title>
      <dc:creator>Doug Bridgens</dc:creator>
      <pubDate>Mon, 20 May 2024 14:43:03 +0000</pubDate>
      <link>https://dev.to/daunderworks/how-i-use-git-on-multiple-devices-2nbm</link>
      <guid>https://dev.to/daunderworks/how-i-use-git-on-multiple-devices-2nbm</guid>
      <description>&lt;p&gt;In the last few years I've been using &lt;a href="https://workingcopy.app"&gt;Working Copy&lt;/a&gt; on iPhone/iPad to refine README, code comments, and func/var naming, as part of feature development.&lt;/p&gt;

&lt;p&gt;Doing this on devices without the ability to change code has been a big quality driver for me. The draw back is a few unconventional commits at the end of a feature branch.&lt;/p&gt;

&lt;p&gt;The commit history can easily get messy, with &lt;code&gt;checkpoint&lt;/code&gt; or the evil twins &lt;code&gt;typo&lt;/code&gt; and &lt;code&gt;update&lt;/code&gt;. Passing temporary changes on my feature branch, up into GitHub, so I can view changes on multiple devices gets a little ugly.&lt;/p&gt;

&lt;p&gt;I posted a &lt;a href="https://dev.to/daunderworks/how-to-use-git-between-devices-3ggo"&gt;question&lt;/a&gt; about how others have solved this, about what process others use. &lt;a class="mentioned-user" href="https://dev.to/ridays2001"&gt;@ridays2001&lt;/a&gt; gave a good solution 👍&lt;/p&gt;

&lt;h3&gt;
  
  
  TL;DR
&lt;/h3&gt;

&lt;p&gt;Create a throw away WIP branch from the feature branch, and pull the changes back to your feature branch with a &lt;code&gt;merge --squash&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; kitchen_orders
  &lt;span class="c"&gt;# do main code changes&lt;/span&gt;

&lt;span class="c"&gt;# setup your wip branch and make accessible&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; wip_kitchen_orders
&lt;span class="nv"&gt;$ &lt;/span&gt;git push &lt;span class="nt"&gt;--set-upstream&lt;/span&gt; origin wip_kitchen_orders
  &lt;span class="c"&gt;# do minor updates and changes on this branch&lt;/span&gt;

&lt;span class="c"&gt;# pull wip changes back onto feature branch&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;git checkout kitchen_orders
&lt;span class="nv"&gt;$ &lt;/span&gt;git merge &lt;span class="nt"&gt;--squash&lt;/span&gt; wip_kitchen_orders

&lt;span class="c"&gt;# commit the wip changes as one&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;git commit &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s1"&gt;'improves godoc comments'&lt;/span&gt;

&lt;span class="c"&gt;# clean up wip branch&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;git push origin &lt;span class="nt"&gt;--delete&lt;/span&gt; wip_kitchen_orders
&lt;span class="nv"&gt;$ &lt;/span&gt;git branch &lt;span class="nt"&gt;-D&lt;/span&gt; wip_kitchen_orders
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These can all be aliased within the shell.&lt;/p&gt;

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

&lt;p&gt;Here's a summary write-up showing what you need to know, for multi-device changes with a clean git history.&lt;/p&gt;

&lt;p&gt;I'm building out my fictional restaurant app, and want to add store and retrieve kitchen orders.&lt;/p&gt;

&lt;p&gt;A feature branch for this change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;checkout &lt;span class="nt"&gt;-b&lt;/span&gt; kitchen_orders
Switched to a new branch &lt;span class="s1"&gt;'kitchen_orders'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I do a bunch of changes as normal, and end up with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git log &lt;span class="nt"&gt;--oneline&lt;/span&gt;
a45fa9e &lt;span class="o"&gt;(&lt;/span&gt;HEAD -&amp;gt; kitchen_orders, origin/kitchen_orders&lt;span class="o"&gt;)&lt;/span&gt; adds kitchen order datastore
2f77e62 adds swp to gitignore
4491447 adds fsvault external package
80df0ca adds config pkg to kitchen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before I create a PR to merge back to &lt;code&gt;develop&lt;/code&gt;, I want to read through the changes and make sure I will understand it in three months time. In a team environment this helps make PR reviews easier too.&lt;/p&gt;

&lt;p&gt;So I create a WIP branch from my feature branch, and push that to Github.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; wip_kitchen_orders          
Switched to a new branch &lt;span class="s1"&gt;'wip_kitchen_orders'&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;git push &lt;span class="nt"&gt;--set-upstream&lt;/span&gt; origin wip_kitchen_orders
branch &lt;span class="s1"&gt;'wip_kitchen_orders'&lt;/span&gt; &lt;span class="nb"&gt;set &lt;/span&gt;up to track &lt;span class="s1"&gt;'origin/wip_kitchen_orders'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This WIP branch is for messy commits, and is used to push and pull changes between devices (iPhone, iPad). It lets me do my own code review in a cafe with a pastry, in a civilised way 😁&lt;/p&gt;

&lt;p&gt;For me, this code review process is almost more important than the actual code. It's really a review of how understandable I'm leaving things for my future self.&lt;/p&gt;

&lt;p&gt;Depending on the feature/change I may make many updates to improve the readability over a few days, with whatever device I have to hand.&lt;/p&gt;

&lt;p&gt;Typically this is what I end up with. A bunch of meaningless commit messages, but essential refinements underneath.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git log &lt;span class="nt"&gt;--oneline&lt;/span&gt;
a04d7a6 &lt;span class="o"&gt;(&lt;/span&gt;HEAD -&amp;gt; wip_kitchen_orders&lt;span class="o"&gt;)&lt;/span&gt; typo
dadc12a stupid typo
8c0dd3c updates &lt;span class="k"&gt;function &lt;/span&gt;godoc comments
a45fa9e &lt;span class="o"&gt;(&lt;/span&gt;origin/wip_kitchen_orders, origin/kitchen_orders, kitchen_orders&lt;span class="o"&gt;)&lt;/span&gt; adds kitchen order datastore
2f77e62 adds swp to gitignore
4491447 adds fsvault external package
80df0ca adds config pkg to kitchen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now for the magic. I can pull these WIP changes back into my feature branch without a merge commit. &lt;/p&gt;

&lt;p&gt;I switch back to my feature branch, and check it's clean.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git checkout kitchen_orders
Switched to branch &lt;span class="s1"&gt;'kitchen_orders'&lt;/span&gt;
Your branch is up to &lt;span class="nb"&gt;date &lt;/span&gt;with &lt;span class="s1"&gt;'origin/kitchen_orders'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;git status
On branch kitchen_orders
Your branch is up to &lt;span class="nb"&gt;date &lt;/span&gt;with &lt;span class="s1"&gt;'origin/kitchen_orders'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;

nothing to commit, working tree clean
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I can merge my WIP branch with &lt;code&gt;squash&lt;/code&gt; to pull over the changes without any commit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git merge &lt;span class="nt"&gt;--squash&lt;/span&gt; wip_kitchen_orders
Updating a45fa9e..a04d7a6
Fast-forward
Squash commit &lt;span class="nt"&gt;--&lt;/span&gt; not updating HEAD
 kitchen/internal/datastore/order.go | 4 +++-
 1 file changed, 3 insertions&lt;span class="o"&gt;(&lt;/span&gt;+&lt;span class="o"&gt;)&lt;/span&gt;, 1 deletion&lt;span class="o"&gt;(&lt;/span&gt;-&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;git status
On branch kitchen_orders
Your branch is up to &lt;span class="nb"&gt;date &lt;/span&gt;with &lt;span class="s1"&gt;'origin/kitchen_orders'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;

Changes to be committed:
  &lt;span class="o"&gt;(&lt;/span&gt;use &lt;span class="s2"&gt;"git restore --staged &amp;lt;file&amp;gt;..."&lt;/span&gt; to unstage&lt;span class="o"&gt;)&lt;/span&gt;
    modified:   kitchen/internal/datastore/order.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This leaves me able to apply these WIP changes as a single commit on my feature branch, as if the WIP branch never existed.&lt;/p&gt;

&lt;p&gt;So I end up with a long term git log graph like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;gl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'git log --graph --pretty="%C(green) %d %C(red)Date: %ad %C(yellow) %s" --date=human'&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;gl
&lt;span class="k"&gt;*&lt;/span&gt;     &lt;span class="o"&gt;(&lt;/span&gt;HEAD -&amp;gt; develop, origin/develop, origin/HEAD&lt;span class="o"&gt;)&lt;/span&gt; Date: 27 seconds ago  Merge pull request &lt;span class="c"&gt;#5 from thisdougb/kitchen_orders&lt;/span&gt;
|&lt;span class="se"&gt;\ &lt;/span&gt; 
| &lt;span class="k"&gt;*&lt;/span&gt;   &lt;span class="o"&gt;(&lt;/span&gt;origin/kitchen_orders, kitchen_orders&lt;span class="o"&gt;)&lt;/span&gt; Date: 82 seconds ago  improves datastore godoc comments
| &lt;span class="k"&gt;*&lt;/span&gt;   Date: 33 minutes ago  adds kitchen order datastore
| &lt;span class="k"&gt;*&lt;/span&gt;   Date: 52 minutes ago  adds swp to gitignore
| &lt;span class="k"&gt;*&lt;/span&gt;   Date: 54 minutes ago  adds fsvault external package
| &lt;span class="k"&gt;*&lt;/span&gt;   Date: 80 minutes ago  adds config pkg to kitchen
|/  
&lt;span class="k"&gt;*&lt;/span&gt;     Date: Wed May 1 13:57  Merge pull request &lt;span class="c"&gt;#4 from thisdougb/thisdougb-patch-1&lt;/span&gt;
|&lt;span class="se"&gt;\ &lt;/span&gt; 
| &lt;span class="k"&gt;*&lt;/span&gt;   Date: Wed May 1 13:57  Update README.md
|/  
&lt;span class="k"&gt;*&lt;/span&gt;     Date: Wed May 1 13:55  Merge pull request &lt;span class="c"&gt;#3 from thisdougb/part1b&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And remember to clean up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git push origin &lt;span class="nt"&gt;--delete&lt;/span&gt; wip_kitchen_orders
To github.com:thisdougb/go-eat.git
 - &lt;span class="o"&gt;[&lt;/span&gt;deleted]         wip_kitchen_orders
&lt;span class="nv"&gt;$ &lt;/span&gt;git branch &lt;span class="nt"&gt;-D&lt;/span&gt; wip_kitchen_orders
Deleted branch wip_kitchen_orders &lt;span class="o"&gt;(&lt;/span&gt;was a04d7a6&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The nice thing about this method is that it's really simple. I can create a WIP branch when I need to, within Working Copy or from another device.&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>programming</category>
      <category>bash</category>
    </item>
    <item>
      <title>How do you use git between devices?</title>
      <dc:creator>Doug Bridgens</dc:creator>
      <pubDate>Tue, 07 May 2024 09:55:31 +0000</pubDate>
      <link>https://dev.to/daunderworks/how-to-use-git-between-devices-3ggo</link>
      <guid>https://dev.to/daunderworks/how-to-use-git-between-devices-3ggo</guid>
      <description>&lt;p&gt;If I'm working on a feature, on a branch, I struggle to do that while working 'remotely'.&lt;/p&gt;

&lt;p&gt;When I work remotely I don't mean from the kitchen table, in a wfh style. My use-case is doing a couple of hours on a desktop at home, then maybe continuing on the train with a laptop as I travel to see some friends. Or going away for a few days and doing a couple of hours coding every morning.&lt;/p&gt;

&lt;p&gt;I also use git history to keep track of progress, or track down rogue commits. So trying to maintain meaningful (one line) commit messages is important.&lt;/p&gt;

&lt;p&gt;Through the years I've never found a good way to maintain a meaningful git history, while updating code on different devices. It's often too impractical to commit fully worked features, because I'm not actually finished yet but have to go off somewhere.&lt;/p&gt;

&lt;p&gt;I've effectively created a &lt;em&gt;desire line&lt;/em&gt;, by committing and pushing whatever I have as a &lt;code&gt;checkpoint&lt;/code&gt; so I can continue the work on another device.&lt;/p&gt;

&lt;p&gt;For example. I'm adding a new function as part of a feature, at home on a desktop. I don't quite have time to get it finished before I have to leave for a meeting. But I can use the sixty minute train ride to finish the work, using my laptop.&lt;/p&gt;

&lt;p&gt;I'd just commit what's done with a message of &lt;code&gt;checkpoint&lt;/code&gt;. Then twenty minutes later pull changes on the branch from my laptop, and finish it off.&lt;/p&gt;

&lt;p&gt;The ideal would be a git log of commits with the checkpoint (or transfer) commits removed, while meaningful commits remain.&lt;/p&gt;

&lt;p&gt;But I'm interested to know if anyone else has a better method?&lt;/p&gt;

&lt;p&gt;What I'm thinking is somehow automating the following, maybe via an alias function or git hook.&lt;/p&gt;

&lt;p&gt;Current state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git log &lt;span class="nt"&gt;--oneline&lt;/span&gt;      
9ac99be &lt;span class="o"&gt;(&lt;/span&gt;HEAD -&amp;gt; feature&lt;span class="o"&gt;)&lt;/span&gt; meaningful 2
1736b60 checkpoint
5de2fe5 meaningful 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Move &lt;code&gt;HEAD&lt;/code&gt; back to the last meaningful commit, but leaving the code intact:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git reset &lt;span class="nt"&gt;--soft&lt;/span&gt; HEAD~2

&lt;span class="nv"&gt;$ &lt;/span&gt;git log &lt;span class="nt"&gt;--oneline&lt;/span&gt;      
5de2fe5 &lt;span class="o"&gt;(&lt;/span&gt;HEAD -&amp;gt; feature&lt;span class="o"&gt;)&lt;/span&gt; meaningful 1
9679310 &lt;span class="o"&gt;(&lt;/span&gt;develop&lt;span class="o"&gt;)&lt;/span&gt; initial commit

&lt;span class="nv"&gt;$ &lt;/span&gt;git status             
On branch feature
Changes to be committed:
  &lt;span class="o"&gt;(&lt;/span&gt;use &lt;span class="s2"&gt;"git restore --staged &amp;lt;file&amp;gt;..."&lt;/span&gt; to unstage&lt;span class="o"&gt;)&lt;/span&gt;
    modified:   main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now commit the next change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s1"&gt;'meaningful 2'&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;feature 6fd925e] meaningful 2

&lt;span class="nv"&gt;$ &lt;/span&gt;git log &lt;span class="nt"&gt;--oneline&lt;/span&gt;           
6fd925e &lt;span class="o"&gt;(&lt;/span&gt;HEAD -&amp;gt; feature&lt;span class="o"&gt;)&lt;/span&gt; meaningful 2
5de2fe5 meaningful 1
9679310 &lt;span class="o"&gt;(&lt;/span&gt;develop&lt;span class="o"&gt;)&lt;/span&gt; initial commit

&lt;span class="nv"&gt;$ &lt;/span&gt;git diff 6fd925e~       
diff &lt;span class="nt"&gt;--git&lt;/span&gt; a/main.go b/main.go
index f33752a..8e3863f 100644
&lt;span class="nt"&gt;---&lt;/span&gt; a/main.go
+++ b/main.go
@@ &lt;span class="nt"&gt;-4&lt;/span&gt;,4 +4,6 @@ import &lt;span class="s2"&gt;"fmt"&lt;/span&gt;

 func main&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        fmt.Println&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"meaningful 1"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
+       fmt.Println&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"checkpoint"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
+       fmt.Println&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"meaningful 2"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>programming</category>
      <category>git</category>
      <category>discuss</category>
      <category>help</category>
    </item>
    <item>
      <title>Building a monolith in Go - Layout</title>
      <dc:creator>Doug Bridgens</dc:creator>
      <pubDate>Wed, 01 May 2024 12:45:43 +0000</pubDate>
      <link>https://dev.to/daunderworks/building-a-monolith-in-go-layout-1no8</link>
      <guid>https://dev.to/daunderworks/building-a-monolith-in-go-layout-1no8</guid>
      <description>&lt;p&gt;&lt;em&gt;I'm building a reasonably sized commercial app in Go. By sharing decisions and choices (using a fictional restaurant app) I am hoping it may solicit advice from better developers, possibly help others, and generally force me to reflect when writing it out.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The crucial point in scaling a business is when you hire enough developers to be able to specialise effort. That jump from one, two, or five developers working on a shared code-base to multiple teams with their own responsibilities.&lt;/p&gt;

&lt;p&gt;This is the moment you have to completely refactor the spaghetti, or have an easy ride to a service architecture. Or probably somewhere in between. Having done this many times I think it's possible to have an easy ride, if and when the time comes to scaling.&lt;/p&gt;

&lt;p&gt;Go's package system is very simple, and really suits separated concerns in distinct repo's. However, the practicality of continuously updating local copies becomes a real drag for a small startup. Deploying and versioning multiple services adds to the friction and cost.&lt;/p&gt;

&lt;p&gt;By contrast, a monolith is simple. Particularly with Go packages, a monolith layout can emulate a service architecture. If this is possible then we gain simple deployments and a simple dev process.&lt;/p&gt;

&lt;p&gt;The key is to build and layout the monolith in a way that makes it easy to extract components later on. While avoiding the (process, infrastructure) overhead of a micro-service architecture from day one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Package Layout
&lt;/h3&gt;

&lt;p&gt;My fictional restaurant Go Eat has five principle areas of concern. So I create a top-level package for each:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./goeat/booking
./goeat/menu
./goeat/staff
./goeat/service
./goeat/kitchen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have tried so many layouts that I now realise layout is not strong enough on its own to force good habits. It would be very easy to create a mess from this layout, with deep calls between packages that are hard to unpick.&lt;/p&gt;

&lt;h3&gt;
  
  
  Domain Boundaries
&lt;/h3&gt;

&lt;p&gt;One of the nasties that hits companies when the try and scale is &lt;em&gt;monolith mess&lt;/em&gt;. Code execution paths that are so intertwined that the pragmatic path to scale is rewriting the app. That's a costly way of scaling, but at least you know what it &lt;em&gt;should&lt;/em&gt; do.&lt;/p&gt;

&lt;p&gt;I want to avoid creating this mess by using Go's &lt;code&gt;internal&lt;/code&gt; package qualifier. For example, when writing functions in &lt;code&gt;kitchen&lt;/code&gt; I want to prevent dipping into &lt;code&gt;staff&lt;/code&gt; because it's convenient.&lt;/p&gt;

&lt;p&gt;Go's package export method (capitalised type names) is too lax for my case, at the service/domain/parent-package level. I want to protect package exports inter-service, but still use them intra-service.&lt;/p&gt;

&lt;p&gt;To solve this I am using a clearly named &lt;code&gt;publicapi.go&lt;/code&gt; file at the top of each (service) package tree. And by protecting all other functions under &lt;code&gt;internal&lt;/code&gt; I have enforcement.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./goeat/kitchen/
./goeat/kitchen/internal/rota.go

./goeat/staff/
./goeat/staff/publicapi.go
./goeat/staff/internal/calendar/main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My &lt;code&gt;kitchen&lt;/code&gt; service needs to update its &lt;code&gt;rota&lt;/code&gt; from the &lt;code&gt;calendar&lt;/code&gt; package in the &lt;code&gt;staff&lt;/code&gt; service. So I create a public API route into &lt;code&gt;staff&lt;/code&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// ./goeat/staff/publicapi.go&lt;/span&gt;

&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;staff&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"goeat/staff/internal/calendar"&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;GetKitchenRota&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;calendar&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRota&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"kitchen"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's really just a local version of an http API. Which will make a future transition easier to do.&lt;/p&gt;

&lt;p&gt;And calling this from the &lt;code&gt;kitchen&lt;/code&gt; service looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// ./goeat/kitchen/internal/rota.go&lt;/span&gt;

&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;rota&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"goeat/staff"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;fetchRota&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;rota&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;staff&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetKitchenRota&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// do something with the rota&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rota&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A readability bonus is the name of the import being &lt;code&gt;staff&lt;/code&gt;. The domain is much clearer than using a sub-package export, which would result in &lt;code&gt;calendar.GetRota&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing
&lt;/h3&gt;

&lt;p&gt;Implementing the public 'api' methods as vars enables me to isolate testing between services.&lt;/p&gt;

&lt;p&gt;It is trivial to mock the responses from the &lt;code&gt;staff&lt;/code&gt; service, independently of that package. I haven't found a simpler way of doing this, every other way seems to involve code gymnastics with interfaces or channels. This particular use-case is emulating services within a monolith.&lt;/p&gt;

&lt;p&gt;A simple test example in the &lt;code&gt;kitchen&lt;/code&gt; service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;//go:build test&lt;/span&gt;

&lt;span class="c"&gt;// ./goeat/kitchen/internal/rota/main_test.go&lt;/span&gt;

&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;rota&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"goeat/staff"&lt;/span&gt;
    &lt;span class="s"&gt;"testing"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/stretchr/testify/assert"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestFetchRota&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;mockRota&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"A"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"B"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;mockStaffGetKitchenRota&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mockRota&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// override the 'api' response with our mock function&lt;/span&gt;
    &lt;span class="n"&gt;staff&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetKitchenRota&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mockStaffGetKitchenRota&lt;/span&gt;

    &lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fetchRota&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;mockRota&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"expect A, B"&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;In this way I can develop features in one service (&lt;code&gt;kitchen&lt;/code&gt;) without touching the code in other services. Fewer typos, fewer bugs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;My choice to develop an app as a monolith depends on a bunch of particular use-case factors.&lt;/p&gt;

&lt;p&gt;I think Go's inherent simplicity around packages make it easy to create domain boundaries. This makes the whole code-base simpler to think about.&lt;/p&gt;

&lt;p&gt;Simplicity is key, because with few (or solo) developers complexity kills motivation. Speed of development is often less about CPU cycles, and more about efficient process.&lt;/p&gt;

&lt;p&gt;A single deployable binary is easier to manage and test, at small scale. Although Go suits a discrete service-architecture, the overhead (repo's, ci, infra) is too much right now.&lt;/p&gt;

&lt;p&gt;The sample code is on GitHub, which I'll update as I go: &lt;a href="https://github.com/thisdougb/go-eat"&gt;Go Eat&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>programming</category>
    </item>
    <item>
      <title>Apple Mouse Design Fail Solved With Bash</title>
      <dc:creator>Doug Bridgens</dc:creator>
      <pubDate>Fri, 26 Apr 2024 09:28:59 +0000</pubDate>
      <link>https://dev.to/daunderworks/catch-that-apple-mouse-design-fail-38co</link>
      <guid>https://dev.to/daunderworks/catch-that-apple-mouse-design-fail-38co</guid>
      <description>&lt;p&gt;The Apple Mouse charging port is a weird design, because while charging the mouse it cannot be used. Aesthetics won out over user productivity.&lt;/p&gt;

&lt;p&gt;On the one hand I appreciate that one hour of charging time every few weeks is a marginal outage. Infrequent enough that the disruption is almost forgotten each time.&lt;/p&gt;

&lt;p&gt;Though on the other hand Mac OS is pretty unusable without a mouse. So my expensive iMac is bricked because the mouse has lies upside down with cable plugged into its belly. &lt;/p&gt;

&lt;p&gt;But hey, I can appreciate the nice aesthetic while I sit and watch it charging. Oh wait..there's no pop-up charging indicator either, so I just guess when it's 100% 😡&lt;/p&gt;

&lt;p&gt;And my mouse alway seems to run out of power when I'm fully into whatever I'm doing. Does this just happen to me?&lt;/p&gt;

&lt;p&gt;I just couldn't stand it anymore, so I built a battery level warning script that I run in the background. If the mouse or keyboard fall below 20% charge then I get a simple pop-up window. &lt;/p&gt;

&lt;p&gt;It's a gentle reminder to charge when I next leave my desk. No rush, no outage, because with 20% I still have a few days of battery left. I typically just plug it in when I finish that day.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/tomtastic"&gt;tomtastic&lt;/a&gt; made some good improvements to the inner workings. And we made sure only two pop-up reminders per day, which feels about right.&lt;/p&gt;

&lt;p&gt;It's a pretty simple bash script run from cron. Aesthetics may have won the design process at Apple, but we can solve the productivity problem it caused.&lt;/p&gt;

&lt;p&gt;It's on GitHub: &lt;a href="https://github.com/thisdougb/MagicPowerAlert"&gt;MagicPowerAlert&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I got a pop-up this morning, which reminded me of the script running quietly in the background. And I am out for a meeting this afternoon, so I'll plug it in before I go. Whenever the pop-up appears it feels like a little win over poor design.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>automation</category>
      <category>bash</category>
    </item>
    <item>
      <title>Create GPS Test Data In Go</title>
      <dc:creator>Doug Bridgens</dc:creator>
      <pubDate>Wed, 24 Apr 2024 15:43:50 +0000</pubDate>
      <link>https://dev.to/daunderworks/create-gps-test-data-in-go-4of6</link>
      <guid>https://dev.to/daunderworks/create-gps-test-data-in-go-4of6</guid>
      <description>&lt;p&gt;Many of the functions I'm working on consume arrays of points (GPS tracks) &lt;code&gt;[]float64{lng,lat}&lt;/code&gt; on which to run statistical analysis. Individual tracks can have 50,000+ points, describing a real journey from A to B.&lt;/p&gt;

&lt;p&gt;Testing functions that process GPS tracks has been unexpectedly difficult. Test data of the form &lt;code&gt;[1.0,2.0]&lt;/code&gt;, to test logically is fine. But beyond that, I want to be able to test consistency in things like finding clusters, or coefficient breakpoints.&lt;/p&gt;

&lt;p&gt;For testing I'm not concerned with where the locations are on earth, but it's handy to be able to view tracks on a map for visual confirmation. So the coordinates need to, sort of, coordinate.&lt;/p&gt;

&lt;p&gt;I've created a function that generates semi-coherent GPS location data tracks. It's trivial to create a 5,000 data input track, designed to test something in particular. Being able to export it as geoJSON and view the shape on map is good for speedy intuition checks.&lt;/p&gt;

&lt;p&gt;I can setup the data to be skewed one way or another, or insert a cluster of weirdness. Particularly useful at larger data sizes, where one analysis method may be better than another.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func CreateGPSData() {

        line := orb.LineString{orb.Point{-3.188267, 55.953251}}

        // general direction and distance per point
        bearingRange := [2]int{Direction_SSE, Direction_SSW}
        distanceRange := [2]int{10 * 1000, 15 * 1000}

        // generate a test GPS track
        for range 5000 {
                newPoint := generateNewLocation(line[len(line)-1],
                        bearingRange,
                        distanceRange)
                line = append(line, newPoint)
        }

        // add skewness in the data
        bearingRange = [2]int{Direction_W, Direction_WNW}
        distanceRange = [2]int{1000, 1500}
        for range 100 {
                newPoint := generateNewLocation(line[len(line)-1],
                        bearingRange,
                        distanceRange)
                line = append(line, newPoint)
        }

        // do testing
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What doesn't seem to be too common is generating a new point, some distance away in a particular direction. The opposite of &lt;code&gt;point.DistanceTo(point2)&lt;/code&gt;. That's really all that the following function does.&lt;/p&gt;

&lt;p&gt;The breakdown starts with some readability. Using compass rose names really helps with reading the calling function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// the compass rose, naming format for readability
const (
        Direction_N = iota
        Direction_NNE
        Direction_NE
        Direction_ENE
        Direction_E
        Direction_ESE
        Direction_SE
        Direction_SSE
        Direction_S
        Direction_SSW
        Direction_SW
        Direction_WSW
        Direction_W
        Direction_WNW
        Direction_NW
        Direction_NNW
)

const (
        compassRoseDegrees = 22.5
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rather than simply a series of linear points, I want a GPS track that wiggles a little. So the parameters are a range of direction and distance, giving a small amount of randomness when calculating the next point.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// generateNewLocation returns a new point in the range of direction and
// distance. It is meant to build non-repetitive but predictable GPS tracks, to
// help generate test input cases.
//
// It's also meant to be readable code.
func generateNewLocation(start orb.Point, direction [2]int, distance [2]int) orb.Point {
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Possibly the most common &lt;em&gt;stupid&lt;/em&gt; mistakes I make are mixing up longitude and latitude indexes through typos. Named index values solve this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Mistakes with lon/lat indexing area easy to make, explicit index names
// helps
const (
    Longitude = 0
    Latitude  = 1
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I use the excellent &lt;a href="https://github.com/paulmach/orb"&gt;orb&lt;/a&gt; package to hide much of the GPS specific calculations. &lt;/p&gt;

&lt;p&gt;A point to note is that the Go maths package works in radians, so here's a helper function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var (
    latitudeOneDegreeOfDistance = 111000  // metres
    newPoint                    orb.Point // []float64{Long, Lat}

    // convert from degrees to radians
    deg2rad = func(d float64) float64 { return d * math.Pi / 180 }
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A while ago I went back to study maths, and have found lots of use for it. Here using trig to workout the distances on the ground.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; // Use trigonometry of a right angled triangle to solve the distances on the ground.
// The hypotenuse is our desired distance to travel,  and one angle
// is our desired bearing.
//
// now work out the vertical (longitude) and horizontal (latitude) sides in
// distance units.
hyp := (rand.Float64() * float64(distance[1]-distance[0])) + float64(distance[0])

// Get the compass bearing in degrees, with a little randomness between the
// general direction. Non-linear tracks are easier to troubleshoot visually.
bearingMin := float64(direction[0]) * 22.5
bearingMax := float64(direction[1]) * 22.5
angle := (rand.Float64() * (bearingMax - bearingMin)) + bearingMin

// Calulate the other side lengths using SOH CAH TOA. The Go math package
// works in radians
adj := math.Cos(deg2rad(angle)) * hyp // adjacent side of angle
opp := math.Sin(deg2rad(angle)) * hyp // opposite side of angle
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each degree change in latitude moves a fixed distance on the earth surface. So it is fairly simple to find the degree change we need to move our required distance on the ground.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Each degree change in every latitude equates to ~111 km on the ground. So
// now find the degree change required for the length of adj
latitudeDelta := (1.0 / float64(latitudeOneDegreeOfDistance)) * adj
newPoint[Latitude] = start[Latitude] + latitudeDelta
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Longitude 'distance moved on the earth surface' on the other hand changes depending on the corresponding latitude. &lt;/p&gt;

&lt;p&gt;First need to find what one longitude degree of travel is, at the current latitude. This is the longitude equivalent to &lt;code&gt;latitudeOneDegreeOfDistance = 111000&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Distance on the ground for each degree of longitude changes depending on
// latitude because the earth is not perfectly spherical. So we need to
// calculate the distance of one degree longitude for our current latitude.
p1 := orb.Point{1.0, start[Latitude]}
p2 := orb.Point{2.0, start[Latitude]}
longitudeOneDegreeOfDistance := geo.Distance(p1, p2) // returns metres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After which it's the same calculation to find the longitude degree required to move our required distance on the ground.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Now we can use this value to calculate the longitude degree change
// required to move opp distance (in a horizontal straight line) at this
// latitude.
longitudeDelta := (1.0 / longitudeOneDegreeOfDistance) * opp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have the new point, located roughly at the direction and distance required.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// The new point is a vertical and horizontal shift to arrive at hyp
// distance from the start point on the required bearing.
newPoint[Longitude] = start[Longitude] + longitudeDelta

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

&lt;/div&gt;



&lt;p&gt;To print the geoJSON object, which can be viewed using the &lt;a href="https://geojson.io"&gt;Mapbox&lt;/a&gt; viewer. The &lt;code&gt;geojson&lt;/code&gt; package is part of &lt;code&gt;orb&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fc := geojson.NewFeatureCollection()
f := geojson.NewFeature(line)
fc.Append(f)

rawJSON, _ := fc.MarshalJSON()
fmt.Println(string(rawJSON))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The geoJSON output, which can be dropped right into the &lt;a href="https://geojson.io"&gt;Mapbox viewer&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"features":[{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-3.188267,55.953251],[-3.135746915373942,55.86790144687853],[-3.1203515713600174,55.77480489552086],[-3.1047549460145367,55.67255911652837],[-3.157869557083529,55.55060126862168],[-3.228498091310353,55.43475670091365],[-3.2496225715462885,55.436812333443044],[-3.2661004391340707,55.439707415244094],[-3.2813563020992547,55.44313063594729],[-3.3021255075874594,55.44562577470332],[-3.319279338837285,55.446998922512265]]},"properties":{"stroke":"#b464d6","stroke-width":3}}],"type":"FeatureCollection"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full code is in a Gist &lt;a href="https://gist.github.com/thisdougb/e8da04a51947e5e5146c3fe5968a55f0"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>go</category>
      <category>programming</category>
      <category>datascience</category>
    </item>
    <item>
      <title>What Happens After Agile Dies?</title>
      <dc:creator>Doug Bridgens</dc:creator>
      <pubDate>Thu, 11 Apr 2024 17:20:36 +0000</pubDate>
      <link>https://dev.to/daunderworks/what-happens-after-agile-dies-bp8</link>
      <guid>https://dev.to/daunderworks/what-happens-after-agile-dies-bp8</guid>
      <description>&lt;p&gt;I started development in 1990, working on a Unix OS written in C. Our 'office' back then was called a lab, we revered old-timers who often only worked at night, and software patches were emailed as file diffs.&lt;/p&gt;

&lt;p&gt;Google, Linux, Amazon, cookies, wifi, laptops, etc, all yet to come. Peak excitement was DNS becoming popular and usable, enabling something called the World Wide Web.&lt;/p&gt;

&lt;p&gt;In the early days I worked on a product called SCAFS, which was a hardware cache between the OS and hard drives. It cached SQL queries, delivering above 20x performance. Very smart back in the days of SCSI ribbons.&lt;/p&gt;

&lt;p&gt;We were a diverse small team of hardware, systems, marketing, sales. There were bugs and features, and a general roadmap. We talked a lot with customers, consulting or at trade shows.&lt;/p&gt;

&lt;p&gt;Our product was autonomous, yet integrated with the OS dev team, high-performance server team, and external database companies. I think we shipped hardware/software rev's every three months, with bug fixes going out quicker (on magnetic tapes!).&lt;/p&gt;

&lt;p&gt;Looking back, it feels like I had an informal apprenticeship. I met so many different types of people and different ways of working on things. The people and culture taught me to be responsible, do what I commit to, and the importance of effective communication.&lt;/p&gt;

&lt;p&gt;Today I can easily recognise that we were a fast moving team that was able to work asynchronously. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The very definition of agile.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But we had no formal culture-framework imposed upon us. Rather than be infantilised and micro-managed, we were expected to be adults. Ideas and opinions travelled freely, whereas now they are siloed off (irony!) by dogmatic Kafka-esque processes. &lt;/p&gt;

&lt;p&gt;I despair at what business has done to the process of technology development. Agile is the business version of engineering process, sold to developers as self-empowerment. It is a cult that feeds on narcissism.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;More agile, less Agile.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These days, while I work solo on a web app, I think a lot about coding less. 11,000 lines-of-code probably has fewer bugs than 22,000? One risk of solo development is that your project is suffocated by time spent fixing bugs. There's a business motive here.&lt;/p&gt;

&lt;p&gt;Writing less code is of course quicker to do, but it requires spending more time on the engineering process. Better engineering means less code, means more engineering time, etc.&lt;/p&gt;

&lt;p&gt;I try to deploy weekly. My commitment to my future self is that I must include 'how to troubleshoot this feature' in the docs. My template is below.&lt;/p&gt;

&lt;p&gt;Monday to Thursday I work up my plan while walking or doing other things. Great ideas seem to surface when you least think about them. Long walks in interesting places seem to be the best place to rationalise thoughts.&lt;/p&gt;

&lt;p&gt;Friday to Sunday is when I code. When there are no design decisions left to make, coding is really easy and quick.&lt;/p&gt;

&lt;p&gt;This goes against Agile, against what many have only known. You can try it, and see what happens. Try a challenge from &lt;a href="https://adventofcode.com"&gt;AdventOfCode&lt;/a&gt;, spend a couple of days working up a plan first. Did you write a cleaner solution? Now extrapolate.&lt;/p&gt;

&lt;p&gt;This 80% thinking and 20% coding may seem counter-intuitive at first. But it results in faster development, lower operational costs, and higher motivation. It just changes things, when you realise good software development is not really about writing code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Maybe not today. Maybe not tomorrow, but soon and for the rest of your life.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This way of planning is not really anything new. It's centred on trying to communicate change more effectively. In my case, currently, I'm communicating with my future self via a walkthrough.&lt;/p&gt;

&lt;p&gt;My assumption is always that no-one spends time doing a literal code review any more. So if someone reviews my walkthrough, and I commit to writing code that reflects it, that's a proxy for a meaningful code review.&lt;/p&gt;

&lt;p&gt;However we do software post-Agile, businesses that ditch micro-management and embrace autonomous asynchronous work will make better software.&lt;/p&gt;

&lt;h3&gt;
  
  
  Template
&lt;/h3&gt;

&lt;p&gt;I created this template in the Things3 app on Mac and iOS. I wanted simple task management, that I could read and edit on the move. The ability to show tasks in a Home Screen widget is great for keeping scope limited.&lt;br&gt;
&lt;/p&gt;

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

What happened to make this worth doing.

#### Purpose

What functionality MUST be delivered.

#### Strategy

- High level decisions I will take to do this.

#### Thoughts

- Any context to decision making, peripheral things, knock-on's, etc.

#### Walkthrough

- [ ] First thing to do

A task description. Adding pseudo code lets me evolve function/var names and types as I build up the walkthrough tasks.

func loadData(id int64) account.Data {
    if !dataExists(id) {
        fetchData(id)
    }
    return getData(id)
}

- [ ] Second thing to do
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Of course I'm generalising about Agile. It's not all bad (hey future employers!). But we do lose something when business runs software development.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>agile</category>
      <category>programming</category>
      <category>discuss</category>
    </item>
  </channel>
</rss>
