{"title":"Drew De Ponte - A backstage perspective on running a company, building & selling apps | drewdeponte.com","subtitle":"Backstage access as I run a consultancy (https:\/\/uptechstudio.com) and build & sell apps.","link":[{"@attributes":{"rel":"self","type":"application\/atom+xml","href":"https:\/\/drewdeponte.com\/atom.xml"}},{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com"}}],"generator":"Zola","updated":"2026-01-31T00:00:00+00:00","id":"https:\/\/drewdeponte.com\/atom.xml","entry":[{"title":"The Git Patch Stack Methodology","published":"2026-01-31T00:00:00+00:00","updated":"2026-01-31T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/the-git-patch-stack-methodology\/"}},"id":"https:\/\/drewdeponte.com\/blog\/the-git-patch-stack-methodology\/","content":"<h2 id=\"tl-dr\">TL;DR<\/h2>\n<p>Modern Git workflows optimize for features, but best practices optimize for integration, review quality, and history.<\/p>\n<p>The Git Patch Stack Methodology replaces feature-centric pull requests with small, architectural patches stacked directly on top of mainline. Each patch is:<\/p>\n<ul>\n<li>Small and reviewable<\/li>\n<li>Continuously rebased and integrated<\/li>\n<li>Architecturally meaningful<\/li>\n<li>Historically valuable<\/li>\n<\/ul>\n<p>Development happens outside-in, while review and integration happen inside-out\u2014one patch at a time. The result is better reviews, fewer conflicts, cleaner history, and faster integration without abandoning pull requests.<\/p>\n<h3 id=\"a-note-on-tooling\">A Note on Tooling<\/h3>\n<p>This article describes a methodology, not a tool requirement.<\/p>\n<p>That said, some of the workflows described here\u2014tracking patch identity across rebases, managing patch stacks, and opening pull requests per patch\u2014introduce friction if done manually.<\/p>\n<p>To address that, the Git Patch Stack CLI exists.<\/p>\n<p>\ud83d\udc49 <a rel=\"external\" href=\"https:\/\/git-ps.sh\">https:\/\/git-ps.sh<\/a><\/p>\n<p>The CLI is designed to support this methodology directly on top of Git and existing platforms like GitHub and Bitbucket. It automates the mechanical work (branching, cherry-picking, tracking patch identity, updating pull requests) so developers can focus on creating high-quality patches and integrating continuously.<\/p>\n<p>The rest of this article explains why the methodology works. Tooling simply makes it practical at scale.<\/p>\n<hr \/>\n<h2 id=\"git-patch-stack-methodology\">Git Patch Stack Methodology<\/h2>\n<p>To truly understand the <strong>Git Patch Stack Methodology<\/strong>, it helps to start with how software changes are typically made.<\/p>\n<p>At a high level, most changes fall into one of two categories:<\/p>\n<ul>\n<li><strong>Large changes<\/strong> \u2014 new features or major system modifications<\/li>\n<li><strong>Small changes<\/strong> \u2014 bug fixes, copy updates, refactors, and minor improvements<\/li>\n<\/ul>\n<p>On the surface, this works fine if you don\u2019t care much about peer review quality, conflict reduction, architectural clarity, or the long\u2011term historical value of your code. In that world, all you need is a <em>feature branch<\/em>, following the workflow popularized by platforms like GitHub and Bitbucket.<\/p>\n<p>At best, a pull request created this way corresponds to a single, coherent change from the diagram above. At worst, it looks like a feature branch full of loosely related commits\u2014snapshots of half\u2011finished thoughts\u2014with little structure or narrative. The commits don\u2019t clearly explain <em>what<\/em> changed, <em>why<\/em> it changed, or how the changes relate to one another. Unfortunately, this pattern is extremely common.<\/p>\n<hr \/>\n<h2 id=\"best-practices\">Best Practices<\/h2>\n<p>So what should we actually be aiming for?<\/p>\n<h3 id=\"small-pull-requests\">Small Pull Requests<\/h3>\n<p>One widely accepted best practice is to keep pull requests small. The primary reason is to enable <em>high\u2011quality peer review<\/em>.<\/p>\n<p>When a pull request is large and overwhelming, reviewers tend to skim it and respond with a quick <strong>LGTM<\/strong>. We\u2019ve all done it. The result is a review that adds little value\u2014often no more than what an automated linter could have caught.<\/p>\n<p>Small pull requests, on the other hand, make it realistic for reviewers to:<\/p>\n<ul>\n<li>Understand the change in context<\/li>\n<li>Evaluate architectural decisions<\/li>\n<li>Reason carefully about correctness and edge cases<\/li>\n<\/ul>\n<h3 id=\"continuous-integration\">Continuous Integration<\/h3>\n<p>Another best practice is <em>continuous integration<\/em>\u2014not the tooling (GitHub Actions, CircleCI, etc.), but the behavior.<\/p>\n<p>Continuous integration means integrating changes into the mainline as quickly and as frequently as possible. Doing so:<\/p>\n<ul>\n<li>Dramatically reduces merge conflicts<\/li>\n<li>Makes conflicts easier to resolve when they do occur<\/li>\n<li>Narrows the scope of investigation when a bug is introduced<\/li>\n<\/ul>\n<p>Small, frequent integrations keep the system stable and make failures easier to reason about.<\/p>\n<h3 id=\"architecture-historical-value\">Architecture &amp; Historical Value<\/h3>\n<p>A third set of best practices concerns the long\u2011term readability and usefulness of your Git history. Ideally, each change should:<\/p>\n<ul>\n<li>Align with a specific architectural element<\/li>\n<li>Clearly describe <strong>what<\/strong> changed<\/li>\n<li>Explain <strong>why<\/strong> the change was necessary<\/li>\n<li>Indicate <strong>how<\/strong> the change was implemented<\/li>\n<\/ul>\n<p>When these conditions are met, your Git history becomes a form of documentation. You can understand <em>why<\/em> the system evolved the way it did, not just <em>what<\/em> the final state looks like. Each commit becomes a small, logical step toward a larger goal\u2014similar to showing your work in a math problem. This is often described as <em>proof of work<\/em>.<\/p>\n<hr \/>\n<h2 id=\"the-conflict\">The Conflict<\/h2>\n<p>When you try to apply these best practices while relying primarily on feature branches, tensions quickly emerge.<\/p>\n<h3 id=\"feature-branches-vs-continuous-integration\">Feature Branches vs. Continuous Integration<\/h3>\n<p>Branches exist to isolate work. Continuous integration exists to <em>avoid<\/em> isolation. By definition, the two concepts are in conflict.<\/p>\n<p>Teams often try to work around this by rebasing feature branches or repeatedly merging mainline back into them. Rebasing adds cognitive and operational overhead. Merging mainline repeatedly results in tangled Git histories that are difficult to understand or reason about.<\/p>\n<h3 id=\"feature-branches-vs-small-pull-requests\">Feature Branches vs. Small Pull Requests<\/h3>\n<p>Feature branches typically bundle an entire feature into a single pull request. The result is often a massive PR that discourages careful review and encourages superficial approval.<\/p>\n<h3 id=\"feature-branches-vs-historical-value\">Feature Branches vs. Historical Value<\/h3>\n<p>Finally, feature branches encourage developers to think in terms of <em>features<\/em>, not <em>architectural changes<\/em>. As a result, commit messages often lack clarity and historical usefulness. A feature is not an architectural unit; it is composed of many smaller architectural steps.<\/p>\n<hr \/>\n<h2 id=\"resolving-the-conflict\">Resolving the Conflict<\/h2>\n<p>To better align with these best practices, we need a different mental model\u2014one that emphasizes small, architectural changes and frequent integration.<\/p>\n<p>Our goals are straightforward:<\/p>\n<ul>\n<li><strong>Small Pull Requests<\/strong> \u2014 to enable meaningful peer review<\/li>\n<li><strong>Continuous Integration<\/strong> \u2014 to reduce conflicts and integration pain<\/li>\n<li><strong>Architectural &amp; Historical Value<\/strong> \u2014 to preserve a readable, valuable history<\/li>\n<\/ul>\n<h3 id=\"the-patch\">The Patch<\/h3>\n<p>Let\u2019s introduce a new unit of work: the <strong>patch<\/strong>.<\/p>\n<p>A <em>patch<\/em> is a single, logical, architecturally meaningful change that provides historical value. This terminology is intentional\u2014it echoes the original patch\u2011based workflows used in early open\u2011source development, and still used today by projects like Git and the Linux kernel.<\/p>\n<p>Conceptually, a patch maps directly to a well\u2011formed Git commit.<\/p>\n<h3 id=\"continuous-integration-without-feature-branches\">Continuous Integration Without Feature Branches<\/h3>\n<p>Instead of developing long\u2011lived feature branches, we develop directly on top of mainline.<\/p>\n<p>Local commits represent patches. As mainline advances, we continuously rebase our local patch stack on top of it. Git makes this easy:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>[pull]<\/span><\/span>\n<span class=\"giallo-l\"><span>    rebase = true<\/span><\/span><\/code><\/pre>\n<p>With this configuration, <code>git pull<\/code> automatically rebases your local patches onto the latest mainline, surfacing conflicts early\u2014when they\u2019re small and easy to fix.<\/p>\n<h3 id=\"the-stack\">The Stack<\/h3>\n<p>Rarely do we create just one patch. More often, we create a <strong>stack of patches<\/strong>, each building on the one below it.<\/p>\n<p>This stack lives locally on top of <code>origin\/main<\/code>. Later patches can depend on earlier ones, which mirrors how real features are developed. Git\u2019s interactive rebase allows us to:<\/p>\n<ul>\n<li>Reorder patches<\/li>\n<li>Edit them<\/li>\n<li>Split or squash them<\/li>\n<li>Drop them entirely<\/li>\n<\/ul>\n<p>Work\u2011in\u2011progress commits can be marked with a <code>WIP:<\/code> prefix and refined over time until they\u2019re ready for review.<\/p>\n<h3 id=\"pull-requests-as-patch-reviews\">Pull Requests as Patch Reviews<\/h3>\n<p>Pull requests still matter\u2014they\u2019re how we request review. But instead of representing <em>features<\/em>, they represent <em>patches<\/em>.<\/p>\n<p>A simple workflow looks like this:<\/p>\n<ol>\n<li>Create a branch from <code>origin\/main<\/code> named after the patch<\/li>\n<li>Cherry\u2011pick the patch onto that branch<\/li>\n<li>Push the branch and open a pull request<\/li>\n<\/ol>\n<p>This introduces some overhead, but it\u2019s manageable\u2014and automatable.<\/p>\n<h3 id=\"independent-vs-dependent-patches\">Independent vs. Dependent Patches<\/h3>\n<p>Independent patches can be reviewed and merged on their own.<\/p>\n<p>Dependent patches require more care. If Patch B depends on Patch A, then Patch A must be reviewed and merged first. This encourages an <strong>inside\u2011out review order<\/strong>, even if development happens outside\u2011in.<\/p>\n<p>In some cases, a <em>patch series<\/em>\u2014a pull request containing multiple dependent patches\u2014may be appropriate. This trades faster integration for convenience and should be used selectively.<\/p>\n<h3 id=\"updating-pull-requests\">Updating Pull Requests<\/h3>\n<p>When a reviewer requests changes, we don\u2019t add new commits\u2014we update the patch.<\/p>\n<p>Using interactive rebase, we modify the original commit and force\u2011push the updated branch. The pull request stays focused on a single logical change, even as it evolves.<\/p>\n<h3 id=\"tracking-patch-identity\">Tracking Patch Identity<\/h3>\n<p>Because rebasing changes commit hashes, we need a stable identifier. Each patch includes a UUID in its commit message:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>ps-id: &lt;uuid&gt;<\/span><\/span><\/code><\/pre>\n<p>This allows tooling to track patches across rebases, branches, and pull requests\u2014and to detect when a patch has diverged from its reviewed version.<\/p>\n<hr \/>\n<h2 id=\"development-outside-in-review-inside-out\">Development: Outside\u2011In, Review: Inside\u2011Out<\/h2>\n<p>Patch stacks naturally reflect dependency structure: foundational changes sit at the bottom, higher\u2011level behavior at the top.<\/p>\n<p>This pairs well with <strong>outside\u2011in development<\/strong>\u2014starting from the system boundary and letting requirements drive lower\u2011level interfaces. Early patches may be incomplete or marked as WIP.<\/p>\n<p>As inner layers solidify, outer patches are revisited and finalized. Review, however, happens <strong>inside\u2011out<\/strong>: dependencies first, consumers later.<\/p>\n<p>This rhythm\u2014<em>develop outside\u2011in, review inside\u2011out<\/em>\u2014is the heart of the Git Patch Stack Methodology.<\/p>\n"},{"title":"Hosting my Git Repositories","published":"2026-01-06T00:00:00+00:00","updated":"2026-01-06T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/hosting-my-git-repositories\/"}},"id":"https:\/\/drewdeponte.com\/blog\/hosting-my-git-repositories\/","content":"<p>For a long while now it has become more and more evident that you just can't\ntrust companies to host your data. This is true even if you are paying for said\nservices. This has become even more obvious with the introduction of large\nlanguage models, etc.<\/p>\n<p>Additionally, the products that exist in the space of Git hosting are less than\nideal in my opinion. Instead of them focusing on making Git hosting as good as\nit possibly could be. They are focused on building AI platforms that integrate\nwith all your Git repositories. Which I have zero interest in when I am paying\nfor Git hosting. Beyond that these companies are built on a software\ndevelopment model that is clearly less than ideal but they have no interest in\nrethinking that model.<\/p>\n<p>For these reasons and more I have decided it probably makes the most sense to\nsimply host all my Git repositories myself.<\/p>\n<p>The following are my notes around how I accomplished this so that if anyone\nelse wants to do the same they would hopefully be able to figure out.<\/p>\n<h2 id=\"what-we-need\">What we need<\/h2>\n<p>Basically, we need a server to host the repositories and run the software. You\ncan of course pay some VPS provider like Digital Ocean or you can run your own\nserver at your house or wherever you want. Note: If you run it at your house\nyou will likely need to figure out some sort of Dynamic DNS. I believe\nCloudflare provides this as a service. But I am sure there are others.<\/p>\n<p>Then you need to have an operating system. I personally prefer Arch Linux so I\nhave a server running Arch Linux.<\/p>\n<p>We are going to be hosting our Git repositories over the SSH protocol with the\nhelp of a tool called <a rel=\"external\" href=\"https:\/\/gitolite.com\">Gitolite<\/a>. <a rel=\"external\" href=\"https:\/\/gitolite.com\">Gitolite<\/a> facilitates authenticating\nusers and managing access to the Git repositories over SSH public\/private key\npairs.<\/p>\n<h2 id=\"the-setup\">The Setup<\/h2>\n<p>This portion assumes you already have an Arch Linux system setup for your server.<\/p>\n<h3 id=\"get-ssh-daemeon-setup\">Get SSH Daemeon Setup<\/h3>\n<p>First thing we need to do on that server is installed the <code>openssh<\/code> package so\nthat we will have the ssh daemon installed.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>sudo pacman -Sy openssh<\/span><\/span><\/code><\/pre>\n<p>Next we want to start the ssh daemon.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>sudo systemctl start sshd<\/span><\/span><\/code><\/pre>\n<p>Then we want to enable it so that it starts up at boot.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>sudo systemctl enable sshd<\/span><\/span><\/code><\/pre>\n<p>We can then verify it is running with the following.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>systemctl status sshd<\/span><\/span><\/code><\/pre>\n<p>Then I configured sshd by editing <code>\/etc\/ssh\/sshd_config<\/code> by updating it so that\nthe following are set. Explanations of what they do are in comments above them\nall below. This is effectively to harden sshd as may be exposed to the internet\ndirectly and it needs to be as secure as possible.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span># Enable public key authentication<\/span><\/span>\n<span class=\"giallo-l\"><span>PublickeyAuthentication yes<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Tell it where to find the file containing authorized keps<\/span><\/span>\n<span class=\"giallo-l\"><span>AuthorizedKeysFile .ssh\/authorized_keys<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Prevent password authentication<\/span><\/span>\n<span class=\"giallo-l\"><span>PasswordAuthentication no<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Prevent challenge response auth<\/span><\/span>\n<span class=\"giallo-l\"><span>ChallengeResponseAuthentication no<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Disable PAM authentication<\/span><\/span>\n<span class=\"giallo-l\"><span>UsePAM no<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Prevent root login<\/span><\/span>\n<span class=\"giallo-l\"><span>PermitRootLogin no<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Prevent keyboard interactive auth<\/span><\/span>\n<span class=\"giallo-l\"><span>KbdInteractiveAuthentication no<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Don&#39;t allow any empty passwords<\/span><\/span>\n<span class=\"giallo-l\"><span>PermitEmptyPasswords no<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Restrict allowed users<\/span><\/span>\n<span class=\"giallo-l\"><span>AllowUsers youruser gitolite<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Disable agent &amp; TCP forwarding unless you need it<\/span><\/span>\n<span class=\"giallo-l\"><span>AllowAgentForwarding no<\/span><\/span>\n<span class=\"giallo-l\"><span>AllowTcpForwarding no<\/span><\/span>\n<span class=\"giallo-l\"><span>X11Forwarding no<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Disable tunneling unless needed<\/span><\/span>\n<span class=\"giallo-l\"><span>PermitTunnel no<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Disable rhosts-style trust<\/span><\/span>\n<span class=\"giallo-l\"><span>IgnoreRhosts yes<\/span><\/span>\n<span class=\"giallo-l\"><span>HostbasedAuthentication no<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Limit auth attempts<\/span><\/span>\n<span class=\"giallo-l\"><span>MaxAuthTries 3<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Limit concurrent connections<\/span><\/span>\n<span class=\"giallo-l\"><span>MaxSessions 2<\/span><\/span>\n<span class=\"giallo-l\"><span>MaxStartups 10:30:60<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Drop idle connections<\/span><\/span>\n<span class=\"giallo-l\"><span>ClientAliveInterval 300<\/span><\/span>\n<span class=\"giallo-l\"><span>ClientAliveCountMax 2<\/span><\/span><\/code><\/pre>\n<p>Then I restarted the sshd to have the changes take place.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>sudo systemctl restart sshd<\/span><\/span><\/code><\/pre>\n<p>After this I simply scp'd my public ssh key over to the server and added it to\nthe <code>authorized_keys<\/code> file as follows so that I could easily ssh over to the\nserver and manage things.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>cat ~\/.ssh\/id_ed25519.pub &gt;&gt; ~\/.ssh\/authorized_keys<\/span><\/span><\/code><\/pre>\n<p>Then I simply made sure that the file had the correct permissions.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>chmod 600 ~\/.ssh\/authorized_keys<\/span><\/span>\n<span class=\"giallo-l\"><span>chown -R user:user ~\/.ssh<\/span><\/span><\/code><\/pre><h3 id=\"install-setup-gitolite\">Install &amp; Setup Gitolite<\/h3>\n<p>First I installed the dependencies as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>sudo pacman -Sy base-devel git<\/span><\/span><\/code><\/pre>\n<p>Then I installed <a rel=\"external\" href=\"https:\/\/gitolite.com\">Gitolite<\/a> as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>sudo pacman -Sy gitolite<\/span><\/span><\/code><\/pre>\n<p>The above created the <code>gitolite<\/code> user on the system and setup\n<code>\/var\/lib\/gitolite<\/code> as it's home directory.<\/p>\n<p>Then simply needed to initialize Gitolite. In order to do this I ran the\nfollowing.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>sudo -u gitolite gitolite setup -pk path\/to\/my\/public\/ssh\/key<\/span><\/span><\/code><\/pre>\n<p>This setup the <code>gitolite-admin<\/code> repository as well as a <code>testing<\/code> repository.\nThe <code>gitolite-admin<\/code> repository is how you configure and manage Gitolite. So\nyou simply clone it and make changes to the configs in there and commit and\npush it up. It then handles creating repositories appropriately for you with\nthe configured access controls.<\/p>\n<p>Which supports what you need for private repositories as well as anonymous\n(unauthenticated) repositories which are useful for opensource.<\/p>\n<h2 id=\"what-about-off-site-backups\">What about Off-site Backups<\/h2>\n<p>One side benefits I was getting from the services I was leaving was off-site\nbackups. So, I figured I might as well address that in my setup as well.<\/p>\n<p>Turns out there is a pretty great backup tool called <a rel=\"external\" href=\"https:\/\/restic.net\/\">Restic<\/a> that integrates\nwith Backblaze B2 that allows you to configure backups to be done and stored in\na Backblaze B2 bucket pretty easily.<\/p>\n<p>First I had to decide what I wanted to backup. In my case I simply wanted the following.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>\/var\/lib\/gitolite\/repositories<\/span><\/span>\n<span class=\"giallo-l\"><span>\/var\/lib\/gitolite\/.gitolite<\/span><\/span><\/code><\/pre>\n<p>Then I installed <a rel=\"external\" href=\"https:\/\/restic.net\/\">Restic<\/a> as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>sudo pacman -Sy restic<\/span><\/span><\/code><\/pre>\n<p>Then I had to go and create my Backblaze B2 bucket and an Application Key,\nwhich I captured the bucket name, key id, and key itself and did the following.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>sudo mkdir -p \/etc\/restic<\/span><\/span>\n<span class=\"giallo-l\"><span>sudo nvim \/etc\/restic\/gitolite.env<\/span><\/span><\/code><\/pre>\n<p>I then created filled <code>\/etc\/restic\/gitolite.env<\/code> with the following content.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>export RESTIC_REPOSITORY=&quot;b2:BUCKET_NAME:\/gitolite&quot;<\/span><\/span>\n<span class=\"giallo-l\"><span>export RESTIC_PASSWORD=&quot;use-a-long-random-passphrase&quot;<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>export B2_ACCOUNT_ID=&quot;your-application-key-id&quot;<\/span><\/span>\n<span class=\"giallo-l\"><span>export B2_ACCOUNT_KEY=&quot;your-application-key&quot;<\/span><\/span><\/code><\/pre>\n<p>Then I set the perms on that directory and file as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>sudo chmod 600 \/etc\/restic\/gitolite.env<\/span><\/span>\n<span class=\"giallo-l\"><span>sudo chown root:root \/etc\/restic\/gitolite.env<\/span><\/span><\/code><\/pre>\n<p>Then I initialized the restic repository.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>sudo -i<\/span><\/span>\n<span class=\"giallo-l\"><span>source \/etc\/restic\/gitolite.env<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>restic init<\/span><\/span><\/code><\/pre>\n<p>Then I ran a manual backup to test things out.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>restic backup \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  \/var\/lib\/gitolite\/repositories \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  \/var\/lib\/gitolite\/.gitolite<\/span><\/span><\/code><\/pre><pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>restic snapshots<\/span><\/span><\/code><\/pre>\n<p>Then I added an exclusion file to exclude unneccessary files.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>nvim \/etc\/restic\/gitolite.exclude<\/span><\/span><\/code><\/pre>\n<p>and gave it the following content.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>**\/tmp<\/span><\/span>\n<span class=\"giallo-l\"><span>**\/*.lock<\/span><\/span>\n<span class=\"giallo-l\"><span>**\/lost+found<\/span><\/span><\/code><\/pre>\n<p>Then I tested manual backup again with it.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>restic backup \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  --exclude-file \/etc\/restic\/gitolite.exclude \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  \/var\/lib\/gitolite\/repositories \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  \/var\/lib\/gitolite\/.gitolite<\/span><\/span><\/code><\/pre>\n<p>Then I came up with a simple retention policy and tested it as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>restic forget \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  --keep-daily 14 \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  --keep-weekly 8 \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  --keep-monthly 12 \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  --prune<\/span><\/span><\/code><\/pre>\n<p>This removes old snapshots, etc. based on the retention policy.<\/p>\n<p>Then I created a backup script so that I could automate this.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>nvim \/usr\/local\/bin\/restic-gitolite-backup.sh<\/span><\/span><\/code><\/pre>\n<p>with the following content.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>#!\/usr\/bin\/env bash<\/span><\/span>\n<span class=\"giallo-l\"><span>set -euo pipefail<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>source \/etc\/restic\/gitolite.env<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>restic backup \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  --exclude-file \/etc\/restic\/gitolite.exclude \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  \/var\/lib\/gitolite\/repositories \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  \/var\/lib\/gitolite\/.gitolite<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>restic forget \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  --keep-daily 14 \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  --keep-weekly 8 \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  --keep-monthly 12 \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  --prune<\/span><\/span>\n<span class=\"giallo-l\"><\/span><\/code><\/pre>\n<p>Then I made it executable as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>chmod +x \/usr\/local\/bin\/restic-gitolite-backup.sh<\/span><\/span><\/code><\/pre>\n<p>Then I registered it as a systemd service by creating the following.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>nvim \/etc\/systemd\/system\/restic-gitolite-backup.service<\/span><\/span><\/code><\/pre>\n<p>with the following content.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>[Unit]<\/span><\/span>\n<span class=\"giallo-l\"><span>Description=Restic backup of Gitolite repositories<\/span><\/span>\n<span class=\"giallo-l\"><span>Wants=network-online.target<\/span><\/span>\n<span class=\"giallo-l\"><span>After=network-online.target<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>[Service]<\/span><\/span>\n<span class=\"giallo-l\"><span>Type=oneshot<\/span><\/span>\n<span class=\"giallo-l\"><span>ExecStart=\/usr\/local\/bin\/restic-gitolite-backup.sh<\/span><\/span><\/code><\/pre>\n<p>Then I created a systemd timer to run it automatically.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>nvim \/etc\/systemd\/system\/restic-gitolite-backup.timer<\/span><\/span><\/code><\/pre>\n<p>with the following content.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>[Unit]<\/span><\/span>\n<span class=\"giallo-l\"><span>Description=Daily Restic Gitolite backup<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>[Timer]<\/span><\/span>\n<span class=\"giallo-l\"><span>OnCalendar=daily<\/span><\/span>\n<span class=\"giallo-l\"><span>Persistent=true<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>[Install]<\/span><\/span>\n<span class=\"giallo-l\"><span>WantedBy=timers.target<\/span><\/span><\/code><\/pre>\n<p>Then I simply enabled it as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>systemctl daemon-reload<\/span><\/span>\n<span class=\"giallo-l\"><span>systemctl enable --now restic-gitolite-backup.timer<\/span><\/span><\/code><\/pre>\n<p>And verified that it was properly registered and running with the following.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>systemctl list-timers<\/span><\/span><\/code><\/pre>\n<p>Finally I tested the backups to make sure I could restore from them as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>restic snapshots<\/span><\/span><\/code><\/pre><pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>restic restore latest --target \/tmp\/restore-test<\/span><\/span><\/code><\/pre>\n<p>In theory you should be able to restore an individual repo as follows. But I\nhave not tested this yet.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>restic restore latest \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  --include \/var\/lib\/gitolite\/repositories\/myrepo.git \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  --target \/tmp\/restore-test<\/span><\/span><\/code><\/pre><h2 id=\"opensource-repositories\">OpenSource Repositories<\/h2>\n<p>Setup hosting of anonymous repositories with <code>git:\/\/<\/code> protocol which doesn't\nfacilitate any authentication. I do this via the <code>git-daemon<\/code> which comes with\ngit. It also has the benefit of playing nicely with <a rel=\"external\" href=\"https:\/\/gitolite.com\">Gitolite<\/a> and therefore\nallows sharing the same repositories store.<\/p>\n<p>To do this I created a systemd service description for it.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>\/etc\/systemd\/system\/git-daemon.service<\/span><\/span><\/code><\/pre>\n<p>with the content<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>[Unit]<\/span><\/span>\n<span class=\"giallo-l\"><span>Description=Git Daemon (anonymous read-only)<\/span><\/span>\n<span class=\"giallo-l\"><span>After=network.target<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>[Service]<\/span><\/span>\n<span class=\"giallo-l\"><span>User=gitolite<\/span><\/span>\n<span class=\"giallo-l\"><span>Group=gitolite<\/span><\/span>\n<span class=\"giallo-l\"><span>ExecStart=\/usr\/lib\/git-core\/git-daemon \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  --reuseaddr \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  --base-path=\/var\/lib\/gitolite\/repositories \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  --disable=receive-pack \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  --informative-errors \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  --verbose<\/span><\/span>\n<span class=\"giallo-l\"><span>Restart=on-failure<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>[Install]<\/span><\/span>\n<span class=\"giallo-l\"><span>WantedBy=multi-user.target<\/span><\/span><\/code><\/pre>\n<p>Then I enabled and started the service as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>sudo systemctl daemon-reload<\/span><\/span>\n<span class=\"giallo-l\"><span>sudo systemctl enable git-daemon<\/span><\/span>\n<span class=\"giallo-l\"><span>sudo systemctl start git-daemon<\/span><\/span>\n<span class=\"giallo-l\"><span>sudo systemctl status git-daemon<\/span><\/span><\/code><\/pre>\n<p>Then I simply exposed the <code>git-daemon<\/code> port <code>9418<\/code> in the firewall so that\nexternally people would be able to access the opensource repositories.<\/p>\n<h2 id=\"dynamic-dns\">Dynamic DNS<\/h2>\n<p>I am using Cloudfare for DNS zone hosting. So I whipped up the following little\nscript <code>~\/bin\/update_dns_record_with_public_ip<\/code>, to dynamically set the IP\naddress.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>#!\/usr\/bin\/env bash<\/span><\/span>\n<span class=\"giallo-l\"><span>set -euo pipefail<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>ZONE_ID=<\/span><\/span>\n<span class=\"giallo-l\"><span>RECORD_ID=<\/span><\/span>\n<span class=\"giallo-l\"><span>CLOUDFLARE_API_TOKEN=<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>SUB_DOMAIN=<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>TIMESTAMP=$(date &#39;+%Y-%m-%d %H:%M:%S&#39;)<\/span><\/span>\n<span class=\"giallo-l\"><span>LOGFILE=&quot;$HOME\/logs\/cloudflare-ddns.log&quot;<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Fetch the RECORD_ID of the subdomain. This only has to be done initially to get the value for RECORD_ID<\/span><\/span>\n<span class=\"giallo-l\"><span># So just uncomment this, save the file, and run the script to get the RECORD_ID. Then simply update the<\/span><\/span>\n<span class=\"giallo-l\"><span># script with the RECORD_ID value, recomment these lines, and save the script and you should be good to go.<\/span><\/span>\n<span class=\"giallo-l\"><span>#<\/span><\/span>\n<span class=\"giallo-l\"><span># curl -X GET &quot;https:\/\/api.cloudflare.com\/client\/v4\/zones\/$ZONE_ID\/dns_records?name=$SUB_DOMAIN&quot; \\<\/span><\/span>\n<span class=\"giallo-l\"><span>#  -H &quot;Authorization: Bearer $CLOUDFLARE_API_TOKEN&quot;<\/span><\/span>\n<span class=\"giallo-l\"><span># exit 0<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Fetch the public IP address using ifconfig.me<\/span><\/span>\n<span class=\"giallo-l\"><span>IP_ADDRESS=$(curl -s ifconfig.me)<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Get the IP address Cloudflare currently has for the subdomain<\/span><\/span>\n<span class=\"giallo-l\"><span>DNS_IP=$(curl -s \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  -H &quot;Authorization: Bearer $CLOUDFLARE_API_TOKEN&quot; \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  -H &quot;Content-Type: application\/json&quot; \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  &quot;https:\/\/api.cloudflare.com\/client\/v4\/zones\/$ZONE_ID\/dns_records\/$RECORD_ID&quot; \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  | jq -r &#39;.result.content&#39;)<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># 3. Compare existing DNS record&#39;s IP addr to the current public IP addr<\/span><\/span>\n<span class=\"giallo-l\"><span>if [[ &quot;$IP_ADDRESS&quot; == &quot;$DNS_IP&quot; ]]; then<\/span><\/span>\n<span class=\"giallo-l\"><span>  echo &quot;$TIMESTAMP - IP unchanged ($IP_ADDRESS). No update needed.&quot; &gt;&gt; &quot;$LOGFILE&quot;<\/span><\/span>\n<span class=\"giallo-l\"><span>  exit 0<\/span><\/span>\n<span class=\"giallo-l\"><span>fi<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>RESPONSE=$(curl -X PUT &quot;https:\/\/api.cloudflare.com\/client\/v4\/zones\/$ZONE_ID\/dns_records\/$RECORD_ID&quot; \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  -H &quot;Authorization: Bearer $CLOUDFLARE_API_TOKEN&quot; \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  -H &quot;Content-Type: application\/json&quot; \\<\/span><\/span>\n<span class=\"giallo-l\"><span>  --data &quot;{<\/span><\/span>\n<span class=\"giallo-l\"><span>    \\&quot;type\\&quot;: \\&quot;A\\&quot;,<\/span><\/span>\n<span class=\"giallo-l\"><span>    \\&quot;name\\&quot;: \\&quot;$SUB_DOMAIN\\&quot;,<\/span><\/span>\n<span class=\"giallo-l\"><span>    \\&quot;content\\&quot;: \\&quot;$IP_ADDRESS\\&quot;,<\/span><\/span>\n<span class=\"giallo-l\"><span>    \\&quot;ttl\\&quot;: 120,<\/span><\/span>\n<span class=\"giallo-l\"><span>    \\&quot;proxied\\&quot;: false<\/span><\/span>\n<span class=\"giallo-l\"><span>}&quot;)<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Check success<\/span><\/span>\n<span class=\"giallo-l\"><span>SUCCESS=$(echo &quot;$RESPONSE&quot; | jq -r &#39;.success&#39;)<\/span><\/span>\n<span class=\"giallo-l\"><span>if [[ &quot;$SUCCESS&quot; == &quot;true&quot; ]]; then<\/span><\/span>\n<span class=\"giallo-l\"><span>  echo &quot;$TIMESTAMP - DNS update successful ($IP_ADDRESS)&quot; &gt;&gt; &quot;$LOGFILE&quot;<\/span><\/span>\n<span class=\"giallo-l\"><span>else<\/span><\/span>\n<span class=\"giallo-l\"><span>  ERR_MSG=$(echo &quot;$RESPONSE&quot; | jq -r &#39;.errors[]?.message&#39;)<\/span><\/span>\n<span class=\"giallo-l\"><span>  echo &quot;$TIMESTAMP - DNS update failed: $ERR_MSG&quot; &gt;&gt; &quot;$LOGFILE&quot;<\/span><\/span>\n<span class=\"giallo-l\"><span>fi<\/span><\/span><\/code><\/pre>\n<p>This script expects your user's hame directory to also have a <code>logs<\/code> directory\nto be able to write it's logs out to.<\/p>\n<p>This script fetches the public IP address using ifconfig.me and then uses the\nCloudflare API to update the subdomain.<\/p>\n<p>In order for this to work you need to create an API Key that is setup for Edit\nzone. This can be done from the Cloudflare -&gt; Avatar -&gt; Profile -&gt; API Keys.<\/p>\n<p>Then to use this you obviously would need to fill in the variables at the\ntop of the script. The ZONE_ID can be obtained by selecting the zone in the\nCloudflare UI and looking in the right sidebar for the API section and grabbing\nthe zone id value. The RECORD_ID I believe is only available by using the API\ncall that is commented out near the top of the script.<\/p>\n<p>Once I had the script setup I was able to test it by just running it. But, I\ndon't want to manually do this. I want it to automatically be run.<\/p>\n<p>So I installed <code>cronie<\/code> with the following.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>sudo pacman -Sy cronie vi<\/span><\/span>\n<span class=\"giallo-l\"><span>mkdir -p ~\/.cache<\/span><\/span>\n<span class=\"giallo-l\"><span>sudo systemctl enable cronie.service<\/span><\/span>\n<span class=\"giallo-l\"><span>sudo systemctl start cronie.service<\/span><\/span><\/code><\/pre>\n<p>Then I edited my users crontab via <code>crontab -e<\/code> and gave it the following\ncontent.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>*\/5 * * * * \/home\/YOUR-USERNAME\/bin\/update_dns_record_with_public_ip &gt;&gt; \/home\/YOUR-USERNAME\/logs\/cloudflare-ddns-cron.log 2&gt;&amp;1<\/span><\/span>\n<span class=\"giallo-l\"><span>0 0 * * * truncate -s 0 \/home\/YOUR-USERNAME\/logs\/cloudflare-ddns.log<\/span><\/span><\/code><\/pre>\n<p>The above registers two cron jobs. The first one runs the script every 5 mins\nto make sure that the DNS record is up to date. The second one runs once every\nday and truncates the log file so it does not get out of hand.<\/p>\n<p>I then waited and checked the log files to make sure everything was working as\nexpected.<\/p>\n<p>Don't also forget that you need to likely modify your router configuration to\nforward port 22 from the outside of your network to your server's internal IP\naddress. That is if you aren't using a hosted VPS. If you are using a hosted\nVPS you shouldn't need this entire Dynamic DNS section at all as you generally\nget a static IP address.<\/p>\n<h2 id=\"what-about-prs-comments-etc\">What about PRs, comments, etc.<\/h2>\n<p>Personally, I don't want or need any of those things. There are plenty of tools\nthat can be used to facilitate those types of interactions without the need for\nthe Git hosting to do them. A very basic one is the concept of patches and\nemails to say the least for open source repositories.<\/p>\n"},{"title":"Shower Thoughts 2026-01-01","published":"2026-01-01T00:00:00+00:00","updated":"2026-01-01T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/shower-thoughts-2026-01-01\/"}},"id":"https:\/\/drewdeponte.com\/blog\/shower-thoughts-2026-01-01\/","content":"<p>Welcome to my first Shower Thoughts arcticle. This is a series that I came up\nwith while taking a shower this morning. I am not planning on writing an\narticle every day or anything. As I don't necessarily have significant shower\nthoughts every day. But when I do this is an outlet for me to get them out and\nshare them with my future self and the world.<\/p>\n<p>Of course they will primarily be business, product and engineering focused as\nthat is where my career is spent.<\/p>\n<h2 id=\"coming-back-to-sharing-things\">Coming back to Sharing Things<\/h2>\n<p>For about a year now I have taken a step back from sharing things that I am\nworking on or sharing things that I have been thinking about. This was\npartially because of various things going on in my life, e.g. switch to\nPrincipal Engineer at OnDiem, etc. But probably most significantly it was\nbecause it takes a decent amount of time and effort to share things and I just\nwasn't sure the value in terms of what it brings to other people, how much\nit helps my standing within the community, etc. was there.<\/p>\n<p>Now that it has been about a year and I am looking back. I believe I have been\nlooking at the \"value\" all wrong. I now believe there is something innate to it\nthat helps me stay happy. I am not sure exactly what it is or why. Maybe it is\nsome deep need within me to connect with people in terms of the things that I\nam working on and thinking about. But I do know at this point it is definitely\na significant component that impacts my emotional state.<\/p>\n<p>Beyond that if there is one thing I have found to be true over the years. It is\nthat taking the extra few minutes to write something down generally ends up\nsaving me more time in the future.<\/p>\n<p>So with those two points in mind I am going to be coming back to sharing things\nmore often.<\/p>\n<h2 id=\"righting-the-wrongs-of-software-development\">Righting the Wrongs of Software Development<\/h2>\n<p>Another topic that came to mind while in the shower this morning is this deep\nseeded need to right what I see as wrongs within the Software Development\nworld. I have tried to let go of this mental stance over the past year or so as\nI haven't necessarily seen a direct &amp; clear path to profit from it.<\/p>\n<p>I have failed in terms of letting go of this and have decided that for my own\nsanity, happiness, etc. I really need to double down on this instead of letting\ngo of it. And not worry about the money and the profitability as much. At least\nright now.<\/p>\n<p>So what is this mentality of \"Righting the Wrongs of Software Development\" you\nmay ask?<\/p>\n<p>Well, it is hard to fully articulate. So, I will simply try to call out some\nwrongs that I see and that I think about, some of which I may be actively\nexploring addressing.<\/p>\n<ul>\n<li>Why is it we can have video games that can perform well above 244 hz while\ndoing tons of computation but we can't have desktop apps that can scroll some\nbasic content smoothly?<\/li>\n<li>Why do there have to be so many layers to things in general? It makes\neverything slower, harder to debug, and much harder to make performant.<\/li>\n<li>Why are we trying to build local state based applications in browsers via\nJavascript? Or maybe even in WebASM if you are really on the bleading edge.\nIt is just more layers of what was already possible in native development in\na much better fashion.<\/li>\n<li>Why does everyone think the only way to build cross-platform applications is\nto do so through a browser? Making everything with much more layers and much\nless performance.<\/li>\n<li>Why is it people are so attached to social networks when they generally don't\nbreed true conversation? People would be better offer writing blog posts and\nsimply having people email them if someone wants to comment on a post. Or maybe\nemail a mailing list for that post so that other people interested in that\ntopic could follow along. It would probably be better to facilitate more real\nmeaning conversations. A mailing list could be at the author level or maybe at\na per article level. And guess what mailto: links allow you to specify a\nsubject line, body, etc. to start the email with.<\/li>\n<li>Why is it every product feels like it has to do a million things. I want the\nproducts I use to do less things but do them better. Example I want a simple\nservice to exist that lets people subscribe to mailing lists and that is it. I\nwant another tool that can interact with said service to get emails from that\nlist. Why does everything have to be so needlessly coupled. In Mailchimp for\nexample why can't I send my own custom made raw emails without paying more\nmoney?<\/li>\n<\/ul>\n<p>The list goes on and on and on an on. With tools, services, products in general\nthat I interact with on a daily basis. The hard part is focusing and\nprioritizing which if any to actual focus effort and energy into. Beyond that,\nidentifying where people went wrong with things and making sure that those\nthings don't happen within my products.<\/p>\n<h2 id=\"simple-mailing-lists-flexible-mail-pipeline\">Simple Mailing Lists &amp; Flexible Mail Pipeline<\/h2>\n<p>This is an idea that I had a while back and I even spent some time working on\nat one point. But in today's shower thoughts it came back up and makes me think\nthat maybe I should finish out the work to get it at least to an initial phase\nof completion. Making it fully usable.<\/p>\n<p>The core idea here is to split out the service that allows people to subcribe\nto a mailing list. Making it extremely focused, light weight, performant,\ncross-platform, etc.<\/p>\n<p>Then have another tool that can take Markdown files and produce HTML email content.<\/p>\n<p>Then have another tool that can take HTML email content and send it to a\ncollection of users.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/shower-thoughts-2026-01-01\/modular_mailing_list_system.png\" alt=\"Modular Mailing List System\" \/><\/p>\n<p>The idea being that all these components that be simple, focused, performant,\nand modular. So that they can do their one thing and do it well. This will give\nme the flexibility. And who knows maybe it will give me some thoughts on how to\npackage things up as a service or a product.<\/p>\n<p>But irrespective it will give me the baseline tools to run my mailing list the\nway I want and give me the flexibility to evolve in the ways that I want.<\/p>\n<p>You are probably going. Well, what out click tracking, and open tracking, and\nall that other stuff that everyone wants their email marketing tools to do. I\nam not doing any of it. I don't value it. What I am sharing with the world I am\ngoing to share if there is 1 person listening or 1 million people listing. That\nmetric isn't useful to me and there are much more valuable and meaningful\nmetrics to pay attention to.<\/p>\n<p>This really boils down to simply architecting things in a better way. But this\nwas actually driven out of my frustration with marketing automation tools like\nMailchimp, etc.<\/p>\n"},{"title":"Install multiple PostgreSQL versions from src on Arch","published":"2025-12-12T00:00:00+00:00","updated":"2025-12-12T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/install-multiple-postgresql-versions-from-src-on-arch\/"}},"id":"https:\/\/drewdeponte.com\/blog\/install-multiple-postgresql-versions-from-src-on-arch\/","content":"<p>On Arch Linux if you need multiple versions of PostgreSQL installed at the same\ntime. Say because you are working on a software product that is currently\nlocked into an older PostgreSQL version while also working on another product\nthat is using a new PostgreSQL version. You are kinda screwed, at least in\nterms of being able to do it from the packagement management system.<\/p>\n<p>They effectively provide the latest version and then also provided through the\nuser packages you can get a previous version to facilitate the upgrade process\nthat PostgreSQL requires.<\/p>\n<p>So we have to look for a solution outside of the normal package management\nsystem. I tried looking for solutions like a PostgreSQL Version Manager similar\nin concept to NVM (Node Version Manager) or RVM (Ruby Version Manager), etc.\nThere were a couple that seemed not fully baked and just seemed like a bunch of\nunnecessary layers of crap.<\/p>\n<p>The only alternative I could find was to use Docker which I also didn't want to\ndo as it is a bunch more layers of crap that I didn't want to deal with.<\/p>\n<p>We should totally be able to install multiple versions of PostgreSQL side by\nside on Arch Linux and it shouldn't be a problem. And guess what. I was\ncorrect. The following are my notes from when I set this up.<\/p>\n<h2 id=\"the-setup\">The Setup<\/h2>\n<p>In the below I specify a custom prefix of <code>\/usr\/local\/pgsql\/15.13<\/code> instead of\nthe default of <code>\/usr\/local\/pgsql<\/code> because I want to be able to have multiple\npostgresql versions installed at the same time.<\/p>\n<p>Additionally, I specified the <code>openssl-1.1<\/code> which is an additional package you\ncan install on Arch linux beside the normal openssl 3.6.0, because the\n<code>pgcrypto<\/code> extension depends on things that were provided by openssl 1.1 but\nwere removed in openssl 3.x.<\/p>\n<p>Beyond that I specified <code>--with-uuid=e2fs<\/code> because on Arch linux the <code>libuuid<\/code>\nis provided and not the <code>ossp-uuid<\/code> library. I needed to specify this so that\nwhen I built the <code>uuid-ossp<\/code> extension in contrib it could properly build.<\/p>\n<p>Out side of that I simply followed the directions in the PostgreSQL\ndocumentation with the following configure command to take the above into\naccount.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>.\/configure --prefix=\/usr\/local\/pgsql\/15.13 --with-openssl --with-includes=\/usr\/include\/openssl-1.1 --with-libraries=\/usr\/lib\/openssl-1.1 --with-uuid=e2fs<\/span><\/span>\n<span class=\"giallo-l\"><span>make<\/span><\/span><\/code><\/pre>\n<p>Note: During the process the build would fail. And I would look at why it was\nfailing and it was always simply a dependency that was not installed. I would\ninstall the package from the package system and then re-run the build. I did\nthis a handfull of times to get the build to fully succeed. I also followed the\nsame process for the extensions that I installed as well.<\/p>\n<h2 id=\"start-stop-versions\">Start\/Stop Versions<\/h2>\n<p>You don't really have two versions running at the same time in this setup. So\nthe key becomes how do you start and stop the different versions of PostgreSQL\nthat you have installed. The way I addressed this was simply to whip up some\nquick shell scripts to start and stop it. For example I created a\n<code>~\/bin\/pgsql_start_15.13<\/code> and <code>~\/bin\/pgsql_stop_15.13<\/code> to facilitate starting\nand stopping that specific version of PostgreSQL.<\/p>\n<p>The following is the content from <code>~\/bin\/pgsql_start_15.13<\/code>.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>#!\/usr\/bin\/env sh<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>PGDATA=&quot;\/usr\/local\/pgsql\/15.13\/data&quot;<\/span><\/span>\n<span class=\"giallo-l\"><span>PGCTL=&quot;\/usr\/local\/pgsql\/15.13\/bin\/pg_ctl&quot;<\/span><\/span>\n<span class=\"giallo-l\"><span>LOGFILE=&quot;$PGDATA\/logfile&quot;<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># go to directory pg_ctl has perms to so that when<\/span><\/span>\n<span class=\"giallo-l\"><span># pg_ctl tries to return to the directory it doesn&#39;t<\/span><\/span>\n<span class=\"giallo-l\"><span># error.<\/span><\/span>\n<span class=\"giallo-l\"><span>cd \/tmp || exit 1<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Check if PostgreSQL is already running<\/span><\/span>\n<span class=\"giallo-l\"><span>if sudo -u postgres $PGCTL -D &quot;$PGDATA&quot; status &gt; \/dev\/null 2&gt;&amp;1; then<\/span><\/span>\n<span class=\"giallo-l\"><span>    echo &quot;PostgreSQL is already running.&quot;<\/span><\/span>\n<span class=\"giallo-l\"><span>else<\/span><\/span>\n<span class=\"giallo-l\"><span>    echo &quot;Starting PostgreSQL...&quot;<\/span><\/span>\n<span class=\"giallo-l\"><span>    sudo -u postgres $PGCTL -D &quot;$PGDATA&quot; -l &quot;$LOGFILE&quot; start<\/span><\/span>\n<span class=\"giallo-l\"><span>    echo &quot;PostgreSQL started.&quot;<\/span><\/span>\n<span class=\"giallo-l\"><span>fi<\/span><\/span><\/code><\/pre>\n<p>The following is the content from the <code>~\/bin\/pgsql_stop_15.13<\/code>.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>#!\/usr\/bin\/env sh<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Variables<\/span><\/span>\n<span class=\"giallo-l\"><span>PGDATA=&quot;\/usr\/local\/pgsql\/15.13\/data&quot;<\/span><\/span>\n<span class=\"giallo-l\"><span>PGCTL=&quot;\/usr\/local\/pgsql\/15.13\/bin\/pg_ctl&quot;<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># go to directory pg_ctl has perms to so that when<\/span><\/span>\n<span class=\"giallo-l\"><span># pg_ctl tries to return to the directory it doesn&#39;t<\/span><\/span>\n<span class=\"giallo-l\"><span># error.<\/span><\/span>\n<span class=\"giallo-l\"><span>cd \/tmp || exit 1<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Check if PostgreSQL is running<\/span><\/span>\n<span class=\"giallo-l\"><span>if sudo -u postgres $PGCTL -D &quot;$PGDATA&quot; status &gt; \/dev\/null 2&gt;&amp;1; then<\/span><\/span>\n<span class=\"giallo-l\"><span>    echo &quot;Stopping PostgreSQL...&quot;<\/span><\/span>\n<span class=\"giallo-l\"><span>    sudo -u postgres $PGCTL -D &quot;$PGDATA&quot; stop<\/span><\/span>\n<span class=\"giallo-l\"><span>    echo &quot;PostgreSQL stopped.&quot;<\/span><\/span>\n<span class=\"giallo-l\"><span>else<\/span><\/span>\n<span class=\"giallo-l\"><span>    echo &quot;PostgreSQL is not running.&quot;<\/span><\/span>\n<span class=\"giallo-l\"><span>fi<\/span><\/span><\/code><\/pre>"},{"title":"Fix PostgreSQL Collation version miss-match","published":"2025-11-25T00:00:00+00:00","updated":"2025-11-25T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/fix-postgresql-collation-version-miss-match\/"}},"id":"https:\/\/drewdeponte.com\/blog\/fix-postgresql-collation-version-miss-match\/","content":"<p>The following is an example of what you need to do to fix PostgreSQL Collation\nversion miss-match issues on Arch Linux.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>REINDEX DATABASE dummy;<\/span><\/span>\n<span class=\"giallo-l\"><span>ALTER DATABASE dummy REFRESH COLLATION VERSION;<\/span><\/span><\/code><\/pre>\n<p>Basically my understanding is that PostgreSQL depends on certain libraries and\nthe version of those libraries are effectively snapshotted when the indexing is\ndone. Hence the COLLATION VERSION. So when libraries are upgraded on the system\nand PostgreSQL is now linking to a different version of the library. There is\nno guarantee that it behaves 100% the same as it did with the previous.<\/p>\n<p>Therefore, you need to reindex things and then update the COLLATION VERSION.<\/p>\n"},{"title":"Install Ruby on Arch Linux","published":"2025-11-25T00:00:00+00:00","updated":"2025-11-25T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/install-ruby-on-arch-linux\/"}},"id":"https:\/\/drewdeponte.com\/blog\/install-ruby-on-arch-linux\/","content":"<p>I had to frum install Buby with the following so that it was setup to find the\nproper system libs, etc.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>CFLAGS=&quot;-std=c99 -I\/usr\/include -L\/usr\/lib -D_GNU_SOURCE&quot; frum install 3.2.2<\/span><\/span><\/code><\/pre>"},{"title":"Upgrade PostgreSQL on Arch Linux","published":"2025-11-25T00:00:00+00:00","updated":"2025-11-25T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/upgrade-postgresql-on-arch-linux\/"}},"id":"https:\/\/drewdeponte.com\/blog\/upgrade-postgresql-on-arch-linux\/","content":"<p>The following are my notes for upgrading PostgreSQL on Arch Linux when I have installed PostgreSQL through the official package management system.<\/p>\n<p>Stop the service with the following.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>sudo systemctl stop postgresql<\/span><\/span><\/code><\/pre>\n<p>The PostgreSQL upgrade process which is done with <code>pg_upgrade<\/code> requires having the previous version of postgres that created the <code>\/var\/lib\/postgres\/data<\/code> directory as the format is locked to major versions. For example you might be upgrading from PostgreSQL 17 to 18. The <code>\/var\/lib\/postgres\/data<\/code> directory was originally setup with 17.<\/p>\n<p>On Arch Linux the way that pacman works with upgrades is that it upgrades to the latest. So when you upgrade postgresql it will have only the latest version installed. But in order to facilitate upgrade with postgresql you have to have the previous version installed already.<\/p>\n<p>To facilitate this Arch User Package's available through <code>yay<\/code> provide a package for the previous version to be installed beside the current. The following facilitates installing these packages.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>yay -Sy postgresql-old-upgrade<\/span><\/span>\n<span class=\"giallo-l\"><span>yay -Sy postgis-old-upgrade # if you were using postgis with postgresql in the previous version<\/span><\/span><\/code><\/pre>\n<p>Once these are installed we need to move the old data dir to a backup. This can be done as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>sudo -iu postgres<\/span><\/span>\n<span class=\"giallo-l\"><span>sudo -u postgres mv data data.old17<\/span><\/span>\n<span class=\"giallo-l\"><span>exit<\/span><\/span><\/code><\/pre>\n<p>Then we need to create new initialized data directory for the new version of the database. Note: The <code>--no-data-checksums<\/code> option is something I added because the 17 data was done without data checksums and the <code>pg_upgrade<\/code> process doesn't allow upgrading from a non-checksum on to checksum one.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>sudo -iu postgres<\/span><\/span>\n<span class=\"giallo-l\"><span>mkdir data<\/span><\/span>\n<span class=\"giallo-l\"><span>initdb --no-data-checksums -D \/var\/lib\/postgres\/data<\/span><\/span>\n<span class=\"giallo-l\"><span>exit<\/span><\/span><\/code><\/pre>\n<p>Once you have the new data directory initialized then you can run the upgrade with the following.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>sudo -iu postgres<\/span><\/span>\n<span class=\"giallo-l\"><span>pg_upgrade -b \/opt\/pgsql-17\/bin -B \/usr\/bin -d data.backup -D data<\/span><\/span>\n<span class=\"giallo-l\"><span>exit<\/span><\/span><\/code><\/pre>\n<p>Then you can start the service up again.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>sudo systemctl start postgresql<\/span><\/span><\/code><\/pre>\n<p>Some statistics are not transferred by pg_upgrade. Once we have started the new\nserver we should be able to do the following to handle this.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>sudo -iu postgres<\/span><\/span>\n<span class=\"giallo-l\"><span>\/usr\/bin\/vacuumdb --all --analyze-in-stages --missing-stats-only<\/span><\/span>\n<span class=\"giallo-l\"><span>\/usr\/bin\/vacuumdb --all --analyze-only<\/span><\/span>\n<span class=\"giallo-l\"><span>exit<\/span><\/span><\/code><\/pre>"},{"title":"Pull Request Alternative Design Doc","published":"2025-02-07T00:00:00+00:00","updated":"2025-02-07T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/pull-request-alternative-design-doc\/"}},"id":"https:\/\/drewdeponte.com\/blog\/pull-request-alternative-design-doc\/","content":"<p>The following is a Design Doc for an idealized alternative to what is currently\nthe pull request work flow.<\/p>\n<h2 id=\"the-problem\">The Problem<\/h2>\n<p>The feature branch\/pull request process is fundamentally broken a bunch of\ndifferent ways. This can be improved significantly with a <a rel=\"external\" href=\"https:\/\/git-ps.sh\">Patch\nStack<\/a> based workflow and tooling. But this is still far\nfrom ideal as when you have dependent patches you can't open a review for the\nhigher level patch until the dependent patch is integrated into mainline. A\nstacked branch approach can elevate this but introduces other problems. Like,\nhaving to manage branch names up front as well as manage the DAG (directed\nacyclic graph) of the relationships between the branches. Also, it requires a\ncompletely different service that companies have to pay for to extend the peer\nreview process, see <a rel=\"external\" href=\"https:\/\/graphite.dev\">Graphite<\/a>.<\/p>\n<h2 id=\"idealized-goals\">Idealized Goals<\/h2>\n<ul>\n<li>open patch review requests as soon as completed, even if they are patches\nthat depend on another patch that has <strong>not<\/strong> been integrated into mainline\nyet.<\/li>\n<li>open patch review requests for a patch series communicating the dependence\nand order of dependence.<\/li>\n<li>don't require an additional service<\/li>\n<li>promote better reviews by actually having people use their own tools to review<\/li>\n<li>support reviewing commits after they are integrated into mainline as well as\nbefore they are integrated into mainline<\/li>\n<\/ul>\n<h2 id=\"the-thinking\">The Thinking<\/h2>\n<p>The initial high level thought is, that if we were to use <code>git format-patch<\/code> or\nsimilar to facilitate the review process. Then the review would be free\nfloating, or independent, even if it actually depended on another patch.<\/p>\n<p>This can could be seen as a drawback. But in reality it is a benefit as it\neffectively lifts the requirement of that dependency from the point of\nrequesting the review and moves it to the application of the patches.<\/p>\n<p>This would allow the developer who wrote the patches to effectively request\nreview for each of them independently even if they had dependence of another\npatch that has yet to be integrated into mainline.<\/p>\n<p>This might not eliminate the concept of a patch series all together. But it\ngives you flexibility and optionality in terms of workflow.<\/p>\n<p><a rel=\"external\" href=\"https:\/\/pr.pico.sh\">pr.pico.sh<\/a> is the closest thing to this that I have come\nacross. Though, it is a separate service that you would need to host.<\/p>\n"},{"title":"Git Commit Creation","published":"2024-10-16T00:00:00+00:00","updated":"2024-10-16T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/git-commit-creation\/"}},"id":"https:\/\/drewdeponte.com\/blog\/git-commit-creation\/","content":"<p>When people are first learning Git they seem to learn something along the lines\nof the following.<\/p>\n<blockquote>\n<p>You can create a commit by doing the following.<\/p>\n<ol>\n<li>staging a change with <code>git add &lt;path\/to\/file&gt;<\/code><\/li>\n<li>create the commit with <code>git commit<\/code><\/li>\n<\/ol>\n<\/blockquote>\n<p>Or maybe something like.<\/p>\n<blockquote>\n<p>You can stage and commit in one shot with <code>git commit -a<\/code>.<\/p>\n<\/blockquote>\n<p>It seems that for the majority of people, once they learn the above about\ncreating commits, they just move on and never look back. In my opinion this\nmoving on and never looking back has massively delayed individual growth as\nwell as hindered the development of maintainable systems.<\/p>\n<p>So with this article I am attempting to provide the ultimate guide to Git\ncommit creation. In the hopes that it will help newer developers understand\neverything that they should be taking into consideration when creating a\ncommit. And who knows, maybe we will be lucky and a couple of those devs that\nhaven't looked back, will slow down for a minute and think about creating Git\ncommits again.<\/p>\n<h2 id=\"the-pressures\">The Pressures<\/h2>\n<p>To really start to understand how we should be creating commits, and the\nreasons why. We need to explore the pressures imparted on Git commit creation\nfrom Git itself, the needs of our team members, both right now and in the\nfuture, as well as the needs of our selves, both right now and in the future.<\/p>\n<h3 id=\"anatomy-of-a-commit\">Anatomy of a Commit<\/h3>\n<p>To start we really need to understand what commits are in Git. Technically from\na Git perspective a commit is a snapshot of a project's contents, files &amp;\nfolders, at a moment in time combined with a message.<\/p>\n<h4 id=\"snapshots-diffs\">Snapshots &amp; Diffs<\/h4>\n<p>In the Git repository these snapshots are related to one another through the\nconcept of ancestry. Meaning that generally each commit has one or more parent\ncommits. These parent commits are the ancestors of the commit.<\/p>\n<blockquote>\n<p><em>Side Note:<\/em> There is technically a special case of commit that doesn't have\nany parents. Commonly you will see this with the first commit in a repository.\nHowever, you can actually create multiple of these within a repository if you\nlike. If you are curious this is generally referred to as an \"orphan\" branch\nand can be done with <code>git checkout --orphan<\/code>. See the git-checkout man page\nfor more details if you are interested.<\/p>\n<\/blockquote>\n<p>So we have snapshots with messages, and they have ancestors. What is the big\ndeal?<\/p>\n<p>Well, the natural thing that falls out of a snapshot having an ancestor is that\nthere is a difference (a.k.a. diff) that exists between two snapshots. In fact\nthis is such a prominent mental model that when you do a <code>git show &lt;sha&gt;<\/code> it\nshows you the difference between the ancestor commit's snapshot and the\nsnapshot of the commit identified by the given <code>&lt;sha&gt;<\/code>.<\/p>\n<p>A lot of people choose to simply think about commits as diffs even though they\nare actually storing full snapshots of the project in the Git repository.<\/p>\n<h4 id=\"commit-message\">Commit Message<\/h4>\n<p>The message portion of the commit is an extremely important and valuable piece\nof information, that if used properly, facilitates code reviews, as well as\nshort-term and long-term maintenance and understanding of a code base.<\/p>\n<p>Git actually has an official format for Git commit messages, which is as\nfollows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>Short summary of change (&lt;= 50 chars in len)<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>One or more paragraphs of text.<\/span><\/span>\n<span class=\"giallo-l\"><span>(hard wrapped at 72 characters)<\/span><\/span><\/code><\/pre>\n<p>If you are working with any editor worth its salt. It will have support\nspecifically for the Git commit message format and help you abide by the\nconstraints of this format.<\/p>\n<p>Abiding by this format is important as it will make sure that our commit\nmessages work within Git as a tool and it's expectations.<\/p>\n<h3 id=\"commit-characteristics\">Commit Characteristics<\/h3>\n<p>In addition to understanding the anatomy of a commit, their associated\nrequirements and implications, it is also important to understand the expected\ncharacteristics of a commit within Git.<\/p>\n<p>When you are making a commit. Two characteristics that are important to make\nsure you take into consideration are that the commit is <strong>buildable<\/strong> and\n<strong>testable<\/strong>.<\/p>\n<p><strong>Buildable<\/strong> simply means that the snapshot that will be captured by the\ncommit, is able to build without failure, using the build system of the\nproject. This is applicable not only to compiled languages but also can be\nconceptually extended to include other tooling like linting and formatting.<\/p>\n<p><strong>Testable<\/strong> simply means that the snapshot that will be captured by the\ncommit, is able to successfully run the automated tests without failure.<\/p>\n<p>These are crucial for a number of reasons. For one, Git provides tools that\ndepend on these characteristics being met. For example <code>git bisect<\/code>, an\nextremely valuable tool that aids in automating finding a commit that\nintroduced a particular issue. If your commits aren't <strong>buildable<\/strong> and\n<strong>testable<\/strong> <code>git bisect<\/code> doesn't work in an automated fashion.<\/p>\n<p>When your commits are <strong>not<\/strong> testable your team will quickly lose faith in the\nvalue that the test suite is providing, which will generally result in people's\ntesting practices waning, likely due to the Broken Windows Theory.<\/p>\n<p>Beyond that if your commits are <strong>not<\/strong> buildable your teammates will quickly\nbecome irritated with you because your commits will be impeding them from\nmaking progress on the things they are trying to push forward.<\/p>\n<h3 id=\"scope-intent\">Scope &amp; Intent<\/h3>\n<p>At this point we know that that commits have a snapshot of the project and an\nassociated message that should abide by the official format, and that the\ncommit should be <strong>Buildable<\/strong> and <strong>Testable<\/strong>. However, we don't have any\nreal guidance in terms of what should be included in the snapshot and the\ncommit message.<\/p>\n<p>This is where scope and intent come into play. Scope and intent are the key to\none of the most important software engineering practices of today,\nunderstanding and properly defining commits.<\/p>\n<p>When we are making changes to software it is crucial to think about the\n<strong>intent<\/strong> behind the change. This can also be thought of as simply answering\nthe question, \"Why was this change made?\" However, thinking about the\n<strong>intent<\/strong> alone is not enough. We also need to think about the <strong>scope<\/strong> in\nterms of the various irreducible (a.k.a. atomic) components of the proposed\nchange. This is due to the fact that the <strong>intents<\/strong> of a change are the\ncombination of the individual <strong>intent<\/strong> of each irreducible component.<\/p>\n<p>This sounds a bit theoretical at this point. But it is actually something that\nis quite concrete and is very easy to see when simply looking at a pull\nrequest's changes. All we have to do is ask ourselves the question, \"Why was\nthis change made?\", and then as we are looking at the code, validate whether\nthat specific code change aligns with that intent.<\/p>\n<p>I recently reviewed a pull request that was created to tackle a particular bug.\nHowever, when looking at the pull request there were a lot more changes than I\nexpected to see. I wasn't sure why those changes were there, but they didn't\nseem to be required to fix the bug.<\/p>\n<p>At this point there is no way anyone should sign off on this pull request as we\nreally have no idea what it is doing, let alone, why these changes were made.<\/p>\n<p>So I talked to the developer that made the pull request and through\nconversation we determined that the changes that weren't specifically fixing\nthe bug were made for a handful of different reasons, to eliminate duplication,\nto fix linting errors\/warnings, and to clean up a toggle.<\/p>\n<p>It looks like we have a bit of a miss alignment here in terms of <strong>intent<\/strong>\nbecause we have code that exists with intents other than <strong>fix the bug<\/strong>. More\naccurately, we have a bit of <strong>scope creep<\/strong>.<\/p>\n<p>If we look at the pull request visually in terms of <strong>intents<\/strong> and <strong>scope<\/strong>\nit would currently look something like the following.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/git-commit-creation\/scope-and-intent-001.png\" alt=\"Visual of commit with individual intents but one scope\" \/><\/p>\n<p>In the above we can visually see that in this case the current scope of the\ncommit involves the following intents.<\/p>\n<ul>\n<li>Commit A\n<ul>\n<li>literal bug fix<\/li>\n<li>clean up toggle<\/li>\n<li>fix lints<\/li>\n<li>correct duplication<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>But, it is a single commit and that commit only has one scope. You could\ntheoretically argue that you can include in the commit message details\ndescribing that the commit addresses all these intents. But that would not\naddress the issue that when looking at the code you wouldn't be able to\ndistinguish if a line of code was changed to fix a linting rule or changed to\naddress the duplication, etc.<\/p>\n<p>So a much better approach is to create separate commits, one for each of the\nintents. This binding between an intent and a commit, which has a singular\nscope, is a crucial underpinning to building maintainable software systems\nefficiently.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/git-commit-creation\/scope-and-intent-002.png\" alt=\"Visual of commits with paired individual intents and scopes\" \/><\/p>\n<p>The above visually represents each scope as a different commit paired with each\nrespective intent. So we would effectively have the following commits.<\/p>\n<ul>\n<li>Commit A: literal bug fix<\/li>\n<li>Commit B: clean up toggle<\/li>\n<li>Commit C: fix lints<\/li>\n<li>Commit D: correct duplication<\/li>\n<\/ul>\n<p>This fundamentally makes the peer review process go much quicker as the commits\nare focused on a singular narrowed intent, and provide the critical context in\nthe commit message.<\/p>\n<h4 id=\"intent-architecture\">Intent &amp; Architecture<\/h4>\n<p>As the old adage says, \"There's more than one way to skin a cat.\" That\nstatement is true in the context of defining intents as well. However, that\ndoesn't mean that all the ways of defining intents are equal.<\/p>\n<p>For example. We could take and break out our commits for a project as follows.<\/p>\n<ul>\n<li>initial set up of listing action v2<\/li>\n<li>added api calls, and computed variables to allow for checking which CTA to display<\/li>\n<li>finished functionality for all of the child components<\/li>\n<\/ul>\n<p>If we look at the summaries we can maybe draw the following diagram based on\nunderstanding.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/git-commit-creation\/intent-and-architecture-non-arch-bound.png\" alt=\"Visual of architecture diagram derived from intent summaries not bound to arch concepts\" \/><\/p>\n<p>If we just look at the summaries and diagram above we can gather the following.<\/p>\n<ol>\n<li>That there is some initial setup. Though we are not sure what that is\nexactly without looking deeper.<\/li>\n<li>That some API calls and computed variables were added to facilitate checking\nwhich CTA to display. But we don't have any information about what\ncomponent(s) are responsible for\/involved in this.<\/li>\n<li>We know some sort of work has been done to finish the functionality for all\nthe child components. But we don't know what that is without looking\ndeeper.<\/li>\n<li>And we know that there is some concept of listing action v2 but don't really\nunderstand how it relates to anything else.<\/li>\n<\/ol>\n<p>The above is technically broken out into intentions. The first intention was,\n\"setup listing action v2\", the second was \"add api calls &amp; computed variables to\nallow for checking which CTA to display\", and the third was \"finishing up some\nfunctionality\".<\/p>\n<p>It is worth noting that the second intention has a smell as it has the keyword,\n<strong>and<\/strong>, in the description. Hinting there are probably multiple intents within\nthe scope of that commit.<\/p>\n<p>Despite multiple intents in that scope, this is definitely a step in the right\ndirection as the commits are broken out into intentions.<\/p>\n<p>But we can do much better with a slight shift in our thinking about intents.<\/p>\n<p>If we add the constraint that our intents should generally be related to\napplication architecture concepts. Things like modules, classes, methods,\nfunctions, services, etc. We would have likely defined the intents and commits\nas follows.<\/p>\n<ul>\n<li>add can apply, reason props <code>ProfessionalViewListing<\/code><\/li>\n<li>mod <code>ProfessionalListing<\/code> API client to map props<\/li>\n<li>add listing actions components (e.g. <code>AcceptShift<\/code>)<\/li>\n<li>add <code>ListingActionV2<\/code> to show proper action component<\/li>\n<li>register the <code>listing_action_v2_fe<\/code> toggle<\/li>\n<li>mod the <code>Listing<\/code> view to show <code>ActionV2<\/code> or <code>ActionV1<\/code><\/li>\n<\/ul>\n<p>This is fantastic as now we have lots of information just from the summaries of\nthese commits. In fact, it gives us enough information to draw the following\ndiagram of the architecture involved.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/git-commit-creation\/intent-and-architecture-arch-bound.png\" alt=\"Visual of architecture diagram derived from intent summaries bound to arch concepts\" \/><\/p>\n<p>So from the above diagram we can see that the commit summaries containing\nintent around different architectural concepts, as an approach, has provided us\nwith a lot more value in terms of understanding changes as well as the code\nbase itself.<\/p>\n<p>In particular, we know that there is some sort of collection of Listing Action\ncomponents and that the <code>ListingActionV2<\/code> component is responsible for choosing\nwhich one to render based on some criteria. We also know that the <code>Listing View<\/code> exists and that it conditionally chooses to render either the\n<code>ListingActionV1<\/code> component or the <code>ListingActionV2<\/code> component based on the\nregistered <code>listing_action_v2_fe<\/code> toggle most likely. We also know that there\nis a <code>ProfessionalViewListing<\/code> that received the <code>can_apply<\/code> and <code>reason<\/code>\nproperties. As well as that there was a <code>ProfessionalListingApiClient<\/code> which\nwas modified to map properties, which are most likely the properties that were\njust added in the previous commit.<\/p>\n<p>We were almost able to build a complete architectural diagram expressing the\nrelationships between the various components involved just from the commit\nsummaries. The one thing we are missing is just how the API client relates\nto the rest of the architectural concepts.<\/p>\n<p>It is amazing how valuable this is and how much it facilitates rapidly gaining\ncontext around an area of effort. So, I hope this makes it clear that even\nthough we can define intents in a bunch of different ways. We generally should\ndefine them based on architectural concepts due to the massive gains that we\nget.<\/p>\n<h4 id=\"fallback-on-scope-intent\">Fallback on Scope &amp; Intent<\/h4>\n<p>Now, you may have noticed that I said aligning your intent is generally the\nright thing to do. That is because there are scenarios where it may not be\nfitting. An example, where you would likely want to fall back to simply using\nScope &amp; Intent to define your commit, is when you are switching from not having\nautomated formatting in a project to having automated formatting.<\/p>\n<p>In this case you could try and argue that you could format sections of the code\nbased on architectural concepts. Though, generally this isn't practical due to\nthe fact that the tooling doesn't make it easy to apply auto formatting only to\nan architectural concept within the code, and secondly because most of the time\npeople don't have a solid well organized architecture to help with this.<\/p>\n<p>So instead you create a commit that formats all the files in the code base. It\ncould be thousands of files, but you get it done in one fell swoop. And you\nmake the summary of the commit clearly communicate the scope &amp; intent of\nformatting all the files.<\/p>\n<p>A similar scenario would be enabling linting across a project that didn't have\nlinting before.<\/p>\n<p>In my experience scenarios that are incompatible with architectural alignment\nare pretty rare. So we should always make sure we are putting effort in to\nstrive for architectural alignment and only fall back to solely scope and\nintent after exhausting all the ways to facilitate architectural alignment.<\/p>\n<h3 id=\"more-than-a-summary\">More than a summary<\/h3>\n<p>At this point we have great guidance in terms using scope &amp; intent to define\nour commits and their commit summaries. And we have learned how valuable a\ncommit's summary can be when the commit is defined with a singular intent\naround architectural components. However, the commit summaries are limited to\n50 characters, or less. Which makes it difficult to fully communicate the\nintention of a change. Let alone provide any additional information about the\napproach that was taken and why.<\/p>\n<p>Luckily, the Git commit message format supports providing any number of\nparagraphs of detail as long as they have blank lines separating them, and they\nare hard wrapped to 72 characters. This allows us to define our own Git commit\nformat based on the official Git commit format plus our learnings from above.<\/p>\n<p>The following is a Git commit message format I have refined over the past 24+\nyears of development experience.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>Short summary of change (&lt;= 50 chars in len)<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>One or more paragraphs explaining the INTENTION &amp; REASON<\/span><\/span>\n<span class=\"giallo-l\"><span>for the change. (hard wrapped at 72 characters)<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>One or more paragraphs explaining the APPROACH taken to<\/span><\/span>\n<span class=\"giallo-l\"><span>attempt to achieve the above described INTENTION or a<\/span><\/span>\n<span class=\"giallo-l\"><span>portion of the INTENTION. (hard wrapped at 72 characters)<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>Associated Ticket Identifiers<\/span><\/span><\/code><\/pre>\n<p>The short summary should describe the change. I like to think of this as the\n<strong>what<\/strong> of the commit. It is valuable to give a high level understanding of\nwhat change the commit represents.<\/p>\n<p>The intention &amp; reason paragraph on the other hand is by far the most valuable\npart of the commit message as it is something that is <strong>impossible<\/strong> to get\nfrom simply reading the code. I like to think of this as the <strong>why<\/strong> of the\ncommit.<\/p>\n<p>Following that is the paragraph covering the approach. This is something we can\nkind of get from reading the code, but not entirely. So we include this as\nadditional context in the commit message. You can think of this as the <strong>how<\/strong>\nof the commit.<\/p>\n<p>Lastly, we provide any associated ticket references. This facilitates ticketing\nsystems tracking commits and their association to tickets in the ticketing\nsystems. This is an extremely beneficial thing to include as it facilitates\ngoing back and finding even more context about a change.<\/p>\n<p>So for every commit we provide the <strong>what<\/strong>, <strong>why<\/strong>, <strong>how<\/strong>, and <strong>associated\ntickets<\/strong> so that peers reviewing the changes have all the associated context\nand so that in the future if we, or another dev, need to understand the intent\nand context of a change we can easily get it from the commit message.<\/p>\n<h3 id=\"proof-of-work\">Proof of Work<\/h3>\n<p>When you create your commits taking the above into consideration another\nnatural benefit appears. It is something that prominent development teams, like\nthe Linux Kernel and Git development teams, talk about in respect to their Git\ncommit requirements. It is this concept of Proof of Work.<\/p>\n<p>The idea is that when you submit a series of commits in a particular order, the\ncommits and the order should communicate the steps you are taking to accomplish\nthe higher level goal &amp; intent of the pull request or patch series.<\/p>\n<p>Think of the commits being the equivalent of writing out the steps you\nperformed when solving a math problem as a kid. In fact, it carries the same\nbenefits. It helps you and others see how you got to the resulting answer. And\nif that answer isn't 100% correct, it helps you or others see the various steps\nyou took and be able to investigate or validate each one of them.<\/p>\n<p>Additionally, if we are following the practice above of binding scope and\nintent to architecture, then it also provides an understanding of how the\ngreater intent was accomplished in the terms of the application architecture.\nThis is extremely valuable from both a peer review standpoint, and from the\nstandpoint of trying to understand changes at a later point in time.<\/p>\n<h2 id=\"tactics\">Tactics<\/h2>\n<p>We have covered some of the details about what commits are, what their contents\nand message should contain, and the arguments backing them. However, we haven't\nreally started covering how you actually go about creating commits like this.\nSo let's shift more into the mental hurdles and the tactical skills needed to\nactually create better commits.<\/p>\n<h3 id=\"commit-permanence\">Commit Permanence<\/h3>\n<p>One of the biggest hurdles people seem to have when shifting from making\nhorrible commits to making great commits, is breaking this misconception that\ncommits are permanent the moment you create them.<\/p>\n<p>I don't know where this misconception comes from. But in my experience it is\nquite prevalent. But it is clear that it doesn't come from Git proper. You can\nsee this fairly quickly just by looking at some of the operations that Git\nfacilitates you performing on commits.<\/p>\n<ul>\n<li><code>git commit --amend<\/code><\/li>\n<li><code>git commit --fixup reword<\/code><\/li>\n<li><code>git commit --fixup amend<\/code><\/li>\n<li><code>git commit --squash<\/code><\/li>\n<li><code>git commit --reset-author<\/code><\/li>\n<li><code>git commit --author<\/code><\/li>\n<\/ul>\n<p>The above is all just in the <code>git commit<\/code> command. There is even more once we\nlook into <code>git rebase<\/code> and other commands.<\/p>\n<p>From looking at the above set of commands alone, it is clear from a Git\nperspective, that commits are made to be modified &amp; mutated at least up until\nsome point in time. It is also clear that, that point in time is <strong>not<\/strong> at the\nmoment of creation of the commit.<\/p>\n<p>So what is that moment of time when a commit should become permanent?<\/p>\n<p>I would argue that it is the moment that a commit is integrated into the\nmainline branch. Prior to that moment the commits should be free to be refined\nand reworked.<\/p>\n<h4 id=\"isn-t-rewriting-history-bad\">Isn't Rewriting History Bad<\/h4>\n<p>If we take that stance though then we have to be ok rewriting history, even\nonce it has been shared. And isn't rewriting shared history a big no no?<\/p>\n<p>Yes and no. Let me explain.<\/p>\n<p>There are cases where it is giant pain in the butt and can be extremely\ndisruptive to other developers if you rewrite shared history. One such scenario\nis if you were to rewrite the history in the published mainline of a\nrepository. In general, it is bad form to rewrite the history of mainline\nof a repository. Although, sometimes it is necessary and worth the disruption,\ne.g. to strip accidentally shared credentials, etc.<\/p>\n<p>So just like everything else in engineering. There are pros and cons. We just\nneed to make sure we understand the pros and cons and choose the path that fits\nour needs the best.<\/p>\n<h5 id=\"rewrite-pull-request-history\">Rewrite Pull Request History<\/h5>\n<p>If we look at the scenario of rewriting history in a shared pull-request\nbranch.<\/p>\n<p>It enables us to have commits that can communicate a ton of context and\nvalue in terms of architecture. Additionally, it facilitates reverting specific\ntargeted changes if necessary. As well as answering questions about the intent\nof changes over time in relation to architectural concepts. And bisecting to\nidentify a specific change that introduced an issue and what the intention of\nthat change was. Just to name a few.<\/p>\n<p>At the same time it has the drawback that it can break direct code\ncollaboration if there isn't communication between the parties. Generally\nthough, there isn't much direct code collaboration on pull requests as the\nauthor is usually the one making the changes. And if the reviewer wants to make\na change and push it up to the pull request branch. All they really need to do\nis let the pull request author know that they have pushed up a change, so they\ncan manage integrating it into the final commit.<\/p>\n<h5 id=\"squash-merge\">Squash &amp; Merge<\/h5>\n<p>Another approach is to take a hard-lined stance about not rewriting any shared\nhistory, including pull requests. This means that the only way we can bring in\nchanges from mainline is to back merge mainline into our branch. It also means\nthat we can't refine our commits once they are pushed up to a pull request. In\nturn requiring us to add additional commits to the pull request that are not\nlogically structured and are not representative of the application architecture\nor the intents at all.<\/p>\n<p>This in turn results in a bunch of commits, that are meaningless to the Git\nhistory existing within the pull request branch. And given that these commits\nare generally <strong>not buildable<\/strong> &amp; <strong>not testable<\/strong> they result in breaking <code>git bisect<\/code> and other functionality.<\/p>\n<p>To help mitigate this people will do a \"Squash &amp; Merge\" in which all the\ncommits within the pull request branch are squashed together into a singular\ncommit. This helps resolve the <strong>buildable<\/strong> &amp; <strong>testable<\/strong> characteristics in\nthe majority of cases. However, we have completely lost the ability to revert\ntargeted changes and the ability to even understand the intention of the\nchanges from an architectural perspective.<\/p>\n<p>The only thing we have gained from this is \"smoother direct code\ncollaboration\". Meaning that in the more rare scenarios when you are reviewing\na pull request, and you want to push a change up to it. You don't need to let\nthe author know that you are pushing a change up to their branch. Although you\ngenerally do anyway.<\/p>\n<h5 id=\"the-choice\">The Choice<\/h5>\n<p>The choice is pretty clear to me as the overhead of pinging the author via\nmessaging is trivial, as a cost for rewriting pull-request history. And it\nunlocks a wealth of benefits. Enabling our commit's moment of permanence to be\nwhen the commit is integrated into the upstream mainline.<\/p>\n<p>If you want to get a deeper understanding of this in relation to the Git tree.\nCheck out my article, <a href=\"\/blog\/when-to-rewrite-git-history\/\">When to rewrite Git\nhistory<\/a>.<\/p>\n<h3 id=\"but-how-do-we-do-this\">But How do we do this?<\/h3>\n<p>You might be thinking. How do I actually accomplish this?<\/p>\n<h4 id=\"learn-git\">Learn Git<\/h4>\n<p>Well the extremely short answer is simply, learn more about Git.<\/p>\n<p>It really boils down to learning a few commands, <code>git add -p<\/code>, <code>git commit --amend<\/code>, and <code>git rebase -i<\/code>. It is worth noting that it is crucial that you\nhave a solid understanding of Git commits and the Git commit tree in general\nwhen truly understanding these commands.<\/p>\n<p>The following is an outline of the related\noperations you would perform with each of those commands and links to\nrespective documentation or guides.<\/p>\n<p><em>Note:<\/em> I included links to guides for <a rel=\"external\" href=\"https:\/\/git-ps.sh\">Git Patch Stack<\/a>, a\nGit extension that facilitates a patch stack workflow with Git, because it\ndocuments the main commit mutation operations with <code>gps rebase<\/code> which is really\njust a convenience mechanism for <code>git rebase -i<\/code>.<\/p>\n<ul>\n<li>creating multiple commits from uncommitted changes\n<ul>\n<li>Add hunks instead of entire file <code>git add -p &lt;patch\/to\/file&gt;<\/code>\n<ul>\n<li><a rel=\"external\" href=\"https:\/\/git-scm.com\/docs\/git-add#Documentation\/git-add.txt---patch\">git add -p manpage<\/a><\/li>\n<\/ul>\n<\/li>\n<li>Use a Git GUI to stage individual lines or selections\n<ul>\n<li>My article on <a href=\"\/blog\/git-add-patch-wont-split\/\">git add -p won't split<\/a><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<li>amending a commit <code>git commit --amend<\/code>\n<ul>\n<li><a rel=\"external\" href=\"https:\/\/git-scm.com\/docs\/git-commit#Documentation\/git-commit.txt---amend\">git commit --amend manpage<\/a><\/li>\n<\/ul>\n<\/li>\n<li><code>git rebase -i<\/code> <a rel=\"external\" href=\"https:\/\/git-scm.com\/book\/en\/v2\/Git-Branching-Rebasing\">ProGit Book - 3.6 Branching - Rebasing<\/a> <a rel=\"external\" href=\"https:\/\/git-scm.com\/docs\/git-rebase#Documentation\/git-rebase.txt--i\">git rebase -i manpage<\/a>\n<ul>\n<li>edit a commit <a rel=\"external\" href=\"https:\/\/book.git-ps.sh\/guides\/edit-a-patch\">Git Patch Stack: Docs - Edit a patch<\/a><\/li>\n<li>reword a commit message\n<ul>\n<li><code>git rebase -i<\/code> and mark for <code>reword<\/code><\/li>\n<\/ul>\n<\/li>\n<li>reorder commits <a rel=\"external\" href=\"https:\/\/book.git-ps.sh\/guides\/reorder-patches\">Git Patch Stack: Docs - Reorder patches<\/a><\/li>\n<li>drop a commit <a rel=\"external\" href=\"https:\/\/book.git-ps.sh\/guides\/drop-a-patch\">Git Patch Stack: Docs - Drop a patch<\/a><\/li>\n<li>add a commit in the middle <a rel=\"external\" href=\"https:\/\/book.git-ps.sh\/guides\/add-patch-in-the-middle\">Git Patch Stack: Docs - Add a patch in the middle<\/a><\/li>\n<li>fix a commit up into another commit <a rel=\"external\" href=\"https:\/\/book.git-ps.sh\/guides\/combine-multiple-patches\">Git Patch Stack: Docs - Combine multiple patches<\/a><\/li>\n<li>squash commits together <a rel=\"external\" href=\"https:\/\/book.git-ps.sh\/guides\/combine-multiple-patches\">Git Patch Stack: Docs - Combine multiple patches<\/a><\/li>\n<li>split a commit up into multiple commits\n<ul>\n<li><a rel=\"external\" href=\"https:\/\/git-scm.com\/docs\/git-rebase#_splitting_commits\">git rebase manpage splitting commits<\/a><\/li>\n<li>My article on <a href=\"\/blog\/git-splitting-commits\/\">Git - Splitting Commits<\/a><\/li>\n<li><a rel=\"external\" href=\"https:\/\/book.git-ps.sh\/guides\/split-a-patch-up\">Git Patch Stack: Docs - Split a patch up<\/a><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h4 id=\"wip-commits\">WIP Commits<\/h4>\n<p>In addition to learning Git. There is another concept that is extremely\nvaluable now that you are living in this world of commits that you evolve over\ntime. And that is the WIP (Work in Progress) commit.<\/p>\n<p>This is simply a convention of prefixing the commit summary with <code>WIP:<\/code> to let\nourselves know that the commit still needs further refinement before we submit\nit in a pull request or integrate it into mainline.<\/p>\n<p>Once we have refined it to the point that we are happy we simply update the\ncommit summary, removing the <code>WIP:<\/code> prefix. In turn finalizing our commit\nconceptually.<\/p>\n<h4 id=\"commit-message-template\">Commit Message Template<\/h4>\n<p>Beyond that. There are things that are just hard to remember. The\nvarious parts of the commit message can be one of those things. Luckily Git\nhelps us out by providing support for commit message templates via the\n<code>commit.template<\/code> configuration setting.<\/p>\n<p>If you want to set this up you can do so by running the following command.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>git config --global commit.template ~\/.gitmessage.txt<\/span><\/span><\/code><\/pre>\n<p>The above configures Git to look for the commit message at <code>~\/.gitmessage.txt<\/code>.\nThis template file is simply a text file that is loaded up into the editor when\nyou go to create a commit.<\/p>\n<p>The contents of my <code>~\/.gitmessage.txt<\/code> are as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Short summary of change (the what) ( &lt;= 50 chars )<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># INTENTION of the change &amp; REASON for change<\/span><\/span>\n<span class=\"giallo-l\"><span># (Hard wrapped at 72 chars) <\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># APPROACH taken &amp; WHY in relation to attempting to achieve INTENTION<\/span><\/span>\n<span class=\"giallo-l\"><span># (Hard wrapped at 72 characters)<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Associated Ticket Identifers<\/span><\/span><\/code><\/pre>\n<p>The layout I provided is based on making it efficient to enter. Starting out\nwhen it loads it in the editor it drops you into the top left corner ready to\nstart writing your 50 character or less summary.<\/p>\n<p>It is important to realize that any line in the Git commit message starting\nwith a <code>#<\/code> is considered a comment and will <strong>not<\/strong> be included in the final\ncommit message. This means that I don't actually need to replace any of these\ncomments. Instead, I just add the respective content below each comment.\nThe one exception to this is the short summary which is added above the comment\ninstead of below. It is also worth mentioning that you will want to keep a\nblank line between each of these sections.<\/p>\n<p>There are many more things you can include in your commit messages and the\ntemplate to help you. Some people add explicit change log entries to their\ncommit messages that are targeted at the consumers of the product rather than\nthe developers. Others manage sign-off from peer developers through commit\nmessage standards they have defined, etc.<\/p>\n<p>In my opinion, all these things can be valuable, and should be considered, but\nthe above template really outlines the core that should always be present.<\/p>\n<h4 id=\"buildable-testable-enforcement\">Buildable &amp; Testable Enforcement<\/h4>\n<p>The <strong>buildable<\/strong> and <strong>testable<\/strong> characteristics are another thing that are\neasy to let slip by if we are just manually verifying them.<\/p>\n<p>To help us hold to these requirements some people set up a Git pre-commit hook\nto make sure that each commit is buildable &amp; testable prior to commit creation.<\/p>\n<p>In my experience this isn't the ideal enforcement mechanism because it assumes\nthat commits are final and that you aren't going to iterate on them or evolve\nthem. Which we have seen above is pretty damn important.<\/p>\n<p>A more appropriate hook would be a hook that is triggered when you want to\neither request that someone review a commit or integrate a commit. In turn\nhelping make sure that any commit you are going to share either via integration\ninto mainline or via pull request\/patch is buildable and testable. Git doesn't\nnatively have such a hook. But if you use <a rel=\"external\" href=\"https:\/\/git-ps.sh\">Git Patch Stack<\/a> it easily\nsupports this via it's <a rel=\"external\" href=\"https:\/\/book.git-ps.sh\/tool\/hooks\">isolate_post_checkout\nhook<\/a>.<\/p>\n<p>You might be thinking. I don't need to worry about this. We have a CI\n(Continuous Integration) service that makes sure my pull requests are both\nbuildable and testable. But in general this isn't a valid enforcement mechanism\nas most teams don't strictly only create pull requests consisting of a single\ncommit.<\/p>\n<p>So if we make sure that our commits are buildable and testable it in turn\nguarantees that our pull requests are buildable and testable. However, if we\nonly make sure that our pull request is buildable and testable it is quite\npossible, if not likely, that one of the commits in the pull request in <strong>not<\/strong>\nbuildable or testable.<\/p>\n<h4 id=\"outside-in\">Outside-In<\/h4>\n<p>Outside-In is an engineering practice in which you drive changes from the\noutermost thing first and work your way inward. Generally, the outermost thing\nis the UI layer.<\/p>\n<p>The massive benefit of this is that you always end up building only what you\nneed to build as it is driven by the need of the layer above. This is in\nopposition to inside-out or middle-out development in which you try and guess\nthe needs of the outer layers and build the inner\/middle layers first.\nGenerally this results in your guesses being not quite right and having to do\nrework or even worse including additional complexity and functionality that\nisn't needed by the layers above.<\/p>\n<p>Outside-In is extremely useful in helping you think about the architecture\nconcepts. As you are effectively working from the outermost architectural\nconcepts, to the innermost architectural concepts, and then working your way\nback up from the inside-out, connecting each of the layers.<\/p>\n<p>Using <code>WIP<\/code> commits can aid this process as your outer layers can start as\n<code>WIP<\/code> commits until you reach that innermost layer which ends up being a\ncomplete logical chunk. Then as you work your way back up the layers connecting\nthem to the layer below you are effectively finalizing them and getting rid of\nthe <code>WIP<\/code>.<\/p>\n<p>To get a better understanding of Outside-In with an example you can check out\nmy article, <a href=\"\/blog\/how-we-should-be-using-git\/\">How we should be using Git<\/a>.<\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>At this point you should hopefully have enough knowledge and context to be able\nto start creating meaningful, logically chunked, buildable and testable commits\nthat are bound to scope, intent and architecture. Resulting in a valuable Git\nrepository history that will aid you through the short-term and long-term\nmaintenance and development of the project.<\/p>\n"},{"title":"When to rewrite Git history?","published":"2024-06-13T00:00:00+00:00","updated":"2024-06-13T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/when-to-rewrite-git-history\/"}},"id":"https:\/\/drewdeponte.com\/blog\/when-to-rewrite-git-history\/","content":"<p>Ok, so let's talk about Git and this \"rule\" that people often get attached to,\n\"Don't rewrite history once it is shared!\"<\/p>\n<p>There is definitely some wisdom in this \"rule\". However, following this \"rule\"\nreligiously also has some side effects. And it is worth understanding those in\nmore depth, as well as understanding any alternatives and what their pros &amp;\ncons might be.<\/p>\n<p>When you follow this rule we have to ask, how do the branches we are working on\nget updated with changes other developers have already integrated into <code>main<\/code>?\nWell, there are a couple of approaches we can use depending on our situation.\nTo explore these, let's assume we start with a Git tree that looks as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>* 9cabcd2 - (HEAD -&gt; main, origin\/main) commit C<\/span><\/span>\n<span class=\"giallo-l\"><span>* 6327c81 - commit B<\/span><\/span>\n<span class=\"giallo-l\"><span>* b68a192 - commit A<\/span><\/span><\/code><\/pre>\n<p>Now let's assume we have created some changes in a branch locally that we\n<strong>have not<\/strong> pushed up to the remote yet. And that someone else has integrated\nanother change into <code>main<\/code>.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>* a0aca6f - (main, origin\/main) commit G<\/span><\/span>\n<span class=\"giallo-l\"><span>| * fe06e90 - (HEAD -&gt; feature\/x) commit F<\/span><\/span>\n<span class=\"giallo-l\"><span>| * 81312b5 - commit E<\/span><\/span>\n<span class=\"giallo-l\"><span>| * f09acea - commit D<\/span><\/span>\n<span class=\"giallo-l\"><span>|\/ <\/span><\/span>\n<span class=\"giallo-l\"><span>* 9cabcd2 - commit C<\/span><\/span>\n<span class=\"giallo-l\"><span>* 6327c81 - commit B<\/span><\/span>\n<span class=\"giallo-l\"><span>* b68a192 - commit A<\/span><\/span><\/code><\/pre>\n<p>One option we have to include the new change in <code>main<\/code> as part of our\n<code>feature\/x<\/code> branch is to use <code>git rebase<\/code>. This can effectively take the\ncommits from our branch and replay them on top of the commit that was added to\n<code>main<\/code>. Our tree would look as follows after doing this.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>* ae46190 - (HEAD -&gt; feature\/x) commit F&#39;<\/span><\/span>\n<span class=\"giallo-l\"><span>* 512b2ba - commit E&#39;<\/span><\/span>\n<span class=\"giallo-l\"><span>* d09efea - commit D&#39;<\/span><\/span>\n<span class=\"giallo-l\"><span>* a0aca6f - (main, origin\/main) commit G<\/span><\/span>\n<span class=\"giallo-l\"><span>* 9cabcd2 - commit C<\/span><\/span>\n<span class=\"giallo-l\"><span>* 6327c81 - commit B<\/span><\/span>\n<span class=\"giallo-l\"><span>* b68a192 - commit A<\/span><\/span><\/code><\/pre>\n<p>This works great! It keeps our tree clean, simple, linear, and easy to\nunderstand. However, <code>commit D<\/code>, <code>commit E<\/code>, and <code>commit F<\/code> all have different\nSHAs now, and are effectively <code>commit D'<\/code>, <code>commit E'<\/code>, and <code>commit F'<\/code>. This\nis totally fine right now because we hadn't shared <code>commit D<\/code>, <code>commit E<\/code> and\n<code>commit F<\/code> with anyone yet.<\/p>\n<p>However, if we had shared commits <code>D<\/code>, <code>E<\/code>, and <code>F<\/code> already by pushing our\nbranch up to the remote. We would have technically shared that history. And our\nGit tree would look as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>* a0aca6f - (main, origin\/main) commit G<\/span><\/span>\n<span class=\"giallo-l\"><span>| * fe06e90 - (HEAD -&gt; feature\/x, origin\/feature\/x) commit F<\/span><\/span>\n<span class=\"giallo-l\"><span>| * 81312b5 - commit E<\/span><\/span>\n<span class=\"giallo-l\"><span>| * f09acea - commit D<\/span><\/span>\n<span class=\"giallo-l\"><span>|\/ <\/span><\/span>\n<span class=\"giallo-l\"><span>* 9cabcd2 - commit C<\/span><\/span>\n<span class=\"giallo-l\"><span>* 6327c81 - commit B<\/span><\/span>\n<span class=\"giallo-l\"><span>* b68a192 - commit A<\/span><\/span><\/code><\/pre>\n<p>Therefore, when following this rule we can no longer use <code>git rebase<\/code> to base\nour branch on top of the latest <code>main<\/code> because it would accomplish that by\nrewriting the commits in the branch. Rewriting the commits could create\ncomplications for other developers who happen to be working on the previous\nstate of the commits as the new commits have now diverged.<\/p>\n<p>Instead, we need to bring the new commits in <code>main<\/code> into our <code>feature\/x<\/code> branch\nin a way that doesn't rewrite history. <code>git merge<\/code> is exactly the command we\nare looking for. As it creates a new merge commit that has multiple parent\ncommits. If we ran <code>git merge main<\/code> it would look something like the following.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>* f1cca4d - N - (HEAD -&gt; feature\/x) commit H<\/span><\/span>\n<span class=\"giallo-l\"><span>|\\<\/span><\/span>\n<span class=\"giallo-l\"><span>| * fe06e90 - N - (origin\/feature\/x) commit F<\/span><\/span>\n<span class=\"giallo-l\"><span>| * 81312b5 - N - commit E<\/span><\/span>\n<span class=\"giallo-l\"><span>| * f09acea - N - commit D<\/span><\/span>\n<span class=\"giallo-l\"><span>* a0aca6f - N - (main, origin\/main) commit G<\/span><\/span>\n<span class=\"giallo-l\"><span>|\/ <\/span><\/span>\n<span class=\"giallo-l\"><span>* 9cabcd2 - G - commit C<\/span><\/span>\n<span class=\"giallo-l\"><span>* 6327c81 - N - commit B<\/span><\/span>\n<span class=\"giallo-l\"><span>* b68a192 - N - commit A<\/span><\/span><\/code><\/pre>\n<p>In the above Git tree we can see that <code>feature\/x<\/code> contains the same commits\nwith the same SHAs as before, but also contains a new commit that merges <code>main<\/code>\ninto <code>feature\/x<\/code>.<\/p>\n<p>This is great as it makes it so that you can easily collaborate with one or\nmore developers on the <code>feature\/x<\/code> branch. However, it also made the Git tree\nmore complex and complicated to understand. It might not be too bad with\none branch. But we haven't gotten to the step of merging our branch back into\n<code>main<\/code> at some point in the future. Or, the reality that often you need to\nmerge main into your feature branch multiple times as new changes are\nintroduced. Or, the reality of dealing with multiple branches doing the same\nthing at the same time. Generally, you will end up with a very complicated tree\nthat looks something like the following.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/when-to-rewrite-git-history\/.\/complicated-git-tree.png\" alt=\"image of extremely complicated git tree\" \/><\/p>\n<p>Having these forward &amp; backward merges happening also ends up eventually\nconfusing Git when it comes to its ability to find merge bases correctly, in\nturn limiting the functionality of Git.<\/p>\n<h2 id=\"what-is-the-alternative\">What is the Alternative?<\/h2>\n<p>The alternative is to simply use <code>git rebase<\/code> on your feature branches.\nHowever, we know that this can create problems for those collaborating on a\nfeature branch.<\/p>\n<p>So we simply have an understanding of how we are going to treat feature\nbranches by default. For example, we can say that by default feature branches\nare owned and controlled by their author. Anyone pulling down that branch and\ntesting it out or reviewing it should understand that the history is owned by\nthe author, and they will likely change it. Generally, the reviewing developer\nuses something like <code>git reset --hard origin\/feature\/x<\/code> to update their local\nstate to match that of the remote state.<\/p>\n<p>Now, if you communicate out of band with the developer that owns the feature\nbranch and let them know that you are going to push up a commit, they can\nintegrate it into their feature branch in their local git repo and still use\nrebase. It just requires some out of band coordination. But this is actually a\nrelatively rare situation for this to occur and therefore isn't a big deal.<\/p>\n<p>It is also worth noting that taking this approach allows you to use the \"Rebase\n&amp; Merge\" strategy in GitHub which will result in a linear Git history. Making\nit much easier to understand and manage over time.<\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>I have used both these strategies and many others throughout my career. But the\nease of understanding &amp; management of a linear Git tree is far more valuable\nthan simply not needing to worry about a tiny bit of out of band communication\nin the rare scenario that you need to directly contribute to someone else\nfeature branch.<\/p>\n<p>The key with this alternative is that you don't rewrite history for <code>main<\/code> as\nit is a central point of collaboration where you don't want to have deal with\nthe overhead of the out of bound communication and coordination. But due to the\nrareness of the need to directly contribute to other peoples feature branches\nthe overhead of the out-of-band communication is more than an acceptable trade.<\/p>\n"},{"title":"Reasonable Code Organization","published":"2024-03-27T00:00:00+00:00","updated":"2024-03-27T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/reasonable-code-organization\/"}},"id":"https:\/\/drewdeponte.com\/blog\/reasonable-code-organization\/","content":"<p>The following are some excerpts related to code organization from a code review\nI did recently.<\/p>\n<p>I started reviewing this PR and wasn't quite sure exactly what the separation\nof concerns were with the boundaries you had drawn. So I drew a diagram to try\nand help myself understand better. It ended up looking as follows.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/reasonable-code-organization\/diagram-at-beginning.png\" alt=\"original diagram\" \/><\/p>\n<p>From looking at the code in far more depth I started to get a better\nunderstanding but still wasn't sure about things. Largely, what component was\nresponsible exactly for what concerns.<\/p>\n<p>I figured out through reading a bunch of the code that\n<code>EventStreamQueryService<\/code> was for querying Athena for Events. Which roughly\nlines up with the diagram above. Part of what threw me on that was the naming\nhaving Stream in there. The following questions were flowing through my head.<\/p>\n<ul>\n<li>what does <code>Stream<\/code> have to do with querying?<\/li>\n<li>is it integrated with a stream somehow?<\/li>\n<li>why is Athena a peer?<\/li>\n<li>is something else using it?<\/li>\n<\/ul>\n<p>Beyond that even when I looked at the code of the Athena service it\nhas knowledge of <code>Events<\/code> which isn't an Athena thing at all.<\/p>\n<p>So, I figured I would throw a quick diagram together to show how I would think\nabout this problem architecturally and how I would draw the lines. That looks\nas follows.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/reasonable-code-organization\/ideal-diagram-dependencies.png\" alt=\"ideal diagram\" \/><\/p>\n<p>I tried to explain the responsibilities of each of the concepts in the text\nnext to it. The arrows represent their relationship in terms of dependence\n(what things depend on what things). This generally helps in terms of making\ndecisions about encapsulation of dependencies (a.k.a. are you going to hide a\ndependency away or are you going to expose it intentionally for other things to\nuse it).<\/p>\n<p>This naturally leads to the question. \"How do you organize this code so that it\nis far more consumable and easy to understand coming into the project?\"<\/p>\n<p>That is where modules come into play as they facilitate encapsulation of\ndependencies as well help to formalize interfaces. It is worth noting that I am\nnot talking about Nest.JS modules, though they can also facilitate\nencapsulation. I am simply talking about standard Typescript modules. If we\ntake the example above you could create a directory structure as such.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/reasonable-code-organization\/better-directory-structure.png\" alt=\"better directory structure\" \/><\/p>\n<p>Having a directory structure like this is extremely valuable because of the\nencapsulation. It tells me that as a consumer and developer of the <code>Event Stream Module<\/code>, that the <code>Event<\/code> and <code>EventStreamReplayService<\/code> are the public interface and\nentry point for the module. I know this very quickly just from looking at the\ndirectory structure.  Everything else is encapsulated and hidden away with in\ntheir respective modules. This is also extremely useful to know as a developer\nbecause I can understand for example that <code>EventStreamService<\/code> depends on\n<code>KinesisService<\/code> but that nothing else should because it is encapsulated within\nthe <code>EventStreamService<\/code> module.<\/p>\n<p>The same goes for <code>EventQueryService<\/code> module. It encapsulates the <code>Athena Service<\/code> telling me as a developer nothing else should be using that <code>Athena Service<\/code>. Instead, they should be going through the <code>EventQueryService<\/code> or if\nsome functionality doesn't make sense being added to <code>EventQueryService<\/code> then\nthe <code>AthenaService<\/code> should be elevated up to a higher level shared dependency.<\/p>\n<p>All of this, aids massively in communicating the boundaries and their actual\nassociation to the architecture itself.<\/p>\n<p>Something like what we originally had doesn't tell us much.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/reasonable-code-organization\/diagram-at-beginning.png\" alt=\"original diagram\" \/><\/p>\n<p>It tells me that a <code>StreamEvent<\/code> exists that a <code>EventStreamQueryService<\/code>\nexists, that a <code>EventStreamService<\/code> exists, and that a <code>AthenaService<\/code> exists,\nbut it doesn't tell me much of anything about their relationships or what their\nresponsibilities are intended to be.<\/p>\n<p>Not having clear boundaries allows you to easily fall into traps that make your\ncode far less maintainable and reusable. For example if we look at the\n<code>AthenaService.fetchQuery()<\/code> method.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">  async<\/span><span style=\"color: #B57EDC;\"> fetchQuery<\/span><span>(<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">    queryExecutionId<\/span><span>: <\/span><span style=\"color: #C6CCD7;\">string<\/span><span>,<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">    nextToken<\/span><span style=\"color: #E06C75;\">?:<\/span><span style=\"color: #C6CCD7;\"> string<\/span><span>,<\/span><\/span>\n<span class=\"giallo-l\"><span>  ): <\/span><span style=\"color: #61AFEF;\">Promise<\/span><span style=\"color: #E06C75;\">&lt;<\/span><span style=\"color: #C6CCD7;\">Result<\/span><span style=\"color: #E06C75;\">&lt;<\/span><span style=\"color: #C6CCD7;\">EventStreamQuery<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> AthenaServiceError<\/span><span style=\"color: #E06C75;\">&gt;&gt;<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span>    try {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">      const<\/span><span style=\"color: #C6CCD7;\"> data<\/span><span style=\"color: #E06C75;\"> = await this<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">client<\/span><span>.<\/span><span style=\"color: #B57EDC;\">send<\/span><span>(<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">        new<\/span><span style=\"color: #B57EDC;\"> GetQueryResultsCommand<\/span><span>({<\/span><\/span>\n<span class=\"giallo-l\"><span>          QueryExecutionId:<\/span><span style=\"color: #C6CCD7;\"> queryExecutionId<\/span><span>,<\/span><\/span>\n<span class=\"giallo-l\"><span>          NextToken:<\/span><span style=\"color: #C6CCD7;\"> nextToken<\/span><span>,<\/span><\/span>\n<span class=\"giallo-l\"><span>        }),<\/span><\/span>\n<span class=\"giallo-l\"><span>      )<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">      const<\/span><span style=\"color: #C6CCD7;\"> events<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> EventStreamEvent<\/span><span>[]<\/span><span style=\"color: #E06C75;\"> =<\/span><span> []<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">      data<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">ResultSet<\/span><span>?.<\/span><span style=\"color: #C6CCD7;\">Rows<\/span><span>?.<\/span><span style=\"color: #B57EDC;\">forEach<\/span><span>((<\/span><span style=\"color: #C6CCD7;\">row<\/span><span>)<\/span><span style=\"color: #61AFEF;\"> =&gt;<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">        events<\/span><span>.<\/span><span style=\"color: #B57EDC;\">push<\/span><span>({<\/span><\/span>\n<span class=\"giallo-l\"><span>          eventSource:<\/span><span style=\"color: #C6CCD7;\"> row<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">Data<\/span><span>[<\/span><span style=\"color: #56B6C2;\">0<\/span><span>].<\/span><span style=\"color: #C6CCD7;\">VarCharValue<\/span><span>,<\/span><\/span>\n<span class=\"giallo-l\"><span>          projectId:<\/span><span style=\"color: #C6CCD7;\"> row<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">Data<\/span><span>[<\/span><span style=\"color: #56B6C2;\">1<\/span><span>].<\/span><span style=\"color: #C6CCD7;\">VarCharValue<\/span><span>,<\/span><\/span>\n<span class=\"giallo-l\"><span>          occurredAt:<\/span><span style=\"color: #E06C75;\"> new<\/span><span style=\"color: #B57EDC;\"> Date<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">row<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">Data<\/span><span>[<\/span><span style=\"color: #56B6C2;\">2<\/span><span>].<\/span><span style=\"color: #C6CCD7;\">VarCharValue<\/span><span>),<\/span><\/span>\n<span class=\"giallo-l\"><span>          payload:<\/span><span style=\"color: #C6CCD7;\"> JSON<\/span><span>.<\/span><span style=\"color: #B57EDC;\">parse<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">row<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">Data<\/span><span>[<\/span><span style=\"color: #56B6C2;\">3<\/span><span>].<\/span><span style=\"color: #C6CCD7;\">VarCharValue<\/span><span>),<\/span><\/span>\n<span class=\"giallo-l\"><span>        })<\/span><\/span>\n<span class=\"giallo-l\"><span>      })<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">      return<\/span><span style=\"color: #B57EDC;\"> Ok<\/span><span>({<\/span><\/span>\n<span class=\"giallo-l\"><span>        queryExecutionId:<\/span><span style=\"color: #C6CCD7;\"> queryExecutionId<\/span><span>,<\/span><\/span>\n<span class=\"giallo-l\"><span>        state:<\/span><span style=\"color: #C6CCD7;\"> EventStreamQueryState<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">SUCCEEDED<\/span><span>,<\/span><\/span>\n<span class=\"giallo-l\"><span>        nextToken:<\/span><span style=\"color: #C6CCD7;\"> data<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">NextToken<\/span><span>,<\/span><\/span>\n<span class=\"giallo-l\"><span>        events:<\/span><span style=\"color: #C6CCD7;\"> events<\/span><span>,<\/span><\/span>\n<span class=\"giallo-l\"><span>      })<\/span><\/span>\n<span class=\"giallo-l\"><span>    } catch (error) {<\/span><\/span>\n<span class=\"giallo-l\"><span>      return <\/span><span style=\"color: #B57EDC;\">Err<\/span><span>(this.mapError(<\/span><span style=\"color: #C6CCD7;\">error<\/span><span>))<\/span><\/span>\n<span class=\"giallo-l\"><span>    }<\/span><\/span>\n<span class=\"giallo-l\"><span>  }<\/span><\/span><\/code><\/pre>\n<p>It is building a <code>EventStreamQuery<\/code> object as well as building an array of\n<code>EventStreamEvents<\/code>. This is a mixing of two concerns here. One is the work\nnecessary to deal with Athena client and the other is work that is structuring\ndata to be <code>Event<\/code> data.<\/p>\n<p>Let's say we decided to use <code>Athena<\/code> for something else in the future in this\nproject, or who knows maybe in another project. Because these two concerns are\nmixed here it would require much more work to rebuild all the context around\nthis problem space and safely do the extraction of just the <code>Athena<\/code> specific\npiece that could be reused. Keeping those boundaries clean now doesn't\ncost very much at all as the context is already present.<\/p>\n<p>If you aren't clear on <a rel=\"external\" href=\"https:\/\/www.typescriptlang.org\/docs\/handbook\/2\/modules.html\">TypeScript\nModules<\/a> it is a\nstandard language feature that is worth understanding. The pattern of having a\ndirectory represent your module with a <code>index.ts<\/code> file in it can be very\nuseful.<\/p>\n<p>To complete the exercise with the same type of diagram I started with, but\nin this proposed new world order. It would look like this.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/reasonable-code-organization\/side-by-side-comparison.png\" alt=\"side by side comparison\" \/><\/p>\n<p>This is of course possible to do with Nest.JS modules. However, it doesn't\nreally give you any advantage and adds more boilerplate code and complexity\nthan is necessary.<\/p>\n<p>I hope this has been useful and am always open to discuss further.<\/p>\n"},{"title":"My Keyboard","published":"2024-01-18T00:00:00+00:00","updated":"2024-01-18T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/projects\/keyboard\/"}},"id":"https:\/\/drewdeponte.com\/projects\/keyboard\/","content":"<h2 id=\"tldr\">TLDR<\/h2>\n<p>The following is a deep dive into my current keyboard which is outlined below.<\/p>\n<ul>\n<li>4x5 Dactyl Manuform (with a few small tweaks)<\/li>\n<li>Printed on my 3D printer<\/li>\n<li>Custom-built<\/li>\n<li>Hand Wired<\/li>\n<li>Cherry MX Blue switches (Cherry MX Silver in my secondary keyboard)<\/li>\n<li>Firmware: <a rel=\"external\" href=\"https:\/\/docs.qmk.fm\/#\/\">QMK<\/a><\/li>\n<li>My fork of QMK firmware (repo: <code>git:\/\/git.drewdeponte.com\/qmk_firmware.git<\/code>)<\/li>\n<li>Dvorak<\/li>\n<li><a href=\"https:\/\/drewdeponte.com\/projects\/keyboard\/dactyl_manuform_keyboard_layout_print_on_letter.pdf\">Pretty Layout for Printing<\/a><\/li>\n<\/ul>\n<p><img src=\"https:\/\/drewdeponte.com\/projects\/keyboard\/IMG_5079.jpeg\" alt=\"\" \/><\/p>\n<h2 id=\"why-a-dactyl-manuform\">Why a Dactyl Manuform?<\/h2>\n<p>Well, the short answer is because I have historically had wrist pain from\ntyping. But not with the 4x5 Dactyl Manuform. I believe at this point with all\nmy experimentation with a ton of other custom keyboards I have built that the\nkey to my success with this build is due to two things.<\/p>\n<p>First, I determined that my wrist pain was caused largely by repetitive\nstretching of my fingers to reach keys at the outer boundaries of keyboards.\nThe only way I could figure to reduce that was to reduce the number of keys I\nhave on the keyboard. Hence, the 4x5 size. However, this meant that I would\nhave to have layers and a comfortable thumb cluster I could use all the keys\nfrom.<\/p>\n<p>The Dactyl Manuform 4x5 meets all these requirements and still maintains all my\nother ergonomic requirements. Also thanks to QMK it is customizable enough to\nsupport a layered layout in a way that I can still be efficient at programming.<\/p>\n<h2 id=\"a-problem-no-more\">A Problem, No More<\/h2>\n<p>I love my Dactyl Manuform keyboard. It is by far the best keyboard I have every\nused and my wrists haven't felt any pain since I started using it. However, it\nhas had one problem. It is too light-weight. Because of this even when I have\nrubber on the bottom of it, it still slides around on my desk.<\/p>\n<p>The other day I decided it was time to address this annoyance. So I took them\napart and first tried to see if I could figure out a good mechanism for adding\nweight to them. Didn't have much luck with that. And then I thought my other\nissue is having my trackpad positioned appropriately relative to the keyboard.<\/p>\n<p>This got me thinking if I could create some sort of thin thing that would hold\nthe keyboards in the appropriate positions to be ergonomic, as well as hold my\ntrackpad.<\/p>\n<p>So I laid out the components on my desk in the correct positions and then took\na rough measurement to figure out how large something would need to be. I then\ntook those measurements to find some scrap Masonite board in my wood shop and\ncut it down to that size. Then I laid the components on it the correct\npositions and then used some CA glue to glue down tiny blocks to hold the\ncomponents in the correct positions &amp; orientations.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/projects\/keyboard\/IMG_6976.jpeg\" alt=\"\" \/><\/p>\n<p>This actually worked extremely well. It kept the keyboards and trackpad in the\ncorrect positions. It also increased the surface area in contact with the\nrubber as well as the weight. Causing it to prevent the devices from sliding\naround on my desk.<\/p>\n<p>However, the next problem that I had was that it was ugly and that it bothered\nmy wrist when I would relax my hand on the desk. Beyond that it was just ugly.\nSo I free hand drew a more organic looking outline around the components,\nvisible in the photo above, and then cut it out with a band saw.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/projects\/keyboard\/IMG_6997.jpeg\" alt=\"\" \/><\/p>\n<p>This made it not only look nicer. But also eliminated the discomfort that I had\nwhen I would wrest my hinds on the desk every so often. In fact, you can see what\nit looks like in use in the following photo.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/projects\/keyboard\/IMG_7001.jpeg\" alt=\"\" \/><\/p>\n<p>It is still a bit ugly and could use some love. So my plan is to cover up the\nblocks and the visible wood by sculpting clay to make in organic looking alien\nblob that is absorbing my keyboards and touchpad. And then I will paint it.\nHopefully, at that point it will look much nicer.<\/p>\n<h2 id=\"the-build\">The Build<\/h2>\n<p>The following are a bunch of photos I took throughout the build. I won't go\ninto all the details about them. But figured I would share.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/projects\/keyboard\/IMG_5041.jpeg\" alt=\"\" \/>\n<img src=\"https:\/\/drewdeponte.com\/projects\/keyboard\/IMG_5042.jpeg\" alt=\"\" \/>\n<img src=\"https:\/\/drewdeponte.com\/projects\/keyboard\/IMG_5045.jpeg\" alt=\"\" \/>\n<img src=\"https:\/\/drewdeponte.com\/projects\/keyboard\/IMG_5046.jpeg\" alt=\"\" \/>\n<img src=\"https:\/\/drewdeponte.com\/projects\/keyboard\/IMG_5047.jpeg\" alt=\"\" \/>\n<img src=\"https:\/\/drewdeponte.com\/projects\/keyboard\/IMG_5050.jpeg\" alt=\"\" \/>\n<img src=\"https:\/\/drewdeponte.com\/projects\/keyboard\/IMG_5051.jpeg\" alt=\"\" \/>\n<img src=\"https:\/\/drewdeponte.com\/projects\/keyboard\/IMG_5053.jpeg\" alt=\"\" \/>\n<img src=\"https:\/\/drewdeponte.com\/projects\/keyboard\/IMG_5054.jpeg\" alt=\"\" \/>\n<img src=\"https:\/\/drewdeponte.com\/projects\/keyboard\/IMG_5057.jpeg\" alt=\"\" \/>\n<img src=\"https:\/\/drewdeponte.com\/projects\/keyboard\/IMG_5071.jpeg\" alt=\"\" \/>\n<img src=\"https:\/\/drewdeponte.com\/projects\/keyboard\/IMG_5072.jpeg\" alt=\"\" \/>\n<img src=\"https:\/\/drewdeponte.com\/projects\/keyboard\/IMG_5073.jpeg\" alt=\"\" \/>\n<img src=\"https:\/\/drewdeponte.com\/projects\/keyboard\/IMG_5074.jpeg\" alt=\"\" \/>\n<img src=\"https:\/\/drewdeponte.com\/projects\/keyboard\/IMG_5075.jpeg\" alt=\"\" \/>\n<img src=\"https:\/\/drewdeponte.com\/projects\/keyboard\/IMG_5079.jpeg\" alt=\"\" \/>\n<img src=\"https:\/\/drewdeponte.com\/projects\/keyboard\/IMG_5080.jpeg\" alt=\"\" \/>\n<img src=\"https:\/\/drewdeponte.com\/projects\/keyboard\/IMG_5080.jpeg\" alt=\"\" \/>\n<img src=\"https:\/\/drewdeponte.com\/projects\/keyboard\/IMG_6220.jpeg\" alt=\"\" \/>\n<img src=\"https:\/\/drewdeponte.com\/projects\/keyboard\/66026839065__7E6872AC-DD8D-4329-81E0-F5292F1B9E52.jpeg\" alt=\"\" \/>\n<img src=\"https:\/\/drewdeponte.com\/projects\/keyboard\/66026840819__51271F4A-6E5C-4396-8174-D3A194DC613F.jpeg\" alt=\"\" \/><\/p>\n<h2 id=\"notes\">Notes<\/h2>\n<p>The following are really more notes for me about things. But it might be useful\nto you if you are trying to get a Dactyl Manuform setup with QMK Firmware.<\/p>\n<h3 id=\"references\">References<\/h3>\n<ul>\n<li><a rel=\"external\" href=\"https:\/\/github.com\/abstracthat\/dactyl-manuform\">abstracthat\/dactyl-manuform<\/a><\/li>\n<\/ul>\n<h3 id=\"parts\">Parts<\/h3>\n<ul>\n<li><a rel=\"external\" href=\"https:\/\/www.mcmaster.com\/#94180a331\/=16yfrx1\">Tapered Heat-Set Inserts for Plastic<\/a> (Brass, M3 x 0.50 mm Thread Size, 3.800 mm Installed Length) - use for attaching a base to the bottom of the housing<\/li>\n<li><a rel=\"external\" href=\"http:\/\/www.metricscrews.us\/index.php?main_page=product_info&amp;cPath=155_185&amp;products_id=455\">M3 wafer-head screws, 5mm<\/a> - used to attach the base to the inserts in the housing<\/li>\n<li><a rel=\"external\" href=\"https:\/\/www.amazon.com\/gp\/product\/B00LQPY0Y0\">1N4148 diodes<\/a><\/li>\n<li><a rel=\"external\" href=\"https:\/\/www.amazon.com\/gp\/product\/B00GWFECWO\">#30 wire<\/a><\/li>\n<li>2 <a rel=\"external\" href=\"https:\/\/www.sparkfun.com\/products\/12640\">Arduino Pro Micros<\/a><\/li>\n<li><a rel=\"external\" href=\"https:\/\/www.amazon.com\/gp\/product\/B008CPVMMU\">Veroboard stripboard<\/a><\/li>\n<li><a rel=\"external\" href=\"https:\/\/www.aliexpress.com\/item\/32949546651.html?spm=a2g0o.cart.0.0.2e4b3c00twBcLt&amp;mp=1\">4pin Telephone Cord<\/a><\/li>\n<li><a rel=\"external\" href=\"https:\/\/www.amazon.com\/gp\/product\/B01HU7BVDU\/\">Female RJ-9 connectors<\/a> - <a rel=\"external\" href=\"https:\/\/www.aliexpress.com\/item\/32763781713.html?spm=a2g0o.cart.0.0.2e4b3c00twBcLt&amp;mp=1\">4pin RJ-9 Female Connectors<\/a><\/li>\n<\/ul>\n<h3 id=\"keyboard-layout\">Keyboard Layout<\/h3>\n<ul>\n<li><a rel=\"external\" href=\"https:\/\/www.youtube.com\/watch?v=ePL2Xbq6mO0\">teej sets up QMK Layout for Dactyl Manuform 5x7<\/a><\/li>\n<\/ul>\n<h3 id=\"qmk\">QMK<\/h3>\n<ul>\n<li><a rel=\"external\" href=\"https:\/\/docs.qmk.fm\/#\/newbs_building_firmware\">QMK - Building Firmware<\/a><\/li>\n<\/ul>\n<h3 id=\"possible-parts\">Possible Parts<\/h3>\n<ul>\n<li><a rel=\"external\" href=\"https:\/\/keeb.io\/collections\/diy-parts\/products\/pro-micro-5v-16mhz-arduino-compatible-atmega32u4\">Pro Micro<\/a><\/li>\n<\/ul>\n<h3 id=\"build-videos\">Build Videos<\/h3>\n<h4 id=\"kevin-eckert-keyboard-build-stream-dactyl-manuform\">Kevin Eckert - Keyboard Build Stream - Dactyl Manuform<\/h4>\n<p>This is great in depth series on the build which matches the types of connectors used in <a rel=\"external\" href=\"https:\/\/github.com\/abstracthat\/dactyl-manuform\">abstracthat\/dactyl-manuform<\/a>.<\/p>\n<ul>\n<li><a rel=\"external\" href=\"https:\/\/www.youtube.com\/watch?v=dWC_8BOArzc\">Keyboard Build Stream - Dactyl Manuform - Part 1<\/a> - assume already printed, put switches in the case, wire the switches<\/li>\n<li><a rel=\"external\" href=\"https:\/\/www.youtube.com\/watch?v=fDc6rjZGYiI\">Keyboard build Stream - Dactyl Manuform - Part 2<\/a> - continue wiring switches with diodes on the rows<\/li>\n<li><a rel=\"external\" href=\"https:\/\/www.youtube.com\/watch?v=r-CKnaoSmCk\">Keyboard build Stream - Dactyl Manuform - Part 3<\/a> - wire up the Pro Micro to the switches<\/li>\n<li><a rel=\"external\" href=\"https:\/\/www.youtube.com\/watch?v=Oloh3Yabu6I\">Keyboard Build Stream - Dactyl Manuform - Part 4<\/a> - Wiring RJ-9, USB-C to micro USB extension, programming mode button<\/li>\n<\/ul>\n<h4 id=\"cmd-dactyl-manuform-build\">CMD - Dactyl Manuform Build<\/h4>\n<p>This build video is great for understanding the background of the Dactyl and seeing someone paint it. However, it is using a slightly different case from <a rel=\"external\" href=\"https:\/\/github.com\/abstracthat\/dactyl-manuform\">abstracthat\/dactyl-manuform<\/a>. It uses a different type of connector to connect the two halves of the keyboard, etc.<\/p>\n<ul>\n<li><a rel=\"external\" href=\"https:\/\/www.youtube.com\/watch?v=CxNKWNKBLMs&amp;t=1127s\">CMD - Dactyl Manuform Build<\/a><\/li>\n<\/ul>\n<h4 id=\"i-built-a-ridiculous-keyboard-by-hand\">I built a Ridiculous Keyboard by Hand<\/h4>\n<ul>\n<li><a rel=\"external\" href=\"https:\/\/www.youtube.com\/watch?v=VlHgEqLVYqA\">I built a Ridiculous Keyboard by Hand<\/a><\/li>\n<li><a rel=\"external\" href=\"https:\/\/www.youtube.com\/watch?v=Wes8jtxbBdo&amp;t=336s\">What I Learned From Building a Ridiculous Keyboard by Hand<\/a><\/li>\n<\/ul>\n<h4 id=\"keyboard-layout-1\">Keyboard Layout<\/h4>\n<ul>\n<li><a rel=\"external\" href=\"https:\/\/i.imgur.com\/N8zCFBp.png\">Developer's 40% Plank layout<\/a><\/li>\n<\/ul>\n<h3 id=\"dactyl-qmk\">Dactyl QMK<\/h3>\n<ul>\n<li><a rel=\"external\" href=\"https:\/\/balatero.com\/writings\/qmk\/getting-started-with-dactyl-manuform-and-qmk\/\">Getting Started with Dactyl Manuform QMK<\/a><\/li>\n<\/ul>\n<h3 id=\"wiring-flashing\">Wiring \/ Flashing<\/h3>\n<ul>\n<li><a rel=\"external\" href=\"https:\/\/github.com\/qmk\/qmk_firmware\/tree\/master\/keyboards\/handwired\/dactyl_manuform\">QMK Wiring for connect two halves<\/a><\/li>\n<li><a rel=\"external\" href=\"https:\/\/golem.hu\/article\/pro-micro-pinout\/\">Firmware Pin Mapping<\/a><\/li>\n<\/ul>\n<h3 id=\"split-not-working\">Split Not Working<\/h3>\n<ul>\n<li><a rel=\"external\" href=\"https:\/\/github.com\/qmk\/qmk_firmware\/issues\/9697\">Dactyl Manuform Split Not Working<\/a><\/li>\n<li><a rel=\"external\" href=\"https:\/\/docs.qmk.fm\/#\/feature_split_keyboard?id=handedness-by-pin\">QMK Split Keyboards Feature<\/a><\/li>\n<li><a rel=\"external\" href=\"https:\/\/www.reddit.com\/r\/olkb\/comments\/c0006h\/dactylmanuform_wlets_split_firmware_slave_side\/erb2y5l\/\">QMK Split Keyboard Not Working<\/a><\/li>\n<\/ul>\n<h3 id=\"qmk-firmware\">QMK Firmware<\/h3>\n<h4 id=\"compile\">Compile<\/h4>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>qmk compile -kb handwired\/dactyl_manuform\/4x5 -km drewdeponte<\/span><\/span><\/code><\/pre><h4 id=\"flash\">Flash<\/h4>\n<ol>\n<li>Connect the keyboard directly to the laptop via straight USB-C cable with\nthe left half plugged into the computer. The right half can stay connected\nto the left half via the telephone wire.<\/li>\n<li>Run the following command, it will get to a point where it starts waiting\nfor the device. When it does, you double tap the button on the back of the\nleft half of the keyboard, and it will switch the controller into flash mode.\nThe app will detect it and flash the device.<\/li>\n<\/ol>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>qmk flash -kb handwired\/dactyl_manuform\/4x5 -km drewdeponte<\/span><\/span><\/code><\/pre>\n<p>After it is done you should be able to disconnect it, reconnect the keyboard\nand everything should be good to go.<\/p>\n<h4 id=\"edit\">Edit<\/h4>\n<p>Edit the following file to make changes to the layout.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>~\/code\/personal\/qmk_firmware\/keyboards\/handwired\/dactyl_manuform\/4x5\/keymaps\/drewdeponte\/keymap.c<\/span><\/span><\/code><\/pre><h4 id=\"notes-1\">Notes<\/h4>\n<p>To get this split to work I had to set up my <code>config.h<\/code> as follows:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>#define USE_SERIAL<\/span><\/span>\n<span class=\"giallo-l\"><span>#define SPLIT_USB_DETECT<\/span><\/span><\/code><\/pre>\n<p>This only really works when the left side is plugged into the computer. If the\nright side is plugged in the keys translate differently.<\/p>\n<p>I tried the <code>EEPROM<\/code> detection but even after flashing the <code>EEPROM<\/code>s it didn't\nseem to work with the <code>EE_HAND<\/code>. So after trying things out this is what I\nfound to work.<\/p>\n<h2 id=\"past-keyboards\">Past Keyboards<\/h2>\n<p>I have had many past keyboards. The following is a list of just a few.<\/p>\n<ul>\n<li><a rel=\"external\" href=\"https:\/\/kinesis-ergo.com\/shop\/advantage2-signature-series\/\">Kinesis Advantage2 Signature Series<\/a><\/li>\n<li><a rel=\"external\" href=\"https:\/\/www.ergodox.io\">ErgoDox<\/a><\/li>\n<li><a rel=\"external\" href=\"https:\/\/kono.store\/products\/infinity-ergodox-keyboard\">Infinity ErgoDox<\/a><\/li>\n<li><a rel=\"external\" href=\"https:\/\/www.zsa.io\/moonlander\/\">Moonlander<\/a><\/li>\n<li><a rel=\"external\" href=\"https:\/\/olkb.com\/collections\/planck\">Planck<\/a><\/li>\n<li><a rel=\"external\" href=\"https:\/\/www.moergo.com\/collections\/glove80-keyboards\">Glove80<\/a><\/li>\n<\/ul>\n"},{"title":"Git Patch Stack 7.0.0","published":"2024-01-09T00:00:00+00:00","updated":"2024-01-09T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/git-patch-stack-7-0-0\/"}},"id":"https:\/\/drewdeponte.com\/blog\/git-patch-stack-7-0-0\/","content":"<p>The <code>7.0.0<\/code> release of Git Patch Stack is an extremely significant release. It\nmarks the beginning of a few major mental &amp; strategic shifts in terms of Git\nPatch Stack.<\/p>\n<h2 id=\"state\">State<\/h2>\n<p>Historically we have tracked state in all versions of Git Patch Stack before\n<code>7.0.0<\/code>. We stored this state in <code>.git\/GIT-PATCH-STACK-PATCH-STATES-V3.json<\/code>,\nand a state entry in it generally would look something like the following.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"json\"><span class=\"giallo-l\"><span>{<\/span><\/span>\n<span class=\"giallo-l\"><span>  &quot;<\/span><span style=\"color: #C6CCD7;\">5543c1e7-b77b-41af-8653-c721b2e9db9d<\/span><span>&quot;: {<\/span><\/span>\n<span class=\"giallo-l\"><span>    &quot;<\/span><span style=\"color: #C6CCD7;\">patch_id<\/span><span>&quot;: &quot;<\/span><span style=\"color: #98C379;\">5543c1e7-b77b-41af-8653-c721b2e9db9d<\/span><span>&quot;,<\/span><\/span>\n<span class=\"giallo-l\"><span>    &quot;<\/span><span style=\"color: #C6CCD7;\">state<\/span><span>&quot;: {<\/span><\/span>\n<span class=\"giallo-l\"><span>      &quot;<\/span><span style=\"color: #C6CCD7;\">Integrated<\/span><span>&quot;: [<\/span><\/span>\n<span class=\"giallo-l\"><span>        &quot;<\/span><span style=\"color: #98C379;\">origin<\/span><span>&quot;,<\/span><\/span>\n<span class=\"giallo-l\"><span>        &quot;<\/span><span style=\"color: #98C379;\">ps\/rr\/fix_grammar___spelling_in_making_code_reusable<\/span><span>&quot;,<\/span><\/span>\n<span class=\"giallo-l\"><span>        &quot;<\/span><span style=\"color: #98C379;\">4c4b3b720b92957b21f11a9b6a331190b55535b1<\/span><span>&quot;<\/span><\/span>\n<span class=\"giallo-l\"><span>      ]<\/span><\/span>\n<span class=\"giallo-l\"><span>    }<\/span><\/span>\n<span class=\"giallo-l\"><span>  },<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>You can conceptually think of it as hash map keyed off of the <code>ps-id<\/code>\n(<code>patch_id<\/code>). The <code>state<\/code> property is simply an object that kept track of\nthe current state of a given patch.<\/p>\n<p>This <code>state<\/code> property could conceptually be in any of the following cases,\neach with their own associated data.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"rust\"><span class=\"giallo-l\"><span>#[derive(<\/span><span style=\"color: #E5C07B;\">Serialize<\/span><span>,<\/span><span style=\"color: #E5C07B;\"> Deserialize<\/span><span>,<\/span><span style=\"color: #E5C07B;\"> Debug<\/span><span>,<\/span><span style=\"color: #E5C07B;\"> Clone<\/span><span>)]<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">pub<\/span><span style=\"color: #61AFEF;\"> enum<\/span><span style=\"color: #E5C07B;\"> PatchState<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">    BranchCreated<\/span><span>(<\/span><span style=\"color: #E5C07B;\">String<\/span><span>,<\/span><span style=\"color: #E5C07B;\"> String<\/span><span>),<\/span><span style=\"color: #5F6672;font-style: italic;\"> \/\/ branch_name, patch_stack_diff_hash<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">    PushedToRemote<\/span><span>(<\/span><span style=\"color: #E5C07B;\">String<\/span><span>,<\/span><span style=\"color: #E5C07B;\"> String<\/span><span>,<\/span><span style=\"color: #E5C07B;\"> String<\/span><span>,<\/span><span style=\"color: #E5C07B;\"> String<\/span><span>),<\/span><span style=\"color: #5F6672;font-style: italic;\"> \/\/ remote, branch_name, patch_stack_diff_hash, remote_diff_hash<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">    RequestedReview<\/span><span>(<\/span><span style=\"color: #E5C07B;\">String<\/span><span>,<\/span><span style=\"color: #E5C07B;\"> String<\/span><span>,<\/span><span style=\"color: #E5C07B;\"> String<\/span><span>,<\/span><span style=\"color: #E5C07B;\"> String<\/span><span>),<\/span><span style=\"color: #5F6672;font-style: italic;\"> \/\/ remote, branch_name, patch_stack_diff_hash, remote_diff_hash<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">    Integrated<\/span><span>(<\/span><span style=\"color: #E5C07B;\">String<\/span><span>,<\/span><span style=\"color: #E5C07B;\"> String<\/span><span>,<\/span><span style=\"color: #E5C07B;\"> String<\/span><span>),<\/span><span style=\"color: #5F6672;font-style: italic;\">              \/\/ remote, branch_name, patch_stack_diff_hash<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>This was a simple straightforward way of getting started with Git Patch Stack\nand representing the state that we believed we needed. However, over time we\nbegan to see flaws &amp; drawbacks with this approach.<\/p>\n<h3 id=\"flaws\">Flaws<\/h3>\n<p>For one, we got numerous reports of the state tracking becoming out of\nalignment with Git proper because users were going outside of Git Patch Stack\nand doing things.<\/p>\n<p>Additionally, Git Patch Stack was tracking state that was fabricated. Meaning\nit was state that we had made up and wasn't actually possible to represent\nwithin Git. This is largely because we were caching this fabricated state from\nremote systems such as GitHub. Creating another point\/opportunity to be out of\nsync in terms of state.<\/p>\n<p>Third, people were trying to use Git Patch Stack across multiple remotes of a\nrepository and our state tracking had no mechanism of being synchronized across\nthe remotes.<\/p>\n<h3 id=\"the-decision\">The Decision<\/h3>\n<p>With the <code>7.0.0<\/code> release we decided to resolve these issues by making a very\nimportant decision. Firstly, we would <strong>no longer track state<\/strong>. Instead, we\nwould <strong>compute the state<\/strong> for Git Patch Stack from the natural state of Git.<\/p>\n<p>This has the benefit that it makes it impossible for a users Git state to be out of\nsync with a users Git Patch Stack state.<\/p>\n<p>Beyond that, it facilitates the sharing of Git Patch Stack state across remotes\nas the Git state is already synchronized over the network.<\/p>\n<p>In addition, it has the massive benefit of now enabling users to use only the\nportions of Git Patch Stack that they find valuable for their workflow without\ngetting out of sync. This same characteristic also enables Git Patch Stack to\nbe combined with other Git extensions &amp; tools (e.g.\n<a rel=\"external\" href=\"https:\/\/github.com\/tummychow\/git-absorb\">git-absorb<\/a>) without worrying about\ngetting out of sync.<\/p>\n<h3 id=\"beneficial-side-effects\">Beneficial Side Effects<\/h3>\n<p>In addition to all the natural benefits we have gained from the decision to\ncompute state instead of track state, we indirectly gained a massive side\neffect. The side effect of creating better alignment between Git Patch Stack\nand Git proper. This modeling alignment ended up enabling us to add a long\nrequested feature of Git Patch Stack, Patch Series support.<\/p>\n<p>A Patch Series within a Patch Stack workflow is a defined consecutive sequence\nof patches. Often times they are dependent on one another and have to be\nsequenced in a specific order. This is useful when you want to simply organize\nrelated patches or when you want to request review of a series of patches.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/git-patch-stack-7-0-0\/gps_list_with_patch_series_no_alt_colors@2x.png\" alt=\"gps list output with a patch series but no alternating patch series colors\" \/><\/p>\n<p>Even though we have introduced the concept of a patch series, it is important\nto realize that they still exist within your Patch Stack. In fact, they can\nexist alongside other solo patches or other patch series within your stack.\nTheir relationship to each other is simply controlled by which order you place\nthem in your stack. The same way that you manage patch relationships.<\/p>\n<p>To understand how we define or refine a patch series lets look at the example above. Prior to having the \"login\" patch series defined our <code>gps ls<\/code> output would look as follows.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/git-patch-stack-7-0-0\/gps_list_no_patch_series_no_alt_colors@2x.png\" alt=\"gps list output no patch series no alternating patch series colers\" \/><\/p>\n<p>Simply a stack of patches. To define or refine a patch series we simply use our new patch index range syntax (e.g. <code>2-5<\/code>), which works generally across the commands. For example if we want to create the patch series locally like we did in this example we would use <code>gps branch -n login 2-5<\/code>. Leaving us with a <code>gps ls<\/code> output that again looks as follows.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/git-patch-stack-7-0-0\/gps_list_with_patch_series_no_alt_colors@2x.png\" alt=\"gps list output with a patch series but no alternating patch series colors\" \/><\/p>\n<p>However, if we wanted to refine the definition of that patch series. We would\nsimply rerun that same command while either increasing or decreasing the patch\nindex range to respectively include or exclude patches from the patch series.<\/p>\n<h4 id=\"alternating-patch-series-coloring\">Alternating Patch Series Coloring<\/h4>\n<p>After using Patch Series support for a while within the Git Patch Stack core\nteam, we decided that to really make it easy to use we would need to be able to\nvisually see a patch series within the stack without having to track from the\nright side of a patch, seeing the branch, all the way over to the left side of\nthe patch to see the index. Our solution for this problem was relatively\nsimple. We introduced alternating patch series colors in the <code>gps list<\/code> output.<\/p>\n<p>This allows you to clearly see which patches are part of which patch series.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/git-patch-stack-7-0-0\/gps_list_with_patch_series_with_alt_colors@2x.png\" alt=\"gps list output with a patch series with alternating patch series colors\" \/><\/p>\n<p>In fact from the screenshot above you can see that <code>gps list<\/code> sees individual\npatches that are not part of a formal patch series as implicit patch series of\none patch. This is why the colors are alternating between the <code>Add Mortgage Calculator<\/code> patch and the <code>Add Loan Calculator<\/code> patch, as well as the <code>Add Savings Calculator<\/code> patch.<\/p>\n<p>In fact if we had the same set of patches but hadn't defined the patch series\nwe would see the following, as the alternate patch series coloring feature is\nenabled by default.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/git-patch-stack-7-0-0\/gps_list_no_patch_series_with_alt_colors@2x.png\" alt=\"gps list output no patch series with alternating patch series colors\" \/><\/p>\n<p>We can also clearly see the every patch in the list has alternate coloring.\nThis is a side effect of the fact that we don't have any formal patch series\ndefined and again that <code>gps list<\/code> sees solo patches as a patch series of one\npatch.<\/p>\n<h5 id=\"colors-and-their-problems\">Colors and their Problems<\/h5>\n<p>Colors especially within the terminal have a number of different problems. For\none the contrast needed for different people is different. Two, the colors\nwithin my terminal are almost always different from the colors in your\nterminal. Not to mention how your monitor is configured and presents the colors\nto you is likely different. Or even, how your eyes interpret the colors and\nthe variance there. Who knows maybe you are color-blind and therefore need\nspecific colors.<\/p>\n<p>To address these issues we have shifted to 24-bit color mode and set the\ndefault colors based on my personal color choices. This way the colors are\nhopefully pretty close to the colors I use on my machine.<\/p>\n<p>We have also opted to support customization of the colors so that if you need\nor just want different colors you can configure the colors that are used in the\n<code>gps list<\/code> output. For details check out the\n<a rel=\"external\" href=\"https:\/\/book.git-ps.sh\/tool\/configuration\">Configuration<\/a> documentation.<\/p>\n<h2 id=\"composition-over-opinion\">Composition over Opinion<\/h2>\n<p>Another characteristic that Git Patch Stack has had historically up until\n<code>7.0.0<\/code> is that it was more opinionated. It expected users to follow a specific\nworkflow and focused primarily on that.<\/p>\n<p>This original decision was largely due to the fact that we were tracking state\nand therefore users had to use our commands &amp; workflow for the tool to\nconsistently work.<\/p>\n<p>As part of the original approach we implemented a decent number of commands\neither for convenience, as an alias, or to try and create a complete\nabstraction to facilitate having a workflow completely within Git Patch Stack.\nA lot of these commands actually did nothing other than proxy down to a Git\ncommand. This has its maintenance problems as we would then have to maintain\nand proxy all the various options for that Git command.<\/p>\n<p>Git commands change over time and therefore this creates a large maintenance\nproblem. Beyond that it turns out that there are a ton of different variations\nof workflows that users have with Git. Having a hard lined view of the workflow\nthat Git Patch Stack is targeted to support and not supporting any other\nworkflows significantly limits its ability to be valuable.<\/p>\n<p>However, now that we are computing state we no longer need to be so opinionated\nin terms of workflow as our users can now use the pieces of Git Patch Stack\nthey prefer and ignore the rest. They can also use Git Patch Stack in\ncombination with other Git commands or even other Git extensions.<\/p>\n<p>So we decided to change the focus of Git Patch Stack from enforcing a\nparticular workflow, to instead be a general tool allowing users to have their\nown custom workflows utilizing Git Patch Stack.<\/p>\n<p>Making this decision then made it clear that to truly support this mindset we\nshould remove all the \"proxy commands\". This will ease our maintenance effort\nsignificantly, and it will make the intention of Git Patch Stack and its stance\nclearer. So we removed the <code>sync<\/code>, <code>log<\/code>, <code>create-patch<\/code>, <code>amend-patch<\/code>,\n<code>status<\/code>, <code>unstage<\/code>, <code>upstream-patches<\/code>, and the <code>add<\/code> commands to simplify the\ncommand surface area.<\/p>\n<p>To continue this effort to simplify the command surface area we removed the\n<code>batch-request-review<\/code> command and added proper first class support for\nbatching to the <code>request-review<\/code> command. When doing so, we added it in a way\nthat would be easy to add to other commands. However, we were hesitant to just\nadd it to all the commands as we weren't sure that it made sense. Additionally,\nwe removed the old <code>branch<\/code> command and renamed the <code>request-review-branch<\/code>\ncommand to <code>branch<\/code> as well as retrofitted it to be a first class citizen.<\/p>\n<p>We also realized, based on proxy command feature requests, that we needed to\nsomehow support all these custom local Git workflows people have. We decided\nthat the best way to do this was to additionally focus Git Patch Stack on the\nconcept of facilitating composition of its concepts with that of Git proper or\nany other command line tool for that matter.<\/p>\n<p>To begin to facilitate this we have provided the <code>gps sha &lt;patch-index&gt;<\/code>\ncommand. It simply takes a <code>patch-index<\/code> as the argument and outputs the\ncorresponding Git commit SHA. Having this basic command now allows people to\nbuild new commands that deal with a patch index instead of a Git commit SHA.<\/p>\n<p>Maybe you are a fan of using <code>git commit --fixup<\/code> and you want to use it with a\npatch index instead of having to have the SHA for a patch and type it in. No\nproblem with <code>gps sha<\/code> you can simply do <code>git commit --fixup=amend:$(gps sha 4)<\/code> to flag patch indexed by 4 for amend. If it is a command you use all the\ntime in your workflow simply make an alias that does it.<\/p>\n<p>This flexibility provided by the composition is key to support the vast variety\nof personalized workflows that people have.<\/p>\n<h2 id=\"platform-agnostic\">Platform Agnostic<\/h2>\n<p>Another choice that we made was that we should be as platform-agnostic as\npossible in our choices and our design so that we can support as many platforms\nas possible. Support for macOS &amp; Linux variants was something we had at a basic\nlevel. However, Windows was a platform that we didn't support primarily because\nof choices of libraries we used.<\/p>\n<p>So part of the effort of the <code>7.0.0<\/code> release was to move toward being able to\nsupport the Windows platform. As I mentioned before the primary issue we had\nwas libraries we were using that weren't cross-platform. So we swapped those\nout for libraries that were cross-platform.<\/p>\n<h3 id=\"gpg-signing\">GPG Signing<\/h3>\n<p>The other major issue we had was GPG signing support. Prior to <code>7.0.0<\/code> we were\nintegrating directly with GPG via C library that we couldn't easily get to\nbuild on Windows. So we decided to follow suite with what Git proper was doing\nin terms of GPG Signing support and have Git Patch Stack simply shell out to\nthe GPG command on that system by utilizing Git propers configuration.<\/p>\n<p>For details on configuring GPG signing please checkout <a rel=\"external\" href=\"https:\/\/book.git-ps.sh\/tool\/commit-signing\">Commit Signing<\/a> docs.<\/p>\n<h3 id=\"ssh-signing\">SSH Signing<\/h3>\n<p>In addition to switching GPG Signing away from the C library. We also had\nnumerous requests to support SSH Signing as GPG was losing in popularity. So\nwhile we were reworking the GPG Signing we also added support for SSH\nSigning in a cross-platform manner.<\/p>\n<p>All of these platform-agnostic choices have also resulted in a far simpler\nbuild process which helps not only in the distribution of the application but\nalso in the maintenance and even developer onboarding.<\/p>\n<p>For details on configuring SSH signing please checkout <a rel=\"external\" href=\"https:\/\/book.git-ps.sh\/tool\/commit-signing\">Commit Signing<\/a> docs.<\/p>\n<h2 id=\"quality-of-life\">Quality of Life<\/h2>\n<p>Beyond these major decisions and the improvements that have fallen out of them\nwe also have a lot of improvements that we have made simply for Quality of\nLife.<\/p>\n<h3 id=\"integrate-verify-hook\">Integrate Verify Hook<\/h3>\n<p>As part of <code>7.0.0<\/code> we now have the <code>integrate_verify<\/code> hook. This is a hook that\ngets executed as part of the <code>gps integrate<\/code> commands lifecycle. Enabling you\nto verify external state, maybe on an SCM hosting provider, is correct prior to\nintegrating.<\/p>\n<p>We have provided an example in the repository,\n<a rel=\"external\" href=\"https:\/\/github.com\/uptech\/git-ps-rs\/blob\/main\/example_hooks\/integrate_verify.sample.github-cli\">example_hooks\/integrate_verify.sample.github-cli<\/a>.\nThis example is written to integrate with GitHub to check to make sure that all\nthe CI checks have passed prior to letting someone integrate their patch.<\/p>\n<p>For further details on this hook check out the <a rel=\"external\" href=\"https:\/\/book.git-ps.sh\/tool\/hooks\">Hooks<\/a> documentation.<\/p>\n<h3 id=\"in-rebase-support\">In Rebase Support<\/h3>\n<p>For quite some time we have had requests to make <code>gps list<\/code> function when in\nthe middle of a rebase. Well <code>7.0.0<\/code> is the release where we finally add\nsupport for this. To see how this works let's get started by setting the state\nto what you see in the screenshot below.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/git-patch-stack-7-0-0\/gps_list_no_patch_series_with_alt_colors@2x.png\" alt=\"gps list output no patch series with alternating patch series colors\" \/><\/p>\n<p>Now let's say that we want to edit the <code>Add Login client<\/code> patch identified by\npatch index <code>3<\/code>. One approch to doing that is to run a <code>gps rebase<\/code> and simply\nmark that patch for <code>edit<\/code>. This will leave us looking like the following.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/git-patch-stack-7-0-0\/in_rebase_support_edit_patch_3@2x.png\" alt=\"gps rebase marking patch 3 for editing\" \/><\/p>\n<p>Git simply outputs the short SHA and summary of the commit it stopped at for\nediting. However, if we now run <code>gps list<\/code> we see the following.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/git-patch-stack-7-0-0\/in_rebase_support_gps_list_in_rebase@2x.png\" alt=\"gps list after stopping in the middle of a rebase\" \/><\/p>\n<p>From the above we can see that it shows us a lot more useful information here.\nIt shows us that we are in the middle of a rebase of <code>main<\/code> and the SHA of the\ncommit we are rebasing it onto.<\/p>\n<p>It also shows us in the bottom section the current state of the patch stack. At\nthis point in time it only consists of the patches that the rebase has already\nplayed. From this we can tell that if we were to run a <code>git commit --amend<\/code> it\nwould be amending the <code>Add Login client<\/code> patch as it is the patch currently at\nthe top of the stack.<\/p>\n<p>Lastly the section above that shows the patches that are still in the rebase\nto-do list and have yet to be replayed. In fact if you squint a little you\nmight have noticed that if you slammed the top section and bottom section\ntogether it would look just like your patch stack. This is by design so that\nyou can quickly and easily figure out what state you are in with respect to\nyour patch stack, even when you are in the middle of a rebase <code>gps list<\/code>.<\/p>\n<h3 id=\"ahead-behind-counts\">Ahead &amp; Behind Counts<\/h3>\n<p>In addition to all these other great improvements we also improved <code>gps list<\/code>\nby adding <strong>ahead<\/strong> and <strong>behind<\/strong> counts to its output. In fact we can see\nthese on the top line of the <code>gps list<\/code> output in the screenshot below.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/git-patch-stack-7-0-0\/gps_list_no_patch_series_with_alt_colors@2x.png\" alt=\"gps list output no patch series with alternating patch series colors\" \/><\/p>\n<p>In the screenshot above we can see that it says that we are currently in a\nstate where our patch stack is <code>ahead 7<\/code> and <code>behind 0<\/code>. What does this mean\nexactly?<\/p>\n<p>Well, <code>ahead X<\/code> is telling you how many patches stack has ahead of it's base.\nIn the screenshot above it is <code>7<\/code> because there are <code>7<\/code> patches.<\/p>\n<p>The <code>behind Y<\/code> is telling you how many commits your stack is behind it's\nassociated upstream tracking branch. You generally want it to be <code>0<\/code> as that\nindicates that your patch stack is up-to-date with what Git sees as the current\nstate of your patch stacks upstream tracking branch. You will notice becoming\nbehind if others are adding commits to the remote of your patch stack, and you\nare using <code>git fetch<\/code> to update your repositories' knowledge, but you aren't\nactively rebasing your patch stack onto the latest.<\/p>\n<h3 id=\"others\">Others<\/h3>\n<p>Beyond all the above-mentioned features &amp; changes. We have also included a\nhandful of other changes that are less crucial and less visible in terms of\npresentation. The summary of these can be seen below.<\/p>\n<h4 id=\"added\">Added<\/h4>\n<ul>\n<li>conflict &amp; merge commit detection errors to isolate command<\/li>\n<li>conflict &amp; merge commit detection errors to branch command<\/li>\n<li>conflict &amp; merge commit detection errors to request-review command<\/li>\n<li>conflict &amp; merge commit detection errors to integrate command<\/li>\n<li>global color option support to the branch command<\/li>\n<li>the command alias \"b\" to the new branch command<\/li>\n<li>build time generation of man pages<\/li>\n<\/ul>\n<h4 id=\"changed\">Changed<\/h4>\n<ul>\n<li>unhandled error output for the list command<\/li>\n<li>unhandled error printing to print the chain of errors<\/li>\n<li>gps --help docs to describe core vs. ancillary cmds<\/li>\n<li>improved CLI help interface<\/li>\n<\/ul>\n<h4 id=\"fixed\">Fixed<\/h4>\n<ul>\n<li>crash in list command when tracking branch is configured but remote branch doesn't exist<\/li>\n<li>bug where commit without diff id in stack would cause list to fail<\/li>\n<\/ul>\n<h2 id=\"where-to-go-from-here\">Where to Go from here?<\/h2>\n<p>The future is somewhat an unknown as we haven't planned a next major release\nyet, let alone a theme for one. To keep tabs on what we are planning or\nspecific issues you can check out our public <a rel=\"external\" href=\"https:\/\/github.com\/orgs\/uptech\/projects\/3\">GitHub\nProject<\/a>.<\/p>\n<p>If you are interested in chatting about Git Patch Stack or just different Git\nworkflows, providing feedback, or contributing. Don't hesitate to reach out via\nour public <a rel=\"external\" href=\"https:\/\/join.slack.com\/t\/gitpatchstack\/shared_invite\/zt-1bubjt6wg-QrvG9aBGniyZ0aPH9BcB4g\">Slack\nGroup<\/a>.<\/p>\n<p>If you are completely new to Git Patch Stack, a good place to start would be\nthe main <a rel=\"external\" href=\"https:\/\/git-ps.sh\">Git Patch Stack site<\/a> as well as the\n<a rel=\"external\" href=\"https:\/\/book.git-ps.sh\">documentation<\/a>.<\/p>\n<p>As always we hope that you find the decisions, changes and features we added\nvaluable.<\/p>\n"},{"title":"Building a StoryBrand: Chapter 4","published":"2023-12-28T00:00:00+00:00","updated":"2023-12-28T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/building-a-storybrand-chapter-4\/"}},"id":"https:\/\/drewdeponte.com\/blog\/building-a-storybrand-chapter-4\/","content":"<p>The content goes here.<\/p>\n<h2 id=\"context\">Context<\/h2>\n<p>Principle One: The customer is the hero not your brand.<\/p>\n<p>Define characters ambition. What they want. So that we open the question, Will\nthe hero get what she wants? This is called a Story Question or story gap.<\/p>\n<p>When we define something our customers want, we posit a story question in the\nmind of the customer: <em>Can this brand really help me get what I want?<\/em><\/p>\n<ul>\n<li>define something your customers want<\/li>\n<li>must be have a singular focus, be one thing<\/li>\n<\/ul>\n<h3 id=\"example-1\">Example 1<\/h3>\n<blockquote>\n<p>High-end resort originally had marketing content that was images of their\nfront desk, staff, etc. Wasn't really inviting a customer into a story. What\ntheir customers really wanted most was a luxurious, restful experience.<\/p>\n<\/blockquote>\n<blockquote>\n<p>After StoryBranding they changed their website from long stories about\nthemselves (which positioned them as the hero) to images of a warm bath,\nplush towels and robes, someone getting a massage in the spa, and a looping\nclip of a back-porch rocking chair against the backdrop of trees blowing in\nthe wind along a golf course.<\/p>\n<\/blockquote>\n<blockquote>\n<p>They replaced the text on their main page with the short and powerful copy:\n\"Find the luxury and rest you've been looking for.\"<\/p>\n<\/blockquote>\n<p>Excerpt From\nBuilding a StoryBrand\nDonald Miller\nhttps:\/\/books.apple.com\/us\/book\/building-a-storybrand\/id1212150000\nThis material may be protected by copyright.<\/p>\n<h3 id=\"example-2\">Example 2<\/h3>\n<blockquote>\n<p>\"One university we worked with defined their customer's desire as \u201ca\nhassle-free MBA you can complete after work.\"<\/p>\n<\/blockquote>\n<p>Excerpt From\nBuilding a StoryBrand\nDonald Miller\nhttps:\/\/books.apple.com\/us\/book\/building-a-storybrand\/id1212150000\nThis material may be protected by copyright.<\/p>\n<h3 id=\"example-3\">Example 3<\/h3>\n<blockquote>\n<p>\"A landscaping company humorously defined their customer\u2019s ambition as \u201ca yard that looks better than your neighbor's.\"<\/p>\n<\/blockquote>\n<p>Excerpt From\nBuilding a StoryBrand\nDonald Miller\nhttps:\/\/books.apple.com\/us\/book\/building-a-storybrand\/id1212150000\nThis material may be protected by copyright.<\/p>\n<h3 id=\"example-4\">Example 4<\/h3>\n<blockquote>\n<p>\"A caterer we worked with in Los Angeles defined his customer's desire as \u201ca mobile fine-dining experience in the environment of your choice.\"<\/p>\n<\/blockquote>\n<p>Excerpt From\nBuilding a StoryBrand\nDonald Miller\nhttps:\/\/books.apple.com\/us\/book\/building-a-storybrand\/id1212150000\nThis material may be protected by copyright.<\/p>\n<h3 id=\"examples-5-10\">Examples 5-10<\/h3>\n<blockquote>\n<p>Financial Advisor: \"A Plan for Your Retirement\"\nCollege Alumni Association: \"Leave a Meaningful Legacy\"\nFine-Dining Restaurant: \"A Meal Everybody Will Remember\"\nReal Estate Agent: \"The Home You\u2019ve Dreamed About\"\nBookstore: \"A Story to Get Lost In\"\nBreakfast Bars: \"A Healthy Start to Your Day\"<\/p>\n<\/blockquote>\n<p>Excerpt From\nBuilding a StoryBrand\nDonald Miller\nhttps:\/\/books.apple.com\/us\/book\/building-a-storybrand\/id1212150000\nThis material may be protected by copyright.<\/p>\n<h2 id=\"actions\">Actions<\/h2>\n<h2 id=\"application\">Application<\/h2>\n"},{"title":"Getting Started with StoryBranding","published":"2023-12-28T00:00:00+00:00","updated":"2023-12-28T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/storybranding-beginning\/"}},"id":"https:\/\/drewdeponte.com\/blog\/storybranding-beginning\/","content":"<p>If I am honest with myself. One of the areas that I lack strength &amp;\nskills, is marketing. I try and focus on areas I am weaker to hopefully improve\nover time. But it is hard to do this when you are running a business, doing\nclient work, and have a family.<\/p>\n<p>A while back a friend of mine introduced me to the book, <a rel=\"external\" href=\"https:\/\/buildingastorybrand.com\">Building a\nStoryBrand<\/a> by Donald Miller. I read it\nthrough, and it resonated pretty strongly with me. But it was non-trivial for\nme to apply. So I let other things get in the way.<\/p>\n<p>I feel like if I had better marketing skills it would seriously impact my\nability to help push my business to the next level. So being that I have some\ntime during Christmas vacation I have decided to give it another go. This time\npushing myself through the discomfort in hopes to grow my skills &amp; abilities.<\/p>\n<p>I figure if I can at least start it during Christmas break and keep a journal\nas I go through then I will be able to actually push through and finish with\nsomething to show for it.<\/p>\n<p>So jumping right in, Chapters 1, 2, and 3 are really an outline and sales pitch\nfor the concept of StoryBranding and the framework to create a StoryBrand.\n<strong>StoryBranding is basically the idea of taking the same fundamental structures\nthat make stories so impactful to people and applying them to your branding.<\/strong><\/p>\n<p>The later chapters get into specific details about the framework and what each\nstep is. I will cover those in detail in future posts as I reread them and\nattempt to apply them.<\/p>\n<p>Right now I am planning on using a rough template consisting of two main sections.<\/p>\n<ul>\n<li>context<\/li>\n<li>actions<\/li>\n<li>application<\/li>\n<\/ul>\n<p>The thinking behind this is that I am focusing on details of how to apply this\nconcept which I will outline in the <strong>actions<\/strong> section. While the <strong>context<\/strong>\nsection will house all my notes from reading the chapter that I believe are\nnecessary context while doing the <strong>actions<\/strong>. The <strong>application<\/strong> section will\nhouse an explanation of how I have attempted to execute the actions.<\/p>\n<p>I have a number of different projects, services, etc. that I could apply this\nconcept to that I may use some of in the <strong>application<\/strong> section just to try\nand get a better handle of things.<\/p>\n<ul>\n<li><a rel=\"external\" href=\"https:\/\/pullwalla.com\">Pullwalla<\/a><\/li>\n<li><a rel=\"external\" href=\"https:\/\/git-ps.sh\">Git Patch Stack<\/a><\/li>\n<li><a rel=\"external\" href=\"https:\/\/uptechstudio.com\">Uptech Studio<\/a><\/li>\n<li>Uptech Studio as a Productized Service<\/li>\n<\/ul>\n"},{"title":"Pullwalla - More Workflows","published":"2023-11-28T00:00:00+00:00","updated":"2023-11-28T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/pullwalla-more-workflows\/"}},"id":"https:\/\/drewdeponte.com\/blog\/pullwalla-more-workflows\/","content":"<p>Hello!<\/p>\n<p>We just released v3.7.0 (51) of <a rel=\"external\" href=\"https:\/\/pullwalla.com\">Pullwalla<\/a> to both macOS and iOS platforms.<\/p>\n<h2 id=\"quick-filters\">Quick Filters<\/h2>\n<p>One of Pullwalla's primary goals has been to help people quickly <strong>discover<\/strong> the pull requests they are interested in. Quick Filters continue to be a key mechanism supporting this.<\/p>\n<h3 id=\"more-workflows\">More Workflows<\/h3>\n<p>One of the things we have done in this release is support additional workflows.<\/p>\n<p>The workflow we supported historically is a workflow where developers take a step back from code every so often, looked at the previously <em>Unclaimed<\/em>, pull requests to find a pull request review, and then \"claim\" it by assigning themselves to it.<\/p>\n<p>This workflow is fine and works for some. But it doesn't work for everyone. Some people instead take the approach of requiring that every pull request when created has to have explicit requests for review, or requires that assignees be used to mean something different. Others have a workflow that is some sort of hybrid between the two approaches.<\/p>\n<p>Therefore, we introduced two new Quick Filters.<\/p>\n<ul>\n<li><strong>Undecided<\/strong> - shows pull requests that have not been approved or had changes requested yet.<\/li>\n<li><strong>Needs my Decision<\/strong> - shows pull requests that either you are an assignee of or that have a request for your review and have not been approved or had changes requested by you.<\/li>\n<\/ul>\n<p>The <strong>Needs my Decision<\/strong> quick filter facilitates workflows where PRs are created and given an assignee or a request for review is explicitly made. While the <strong>Undecided<\/strong> quick filter supports workflows where developers pick their own pull requests to review. You can see this in the screenshot below.<\/p>\n<p><img src=\"https:\/\/files.pullwalla.com\/v3.7.0-51-quick-filters-transparent-wallpaper@2x.png\" alt=\"\" \/><\/p>\n<h3 id=\"clarification\">Clarification<\/h3>\n<p>Beyond the addition of the <strong>Undecided<\/strong> and <strong>Needs my Decision<\/strong> quick filters. We also renamed the other quick filters to try and make them more accurate and clear. Previously Pullwalla's Quick Filters looked as follows.<\/p>\n<p><img src=\"https:\/\/files.pullwalla.com\/v3.6.2-50-quick-filters-transparent-wallpaper@2x.png\" alt=\"\" \/><\/p>\n<p>The mapping of the name changes are as follows.<\/p>\n<ul>\n<li><strong>Created<\/strong> is now <strong>My PRs<\/strong> as it shows pull requests that you have created.<\/li>\n<li><strong>Assigned<\/strong> is now <strong>My Assignments<\/strong> as it shows pull requests that you are an assignee of.<\/li>\n<li><strong>Mentioned<\/strong> is now <strong>My Mentions<\/strong> as it shows pull requests that you are mentioned in.<\/li>\n<li><strong>Review Requests<\/strong> is now <strong>Request for my Review<\/strong> as it shows pull requests where someone has requested your review.<\/li>\n<li><strong>Participated<\/strong> is now <strong>Participated In<\/strong> as it shows pull requests that you have participated in.<\/li>\n<li><strong>Unclaimed<\/strong> is now <strong>Unassigned<\/strong> as it shows you pull requests that have no assignees.<\/li>\n<li><strong>Claimed<\/strong> is now <strong>Assigned<\/strong> as it shows you pull requests that have one or more assignees.<\/li>\n<\/ul>\n<p>We hope these improvements to Quick Filters will have a big impact on peoples day to day interactions with Pullwalla. If you think we are missing something or have any feedback in general please don't hesitate to reach out at <a href=\"mailto:pullwalla@uptechstudio.com\">pullwalla@uptechstudio.com<\/a>.<\/p>\n<h3 id=\"thoughts-or-suggestions\">Thoughts or suggestions?<\/h3>\n<p>As always, we\u2019d love to hear from you.<\/p>\n<p><strong>Need help with an App (mobile, web, desktop, etc.)?<\/strong> <a rel=\"external\" href=\"https:\/\/uptechstudio.com\">Uptech Studio<\/a>, the creators of Pullwalla, are here to help. Just reach out to <a href=\"mailto:info@uptechstudio.com\">info@upechstudio.com<\/a>, and we are happy to explore how we might work together.<\/p>\n<p>Cheers,\nThe Pullwalla Team<\/p>\n"},{"title":"Git Patch Stack Goals","published":"2023-11-03T00:00:00+00:00","updated":"2023-11-03T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/git-patch-stack-goals\/"}},"id":"https:\/\/drewdeponte.com\/blog\/git-patch-stack-goals\/","content":"<p><em>The following is a transcript of an audio recording I made exploring the goals\nand intent behind the Git Patch Stack project. I have also included the audio\nbelow if you would prefer to listen.<\/em><\/p>\n<audio controls>\n\t<source src=\"git-patch-stack-goals.mp3\" type=\"audio\/mpeg\">\nYour browser does not support the audio element.\n<\/audio>\n<h2 id=\"transcript\">Transcript<\/h2>\n<p>Hi I am Drew De Ponte, Partner at Uptech Studio &amp; creator of Git Patch Stack.<\/p>\n<p>Today I wanted to talk about Git Patch Stack and the driving forces and purpose\nbehind it. Largely in my mind, the goal was and still is to find alignment\nbetween Git itself as a tool, its characteristics, best practices and the\ndevelopment process and tooling that we all generally use at mass today.<\/p>\n<p>In my mind that is using Bitbucket, GitHub, GitLab and the pull requests or\nmerge requests peer review workflow that the mass is still using today. The\nreason I was looking for alignment between these things was because I felt like\nthere was a dichotomy between the pull request feature branch workflow and Git\nbest practices.<\/p>\n<p>How people were using feature branches in the feature branch workflow\nat large was very much against the best practices of Git. One great example\nof this is the atomic or logical commit being a best practice of Git. This is\nbecause people would focus largely on feature branches as the primary concept\nand they would ignore the value of commits.<\/p>\n<p>They would ignore the benefits you gain longer term by having well structured\nlogical commits, and they would ignore the benefits you get in terms of all the\nfeatures of Git being available to you.<\/p>\n<p>So Git Patch Stack has always been an exploration on my part to try and find\nthe best alignment between those two worlds. Most of our values have fallen out\nof this exploration for this alignment. For example one of our values is that\nwe support the Continuous Integration Methodology as closely as possible.<\/p>\n<p>We want to eliminate the negative side effects of branch isolation. So, We\nalways lean towards Continous Integration in terms of decision making. Having\nthe user confront conflicts and bring integration to the forefront so that it\nhappens at a much quicker iterative pace, and so that the surface area of the\nconflicts are much smaller when they occur. Rather than waiting until a giant\nfeature branch is finally ready to be merged and having to deal with a\nnightmare in terms of integration.<\/p>\n<p>We are also big on aligning with Git. Hence why we focus on atomic or logical\ncommits and the tooling is designed around it. Not that you can't use branches,\nbecause you can if you really want. Branches are fine. But you have to be a\nvery cognizant and aware of the consequences of those branches when you are\nmakeing those decisions. I think that's largely one of the downfalls that\noccurred when Git came out.<\/p>\n<p>Git itself is fantastic as a tool. It gives you all this flexibility and power.\nHowever, when GitHub came out as a tool it created this next level of focus on\nthe concepts of the feature branch &amp; pull request. Which only perpetuated heavy\nuse branches and drove developers to not think about the concept of logical\ncommits. Or even the fundamentals of Git anymore.<\/p>\n<p>It's a higher level abstraction that they created &amp; popularized. And most\ndevelopers nowadays come into Git through these higher level abstractions. I\ncan't tell you how many developers I've met that don't even know that there is\na tree of commits.<\/p>\n<p>And that's a side effect of them learning from this higher level abstraction\nand not learning the fundamental underlying abstractions. There's downsides to\nthat. Which is, without diving deeper and learning those underlying\nabstractions, those developers just believe that, that's the truth.<\/p>\n<p>They believe that, that is the only way. Or, that in actuality, they believe\nthat, that is the way it was designed and intended to be used, which is not\nnecessarily true. That's one person or one group of people's take on how they\nthink it should be used. And none of those people were the creator of Git.<\/p>\n<p>They built that and put it out into the world and it resonated with people.\nThis makes sense because it's a simplification. Instead of thinking about\ncommits and how they relate to each other &amp; all the complexities of the\nmechanics of managing them in Git. You don't have to think about any of that.\nYou just think about this much grander, larger scale thing called a feature.\nBut that has its downsides as well.<\/p>\n<p>So again, we as Git Patch Stack are focused on trying to find &amp; and refine that\nalignment between these worlds, where we can focus on the right things for Git\nas a tool itself. But also play in a world with these higher level concepts\nlike feature branches &amp; pull requsets, when we want or need to.<\/p>\n<p>And also allow the user to, jump out and use a branch or any other normal Git\ncommads when it is appropriate. But the focus of the tooling is all on these\nlogical commits.<\/p>\n<p>So we have this core value of, <strong>abide by Git best practices<\/strong>. That way we are\nin alignment with what Git expects. <strong>Support as much as possible the concept\nof continuous integration<\/strong>. So that we can reduce the surface area and\ncomplexity of conflicts.<\/p>\n<p>Third we have, <strong>finding alignment with this concept of feature branches and\npull requests and peer reviews<\/strong>. But implementing it in such a way, that you\naren't forced into the feature branch, pull request, or peer review concepts.<\/p>\n<p>Instead, Git Patch Stack allows you from a workflow standpoint to plug into\nthose concepts, or it would allow you to, plug into an email based workflow\ninstead. When you conceptually request review in Git Patch Stack, there's a\nhook system.<\/p>\n<p>And that hook system allows you to plug in a hook to, go create a pull request\nin GitHub, or go create a patch and email that patch to a particular mailing\nlist, or do anything you want. It's completely up to you.<\/p>\n<p>But the goal of Git Patch Stack is to provide that core structure to think in\nterms of the right concepts so that you are in alignment with all of these\nthings, not just GitHub's model.<\/p>\n<p>Another value, is <strong>being able to focus on how to mentally think about these\nthings and trying to do that with as little overhead as possible<\/strong>.<\/p>\n<p>You can see this point in terms of a comparison of the Stacked Branch workflow\nversus a Patch Stack workflow. In a Stacked Branch workflow, you are creating\nbranches for each of your atomic commits, and then you're defining the\nrelationships between those branches so it can formally store a directed\nacyclic graph of those relationships.<\/p>\n<p>And then there's extra tooling both on the client side, where you manage your\nchanges locally, but also tooling on the application\nside where it can read the directed acyclic graph and facilitate reviewing\npull requests in the particular order defined in the directed acyclic graph.\nThe DAG also enables additional automation in terms of management of these\nbranches and the DAG.<\/p>\n<p>In my opinion, the Stacked Branch workflow's functionally solves some of the\nproblems that you have when you start thinking about atomic commits. But it\ndoesn't solve all of them. And it requires this overhead of defining and\nmanaging the branches &amp; the DAG in order to even solve the problems it does.<\/p>\n<p>Again my goal with Git Patch Stack was to explore and find a way that things\nfall into alignment with all of these things. That way developers can just\nfocus on the core concepts and principles &amp; naturally be in alignment.<\/p>\n<p>For example ideally, you don't want to have to worry about creating branches up\nfront and maintaining branches at all. Instead, the relationships of your\npatches are managed through their order in your patch stack. And it's much\neasier to think about those dependencies because that is the way those\ndependencies end up in your actual tree later on.<\/p>\n<p>The last value that I can think that we focus on is, and this isn't that you're\nforced to do this but we do encourage it, that <strong>you have a linear Git\nhistory<\/strong>.<\/p>\n<p>If you think about it, the concept of a patch stack is a linear history itself.\nAnd so the idea is that when you integrate, if you're integrating through Git\nPatch Stacks tooling, you will end up with a linear history.<\/p>\n<p>Now, technically Git Patch Stack is flexible enough that you can integrate\nthrough GitHub, Bitbucket, or GitLab merges. If you do we recommend that you\nconfigure it to do a rebase and merge so that you end up with a linear git\nhistory.<\/p>\n<p>But from a technical standpoint, you don't have to integrate that way. You\ncould do forced merges where you always have a merge commit, for example, where\nyou use <code>git merge --no-ff<\/code>.<\/p>\n<p>It'll always create a commit even if it could fast forward. Some people prefer\nto do that. Long ago in my career I believed that was the best approach because\nI was focused on feature branches back then. However, now I no longer agree\nwith it.<\/p>\n<p>So instead we push this idea of linear Git history. This helps all kinds of\nthings down the road. It helps you understand your Git history more quickly and\neasily. It helps you understand and be able to revert logical changes from your\nGit history.<\/p>\n<p>I hope that provides some insight into what the goals are and why Git Patch\nStack exists. It's definitely continues to be a journey exploring and trying to\nfind, or now days more like, refine this alignment.<\/p>\n"},{"title":"Git Workflows","published":"2023-10-26T00:00:00+00:00","updated":"2023-10-26T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/git-workflows\/"}},"id":"https:\/\/drewdeponte.com\/blog\/git-workflows\/","content":"<p><em>The following is a transcript of an audio recording I made exploring some of\nthe benefits and drawbacks of the Feature Branch, Classic Continuous\nIntegration, Stacked Branch, and Patch Stack workflows. I have also included\nthe audio below if you would prefer to listen.<\/em><\/p>\n<audio controls>\n\t<source src=\"git-workflows.mp3\" type=\"audio\/mpeg\">\nYour browser does not support the audio element.\n<\/audio>\n<p><em>Note: I added some headers into the transcript to try and make it a tad bit\neasier to jump to different sections if you are particularly interested in one\nworkflow.<\/em><\/p>\n<h2 id=\"transcript\">Transcript<\/h2>\n<p>All right.<\/p>\n<p>So let's talk about some common git workflows.<\/p>\n<h3 id=\"feature-branch-workflow\">Feature Branch Workflow<\/h3>\n<p>One of the most common git workflows is the Feature Branching workflow.<\/p>\n<p>And I think one of the primary reasons why it's so common, is largely due to\nthe fact that GitHub was created and GitHub heavily promoted this workflow.\nThis of course is because GitHub is built around this workflows model.<\/p>\n<p>It is worth noting, that just because it's the most common doesn't mean it is\nthe best workflow. It doesn't mean it's the worst workflow. It could be\nsomewhere in between. The truth is the commonality of it doesn't necessarily\nindicate its value, quality, applicability to our needs.<\/p>\n<p>We don't know until we actually reason about it. Comparing and contrasting the\nbenefits and drawbacks of these various workflows. And even then, we still\nmight not know definitively. But it will likely give us a bit more confidence\nand definitely a deeper understanding.<\/p>\n<p>So let's get into the Feature Branch workflow.<\/p>\n<p>The way this works is that you have your git tree, and you have a main branch,\nwhich is your main line of development.<\/p>\n<p>Now, you don't work directly on that branch.<\/p>\n<p>Instead, what you do is you create a feature branch based on it, to do your\nwork in.<\/p>\n<p>And so in that feature branch, you make, all of your commits, all of your\nchanges, inside of that feature branch which is isolated away from all of the\ncode in the main line that's coming in from other developers.<\/p>\n<p>So you're in this safe little isolated world where you can pretend that no\nother changes are really happening for some period of time. Basically the\nperiod of time that it takes you to develop that feature.<\/p>\n<p>And then you have to work to get that feature branch merged back into the main\nbranch and pushed up to the upstream's main.<\/p>\n<p>OK. Now how you go about this depends on your team dynamics and structure.<\/p>\n<p>The way github facilitates this is via this concept they introduced called a\nPull Request. To use it you take your feature branch, you push your feature\nbranch up to the remote as a separate branch and then you open a Pull Request\nwhich is effectively a merge request asking one of your teammates to review &amp;\napprove the merge of that branch into main line. This creates a Pull Request on\nGitHub. And you can see the diff between the main line and the Feature Branch.<\/p>\n<p>So you can see the changes made and people can comment on them and you can\ndiscuss and debate whether those changes are good and you can iterate on your\nfeature and so on and so forth.<\/p>\n<p>And then eventually, you know, once that's approved and good, then you can\nmerge that into main, and you can push main up to the upstream main.<\/p>\n<p>GitHub helps kind of streamline the merging process a little bit by providing a\nbutton to trigger the merge via the web interface.<\/p>\n<p>OK. Now, so far, we've talked about a few things.<\/p>\n<p>There's a couple of things that are nice and a couple of things that are not nice.<\/p>\n<p>One of the things we've talked about is this convenient green button merge thing.<\/p>\n<p>We talked about this idea of a Pull Request where you can see the changes and\nthat's also kind of nice.<\/p>\n<p>But the other thing we talked about was this branch and this idea of\nbranch isolation. Which might seem like a nice thing initially, but you soon\nrealize that the longer you have a branch in isolation and you pretend that\nthere aren't other changes being made to the repository from other teammates,\nyou are increasing the probability that you will run into a conflict and you\nare increasing the probability that the conflict when you run into, will be\nlarger and more complicated to resolve.<\/p>\n<p>So that is a downside to this approach.<\/p>\n<p>The other downside that's kind of hidden to this approach.<\/p>\n<p>Or atleast less obvious, is that Git as a tool promotes this concept of having\nlogical small commits.<\/p>\n<p>OK.<\/p>\n<p>And logical commits means that they are a unit in themselves.<\/p>\n<p>Another term related to this is called atomic commits.<\/p>\n<p>Which can be conceptually thought of as the smallest, atom like commit, or\nindivisible logical unit.<\/p>\n<p>But fundamentally, the idea is that it's a small logical change that that\ndoesn't depend on other changes above it?<\/p>\n<p>It only depends on changes that already exist in the git tree.<\/p>\n<p>It's a logical change that is buildable and testable and everything the way you\nwant it. And that commits in general should be designed in this way.<\/p>\n<p>So the Feature Branch workflow with GitHub has kind of led away from this idea.<\/p>\n<p>What generally happens is that people don't take the time or think about the\ncommits themselves how they should be logically chunked.<\/p>\n<p>In general people using the Feature Branch workflow don't tend to think much\nabout the commits at all.<\/p>\n<p>Instead the developers tend to just think about the Feature Branch as this\nlogically chunked thing and they don't worry about the state of the commits or\nwhether they're logically chunked or not.<\/p>\n<p>Now, the reason this is a problem is because the Git tool itself, and the\ntooling that it provides is designed around these commits being smaller logical\ncommits.<\/p>\n<p>So Git has features that are built around this concept logical commits, and if\nyou don't treat your commits that way, these features don't really work.<\/p>\n<p>So one of those super useful features is called git bisect. It is extremely\nuseful when you're trying to isolate a bug by narrow down what code that\nintroduced it.<\/p>\n<p>But there are other commands as well. Even when you do something like a git\nblame and you're trying to understand why a change was was made. Having logical\ncommits is invaluable.<\/p>\n<p>So the downsides of this Feature Branch workflow are that it kind of takes the\nfocus off of the commit and moves the focus strictly on to this idea of a\nFeature Branch.<\/p>\n<p>And therefore people naturally pay less attention to the commits.<\/p>\n<p>Resulting in you losing the ability to use those vuluable tools.<\/p>\n<p>Then we also have the other downside of it, which is the branch isolation.<\/p>\n<p>When you have a branch isolated for that long, you increase the probability of\nconflicts and the difficulty of those conflicts.<\/p>\n<p>OK. So that's the Feature Branch workflow.<\/p>\n<h3 id=\"classic-continuous-integration-workflow\">Classic Continuous Integration Workflow<\/h3>\n<p>So let's talk about this other workflow I will call the Classic Continuous\nIntegration workflow.<\/p>\n<p>The reason I call this the Classic Continuous Integration workflow is\nbecause it is largely a workflow that people do when following the Continuous\nIntegration methodology.<\/p>\n<p>It is a bit simpler than the branch based workflow because it doesn't really\ndeal with branches.<\/p>\n<p>That doesn't mean you can't do branches with it if you want.<\/p>\n<p>But in general you don't.<\/p>\n<p>Instead, what you do is you make commits directly on your local main branch and\nyou pull in changes from upstream on a regular basis. Now when you pull these\nchanges in you do it in such a way that youl local main branch is rebased onto\nthe latest changes from upstream's main.<\/p>\n<p>Once you get your commit on local main to a good place you integrate your\ncommit with upstream's main by pushing it up.<\/p>\n<p>So it's much simpler and it lets you focus on the commits.<\/p>\n<p>Which makes it easier to create logical commits and think about the commits.<\/p>\n<p>So we are in alignment with the Git and their best practices and\nexpectations to support functionality within their tooling.<\/p>\n<p>The reason it's called continuous integration is because this workflow is\ndriven off of people following the continuous integration methodology. Where\nthe idea is that you pull in changes on a regular basis and that you are\nintegrating your changes with upstream on a regular basis.<\/p>\n<p>When we say a regular basis, we generally mean as quickly as possible.<\/p>\n<p>This is because the the purest idea of continuous integration is that you and\nyour team are always working off of a shared single state.<\/p>\n<p>Now, in reality, with the tooling today that's not completely possible.<\/p>\n<p>But with the idea is that with continuous integration, you want to get as close\nto that ideal as possible.<\/p>\n<p>And so we have this, via this constant pulling and making a commit and\nintegrating it upstream and then pulling, making a commit, integrating it\nupstream, etc. is fundamentally the flow that you have.<\/p>\n<p>Now there this solved some problems, right?<\/p>\n<p>For example we're now focused on the commit, the logical commit.<\/p>\n<p>So we don't have that problem, we don't have branches.<\/p>\n<p>We don't have to worry about branch isolation, which reduces the odds of\nconflicts and their respective difficulty. This is because we're working closer\nto that shared concept of shared truth.<\/p>\n<p>And all of that's great, but it does have its drawbacks, right?<\/p>\n<p>The rub comes when you're dealing with creating these logical commits on top of\nmain.<\/p>\n<p>It requires you to do one of two things, to either write your code from an\ninside out perspective.<\/p>\n<p>Meaning you have to guess what's going to be needed as a dependency to build\nthe future feature you're trying to build and go do that inner most dependency\nfirst, hoping that you've written its interface correctly and then integrate\nthat into upstream. And then the layer above it, and so on and so forth,\neventually to get to where your feature is complete.<\/p>\n<p>But just hoping that your interface is correct, in my experience, is very\ndifficult. It is not very comfortable because you don't have confidence that\nit's correct because you have no other software driving that development or\ngiven you feedback about its correctness.<\/p>\n<p>So that's one approach you can take, but that's very difficult.<\/p>\n<p>The other approach you can take is to use a technique called Feature Toggling,\nwhich allows you to integrate code into upstream that is incomplete.<\/p>\n<p>So you can write code that is not complete, that is not currently being used\nbecause you have a feature toggle that's turned off. But you can\nintegrate that code so that your other developers on your team are aware of it\nand know about it and could build on it, provide feedback to you, or\nprovide guidance about how it might be used in another case, etc.<\/p>\n<p>All of which are positive, useful things.<\/p>\n<p>However, managing feature toggles is another layer of complexity, that has to\nbe dealt with.<\/p>\n<p>Now managing that layer of complexity has a lot of other benefits.<\/p>\n<p>Specifically Feature Toggles end up having a bunch of other benefits when\nyou start talking about things like release management.<\/p>\n<p>So there's a ton of other benefits that you get that in my opinion are much\nmore worth the overhead than the complexity of branches which don't help you in\nthose realms at all. Branches only really help you with organization at the\nworkflow layer, but they don't help you beyond that.<\/p>\n<p>One of the other side effects of this workflow, is that the peer review process\nis very different.<\/p>\n<p>The idea is that instead of opening a pull request and having your code\nreviewed, you do post commit review, which is where you have committed and\nintegrated your commit upstream prior to any review by a peer.<\/p>\n<p>And you're saying that is OK because it passes the build, and it passes all the\ntests.<\/p>\n<p>It is also been integrated into main and our automated CI tests have not\nfailed.<\/p>\n<p>This is very different from the Feature Branch workflow where the review\nhappens prior to integration into upstream main.<\/p>\n<p>Because in the Classic Continuous Integration workflow, the code gets\nintegrated into main prior to being reviewed by other developers.<\/p>\n<p>You might be going well. This is a horrible idea. How could we do that? The\nmain is supposed to be this pure, perfect pristine thing.<\/p>\n<p>The justification for this is simply the realization that you are lying to\nyourself if you actually believe any code is perfect before it goes in. Just\nthink of how many times you have integrated a reviewed feature and then found a\nbug or realized that it needed to be refactored to support something else.<\/p>\n<p>The Classic Continuous Integration stance is simply accepting that main is this\never evolving and changing thing and therefore adjusting it with a future\ncommit is no big deal as that is how we normally change it.<\/p>\n<p>Now, if you talk about tooling Feature Branch workflow works great with GitHub,\nBitbucket, and GitLab. As they are all built around the same model.<\/p>\n<p>But when you start talking about the continuous integration methodology, and\nyou start talking about this post commit review process, it's really difficult\nto find off the shelf tools out there that focus on post commit review process<\/p>\n<p>Most of them are kind of homegrown tools.<\/p>\n<p>But yeah, so, that's the Classic Continuous Integration workflow.<\/p>\n<h3 id=\"stacked-branch-workflow\">Stacked Branch Workflow<\/h3>\n<p>This brings us to the Stacked Branch Workflow.<\/p>\n<p>The Stacked Branch workflow is trying to solve one of the complexities that I\ntalked about in the Classic Continuous Integration workflow while keeping the\nfocus on these atomic logical commits.<\/p>\n<p>So if you remember, one of the hidden complexities I mentioned was that when\nyou're doing the Classic Continuous Integration workflow, you have to develop\nyour code from the inside out, or you have to use feature toggles.<\/p>\n<p>So the way that the Stacked Branches workflow works is that you create a\nbranch, but each of your branches are really focused on one of these atomic\nlogical commits conceptually.<\/p>\n<p>You're making a branch for each of these tiny logical changes that you're\nmaking and then there's additional tooling on top of Git that creates this\ndirected acyclic graph (DAG) that understands the relationship between your\nbranches.<\/p>\n<p>This allows you to create branches and then define the dependency relationships\nbetween them, effectively building a stack (or sequence) of related (or\ndependent) branches.<\/p>\n<p>Having this directed acyclic graph and tooling that is aware of it enables the\ntooling to automate what would be an extremely tedious process of rebasing all\nthe following branches when a lower level dependency branch changes.<\/p>\n<p>It also gives other tooling the information to know in which order the branches\nneed to be integrated.<\/p>\n<p>OK. So it's trying to focus on these logical commits.<\/p>\n<p>It's not totally in alignment with the continuous integration methodology.<\/p>\n<p>But it does facilitate when you pull, rebasing all of your stack of branches on\ntop of the latest main.<\/p>\n<p>So you're at least locally integrating more frequently.<\/p>\n<p>However, it fixes these problems by adding all of these layers of extra work.\nHaving to manage creating of branches, having to define relationships between\nbranches, having to rebase branches, etc.<\/p>\n<p>It does enable you to build your code from the outside in though, which is good.<\/p>\n<p>You still have to integrate your code in an inside out fashion or use feature\ntoggles.<\/p>\n<p>The other downside to this approach is that because this isn't part of pure Git.\nAnd it does not align with the current Feature Branch workflow of GitHub,\nBitbucket, or GitLab. You need additional tooling on top of that.<\/p>\n<p>So your entire team has to switch to this additional tooling and this\nadditional application to see the branches, code review, and know the order in\nwhich those branches need to be reviewed, etc.<\/p>\n<p>OK. So that's the other major downside that even though it starts to solve one\nof the core issues with the Classic Continuous Integration workflow.<\/p>\n<h3 id=\"patch-stack\">Patch Stack<\/h3>\n<p>So the last workflow that I'll talk about is what we call the Patch Stack\nworkflow.<\/p>\n<p>As you might imagine from the name, this workflow is model around the concept\nof managing a stack of patches.<\/p>\n<p>Now this isn't a stack in the purest sense, you aren't limited to just push &amp; pop operations.\nLike you might think if you're thinking about a stack data structure. You can\nreorder patches in the stack, put things in the middle of the stack, pull\nthings out of the stack, etc.<\/p>\n<p>You can imagine it's just this mutable stack of patches that you can change and\nmutate and do whatever you want with.<\/p>\n<p>In this flow, you have a stack of patches which, are basically the commits that\nyou make on top of your local main that have not yet been integrated into\nupstream main.<\/p>\n<p>And when you do the get pull, it rebases your local stack of patches onto the\nnow updated origin\/main.<\/p>\n<p>So this meets the continuous integration needs because anytime you do a Git\npull, it is forcing you to integrate your local stack with the changes that\nhave been introduced by your other teammates.<\/p>\n<p>Not only that, it focuses on this concept of patches or commits, which are just\nlogical commits.<\/p>\n<p>These are the same small logical commits, which are also in alignment with\ncontinuous integration.<\/p>\n<p>Beyond that it allows you to develop your software in an outside-in fashion\nwhile still doing the inside out integration similar to the stacked branch\nworkflow. Except in this workflow, you don't really concern yourself with\nbranches at all.<\/p>\n<p>You don't deal with the overhead of branches.<\/p>\n<p>Instead, you think about a stack of patches.<\/p>\n<p>And if you want a commit to exist, that depends on another commit in your\nstack, you just have to make sure that the commit that it depends on is lower\nin the stack than the commit that you're working on.<\/p>\n<p>This allows you to refine patches over time and work outside in.<\/p>\n<p>But as you resolve and figure out what those inner most dependencies are, you\ncan integrate those right away like you would in a continuous integration\nworkflow or you could request review for them earlier to get them reviewed and\nintegrated into the code base as quickly as possible, which is also more in\nalignment with the continuous integration methodology.<\/p>\n<p>That sounds great.<\/p>\n<p>It sounds like it's solving all the problems of the continuous integration workflow.<\/p>\n<p>So let's talk about peer reviews though.<\/p>\n<p>The Patch Stack workflow allows you to request review of an individual patch or\na series of patches.<\/p>\n<p>And what happens behind the scenes is that this actually ends up mapping back\nto branches.<\/p>\n<p>When you request review of an individual patch, behind the scenes, it goes and\ncreates the branch, cherry-picks that patch into that branch, pushes that\nbranch up to the remote, and can even open the pull request for you on GitHub,\nBitbucket, etc.<\/p>\n<p>This makes it so, it works with the existing tooling that's there. In turn\nallowing you your team to use the same workflow they've always used and the\nsame tooling they've already been using.<\/p>\n<p>But locally, you can develop your code using the Patch Stack workflow.<\/p>\n<p>You might be asking yourself, wait, what about this dependency thing?<\/p>\n<p>Do we have deal with all the complexity of managing branches locally and then\ndefining the relationships between those branches to define the relationships\nbetween dependencies?<\/p>\n<p>Nope, the Patch Stack workflow takes a different stance.<\/p>\n<p>When you have dependent commits like that you have two options.<\/p>\n<p>One is you request review for those individual patches one at a time in the\norder that they need to be integrated and wait for the core dependent one to be\nintegrated before you request review of the next one.<\/p>\n<p>Or you can create what's called a patch series and request review of a patch\nseries.<\/p>\n<p>All that is, is saying, hey, I want to request review for these three patches\nin series in this order they exist in the stack.<\/p>\n<p>And that does the same thing behind the scenes.<\/p>\n<p>It'll create a branch, it'll cherry-pick those patches in sequence into that\nbranch, push it up to the remote, and create a pull request for you based on\nthose patches going into upstream main.<\/p>\n<p>So that's how the Patch Stack workflow deals with dependent patches.<\/p>\n<p>You can have multiple patch series stacked on top of each other in your patch\nstack.<\/p>\n<p>You can also have individual patches inter weaved in there if you want. It\nallows you to manage all of that directly within your patch stack, which is\njust you focusing and thinking about logical patches.<\/p>\n<p>So that's the Patch Stack workflow.<\/p>\n<p>The benefits of the Patch Stack workflow is that it gets you as close as\npossible to continuous integration but gives you the flexibility of defining a\npatch series if you need to.<\/p>\n<p>So you don't have that overhead of being required to manage these branches and\ntheir dependencies between each other.<\/p>\n<p>Beyond that, it works with the existing tooling.<\/p>\n<p>So it works with the normal GitHub, Bitbucket, GitLab, etc. and their Feature\nBranch based review process.<\/p>\n<p>So you don't have to worry about any of this post commit review stuff, or the\ntooling around that, or getting custom tooling made, or any of that stuff. You\njust use the same tools you are likely already using.<\/p>\n<p>In fact, another benefit of this is that it facilitates adoption more easily.<\/p>\n<p>So let's say you as an individual are impressed with what you've seen from the\natomic commit community and want to start adopting those practices.<\/p>\n<p>But your team is still working in a Feature Branch workflow.<\/p>\n<p>Well, that's completely fine.<\/p>\n<p>You can use the Patch Stack workflow locally and the rest of your team never\neven has to know.<\/p>\n<p>They don't care, they just see a Pull Request, and they do a review, the same\nway they have previously for the Feature Branch workflow.<\/p>\n<h3 id=\"conclusion\">Conclusion<\/h3>\n<p>So those are the four different workflows I wanted to talk about.<\/p>\n<p>Hopefully, this helps clear up some of the concepts as well as some of the pros and cons of these four workflows.<\/p>\n<p>Personally, I think the Patch Stack workflow makes the most sense.<\/p>\n<p>Anyway, hope you enjoyed. Until next time.<\/p>\n"},{"title":"Cursor based Pagination with multi-key cursor","published":"2023-04-02T00:00:00+00:00","updated":"2023-04-02T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/cursor-based-pagination-with-multi-key-cursor\/"}},"id":"https:\/\/drewdeponte.com\/blog\/cursor-based-pagination-with-multi-key-cursor\/","content":"<p>Let's say for sake of discussion that we want to paginate a set of transactions\nto be presented in a mobile app with infinite scroll implemented. We will also\nassume that for sake of discussion our service is backed by PostgreSQL as our\ndatabase.<\/p>\n<p>We obviously want to see these transactions in relation to time. Probably the\nmost recently created transactions first and then the older transactions as we\nscroll through the infinite scroll. So we will likely want to order these\ntransactions by their timestamp in descending order.<\/p>\n<h2 id=\"cursor-based-pagination\">Cursor based Pagination<\/h2>\n<p>Because of our use case we can use a strategy called Cursor Based pagination.\nIn this strategy we pass a \"cursor\" identifying the transaction of the last\nitem in the previous pages results and a limit to get back the next page.<\/p>\n<h2 id=\"use-transaction-datetime-as-cursor\">Use <code>transaction_datetime<\/code> as cursor<\/h2>\n<p>You might be thinking, well we have the <code>transaction_datetime<\/code>, can't we just\nuse that as the \"cursor\". The answer is of course you could. However, if you\nhave timestamps that aren't super granular. This happens when dealing with\nbanks &amp; transactions as you will get back a full timestamp object, but it will\nonly have information for the day, month, and year. This means in your dataset\nyou may have a decent number of transactions that are all effectively at the\nsame timestamp.<\/p>\n<p>This is problematic because you have to either do a where condition in your\nquery of <code>&lt;<\/code> or <code>&lt;=<\/code> to the cursor. If we do <code>&lt;<\/code> we will miss out on\ntransactions that should be visible. If we do <code>&lt;=<\/code> we will end up with\nduplicate transactions. Neither of these are acceptable from a user experience\nstandpoint.<\/p>\n<h2 id=\"what-if-we-combine-transaction-datetime-with-another-cursor\">What if we combine <code>transaction_datetime<\/code> with another cursor<\/h2>\n<p>Well if we for example had a UUID for the <code>id<\/code> column of a transaction we could\nuse that, as ordering of UUIDs would be consistent. This would allow us to have\na query like the following.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"sql\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">SELECT * FROM<\/span><span> transactions <\/span><span style=\"color: #E06C75;\">ORDER BY<\/span><span> transaction_datetime <\/span><span style=\"color: #E06C75;\">DESC<\/span><span>, id <\/span><span style=\"color: #E06C75;\">DESC<\/span><span>;<\/span><\/span><\/code><\/pre>\n<p>In the above we use a primary order of <code>transaction_datetime<\/code> with a secondary\norder of <code>id<\/code>. This makes it so that if there are multiple transactions\nassociated with the same <code>transaction_datetime<\/code> that they will all be\nconsistently ordered by the <code>id<\/code>.<\/p>\n<h2 id=\"getting-the-next-page\">Getting the Next Page<\/h2>\n<p>Somehow we have to use the cursor now composed of two values\n<code>transaction_datetime<\/code> and <code>id<\/code> to figure out where the dataset should start\nfor the next page of values.<\/p>\n<p>If we tried to do something like the following<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"sql\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">SELECT * FROM<\/span><span> transactions <\/span><span style=\"color: #E06C75;\">WHERE<\/span><span> transaction_datetime <\/span><span style=\"color: #E06C75;\">&lt;<\/span><span> &#39;<\/span><span style=\"color: #98C379;\">2021-02-23 04:23:23<\/span><span>&#39;<\/span><span style=\"color: #E06C75;\"> AND<\/span><span> id <\/span><span style=\"color: #E06C75;\">&lt;<\/span><span> &#39;<\/span><span style=\"color: #98C379;\">A234U-23432aue-2343aue-aauo234<\/span><span>&#39;<\/span><\/span><\/code><\/pre>\n<p>We would have a problem because we would be filtering out transactions that are\nless than the datetime but greater than the UUID. In turn making the resulting\nset not what we want.<\/p>\n<p>So instead we have to use a feature of PostgreSQL called <em>Composite Values<\/em> in\nthe where query. I think of this as allowing us to create a tuple of\nvalues that it then knows how to compare in combination rather than\nindividually.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"sql\"><span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">SELECT * FROM<\/span><span> transactions <\/span><span style=\"color: #E06C75;\">WHERE<\/span><span> (transaction_datetime, id) <\/span><span style=\"color: #E06C75;\">&lt;<\/span><span> (&#39;<\/span><span style=\"color: #98C379;\">2021-02-23 04:23:23<\/span><span>&#39;, &#39;<\/span><span style=\"color: #98C379;\">A234U-23432aue-2343aue-aauo234<\/span><span>&#39;)<\/span><\/span><\/code><\/pre>\n<p>With this we actually get back the correct set as we are sorting on the\ncombined value not the individual values.<\/p>\n"},{"title":"Inversion of Control","published":"2023-02-09T00:00:00+00:00","updated":"2023-02-09T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/inversion-of-control\/"}},"id":"https:\/\/drewdeponte.com\/blog\/inversion-of-control\/","content":"<p>Within the last month, I have interacted with several developers that don't have\na great understanding of the concept of <em>Inversion of Control (IoC)<\/em>. Just the\nother day one of the developers at <a rel=\"external\" href=\"https:\/\/uptechstudio.com\">Uptech Studio<\/a> asked me to explain it\nafter I mentioned it as a way of separating DB concerns from business logic\nconcerns when the DB concerns are tied together by a DB transaction.<\/p>\n<p>I introduced this developer to the concept of <em>IoC<\/em> and walked him through how\nI think about code and the concept of <em>Separation of Concerns<\/em> in a way that\nenables me to more easily understand how and where to apply <em>IoC<\/em>. So I figured\nI might as well write it up so that I can share it with the rest of the world\nin hopes that it can help others as well.<\/p>\n<p>So let's get into it.<\/p>\n<h2 id=\"what-is-inversion-of-control\">What Is Inversion of Control?<\/h2>\n<p>The other day I was talking to another developer about struggling to find a\ngood resource that really explained <em>IoC<\/em>, and half-jokingly he asked\nChatGPT to explain it with the following.<\/p>\n<blockquote>\n<p>Can you explain the coding principle \u201cinversion of control\u201d?<\/p>\n<\/blockquote>\n<p>It came back with the following.<\/p>\n<blockquote>\n<p>Inversion of control (IoC) is a software design principle that encourages the\ndesign of loosely coupled components in which the flow of control is not\ndetermined by a component itself, but by a container or framework that is\nresponsible for assembling the components and coordinating their behavior.\nThis results in a separation of concerns between the components and the\ncontainer, where the components focus on their own specific tasks, while the\ncontainer is responsible for managing their interactions.<\/p>\n<p>IoC is often achieved through dependency injection, in which the container\nsupplies the components with their dependencies, rather than the components\ncreating or looking up their own dependencies. This leads to a flexible and\nmodular architecture, where components can be easily replaced or reused\nwithout affecting the rest of the system.<\/p>\n<\/blockquote>\n<p>To be honest it did a great job at returning a pretty clear definition of what\n<em>IoC<\/em> is. Good enough in fact that I wouldn't even try to change it. Trying to\nget ChatGPT to provide meaningful examples in article form, that I could hand\nsomeone to teach them <em>IoC<\/em>, was a massive failure. And as we all know being able\nto read a conceptual definition like the one above and being able to understand\nit, internalize it, and apply it while coding in the wild is an\nentirely different thing. Hence, why I am writing this article.<\/p>\n<h2 id=\"ioc-at-all-the-levels\">IoC at all the levels<\/h2>\n<p><em>IoC<\/em> as a concept is applied &amp; can be applied across all the different levels\nof abstraction in programming. At the higher levels, we can classically see it\nwith the distinction between the concept of libraries and frameworks.\nFrameworks are simply libraries that apply the concept of <em>IoC<\/em>.<\/p>\n<p>If we consider how we use a GUI framework. We generally create some sort of\nApplication level object and call <code>run()<\/code> on it. The GUI framework is in\ncontrol of the flow of execution of the program and reaches out to other\nobjects, we provide, to ask them to perform different pieces and hand the\nframework back results so that it can continue to manage the control flow of\nthe application. On the other hand, if we were using a GUI library. We would be\nhaving to manage the control flow of the application ourselves, and we would in\nturn be calling out to different functions\/methods in the library to do\ndifferent pieces for us. So with this distinction, we can see that in the\nlibrary case we are responsible for the control flow of the application but in\nthe framework case we hand that responsibility of control flow over to the\nframework.<\/p>\n<p>At the lower levels of abstraction, we classically see it with most higher-order\nfunctions, especially the higher-order functions on collections, e.g. <code>map<\/code>,\n<code>reduce<\/code>, <code>filter<\/code>, etc. If we consider using <code>filter<\/code> as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">someCollection<\/span><span>.<\/span><span style=\"color: #B57EDC;\">filter<\/span><span>((<\/span><span style=\"color: #C6CCD7;\">item<\/span><span>)<\/span><span style=\"color: #61AFEF;\"> =&gt;<\/span><span style=\"color: #C6CCD7;\"> item<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">isOld<\/span><span>)<\/span><\/span><\/code><\/pre>\n<p>We are handing the control flow of the program over to the <code>filter()<\/code> procedure\nwhile it asks us to provide criteria to filter with via the provided closure.\nThis is an example of <em>IoC<\/em> just as the framework vs. library concept is. The\nbig distinction between them is that in the case of a full application\nframework you are handing over control for the lifetime of the application. In\nthe case of <code>filter()<\/code> we are handing over control only for the lifetime of\nthat particular application of the filter process.<\/p>\n<p>The key is understanding that we can hand over the control flow of the\napplication at any of the levels of abstraction and it is all <em>IoC<\/em>. It is\ninverting the control by handing it over to something that in classical\nimperative procedural code does not have the \"control\".<\/p>\n<h2 id=\"understanding-ioc-with-code\">Understanding IoC with Code<\/h2>\n<p>To better understand how to think about this concept in relation to code let's\ntake a look at a simple example where we have a collection of numbers and\nwe want to have a new collection of numbers that has each value in it but with\n5 added to it. In classic imperative procedural code, we would implement this\nsomething like the following.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> myNumbers<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Array<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">number<\/span><span>&gt;<\/span><span style=\"color: #E06C75;\"> =<\/span><span> [<\/span><span style=\"color: #56B6C2;\">2<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 4<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 7<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 13<\/span><span>]<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">let<\/span><span style=\"color: #C6CCD7;\"> myNewNumbers<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Array<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">number<\/span><span>&gt;<\/span><span style=\"color: #E06C75;\"> =<\/span><span> []<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">for<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">num<\/span><span style=\"color: #E06C75;\"> in<\/span><span style=\"color: #C6CCD7;\"> myNumbers<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">\tlet<\/span><span style=\"color: #C6CCD7;\"> newNum<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #C6CCD7;\"> num<\/span><span style=\"color: #E06C75;\"> +<\/span><span style=\"color: #56B6C2;\"> 5<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\tmyNewNumbers<\/span><span>.<\/span><span style=\"color: #B57EDC;\">push<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">newNum<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ myNewNumbers is now [7, 9, 12, 18]<\/span><\/span><\/code><\/pre><h3 id=\"separation-of-concerns\">Separation of Concerns<\/h3>\n<p>Before we can start to think about the possibility of applying the concept of\n<em>IoC<\/em> we have some groundwork that we have to do first. We need to understand\nthe various <em>concerns\/responsibilities<\/em> involved in the code and classify the\nvarious lines, statements, and expressions in terms of these\n<em>concerns\/responsibilities<\/em>. It is also extremely useful to understand the\nhigh-level goal of the segment of code being analyzed as it will help form our\nthinking conceptually when applying <em>IoC<\/em>.<\/p>\n<p>It is probably worth talking about the idea of a <em>concern<\/em>, <em>responsibility<\/em>,\n<em>domain<\/em>, or <em>problem space<\/em> which are mostly used interchangeably. They all are\nused to define a particular bound around a problem. The difficulty people find\nwith them is that you can in theory take a bound around any problem and break\nit down further into smaller bounds around sub-problems infinitely. The other\nthing that people tend to find difficult is that they are arbitrary. Meaning\nyou can define the bounds to be whatever you want. In practice in software\ndevelopment, this is simply defining a bound around a problem space and\ngenerally mapping an abstraction of some sort to that bound.<\/p>\n<p>This breaking up of a bound around a problem into sub-problems with their\nbounds is the foundation of the concept of <em>Separation of Concerns<\/em>.\nAnd as we will see throughout this process <em>IoC<\/em> is simply a specific example\nof <em>Separation of Concerns<\/em>.<\/p>\n<h3 id=\"understanding-the-concerns\">Understanding the Concerns<\/h3>\n<p>Let's take a look at the example from above and see if we can identify the\noverall <em>concern\/responsibility<\/em> and then try to classify concerns that the\nvarious lines of code belong to.<\/p>\n<p>Overall the <em>concern<\/em> of the example above is to take the <code>myNumbers<\/code> array and\ncreate a new array containing the values from <code>myNumbers<\/code> but with 5 added to\nit. That doesn't feel like much of a <em>concern<\/em>, but technically it is and since\nit is what we have right now let's run with it.<\/p>\n<p>Now that we feel like we have a high-level <em>concern<\/em> figured\nout let's think about <em>IoC<\/em> a bit deeper. We talked at a high level about what\nthe inversion portion of <em>IoC<\/em> is. But what is the control portion in our current\ncontext. The way I think about it is by trying to break the overall <em>concern<\/em>\ninto <em>sub concerns<\/em> where one of the <em>sub concerns<\/em> is the \"control\".<\/p>\n<p>To do that we need to understand what the \"control\" is. It is essentially any\nportion of the code that controls the flow of a process. So we have to start\nthinking about the code that controls the flow of a process as one <em>concern<\/em>\nand the various steps\/pieces of the process as other <em>concerns<\/em>. I also think\nabout this as code representing one of two things, the triggering of doing\nsomething &amp; the actual how of doing something. So we can start thinking about\nthe ability to separate the how of doing something from the triggering of doing\nsomething. It may not be a perfect way of thinking, but it has tended to help\nme.<\/p>\n<p>Let's try and define the various concerns of our example. It is important to\nrealize that this is a process. It isn't magical, and you don't always know all\nthe <em>concerns<\/em> right out of the gate. Sometimes you just start knowing that\none concern is the \"control\" and the others you aren't sure about yet. Removing\nthe noise of the code that you know is the \"control\" can help you think more\nclearly about the code that is <strong>not<\/strong> control and its respective <em>concerns<\/em>.\nBelow is our example from above in classical imperative procedural form. We\nwill annotate it with comments to try and communicate the concerns as we\nprogress below.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> myNumbers<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Array<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">number<\/span><span>&gt;<\/span><span style=\"color: #E06C75;\"> =<\/span><span> [<\/span><span style=\"color: #56B6C2;\">2<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 4<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 7<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 13<\/span><span>]<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">let<\/span><span style=\"color: #C6CCD7;\"> myNewNumbers<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Array<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">number<\/span><span>&gt;<\/span><span style=\"color: #E06C75;\"> =<\/span><span> []<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">for<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">num<\/span><span style=\"color: #E06C75;\"> in<\/span><span style=\"color: #C6CCD7;\"> myNumbers<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">\tlet<\/span><span style=\"color: #C6CCD7;\"> newNum<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #C6CCD7;\"> num<\/span><span style=\"color: #E06C75;\"> +<\/span><span style=\"color: #56B6C2;\"> 5<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\tmyNewNumbers<\/span><span>.<\/span><span style=\"color: #B57EDC;\">push<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">newNum<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ myNewNumbers is now [7, 9, 12, 18]<\/span><\/span><\/code><\/pre>\n<p>Looking at <code>const myNumbers: Array&lt;number&gt; = [2, 4, 7, 13]<\/code> it is just\nproviding the starting value for the process. It isn't controlling the flow of\nthe process. Therefore, I would <strong>not<\/strong> classify it as part of the \"control\"\n<em>concern<\/em>. So let's tag it with an empty comment <code>\/\/<\/code>.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> myNumbers<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Array<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">number<\/span><span>&gt;<\/span><span style=\"color: #E06C75;\"> =<\/span><span> [<\/span><span style=\"color: #56B6C2;\">2<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 4<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 7<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 13<\/span><span>]<\/span><span style=\"color: #5F6672;font-style: italic;\">  \/\/ other<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">let<\/span><span style=\"color: #C6CCD7;\"> myNewNumbers<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Array<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">number<\/span><span>&gt;<\/span><span style=\"color: #E06C75;\"> =<\/span><span> []<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">for<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">num<\/span><span style=\"color: #E06C75;\"> in<\/span><span style=\"color: #C6CCD7;\"> myNumbers<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">\tlet<\/span><span style=\"color: #C6CCD7;\"> newNum<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #C6CCD7;\"> num<\/span><span style=\"color: #E06C75;\"> +<\/span><span style=\"color: #56B6C2;\"> 5<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\tmyNewNumbers<\/span><span>.<\/span><span style=\"color: #B57EDC;\">push<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">newNum<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ myNewNumbers is now [7, 9, 12, 18]<\/span><\/span><\/code><\/pre>\n<p>If we look at the <code>let myNewNumbers: Array&lt;number&gt; = []<\/code> on the first line of\ncode and ask ourselves \"Why is it there?\" In this case, it is there to\nsupport the flow of this process by being the new array that the new values\nwill live in. So let's flag it as \"control\" with a comment.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> myNumbers<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Array<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">number<\/span><span>&gt;<\/span><span style=\"color: #E06C75;\"> =<\/span><span> [<\/span><span style=\"color: #56B6C2;\">2<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 4<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 7<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 13<\/span><span>]<\/span><span style=\"color: #5F6672;font-style: italic;\">  \/\/<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">let<\/span><span style=\"color: #C6CCD7;\"> myNewNumbers<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Array<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">number<\/span><span>&gt;<\/span><span style=\"color: #E06C75;\"> =<\/span><span> []<\/span><span style=\"color: #5F6672;font-style: italic;\">            \/\/ control<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">for<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">num<\/span><span style=\"color: #E06C75;\"> in<\/span><span style=\"color: #C6CCD7;\"> myNumbers<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">\tlet<\/span><span style=\"color: #C6CCD7;\"> newNum<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #C6CCD7;\"> num<\/span><span style=\"color: #E06C75;\"> +<\/span><span style=\"color: #56B6C2;\"> 5<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\tmyNewNumbers<\/span><span>.<\/span><span style=\"color: #B57EDC;\">push<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">newNum<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ myNewNumbers is now [7, 9, 12, 18]<\/span><\/span><\/code><\/pre>\n<p>OK so what about the <code>for (num in myNumbers) {<\/code>? Well, it is there to\nfacilitate iterating through the collection. This sounds like\ncontrolling the flow of the process to me. So let's flag it as control.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> myNumbers<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Array<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">number<\/span><span>&gt;<\/span><span style=\"color: #E06C75;\"> =<\/span><span> [<\/span><span style=\"color: #56B6C2;\">2<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 4<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 7<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 13<\/span><span>]<\/span><span style=\"color: #5F6672;font-style: italic;\">  \/\/<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">let<\/span><span style=\"color: #C6CCD7;\"> myNewNumbers<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Array<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">number<\/span><span>&gt;<\/span><span style=\"color: #E06C75;\"> =<\/span><span> []<\/span><span style=\"color: #5F6672;font-style: italic;\">            \/\/ control<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">for<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">num<\/span><span style=\"color: #E06C75;\"> in<\/span><span style=\"color: #C6CCD7;\"> myNumbers<\/span><span>) {<\/span><span style=\"color: #5F6672;font-style: italic;\">                        \/\/ control<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">\tlet<\/span><span style=\"color: #C6CCD7;\"> newNum<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #C6CCD7;\"> num<\/span><span style=\"color: #E06C75;\"> +<\/span><span style=\"color: #56B6C2;\"> 5<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\tmyNewNumbers<\/span><span>.<\/span><span style=\"color: #B57EDC;\">push<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">newNum<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><span style=\"color: #5F6672;font-style: italic;\">                                               \/\/ control<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ myNewNumbers is now [7, 9, 12, 18]<\/span><\/span><\/code><\/pre>\n<p>OK, now we are onto <code>let newNum = num + 5<\/code>. This is interesting because this is\ndoing two things. The <code>let newNum =<\/code> portion is controlling the flow of the\nprocess while the <code>num + 5<\/code> portion is more about the work of computing a new\nvalue and not really about the flow control of the process. So let's flag it to\nspecify this detail.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> myNumbers<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Array<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">number<\/span><span>&gt;<\/span><span style=\"color: #E06C75;\"> =<\/span><span> [<\/span><span style=\"color: #56B6C2;\">2<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 4<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 7<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 13<\/span><span>]<\/span><span style=\"color: #5F6672;font-style: italic;\">  \/\/<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">let<\/span><span style=\"color: #C6CCD7;\"> myNewNumbers<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Array<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">number<\/span><span>&gt;<\/span><span style=\"color: #E06C75;\"> =<\/span><span> []<\/span><span style=\"color: #5F6672;font-style: italic;\">            \/\/ control<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">for<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">num<\/span><span style=\"color: #E06C75;\"> in<\/span><span style=\"color: #C6CCD7;\"> myNumbers<\/span><span>) {<\/span><span style=\"color: #5F6672;font-style: italic;\">                        \/\/ control<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">\tlet<\/span><span style=\"color: #C6CCD7;\"> newNum<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #C6CCD7;\"> num<\/span><span style=\"color: #E06C75;\"> +<\/span><span style=\"color: #56B6C2;\"> 5<\/span><span style=\"color: #5F6672;font-style: italic;\">                        \/\/ `let newNum =` control, `num + 5` some other concern<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\tmyNewNumbers<\/span><span>.<\/span><span style=\"color: #B57EDC;\">push<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">newNum<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><span style=\"color: #5F6672;font-style: italic;\">                                               \/\/ control<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ myNewNumbers is now [7, 9, 12, 18]<\/span><\/span><\/code><\/pre>\n<p>What about <code>myNewNumbers.push(newNum)<\/code>? This seems to be code that manages how\nthis process flows, so it seems to be control. Let's flag it as control.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> myNumbers<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Array<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">number<\/span><span>&gt;<\/span><span style=\"color: #E06C75;\"> =<\/span><span> [<\/span><span style=\"color: #56B6C2;\">2<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 4<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 7<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 13<\/span><span>]<\/span><span style=\"color: #5F6672;font-style: italic;\">  \/\/<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">let<\/span><span style=\"color: #C6CCD7;\"> myNewNumbers<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Array<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">number<\/span><span>&gt;<\/span><span style=\"color: #E06C75;\"> =<\/span><span> []<\/span><span style=\"color: #5F6672;font-style: italic;\">            \/\/ control<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">for<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">num<\/span><span style=\"color: #E06C75;\"> in<\/span><span style=\"color: #C6CCD7;\"> myNumbers<\/span><span>) {<\/span><span style=\"color: #5F6672;font-style: italic;\">                        \/\/ control<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">\tlet<\/span><span style=\"color: #C6CCD7;\"> newNum<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #C6CCD7;\"> num<\/span><span style=\"color: #E06C75;\"> +<\/span><span style=\"color: #56B6C2;\"> 5<\/span><span style=\"color: #5F6672;font-style: italic;\">                        \/\/ `let newNum =` control, `num + 5` some other concern<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\tmyNewNumbers<\/span><span>.<\/span><span style=\"color: #B57EDC;\">push<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">newNum<\/span><span>)<\/span><span style=\"color: #5F6672;font-style: italic;\">                   \/\/ control<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><span style=\"color: #5F6672;font-style: italic;\">                                               \/\/ control<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ myNewNumbers is now [7, 9, 12, 18]<\/span><\/span><\/code><\/pre>\n<p>At this point, we have classified all lines, statements, and expressions of code,\nand it feels like we have a pretty decent understanding of what code exists for\nthe purpose of controlling the flow of the process and which code doesn't. In\nfact there are only two chunks of code that don't belong to the \"control\"\n<em>concern<\/em>, <code>const myNumbers: Array&lt;number&gt; = [2, 4, 7, 13]<\/code> and <code>num + 5<\/code>.<\/p>\n<h3 id=\"grouping-separating-concerns\">Grouping &amp; Separating Concerns<\/h3>\n<p>Now that we have an understanding of at least some portion of the various\nconcerns. We need to focus on conceptually grouping and separating them. In\nour case, we have the \"control\" <em>concern<\/em> and then two pieces of code that right\nnow we know are not part of the \"control\" <em>concern<\/em>. The first step in\nseparating concerns is to simply map those concerns to specific abstractions.<\/p>\n<p>To start let's take all the code that is \"control\" code and extract it to a\nfunction we will call <code>foo<\/code> for now. <em>Note:<\/em> I use a garbage name for the\nfunction like <code>foo<\/code> right now because I like to think about the concern\nconceptually after having moved the \"control\" code into it as I think it makes\nit easier to think about. If we do this we end up with something like the\nfollowing.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">function<\/span><span style=\"color: #B57EDC;\"> foo<\/span><span>() {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">    let<\/span><span style=\"color: #C6CCD7;\"> myNewNumbers<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Array<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">number<\/span><span>&gt;<\/span><span style=\"color: #E06C75;\"> =<\/span><span> []<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    for<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">num<\/span><span style=\"color: #E06C75;\"> in<\/span><span style=\"color: #C6CCD7;\"> _________<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">\t    let<\/span><span style=\"color: #C6CCD7;\"> newNum<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #C6CCD7;\"> _______<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\t    myNewNumbers<\/span><span>.<\/span><span style=\"color: #B57EDC;\">push<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">newNum<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span>    }<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> myNumbers<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Array<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">number<\/span><span>&gt;<\/span><span style=\"color: #E06C75;\"> =<\/span><span> [<\/span><span style=\"color: #56B6C2;\">2<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 4<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 7<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 13<\/span><span>]<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\tnum<\/span><span style=\"color: #E06C75;\"> +<\/span><span style=\"color: #56B6C2;\"> 5<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ myNewNumbers is now [7, 9, 12, 18]<\/span><\/span><\/code><\/pre>\n<p>I put <code>________<\/code> in the function where the other <em>concerns<\/em> were interacting\nbefore. This helps us think about those elements in a more generic way.\nTo make this function usable we have to fill in those <code>______<\/code> blanks. The only\nway to do that while maintaining the abstraction is to pass them in as\nparameters to the function. Our example would now look as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">function<\/span><span style=\"color: #B57EDC;\"> foo<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">myNumbers<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Array<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">number<\/span><span>&gt;,<\/span><span style=\"color: #B57EDC;\"> op<\/span><span style=\"color: #E06C75;\">:<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">number<\/span><span>)<\/span><span style=\"color: #61AFEF;\"> =&gt;<\/span><span style=\"color: #E5C07B;\"> number<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">    let<\/span><span style=\"color: #C6CCD7;\"> myNewNumbers<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Array<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">number<\/span><span>&gt;<\/span><span style=\"color: #E06C75;\"> =<\/span><span> []<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    for<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">num<\/span><span style=\"color: #E06C75;\"> in<\/span><span style=\"color: #C6CCD7;\"> myNumbers<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">\t    let<\/span><span style=\"color: #C6CCD7;\"> newNum<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #B57EDC;\"> op<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">num<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\t    myNewNumbers<\/span><span>.<\/span><span style=\"color: #B57EDC;\">push<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">newNum<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span>    }<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\treturn<\/span><span style=\"color: #C6CCD7;\"> myNewNumbers<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">foo<\/span><span>([<\/span><span style=\"color: #56B6C2;\">2<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 4<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 7<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 13<\/span><span>], (<\/span><span style=\"color: #C6CCD7;\">num<\/span><span>)<\/span><span style=\"color: #61AFEF;\"> =&gt;<\/span><span style=\"color: #C6CCD7;\"> num<\/span><span style=\"color: #E06C75;\"> +<\/span><span style=\"color: #56B6C2;\"> 5<\/span><span>)<\/span><span style=\"color: #5F6672;font-style: italic;\"> \/\/ =&gt; [7, 9, 12, 18]<\/span><\/span><\/code><\/pre>\n<p>By doing this we have not only made the <code>foo<\/code> function usable. But we have made\nit more generic as we can now hand it any Array of numbers to operate on, and we\ncan hand it any operation to perform on each element as long as it results in a\nnumber.<\/p>\n<p>At this point, we have successfully <strong>inverted control<\/strong>, and we have done so by\nidentifying the control code, extracted it out into a function, and then using\nthat function in its original place. It is important to understand that this\nis <strong>IoC<\/strong> because when we call the <code>foo()<\/code> function. We are handing control\nover to the <code>foo()<\/code> function for the lifetime of the <code>foo()<\/code> process while we\nare still providing the details of the work it does.<\/p>\n<h3 id=\"generalize-types\">Generalize Types<\/h3>\n<p>When using IoC it is common practice to generalize functions to remove\nunnecessary coupling to specific types from the \"control\" code. This is another\nmechanism for focusing only on the flow control of the process and less on the\nspecifics of the types involved. This also has the benefit of generally making\nthe extracted abstraction more flexible.<\/p>\n<p>If we start by looking at <code>myNumbers: Array&lt;number&gt;<\/code> and that it is only used\nin the <code>for (num in myNumbers)<\/code> line. <code>for (x in y)<\/code> doesn't care what type of\narray <code>y<\/code> is. It just cares that it is an Array. If we make that change it\nwould look as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">function<\/span><span style=\"color: #B57EDC;\"> foo<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">A<\/span><span>&gt;(<\/span><span style=\"color: #C6CCD7;\">myNumbers<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Array<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">A<\/span><span>&gt;,<\/span><span style=\"color: #B57EDC;\"> op<\/span><span style=\"color: #E06C75;\">:<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">number<\/span><span>)<\/span><span style=\"color: #61AFEF;\"> =&gt;<\/span><span style=\"color: #E5C07B;\"> number<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">    let<\/span><span style=\"color: #C6CCD7;\"> myNewNumbers<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Array<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">number<\/span><span>&gt;<\/span><span style=\"color: #E06C75;\"> =<\/span><span> []<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    for<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">num<\/span><span style=\"color: #E06C75;\"> in<\/span><span style=\"color: #C6CCD7;\"> myNumbers<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">\t    let<\/span><span style=\"color: #C6CCD7;\"> newNum<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #B57EDC;\"> op<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">num<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\t    myNewNumbers<\/span><span>.<\/span><span style=\"color: #B57EDC;\">push<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">newNum<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span>    }<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\treturn<\/span><span style=\"color: #C6CCD7;\"> myNewNumbers<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">foo<\/span><span>([<\/span><span style=\"color: #56B6C2;\">2<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 4<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 7<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 13<\/span><span>], (<\/span><span style=\"color: #C6CCD7;\">num<\/span><span>)<\/span><span style=\"color: #61AFEF;\"> =&gt;<\/span><span style=\"color: #C6CCD7;\"> num<\/span><span style=\"color: #E06C75;\"> +<\/span><span style=\"color: #56B6C2;\"> 5<\/span><span>)<\/span><span style=\"color: #5F6672;font-style: italic;\"> \/\/ =&gt; [7, 9, 12, 18]<\/span><\/span><\/code><\/pre>\n<p>This creates a temporary problem which is that the typing of <code>op<\/code> would now\nalso need to change so that it would take in a <code>A<\/code>. And then we are immediately\nconfronted with the next question. What type does <code>op<\/code> return. From looking at\nthe code, currently we have the values ending up in <code>myNewNumbers<\/code> which is a\n<code>Array&lt;number&gt;<\/code>. So we would have to have it return a <code>number<\/code> if we didn't\nchange the code further. But it doesn't seem like there is any reason we\nshouldn't just allow that array to be generic as well so that <code>op<\/code> can return a\ndifferent type. This would allow the user to convert the types of\nelements within a collection if they wanted. We should also update the names of\nthe parameters to represent more generic concepts.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">function<\/span><span style=\"color: #B57EDC;\"> foo<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">A<\/span><span>,<\/span><span style=\"color: #E5C07B;\"> B<\/span><span>&gt;(<\/span><span style=\"color: #C6CCD7;\">collection<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Array<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">A<\/span><span>&gt;,<\/span><span style=\"color: #B57EDC;\"> op<\/span><span style=\"color: #E06C75;\">:<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">A<\/span><span>)<\/span><span style=\"color: #61AFEF;\"> =&gt;<\/span><span style=\"color: #E5C07B;\"> B<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">    let<\/span><span style=\"color: #C6CCD7;\"> newCollection<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Array<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">B<\/span><span>&gt;<\/span><span style=\"color: #E06C75;\"> =<\/span><span> []<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    for<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">item<\/span><span style=\"color: #E06C75;\"> in<\/span><span style=\"color: #C6CCD7;\"> collection<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">\t    let<\/span><span style=\"color: #C6CCD7;\"> newItem<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #B57EDC;\"> op<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">item<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\t    newCollection<\/span><span>.<\/span><span style=\"color: #B57EDC;\">push<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">newItem<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span>    }<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\treturn<\/span><span style=\"color: #C6CCD7;\"> newCollection<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">foo<\/span><span>([<\/span><span style=\"color: #56B6C2;\">2<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 4<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 7<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 13<\/span><span>], (<\/span><span style=\"color: #C6CCD7;\">num<\/span><span>)<\/span><span style=\"color: #61AFEF;\"> =&gt;<\/span><span style=\"color: #C6CCD7;\"> num<\/span><span style=\"color: #E06C75;\"> +<\/span><span style=\"color: #56B6C2;\"> 5<\/span><span>)<\/span><span style=\"color: #5F6672;font-style: italic;\"> \/\/ =&gt; [7, 9, 12, 18]<\/span><\/span><\/code><\/pre>\n<p>The above <code>foo()<\/code> function now supports our existing use case of wanting to add\n5 to each element of the array but also supports a bunch of other cool things\nwe can do because we have simply focused on separating an abstraction that\nfocuses as much as possible on the control flow of the process. This is what\n<em>Inversion of Control<\/em> is all about.<\/p>\n<h3 id=\"naming-abstraction\">Naming Abstraction<\/h3>\n<p>So now it seems we need to think about what our <code>foo()<\/code> function\nfacilitates as a process, and we should try and come up with a meaningful name.\nIt lets us map each element of a collection from one value into\nanother value. So maybe we should call it something like <code>map()<\/code>.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">function<\/span><span style=\"color: #B57EDC;\"> map<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">A<\/span><span>,<\/span><span style=\"color: #E5C07B;\"> B<\/span><span>&gt;(<\/span><span style=\"color: #C6CCD7;\">collection<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Array<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">A<\/span><span>&gt;,<\/span><span style=\"color: #B57EDC;\"> op<\/span><span style=\"color: #E06C75;\">:<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">A<\/span><span>)<\/span><span style=\"color: #61AFEF;\"> =&gt;<\/span><span style=\"color: #E5C07B;\"> B<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">    let<\/span><span style=\"color: #C6CCD7;\"> newCollection<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Array<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">B<\/span><span>&gt;<\/span><span style=\"color: #E06C75;\"> =<\/span><span> []<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    for<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">item<\/span><span style=\"color: #E06C75;\"> in<\/span><span style=\"color: #C6CCD7;\"> collection<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">\t    let<\/span><span style=\"color: #C6CCD7;\"> newItem<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #B57EDC;\"> op<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">item<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\t    newCollection<\/span><span>.<\/span><span style=\"color: #B57EDC;\">push<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">newItem<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span>    }<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\treturn<\/span><span style=\"color: #C6CCD7;\"> newCollection<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">map<\/span><span>([<\/span><span style=\"color: #56B6C2;\">2<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 4<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 7<\/span><span>,<\/span><span style=\"color: #56B6C2;\"> 13<\/span><span>], (<\/span><span style=\"color: #C6CCD7;\">num<\/span><span>)<\/span><span style=\"color: #61AFEF;\"> =&gt;<\/span><span style=\"color: #C6CCD7;\"> num<\/span><span style=\"color: #E06C75;\"> +<\/span><span style=\"color: #56B6C2;\"> 5<\/span><span>)<\/span><span style=\"color: #5F6672;font-style: italic;\"> \/\/ =&gt; [7, 9, 12, 18]<\/span><\/span><\/code><\/pre>\n<p>This is actually how the standard <code>map()<\/code> function is implemented.<\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>We still have the \"other\" concern that we talked about before. What is\ninteresting is that through this process that concern has also received its own\nabstraction, the <code>op<\/code> closure.<\/p>\n<p>So now we can see fully how Inversion of Control is a specific type of\nSeparation of Concerns, as the \"control\" concern and \"map item value\" concern\nhave been separated by abstractions. We can also see how the standard <code>map<\/code>,\n<code>reduce<\/code>, and <code>filter<\/code> functions are examples of IoC. But we have also seen\nwhat the process looks like for classifying and separating concerns looks like,\nwhich is a great skill that is applicable far beyond Inversion of Control.<\/p>\n"},{"title":"Making Code Reusable","published":"2023-01-18T00:00:00+00:00","updated":"2023-01-18T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/making-code-reusable\/"}},"id":"https:\/\/drewdeponte.com\/blog\/making-code-reusable\/","content":"<p>One thing that people don't seem to naturally understand is that to make code\nreusable you have to make it generic. To get a better understanding of this\nlets walk through some examples and see if we can get a better understanding of\nsome good ways to make code more generic.<\/p>\n<p>Let's say we have a class called <code>User<\/code> and user has a <code>firstName<\/code> and\n<code>lastName<\/code> properties. It might look something like the following.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">class<\/span><span style=\"color: #E5C07B;\"> User<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">  constructor<\/span><span>(<\/span><span style=\"color: #61AFEF;\">public<\/span><span style=\"color: #C6CCD7;\"> firstName<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> string<\/span><span>,<\/span><span style=\"color: #61AFEF;\"> public<\/span><span style=\"color: #C6CCD7;\"> lastName<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> string<\/span><span>) { }<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>Now we find out that there is a need to access the user's <code>fullName<\/code> within the\napplication.<\/p>\n<h2 id=\"computed-property-getter\">Computed Property\/Getter<\/h2>\n<p>Most people's natural instinct is to simply add a <code>fullName<\/code> getter to the\n<code>User<\/code> class as a computed property.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">class<\/span><span style=\"color: #E5C07B;\"> User<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">  constructor<\/span><span>(<\/span><span style=\"color: #61AFEF;\">public<\/span><span style=\"color: #C6CCD7;\"> firstName<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> string<\/span><span>,<\/span><span style=\"color: #61AFEF;\"> public<\/span><span style=\"color: #C6CCD7;\"> lastName<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> string<\/span><span>) { }<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">  get<\/span><span style=\"color: #B57EDC;\"> fullName<\/span><span>()<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> string<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    return this<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">firstName<\/span><span style=\"color: #E06C75;\"> +<\/span><span> &quot; &quot;<\/span><span style=\"color: #E06C75;\"> + this<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">lastName<\/span><span>;<\/span><\/span>\n<span class=\"giallo-l\"><span>  }<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>This is great! Our immediate need is met. It lets us get the full name of a\nuser as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> user<\/span><span style=\"color: #E06C75;\"> = new<\/span><span style=\"color: #B57EDC;\"> User<\/span><span>(&#39;<\/span><span style=\"color: #98C379;\">Bob<\/span><span>&#39;, &#39;<\/span><span style=\"color: #98C379;\">Villa<\/span><span>&#39;);<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> fullUserName<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #C6CCD7;\"> user<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">fullName<\/span><span>;<\/span><\/span><\/code><\/pre>\n<p>However, this code isn't reusable. You need to ask yourself, \"What does the\naction of combining a first name and last name conceptually have to do with a\nuser?\" The answer is, nothing other than that the user happens to have a first\nname and a last name.<\/p>\n<p>So how do we go about making this code more reusable? Well, step one is going\nback to the version of <code>User<\/code> that didn't have a <code>fullName<\/code> getter.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">class<\/span><span style=\"color: #E5C07B;\"> User<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">  constructor<\/span><span>(<\/span><span style=\"color: #61AFEF;\">public<\/span><span style=\"color: #C6CCD7;\"> firstName<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> string<\/span><span>,<\/span><span style=\"color: #61AFEF;\"> public<\/span><span style=\"color: #C6CCD7;\"> lastName<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> string<\/span><span>) { }<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre><h2 id=\"parameterized-function\">Parameterized Function<\/h2>\n<p>Let's try pulling this <code>fullName<\/code> computed property out into a parameterized\nfunction and see how that feels.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">function<\/span><span style=\"color: #B57EDC;\"> fullName<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">firstName<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> string<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> lastName<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> string<\/span><span>)<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> string<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  return<\/span><span style=\"color: #C6CCD7;\"> firstName<\/span><span style=\"color: #E06C75;\"> +<\/span><span> &quot; &quot;<\/span><span style=\"color: #E06C75;\"> +<\/span><span style=\"color: #C6CCD7;\"> lastName<\/span><span>;<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>Now we can get the full name as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> user<\/span><span style=\"color: #E06C75;\"> = new<\/span><span style=\"color: #B57EDC;\"> User<\/span><span>(&#39;<\/span><span style=\"color: #98C379;\">Alice<\/span><span>&#39;, &#39;<\/span><span style=\"color: #98C379;\">Jones<\/span><span>&#39;);<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> fullUserName<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #B57EDC;\"> fullName<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">user<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">firstName<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> user<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">lastName<\/span><span>);<\/span><\/span><\/code><\/pre>\n<p>Ok now that we have extracted this into a function that is parameterized based\non the <code>firstName<\/code> and <code>lastName<\/code> arguments it is able to combine any pair of\n<code>firstName<\/code> and <code>lastName<\/code> irrespective of where they came from. This is now\nreusable by definition.<\/p>\n<p>However, you may have noticed that the ergonomics is not quite as nice as it\nwas with the Getter\/Method approach. Let's see if we can improve the ergonomics\na bit.<\/p>\n<h2 id=\"function-parameterized-to-anonymous-interface\">Function Parameterized to Anonymous Interface<\/h2>\n<p>One of the complexities we have is that with it strictly parameterized to\n<code>firstName<\/code> and <code>lastName<\/code> we have to create a mapping between the\n<code>otherUser.firstName<\/code> and the <code>otherUser.lastName<\/code> by passing them as\nparameters. It would be nice if we could simply pass a <code>User<\/code> to the <code>fullName<\/code>\nfunction.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">function<\/span><span style=\"color: #B57EDC;\"> fullName<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">user<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> User<\/span><span>)<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> string<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  return<\/span><span style=\"color: #C6CCD7;\"> user<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">firstName<\/span><span style=\"color: #E06C75;\"> +<\/span><span> &quot; &quot;<\/span><span style=\"color: #E06C75;\"> +<\/span><span style=\"color: #C6CCD7;\"> user<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">lastName<\/span><span>;<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>This simply moves the knowledge of how to access the <code>firstName<\/code> and <code>lastName<\/code>\nof the <code>User<\/code> object from outside the function to the inside the\nfunction. This has a problem though. We just made the code no longer reusable,\nbecause it once again only works with objects of type <code>User<\/code>.<\/p>\n<p>To address these we can use TypeScript's anonymous interface typing to specify\nthat the function should accept any object that has both a property of\n<code>firstName<\/code> and a property of <code>lastName<\/code>. This looks as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">function<\/span><span style=\"color: #B57EDC;\"> fullName<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">named<\/span><span style=\"color: #E06C75;\">:<\/span><span> {<\/span><span style=\"color: #C6CCD7;\"> firstName<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> string<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> lastName<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> string<\/span><span> })<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> string<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  return<\/span><span style=\"color: #C6CCD7;\"> named<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">firstName<\/span><span style=\"color: #E06C75;\"> +<\/span><span> &quot; &quot;<\/span><span style=\"color: #E06C75;\"> +<\/span><span style=\"color: #C6CCD7;\"> named<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">lastName<\/span><span>;<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>Which allows us to now get the full name of the user as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> user<\/span><span style=\"color: #E06C75;\"> = new<\/span><span style=\"color: #B57EDC;\"> User<\/span><span>(&#39;<\/span><span style=\"color: #98C379;\">Bob<\/span><span>&#39;, &#39;<\/span><span style=\"color: #98C379;\">Villa<\/span><span>&#39;);<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> fullUserName<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #B57EDC;\"> fullName<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">user<\/span><span>);<\/span><\/span><\/code><\/pre>\n<p>Woot woot! We regained the generic and reusable nature that we had in the\nparameterized function but in the cases where we have an object that matches\nthe properties of <code>firstName<\/code> and <code>lastName<\/code> it streamlines the usage. In the\ncases where the properties don't match that we simply have to do a mapping\nsimilar to the parameterized function example.<\/p>\n<p>Another cool thing about this is that it is a function that takes a single\nargument which now plays much nicer in a functional programming world.<\/p>\n<p>One question people generally ask at this point is where do I put this\n<code>fullName<\/code> function though. If it isn't part of <code>User<\/code> it shouldn't be in the\nsame file as <code>User<\/code>. I would argue you are correct. It has nothing to do with\n<code>User<\/code>. This is where the concept of domain, a.k.a. problem space, a.k.a.\nconcern comes into play. Organizing code based on concern is a much more useful\nstrategy than by data as data is a byproduct of the concern. In this particular\ncase we have a concern of \"Name Composition\" so I would move it to a\nTypeScript module named <code>name_composition.ts<\/code> for the time being.<\/p>\n<p>You may have also noticed that we aren't 100% back to the ergonomics oh what we\nhad at the very beginning with the Getter\/Method approach. We can gain the full\nergonomics back by adding the following getter to the <code>User<\/code> class as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">class<\/span><span style=\"color: #E5C07B;\"> User<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">  constructor<\/span><span>(<\/span><span style=\"color: #61AFEF;\">public<\/span><span style=\"color: #C6CCD7;\"> firstName<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> string<\/span><span>,<\/span><span style=\"color: #61AFEF;\"> public<\/span><span style=\"color: #C6CCD7;\"> lastName<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> string<\/span><span>) { }<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">  get<\/span><span style=\"color: #B57EDC;\"> fullName<\/span><span>()<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> string<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    return<\/span><span style=\"color: #B57EDC;\"> fullName<\/span><span>(<\/span><span style=\"color: #E06C75;\">this<\/span><span>);<\/span><\/span>\n<span class=\"giallo-l\"><span>  }<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>This gives us the best of both worlds as it gives us the reusability of the\n<code>fullName()<\/code> function while also giving us the convenience of having the\noperation be discoverable via tooling on the <code>User<\/code> class. In the end giving us\nthe ability to get the <code>fullName<\/code> with either of the two following approaches.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> user<\/span><span style=\"color: #E06C75;\"> = new<\/span><span style=\"color: #B57EDC;\"> User<\/span><span>(&#39;<\/span><span style=\"color: #98C379;\">Bob<\/span><span>&#39;, &#39;<\/span><span style=\"color: #98C379;\">Villa<\/span><span>&#39;);<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> fullUserName<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #B57EDC;\"> fullName<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">user<\/span><span>);<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> fullUserName2<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #C6CCD7;\"> user<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">fullName<\/span><span>;<\/span><\/span><\/code><\/pre>\n<p>I personally prefer not having the getter on the <code>User<\/code> class as it is just\nanother layer of abstraction that isn't necessary. However, a lot of people\nvalue the discoverability of operations through tooling on the <code>User<\/code> class to\na high degree and think it is worth the cost.<\/p>\n<h2 id=\"function-parameterized-to-named-interface\">Function Parameterized to Named Interface<\/h2>\n<p>Another option we have is to parameterize the function based on a named\ninterface. For example, we could have an interfaced called <code>Named<\/code> which would\ndescribed our constraints of an object that has both a <code>firstName<\/code> and\n<code>lastName<\/code> property.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">interface<\/span><span style=\"color: #E5C07B;\"> Named<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">  firstName<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> string<\/span><span>;<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">  lastName<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> string<\/span><span>;<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>This would then allow us to write our function as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">function<\/span><span style=\"color: #B57EDC;\"> fullName<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">named<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Named<\/span><span>)<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> string<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  return<\/span><span style=\"color: #C6CCD7;\"> named<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">firstName<\/span><span style=\"color: #E06C75;\"> +<\/span><span> &quot; &quot;<\/span><span style=\"color: #E06C75;\"> +<\/span><span style=\"color: #C6CCD7;\"> named<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">lastName<\/span><span>;<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>This is definitely more readable and easier to conceptually understand that the\n<code>fullName()<\/code> function operates on conceptually any named object. However, it\ndoes add some more weight to the implementation. So I can see going back and\nforth on this. I think if there is more than one function that operates on the\nconcept of a <code>Named<\/code> object then it should definitely be formalized as a named\ninterface. If it is a weird one off concept and function maybe it is fine to\nuse the anonymous interface approach.<\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>If we look back at what we did at a high level it looks as follows.<\/p>\n<ul>\n<li>question the belonging of some code<\/li>\n<li>identify that it is <strong>not<\/strong> owned naturally by the class<\/li>\n<li>extract it to a parameterized function<\/li>\n<li>decide to parameterize with an interface (either anonymous\/named) or leave it multiple parameters<\/li>\n<\/ul>\n<p>This process is always the same. You just rinse and repeat the above steps over\nand over again as you are coding, and you will end up with a far more robust,\nmeaningful, and reusable code base.<\/p>\n"},{"title":"Uptech Studio Continuous Integration Standards","published":"2022-07-22T00:00:00+00:00","updated":"2022-07-22T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/uptech-continuous-integration\/"}},"id":"https:\/\/drewdeponte.com\/blog\/uptech-continuous-integration\/","content":"<p>We are experimenting with a new variation of our workflow based on the Continous Integration Methology. It is designed to facilitate feature level engineering reviews while still enabling us to follow the Continuous Integration Methodology and operate purely on the main trunk.<\/p>\n<h2 id=\"tl-dr\">TL;DR<\/h2>\n<h3 id=\"create-patch\">Create Patch<\/h3>\n<ul>\n<li>use <a rel=\"external\" href=\"https:\/\/git-ps.sh\">Git Patch Stack<\/a> and follow <a href=\"\/blog\/uptech-git-commit\/\">Git Commit Standards<\/a> as you normal would.<\/li>\n<\/ul>\n<h3 id=\"integrating-a-patches\">Integrating a Patches<\/h3>\n<ul>\n<li>integrate as soon as patch is complete and passes all automated checks verifying the requirements: <strong>buildable<\/strong>, <strong>testable<\/strong>, <strong>isolatable<\/strong>, <strong>releasble<\/strong> are met.<\/li>\n<li>request review of the patch &amp; <strong>immediately<\/strong> integrate the patch<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>gps rr &lt;patch-index&gt;<\/span><\/span>\n<span class=\"giallo-l\"><span>gps int &lt;patch-index&gt;<\/span><\/span><\/code><\/pre><\/li>\n<\/ul>\n<p><em>Note:<\/em> The <code>gps rr<\/code> is done solely to create notifications in GitHub as some people use those notifications to help with the \"Keeping up with the code base\".<\/p>\n<h3 id=\"keeping-up-with-the-code-base\">Keeping up with the code base<\/h3>\n<ul>\n<li><code>gps fetch<\/code> or <code>gps pull<\/code> show the upstream patches<\/li>\n<li>scan patches to identify any that are of immediate interest\n<ul>\n<li>related to what you are working on<\/li>\n<li>patch created by someone you are mentoring, etc.<\/li>\n<\/ul>\n<\/li>\n<li>review immediate interest patches by going to their GitHub Commit pages and commenting on the code with questions, suggestions, etc.<\/li>\n<li>track patches you have looked at with <a rel=\"external\" href=\"https:\/\/www.sublimemerge.com\">Sublime Merge<\/a> commit selection state &amp; auto refresh<\/li>\n<li>read the commits to keep up with understanding how the code base is changing. To comment, ask questions, or make suggestions open the GitHub Commit page for that commit.<\/li>\n<\/ul>\n<p><em>Note:<\/em> Some people use the PR notifications in GitHub to help them do this.\nIdeally we would have some better tooling around this. Exploration in this is still happening.<\/p>\n<h3 id=\"complete-feature\">Complete Feature<\/h3>\n<p>Once all your feature's patches are integrated:<\/p>\n<ul>\n<li>add a git ticket summary for each of the repositories involved as a comment, use <a rel=\"external\" href=\"https:\/\/github.com\/drewdeponte\/git-ticket-summary\">git-ticket-summary<\/a><\/li>\n<li>move the user story you have been working to the \"Needs Review\" column.<\/li>\n<li>move onto your next task until someone reviews your feature<\/li>\n<\/ul>\n<h3 id=\"feature-review\">Feature Review<\/h3>\n<p>You sould try and review a feature everytime you put a story in \"Needs Review\".<\/p>\n<ul>\n<li>pick a story in the \"Needs Review\" column that you want to review<\/li>\n<li>drag the story you are going to review to \"In Review\"<\/li>\n<li><strong>review the feature's functionality<\/strong> in the <strong>dev<\/strong> environment to gain context and make sure it is meeting all the acceptance criteria<\/li>\n<li><strong>review the application architecture<\/strong> involved (how this feature works through the application architecture) while noting any logic\/algorithms that need further focus<\/li>\n<li><strong>review logic\/algorithm design<\/strong> in any areas of possible concern identified above<\/li>\n<li>provide your feedback on the feature's application architecture &amp; algorithms\/logic in the ticket<\/li>\n<li>if helpful you can comment on code in the commits, but make sure you still reference it in your comments in the ticket<\/li>\n<li>if there are changes that need to be made move the ticket back to \"In Progress\" for the developer to address the concerns<\/li>\n<li>rinse and repeat<\/li>\n<li>once the story is good move the ticket to \"Ready for Release\"<\/li>\n<\/ul>\n<h2 id=\"references\">References<\/h2>\n<h3 id=\"github-commit-pages\">GitHub Commit Pages<\/h3>\n<p>If you aren't familiar with GitHub's Commit pages. You can access them with the following url structure.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>https:\/\/github.com\/uptech\/git-ps-rs\/commit\/da5944f6157ff29cedaf8972e7f82f0d4e7a3a8c<\/span><\/span><\/code><\/pre>\n<p>The SHA at the end of the URL, <code>da5944f6157ff29cedaf8972e7f82f0d4e7a3a8c<\/code>, can be replaced with an SHA or short SHA of a commit get the URL for that commits GitHub Commit page.<\/p>\n<p>This is useful because GitHub allows you to inline comment on the within the commit similar to how you can within Pull Requests.<\/p>\n<p><em>Note:<\/em> Some terminals support detecting and linking to the GitHub Commit Pages from the SHAs or short SHAs in the terminal buffers, e.g. Visual Studio Code, <a rel=\"external\" href=\"https:\/\/github.com\/alacritty\/alacritty\">Alacritty<\/a>.<\/p>\n<h3 id=\"sublime-merge\">Sublime Merge<\/h3>\n<p>If you aren't familiar with <a rel=\"external\" href=\"https:\/\/www.sublimemerge.com\">Sublime Merge<\/a> it is a very nice cross-platform Git GUI that detects changes to the Git repo and auto refreshes state. This makes it extremely nice as you can be working in a terminal or another app and make changes and they automatically update and reflect in <a rel=\"external\" href=\"https:\/\/www.sublimemerge.com\">Sublime Merge<\/a>.<\/p>\n<p>In the context of this process I keep it up with the repository opened and as I <code>gps pull<\/code> I can quickly and easily see the new patches in <a rel=\"external\" href=\"https:\/\/www.sublimemerge.com\">Sublime Merge<\/a> and checkout the code right there. If I find something I need to comment on I simply open that commit's GitHub Commit Page and start a discussion about the code.<\/p>\n"},{"title":"Journey to Small Pull Requests","published":"2022-03-15T00:00:00+00:00","updated":"2022-03-15T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/journey-to-small-pull-requests\/"}},"id":"https:\/\/drewdeponte.com\/blog\/journey-to-small-pull-requests\/","content":"<p>We were struggling to deal with our pull requests. We weren't getting quality peer reviews from our peers, we were having a hard time reverting commits when necessary, and had a hard time understanding our <a rel=\"external\" href=\"https:\/\/git-scm.com\">Git<\/a> history.<\/p>\n<p>So we decided to dig into this and see if we could get to the core of it and possibly come up with a solution.<\/p>\n<p>The first thing was just understanding all the of the issues we were running into and coming to the conclusion that they are all a side effect of <strong>big<\/strong> pull requests.<\/p>\n<p><strong>Big<\/strong> pull requests<\/p>\n<ul>\n<li>effectively <strong>eliminate the value of peer review<\/strong> as the changesets are just too overwhelming for people to focus on.<\/li>\n<li>silo changes from the team <strong>delaying feedback<\/strong><\/li>\n<li>silo changes from the team <strong>postponing integration<\/strong><\/li>\n<li>make <strong>conflict resolution more difficult<\/strong><\/li>\n<li>encourage <strong>poor git history &amp; practices<\/strong><\/li>\n<li>make it <strong>hard to revert changes<\/strong><\/li>\n<li>result in a <strong>complicated git history<\/strong> making it hard to look back and understand how best to interact with or even understand changes<\/li>\n<\/ul>\n<h2 id=\"why-are-they-so-big\">Why are they so big?<\/h2>\n<p>After understanding the issues were coming from <strong>big<\/strong> pull requests we decided we should try and understand why our pull requests were so <strong>big<\/strong> and see if there was a good reason for it.<\/p>\n<p>We were following the <a rel=\"external\" href=\"https:\/\/docs.github.com\/en\/get-started\/quickstart\/github-flow\">GitHub Flow<\/a> and using feature branches, which in turn became our pull requests. Containing all the necessary changes for an entire feature turned out to be the primary reason that our pull requests were <strong>big<\/strong>. Not only that, we noticed that developers weren't caring about the individual commits at all and would have partial or broken commits in their branches or even arbitrary save point commits. This resulted in making reverting commits more difficult as well as breaking useful features of git like <code>git bisect<\/code>.  Beyond that it lended to the issues we had identified before.<\/p>\n<h2 id=\"alternatives\">Alternatives<\/h2>\n<p>So now we knew that if we wanted our pull requests to be <strong>small<\/strong> we couldn't use feature branches. Instead we would have to scope our changes by a smaller concept than a feature. To help us identify which scoping concepts made the most sense we researched many other development workflows. The two that stood out as being different were the Continous Integration Methodology and Trunk Based Development, as the others all seem to use feature branches.<\/p>\n<p>Both the Continous Integration Methodology and Trunk Based Development are focused around getting small, logical, buildable, testable, not-necessarily complete changes into mainline as quickly as possible. They both see this as a core principle to support other developers on the team being aware of the work being done and enabling contribution and planning around it.<\/p>\n<p>Digging further into how people follow these methodologies and scope their changes. It seems to range from a single small addition of an architectural concept (e.g. add method to class) to the addition of a small\/initial architectural concept (e.g. adding a small class).<\/p>\n<p>One other difference is that people following these methodologies integrate code prior to it being utilized by other components. For example they would write a class and integrate that class prior to that class being used by whatever layers or features above it.<\/p>\n<p>Beyond that they even go as far as using Feature Toggles to facilitate including code in the code base but preventing it from being executed. This allows all of the code to be integrated and shared with the development team as quickly and early as possible while still preventing consumers from seeing incomplete features.<\/p>\n<h2 id=\"what-about-peer-review\">What about Peer Review?<\/h2>\n<p>One of our driving forces for looking at alternatives was to eliminate our problems with our peer reviews. Turns out that Continuous Integration &amp; Trunk Based Development don't provide much guidance on that front.<\/p>\n<p>Many Continous Integration &amp; Trunk Based Development teams forego what we think of as a peer review process coming from feature branches. Instead they opt for getting the small, logical, buildable, testable, but not-necessarily complete changes into mainline as quickly as possible and then in theory go back and review each of the commits after the fact. This is known as <strong>Post-commit<\/strong> review. What we classically think of as peer review with feature branches is known as <strong>Pre-commit<\/strong> review. The difference is simply <strong>Post-commit<\/strong> happens after a commit is integrated into mainline while the <strong>Pre-commit<\/strong> happens prior to a change being integrated into mainline.<\/p>\n<p>So we then started exploring the tools and processes around <strong>Post-commit<\/strong> peer review. Turns out that there isn't much there and the few tools that do exist are relatively antiquated and also require a full shift in terms of tooling and mentality. This was effectively a no-go for us as we work with many different clients and dev teams which all use <a rel=\"external\" href=\"https:\/\/github.com\">GitHub<\/a>\/<a rel=\"external\" href=\"https:\/\/bitbucket.org\">Bitbucket<\/a> pull requests for peer review. So we needed to figure out a way to do <strong>small pull requests<\/strong> for <strong>Pre-commit<\/strong> peer reviews.<\/p>\n<h2 id=\"branches-on-branches-on-branches\">Branches on, Branches, on Branches<\/h2>\n<p>Following our only path forward for peer review, <strong>Pre-commit<\/strong>, we started making <strong>small pull requests<\/strong> scoped down just as people do in Continous Integration and Trunk Based Development. This was great as we were seeing the benefits of <strong>small pull requests<\/strong> and see that it definitely does address the issues we identified at the beginning. However there was a ton of extra overhead and complexities in terms of managing these branches locally.<\/p>\n<p>Generally we focus on implementing a specific feature\/change. In order to do this we build up context around it in our mind so that we can plan and implement an appropriate change to achieve our goal. This is great when doing feature branches because there is a 1-to-1 mapping between that context and the branch. However, remember we are creating a number of pull requests that are small, logical, buildable, testable, and not-necessarily complete changes that when combined should accomplish the end goal of the feature\/change. Continous Integration and Trunk Based Development teams sometimes refer to these smaller changes as Proof of Work. Just like when you were a kid in school and the teacher made you show your work on your math homework. Each of the small changes should be logical steps showing how you achieved your end goal. You might think it is natural to just create a branch for each of these logical changes. However, what you quickly find is that the logical changes begin to have dependencies on other logical changes you have made.<\/p>\n<p>This means for you to continue being able to develop the feature you effectively have to create your next branch on top of the previous branch and so on. You then end up with effectively a chain of branches composing up your feature. The only one you can actually open a pull request for is the inner most branch. This is because if you opened a pull request for any of the other branches it would also include the changes beneath it as well. Putting us back in the camp of <strong>big pull requests<\/strong> which we don't want to be in.<\/p>\n<p>So we continued our experiment and dealing with the branch management overhead by accepting that it is ok that the inner most branch is the only one you can open a pull request for until it is integrated into mainline. However we quickly ran into the next issue. What happens when someone provides feedback on the pull request of the inner most branch and you have to make changes to it. Well, you have to make changes to it and then effectively go rebase each of the branches above it ontop of eachother one by one all of over again. This made the branch management overhead just too much to deal with.<\/p>\n<h2 id=\"eliminate-branch-management-overhead\">Eliminate Branch Management Overhead<\/h2>\n<p>So for <strong>small pull requests<\/strong> to be viable we determined we needed to find some way to eliminate or at least streamline all this branch management overhead. We pondered and struggled with this over time largely feeling like maybe it wasn't possible. However while researching development workflows of various teams we came across an old idea, the <strong>patch<\/strong>. Patches were how changes were managed and shared back in the day with many open source projects. A diff\/patch would be created and emailed to the maintainer of the project. They would then review the patch and either apply it or reply back to the email requesting changes.<\/p>\n<p>Teams that use patches still generally require that they are small, logical, buildable, testable, and most importantly <strong>independent<\/strong>. <strong>Independent<\/strong> doesn't mean that one patch can't depend on another patch. But it means that if the patch doesn't have to depend on another patch it shouldn't. This is generally addressed by the need to be <strong>logical<\/strong>, but having the additional explicit requirement of <strong>independence<\/strong> is worth thinking about. In the cases where a patch must have a dependency on another patch it is simply handled by communicating the required order in which those particular patches must be integrated.<\/p>\n<p>Another interesting thing that maintainers do is locally manage a stack of patches they have recieved from contributors as they prepare changes to go into mainline.<\/p>\n<p>This got our brains rolling again and got us thinking maybe if we added this requirement of <strong>independence<\/strong> and we start conceptually thinking about our commits on <code>main<\/code> as a stack of patches resting on <code>origin\/main<\/code>. It would eliminate the branch overhead all together. Not only that, if we used the <code>pull.rebase = true<\/code> <a rel=\"external\" href=\"https:\/\/git-scm.com\">Git<\/a> feature it would force us to integrate our local stack of patches every time we pull. This would get us even closer to Continuous Integration and Trunk Based Development.<\/p>\n<p>So now we had all the branch overhead gone and we are closer to Continuous Integration and Trunk Based Development than we have ever been before. But we needed to be able to do the equivalent of sending a patch to a maintainer for review but using <strong>Pre-commit<\/strong> review via pull requests.<\/p>\n<p>Given that we are targeting <strong>independence<\/strong> and thinking about these commits as patches. It seemed natural when we wanted to request review of a patch to simply create a branch and cherry-pick the particular patch into that branch so that we could make a pull request from it. This worked and was far less overhead than all the branch management in the other path. Not only that it then allows <a rel=\"external\" href=\"https:\/\/git-scm.com\">Git<\/a> to help push us to <strong>independence<\/strong> by calling us out when we request review of a patch and it is <strong>not<\/strong> independent.<\/p>\n<h2 id=\"too-many-pull-requests\">Too Many Pull Requests?<\/h2>\n<p>One concern we had initially with this workflow was how the increase in the number of pull requests would impact things both on the producing pull requests side as well as the reviewing pull reqeusts side.<\/p>\n<p>On the producing pull requests side we were pleasently surprised that we actually eliminated overhead. Before we used to have to write a pull request summary and description to give context about what the pull request is for and what it does in addition to all the commit messages. This went away because when you have a single commit branch for a pull request it grabs the commit summary and message as the pull request summary and description.<\/p>\n<p>You could argue that there must be additional overhead in terms of the context that must be included in the commit messages. For us there wasn't as our <a href=\"\/docs\/standards\/git-commit\">Git Commit Standards<\/a> already required us to write good commit messages that include the necessary context. If your team is coming from a place of not having good commit messages there will likely be a bit of overhead in terms of commit message writing. It is well worth it though and will pay dividends not only in supporting this workflow but also in supporting <a rel=\"external\" href=\"https:\/\/git-scm.com\">Git<\/a> history spelunking.<\/p>\n<p>We were also pleasently suprised when we realized that we stopped caring about pull requests being reviewed immediately. It turns out that this is a wonderful side effect of having your patches in a stack. You can continue working on the feature without any branch overhead irrespective of it takes 30 mins or 2 days to get a review.<\/p>\n<p>On the reviewing pull requests side of things the act of reviewing pull requests becomes a lot easier as everything you are reviewing is logically scoped, buildable, testable, etc. It allows you to really focus in on the change and how it plays out with the application architecture. It also makes it easier to allow your self to do some deep focus work as your team doesn't have the incessant need to ping you about their pull request to get it reviewed instantly.<\/p>\n<p>There are definitely more pull requests. But when you come up for air from your deep focus you just tackle reviewing a few pull requests instead of one giant one. We have found that the organization and separation of these pull requests has only increased the speed and quality of our peer reviews. It is probably also worth mentioning that we use a product of ours, <a rel=\"external\" href=\"https:\/\/pullwalla.com\">Pullwalla<\/a>, to identify which pull requests need review. This also helps us stay in deep focus.<\/p>\n<h2 id=\"outside-in-inside-out\">Outside-In &amp; Inside-Out<\/h2>\n<p>In addition we were unsure exactly how things would pan out in situations where we have to go down a path to figure out how to implement something. Turns out that following best practices and doing outside-in development works great with this workflow. The key addition is that once we get to the inner most dependency we work our way inside-out requesting review of the patches.<\/p>\n<p>Basically what ends up happening is that you create a bunch of logical WIP commits as you work from the outside-in. You do this until you get to the the inner most dependency (C) that has all the dependencies it needs. That patch (C) of the inner most dependency is what is fully baked and ready to have a pull request open. You are confident that your changes in C met the needs of B because you just walked through from the outside-in understanding the needs before you implemented C.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>A - WIP: Add ability for user to Login<\/span><\/span>\n<span class=\"giallo-l\"><span>B - WIP: Add login ability to authentication service<\/span><\/span>\n<span class=\"giallo-l\"><span>C - Add login() function to core underlying authentication library<\/span><\/span><\/code><\/pre>\n<p>Then you step back up to then next level above that (B) and you refine that patch updating it to use the stuff provided by C and make it no longer a WIP commit.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>A - WIP: Add ability for user to Login<\/span><\/span>\n<span class=\"giallo-l\"><span>B - Add login ability to authentication service<\/span><\/span>\n<span class=\"giallo-l\"><span>C - Add login() function to core underlying authentication library<\/span><\/span><\/code><\/pre>\n<p>At this point you open a pull request for B. Then you start working on refining patch A, using the bits that you implemented in B and get it to a good place.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>A - Add ability for user to Login<\/span><\/span>\n<span class=\"giallo-l\"><span>B - Add login ability to authentication service<\/span><\/span>\n<span class=\"giallo-l\"><span>C - Add login() function to core underlying authentication library<\/span><\/span><\/code><\/pre>\n<p>At this point you have a patch that technically implements the feature from a user perspective &amp; adds the associated e2e tests. This is still a small, logical, buildable, testable, patch that you open a pull request for. The peer reviewer understand that this patch adds the feature and therefore when they review this pull request they do the full feature review as well as code review. Generally these inner patches are paired with unit tests while the outer most feature patch is paired with e2e tests.<\/p>\n<p>There are many ways this can vary. For example with someone not doing outside-in development or maybe you have multiple internal dependency patches that aren\u2019t intertwined. I think the important thing is just that the pull requests are opened from the inside-out.<\/p>\n<p>Also one of the beautiful things about this workflow is that it decouples you from caring about how quickly your pull requests get reviewed because you are able to still continue development of your feature irrespective of how long it takes someone to review your pull requests. This happens thanks to the fact that the patch stack continues to hold the patches even after requesting review.<\/p>\n<p>Further details on this process of outside-in and inside-out in relation to this workflow are covered in our article <a href=\"\/blog\/how-we-should-be-using-git\/\">How we should be using Git<\/a>.<\/p>\n<h2 id=\"ticketing-alignment\">Ticketing Alignment<\/h2>\n<p>When we were doing feature branches before the ticketing alignment was trivial. We basically had a 1-to-1 mapping between a user story and feature pull request. With the Git Patch Stack methodology we had to make a choice. Do we have multiple patches reference a single user story or do we modify how we do ticketing so that it better aligns with the logical patches.<\/p>\n<p>After thinking about it briefly we decided that it makes the most sense to just have multiple patches reference the same user story. Each of the patches could also reference any related sub-tasks that belong to the parenting user story. The key for this to be successful is really just providing the necessary context in the patch commit messages.<\/p>\n<p>The alternate path of changing how we define and structure tickets seemed like a bad idea as it would have required not just engineering to change but also the product and project managers.<\/p>\n<h2 id=\"git-patch-stack\">Git Patch Stack<\/h2>\n<p>After using this workflow and seeing and feeling the value that <strong>small pull requests<\/strong> provide. We decided we could relatively easily streamline the little bit of branch overhead involved with this workflow and further enforce the conceptual thinking around patches by creating a simple <a rel=\"external\" href=\"https:\/\/git-scm.com\">Git<\/a> extension.<\/p>\n<p>So we created <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/git-ps-rs\">Git Patch Stack<\/a> and it has only further solidified the workflow, the mentality around it, and the vast benefits that fall out of <strong>small pull requests<\/strong> and this workflow in general.<\/p>\n<p>For a deeper dive on <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/git-ps-rs\">Git Patch Stack<\/a>, this workflow and concepts to help identify these smaller logical chunks of work, see our post on <a href=\"\/blog\/how-we-should-be-using-git\/\">How we should be using Git<\/a>.<\/p>\n"},{"title":"Uptech Studio Pull Request Standards","published":"2022-03-01T00:00:00+00:00","updated":"2022-03-01T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/uptech-pull-request\/"}},"id":"https:\/\/drewdeponte.com\/blog\/uptech-pull-request\/","content":"<p>We <strong>require<\/strong> that Pull Requests have the following characteristics.<\/p>\n<ul>\n<li>small<\/li>\n<li>logical<\/li>\n<li>buildable<\/li>\n<li>testable<\/li>\n<li>releasable<\/li>\n<\/ul>\n<h3 id=\"small\">Small<\/h3>\n<p>It is crucial that pull requests are <strong>small<\/strong>. The quality of peer review exponentially decreases as the size of the pull request increases. If you are going to have PRs in the hundreds or thousands of lines of change range you may as well just not do peer review. Here at <a rel=\"external\" href=\"https:\/\/uptechstudio.com\">Uptech Studio<\/a> we believe in producing the highest quality code that we can and therefore we <strong>must<\/strong> have <strong>small<\/strong> pull requests.<\/p>\n<p>If you are interested in how we got to small pull requests, checkout our post <a href=\"\/blog\/journey-to-small-pull-requests\">Journey to Small Pull Requests<\/a>.<\/p>\n<h3 id=\"logical\">Logical<\/h3>\n<p>It is also important that pull requests are <strong>logical<\/strong>. This means that the bounds of the content are well ordered and have clear &amp; sound reasoning.<\/p>\n<p>You might say well a <em>feature<\/em> is <strong>logical<\/strong>. Well, you would be correct. However, having a pull request contain an entire feature is in contradiction with the requirement of a pull request being small. Therefore we need to find some other logical bounding for the change. Generally application architecture concepts are a great aid for this. For example you could have a pull request that adds a method to a service to allow consumers to get a collection of items which would be logically bound around the change to that service. It could also be that you are making a cross-cutting change that is still logical. For example you could have a pull lequest that changes code from using strings to symbols which is spread across multiple application architecture concepts but is still <strong>logical<\/strong>.<\/p>\n<p>Having a pull request be logical is crucial not only for understanding the evolution of the code base over time but is crucial to support functionality in <a rel=\"external\" href=\"https:\/\/git-scm.com\">Git<\/a> like <code>bisect<\/code> and <code>revert<\/code>.<\/p>\n<h2 id=\"buildable\">Buildable<\/h2>\n<p>Pull requests also must be <strong>buildable<\/strong>. Meaning that the state of the code in the pull request has to be in such a place that the library\/framework\/application\/suite can be built without error. This is crucial again to support functionality in <a rel=\"external\" href=\"https:\/\/git-scm.com\">Git<\/a> like <code>bisect<\/code> and <code>revert<\/code> but is also import to not impede the development process of the team.<\/p>\n<h2 id=\"testable\">Testable<\/h2>\n<p>A pull request being <strong>testable<\/strong> means that the change has automated tests covering it and that the test suite is able to be run with those tests included. In cases where code isn't able to be tested with automated tests, maybe some UI change, then the changes have to be <strong>manually testable<\/strong> and the pull request description should include enough information to educate someone on how to manually test it.<\/p>\n<p>This is crucial, especially when making smaller pull requests because it doesn't make sense to pull down a pull request and manually test changes to an internal application architecture concept. So instead of getting our confidence from manual testing we get it through making sure that we have well written automated test coverage and that they are passing.<\/p>\n<h2 id=\"releasable\">Releasable<\/h2>\n<p>We also require a pull request to be <strong>releasable<\/strong>. This doesn't mean that you would idealisticly want to cut a release with just this one pull request. But it means that if push came to shove and you needed to for some reason the application\/library would be in such a state where it could be released.<\/p>\n<p>This generally is a pretty easy requirement to meet as you can have code in your code base that is <strong>buildable<\/strong> &amp; <strong>testable<\/strong> that isn't used, and that is totally fine. The complexity with this requirement comes when you are dealing with changes that are visible to the consumer of the library\/application. That is where you have to then ask yourself if I make this change to the UI if push came to shove would this change be <strong>releasable<\/strong>?<\/p>\n<p>Remember it isn't that if it was released it would be the ideal release. It is that it would be possible to release it.<\/p>\n"},{"title":"Backend Web Framework Design Exploration - Part 3","published":"2022-01-05T00:00:00+00:00","updated":"2022-01-05T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/be-wf-design-exploration-part-003\/"}},"id":"https:\/\/drewdeponte.com\/blog\/be-wf-design-exploration-part-003\/","content":"<p>This is the third in a series of articles I am writing in which I do a Design\nExploration of a Backend Web Framework with the following constraints.<\/p>\n<ul>\n<li>only use functions &amp; data - no classes w\/ methods<\/li>\n<li>use strong typing (via TypeScript) - no use of <code>any<\/code><\/li>\n<li>trivially testable via basic unit style state based tests<\/li>\n<\/ul>\n<p>If you didn't start at the beginning I highly recommend it,\n<a href=\"\/blog\/be-wf-design-exploration-part-001\/\">Backend Web Framework Design Exploration - Part 1<\/a>.<\/p>\n<p><em>Note:<\/em> Within this article I will include simplified code examples. If however\nyou are interested in the real code &amp; tests I used to do this design\nexploration you can see them via the commits at my\n<a rel=\"external\" href=\"https:\/\/github.com\/drewdeponte\/be-wf-design-exploration\">be-wf-design-exploration<\/a>\nGit repository.<\/p>\n<h2 id=\"dependencies-constructed-per-request\">Dependencies Constructed per Request<\/h2>\n<p>Picking up where I left off in the previous article. I need to figure out how\nthis web framework should support Dependencies that need to be constructed\nfresh per Request. This is something that most web frameworks support via their\ndependency injection frameworks. Of course I have to manage this while\nfollowing the 3 key constraints above.<\/p>\n<p>Previously I had the <code>fooHandler<\/code> with a dependency of <code>dbPool<\/code>.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">export<\/span><span style=\"color: #61AFEF;\"> function<\/span><span style=\"color: #B57EDC;\"> fooHandler<\/span><span>(<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">  dbPool<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> DatabasePool<\/span><\/span>\n<span class=\"giallo-l\"><span>)<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> wf<\/span><span>.<\/span><span style=\"color: #E5C07B;\">RouteHandler<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  return<\/span><span style=\"color: #61AFEF;\"> function<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">request<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> wf<\/span><span>.<\/span><span style=\"color: #E5C07B;\">Request<\/span><span>)<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> wf<\/span><span>.<\/span><span style=\"color: #E5C07B;\">Response<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    return<\/span><span> { status:<\/span><span style=\"color: #56B6C2;\"> 200<\/span><span>, body:<\/span><span style=\"color: #C6CCD7;\"> dbPool<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">id<\/span><span> };<\/span><\/span>\n<span class=\"giallo-l\"><span>  };<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>I am going to expand on this example with the <code>DatabasePool<\/code> by saying that our\n<code>fooHandler<\/code> also needs another database pool. However, this one needed to be\nconstructed fresh with each request.<\/p>\n<h3 id=\"dependency-injection-frameworks\">Dependency Injection Frameworks<\/h3>\n<p>Looking at all the dependency injection frameworks I have worked with. They are\neffectively a set of functions that are registered often by type or name. Then\nat some later point in time you call a dependency injection framework function\nto instantiate an instance by either a type or name.  Often times the\nregistered function is executed only the first time and then memoized as a\nsingleton.  Effectively making them global singletons.<\/p>\n<h3 id=\"functions-as-parameters\">Functions as Parameters<\/h3>\n<p>Thinking about dependency injection frameworks and the fact that I need\nsomething that can at some later point in time give me back in instance of this\nother <code>DatabasePool<\/code>. It sounds very much to me like a function that is simply\nresponsible for knowing how to construct this other <code>DatabasePool<\/code> is the key.\nIn fact it can just be an explicit dependency of the <code>fooHandler<\/code>.<\/p>\n<p>This aligns at a high level with how common Dependency Injection frameworks\nwork to some extent while also abiding by the constraints. Specifically the\nexplicit dependency declaration constaint. Which in turn supports the trivial\ntesting requirement.<\/p>\n<p>Applying this concept to a handler might look something like this.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">export<\/span><span style=\"color: #61AFEF;\"> function<\/span><span style=\"color: #B57EDC;\"> fooHandler<\/span><span>(<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">  dbPool<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> DatabasePool<\/span><span>,<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">  getOtherDbPool<\/span><span style=\"color: #E06C75;\">:<\/span><span> ()<\/span><span style=\"color: #61AFEF;\"> =&gt;<\/span><span style=\"color: #E5C07B;\"> DatabasePool<\/span><\/span>\n<span class=\"giallo-l\"><span>)<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> wf<\/span><span>.<\/span><span style=\"color: #E5C07B;\">RouteHandler<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  return<\/span><span style=\"color: #61AFEF;\"> function<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">request<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> wf<\/span><span>.<\/span><span style=\"color: #E5C07B;\">Request<\/span><span>)<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> wf<\/span><span>.<\/span><span style=\"color: #E5C07B;\">Response<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">    const<\/span><span style=\"color: #C6CCD7;\"> otherDbPool<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #B57EDC;\"> getOtherDbPool<\/span><span>();<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    return<\/span><span> { status:<\/span><span style=\"color: #56B6C2;\"> 200<\/span><span>, body: `${<\/span><span style=\"color: #C6CCD7;\">dbPool<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">id<\/span><span>}<\/span><span style=\"color: #98C379;\"> &amp; <\/span><span>${<\/span><span style=\"color: #C6CCD7;\">otherDbPool<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">id<\/span><span>}` };<\/span><\/span>\n<span class=\"giallo-l\"><span>  };<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>With the usage in the routes builder looking as follows:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">export<\/span><span style=\"color: #61AFEF;\"> function<\/span><span style=\"color: #B57EDC;\"> buildRoutes<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">dbPool<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> DatabasePool<\/span><span>,<\/span><span style=\"color: #B57EDC;\"> getOtherDbPool<\/span><span style=\"color: #E06C75;\">:<\/span><span> ()<\/span><span style=\"color: #61AFEF;\"> =&gt;<\/span><span style=\"color: #E5C07B;\"> DatabasePool<\/span><span>)<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> wf<\/span><span>.<\/span><span style=\"color: #E5C07B;\">Routes<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  return<\/span><span> [<\/span><\/span>\n<span class=\"giallo-l\"><span>    { pattern: &quot;<\/span><span style=\"color: #98C379;\">\/foo<\/span><span>&quot;, handler:<\/span><span style=\"color: #B57EDC;\"> fooHandler<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">dbPool<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> getOtherDbPool<\/span><span>) },<\/span><\/span>\n<span class=\"giallo-l\"><span>    { pattern: &quot;<\/span><span style=\"color: #98C379;\">\/bar<\/span><span>&quot;, handler:<\/span><span style=\"color: #B57EDC;\"> barHandler<\/span><span>() },<\/span><\/span>\n<span class=\"giallo-l\"><span>    { pattern: &quot;<\/span><span style=\"color: #98C379;\">\/health<\/span><span>&quot;, handler:<\/span><span style=\"color: #B57EDC;\"> healthHandler<\/span><span>() },<\/span><\/span>\n<span class=\"giallo-l\"><span>  ];<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre><h3 id=\"maybe-use-environment\">Maybe use Environment<\/h3>\n<p>Some frameworks I have worked with use a concept known as Environments for\ndependency injection. Basically, it is just a formalized Type that you\nexplicitly pass in as a parameter to your function that houses all your\ndependency injection dependencies.<\/p>\n<p>The environment for <code>fooHandler<\/code> might look something like the following.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">class<\/span><span style=\"color: #E5C07B;\"> FooEnvironment<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\tdbPool<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> DatabasePool<\/span><span>;<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\tgetOtherDbPool<\/span><span style=\"color: #E06C75;\">:<\/span><span> ()<\/span><span style=\"color: #61AFEF;\"> =&gt;<\/span><span style=\"color: #E5C07B;\"> DatabasePool<\/span><span>;<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>Then I could use it in place of the parameters of <code>fooHandler<\/code>.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">export<\/span><span style=\"color: #61AFEF;\"> function<\/span><span style=\"color: #B57EDC;\"> fooHandler<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">environment<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> FooEnvironment<\/span><span>)<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> wf<\/span><span>.<\/span><span style=\"color: #E5C07B;\">RouteHandler<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  return<\/span><span style=\"color: #61AFEF;\"> function<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">request<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> wf<\/span><span>.<\/span><span style=\"color: #E5C07B;\">Request<\/span><span>)<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> wf<\/span><span>.<\/span><span style=\"color: #E5C07B;\">Response<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">    const<\/span><span style=\"color: #C6CCD7;\"> otherDbPool<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #C6CCD7;\"> environment<\/span><span>.<\/span><span style=\"color: #B57EDC;\">getOtherDbPool<\/span><span>();<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    return<\/span><span> { status:<\/span><span style=\"color: #56B6C2;\"> 200<\/span><span>, body: `${<\/span><span style=\"color: #C6CCD7;\">environment<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">dbPool<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">id<\/span><span>}<\/span><span style=\"color: #98C379;\"> &amp; <\/span><span>${<\/span><span style=\"color: #C6CCD7;\">otherDbPool<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">id<\/span><span>}` };<\/span><\/span>\n<span class=\"giallo-l\"><span>  };<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>Looking at this I think the real value is that it facilitates grouping of\ndependencies together. I think this would be valuable for grouping dependencies\nmaybe at a module level. I guess the only other value I would see from this on\na handler basis is just to provide a more defined convention for how a user\nwould want get dependencies into the handler. However, I could also just\nprovide examples probably without the need for this.<\/p>\n<h2 id=\"review-next-up\">Review &amp; Next Up<\/h2>\n<p>Looking at all of the above. The passing a function as an explicit dependency\nto get get an instance of a something per request seems like the most direct\nsolution. It also meets all the constraints.<\/p>\n<p>The Environment concept might make more sense if later on I decided that\nrequiring some modularization makes sense and therefore grouping dependencies\nby module makes sense. But until then I think starting with the function\nparamater technique makes the most sense.<\/p>\n<p>Both of these techniques enable more precision in terms of instance creation\nthan being bound to a Request. This is because the handler can call the\nfunction multiple times.<\/p>\n<p>It is worth recognizing that it is really the <strong>builder function<\/strong> technique\nthat is still facilitating all of this. It is just that being able to pass a\nfunction as a parameter combined with that approach facilitates this specific\nuse case.<\/p>\n<p>This concept of <strong>builder function<\/strong> is used generally to implement the concept\nof <a rel=\"external\" href=\"https:\/\/en.wikipedia.org\/wiki\/Currying\">currying<\/a> in function programming.<\/p>\n<p>Next up is figuring out midddleware &amp; guards and how they might work in this\nframework.<\/p>\n"},{"title":"Backend Web Framework Design Exploration - Part 2","published":"2022-01-03T00:00:00+00:00","updated":"2022-01-03T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/be-wf-design-exploration-part-002\/"}},"id":"https:\/\/drewdeponte.com\/blog\/be-wf-design-exploration-part-002\/","content":"<p>This is the second in a series of articles I am writing in which I do a Design\nExploration of a Backend Web Framework with the following constraints.<\/p>\n<ul>\n<li>only use functions &amp; data - no classes w\/ methods<\/li>\n<li>use strong typing (via TypeScript) - no use of <code>any<\/code><\/li>\n<li>trivially testable via basic unit style state based tests<\/li>\n<\/ul>\n<p>If you didn't start at the beginning I highly recommend it,\n<a href=\"\/blog\/be-wf-design-exploration-part-001\/\">Backend Web Framework Design Exploration - Part 1<\/a>.<\/p>\n<p><em>Note:<\/em> Within this article I will include simplified code examples. If however\nyou are interested in the real code &amp; tests I used to do this design\nexploration you can see them via the commits at my\n<a rel=\"external\" href=\"https:\/\/github.com\/drewdeponte\/be-wf-design-exploration\">be-wf-design-exploration<\/a>\nGit repository.<\/p>\n<h2 id=\"dependency-injection-for-handlers\">Dependency Injection for Handlers<\/h2>\n<p>Picking up where I left off in the previous article. I need to figure out how\nthis web framework should support injecting dependencies into handlers while\nstill supporting the 3 constraints above.<\/p>\n<p>A very common use case for this would be for a handler to need to interact\nwith a database. This is generally done through a database connection pool.\nSo I am going to use that as an example use case.<\/p>\n<h3 id=\"builder-function-technique\">Builder Function Technique<\/h3>\n<p>In the previous article I used the <strong>builder function<\/strong> technique while\nimplementing the <code>router<\/code> function so that I could give that scope dependencies\nwhile keeping the router signature the same <code>(Request) =&gt; Route<\/code>.<\/p>\n<p>I can simply use the same technique here but applied to a handler if it needs\na dependency or two like a <code>dbPool<\/code> or something.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">function<\/span><span style=\"color: #B57EDC;\"> fooHandler<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">dbPool<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> DatabasePool<\/span><span>)<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> RouteHandler<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  return<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">request<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Request<\/span><span>)<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Response<\/span><span style=\"color: #61AFEF;\"> =&gt;<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">    let<\/span><span style=\"color: #C6CCD7;\"> dbConn<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #C6CCD7;\"> dbPool<\/span><span>.<\/span><span style=\"color: #B57EDC;\">getConnect<\/span><span>();<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">    let<\/span><span style=\"color: #C6CCD7;\"> queryResults<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #C6CCD7;\"> dbConn<\/span><span>.<\/span><span style=\"color: #B57EDC;\">query<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">some query<\/span><span>&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    ...<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    return new<\/span><span style=\"color: #B57EDC;\"> Response<\/span><span>(<\/span><span style=\"color: #E06C75;\">...<\/span><span>);<\/span><\/span>\n<span class=\"giallo-l\"><span>  };<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>This changes how I have to use it in the route definitions as we have to pass\nthe <code>dbPool<\/code> into the <code>fooHandler<\/code> builder function as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> routes<\/span><span style=\"color: #E06C75;\"> =<\/span><span> [<\/span><\/span>\n<span class=\"giallo-l\"><span>    { pattern: &quot;<\/span><span style=\"color: #98C379;\">\/foo<\/span><span>&quot;, handler:<\/span><span style=\"color: #B57EDC;\"> fooHandler<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">dbPool<\/span><span>) },<\/span><\/span>\n<span class=\"giallo-l\"><span>    { pattern: &quot;<\/span><span style=\"color: #98C379;\">\/bar<\/span><span>&quot;, handler:<\/span><span style=\"color: #C6CCD7;\"> barHandler<\/span><span> }];<\/span><\/span><\/code><\/pre>\n<p>This is functionally just fine. The handler resulting from the <code>fooHandler<\/code>\nbuilder function is trivial to test via state based unit testing. However, if I\ntry to do an end-to-end test lets say as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #B57EDC;\">test<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">end-to-end request -&gt; response<\/span><span>&quot;, ()<\/span><span style=\"color: #61AFEF;\"> =&gt;<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">  const<\/span><span style=\"color: #C6CCD7;\"> request<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> wf<\/span><span>.<\/span><span style=\"color: #E5C07B;\">Request<\/span><span style=\"color: #E06C75;\"> =<\/span><span> { path: &quot;<\/span><span style=\"color: #98C379;\">some\/path<\/span><span>&quot; };<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">  const<\/span><span style=\"color: #C6CCD7;\"> response<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #B57EDC;\"> process<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">request<\/span><span>,<\/span><span style=\"color: #B57EDC;\"> router<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">routes<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> notFoundRoute<\/span><span>));<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">  expect<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">response<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">status<\/span><span>).<\/span><span style=\"color: #B57EDC;\">toBe<\/span><span>(<\/span><span style=\"color: #56B6C2;\">200<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span>});<\/span><\/span><\/code><\/pre>\n<p>I would have a big problem because it would be using the real database pool.\nThis is something that I want to be able to stub out in our end-to-end unit\ntests.<\/p>\n<p>This <strong>builder function<\/strong> technique can come in and save the day again. If I\napply the concept to building the routes and then use explicit dependency\ndeclaration I get something like the following.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">function<\/span><span style=\"color: #B57EDC;\"> buildRoutes<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">dbPool<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> DatabasePool<\/span><span>)<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Routes<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  return<\/span><span> [<\/span><\/span>\n<span class=\"giallo-l\"><span>    { pattern: &quot;<\/span><span style=\"color: #98C379;\">\/foo<\/span><span>&quot;, handler:<\/span><span style=\"color: #B57EDC;\"> fooHandler<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">dbPool<\/span><span>) },<\/span><\/span>\n<span class=\"giallo-l\"><span>    { pattern: &quot;<\/span><span style=\"color: #98C379;\">\/bar<\/span><span>&quot;, handler:<\/span><span style=\"color: #C6CCD7;\"> barHandler<\/span><span> }];<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>In this case I am not building another function as I have in the past, but\ninstead I am building a collection of routes.<\/p>\n<p>Which now enables the simple unit style testing at the higher end-to-end level\nwhile still being able to stub out the <code>dbPool<\/code>.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #B57EDC;\">test<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">end-to-end request -&gt; response<\/span><span>&quot;, ()<\/span><span style=\"color: #61AFEF;\"> =&gt;<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">  const<\/span><span style=\"color: #C6CCD7;\"> request<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> wf<\/span><span>.<\/span><span style=\"color: #E5C07B;\">Request<\/span><span style=\"color: #E06C75;\"> =<\/span><span> { path: &quot;<\/span><span style=\"color: #98C379;\">some\/path<\/span><span>&quot; };<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">  const<\/span><span style=\"color: #C6CCD7;\"> response<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #B57EDC;\"> process<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">request<\/span><span>,<\/span><span style=\"color: #B57EDC;\"> router<\/span><span>(<\/span><span style=\"color: #B57EDC;\">buildRoutes<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">dbPool<\/span><span>),<\/span><span style=\"color: #C6CCD7;\"> notFoundRoute<\/span><span>));<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">  expect<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">response<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">status<\/span><span>).<\/span><span style=\"color: #B57EDC;\">toBe<\/span><span>(<\/span><span style=\"color: #56B6C2;\">200<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span>});<\/span><\/span><\/code><\/pre>\n<p>The <code>buildRoutes<\/code> <strong>builder function<\/strong> can be used to support other\ndependencies as well.<\/p>\n<h2 id=\"review-next-up\">Review &amp; Next Up<\/h2>\n<p>Looking at things thus far I think design wise it is still in a very good\nplace. This idea of using a <strong>builder function<\/strong> to build the routes helps\nme meet all the constraints. It also provides a natural logical grouping of routes.<\/p>\n<p>For example I could do something like define a builder specific to a particular\nREST resource and compose its routes together with those of another builder.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> routes<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #B57EDC;\"> Array<\/span><span>().<\/span><span style=\"color: #B57EDC;\">concat<\/span><span>(<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\t\t\tbuildResourceARoutes<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">dbConn<\/span><span>),<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\t\t\tbuildResourceBRoutes<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">dbConn<\/span><span>))<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> response<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #B57EDC;\"> process<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">request<\/span><span>,<\/span><span style=\"color: #B57EDC;\"> router<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">routes<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> notFoundRoute<\/span><span>));<\/span><\/span><\/code><\/pre>\n<p>Beyond that it can provide a natural grouping mechanism for dependencies.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> routes<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #B57EDC;\"> Array<\/span><span>().<\/span><span style=\"color: #B57EDC;\">concat<\/span><span>(<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\t\t\tbuildRoutesNeedingDb<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">dbConn<\/span><span>),<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\t\t\tbuildRoutesNeeding<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">depA<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> depB<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> depC<\/span><span>))<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> response<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #B57EDC;\"> process<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">request<\/span><span>,<\/span><span style=\"color: #B57EDC;\"> router<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">routes<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> notFoundRoute<\/span><span>));<\/span><\/span><\/code><\/pre>\n<p>The above two <code>concat<\/code> examples don't read the nicest. But that is easy enough\nto fix with the addition of another function to make composition of routes\neasier if later on I decide it is worth it.<\/p>\n<p>Next up I am planning on tackeling how I address dependencies that should\nbe constructed fresh for each request.<\/p>\n"},{"title":"Backend Web Framework Design Exploration - Part 1","published":"2021-12-30T00:00:00+00:00","updated":"2021-12-30T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/be-wf-design-exploration-part-001\/"}},"id":"https:\/\/drewdeponte.com\/blog\/be-wf-design-exploration-part-001\/","content":"<p>This is the first in a series of articles I am writing in which I do a Design\nExploration of a Backend Web Framework with the following constraints.<\/p>\n<ul>\n<li>only use functions &amp; data - no classes w\/ methods<\/li>\n<li>use strong typing (via TypeScript) - no use of <code>any<\/code><\/li>\n<li>trivially testable via basic unit style state based tests<\/li>\n<\/ul>\n<p><em>Note:<\/em> Within this article I will include simplified code examples. If however\nyou are interested in the real code &amp; tests I used to do this design\nexploration you can see them via the commits at my\n<a rel=\"external\" href=\"https:\/\/github.com\/drewdeponte\/be-wf-design-exploration\">be-wf-design-exploration<\/a>\nGit repository.<\/p>\n<h2 id=\"why-the-constraints\">Why the constraints?<\/h2>\n<p>It has been my experience that applying constraints like this pushes you\nto think about problem solving in different ways. My hope is that by forcing\nmyself to think within these constraints that I will have some valuable take\naways in terms of deeper understandings of design principles and best\npractices.<\/p>\n<h2 id=\"why-these-constraints\">Why these constraints?<\/h2>\n<p>Throughout my career I have used a lot of different backend web frameworks in a\nlot of different languages. Sadly I can't think of one that actually\nfacilitates end-to-end <code>request -&gt; response<\/code> testing in a simple unit testing style.\nGenerally you have to do a bunch of horrible magic to facilitate dealing with\ndependencies (database, etc.) or use a bunch of unique magic provided by the\nframework.<\/p>\n<p>I believe this magic is unnecessary nonsense and that it is possible to\ndesign a framework in such a way that would naturally facilitate simple unit\nstyle testing.  Hence, the constraint of requiring everything be trivially\ntestable via basic unit style state based tests.<\/p>\n<p>Additionally, I am constantly faced with developers having this strange\nimpulsive fear of functions.  These developers generally build classes that\nhave no real state and no need to actually be a class.  In fact they seem to\nsimply use the class to implicitly share dependencies.<\/p>\n<p>I think this conciously or unconciously boils down to people thinking that\nexplicit dependency declarations via function paramaters are bad. I believe\nthis is due to people worrying about \"threading\" dependencies through layers\nand layers of functions and not understanding how modularity works.<\/p>\n<p>My instinct is that explicit dependency declaration is extremely valuable. Not\nonly does it facilitate testing but  it can also act as an indicator of poor\ndesign. For example seeing heavy parameter \"threading\" should tell you\nsomething is likely off with the design.  Therefore I have included the\nconstraint of only using functions &amp; data to try and prove not only to myself\nbut hopefully to others that using explicit dependency declarations is\nextremely valuable.<\/p>\n<p>Beyond that, over the years I have moved further and further away from dynamic\nlanguages and closer to static strongly typed languages. The amount of aid the\nthe compiler provides these days is amazingly valuable and I believe it outways\nthe limitations.  So I thought it would be interesting to see what sort of\nimplications there are from having that requirement in this context.<\/p>\n<h2 id=\"guiding-architectural-concept\">Guiding Architectural Concept<\/h2>\n<p>For the sake of discussion lets say we want a web framework that would help us\nbuild a REST API. REST APIs are built on top of the HTTP protocol and the\nHTTP protocol is a Request-Response protocol. That means that a client makes a\nrequest with some data and the server responds with some data.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/be-wf-design-exploration-part-001\/high-level-client-server.png\" alt=\"Client Server\" \/><\/p>\n<p>When I think about this Request-Response concept it feels very similar to the\nconcept of a <strong>function<\/strong>.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">function<\/span><span style=\"color: #B57EDC;\"> add<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">x<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> number<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> y<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> number<\/span><span>)<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> number<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  return<\/span><span style=\"color: #C6CCD7;\"> x<\/span><span style=\"color: #E06C75;\"> +<\/span><span style=\"color: #C6CCD7;\"> y<\/span><span>;<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>When you call the function <code>add(5, 2)<\/code> it is equivalent in my eyes to a client\nmaking a <strong>request<\/strong> to a server with the data <code>{ \"x\": 5, \"y\": 2 }<\/code> and getting\nback a <strong>response<\/strong>, in this case the return value of the function.<\/p>\n<p>Given such similarity to a concept that exists naturally in all these languages\nI am going to think of each request-response pair as a function in the hope\nthat it can help guide my design.<\/p>\n<h2 id=\"what-a-backend-web-framework-does\">What a Backend Web Framework does?<\/h2>\n<p>I am going to start by focusing on the core functionality of the framework and\nsee if I can evolve it from there.  At the most basic level I think a Backend\nWeb Framework needs to do the following.<\/p>\n<ol>\n<li>Receive a request<\/li>\n<li>Identify the request &amp; it's associated handler<\/li>\n<li>Execute the matched handler<\/li>\n<li>Receive the response from the handler<\/li>\n<li>Transport the response back to the client<\/li>\n<\/ol>\n<h3 id=\"design-001\">Design 001<\/h3>\n<p>Naively this might look something like the following.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/be-wf-design-exploration-part-001\/design-001.png\" alt=\"Design 001\" \/><\/p>\n<p>However this design has one major problem. The <code>router<\/code> doesn't meet <a rel=\"external\" href=\"https:\/\/en.wikipedia.org\/wiki\/Single-responsibility_principle\">single\nresponsibility principle<\/a>. Infact in this design it is responsible for doing the following.<\/p>\n<ul>\n<li>mapping a request to a handler<\/li>\n<li>coordinating execution of the handler &amp; tunneling the response<\/li>\n<\/ul>\n<p>This problem would prevent the <code>router<\/code> from being tested in isolation from\nthe execution of the mapped handler which goes against defined constraints.<\/p>\n<h3 id=\"design-002\">Design 002<\/h3>\n<p>Taking the learnings from the first design. I decided to separate the concerns\nof the <strong>mapping a request to a handler<\/strong> and the <strong>coordination of the\nexecution of a handler<\/strong>.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/be-wf-design-exploration-part-001\/design-002-w-improvements.png\" alt=\"Design 002\" \/><\/p>\n<p>This shift now makes it so the <strong>process request<\/strong> &amp; <strong>router<\/strong> abide by the\n<a rel=\"external\" href=\"https:\/\/en.wikipedia.org\/wiki\/Single-responsibility_principle\">single responsibility principle<\/a>. In turn enabling testing of the router\nconfiguration in isolation.<\/p>\n<h2 id=\"initial-framework\">Initial Framework<\/h2>\n<p>The following is the initial framework implementation based on Design 002. To\nsee the full implementation at this stage checkout commit\n<a rel=\"external\" href=\"https:\/\/github.com\/drewdeponte\/be-wf-design-exploration\/commit\/9bd3e3a30c330736e9620ca0c60b98e9e58fd8a8?diff=unified\">9bd3e3<\/a>.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">export<\/span><span style=\"color: #61AFEF;\"> interface<\/span><span style=\"color: #E5C07B;\"> Request<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">  path<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> string<\/span><span>;<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">  \/\/ ...<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">export<\/span><span style=\"color: #61AFEF;\"> interface<\/span><span style=\"color: #E5C07B;\"> Response<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">  status<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> number<\/span><span>;<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">  \/\/ ...<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">export<\/span><span style=\"color: #61AFEF;\"> type<\/span><span style=\"color: #E5C07B;\"> RouteHandler<\/span><span style=\"color: #E06C75;\"> =<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">request<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Request<\/span><span>)<\/span><span style=\"color: #61AFEF;\"> =&gt;<\/span><span style=\"color: #E5C07B;\"> Response<\/span><span>;<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">export<\/span><span style=\"color: #61AFEF;\"> interface<\/span><span style=\"color: #E5C07B;\"> Route<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">  pattern<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> string<\/span><span>;<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">  handler<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> RouteHandler<\/span><span>;<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">export<\/span><span style=\"color: #61AFEF;\"> type<\/span><span style=\"color: #E5C07B;\"> Routes<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #E5C07B;\"> Array<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">Route<\/span><span>&gt;;<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">export<\/span><span style=\"color: #61AFEF;\"> type<\/span><span style=\"color: #E5C07B;\"> Router<\/span><span style=\"color: #E06C75;\"> =<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">request<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Request<\/span><span>)<\/span><span style=\"color: #61AFEF;\"> =&gt;<\/span><span style=\"color: #E5C07B;\"> Route<\/span><span>;<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">function<\/span><span style=\"color: #B57EDC;\"> routeMatches<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">request<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Request<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> route<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Route<\/span><span>)<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Bool<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  return<\/span><span style=\"color: #56B6C2;\"> true<\/span><span>;<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">export<\/span><span style=\"color: #61AFEF;\"> function<\/span><span style=\"color: #B57EDC;\"> router<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">routes<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Routes<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> notFoundRoute<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Route<\/span><span>)<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Router<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  return<\/span><span style=\"color: #61AFEF;\"> function<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">request<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Request<\/span><span>)<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Route<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">    const<\/span><span style=\"color: #C6CCD7;\"> matchedRoute<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #C6CCD7;\"> routes<\/span><span>.<\/span><span style=\"color: #B57EDC;\">find<\/span><span>((<\/span><span style=\"color: #C6CCD7;\">route<\/span><span>)<\/span><span style=\"color: #61AFEF;\"> =&gt;<\/span><span style=\"color: #B57EDC;\"> routeMatches<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">request<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> route<\/span><span>));<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    if<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">matchedRoute<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">      return<\/span><span style=\"color: #C6CCD7;\"> matchedRoute<\/span><span>;<\/span><\/span>\n<span class=\"giallo-l\"><span>    }<\/span><span style=\"color: #E06C75;\"> else<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">      return<\/span><span style=\"color: #C6CCD7;\"> notFoundRoute<\/span><span>;<\/span><\/span>\n<span class=\"giallo-l\"><span>    }<\/span><\/span>\n<span class=\"giallo-l\"><span>  };<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">export<\/span><span style=\"color: #61AFEF;\"> function<\/span><span style=\"color: #B57EDC;\"> process<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">request<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Request<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> router<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Router<\/span><span>)<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Response<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  return<\/span><span style=\"color: #B57EDC;\"> router<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">request<\/span><span>).<\/span><span style=\"color: #B57EDC;\">handler<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">request<\/span><span>);<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>The above seems pretty reasonable at least at a high level. Probably the most\ninteresting technique I have used thus far is in the implementation of the\n<code>router<\/code> function. It is simply a builder function that builds the\nactual instance of type <code>Router<\/code> and returns it. It accomplishes this by\nreturning an anonymous function that matches the <code>Router<\/code> type signature.<\/p>\n<p>That by itself isn't really that interesting. However the fact that the builder\nfunction can take in parameters which are then available to the scope of\nactual router implementation (anonymous function) is extremely valuable. It is\nwhat enables us to feed dependencies into a function while having it's\nsignature map to typed signature that knows nothing about those dependencies.<\/p>\n<p>This technique is commonly used in functional programming.<\/p>\n<p>To make sure it supports all the levels of testing I want before adding support\nfor anything else. I whip up a couple tests.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #B57EDC;\">test<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">routing configuration<\/span><span>&quot;, ()<\/span><span style=\"color: #61AFEF;\"> =&gt;<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">  const<\/span><span style=\"color: #C6CCD7;\"> request<\/span><span style=\"color: #E06C75;\"> = ...<\/span><span>;<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">  const<\/span><span style=\"color: #C6CCD7;\"> route<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #B57EDC;\"> router<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">routes<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> notFoundRoute<\/span><span>)(<\/span><span style=\"color: #C6CCD7;\">request<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">  expect<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">route<\/span><span>).<\/span><span style=\"color: #B57EDC;\">toStrictlyEqual<\/span><span>({ pattern: &quot;<\/span><span style=\"color: #98C379;\">some\/path<\/span><span>&quot;, handler:<\/span><span style=\"color: #C6CCD7;\"> somePathHandler<\/span><span> });<\/span><\/span>\n<span class=\"giallo-l\"><span>});<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">test<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">end-to-end request -&gt; response<\/span><span>&quot;, ()<\/span><span style=\"color: #61AFEF;\"> =&gt;<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">  const<\/span><span style=\"color: #C6CCD7;\"> request<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> wf<\/span><span>.<\/span><span style=\"color: #E5C07B;\">Request<\/span><span style=\"color: #E06C75;\"> =<\/span><span> { path: &quot;<\/span><span style=\"color: #98C379;\">some\/path<\/span><span>&quot; };<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">  const<\/span><span style=\"color: #C6CCD7;\"> response<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #B57EDC;\"> process<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">request<\/span><span>,<\/span><span style=\"color: #B57EDC;\"> router<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">routes<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> notFoundRoute<\/span><span>));<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">  expect<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">response<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">status<\/span><span>).<\/span><span style=\"color: #B57EDC;\">toBe<\/span><span>(<\/span><span style=\"color: #56B6C2;\">200<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span>});<\/span><\/span><\/code><\/pre>\n<p>The above allows us to test the following things.<\/p>\n<ul>\n<li>end-to-end test with a request<\/li>\n<li>test routing configuration in isolation<\/li>\n<li>test the route handler in isolation<\/li>\n<\/ul>\n<h2 id=\"usage\">Usage<\/h2>\n<p>To get a better feel for what this framework might actually look like to use\nin this state. I have broken down a rough example.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">import<\/span><span style=\"color: #56B6C2;\"> *<\/span><span style=\"color: #E06C75;\"> as<\/span><span style=\"color: #C6CCD7;\"> wf<\/span><span style=\"color: #E06C75;\"> from<\/span><span> &quot;<\/span><span style=\"color: #98C379;\">.\/wf<\/span><span>&quot;;<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">function<\/span><span style=\"color: #B57EDC;\"> fooHandler<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">request<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> wf<\/span><span>.<\/span><span style=\"color: #E5C07B;\">Request<\/span><span>)<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> wf<\/span><span>.<\/span><span style=\"color: #E5C07B;\">Response<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\treturn<\/span><span> { status:<\/span><span style=\"color: #56B6C2;\"> 200<\/span><span> };<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">function<\/span><span style=\"color: #B57EDC;\"> barHandler<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">request<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> wf<\/span><span>.<\/span><span style=\"color: #E5C07B;\">Request<\/span><span>)<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> wf<\/span><span>.<\/span><span style=\"color: #E5C07B;\">Response<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\treturn<\/span><span> { status:<\/span><span style=\"color: #56B6C2;\"> 200<\/span><span> };<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">function<\/span><span style=\"color: #B57EDC;\"> notFoundHandler<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">request<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> wf<\/span><span>.<\/span><span style=\"color: #E5C07B;\">Request<\/span><span>)<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> wf<\/span><span>.<\/span><span style=\"color: #E5C07B;\">Response<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\treturn<\/span><span> { status:<\/span><span style=\"color: #56B6C2;\"> 404<\/span><span> };<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> routes<\/span><span style=\"color: #E06C75;\"> =<\/span><span> [<\/span><\/span>\n<span class=\"giallo-l\"><span>    { pattern: &quot;<\/span><span style=\"color: #98C379;\">\/foo<\/span><span>&quot;, handler:<\/span><span style=\"color: #C6CCD7;\"> fooHandler<\/span><span> },<\/span><\/span>\n<span class=\"giallo-l\"><span>    { pattern: &quot;<\/span><span style=\"color: #98C379;\">\/bar<\/span><span>&quot;, handler:<\/span><span style=\"color: #C6CCD7;\"> barHandler<\/span><span> }];<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> router<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #C6CCD7;\"> wf<\/span><span>.<\/span><span style=\"color: #B57EDC;\">router<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">routes<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> notFoundRoute<\/span><span>);<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ Normally this request would come in via an HTTP Server and get translated<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ into a wf.Request object and in turn handed to the wf.process() function.<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ However, this works as a means of simulating this for the development of<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ the framework without needing to build the HTTP Server portion.<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> request<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> wf<\/span><span>.<\/span><span style=\"color: #E5C07B;\">Request<\/span><span style=\"color: #E06C75;\"> =<\/span><span> { path: &quot;<\/span><span style=\"color: #98C379;\">\/foo<\/span><span>&quot; };<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> response<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #C6CCD7;\"> wf<\/span><span>.<\/span><span style=\"color: #B57EDC;\">process<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">request<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> router<\/span><span>);<\/span><\/span><\/code><\/pre>\n<p>I threw the above into a single file to facilitate seeing it all at once. In\nreality this would obviously be broken up into different files.<\/p>\n<h2 id=\"review-next-up\">Review &amp; Next Up<\/h2>\n<p>Looking at what I have thus far I am feeling pretty good about the design. It\nfeels simple and clean yet still extremely robust and flexible while meeting\nall of the outlined constraints.<\/p>\n<p>Next up is going to be interesting as I need to figure out how to ideally\nfacilitate dependency injection within the framework.<\/p>\n"},{"title":"Git Patch Stack - Add a Patch","published":"2021-09-23T00:00:00+00:00","updated":"2021-09-23T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/git-patch-stack-add-patch\/"}},"id":"https:\/\/drewdeponte.com\/blog\/git-patch-stack-add-patch\/","content":"<div class=\"video-wrapper\">\n  <iframe src=\"https:\/\/www.youtube.com\/embed\/NTh8dr4HaTQ\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div>\n<p>Today we are focused on <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/git-ps\">Git Patch Stack<\/a>, an open source tool we developed\nand use for a patch stack based workflow in <code>git<\/code>, instead of doing feature\nbranches.<\/p>\n<h2 id=\"what-is-git-patch-stack\">What is Git Patch Stack?<\/h2>\n<p>You might still be asking, \"Well, what is that?\" Basically it's a workflow that\nwe can use to conceptually create logically independent patches, and we have a\nstack of them. So think of a stack of plates. Except, you can do things like\nreorder them, squash them together, split them apart, etc.<\/p>\n<p>In this workflow you generally work all on the <code>main<\/code> branch, and it\nfacilitates you building on top of the changes you have already made. Rather\nthan you having to worry about creating a branch that is based on top of\nanother branch and then based on top of another branch and so on and so forth.<\/p>\n<p>It also promotes proper <code>git<\/code> usage so that you have logical chunks. Enabling\ntooling like <code>git bisect<\/code> to actually work.<\/p>\n<p>To learn more about this workflow checkout my post, <a rel=\"external\" href=\"https:\/\/drewdeponte.com\/blog\/how-we-should-be-using-git\/\">How we should be using\nGit<\/a>.<\/p>\n<h2 id=\"adding-patches-to-your-stack\">Adding Patches to your Stack<\/h2>\n<p>Today we're going to focus on how we create patches on our stack and what is a\nstack in terms of <code>git<\/code>? <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/git-ps\">Git Patch Stack<\/a> is really just a layer of\ntooling built directly on top of <code>git<\/code> itself. So if we look at our <code>git<\/code> tree\nhere, we have a small project.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/git-patch-stack-add-patch\/initial-git-tree-with-skeleton.png\" alt=\"Initial git tree with skeleton\" \/><\/p>\n<p>It has an initial skeleton of a Rust project. That's just a Hello World program\nright now, and we can see we have a branch called <code>main<\/code> that were checked out\non. That's why <code>HEAD<\/code> points to it, and we then have an upstream <code>main<\/code> with a\nremote of <code>origin<\/code>. That happens to be where <code>main<\/code> points to on our GitHub\nrepository for this.<\/p>\n<p>Technically, in <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/git-ps\">Git Patch Stack<\/a> terms, any branch that has an upstream is\na Patch Stack. So you can have as many of these Patch Stacks as you want, as long\nas they are branches that have remote upstreams. To add patches on to a stack,\nyou first have to be checked out on a branch that has an associated upstream.\nIn our case, <code>main<\/code>.<\/p>\n<h3 id=\"make-a-change\">Make a change<\/h3>\n<p>Then we just make changes like we normally would in <code>git<\/code>. We add a commit\nand that commit conceptually becomes a patch. So let's do that real quick.\nLet's go look at our source code here. We have a <code>main()<\/code> function.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"rust\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">fn<\/span><span style=\"color: #B57EDC;\"> main<\/span><span>() {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">    println!<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">Hello, world!<\/span><span>&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>Let's say that we want to add another function called <code>foo()<\/code> for some reason,\nand we want this function to print \"Foo\", nothing too crazy.  So we add a\nfunction.  We're not even going to use the function. We're just going to add it\nto the code base so that we can use it in the future.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"rust\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">fn<\/span><span style=\"color: #B57EDC;\"> main<\/span><span>() {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">    println!<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">Hello, world!<\/span><span>&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">fn<\/span><span style=\"color: #B57EDC;\"> foo<\/span><span>() {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">    println!<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">Foo<\/span><span>&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>That's it. Now we just do a <code>git diff<\/code> to verify our changes.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"diff\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">diff --git a\/src\/main.rs b\/src\/main.rs<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;\">index e7a11a9..0e47771 100644<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">--- a\/src\/main.rs<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #98C379;\">+++ b\/src\/main.rs<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">@@ -1,3 +1,7 @@<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;\"> fn main() {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;\">     println!(&quot;Hello, world!&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;\"> }<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #98C379;\">+<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #98C379;\">+fn foo() {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #98C379;\">+    println!(&quot;Foo&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #98C379;\">+}<\/span><\/span><\/code><\/pre>\n<blockquote>\n<p>I have a git alias of <code>di<\/code> setup so I can just run <code>git di<\/code> to get the diff\nquicker. <a rel=\"external\" href=\"https:\/\/git-scm.com\/book\/en\/v2\/Git-Basics-Git-Aliases\">Git Aliases<\/a><\/p>\n<\/blockquote>\n<p>Once we verify the change is what we want, then we stage that change.\nWe can stage that change by doing a <code>git add<\/code> of that file. So far, we're just\nusing all normal <code>git<\/code> commands, and now it's staged. So we can do a <code>git diff --cached<\/code> and that will show us all the changes that are staged as a diff.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"diff\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">diff --git a\/src\/main.rs b\/src\/main.rs<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;\">index e7a11a9..0e47771 100644<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">--- a\/src\/main.rs<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #98C379;\">+++ b\/src\/main.rs<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">@@ -1,3 +1,7 @@<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;\"> fn main() {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;\">     println!(&quot;Hello, world!&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;\"> }<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #98C379;\">+<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #98C379;\">+fn foo() {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #98C379;\">+    println!(&quot;Foo&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #98C379;\">+}<\/span><\/span><\/code><\/pre>\n<p>It looks good. Looks like the changes we want.<\/p>\n<blockquote>\n<p>I have a git alias of <code>dc<\/code> setup so I can just run <code>git dc<\/code> to get the diff\nquicker. <a rel=\"external\" href=\"https:\/\/git-scm.com\/book\/en\/v2\/Git-Basics-Git-Aliases\">Git Aliases<\/a><\/p>\n<\/blockquote>\n<h3 id=\"add-patch-to-stack\">Add Patch to Stack<\/h3>\n<p>If we do a <code>git commit<\/code>, it pops up my editor and I can enter the commit message<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>Add foo() function<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>So that in the future we can print the message when necessary.<\/span><\/span><\/code><\/pre>\n<p>Ideally, we provide more context but because this is a contrived example. We\ndon't really have it. Now we have a commit.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/git-patch-stack-add-patch\/added-foo-commit.png\" alt=\"Added foo() commit\" \/><\/p>\n<p>If we look at our <code>git<\/code> tree, you\ncan see we have a commit here that we're pointing to on <code>main<\/code>, which is the\n\"Add foo() function\" patch. And then underneath that in the tree, we have the\ninitial skeleton commit we had before.<\/p>\n<p>Now, if we run <code>git ps ls<\/code>, which is how we list our stack of patches.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/git-patch-stack-add-patch\/git-ps-ls-with-added-foo-commit.png\" alt=\"\" \/><\/p>\n<p>We can now see that our stack of patches consist of one commit, the commit that\nwe just added on there, aka a patch.<\/p>\n<h3 id=\"add-another-patch\">Add another Patch<\/h3>\n<p>So let's add another one real quick just so we can\nsee what it's like to have two. Let's just copy this and paste that, and change this\nword to bar, change this to bar and then we'll add another function.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"rust\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">fn<\/span><span style=\"color: #B57EDC;\"> main<\/span><span>() {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">    println!<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">Hello, world!<\/span><span>&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">fn<\/span><span style=\"color: #B57EDC;\"> foo<\/span><span>() {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">    println!<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">Foo<\/span><span>&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">fn<\/span><span style=\"color: #B57EDC;\"> bar<\/span><span>() {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">    println!<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">Bar<\/span><span>&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>Now we just <code>git add<\/code>, <code>git dc<\/code> to verify our staged code. All looks good. We\ncommit it with the message.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>Add bar() function<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>So that we can print the &quot;Bar&quot; message in the future.<\/span><\/span><\/code><\/pre>\n<p>Right now we should have two patches in our stack.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/git-patch-stack-add-patch\/two-patches-on-stack.png\" alt=\"\" \/><\/p>\n<p>We have patches zero, which is the index of that patch currently. And we have\npatch index one.<\/p>\n<p>The short sha of those commits, a.k.a. patches visible to the right of the\nshas. We can also see the patch summaries.<\/p>\n<p>As you get into <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/git-ps\">Git Patch Stack<\/a> further, you'll see indications here that\nwill indicate things like whether that patch has been requested for review, the\npatch has been published, or there's been changes since you requested review.\nBut for now that's it.<\/p>\n<p>That's how you add a patch to the top of your stack.<\/p>\n<p>Hope you enjoyed.<\/p>\n"},{"title":"SwiftUI: Composable Button Styles","published":"2021-06-14T00:00:00+00:00","updated":"2021-06-14T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/swiftui-composable-button-styles\/"}},"id":"https:\/\/drewdeponte.com\/blog\/swiftui-composable-button-styles\/","content":"<p>One frustrating\/annoying things about SwiftUI Buttons for me has been the\nmechanism for styling them. In order to get information about the state of the\nbutton (e.g. if it is pressed, not pressed) you have to build a <code>struct<\/code>\nthat implements the <code>ButtonStyle<\/code> protocol. This would generally look something\nlike the following.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">struct<\/span><span style=\"color: #E5C07B;\"> MyColoredButtonStyle<\/span><span>:<\/span><span style=\"color: #D19A66;\"> ButtonStyle<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\tlet<\/span><span> pressedBackgroundColor: Color <\/span><span style=\"color: #E06C75;\">= ...<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\tlet<\/span><span> notPressedBackgroundColor: Color <\/span><span style=\"color: #E06C75;\">= ...<\/span><\/span>\n<span class=\"giallo-l\"><span>\t<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">\tfunc<\/span><span style=\"color: #B57EDC;\"> makeBody<\/span><span>(<\/span><span style=\"color: #B57EDC;\">configuration<\/span><span>: Configuration)<\/span><span style=\"color: #E06C75;\"> -&gt; some<\/span><span> View {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\treturn<\/span><span> configuration.<\/span><span style=\"color: #C6CCD7;\">label<\/span><span>.<\/span><span style=\"color: #B57EDC;\">background<\/span><span>(configuration.<\/span><span style=\"color: #C6CCD7;\">isPressed<\/span><span style=\"color: #E06C75;\"> ?<\/span><span> pressedBackgroundColor <\/span><span style=\"color: #E06C75;\">:<\/span><span> notPressedBackgroundColor)<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>Then to use it you simply do the following:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span style=\"color: #B57EDC;\">Button<\/span><span>(<\/span><span style=\"color: #E06C75;\">...<\/span><span>).<\/span><span style=\"color: #B57EDC;\">buttonStyle<\/span><span>(<\/span><span style=\"color: #B57EDC;\">MyColoredButtonStyle<\/span><span>())<\/span><\/span><\/code><\/pre><h2 id=\"the-problem\">The Problem<\/h2>\n<p>This is great and all until you decide that you also want your button to have\nan animated scale effect tied to the <code>isPressed<\/code> state as well. You might think\nyou could just create a new <code>MyAnimatedScaleButtonStyle<\/code> struct and tack it on\nlike so.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span style=\"color: #B57EDC;\">Button<\/span><span>(<\/span><span style=\"color: #E06C75;\">...<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span>\t.<\/span><span style=\"color: #B57EDC;\">buttonStyle<\/span><span>(<\/span><span style=\"color: #B57EDC;\">MyColoredButtonStyle<\/span><span>())<\/span><\/span>\n<span class=\"giallo-l\"><span>\t.<\/span><span style=\"color: #B57EDC;\">buttonStyle<\/span><span>(<\/span><span style=\"color: #B57EDC;\">MyAnimatedScaleButtonStyle<\/span><span>())<\/span><\/span><\/code><\/pre>\n<p>Sadly SwiftUI only takes the first button style in the chain and applies\nit. The rest are ignored. This means you basically have to create a super\nunique button style for each particular use case and have all your styling\nslammed into it.<\/p>\n<h2 id=\"a-solution-a-micro-framework\">A Solution (A Micro Framework)<\/h2>\n<p>Taking from Functional Programming we can effectively use Function Composition\nto facilitate making a tiny lightweight framework to facilitate building\ncomposable button styles.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">typealias<\/span><span style=\"color: #E5C07B;\"> ButtonStyleClosure<\/span><span>&lt;<\/span><span style=\"color: #E06C75;\">A<\/span><span>:<\/span><span style=\"color: #D19A66;\"> View<\/span><span>,<\/span><span style=\"color: #E06C75;\"> B<\/span><span>:<\/span><span style=\"color: #D19A66;\"> View<\/span><span>&gt;<\/span><span style=\"color: #E06C75;\"> =<\/span><span> (ButtonStyleConfiguration, A)<\/span><span style=\"color: #E06C75;\"> -&gt;<\/span><span> B<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">precedencegroup<\/span><span style=\"color: #E5C07B;\"> ForwardComposition<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">\tassociativity<\/span><span>: <\/span><span style=\"color: #E06C75;\">left<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">infix operator<\/span><span style=\"color: #B57EDC;\"> &gt;&gt;&gt;<\/span><span>: <\/span><span style=\"color: #D19A66;\">ForwardComposition<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">func<\/span><span style=\"color: #B57EDC;\"> &gt;&gt;&gt;<\/span><span> &lt;<\/span><span style=\"color: #E06C75;\">A<\/span><span>:<\/span><span style=\"color: #D19A66;\"> View<\/span><span>,<\/span><span style=\"color: #E06C75;\"> B<\/span><span>:<\/span><span style=\"color: #D19A66;\"> View<\/span><span>,<\/span><span style=\"color: #E06C75;\"> C<\/span><span>:<\/span><span style=\"color: #D19A66;\"> View<\/span><span>&gt;(<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">    _<\/span><span style=\"color: #C6CCD7;\"> f<\/span><span>: @<\/span><span style=\"color: #61AFEF;\">escaping<\/span><span> ButtonStyleClosure&lt;A, B&gt;,<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">    _<\/span><span style=\"color: #C6CCD7;\"> g<\/span><span>: @<\/span><span style=\"color: #61AFEF;\">escaping<\/span><span> ButtonStyleClosure&lt;B, C&gt;<\/span><\/span>\n<span class=\"giallo-l\"><span>)<\/span><span style=\"color: #E06C75;\"> -&gt;<\/span><span> ButtonStyleClosure&lt;A, C&gt; {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    return<\/span><span> { configuration, a <\/span><span style=\"color: #E06C75;\">in<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">        g<\/span><span>(configuration, <\/span><span style=\"color: #B57EDC;\">f<\/span><span>(configuration, a))<\/span><\/span>\n<span class=\"giallo-l\"><span>    }<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">struct<\/span><span style=\"color: #E5C07B;\"> ComposableButtonStyle<\/span><span>&lt;<\/span><span style=\"color: #E06C75;\">B<\/span><span>:<\/span><span style=\"color: #D19A66;\"> View<\/span><span>&gt;:<\/span><span style=\"color: #D19A66;\"> ButtonStyle<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    let<\/span><span> buttonStyleClosure: ButtonStyleClosure&lt;ButtonStyleConfiguration.Label, B&gt;<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">    init<\/span><span>(<\/span><span style=\"color: #B57EDC;\">_<\/span><span style=\"color: #C6CCD7;\"> buttonStyleClosure<\/span><span>: @<\/span><span style=\"color: #61AFEF;\">escaping<\/span><span> ButtonStyleClosure&lt;ButtonStyleConfiguration.Label, B&gt;) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">        self<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">buttonStyleClosure<\/span><span style=\"color: #E06C75;\"> =<\/span><span> buttonStyleClosure<\/span><\/span>\n<span class=\"giallo-l\"><span>    }<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">    func<\/span><span style=\"color: #B57EDC;\"> makeBody<\/span><span>(<\/span><span style=\"color: #B57EDC;\">configuration<\/span><span>: Configuration)<\/span><span style=\"color: #E06C75;\"> -&gt; some<\/span><span> View {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">        return<\/span><span style=\"color: #B57EDC;\"> buttonStyleClosure<\/span><span>(configuration, configuration.<\/span><span style=\"color: #C6CCD7;\">label<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span>    }<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">extension<\/span><span style=\"color: #E5C07B;\"> Button<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">    func<\/span><span style=\"color: #B57EDC;\"> composableStyle<\/span><span>&lt;<\/span><span style=\"color: #E06C75;\">B<\/span><span>:<\/span><span style=\"color: #D19A66;\"> View<\/span><span>&gt;(<\/span><span style=\"color: #B57EDC;\">_<\/span><span style=\"color: #C6CCD7;\"> buttonStyleClosure<\/span><span>: @<\/span><span style=\"color: #61AFEF;\">escaping<\/span><span> ButtonStyleClosure&lt;ButtonStyleConfiguration.Label, B&gt;)<\/span><span style=\"color: #E06C75;\"> -&gt; some<\/span><span> View {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">        return self<\/span><span>.<\/span><span style=\"color: #B57EDC;\">buttonStyle<\/span><span>(<\/span><span style=\"color: #B57EDC;\">ComposableButtonStyle<\/span><span>(buttonStyleClosure))<\/span><\/span>\n<span class=\"giallo-l\"><span>    }<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>This allows us to define custom <code>ButtonStyleClosure<\/code> functions. The follow are\njust a few examples. But the idea is that you can create whatever of these\n<code>ButtonStyleClosure<\/code>s you want and then use Function Composition to combine\nthem.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">struct<\/span><span style=\"color: #E5C07B;\"> ButtonStateColors<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    let<\/span><span> pressed: Color<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    let<\/span><span> notPressed: Color<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">func<\/span><span style=\"color: #B57EDC;\"> scaledButtonStyle<\/span><span>&lt;<\/span><span style=\"color: #E06C75;\">A<\/span><span>:<\/span><span style=\"color: #D19A66;\"> View<\/span><span>&gt;(<\/span><span style=\"color: #B57EDC;\">_<\/span><span style=\"color: #C6CCD7;\"> configuration<\/span><span>: ButtonStyleConfiguration, <\/span><span style=\"color: #B57EDC;\">_<\/span><span style=\"color: #C6CCD7;\"> view<\/span><span>: A)<\/span><span style=\"color: #E06C75;\"> -&gt; some<\/span><span> View {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    return<\/span><span> view<\/span><\/span>\n<span class=\"giallo-l\"><span>        .<\/span><span style=\"color: #B57EDC;\">scaleEffect<\/span><span>(configuration.<\/span><span style=\"color: #C6CCD7;\">isPressed<\/span><span style=\"color: #E06C75;\"> ?<\/span><span style=\"color: #56B6C2;\"> 0.98<\/span><span style=\"color: #E06C75;\"> :<\/span><span style=\"color: #56B6C2;\"> 1<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span>        .<\/span><span style=\"color: #B57EDC;\">animation<\/span><span>(.<\/span><span style=\"color: #B57EDC;\">spring<\/span><span>(<\/span><span style=\"color: #B57EDC;\">response<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> 0.2<\/span><span>, <\/span><span style=\"color: #B57EDC;\">dampingFraction<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> 0.5<\/span><span>, <\/span><span style=\"color: #B57EDC;\">blendDuration<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> 0.5<\/span><span>))<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">func<\/span><span style=\"color: #B57EDC;\"> roundedButtonStyle<\/span><span>&lt;<\/span><span style=\"color: #E06C75;\">A<\/span><span>:<\/span><span style=\"color: #D19A66;\"> View<\/span><span>&gt;(<\/span><span style=\"color: #B57EDC;\">_<\/span><span style=\"color: #C6CCD7;\"> configuration<\/span><span>: ButtonStyleConfiguration, <\/span><span style=\"color: #B57EDC;\">_<\/span><span style=\"color: #C6CCD7;\"> view<\/span><span>: A)<\/span><span style=\"color: #E06C75;\"> -&gt; some<\/span><span> View {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    return<\/span><span> view.<\/span><span style=\"color: #B57EDC;\">cornerRadius<\/span><span>(<\/span><span style=\"color: #56B6C2;\">8<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">func<\/span><span style=\"color: #B57EDC;\"> defaultPaddingButtonStyle<\/span><span>&lt;<\/span><span style=\"color: #E06C75;\">A<\/span><span>:<\/span><span style=\"color: #D19A66;\"> View<\/span><span>&gt;(<\/span><span style=\"color: #B57EDC;\">_<\/span><span style=\"color: #C6CCD7;\"> configuration<\/span><span>: ButtonStyleConfiguration, <\/span><span style=\"color: #B57EDC;\">_<\/span><span style=\"color: #C6CCD7;\"> view<\/span><span>: A)<\/span><span style=\"color: #E06C75;\"> -&gt; some<\/span><span> View {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    return<\/span><span> view.<\/span><span style=\"color: #B57EDC;\">padding<\/span><span>()<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">func<\/span><span style=\"color: #B57EDC;\"> coloredButtonStyle<\/span><span>&lt;<\/span><span style=\"color: #E06C75;\">A<\/span><span>:<\/span><span style=\"color: #D19A66;\"> View<\/span><span>&gt;(<\/span><span style=\"color: #B57EDC;\">_<\/span><span style=\"color: #C6CCD7;\"> configuration<\/span><span>: ButtonStyleConfiguration, <\/span><span style=\"color: #B57EDC;\">_<\/span><span style=\"color: #C6CCD7;\"> view<\/span><span>: A)<\/span><span style=\"color: #E06C75;\"> -&gt; some<\/span><span> View {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    let<\/span><span> backgroundColors <\/span><span style=\"color: #E06C75;\">=<\/span><span style=\"color: #B57EDC;\"> ButtonStateColors<\/span><span>(<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">        pressed<\/span><span>:<\/span><span style=\"color: #B57EDC;\"> Color<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">cta_button_highlight_color<\/span><span>&quot;),<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">        notPressed<\/span><span>:<\/span><span style=\"color: #B57EDC;\"> Color<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">cta_button_color<\/span><span>&quot;))<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    let<\/span><span> foregroundColors <\/span><span style=\"color: #E06C75;\">=<\/span><span style=\"color: #B57EDC;\"> ButtonStateColors<\/span><span>(<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">        pressed<\/span><span>:<\/span><span style=\"color: #B57EDC;\"> Color<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">cta_button_text_color<\/span><span>&quot;),<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">        notPressed<\/span><span>:<\/span><span style=\"color: #B57EDC;\"> Color<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">cta_button_text_color<\/span><span>&quot;))<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    return<\/span><span> view<\/span><\/span>\n<span class=\"giallo-l\"><span>        .<\/span><span style=\"color: #B57EDC;\">background<\/span><span>(configuration.<\/span><span style=\"color: #C6CCD7;\">isPressed<\/span><span style=\"color: #E06C75;\"> ?<\/span><span> backgroundColors.<\/span><span style=\"color: #C6CCD7;\">pressed<\/span><span style=\"color: #E06C75;\"> :<\/span><span> backgroundColors.<\/span><span style=\"color: #C6CCD7;\">notPressed<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span>        .<\/span><span style=\"color: #B57EDC;\">foregroundColor<\/span><span>(configuration.<\/span><span style=\"color: #C6CCD7;\">isPressed<\/span><span style=\"color: #E06C75;\"> ?<\/span><span> foregroundColors.<\/span><span style=\"color: #C6CCD7;\">pressed<\/span><span style=\"color: #E06C75;\"> :<\/span><span> foregroundColors.<\/span><span style=\"color: #C6CCD7;\">notPressed<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>Allowing us to simply use these with our Function Composition operator we\ndefined in the framework as follows:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span style=\"color: #B57EDC;\">Button<\/span><span>(<\/span><span style=\"color: #E06C75;\">...<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span>\t.<\/span><span style=\"color: #B57EDC;\">composableButtonStyle<\/span><span>(defaultPaddingButtonStyle <\/span><span style=\"color: #E06C75;\">&gt;&gt;&gt;<\/span><span> coloredButtonStyle <\/span><span style=\"color: #E06C75;\">&gt;&gt;&gt;<\/span><span> roundedButtonStyle <\/span><span style=\"color: #E06C75;\">&gt;&gt;&gt;<\/span><span> scaledButtonStyle)<\/span><\/span><\/code><\/pre><h2 id=\"conclusion\">Conclusion<\/h2>\n<p>This gives us lots of reuse and facilitates making more generic button styles\nwhile allowing us to use them throughout our app rather than having to make\nunique button styles for each case.<\/p>\n<p>It does have a limitation though. It would be ideal if we could create\nhigher level functions by nesting closures. Which would give us even more\npower. For example we could create a more general\n<code>foregroundColorButtonStyle(colors:)<\/code> function like the following.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">func<\/span><span style=\"color: #B57EDC;\"> foregroundColorButtonStyle<\/span><span>&lt;<\/span><span style=\"color: #E06C75;\">A<\/span><span>:<\/span><span style=\"color: #D19A66;\"> View<\/span><span>&gt;(<\/span><span style=\"color: #B57EDC;\">_<\/span><span style=\"color: #C6CCD7;\"> colors<\/span><span>: ButtonStateColors)<\/span><span style=\"color: #E06C75;\"> -&gt;<\/span><span> (ButtonStyleConfiguration, A)<\/span><span style=\"color: #E06C75;\"> -&gt; some<\/span><span> View {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\treturn<\/span><span> { configuration, view <\/span><span style=\"color: #E06C75;\">in<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\tview.<\/span><span style=\"color: #B57EDC;\">foregroundColor<\/span><span>(configuration.<\/span><span style=\"color: #C6CCD7;\">isPressed<\/span><span style=\"color: #E06C75;\"> ?<\/span><span> colors.<\/span><span style=\"color: #C6CCD7;\">pressed<\/span><span style=\"color: #E06C75;\"> :<\/span><span> colors.<\/span><span style=\"color: #C6CCD7;\">notPressed<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>In theory this would enable us to be able to be even more composible. However,\nit turns out Swift 5.4.1 doesn't support <code>some View<\/code> in closures. So the above\nsadly won't work. I briefly tried to come up with a nice workaround but wasn't\nable to get anything nice to work.<\/p>\n<p>Despite this one limitation it is still far superior in my mind compared to\nwhat SwiftUI provides out of the box.<\/p>\n"},{"title":"Changelog Standards","published":"2021-05-01T00:00:00+00:00","updated":"2021-05-01T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/changelog-standards\/"}},"id":"https:\/\/drewdeponte.com\/blog\/changelog-standards\/","content":"<p>At <a rel=\"external\" href=\"https:\/\/uptechstudio.com\">Uptech Studio<\/a> we keep a <code>CHANGELOG.md<\/code> following the\n<a rel=\"external\" href=\"https:\/\/keepachangelog.com\">keepachangelog.com<\/a> standard with all libraries and applications we create.<\/p>\n<p>We do this by using the <a rel=\"external\" href=\"https:\/\/github.com\/drewdeponte\/git-cl\">git-cl<\/a> tool that we built. It enables us to get all\nthe values of keeping a <code>CHANGELOG.md<\/code> while not having to deal with the\nworkflow impedances.<\/p>\n<p>To understand more about <a rel=\"external\" href=\"https:\/\/github.com\/drewdeponte\/git-cl\">git-cl<\/a> you can checkout our <a href=\"\/blog\/keep-a-changelog-without-conflicts\/\">Keep a Changelog\nwithout Conflicts<\/a> blog post which also includes a video introducing the\ntool.<\/p>\n"},{"title":"Codebase Organization","published":"2021-05-01T00:00:00+00:00","updated":"2021-05-01T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/codebase-organization\/"}},"id":"https:\/\/drewdeponte.com\/blog\/codebase-organization\/","content":"<h2 id=\"tldr\">TLDR<\/h2>\n<p>The following is the TLDR of how I believe you should organize your code base.<\/p>\n<ul>\n<li>organize code according to architecture <strong>not<\/strong> types of architecture element types<\/li>\n<li>if in framework that does organize by architecture elment types\n<ul>\n<li>see if you can organize by architecture and then move the organization by element types into the folders representing the architectural components.<\/li>\n<li>if you can't, follow the framework convention up until you reach natural boundary (e.g. <code>lib<\/code> folder), then switch to organizing by architecture<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h2 id=\"major-strategies\">Major Strategies<\/h2>\n<p>There are basically two major strategies people use to organize their codebases.<\/p>\n<ul>\n<li>around types of architecture components (e.g. <code>models<\/code>, <code>views<\/code>, <code>controllers<\/code>)<\/li>\n<li>around actual application architecture<\/li>\n<\/ul>\n<h3 id=\"around-types-of-architecture-components\">Around Types of Architecture Components<\/h3>\n<p>Frameworks like Rails and NestJS are examples that follow this strategy where\nthey have a folder for <code>models<\/code>, <code>controllers<\/code>, etc.<\/p>\n<p>Organizing your codebase around these types of architecture components has the\nfollowing advantages.<\/p>\n<ul>\n<li>you don't have to think very hard when you are trying to figure out where to\nput something. Controllers go in the <code>controllers<\/code> folder, <code>models<\/code> in the\nmodels folder, etc.<\/li>\n<\/ul>\n<p>This benefit however is not always true because there is generally a boundary\nwhere this falls apart as the framework doesn't provide direction. The <code>lib<\/code>\nfolder is often one of these boundaries. The framework provides guidence in\nterms of organization right up until that point and then all of the sudden\n<code>lib<\/code> is just a dumpster full of stuff.<\/p>\n<p>Organizing your codebase around these types of architecture components also\ndoesn't help people understand anything from the codebase other than that there\nare things called <code>controllers<\/code>, <code>models<\/code>, and <code>views<\/code>. But it doesn't give you\nany knowledge in terms of how certain controllers, models, or views relate to\none another. You have no idea from the organization of the codebase how to find\na particular part of the application.<\/p>\n<h3 id=\"around-application-architecture\">Around Application Architecture<\/h3>\n<p>An alternative approach that people take is to instead organize their codebase\naround the actual application architecture.<\/p>\n<p>When you have put that thought in, people coming into the code base can\n<strong>quickly and easily understand the application architecture<\/strong>. They can also\n<strong>quickly navigate<\/strong> to appropriate areas of the code they want to focus on.<\/p>\n<p>This strategy also helps with creating <strong>logical commits<\/strong>, which I am a big\nfan of, and <strong>reducing conflicts<\/strong> in source control systems.<\/p>\n<p>People often perceive the fact that you have to think about where to put\nfolders and files in your codebase as a negative thing.<\/p>\n<p>I believe this effort is one of the more valuable activities actually. It is\neffectively a forcing function driving you to make sure you have a sound\napplication architecture and understand where the piece you are working on\nshould fit within it.<\/p>\n<p>Using techniques like modularization and concepts like the The Onion\narchitecture can help you figure out and evolve your application archicture and\nyour codebase organization.<\/p>\n<h2 id=\"but-with-frameworks\">But with Frameworks<\/h2>\n<p>I know from experience fighting against a frameworks inherent design is\ngenerally not a great strategy. So how do we organize our codebase around the\nApplication Architecture when we working with a framework like Rails or NestJS?<\/p>\n<p>Simple, we follow the convention of the framework up until we reach that\nnatural boundary where it stops providing direction, e.g. the <code>lib<\/code> folder.\nEverything inside that <code>lib<\/code> folder we would organize around application\narchitecture and <strong>not<\/strong> by category of architecture component.<\/p>\n<p>This at least gives developers the knowledge around that area of the code.\nIdeally it would be great if we could get those benefits at the framework level\nof things as well. However, it just isn't worth fighting against the framework\nto make it happen.<\/p>\n"},{"title":"Squads & Guilds","published":"2021-05-01T00:00:00+00:00","updated":"2021-05-01T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/squads-and-guilds\/"}},"id":"https:\/\/drewdeponte.com\/blog\/squads-and-guilds\/","content":"<p>At Uptech Studio we organize ourselves in a similar fashion to Spotify with Squads &amp; Guilds. However, how we define Squads &amp; Guilds is a bit different.<\/p>\n<h2 id=\"tldr\">TLDR<\/h2>\n<p>Below is an explainer describing what Squads &amp; Guilds are to us and some examples of how people fit into them.<\/p>\n<p><a href=\"https:\/\/drewdeponte.com\/blog\/squads-and-guilds\/uptech-squads-explainer.png\"><img src=\"https:\/\/drewdeponte.com\/blog\/squads-and-guilds\/uptech-squads-explainer.png\" alt=\"Visual explaining what Squads &amp; Guilds\" \/><\/a><\/p>\n<h2 id=\"squads\">Squads<\/h2>\n<p>A <strong>squad<\/strong> is simply a group of people organized around delivering a particular project\/product. They are <strong>on the hook<\/strong> as they are responsible for delivering the project\/product to the client. Squads are formed and disbanded as needed around the various projects\/products.<\/p>\n<h3 id=\"squad-representatives\">Squad Representatives<\/h3>\n<p>Squad <strong>representatives<\/strong> are a subset of the Squad that is responsible for representing the various competencies to the client. Generally there is one person from each of the necessary competencies, e.g. Design, Engineering, Product. Squad Representatives are generally comitted for the lifetime of the project as they are the face of the squad to the client. If projects are long term we may shift representives around but generally try to minimize that.<\/p>\n<h2 id=\"guilds\">Guilds<\/h2>\n<p>A <strong>guild<\/strong> is a group of people focused on a particular competency, e.g. Dev-Ops, Mobile, Web, Backend, Product, Desktop, Data Engineering, Design. The following are things that guild members often help with.<\/p>\n<ul>\n<li><strong>helping<\/strong> people do a certain task to hit deadlines. The assisting guild member bills their time against the project though.<\/li>\n<li><strong>pairing<\/strong> with you if you are stuck, or need to be leveled up on a tool\/concept. The helping guild member <strong>should NOT<\/strong> bill their time to the project.<\/li>\n<li><strong>peer review<\/strong> the guild is responsible for helping you when you need a peer review and you don't have anyone with necessary competency in your Squad to do it. If you are doing the peer review, <strong>bill your time<\/strong> against the project.<\/li>\n<li><strong>manage time<\/strong> being a Guild member you have obligations to get your own work done. You shouldn't be doing peer reviews &amp; pairing for other projects non-stop. So manage your time and make sure you can meet your obligations.<\/li>\n<li><strong>sharing<\/strong> you share your knowledge, tools, reusable code, best practices, etc. within the guild.<\/li>\n<li><strong>standards<\/strong> you collaborate within the guild to define standards &amp; best practices.<\/li>\n<li><strong>multiple guilds<\/strong> you can be a member of more than one guild as you may have more than one competency.<\/li>\n<\/ul>\n<p>Our primary communication mechanism for Guilds is <strong>Slack Channels<\/strong>, e.g. <strong>#mobile-guild<\/strong>, <strong>#backend-guild<\/strong>, etc.<\/p>\n<h2 id=\"why-squads-guilds\">Why Squads &amp; Guilds?<\/h2>\n<p>Basically we needed a way to facilitate making sure we were focusing and delivering for our clients (the focus of the Squad) while also facilitating cross-pollination and elimination of siloing across the organization (the focus of the Guild). We have found this structure and the intention behind it to be extremely effective on both these fronts.<\/p>\n<p>Below you can see a visualization of what Squad might look like in relation to Guilds.<\/p>\n<p><a href=\"https:\/\/drewdeponte.com\/blog\/squads-and-guilds\/uptech-squads-example.png\"><img src=\"https:\/\/drewdeponte.com\/blog\/squads-and-guilds\/uptech-squads-example.png\" alt=\"Visual example of Squads &amp; Guilds\" \/><\/a><\/p>\n"},{"title":"Uptech Studio Agile Development","published":"2021-05-01T00:00:00+00:00","updated":"2021-05-01T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/uptech-agile-development\/"}},"id":"https:\/\/drewdeponte.com\/blog\/uptech-agile-development\/","content":"<h1 id=\"kanban\">Kanban<\/h1>\n<p>We are big believers in Agile methodologies. However, we don't prescribe to strict Scrum. Instead we use Kanban as we feel it better aligns with the fluidity and agileness necessary with the earlier stage companies we work with.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/uptech-agile-development\/example-kanban-board.png\" alt=\"Example Kanban Board\" \/><\/p>\n<p>In Kanban the columns on the board represent the prioritized queue of items that need to be done by each role in the software development process. Items move across the board until complete.<\/p>\n<p>We often use work in progress limits for each column to guide us around capacity issues and enforce prioritization.<\/p>\n<p>The Kanban boards provide all stakeholders with a clear view into the development pipeline. Boards can be customized via grouping and filtering.<\/p>\n<h2 id=\"columns\">Columns<\/h2>\n<p>Columns in Kanban are intended to be customized to the process at hand. By default most Kanban tools provide the following columns at a minimum.<\/p>\n<ul>\n<li><strong>TODO<\/strong> - all things that the team can work on. By default, this list will continue to grow each week.<\/li>\n<li><strong>In Progress<\/strong> - all the things the team is working on<\/li>\n<li><strong>Done<\/strong> - the things the team has completed.<\/li>\n<\/ul>\n<p>We generally evolve these on our projects to some combination of the following colmuns.<\/p>\n<ul>\n<li><strong>Selected for Dev<\/strong> - Every new issue starts in Needs Review. Each week, we review this list and move issues to keep it empty.<\/li>\n<li><strong>Specs Needed<\/strong> - Stories that need further specification, UX, and\/or UI assets.<\/li>\n<li><strong>Ready for Dev<\/strong> - Issues that are ready for the developers.<\/li>\n<li><strong>In Progress<\/strong> - Issues being worked on by a developer.<\/li>\n<li><strong>QA\/Acceptance<\/strong> - Issues delivered by development and is now with QA and\/or Product for testing.<\/li>\n<li><strong>Accepted<\/strong> - Issues that are accepted and ready for release.<\/li>\n<li><strong>Released<\/strong> - Issues that have been deployed to users.<\/li>\n<\/ul>\n<p><em>Note:<\/em> The above columns are guidelines we use to stay in alignment with each other. But if we are on a project where we feel like a couple of these columns aren't need and are just ritual we will get rid of those columns.<\/p>\n<h1 id=\"standups\">Standups<\/h1>\n<h3 id=\"the-problem\">The Problem<\/h3>\n<p><a rel=\"external\" href=\"https:\/\/uptechstudio.com\">Uptech Studio<\/a> has remote employees all over the US. Therefore our team is often pretty fragmented in terms of physical location. For example, I might be on-site at a client's office while another member is at a different client's office, and yet other members are at our office. This physical fragmentation makes it difficult to stay on top of what is going on across the organization on a day-to-day basis.<\/p>\n<h3 id=\"normal-solution\">Normal Solution<\/h3>\n<p>Normally, in a startup we would have a standup every morning to kick-off the day. If you aren't familiar, a standup is a time-boxed meeting that is used in many agile methodologies to facilitate communication around individuals' daily commitments, as well as raise awareness of any challenges (a.k.a.  blockers). The reason it is called a standup is because the thinking is that if everyone has to stand during the meeting it would help keep the meeting short. For further details on standups and their intent check out the <a rel=\"external\" href=\"https:\/\/en.m.wikipedia.org\/wiki\/Stand-up_meeting\">Wikipedia page<\/a>.<\/p>\n<h3 id=\"our-solution\">Our Solution<\/h3>\n<p>Given that our team is rarely in the same physical location at the same time, we have taken the principles and intent from the standup and adapted it to our situation by using a <a rel=\"external\" href=\"https:\/\/slack.com\">Slack<\/a> <code>#standups<\/code> channel to share our commitments and blockers every morning at 10 am. To aid with making sure we have the standup right at 10am everyday, we set up a <a rel=\"external\" href=\"https:\/\/get.slack.help\/hc\/en-us\/articles\/208423427-Set-a-reminder\">Slack reminder<\/a> in the <code>#standups<\/code> channel.<\/p>\n<p>Then in the <code>#standups<\/code> channel we communicate the classic standup information: What did I do yesterday?, What am I doing today?, Do I have any blockers? Specifically, we use the following template and generally prepare the mornings standup in a text buffer before pasting it into the <code>#standups<\/code> channel. The template\/example below is based on the <a rel=\"external\" href=\"https:\/\/get.slack.help\/hc\/en-us\/articles\/202288908-Format-your-messages\">Slack message format<\/a>.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>_Tue July 11, 2017 - Standup_<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>*Yesterday*<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>_client_1_<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\u2022 One thing I did yesterday<\/span><\/span>\n<span class=\"giallo-l\"><span>\u2022 Another thing I did yesterday<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>_client_2_<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\u2022 Yet another thing I did yesterday<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>*Today*<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>_client_1_<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\u2022 A thing I am going to do today<\/span><\/span>\n<span class=\"giallo-l\"><span>\u2022 Something else I am going to do today<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>_client_2_<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\u2022 Another thing I am going to do today<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>*Blockers*<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\u2022 A blocker that is currently preventing me from accomplishing something or will in the near future<\/span><\/span><\/code><\/pre>\n<p>When the above is pasted into <a rel=\"external\" href=\"https:\/\/slack.com\">Slack<\/a> it looks as follows:<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/uptech-agile-development\/slack-screenshot.png\" alt=\"\" \/><\/p>\n<p>Without this virtual standup every morning we wouldn't be able to successfully run our business. It constantly triggers discussions, questions, suggestions, etc. that help keep the business on track and moving in the right directions.<\/p>\n<h2 id=\"client-standups\">Client Standups<\/h2>\n<p>Not only do we post our complete standup in the <code>#standups<\/code> channel to share with <a rel=\"external\" href=\"https:\/\/uptechstudio.com\">Uptech Studio<\/a>. We also post client scoped versions of our standups in shared channels between <a rel=\"external\" href=\"https:\/\/uptechstudio.com\">Uptech Studio<\/a> and the respective client, .e.g. <code>#client1-uptech<\/code>. This helps in a number of ways.<\/p>\n<ul>\n<li>it helps keeps the client updated and clued into what things are progressing<\/li>\n<li>it gives an opportunity to highlight and remind the client about blockers that they might be able to facilitate<\/li>\n<li>it helps stay in alignment with client team and any coordination efforts between <a rel=\"external\" href=\"https:\/\/uptechstudio.com\">Uptech Studio<\/a> and them<\/li>\n<\/ul>\n<p>The following is what the above <a rel=\"external\" href=\"https:\/\/uptechstudio.com\">UpTech Studio<\/a> standup would look like scoped down to <code>client1<\/code> and would be what we would shared in the <code>#client1-uptech<\/code> channel.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>_Tue July 11, 2017 - Standup_<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>*Yesterday*<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\u2022 One thing I did yesterday<\/span><\/span>\n<span class=\"giallo-l\"><span>\u2022 Another thing I did yesterday<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>*Today*<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\u2022 A thing I am going to do today<\/span><\/span>\n<span class=\"giallo-l\"><span>\u2022 Something else I am going to do today<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>*Blockers*<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\u2022 None<\/span><\/span><\/code><\/pre><h2 id=\"tools\">Tools<\/h2>\n<p>The following are a few tools we have created\/found that help with this process. Though at the end of the day, like any tool\/process it comes down to having an open minded collaborative team that understands the importance of process and communication to really get the value out of a practice like this. Tools are never a solution for culture but they can reduce friction.<\/p>\n<h3 id=\"gumleaf\">Gumleaf<\/h3>\n<p>Gumleaf is a side project of <a rel=\"external\" href=\"https:\/\/drewdeponte.com\">Drew De Ponte<\/a> &amp; <a rel=\"external\" href=\"http:\/\/anthonycastelli.me\">Anthony Castelli<\/a> where the beta release is being shared with <a rel=\"external\" href=\"https:\/\/uptechstudio.com\">Uptech Studio<\/a> to get feedback. It is a personal task management solution built around the concept of daily standups and also provides features to trivially scope standups down to specific clients as well as export standups in the Slack format.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/uptech-agile-development\/gumleaf-screenshot.png\" alt=\"\" \/><\/p>\n<p>This has become a daily driver for most of <a rel=\"external\" href=\"https:\/\/uptechstudio.com\">Uptech Studio<\/a>.<\/p>\n<h3 id=\"vim-neovim\">Vim\/Neovim<\/h3>\n<p>Given that this is such a core part of our process we created the <a rel=\"external\" href=\"http:\/\/github.com\/drewdeponte\/vim-slack-format\">Vim Slack Format<\/a> plugin to provide syntax highlighting in <a rel=\"external\" href=\"http:\/\/www.vim.org\">Vim<\/a> or <a rel=\"external\" href=\"https:\/\/neovim.io\">Neovim<\/a> for the <a rel=\"external\" href=\"https:\/\/get.slack.help\/hc\/en-us\/articles\/202288908-Format-your-messages\">Slack message format<\/a>. When using this plugin it looks as follows:<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/uptech-agile-development\/vim-slack-format-screenshot.png\" alt=\"\" \/><\/p>\n<p>This allows you to have a <code>standups.slack<\/code> file to facilitate keeping historical track of your standups in an easily searchable format where you keep the most recent standup at the top of the file. Some people even like to keep this in a <a rel=\"external\" href=\"https:\/\/git-scm.com\">Git<\/a> repository so that their history is pushed up to a remote on a regular basis.<\/p>\n<h1 id=\"planning-prioritization\">Planning &amp; Prioritization<\/h1>\n<p>Given that we don't do Scrum. We don't really do formal \"sprint planning\". In fact we don't actually believe in the constraint of a \"sprint\".<\/p>\n<p>Instead we focus on \"roadmap planning\" and \"iteration planning\". The destinction is that we do plan for an iteration (roughly a week or two out). However, that planning simply funnels into the Kanban board as to what we are targeting working on. If that changes as more information comes in it isn't a problem as we are just working down through the prioritized columns. This differs from the concept of formal sprints where a sprint's tickets are usually locked in until the following sprint.<\/p>\n<p>We have a <strong>weekly<\/strong> Planning &amp; Prioritization meetings.<\/p>\n<h3 id=\"planning\">Planning<\/h3>\n<p>During the <strong>Planning<\/strong> portion of the meeting we do the following.<\/p>\n<ul>\n<li>quickly review the roadmap against all new information and make sure it still holds, adjusting as needed<\/li>\n<li>review all the items in <strong>Selected for Dev<\/strong> column and determine where on the board the tickets should go. The <strong>Selected for Dev<\/strong> column needs to be empty at the end of the meeting. Items can go back to the backlog if needed.<\/li>\n<li>make sure WIP limits are adhered to and we remove items from a column to make room for other higher priority tickets when the limit has been reached.<\/li>\n<\/ul>\n<h2 id=\"prioritization\">Prioritization<\/h2>\n<p>During the <strong>Prioritization<\/strong> portion of the meeting we do the following.<\/p>\n<ul>\n<li>review the priority of the epics making sure they still hold, adjusting as needed<\/li>\n<li>prioritize the tickets within each of the epics covering at least a few iterations worth of work out (making sure we prioritized work in the backlog if we complete all the work planned for this iteration).<\/li>\n<\/ul>\n<h2 id=\"attendees\">Attendees<\/h2>\n<p>This meaning is owned &amp; lead by Product but Engineers, QA, Design, etc. generally attend.<\/p>\n<h1 id=\"demos\">Demos<\/h1>\n<p>We generally do <strong>weekly<\/strong> or <strong>bi-weekly<\/strong> demos with the client.<\/p>\n<p>We use these as clear opportunities to <strong>show progress<\/strong> and <strong>get feedback<\/strong> on the work we have done for the client. Some clients like to be in the nitty gritty and get regular builds of the apps and provide feedback along the way. Others even when we provided regular builds won't really look at them until we have a <strong>demo<\/strong>. We do demos irrespective of the clients behavior as it is good practice to take a step back and look at the work that has been done and how it relates to the higher level product goals.<\/p>\n<p>When we do our <strong>Planning &amp; Prioritization<\/strong> meeting each week we discuss our plans for what we are going to demo the next week. We are extremely careful not to let things be driven completely by the demo and become DDD (Demo Driven Development). But it is an important attribute to take into consider while planning and prioritizing work.<\/p>\n<p>We generally try and avoid demos of APIs and network requests as much as possible and focus on demos of application UI as much as possible. There are of course exceptions to this. For example if the project was to build a public facing API for a service. Then it would make sense for the demo to be Paw\/Postman interacting with the public API.<\/p>\n<h1 id=\"retrospective\">Retrospective<\/h1>\n<p>We do hold Retrospective meetings <strong>bi-weekly<\/strong> for the squad to review how things are going. We also collect the following information from the team.<\/p>\n<ul>\n<li>what went well?<\/li>\n<li>what didn't go well?<\/li>\n<li>define action items with owners as a team to improve the process<\/li>\n<\/ul>\n<p>We also review cycle time and throughput trends to make sure there are no underlying issues that need to be addressed.<\/p>\n<p>This meeting is owned and lead by Product.<\/p>\n<h1 id=\"feedback-loop\">Feedback Loop<\/h1>\n<p>In our minds <strong>Agile Development<\/strong> is really all about feedback loops. It is about listening, being aware, and gather feedback continuously throughout the process so that we adjust according to that feedback as we progress along.<\/p>\n<p>Once we start the <strong>Agile Development<\/strong> process doesn't mean we forgo all of the other processes we did initially (e.g. Discovery &amp; Definition, Project Planning, Design Review, Roadmaping, Milestone Planning). In fact we continue to do them iteratively. Basically maintaining the goals of them along the way as we learn things through the <strong>Agile Development<\/strong> process.<\/p>\n<p>The way we think about it is really that we are maintaining and evolving the artifacts that came out of the initially upfront engineering process while we iterate through the <strong>Agile Development<\/strong> process.<\/p>\n"},{"title":"Uptech Studio Branching Strategies","published":"2021-05-01T00:00:00+00:00","updated":"2021-05-01T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/uptech-branching-strategies\/"}},"id":"https:\/\/drewdeponte.com\/blog\/uptech-branching-strategies\/","content":"<p>Branching strategies are generally applicable in two contexts.<\/p>\n<ul>\n<li>strategies used for development<\/li>\n<li>strategies used for releases<\/li>\n<\/ul>\n<h2 id=\"strategies-for-development\">Strategies for Development<\/h2>\n<p>Our preferred branching strategy is a style of <strong>trunk-based development<\/strong>. This means that in terms of collaboration we have that happen over the mainline branch (a.k.a. trunk) by following a workflow we developed to facilitate the creation of <strong>small pull requests<\/strong> and the highest quality peer reviews. You can read more about this in our post, <a href=\"\/blog\/journey-to-small-pull-requests\">Journey to Small Pull Requests<\/a>.<\/p>\n<p>Behind the scenes our workflow does use branches (<strong>not<\/strong> feature branches) when requesting review of patches. In turn enabling the peer reviewer to review in a manner that they would be used to. This also enables us to work with teams that don't follow our workflow and allows us to still use our workflow and get the values and benefits while still playing nicely with a team that is doing feature branches.<\/p>\n<p>We don't believe in Feature Branches as outlined in <a href=\"\/blog\/journey-to-small-pull-requests\">Journey to Small Pull Requests<\/a> as they introduce a number of difficult problems in relation to writing quality code.<\/p>\n<p>We also keep a linear <a rel=\"external\" href=\"https:\/\/git-scm.com\">Git<\/a> history through our workflow so that it is easy to go back and revert commits, etc. if necessary.<\/p>\n<h2 id=\"strategies-for-release\">Strategies for Release<\/h2>\n<p>Many people use a branching strategy for releases. For example they might have a <strong>production<\/strong>, <strong>qa<\/strong>, and <strong>dev<\/strong> branch mapping to respective environments. We do <strong>NOT<\/strong> use environment based branching or any other branching strategy for releases generally. We instead use <a rel=\"external\" href=\"https:\/\/git-scm.com\">Git<\/a> tags to manage releases.<\/p>\n<p>This enables us to have and maintain our linear history while actually using immutable tags to represent releases and in turn trigger automated builds and deployments. In some cases we might also choose to do manually triggered releases.<\/p>\n<p>Sometimes we are stuck in a client's environment in which they have built up a bunch of automation around environment based branching. In those case we generally recommend that client make their automation work in such a way that they don't have to merge branches back and forth and instead the branches can be force pushed to, triggering automation and deploys. This helps mitigate a bunch of confusion within the <a rel=\"external\" href=\"https:\/\/git-scm.com\">Git<\/a> history.<\/p>\n"},{"title":"Uptech Studio Error Handling","published":"2021-05-01T00:00:00+00:00","updated":"2021-05-01T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/uptech-error-handling\/"}},"id":"https:\/\/drewdeponte.com\/blog\/uptech-error-handling\/","content":"<p>Our general position on error handling is that we should be using <code>Result<\/code> types with base exception classes so that we can easily transition between the <code>Result<\/code> type world and the <code>Exception<\/code> worlds.<\/p>\n<p>The following is a presentation <a rel=\"external\" href=\"https:\/\/drewdeponte.com\">Drew De Ponte<\/a> gave that covers this topic and how we should be thinking about error handling.<\/p>\n<iframe width=\"560\" height=\"315\" src=\"https:\/\/www.youtube.com\/embed\/--t3vn-dE8Y\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen><\/iframe>\n<p><a href=\"https:\/\/drewdeponte.com\/blog\/uptech-error-handling\/abstractions-and-errors-export.pdf\">Abstractions &amp; Errors - Slides<\/a><\/p>\n<h2 id=\"typescript-example\">TypeScript Example<\/h2>\n<p>The following is an example of how we can create a Base exception in TypeScript and how we can create Custom exception types and use them with <code>Result<\/code> types and how we can programmatically handle the cases.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"typescript\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">import<\/span><span> {<\/span><span style=\"color: #C6CCD7;\"> Ok<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> Err<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> Result<\/span><span> }<\/span><span style=\"color: #E06C75;\"> from<\/span><span> &#39;<\/span><span style=\"color: #98C379;\">ts-results<\/span><span>&#39;;<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ Here we define a CustomError that all custom errors should extend from so<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ that the custom errors don&#39;t have to worry about setting their name<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ appropriately.<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">class<\/span><span style=\"color: #E5C07B;\"> CustomError<\/span><span style=\"color: #61AFEF;\"> extends<\/span><span style=\"color: #D19A66;\"> Error<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">\tconstructor<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">message<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> string<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tsuper<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">message<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tthis<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">name<\/span><span style=\"color: #E06C75;\"> = this<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">constructor<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">name<\/span><span>;<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ This is something that we have seen on the web for issues where Babel won&#39;t<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ set the stack trace properly when the class doesn&#39;t extend properly. But<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ from our testing this hasn&#39;t seemed to be needed and it is also our<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ understanding that super() now does this.<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/         if (typeof Error.captureStackTrace === &#39;function&#39;) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/             Error.captureStackTrace(this, this.constructor);<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/         } else {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/             this.stack = (new Error(message)).stack;<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/         }<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ This is an error type that is useful to have around because if you have a<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ function that returns Result&lt;someType, yourErrorType&gt; you don&#39;t want that<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ function to throw an exception because you are using Result. So inside<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ the function you would do something like the following:<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ try {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ \t\t...<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ } catch (e: any) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ \t\treturn new Err(new UnhandledError(&#39;some message&#39;, e))<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ }<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ This allows you to identify it is an unhandled error but still get to the<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ internal error object if needed later on.<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">class<\/span><span style=\"color: #E5C07B;\"> UnhandledError<\/span><span style=\"color: #61AFEF;\"> extends<\/span><span style=\"color: #D19A66;\"> CustomError<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\torigError<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Error<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">\tconstructor<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">message<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> string<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> origError<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Error<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tsuper<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">message<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tthis<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">origError<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #C6CCD7;\"> origError<\/span><span>;<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ The following are examples of defined custom error types as a hierarchy<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ for categorization. In this example we have a DashboardEventsError as a<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ base category for the errors and then we have MyErrorClassA and<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ MyErrorClassB as specific errors within that category.<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">class<\/span><span style=\"color: #E5C07B;\"> DashboardEventsError<\/span><span style=\"color: #61AFEF;\"> extends<\/span><span style=\"color: #D19A66;\"> CustomError<\/span><span> {}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">class<\/span><span style=\"color: #E5C07B;\"> MyErrorClassA<\/span><span style=\"color: #61AFEF;\"> extends<\/span><span style=\"color: #D19A66;\"> DashboardEventsError<\/span><span> {}<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">class<\/span><span style=\"color: #E5C07B;\"> MyErrorClassB<\/span><span style=\"color: #61AFEF;\"> extends<\/span><span style=\"color: #D19A66;\"> DashboardEventsError<\/span><span> {}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ The following is code I used to vet the functionality of the custom errors<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ and make sure that they meet all the expectations of the interface for the<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ Error type<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">const<\/span><span style=\"color: #C6CCD7;\"> e<\/span><span style=\"color: #E06C75;\"> = new<\/span><span style=\"color: #B57EDC;\"> MyErrorClassB<\/span><span>(&#39;<\/span><span style=\"color: #98C379;\">some message<\/span><span>&#39;);<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ - can we get the name and is it the correct custom name<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">console<\/span><span>.<\/span><span style=\"color: #B57EDC;\">log<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">name: <\/span><span>&quot;,<\/span><span style=\"color: #C6CCD7;\"> e<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">name<\/span><span>);<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ - can we get the message<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">console<\/span><span>.<\/span><span style=\"color: #B57EDC;\">log<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">message: <\/span><span>&quot;,<\/span><span style=\"color: #C6CCD7;\"> e<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">message<\/span><span>);<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ - can get stack trace<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">console<\/span><span>.<\/span><span style=\"color: #B57EDC;\">log<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">message: <\/span><span>&quot;,<\/span><span style=\"color: #C6CCD7;\"> e<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">stack<\/span><span>);<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ - can we conditionally check the custom type to programmatically handle specific errors<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">if<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">e<\/span><span style=\"color: #E06C75;\"> instanceof<\/span><span style=\"color: #E5C07B;\"> MyErrorClassA<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\tconsole<\/span><span>.<\/span><span style=\"color: #B57EDC;\">log<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">it is an instance of MyErrorClassA<\/span><span>&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><span style=\"color: #E06C75;\"> else<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\tconsole<\/span><span>.<\/span><span style=\"color: #B57EDC;\">log<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">it is NOT an instance of MyErrorClassA<\/span><span>&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">if<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">e<\/span><span style=\"color: #E06C75;\"> instanceof<\/span><span style=\"color: #E5C07B;\"> MyErrorClassB<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\tconsole<\/span><span>.<\/span><span style=\"color: #B57EDC;\">log<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">it is an instance of MyErrorClassB<\/span><span>&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><span style=\"color: #E06C75;\"> else<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\tconsole<\/span><span>.<\/span><span style=\"color: #B57EDC;\">log<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">it is NOT an instance of MyErrorClassB<\/span><span>&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ - can conditionally check if it is an instance of custom base class type<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">if<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">e<\/span><span style=\"color: #E06C75;\"> instanceof<\/span><span style=\"color: #E5C07B;\"> DashboardEventsError<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\tconsole<\/span><span>.<\/span><span style=\"color: #B57EDC;\">log<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">it is an DashboardEventsError<\/span><span>&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><span style=\"color: #E06C75;\"> else<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\tconsole<\/span><span>.<\/span><span style=\"color: #B57EDC;\">log<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">it is NOT an DashboardEventsError<\/span><span>&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ - can conditionally check if it is an instance of Error type<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">if<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">e<\/span><span style=\"color: #E06C75;\"> instanceof<\/span><span style=\"color: #E5C07B;\"> Error<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\tconsole<\/span><span>.<\/span><span style=\"color: #B57EDC;\">log<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">it is an Error<\/span><span>&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><span style=\"color: #E06C75;\"> else<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\tconsole<\/span><span>.<\/span><span style=\"color: #B57EDC;\">log<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">it is NOT an Error<\/span><span>&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ - do we get the custom type name and stack trace when it is thrown<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">throw<\/span><span style=\"color: #C6CCD7;\"> e<\/span><span>;<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ Sadly because of limitations of TypeScript I haven&#39;t found a good way yet<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ to meet the following criteria. <\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ - communicating to the user via the type system what the possible error types are<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ - requiring handling of all possible returned errors or explicitly declare not handling<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ The following are just explorations I started but didn&#39;t really go anywhere to meet those requirements<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ - communicating to the user via the type system what the possible error types are<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ typescript doesn&#39;t seem to complain even through I declared the possible<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ return types that I am returning a different error type. So not really<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ useful as a tool for the author of the function. Maybe it could be seen as<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ documentation for the consumer<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">function<\/span><span style=\"color: #B57EDC;\"> foo<\/span><span>()<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> Result<\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">any<\/span><span>,<\/span><span style=\"color: #E5C07B;\"> MyErrorClassA<\/span><span style=\"color: #E06C75;\"> |<\/span><span style=\"color: #E5C07B;\"> UnhandledError<\/span><span>&gt; {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\ttry<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\t\tconsole<\/span><span>.<\/span><span style=\"color: #B57EDC;\">log<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">in try<\/span><span>&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\treturn new<\/span><span style=\"color: #B57EDC;\"> Err<\/span><span>(<\/span><span style=\"color: #E06C75;\">new<\/span><span style=\"color: #B57EDC;\"> MyErrorClassB<\/span><span>(&#39;<\/span><span style=\"color: #98C379;\">error a<\/span><span>&#39;));<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><span style=\"color: #E06C75;\"> catch<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">e<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #E5C07B;\"> any<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\t\tconsole<\/span><span>.<\/span><span style=\"color: #B57EDC;\">log<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">in the catch<\/span><span>&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\treturn new<\/span><span style=\"color: #B57EDC;\"> Err<\/span><span>(<\/span><span style=\"color: #E06C75;\">new<\/span><span style=\"color: #B57EDC;\"> UnhandledError<\/span><span>(&#39;<\/span><span style=\"color: #98C379;\">message<\/span><span>&#39;,<\/span><span style=\"color: #C6CCD7;\"> e<\/span><span>));<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ - requiring handling of all possible returned errors or explicitly declare not handling<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ I don&#39;t believe there is anything in TypeScript to support exhaustive<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ handling of error types. I have contemplated having a pairing function with<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ each function that enforces this. However, there is nothing to help the<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ author of that function make sure they aren&#39;t forgetting any error types.<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ Also it doesn&#39;t help the use case when you are mapping errors in the middle<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ of a chain.<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ So I think for the time being unless we were to look into a TypeScript<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">\/\/ compiler extension we are stuck in this less than ideal world.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">let<\/span><span style=\"color: #C6CCD7;\"> result<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #B57EDC;\"> foo<\/span><span>();<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">if<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">result<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">ok<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\tconsole<\/span><span>.<\/span><span style=\"color: #B57EDC;\">log<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">result ok<\/span><span>&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><span style=\"color: #E06C75;\"> else<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\tconsole<\/span><span>.<\/span><span style=\"color: #B57EDC;\">log<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">result err<\/span><span>&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\tif<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">e<\/span><span style=\"color: #E06C75;\"> instanceof<\/span><span style=\"color: #E5C07B;\"> MyErrorClassA<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\t\tconsole<\/span><span>.<\/span><span style=\"color: #B57EDC;\">log<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">it is an instance of MyErrorClassA<\/span><span>&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><span style=\"color: #E06C75;\"> else<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\t\tconsole<\/span><span>.<\/span><span style=\"color: #B57EDC;\">log<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">it is NOT an instance of MyErrorClassA<\/span><span>&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\tif<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">e<\/span><span style=\"color: #E06C75;\"> instanceof<\/span><span style=\"color: #E5C07B;\"> MyErrorClassB<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\t\tconsole<\/span><span>.<\/span><span style=\"color: #B57EDC;\">log<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">it is an instance of MyErrorClassB<\/span><span>&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><span style=\"color: #E06C75;\"> else<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\t\tconsole<\/span><span>.<\/span><span style=\"color: #B57EDC;\">log<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">it is NOT an instance of MyErrorClassB<\/span><span>&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre><h2 id=\"envolving-classified-error-classes\">Envolving Classified Error Classes<\/h2>\n<p>Creating unique error classes for each specific function will result in a lot of different error classes. However, we need to make sure that we formalize explicitly all the possible errors that can result from a function. One obvious way to collapse the number of errors is to make Errors more generic. For example you could have a <code>FileNotFound<\/code> error for one function and you can also have another function use the same <code>FileNotFound<\/code> error. How do you know how to make an error generic or not or if you make it too generic.<\/p>\n<p>I have found that defining the error classes with their associated data helps a lot. It gives you a guide to understand if the associated data makes sense in the other generic cases as well. So the recommendation is to start wtih a specific error definition with associated data and then extrapolate from there into a generic type that can be reused.<\/p>\n<h2 id=\"faqs\">FAQs<\/h2>\n<p><strong>Is it common for nearly every function in an application to return a Result to handle potential errors? At which point, if any, would that become unnecessary? It seems like this has become the ideal solution for dealing with breaking errors in the Trans-Serv, but I don\u2019t want to find myself in a position of over-using or defaulting to Results if that is not generally the ideal way to design\/build applications.<\/strong><\/p>\n<p>In reference to, \"Is it common for nearly every function in an application to return a Result to handle potential error?\" The answer is generally yes it is common to use it everywhere and you generally should. The point at which it becomes un-necessary is the point at which an error is handled programmatically. Usually this is at the bounds of the application (e.g. user interface where error is presented to the user, or where an error is internally programmatically handled). You should be using Result whenever it is possible for the operation you are performing to fail. The only caveat to this is if you are in an async chain (aka a promise chain) and you want a function to throw an exception explicitly for being used in that context of an async chain. However, generally in these cases the async chain is basically mapped back to a result at the end anyways.<\/p>\n<p><strong>At what point does piping, mapping, or promise chaining become \u201ctoo large\u201d? Is the limit determined on the amount of the logic inside each consecutive call? For example, would it be easier to follow a larger number of chains if it\u2019s purely passing in the next function, as opposed to having chains where logic must be implemented beyond calling a single function (even if it is only a few lines)? Is the latter case a \u201csmell\u201d that the logic should be extracted into a new function to cater to the former?<\/strong><\/p>\n<p>In reference to \"At what point does piping, mapping, or promise chaining become \u201ctoo large\u201d?\" This is a subjective thing and hard to get away from it being subjective because the driver of it is readability and being able to easily understand what is going on in the chain. If I have to read through a chain of 20 different steps to understand what the entire chain is trying to accomplish it is too large. I should be able to quickly look at it and understand and somewhere around up to 5 steps or so is a good rule of thumb. It is worth noting though that if you have a really large chain, you should be collapsing multiple steps into a single step with a name that has meaning using pipe(). Having chains where you hand it a lambda is a smell. The ideal is to be in a situation where your chain is completely composed of named functions. This drastically aids with the understanding of the chain and its intent without having to worry about the details of understanding the implementations of those lambdas. If you feel like you have to do logic beyond a single function it is a \"smell\" that, that logic should be extracted out into a new function.<\/p>\n<p><strong>Ok so new question relating to error handling.  For the stream creation helper function, Jon suggested that we return a FileNotFound error to be specific if a file is not found.  The way our Error handling works currently, everything bubbles up to the TransactionServiceError class which then emits the error with an http error code - if I understand correctly.  If an internal function is using the helper function to transform a file path into a stream, we would want to throw a FileNotFound error - but it would not be an http error of 404, it would be an internal error.  When handled by the calling function it might be transformed into a 404, depending on the context (e.g. if it is interacting with the outside and needs to communicate that the file path is missing), or it might remain an internal matter to be passed along as a generic 500 error for the greater service.   So how do we handle this sort of instance?  I created a FileNotFound error so it would contain the message to be handled internally, and it could theoretically be caught and transformed into a TransactionServiceError code 404 by something that handles it...  but I am not sure if that is how we want to manage this?<\/strong><\/p>\n<p>Im not certain we want everything to be a TransactionServiceError right, since not everything has an http code. Functions can and should have their own errors to inform the caller of the status via Result. TransactionServiceError was designed in relation to Faktory Jobs consuming failures when calling routes, which is separate from this. The potential transformation is outside the scope of this context. This <code>FileNotFoundError<\/code> could be transformed by the caller (if needed).<\/p>\n<p><strong>I would ideally like to catch any errors that might occur when creating the stream - in a try catch or something similar.  Since it's a stream it is a bit more complex, since it has an event to listen to for errors .. which I assume the handler would want to handle in this case.  But in general, if I were to need to catch unexpected errors how would I manage an exception that doesn't conform to the Result signature here? Or should I use a more generic Result signature with Error as the Error type and just pass up exceptions as wrapped errors in the result, for e.g. (and the missing file error would still be of type FileNotFoundError)<\/strong><\/p>\n<p>The function should try to do its best to prevent rouge exceptions. What we have here in this example is good and if there is a rogue exception then the caller should handle it or not. I dont like the idea of a generic Error type in the signature since its unclear of the intent of the function. Rogue exceptions are well that, rouge. We should do our best and the example code above seems to do the best it can. I\u2019d say make a judgement call and we can go from there.<\/p>\n<p>The one addition I would add to that is that you can handle \"rouge\" \/ unhandled errors not by generalizing the return type to use Error but instead by formalizing the concept of Unhandled error as part of the valid error type in the return type.<\/p>\n"},{"title":"Uptech Studio Git Commit Standards","published":"2021-05-01T00:00:00+00:00","updated":"2021-05-01T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/uptech-git-commit\/"}},"id":"https:\/\/drewdeponte.com\/blog\/uptech-git-commit\/","content":"<p>The Git commit is probably the most fundamental unit we have to manage the growth and evolution of our source code. Git commits can be extremely useful sometimes and other times they can be useless. The following are the guidelines we have honed in on over the years to help make sure that our commits end up being the valuable kind.<\/p>\n<h2 id=\"commit-characteristics\">Commit Characteristics<\/h2>\n<p>To aid with making sure our commits are the valuable kind and ensure they play nicely in the <a rel=\"external\" href=\"https:\/\/git-ps.sh\">Git Patch Stack<\/a> and Continuous Integration methodologies we use the following characteristics as a checklist.<\/p>\n<ul>\n<li>logical<\/li>\n<li>buildable<\/li>\n<li>testable<\/li>\n<li>not-necessarily complete<\/li>\n<li>releasable<\/li>\n<\/ul>\n<h3 id=\"logical\">Logical<\/h3>\n<p><strong>Each commit should be a logical unit.<\/strong> This means it should include only changes related to that logical unit. This includes related code changes, automated test changes, tooling changes, etc. This is <strong>crucial<\/strong> to support the ability to easily revert changes when necessary. It is also crucial for aiding in doing any sort of git commit tree spelunking.<\/p>\n<h3 id=\"buildable\">Buildable<\/h3>\n<p><strong>Each commit should be buildable.<\/strong> This means that each commit must NOT break the build tooling or build process of the application. This is important to make sure that all the Git tooling (e.g. git bisect) continues to be able to be used. It also more clearly supports Git tree spelunking and hot fixing as no matter what commit you pick it should be in a buildable state.<\/p>\n<h3 id=\"testable\">Testable<\/h3>\n<p><strong>Each commit should be testable.<\/strong> This means that each commit must NOT break the automated testing tools or the tests. Similar to buildable this is important to make sure that all the Git tooling (e.g. git bisect) continue to work. It also helps with hot fixing, etc.<\/p>\n<h3 id=\"not-ncessarily-complete\">Not-Ncessarily Complete<\/h3>\n<p><strong>Each commit does NOT have to be complete.<\/strong> This means that you could for example have a commit that introduces a new method to an existing class. That is a complete logical change that is buildable and testable. But it isn't complete in terms of its interactions with other application architecture concepts. This is really just a reminder that we should <strong>NOT<\/strong> be including tagentially related changes into our logical commits.<\/p>\n<h3 id=\"releasable\">Releasable<\/h3>\n<p><strong>Each commit should be releasable.<\/strong> This means that once that commit is integrated into mainline. We should be able to cut a release of the application without a problem. So it shouldn't break the release process and it shouldn't leave the user experience of the application in a broken state. Most of the time you will find that commits can just naturally be releasable. But sometimes it means that you have to create initially unused alternate code paths or use feature flags\/toggles to manage code paths so that code can be integrated into mainline while still keeping the commit releasable.<\/p>\n<h2 id=\"git-patch-stack\">Git Patch Stack<\/h2>\n<p>To help us follow these best practices we have developed a workflow on top of Git called <a rel=\"external\" href=\"https:\/\/git-ps.sh\">Git Patch Stack<\/a> and built a small command line Git extension to help streamline it. You can get a deeper understanding of this workflow and how to use it via our blog posts, <a href=\"\/blog\/journey-to-small-pull-requests\/\">Journey to Small Pull Requests<\/a> and <a href=\"\/blog\/how-we-should-be-using-git\/\">How we should be using Git<\/a>.<\/p>\n<h2 id=\"a-communication-tool\">A Communication Tool<\/h2>\n<p>Just as import if not <em>more important<\/em> than the source code change is the commit message itself. This is the only mechanism a commit author has to communicate very important things like, <strong>what<\/strong>, <strong>intention<\/strong>, <strong>reason (a.k.a. why)<\/strong>, <strong>approach (a.k.a. how)<\/strong>, all crafted with <strong>context<\/strong>. Having this crucial information bound to the source code change is amazingly powerful and valuable when developing and maintaining an application over time.<\/p>\n<p>We have found it is also a useful communication tool for some automation tasks. Specifically for including Changelog entry information so that we don't create unnecessary conflicts in the commit itself. <a href=\"\/blog\/keep-a-changelog-without-conflicts\/\">Git Changelog<\/a> can then use this information to generate our Changelog files for us.<\/p>\n<h3 id=\"official-git-commit-message-format\">Official Git Commit Message Format<\/h3>\n<p>Git as a tool has guidelines for commit messages to work best with git and its various tools. This format is as follows:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>Short summary of change (50 charecters or less in length)<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>One ore more paragraphs of text hard wrapped at 72 characters in length.<\/span><\/span><\/code><\/pre><h3 id=\"our-git-commit-message-format\">Our Git Commit Message Format<\/h3>\n<p>We have built on the Official Git Commit Message Format to make sure we include this extremely important information. The following outlines our format.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>Short summary of change (the what) - 50 characters or less in<\/span><\/span>\n<span class=\"giallo-l\"><span>length<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>One or more paragraphs explaining the INTENTION &amp; REASON of the<\/span><\/span>\n<span class=\"giallo-l\"><span>change with valuable context. (Hard wrapped at 72 characters)<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>One or more paragraphs explaining the APPROACH taken to attempt to achieve<\/span><\/span>\n<span class=\"giallo-l\"><span>the INTENTION or a portion of the INTENTION.<\/span><\/span>\n<span class=\"giallo-l\"><span>(Hard wrapped at 72 characters)<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>Associated Ticket Identifers<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>Git Changelog header and entries if applicable<\/span><\/span><\/code><\/pre><h3 id=\"examples\">Examples<\/h3>\n<h4 id=\"definitely-not-what-we-are-looking-for\">Definitely <strong>NOT<\/strong> what we are looking for<\/h4>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>Intergrate Sentry for crash handling<\/span><\/span><\/code><\/pre>\n<p>or even worse<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>Update project.pbxproj<\/span><\/span><\/code><\/pre>\n<p>Picture someone throwing this commit over a wall to you with no other information and they asked you to review it. What would you actually be able to say about it other than what is already included in the diff. You could maybe call out a small syntactical, style, or maybe some logic things strictly based on the diff itself. But you wouldn't be able to provide any value in the peer review on if the changes meet the <strong>intent<\/strong> because you don't know what the <strong>intent<\/strong> is. You can't analyze the <strong>approach<\/strong> and provide valuable analysis of it, nor can you understand if the change is the right change to make to reach the over arching <strong>reason<\/strong> for the change. We don't even know from the short summary provided what in the project.pbxproj file changed. As a result you'd have to read the entire diff of changes and infer the intent, approach, and reason.<\/p>\n<p>Making logical, purposeful, buildable, testable, not-necessarily complete, but releasable commits with verbose commit messages may seem like overkill, but it becomes immensely useful for all the reasons stated in this guide, and more important when you no longer have access to the original developer who made the change! There are even benefits seen when the developer can't recall the intent, approach, and reason of changes that were made only a couple months ago.<\/p>\n<h4 id=\"what-we-are-looking-for\">What we are looking for<\/h4>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>Intergrate Sentry for crash handling<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>We did this because we previously had to get rid of MS AppCenter because<\/span><\/span>\n<span class=\"giallo-l\"><span>it wasn&#39;t compatible with mac Catalyst. The belief is that Sentry is<\/span><\/span>\n<span class=\"giallo-l\"><span>actually compatible and may be a better solution for us with the<\/span><\/span>\n<span class=\"giallo-l\"><span>need to support mac Catalyst. Also, living with out crash reporting is<\/span><\/span>\n<span class=\"giallo-l\"><span>just not smart.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>This involved making a build configuration change to the Debug<\/span><\/span>\n<span class=\"giallo-l\"><span>Information Format changing it from `dwarf` to `dwarf-with-dysm` so that<\/span><\/span>\n<span class=\"giallo-l\"><span>we could also add a build phase script that would upload the build&#39;s<\/span><\/span>\n<span class=\"giallo-l\"><span>dsyms to Sentry so that it could symbolic the backtraces.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>Beyond that we registered the sentry API tokens and enabled the crash<\/span><\/span>\n<span class=\"giallo-l\"><span>handler at app launch. Note: We did this with two different Sentry<\/span><\/span>\n<span class=\"giallo-l\"><span>projects, one for the mac Catalyst build and the other for the iOS<\/span><\/span>\n<span class=\"giallo-l\"><span>(iPad\/iPhone) build. This is necessary because they are separate builds<\/span><\/span>\n<span class=\"giallo-l\"><span>that get deployed separately.<\/span><\/span><\/code><\/pre><h3 id=\"commit-message-template\">Commit Message Template<\/h3>\n<p>If you are having difficulties remembering the git commit message format and what you need to provide in your message you are in luck. Git provides a commit message template feature. You can set it up with the follownig steps.<\/p>\n<ol>\n<li>Create your template file in a safe place with the following content. Common practice is to put it in <code>~\/.gitmessage<\/code>.<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Short summary of change (the what) ( &lt;= 50 chars )<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># INTENTION of the change &amp; REASON for change<\/span><\/span>\n<span class=\"giallo-l\"><span># (Hard wrapped at 72 chars) <\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># APPROACH taken &amp; WHY in relation to attempting to achieve INTENTION<\/span><\/span>\n<span class=\"giallo-l\"><span># (Hard wrapped at 72 characters)<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Deployment or Rollout Considerations<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Automated Tests considerations - if **NOT** why?<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Associated Ticket Identifers<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span># Git Changelog header and entries if applicable<\/span><\/span>\n<span class=\"giallo-l\"><span># [changelog]<\/span><\/span>\n<span class=\"giallo-l\"><span># added: some addition you made that you want in your changelog<\/span><\/span>\n<span class=\"giallo-l\"><span># changed: some change you made that you want in your changelog<\/span><\/span>\n<span class=\"giallo-l\"><span># deprecated: some deprecation notice you want in your changelog<\/span><\/span>\n<span class=\"giallo-l\"><span># removed: some removal you want in your changelog<\/span><\/span>\n<span class=\"giallo-l\"><span># fixed: some fix you want in your changelog<\/span><\/span>\n<span class=\"giallo-l\"><span># security: some security fix you want in your changelog<\/span><\/span><\/code><\/pre><\/li>\n<li>Configure Git to use your new commit message template<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>$ git config --global commit.template ~\/.gitmessage<\/span><\/span><\/code><\/pre><\/li>\n<li>Make commits <strong>without<\/strong> (<code>-m<\/code>) and it will bring up the commit message template in your editor.<\/li>\n<\/ol>\n"},{"title":"Uptech Studio Peer Reviews","published":"2021-05-01T00:00:00+00:00","updated":"2021-05-01T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/uptech-peer-reviews\/"}},"id":"https:\/\/drewdeponte.com\/blog\/uptech-peer-reviews\/","content":"<p>The intent of this document is to provide a reference document for the Peer\nReview Process that we follow.<\/p>\n<h2 id=\"goals\">Goals<\/h2>\n<p>The following are the main intentions of this process.<\/p>\n<ul>\n<li>improve code maintainability<\/li>\n<li>reduce defects in production<\/li>\n<li>developer growth<\/li>\n<\/ul>\n<h2 id=\"participants\">Participants<\/h2>\n<p>All developers, no matter skill level, should participate in the peer\nreview process. If only a subset of the team does the peer reviews, this bogs\ndown the more senior people with a disproportionate amount of work. The senior\npeople usually are already spending time helping others, so we don't want to put\neven more work on them. Additionally junior people can benefit from looking at\nsenior developer's work. They can see what the code should be like and learn\ntips\/tricks.<\/p>\n<h2 id=\"mindset\">Mindset*<\/h2>\n<p>Please be open to suggestions from your peer reviewers and don't take things\npersonally. On the flip side, please be aware that someone has put in a lot of\neffort into their code and sometimes there can be multiple viewpoints. In order\nto have a better collaborative experience for all people involved and still\nachieve the stated goals, please consider the following suggestions:<\/p>\n<ul>\n<li><strong>Ask questions:<\/strong> How does this method work? If this requirement changes,\nwhat else would have to change? How could we make this more maintainable?<\/li>\n<li><strong>Compliment \/ reinforce good practices:<\/strong> One of the most important parts of\nthe code review is to reward developers for growth and effort. Few things feel\nbetter than getting praise from a peer. Try to offer as many positive\ncomments as possible.<\/li>\n<li><strong>Discuss directly for more detailed points:<\/strong> On occasion, a recommended\narchitectural change might be large enough that it\u2019s easier to discuss it in\nperson rather than in the comments. Similarly, if you are discussing a point\nand it goes back and forth, just pick it up in person and finish out the\ndiscussion.<\/li>\n<li><strong>Explain reasoning:<\/strong> It\u2019s best both to ask if there\u2019s a better alternative\nand justify why you think it\u2019s worth fixing. Sometimes it can feel like the\nchanges suggested can seem nit-picky without context or explanation.<\/li>\n<li><strong>Make it about the code:<\/strong> It\u2019s easy to take notes from code reviews\npersonally, especially if we take pride in our work. It\u2019s best to make\ndiscussions about the code than about the developer. It lowers resistance and\nit\u2019s not about the developer anyway, it\u2019s about improving the quality of the\ncode.<\/li>\n<li><strong>Suggest importance of fixes:<\/strong> Not all suggestions made need to be acted\nupon. Clarifying if an item is important to fix before it can be considered\ndone is useful both for the reviewer and the reviewee. It makes the results of\na review clear and actionable.<\/li>\n<\/ul>\n<h2 id=\"process\">Process<\/h2>\n<h3 id=\"developer-responsibilities\">Developer Responsibilities<\/h3>\n<ul>\n<li>Developer makes a patch<\/li>\n<li>Developer runs all automated test suites and makes sure it's green, or\nrelies on CI server to do so and awaits green response.<\/li>\n<li>Developer manually tests the feature to see if acceptance, functional, ux,\nand ui criteria are met.<\/li>\n<li>Deveolper Requests Review of their patch\n<ul>\n<li><em>Note:<\/em> at this point the pull request should meet the <a href=\"\/blog\/uptech-pull-request\/\">Pull Request Standards<\/a><\/li>\n<li>If a patch is not complete, but you want feedback on it earlier in the\nprocess, you can Request Review of it, but indicate clearly in the title\nand description that the patch is not complete and not ready to be\nmerged and what you're looking for feedback on.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h3 id=\"reviewer-responsibilities\">Reviewer Responsibilities<\/h3>\n<ul>\n<li>Peer Reviewer assigns themselves to the pull request.<\/li>\n<li>Peer Reviewer checks out the code<\/li>\n<li>Peer Reviewer runs automated tests, if they fail, deny the pull request.<\/li>\n<li>Peer Reviewer does manual testing\n<ul>\n<li>Does it meet the acceptance criteria identified in the ticket?<\/li>\n<li>Are there any performance issues?<\/li>\n<\/ul>\n<\/li>\n<li>Peer Reviewer reads through the diff and comments on it. (see What to look\nfor when reviewing below)<\/li>\n<li>Peer Reviewer approves\/rejects the PR depending on their findings.<\/li>\n<\/ul>\n<h3 id=\"more-developer-responsibilities\">More Developer Responsibilities<\/h3>\n<ul>\n<li>Once approved, the Developer should then publish the patch to close the\npull request and integrate the code. The peer reviewer should <strong>NOT<\/strong> merge\nthe code as the developer knows best if it should be integrated into mainline\nor not.<\/li>\n<li>Developer should cleanup their remote branch associated with their PR.<\/li>\n<\/ul>\n<h2 id=\"what-to-look-for-when-reviewing\">What to look for when reviewing*<\/h2>\n<h3 id=\"high-level\">High Level<\/h3>\n<ul>\n<li><strong>Don't Repeat Yourself (DRY):<\/strong> If you see a block of code that is\nduplicated, wrap it in a function and re-use it.<\/li>\n<li><strong>Code left in a better state than found:<\/strong> If you are changing an area of the\ncode that\u2019s messy, it\u2019s tempting to add in a few lines and leave. Go one step\nfurther and leave the code nicer than it was found.<\/li>\n<\/ul>\n<h3 id=\"style\">Style<\/h3>\n<ul>\n<li><strong>Commented code:<\/strong> Good idea to remove any commented out lines.<\/li>\n<li><strong>Function length:<\/strong> Rule of thumb is that a function should be somewhere\naround 1-10 lines in length. If you see a method above 10 lines, it\u2019s likely\nneeds to be broken down into smaller pieces<\/li>\n<\/ul>\n<h3 id=\"testing\">Testing<\/h3>\n<ul>\n<li><strong>Test coverage:<\/strong> All new features and bug fixes should have tests. Are the\ntests thoughtful? Do they cover the failure conditions? Are they easy to read?\nHow fragile are they? How big are the tests? Are they slow?<\/li>\n<li><strong>Number of Mocks:<\/strong> Mocking is great. Mocking everything is not great. Use a\nrule of thumb where if there\u2019s more than 3 mocks in a test, it should be\nrevisited. Either the test is testing too broadly or the function is too\nlarge. Maybe it doesn\u2019t need to be tested at a unit test level and would\nsuffice as an integration test. Either way, it\u2019s something to discuss.<\/li>\n<li><strong>Meets requirements:<\/strong> Take a look at the requirements of the story, task, or\nbug which the work was filed against. If it doesn\u2019t meet one of the criteria,\nit\u2019s better to bounce it back before it goes to QA.<\/li>\n<\/ul>\n<p>* some items taken from: <a rel=\"external\" href=\"http:\/\/kevinlondon.com\/2015\/05\/05\/code-review-best-practices.html\">Code Review BestPractices<\/a><\/p>\n"},{"title":"Uptech Studio Process","published":"2021-05-01T00:00:00+00:00","updated":"2021-05-01T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/uptech-process\/"}},"id":"https:\/\/drewdeponte.com\/blog\/uptech-process\/","content":"<h1 id=\"discovery-definition\">Discovery &amp; Definition<\/h1>\n<p>One of the first phases we go through in a project is what we refer to in the engineering space as <strong>Discovery &amp; Definition<\/strong>.<\/p>\n<p>The goal of this phase is to get a decent understanding of what the project should be and what the initial scope of the project should be.<\/p>\n<p>This is generally done through a series of meetings between the stake holders and Uptech Studio Product, Engineering, and Design representatives. This is where we combine what we know the client wants with our knowledge and expertise to eventually result in what we are actually going to build for them. This is also often where we pose questions or push back on the client to identify if ideas have been vetted and tested to make sure that their money will be well invested.<\/p>\n<p>Once we have these meetings and have come to a conclusion as to what the product should be and what our initial scope will be we move onto the next phase, Estimations.<\/p>\n<h1 id=\"estimations\">Estimations<\/h1>\n<p>Prior to this Product is responsible for outlining User Stories based on the learnings from the Discovery &amp; Definition phase.<\/p>\n<p>Here at Uptech Studio we aren't big believers in estimates. In fact we generally don't believe or trust in estimates. But our clients need to be able to budget and plan, not down to a date or week even. But they need to have an understand of relatively large time range and budget range to find out if they can even afford to pay us and if the timeline will work for their business need.<\/p>\n<p>So we use this as an opportunity to have the Tech Lead start to think through all the stories and what is going to be involved with them and estimate the story at the <strong>scale of indivdual developer worker days<\/strong>. This is generally done in Airtable.<\/p>\n<p>We then sum up all the working days and add a <strong>multiplier<\/strong> based on confidence and unknows using around <strong>1.5<\/strong> and <strong>2<\/strong> to account for the fact that estimates are generally over optimistic.<\/p>\n<p>These numbers can then be used to compute roughly time range and cost range based on these estimates.<\/p>\n<h1 id=\"project-planning\">Project Planning<\/h1>\n<p>The <strong>Project Planning<\/strong> phase is where we take the knowledge we gained around the project scope and start planning out the high level technical details around the work. The goal of this phase is to do an initial relatively high level of planning to start to breaking down the technical problem space and identify unknowns, concerns, and blockers. This is generally achieved with the following artifacts of this process which are the responsibility of the Tech Lead to produce.<\/p>\n<ul>\n<li>Systems Architecture<\/li>\n<li>Project Detailed Spec<\/li>\n<li>Defining Unknowns<\/li>\n<\/ul>\n<h2 id=\"systems-architecture\">Systems Architecture<\/h2>\n<p>The first artifact for this phase is a Systems Level Architecture diagram that the Tech Lead is responsible for putting together. The goal of this is to simply start the conversation and discussion around all the entities in play at a system level and how they are going to interact with eachother. Over time this continues to be updated and used as a conversation piece to discuss and review systems level interactions.<\/p>\n<h2 id=\"project-detailed-spec\">Project Detailed Spec<\/h2>\n<p>The Project Detailed Spec is a document that the Tech Lead is again responsible for. This document defines the project scope and goals for review as well as the Systems Architecture to provide context. Beyond that it is a vehicle to explore the next tier down of technical problem solving. This could be how the various components would be interacting with each other. Is it done via GraphQL, REST, etc? What are some rough ideas of what the key functional data is needed to be passed between these system components. These types of questions and thinking. It isn't to formalize 100% exactly what the payloads will look like. It is just to get an idea and an understanding to gain more confidence.<\/p>\n<p>The process of the Tech Lead walking through, planning this out and documenting it helps them start to think through all of the components and how they will interact to accomplish the defined goals. This also helps identify up front major unknowns, concerns, and blockers very early on in the project so that those things can be focused on from the get go.<\/p>\n<h2 id=\"project-design-review\">Project Design Review<\/h2>\n<p>Once the Project Detailed Spec is produced the Tech Lead then schedules a <strong>Project Design Review<\/strong> internal to Uptech Studio. This meeting enables a round of review and feedback on the architecture and design that is documented in the Project Detailed Spec. This generally should have a representative from Product, Design, and Engineering. The people at this point aren't from your Squad.  As a Tech Lead you can choose to do Squad internal rounds of feedback with the engineers prior to this meeting.<\/p>\n<h2 id=\"project-kick-off\">Project Kick-off<\/h2>\n<p>Once the Tech Lead has held the Project Design Review, gotten feedback, and integrated that feedback into their Systems Level Architecture diagram and Project Detailed Spec they then schedule a <strong>Project Kick-off<\/strong> meeting with their Squad. This involves all members of the Squad and provides in opportunity to go over everything covered in the Detailed Spec from the project Goals down to how the various components will be interacting and with what data.<\/p>\n<p>The purpose of this meeting is for all the members to get comfortable with all of this and voice any concerns, suggestions, etc.<\/p>\n<h2 id=\"upfront-planning\">Upfront Planning?<\/h2>\n<p>I thought Uptech Studio did Agile. We do! Nothing in Agile says that you shouldn't do up front planning. Agile is all about iterating and getting feedback throughout the process and adjusting to that feedback. But nothing in it says you shouldn't think about how you would build or implement something. Our experience over the years has informed our opinion that doing a small amount of up front planning like this is often a game changer in terms of the success of a project.<\/p>\n<h1 id=\"design-review\">Design Review<\/h1>\n<p>This document describes how and when a software design review should take place.<\/p>\n<h2 id=\"what-is-it\">What is it?<\/h2>\n<p>A design review is an opportunity to get feedback on your application design prior to spending a\nlot of time working on the implementation.<\/p>\n<p>Even if you love to do emergent design using TDD, when projects are large and complex, some\nupfront design is necessary. It's hard to identify all the components that exist and what their\ninteractions are with existing systems without taking some time to think through it. It's also\nreally helpful so you can break down a larger project into smaller components.<\/p>\n<p>As a general rule of thumb if a project is going to take <strong>more than a week<\/strong> to develop, then it\nprobably should have a design review. Smaller projects will have real quick meetings, whereas\nlarger projects will probably take some serious brain power.<\/p>\n<h2 id=\"when-does-it-take-place\">When does it take place?<\/h2>\n<p>A design review should happen after the requirements have been documented and delivered to the\nimplementation team, but before any code has been written.<\/p>\n<p>Ideally the product owner will have a kickoff meeting with the team to go over this new feature\nand how it should work. After that, the engineers can review the requirements in detail and start\ncoming up with an implementation plan.<\/p>\n<p>Within a few days of the kickoff meeting, the engineers should have a design review meeting to go\nover the plan.<\/p>\n<h2 id=\"who-should-be-part-of-it\">Who should be part of it?<\/h2>\n<p>The technical lead of the project is responsible for scheduling time to plan and document the\nproposed design. They are also responsible for scheduling the review meeting and making sure they\nhave proper representation at the meeting.<\/p>\n<p>A design review should have the following people:<\/p>\n<ul>\n<li>Technical Lead<\/li>\n<li>Backend Representative<\/li>\n<li>Front-end Representative<\/li>\n<li>Infrastructure Representative<\/li>\n<li>QA Representative (Ideally the person that will be testing this feature)<\/li>\n<\/ul>\n<p>Including members from other teams helps to address any concerns they may have earlier in the\nprocess as opposed to later when it's more expensive to change.<\/p>\n<h2 id=\"how-does-the-meeting-run\">How does the meeting run?<\/h2>\n<p>During the meeting, the technical lead on the project should review the feature being implemented\nat a high level so everyone in the room understands what the business objectives are. After that,\nthey should walk through a document detailing the implementation plan.<\/p>\n<p>The document should include the following:<\/p>\n<ul>\n<li>A high level description<\/li>\n<li>A diagram of the different systems\/components<\/li>\n<li>A sequence diagram indicating interactions between components\/systems<\/li>\n<li>Interface specifications (REST API, Message Formats, etc.)<\/li>\n<li>Callout for new Infrastructure components<\/li>\n<li>Rollout plan - call out any sequencing required<\/li>\n<\/ul>\n<h2 id=\"why-should-we-do-this\">Why should we do this?<\/h2>\n<p>Because we want to build awesome products with the highest standards in mind and avoid making\ndesicions that may bite us in the future.<\/p>\n<h1 id=\"roadmapping\">Roadmapping<\/h1>\n<p>The <strong>Roadmapping<\/strong> phase is where the Tech Lead and the Product representative collaborate together to define a Roadmap of Milestones for the project. During this process we take a number of things into account.<\/p>\n<ul>\n<li>technical dependencies &amp; constraints<\/li>\n<li>best way to show client progress without fabricating Demos or doing Demo Driven Development.<\/li>\n<li>make sure we can internally deliver testable versions to stakeholders<\/li>\n<\/ul>\n<p>This is done in Jira using the Roadmap feature. The high level Milestones are broken out and defined &amp; prioritized first based on the criteria above in collaboration with Product.<\/p>\n<p>The Product person then takes the User Stories from the Estimations phase, cleans them up (adding any new ones, adding details to stories, etc.), and organizes them into the roadmapped milestones.<\/p>\n<h1 id=\"milestone-planning\">Milestone Planning<\/h1>\n<p>The <strong>Milestone Planning<\/strong> process is basically the same process that we do in terms of <strong>Project Planning<\/strong>. The main difference is that we are planning within the scope of a defined <strong>Milestone<\/strong>. This process is again driven by the Tech Lead. It can be thought of in the following sections.<\/p>\n<h2 id=\"scoped-systems-architecture-diagram\">Scoped Systems Architecture Diagram<\/h2>\n<p>When doing Milestone Planning you are scoping your thought process and in turn your created artifacts to a particular milestone. Therefore, you are creating a System Architecture Diagram that only focuses on the needs for this particular milestone. This is useful again to provide context but also useful to remove the noise of everything outside of the scope of this Milestone.<\/p>\n<h2 id=\"application-architecture\">Application Architecture<\/h2>\n<p>The Application Architecture is where you start to visually layout how your team is going to build an application that will meet the needs of the goals. This again is about components, how data flows through them, and what components handle different types of processing.<\/p>\n<h2 id=\"scoped-protocol-specifications\">Scoped Protocol Specifications<\/h2>\n<p>These protocol specifications again aren't inteded to be 100% accurate in terms of every piece of data. It is intended to be a tool to make the Tech Lead think through the core data that needs to be exchanged via this different channels and what those protocols would look like.<\/p>\n<h2 id=\"infrastructure-diagram-depends-on-need\">Infrastructure Diagram (depends on need)<\/h2>\n<p>Infrastructucture Diagrams aren't always necessary. Basically if there is something complex and abnormal necessary in the infrastructure then we should create an infrastructure diagram. The other use case might be that a client is explicitly requesting or pushing for an Infrastructure Diagram.<\/p>\n<h2 id=\"sequence-diagram-depends-on-need\">Sequence Diagram (depends on need)<\/h2>\n<p>A Sequence Diagram is again dependent. If you are in a situation where you need to clearly define and communicate about complex interactions between system level or software level components a Sequence Diagram is the tool for you.<\/p>\n<h2 id=\"flow-chart-depends-on-need\">Flow Chart (depends on need)<\/h2>\n<p>Flow Charts again aren't required but are super useful when you have some complex logic that needs to be planned out and thought out.<\/p>\n<h2 id=\"detailed-design-spec\">Detailed Design Spec<\/h2>\n<p>The Tech Lead then puts together a Detailed Design Spec similar to the concept and structure of the Project Design Spec from before except scoped down to the concerns of this one milestone.<\/p>\n<h2 id=\"unknowns\">Unknowns<\/h2>\n<p>Part of this process is identifying unknowns. We use these unknowns to guide us by effectively being blockers that need to be figured out to complete this milestone. We need to try and answer these unknowns as much as possible prior to the design review. However, some unknowns might be dependent on getting feedback from 3rd party vendors, etc. We should start those conversations with product and the 3rd party vendor ASAP, prior to the design review.<\/p>\n<p>This list of unknowns should be a working list of blockers that should get formalized as tickets and prioritized generally to be figured out ASAP. There are sometimes cases where you might want to not prioritize resolving one of these unknowns until later. This should be vetted with the project's principal engineer.<\/p>\n<h2 id=\"design-review-1\">Design Review<\/h2>\n<p>Once completed the Tech Lead schedules a design review (same process as before) except this time it happens within the Squad. If the Squad feels they need external imput from people outside the Squad they are welcome to invite other people.<\/p>\n<h2 id=\"kick-off-meeting\">Kick-off Meeting<\/h2>\n<p>Once the Design Review process is complete. The Tech Lead schedules a kick-off meeting for the Squad. This has the same purpose as before. It is to get everyone comfortable with everything and provide a venue for people to raise concerns, ask questions, etc.<\/p>\n<h2 id=\"task-breakout\">Task Breakout<\/h2>\n<p>One thing that different Squads tend to do differently is task breakout. Some Squads have an extra step here after the kick-off meeting where they schedule a meeting with Squad Product representative and the Engineers on the Squad and go through the tickets in the Milestone and breakout sub-tasks for each of the stories.<\/p>\n<p>Other Squads don't do this meeting and instead leave the task breakout to the Engineer that grabs the story. We don't have strong opinions on this. It is really whatever the Squad is comfortable with. The key is that our Squads are constantly self evaluating so if there is a problem in this area they can quickly adjust.<\/p>\n<h1 id=\"milestone-estimations\">Milestone Estimations<\/h1>\n<p>Despite our dislike for estimates due to their inaccurate nature we still do\nthem in some form because the reality of the situation is that we need to know\nrelatively early in the process if we are going to be able to hit a timeline or\nnot. Obviously we can't know this 100% because as we have stated estimates are\nvery inaccurate. However, they can gives us a pulse to know if we\nthink we are significantly going to miss a timeline. This is crucial as that\npulse is what instigates pruning of non-crucial features and setting\nexpectations appropriately with the client.<\/p>\n<p>To facilitate this we of course start with the initial estimates from the\nDiscovery &amp; Definition phase. However, things have likely changed since that\ntime and the developers haven't thought about the complexities of each of those\nmilestones.<\/p>\n<p>To facilitate this when a Journey is reviewed by product with the associated\ndev team. The dev team goes back through the milestones of that Journey and\nputs together estimates based on the following thinking.<\/p>\n<h3 id=\"origination-of-thinking\">Origination of Thinking<\/h3>\n<p>Historically we have focused on the concepts of <strong>Complexity<\/strong> and\n<strong>Effort<\/strong> while putting together estimates and it was only our\nMilestone Planning that thought about unknowns and risk.<\/p>\n<p>However, relatively recently Theo dropped a video where he broke down what he\nhas thought of for a system for estimates. It is very much in alignment with\nour current approach as it takes <strong>Complexity<\/strong> and <strong>Effort<\/strong> into\nconsideration. However, beyond that it takes into account <strong>Risk<\/strong> and goes\nthrough the reasons for estimating in a more analytical fashion. Therefore, we\nhave effectively adopted his approach with some small tweaks.<\/p>\n<p>We also differ from his approach in that after thinking through these\ncharacteristics we then estimate how long things will take (in developer days)\nusing these characteristics guidence. This is critical for us to understand how\nwe are making progress in terms of our estimates so that we can make\nadjustments and manage client expectations appropriately.<\/p>\n<p><a rel=\"external\" href=\"https:\/\/www.youtube.com\/watch?v=XhUAIVJ62dQ\">Theo - Why Are Estimations So Hard??? PLANNING TASKS AS AN ENGINEER<\/a><\/p>\n<h2 id=\"why-we-estimate\">Why we estimate?<\/h2>\n<p>We do estimates at the epic level for the following reasons.<\/p>\n<ul>\n<li>Identify who should do it<\/li>\n<li>Identify when it should be done<\/li>\n<li>Identify low performers<\/li>\n<li>Bring risk to the forefront so that it can be addressed\/mitigated<\/li>\n<li>Prioritize based one ^<\/li>\n<li>Identify how long it will take (this will be wrong, but hopefully taking the following system into account will help make it less wrong)<\/li>\n<\/ul>\n<h2 id=\"the-breakdown\">The Breakdown<\/h2>\n<p>Our estimation system is broken down into 3 categories, Complexity,\nEffort, and Risk.<\/p>\n<ul>\n<li><strong>Complexity<\/strong> (1-10) - How \"hard\" is this thing to do?<\/li>\n<li><strong>Effort<\/strong> (1-10) - How much work does it take?<\/li>\n<li><strong>Risk<\/strong> (1-10) - What are the chances that Complexity or Effort is off?<\/li>\n<\/ul>\n<h2 id=\"getting-started\">Getting Started<\/h2>\n<p>When first getting a team started thinking in this way we have found that it is\nuseful to often go down to the story level and document the <strong>Complexity<\/strong>,\n<strong>Effort<\/strong>, and <strong>Risk<\/strong> as it ends up being a forcing function to think about\nthe complexities and risk. Then go back up to the milestone and produce the\nmilestones <strong>Complexity<\/strong>, <strong>Effort<\/strong>, and <strong>Risk<\/strong>. Then you can look at the\nstories overall and validate that your estmiate for the milestone makes sense.<\/p>\n<p>Once the devs start to get the feeling for thinking about <strong>Complexity<\/strong>,\n<strong>Effort<\/strong>, and <strong>Risk<\/strong> this process goes really quickly.<\/p>\n<h2 id=\"breaking-out-research-tasks\">Breaking out Research Tasks<\/h2>\n<p>As we go through this process we are often running into <strong>Risks<\/strong> and most of\nthe time you need to do some further research to understand and mitigate the\nrisk. This is the perfect time to break those tasks out.<\/p>\n<p>You can also see by doing so how breaking the research task out helps you to\nthink about and adjust the characteristics for the story and its associated\nresearch task.<\/p>\n<h2 id=\"example\">Example<\/h2>\n<p>The following are a couple examples from the video referenced above with the addition of the developer days estimates.<\/p>\n<p>Display voice levels in pre-call view<\/p>\n<ul>\n<li><strong>Complexity<\/strong>: 4\/10<\/li>\n<li><strong>Effort<\/strong>: 2\/10<\/li>\n<li><strong>Risk<\/strong>: 6\/10<\/li>\n<li><strong>Estimate<\/strong>: 4 dev days<\/li>\n<\/ul>\n<p>To reduce the risk of this task we could create a research task to figure out\none of the bigger pieces of risk. For example.<\/p>\n<p>Identify how to get voice levels from 3p SDK<\/p>\n<ul>\n<li><strong>Complexity<\/strong>: 2\/10<\/li>\n<li><strong>Effort<\/strong>: 2\/10<\/li>\n<li><strong>Risk<\/strong>: 4\/10<\/li>\n<li><strong>Estimate<\/strong>: 1 dev day<\/li>\n<\/ul>\n"},{"title":"Uptech Studio README.md Standards","published":"2021-05-01T00:00:00+00:00","updated":"2021-05-01T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/uptech-readme\/"}},"id":"https:\/\/drewdeponte.com\/blog\/uptech-readme\/","content":"<p>We require <code>README.md<\/code> files in our repositories so that people can understand the following.<\/p>\n<ul>\n<li>what the repository is<\/li>\n<li>how it fits into the bigger context<\/li>\n<li>how to get the development environment up and running<\/li>\n<li>how to contribute to it<\/li>\n<li>how to run the test suites<\/li>\n<li>how to deploy it.<\/li>\n<\/ul>\n<h2 id=\"readme-md-content\">README.md Content<\/h2>\n<p>The following is an example of a <code>README.md<\/code> file and its content. Use this as a template.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"markdown\"><span class=\"giallo-l\"><span>#<\/span><span style=\"color: #E5C07B;\"> git-ps - Git Patch Stack<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>Git patch stack is an add-on command for Git that facilitates using a patch-stack [<\/span><span style=\"color: #98C379;\">Git<\/span><span>][] workflow while still using pull requests for peer review. It accomplishes this by managing your stack of patches and behind the scenes creating branches for patches you request review on and pushing them up to origin.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>It consists of the following commands:<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>- `<\/span><span style=\"color: #98C379;\">git-ps ls<\/span><span>` to list the stack of patches<\/span><\/span>\n<span class=\"giallo-l\"><span>- `<\/span><span style=\"color: #98C379;\">git-ps show &lt;patch-index&gt;<\/span><span>`to show the patch diff and details<\/span><\/span>\n<span class=\"giallo-l\"><span>- `<\/span><span style=\"color: #98C379;\">git-ps pull<\/span><span>` to fetch the state of origin\/master and rebase the stack of patches onto it<\/span><\/span>\n<span class=\"giallo-l\"><span>- `<\/span><span style=\"color: #98C379;\">git-ps rebase<\/span><span>` to interactive rebase the stack of patches<\/span><\/span>\n<span class=\"giallo-l\"><span>- `<\/span><span style=\"color: #98C379;\">git-ps rr &lt;patch-index&gt;<\/span><span>` to request review of the patch or update existing request to review<\/span><\/span>\n<span class=\"giallo-l\"><span>- `<\/span><span style=\"color: #98C379;\">git-ps pub &lt;patch-index&gt;<\/span><span>` to publish a patch into upstream&#39;s mainline (aka origin\/master)<\/span><\/span>\n<span class=\"giallo-l\"><span>- `<\/span><span style=\"color: #98C379;\">git-ps --version<\/span><span>` to output the version of information for reference &amp; bug reporting<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>##<\/span><span style=\"color: #E5C07B;\"> Installation<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>If you are on a platform other than macOS you will have to build your own<\/span><\/span>\n<span class=\"giallo-l\"><span>version from source.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>###<\/span><span style=\"color: #E5C07B;\"> macOS<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>To install on macOS we provide a [<\/span><span style=\"color: #98C379;\">Homebrew<\/span><span>](http:\/\/brew.sh) tap which provides<\/span><\/span>\n<span class=\"giallo-l\"><span>the `<\/span><span style=\"color: #98C379;\">git-ps<\/span><span>` formula. You can use it by doing the following:<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>####<\/span><span style=\"color: #E5C07B;\"> Add the Tap<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\tbrew tap &quot;uptech\/homebrew-oss&quot;<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>####<\/span><span style=\"color: #E5C07B;\"> brew install<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\tbrew install uptech\/oss\/git-ps<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>###<\/span><span style=\"color: #E5C07B;\"> Build from Source<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>If you are on another platform you will have to build from source. Given<\/span><\/span>\n<span class=\"giallo-l\"><span>that `<\/span><span style=\"color: #98C379;\">git-ps<\/span><span>` is managed via [<\/span><span style=\"color: #98C379;\">GNU make<\/span><span>][]. It can be built as follows:<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\t$ make build<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>Once you have built it successfully you can install it in `<\/span><span style=\"color: #98C379;\">\/usr\/local\/bin<\/span><span>` using the following:<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\t$ make install<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>##<\/span><span style=\"color: #E5C07B;\"> Development<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>We use [<\/span><span style=\"color: #98C379;\">GNU make<\/span><span>][] to manage the developer build process with the following commands.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>- `<\/span><span style=\"color: #98C379;\">make build<\/span><span>` - build release version of the `<\/span><span style=\"color: #98C379;\">git-ps<\/span><span>`<\/span><\/span>\n<span class=\"giallo-l\"><span>- `<\/span><span style=\"color: #98C379;\">make install<\/span><span>` - install the release build into `<\/span><span style=\"color: #98C379;\">\/usr\/local\/bin<\/span><span>`<\/span><\/span>\n<span class=\"giallo-l\"><span>- `<\/span><span style=\"color: #98C379;\">make uninstall<\/span><span>` - uninstall the release build from `<\/span><span style=\"color: #98C379;\">\/usr\/local\/bin<\/span><span>`<\/span><\/span>\n<span class=\"giallo-l\"><span>- `<\/span><span style=\"color: #98C379;\">make clean<\/span><span>` - clean the build directory<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>##<\/span><span style=\"color: #E5C07B;\"> Recommended Git Configuration<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>People using the patch-stack workflow often enable the pull-rebase option in their `<\/span><span style=\"color: #98C379;\">.gitconfig<\/span><span>` as follows. <\/span><span style=\"font-weight: bold;\">**Note:**<\/span><span> This is <\/span><span style=\"font-weight: bold;\">**NOT**<\/span><span> required as `<\/span><span style=\"color: #98C379;\">git ps rebase<\/span><span>` is used generally. However, it is recommended so when you play outside of `<\/span><span style=\"color: #98C379;\">git ps<\/span><span>` you get the same awesome benefits.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\t[pull]<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\trebase = true<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>This makes it so that when you do a `<\/span><span style=\"color: #98C379;\">git pull<\/span><span>` it will automatically trigger a rebase on top of the tracked branches remote. So, if you are using `<\/span><span style=\"color: #98C379;\">master<\/span><span>` and you do a `<\/span><span style=\"color: #98C379;\">git pull<\/span><span>` to get recent changes from `<\/span><span style=\"color: #98C379;\">origin\/master<\/span><span>`. [<\/span><span style=\"color: #98C379;\">Git<\/span><span>][] will automatically initiate a rebase of your changes that were sitting in `<\/span><span style=\"color: #98C379;\">master<\/span><span>` ontop of `<\/span><span style=\"color: #98C379;\">origin\/master<\/span><span>` on to the new `<\/span><span style=\"color: #98C379;\">origin\/master<\/span><span>`. This is exactly what you want as conceptually your stack of patches are always sitting on top of the latest `<\/span><span style=\"color: #98C379;\">origin\/master<\/span><span>`.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>##<\/span><span style=\"color: #E5C07B;\"> What is a patch-stack workflow?<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>The patch stack workflow is a different mental model for doing development locally. Instead of having feature branches and doing work inside them you generally do all your work directly on `<\/span><span style=\"color: #98C379;\">master<\/span><span>` by convention. You can think of your local `<\/span><span style=\"color: #98C379;\">master<\/span><span>` being a conceptual stack of patches (a.k.a. commits) on top of `<\/span><span style=\"color: #98C379;\">origin\/master<\/span><span>`.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>The idea is that instead of creating feature branches you simply make commits on your local `<\/span><span style=\"color: #98C379;\">master<\/span><span>` and perform interactive rebases to squash, reorder, ammend, etc. your patches as you continue to develop. This has an amazing benefit. <\/span><span style=\"font-weight: bold;\">**It eliminates the dependent branch problem.**<\/span><span> You know the one where you created one change in a branch and submitted for review, but then you went off to create another branch on top of the last one, and so on. Soon enough you have to make changes as a result of the review to the first feature branch. And, down the feature branch rebasing train you go.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>The Patch Stack workflow also expects each patch (a.k.a. commit) to be <\/span><span style=\"font-weight: bold;\">**small, buildable, logical units of work with valuable messages**<\/span><span> providing the <\/span><span style=\"font-style: italic;\">*what*<\/span><span>, <\/span><span style=\"font-style: italic;\">*why*<\/span><span>, and any <\/span><span style=\"font-style: italic;\">*contextual details around how*<\/span><span>. This aids drastically in the code review process as each patch becomes a very focused piece of work with clear intent. It also helps conceptually &quot;show your work&quot;, the logical progression of changes (patches) needed to accomplish some larger goal. These principles are as extremely valuable when doing historical spelunking to learn a code base or to iron out a bug. These principles bring even more value as they enable you to take advantage of all the amazing features of [<\/span><span style=\"color: #98C379;\">Git<\/span><span>][], e.g. `<\/span><span style=\"color: #98C379;\">git bisect<\/span><span>`, etc.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>##<\/span><span style=\"color: #E5C07B;\"> Common tasks git-ps assists with<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>###<\/span><span style=\"color: #E5C07B;\"> Visualizing your Stack of Patches<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>[<\/span><span style=\"color: #98C379;\">git-ps<\/span><span>][] provides the <\/span><span style=\"font-weight: bold;\">**list**<\/span><span> command (`<\/span><span style=\"color: #98C379;\">git ps ls<\/span><span>`) to visualize your current stack of patches and the state around each of the patches. The output of this command for the example above would look something like the following.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\t\t2     4387d1 add login view<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t1     23d7a6 add login support to API client<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t0     af73d2 add lib to facilitate making REST API requests<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>This outputs what we call the `<\/span><span style=\"color: #98C379;\">&lt;patch-index&gt;<\/span><span>` on the left followed by, state identifiers (not visible in this example), and then the short SHA of the patch and its summary.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>The following is a description of the structure and the possible state identifiers.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\tgit-ps ls result structure:<\/span><\/span>\n<span class=\"giallo-l\"><span>\t[patch-index] [review status] [commit short sha] [commit summary]<\/span><\/span>\n<span class=\"giallo-l\"><span>\t&quot;0  rr  788032 Update README with deployment instructions&quot;<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\tgit-ps ls Review Status:<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\tHas NOT requested a review (AKA Empty)<\/span><\/span>\n<span class=\"giallo-l\"><span>\trr    Requested a review and it&#39;s unchanged since requesting<\/span><\/span>\n<span class=\"giallo-l\"><span>\trr+   Requested a review and it has changed since requesting<\/span><\/span>\n<span class=\"giallo-l\"><span>\tp     Commit has been published to upstreams mainline<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>###<\/span><span style=\"color: #E5C07B;\"> Submitting a Patch for Review or Integration<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>[<\/span><span style=\"color: #98C379;\">git-ps<\/span><span>][] also makes submitting a patch for review or integration much easier. Instead of having to do these four steps as discussed in sections above.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>1. create a branch based on the current branch&#39;s upstream remote branch (e.g. `<\/span><span style=\"color: #98C379;\">origin\/main<\/span><span>`)<\/span><\/span>\n<span class=\"giallo-l\"><span>2. cherry pick the patch you want reviewed to this new branch<\/span><\/span>\n<span class=\"giallo-l\"><span>3. push this branch up to the remote (e.g. `<\/span><span style=\"color: #98C379;\">origin\/this-patches-branch<\/span><span>`)<\/span><\/span>\n<span class=\"giallo-l\"><span>4. create a pull request from this new branch (`<\/span><span style=\"color: #98C379;\">origin\/this-patches-branch<\/span><span>`) into upstream mainline (`<\/span><span style=\"color: #98C379;\">origin\/main<\/span><span>`)<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>[<\/span><span style=\"color: #98C379;\">git-ps<\/span><span>][] simplifies this down to a single command called <\/span><span style=\"font-weight: bold;\">**request review**<\/span><span> (`<\/span><span style=\"color: #98C379;\">git ps rr &lt;patch-index&gt;<\/span><span>`).<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>If we wanted to request review or even re-request review of a patch we simply do the following.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\tgit ps rr &lt;patch-index&gt;<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>Don&#39;t worry if the SHA of a patch changes since your original request for review. [<\/span><span style=\"color: #98C379;\">git-ps<\/span><span>][] is smart enough to handle this. <\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>###<\/span><span style=\"color: #E5C07B;\"> Pull Changes down from Upstream<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>[<\/span><span style=\"color: #98C379;\">git-ps<\/span><span>][] also helps you pull changes down from upstream so that you can integrate your local patches with the work that has recently been added to upstream.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>This is as simple as running<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\tgit ps pull<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>This facilitates fetching the changes from upstream and rebasing your stack of patches on top of them.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"font-style: italic;\">*Note:*<\/span><span> If one of your patches was integrated into upstream, it will simply collapse out of your stack of patches as Git sees that it is a duplicate of a change that is already integrated in upstream.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>###<\/span><span style=\"color: #E5C07B;\"> Publish a Commit Upstream<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>Once a patch has been approved through the peer review process, you generally want to integrate it into the upstream branch. This can be done by using things like GitHub&#39;s green button. However, this often creates useless noise in the Git tree, and also requires additional manual cleanup steps on your part to remove the no longer needed remote branches.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>So [<\/span><span style=\"color: #98C379;\">git-ps<\/span><span>][] provides the <\/span><span style=\"font-weight: bold;\">**publish**<\/span><span> command specifically to address publishing the local patch to the upstream branch without creating useless noise in the Git tree and cleaning up the no longer necessary remote branch. This can be done as follows.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\tgit ps pub &lt;patch-index&gt;<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>###<\/span><span style=\"color: #E5C07B;\"> See a particular Patch<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>Another feature that [<\/span><span style=\"color: #98C379;\">git-ps<\/span><span>][] provides is the ability to quickly show the contents of a particular patch. This is as simple as the following.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\tgit ps show &lt;patch-index&gt;<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>###<\/span><span style=\"color: #E5C07B;\"> Reorder &amp; Update your Patch Stack<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>In this methodology one of the most common things you do is reorder and update your respective patches while you are iterating on things, and getting them to an acceptable state to request review. As we covered above, interactive rebase `<\/span><span style=\"color: #98C379;\">git rebase -i<\/span><span>` is an extremely valuable tool that helps facilitate this.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>Therefore, [<\/span><span style=\"color: #98C379;\">git-ps<\/span><span>][] simplifies the use of it by using the understanding of the stack of patches concept to automatically fill in the appropriate arguments for the `<\/span><span style=\"color: #98C379;\">git rebase -i<\/span><span>` command. In turn making an interactive rebase of your stack of patches as simple as<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\tgit ps rebase<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>##<\/span><span style=\"color: #E5C07B;\"> License<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>`<\/span><span style=\"color: #98C379;\">git-ps<\/span><span>` is Copyright \u00a9 2020 UpTech Works, LLC. It is free software, and<\/span><\/span>\n<span class=\"giallo-l\"><span>may be redistributed under the terms specified in the LICENSE file.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>##<\/span><span style=\"color: #E5C07B;\"> About <\/span><span>&lt;<\/span><span style=\"color: #E5C07B;\">img<\/span><span style=\"color: #D19A66;\"> src<\/span><span>=&quot;<\/span><span style=\"color: #98C379;\">http:\/\/upte.ch\/img\/logo.png<\/span><span>&quot;<\/span><span style=\"color: #D19A66;\"> alt<\/span><span>=&quot;<\/span><span style=\"color: #98C379;\">uptech<\/span><span>&quot;<\/span><span style=\"color: #D19A66;\"> height<\/span><span>=&quot;<\/span><span style=\"color: #98C379;\">48<\/span><span>&quot;&gt;<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>`<\/span><span style=\"color: #98C379;\">git-ps<\/span><span>` is maintained and funded by [<\/span><span style=\"color: #98C379;\">UpTech Works, LLC<\/span><span>][<\/span><span style=\"color: #56B6C2;\">uptech<\/span><span>], a software<\/span><\/span>\n<span class=\"giallo-l\"><span>design &amp; development agency &amp; consultancy.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>We love open source software. See [<\/span><span style=\"color: #98C379;\">our other projects<\/span><span>][<\/span><span style=\"color: #56B6C2;\">community<\/span><span>] or<\/span><\/span>\n<span class=\"giallo-l\"><span>[<\/span><span style=\"color: #98C379;\">hire us<\/span><span>][<\/span><span style=\"color: #56B6C2;\">hire<\/span><span>] to design, develop, and grow your product.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>[<\/span><span style=\"color: #56B6C2;\">Git<\/span><span>]: https:\/\/git-scm.com<\/span><\/span>\n<span class=\"giallo-l\"><span>[<\/span><span style=\"color: #56B6C2;\">GNU make<\/span><span>]: https:\/\/www.gnu.org\/software\/make\/<\/span><\/span>\n<span class=\"giallo-l\"><span>[<\/span><span style=\"color: #56B6C2;\">community<\/span><span>]: https:\/\/github.com\/uptech<\/span><\/span>\n<span class=\"giallo-l\"><span>[<\/span><span style=\"color: #56B6C2;\">hire<\/span><span>]: http:\/\/upte.ch<\/span><\/span>\n<span class=\"giallo-l\"><span>[<\/span><span style=\"color: #56B6C2;\">uptech<\/span><span>]: http:\/\/upte.ch<\/span><\/span>\n<span class=\"giallo-l\"><span>[<\/span><span style=\"color: #56B6C2;\">git-ps<\/span><span>]: https:\/\/github.com\/uptech\/git-ps<\/span><\/span><\/code><\/pre>"},{"title":"Uptech Studio Releasing Standards","published":"2021-05-01T00:00:00+00:00","updated":"2021-05-01T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/uptech-releasing\/"}},"id":"https:\/\/drewdeponte.com\/blog\/uptech-releasing\/","content":"<p>Here at <a rel=\"external\" href=\"https:\/\/uptechstudio.com\">Uptech Studio<\/a> given that we use semantic versioning as our\n<a href=\"\/blog\/uptech-versioning\/\">standard for versioning<\/a> we can talk instead about the process of doing a\nrelease.<\/p>\n<h2 id=\"normal-release\">Normal release<\/h2>\n<p>Cutting a release consists of the following steps.<\/p>\n<ul>\n<li>identify the commit in the Git history that is to be the release - generally this is <code>HEAD<\/code><\/li>\n<li>tag the commit with the new release version number, e.g. <code>1.2.3<\/code> or <code>v1.2.3<\/code><\/li>\n<li>push tags up to the remote, e.g. <code>git push --tags<\/code><\/li>\n<li>update the <code>CHANGELOG.md<\/code> if not automatically handled by CI, e.g. <code>git cl full &gt; CHANGELOG.md<\/code> - <em>Note:<\/em> if this was an app with build numbers in the version you would use <code>git cl full -p &gt; CHANGELOG.md<\/code>.\n<ul>\n<li>commit and push the update <code>CHANGELOG.md<\/code> commit for that version release<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h2 id=\"avoid-hot-fix-release\">Avoid Hot-fix release<\/h2>\n<p>We avoid \"hot-fix\" releases if we can. What this means in practice is that if we have a version that is released and we identify an issue with it. Instead of prepping a hot-fix release we just add the fix to the top of the tree and cut a new release including that fix as well as the other changes integrated into mainline.<\/p>\n<p>This works because our <a href=\"\/blog\/uptech-branching-strategies\/\">Branching Strategy<\/a> is based on Continous Integration and Trunk Based Development which keeps our mainline in a build &amp; releasable state.<\/p>\n<h2 id=\"hot-fix-release\">Hot-fix release<\/h2>\n<p>If for some reason we had to do a hot-fix release. There is nothing stopping us from being able to do it. We would have to simply perform the following steps.<\/p>\n<ul>\n<li>identify the tagged release that the issue is found on in our Git tree<\/li>\n<li>create a new hot-fix branch based on that tagged commit<\/li>\n<li>add a commit to that hot-fix branch to resolve the issue<\/li>\n<li>tag that commit with the new release version number<\/li>\n<li>backport that commit to mainline so the next release will also include it<\/li>\n<\/ul>\n<h2 id=\"automated-releases\">Automated Releases<\/h2>\n<p>We do <strong>not<\/strong> setup automated releases for libraries. Apps and Services on the other hand we do generally in the lower environments. But with some projects even in the production environment.<\/p>\n<p>In the App case the automated releases are only used for internal release, bumping build numbers, and getting feedback from stakeholders. We generally use MS AppCenter for this.<\/p>\n<p>In the Services case the automated releases are fine as build numbers as the interfaces are versioned within the code base.<\/p>\n<h2 id=\"marketing-releases\">Marketing Releases<\/h2>\n<p>When we are doing a public Marketing Release of an application we go through the <a href=\"https:\/\/drewdeponte.com\/blog\/uptech-releasing\/#normal-release\">normal release<\/a> process with tags and changelog, etc.<\/p>\n"},{"title":"Uptech Studio Roles","published":"2021-05-01T00:00:00+00:00","updated":"2021-05-01T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/uptech-studio-roles\/"}},"id":"https:\/\/drewdeponte.com\/blog\/uptech-studio-roles\/","content":"<p>Lets first understand exactly what a <strong>role<\/strong> is. According to the macOS Dictionary it is \"the function or part played by a person or thing\". The distinction here is that a role is <strong>not a person or their title<\/strong>.<\/p>\n<p>It is something a person takes on. For our discussion the role itself is a categorization of work that is to be performed or acted by a person. This means that a person could take on multiple roles potentially. As you can see below the roles of Staff Engineer &amp; Engineering Manager are both played by our Partners.<\/p>\n<p>The following are a breakdown of the Engineering roles at <a rel=\"external\" href=\"https:\/\/uptechstudio.com\">Uptech Studio<\/a>.<\/p>\n<ul>\n<li>Developers<\/li>\n<li>Tech Lead<\/li>\n<li>Staff Engineer (played by Partner)<\/li>\n<li>Engineering Manager (played by Partner)<\/li>\n<\/ul>\n<h3 id=\"client-focused\">Client Focused<\/h3>\n<p>No matter what role(s) a person is playing it is crucial that they are client focused. We are all responsible for understanding the client's business needs and helping to ensure we're always doing what's best for them.<\/p>\n<p>For example critically thinking about feature requests and how those impact the user and presenting alternatives that may not be quite as effective, but much less costly in terms of development. This type of thinking and conversation is crucial to have with clients to make sure together we are doing what is best for their business at their current lifecycle stage.<\/p>\n<p>We expect everyone within Engineering at <a rel=\"external\" href=\"https:\/\/uptechstudio.com\">Uptech Studio<\/a> to be able to mentally step back from their technical tasks and critically think about the client's business.<\/p>\n<h3 id=\"developers\">Developers<\/h3>\n<p>The following are expectations we have of people playing the role of Senior Developer. These are intended to be used as a guiding light for the long term direction developers should be heading as they grow to play a Senior Developer within <a rel=\"external\" href=\"https:\/\/uptechstudio.com\">Uptech Studio<\/a>.<\/p>\n<ul>\n<li>client focused<\/li>\n<li>deep experience in specific platform\/tech stack with broad experience in other areas (T shaped employee)<\/li>\n<li>understanding of engineering processes and practices\n<ul>\n<li>having a good understanding of how we do engineering here at <a rel=\"external\" href=\"https:\/\/uptechstudio.com\">Uptech Studio<\/a>, agile processes, git processes, etc.<\/li>\n<\/ul>\n<\/li>\n<li>have sound understanding and usage of application architecture\n<ul>\n<li>architecting a solution within the scope of a framework<\/li>\n<li>architecting a solution outside the scope of a framework<\/li>\n<li>identifying framework architectural concepts and boundaries and where to apply framework architecture vs outside scoped architecture<\/li>\n<\/ul>\n<\/li>\n<li>break down problem space\n<ul>\n<li>ability to understand &amp; collaborate with the Tech Lead around the system architecture, application architecture and further breakdown various libraries, components, services, etc.<\/li>\n<\/ul>\n<\/li>\n<li>drive tasks\/components\/etc.\n<ul>\n<li>identifying broken down components, unknowns, etc. and being the driving force to get them prioritized and resolved to aid in accomplishing the primary goal<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h3 id=\"tech-lead\">Tech Lead<\/h3>\n<p>Tech Leads have all the same expectations of a Senior Developer in addition to the following. Tech Leads high level focuses are around Architecture, Foundation, Mentorship, Development, and Process management.<\/p>\n<ul>\n<li>more client interactions because they are generally a <strong>Squad Representative<\/strong><\/li>\n<li>run tech meetings<\/li>\n<li>primary point of contact for engineering questions &amp; interactions<\/li>\n<li>working with Product to do planning for Roadmap and tasks<\/li>\n<li>architect, problem solve, etc.<\/li>\n<li>drive the technical side of achieving the client's goal\n<ul>\n<li>breaking down the problem space at each of the levels<\/li>\n<li>monitoring progress<\/li>\n<li>removing road blocks for team mates<\/li>\n<li>course correcting<\/li>\n<\/ul>\n<\/li>\n<li>have sound understanding and usage of infrastructure (AWS, terraform, kubernetes)<\/li>\n<li>have sound understanding and usage of backend &amp; front-end development<\/li>\n<\/ul>\n<h3 id=\"partners\">Partners<\/h3>\n<p>Due to the current size of <a rel=\"external\" href=\"https:\/\/uptechstudio.com\">Uptech Studio<\/a> there are a number of roles, <strong>Staff Engineer<\/strong> &amp; <strong>Engineering Manager<\/strong>, that are strictly played by our Engineering Partners, <a rel=\"external\" href=\"https:\/\/drewdeponte.com\">Drew De Ponte<\/a> &amp; <a rel=\"external\" href=\"https:\/\/www.linkedin.com\/in\/cciocan\/\">Claude Ciocan<\/a>. It is possible that as the company continues to grow we may decide to have other people take on these roles. However, for the moment they are handled by the Engineering Partners.<\/p>\n<h4 id=\"staff-engineer\">Staff Engineer<\/h4>\n<p>Staff Engineers are intended to be a resource shared across the organization to help with standardization, best practices, mentorship, and contribution. Staff Engineers are expected to have all the same expectations of a Tech Lead in addition to the following.<\/p>\n<ul>\n<li>shared across the engineering organization<\/li>\n<li>advising tech leads and developers<\/li>\n<li>advising on architecture<\/li>\n<li>advising on <a rel=\"external\" href=\"https:\/\/uptechstudio.com\">Uptech Studio<\/a> process<\/li>\n<li>advising on development process\/tooling<\/li>\n<li>advising on coding best practices<\/li>\n<li>helps on specific tasks<\/li>\n<\/ul>\n<h4 id=\"engineering-manager\">Engineering Manager<\/h4>\n<p>The Engineering Manager role is responsible for being direct managers of the Developers &amp; Tech Leads from an HR standpoint. We believe in servant leadership and the idea of trying to understand peoples interests so that we can find their best fit and growth path within <a rel=\"external\" href=\"https:\/\/uptechstudio.com\">Uptech Studio<\/a>. This involves working to try to align people with projects that would most interest them or aid in their growth in specific areas of interest. We have found that when people are aligned with their interests they tend to do their best work.<\/p>\n"},{"title":"Uptech Studio Testing Standards","published":"2021-05-01T00:00:00+00:00","updated":"2021-05-01T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/uptech-testing\/"}},"id":"https:\/\/drewdeponte.com\/blog\/uptech-testing\/","content":"<p>We have always been big believers in automated testing and Test Driven Development (<a rel=\"external\" href=\"https:\/\/en.wikipedia.org\/wiki\/Test-driven_development\">TDD<\/a>). Back in the day we were even big believers in Behavior Driven Development (<a rel=\"external\" href=\"https:\/\/en.wikipedia.org\/wiki\/Behavior-driven_development\">BDD<\/a>).<\/p>\n<p>Over the many years we have learned that <a rel=\"external\" href=\"https:\/\/cucumber.io\">Cucumber<\/a> and <a rel=\"external\" href=\"https:\/\/cucumber.io\/docs\/gherkin\/\">Gherkin<\/a> were nice ideas but we never really saw huge value and it required additional adoption from Product which was always a hurdle.<\/p>\n<p>The concept of <strong>emergent design<\/strong>, the process of evolving application architecture from mocked tests, sounds great in theory. But in practice we didn't see any real added value once you have written the non-mocked tests. This is because the mocked tests become brittle noise that isn't worth maintaing and in fact interfere with the process of refactoring functionality. Even if you take the stance of removing these mocked tests, the development team has then taken on the burden of identifying which tests to remove and when to remove them. This is simply more overhead that doesn't provide any real payout.<\/p>\n<p>Now there is no reason to throw the baby out with the bath water. There were ideas in <a rel=\"external\" href=\"https:\/\/en.wikipedia.org\/wiki\/Behavior-driven_development\">BDD<\/a> that we found to be extremely valuable. For example the concept of applying Outside-In Development as a way of making sure that what is being developed throughout the application architecture is actually necessary. We found this practice aids immensely with not only making sure the right software is built but also with the process of identifying architectural boundaries and doing application architecture.<\/p>\n<p>Therefore our current position on testing is a hybrid of the various methodologies we have used over the years. The principles we follow are listed below.<\/p>\n<ul>\n<li>Outside-In<\/li>\n<li>Test First<\/li>\n<li>Small Tests<\/li>\n<li>Red, Green, Refactor<\/li>\n<li>State over Behavior<\/li>\n<li>Unit &amp; Integration<\/li>\n<li>Setup in Test<\/li>\n<li>Full-Stack Integration within Reason<\/li>\n<\/ul>\n<h2 id=\"outside-in\">Outside-In<\/h2>\n<p>As described above we value Outside-In Development from <a rel=\"external\" href=\"https:\/\/en.wikipedia.org\/wiki\/Behavior-driven_development\">BDD<\/a> as a way of making sure that what is being developed throughout the application architecture is actually necessary. Additionaly we value it as a tool for aiding with architecting an application as well as identifying application architecture boundaries. Which are both extremely useful when writing software in a fashion supporting collaboration. You can see more on Outside-In and how it relates to some of our other processes in our <a href=\"\/blog\/journey-to-small-pull-requests\/\">Journy to Small Pull Requests<\/a> and <a href=\"\/blog\/how-we-should-be-using-git\/\">How we should be using Git<\/a> posts.<\/p>\n<h2 id=\"test-first\">Test First<\/h2>\n<p>We also strongly abide by the concept of \"Test First\", the practice of writing a failing test against a nonexistent idealized API and then following the Red, Green, Refactor process to iterate the test &amp; implementation to its final state.<\/p>\n<p>The primary reason we value \"Test First\" so much is because the practice of thinking about what the ideal API would look like and using the first iteration of writing the test as a tool for this has huge positive impacts in terms of the application architecture and its maintainability over time.<\/p>\n<p>Beyond that it also benefits in helping to make sure that the tests fully cover the implemented code. If instead you were to for example to write a function first and then go back and write the test after the fact. You would discover over time that it is very easy to miss cases and end up with a test suite that doesn't fully cover all the cases.<\/p>\n<h2 id=\"small-tests\">Small Tests<\/h2>\n<p>As a general rule of thumb we stick to one conceptual assertion per test. What this means is that we effectively want to test and verify one thing. If we are testing say a function that returns an object that should have multiple attributes on it in a new state. Then we would have multiple assertions to verify those. But conceptually we are still verifying the resultant state of that one action.<\/p>\n<p>There are cases, when doing higher level integration tests especially, where it may make sense to actually do multiple conceptual assertions. However, this is the exception not the rule.<\/p>\n<p>Sticking to one conceptual assertion keeps our tests focused in such a manner that when one fails it is focused enough that we don't have to do a ton of debugging to identify what the problem is. Generally the failing test immedietly tells us exactly what the problem is. This of course does not hold true for higher level integration tests, especially the ones that have multiple conceptual assertions.<\/p>\n<h2 id=\"red-green-refactor\">Red, Green, Refactor<\/h2>\n<p>Red, Green, Refactor is really just a name for a process that occurs naturally when doing \"Test First\" <a rel=\"external\" href=\"https:\/\/en.wikipedia.org\/wiki\/Test-driven_development\">TDD<\/a>. It simply communicates the enforcement of the sequence of the testing process.<\/p>\n<ol>\n<li>write a failing test - a.k.a. Red<\/li>\n<li>write code that makes the test pass - a.k.a. Green<\/li>\n<li>clean up the the implementation that makes the test pass by refactoring it - a.k.a. Refactor<\/li>\n<\/ol>\n<p>Over the years this naming has been overloaded by being utilized at a micro level to explain the iterative process of doing \"Test First\" <a rel=\"external\" href=\"https:\/\/en.wikipedia.org\/wiki\/Test-driven_development\">TDD<\/a>. In this context is more along the lines of the following.<\/p>\n<ol>\n<li>write a failing test (in initial state, not final state) - a.k.a. Red<\/li>\n<li>write code that makes the test pass - a.k.a. Green<\/li>\n<li>evolve the test to test more of what it should, leaving you with a now failing test - a.k.a. Refactor, Red<\/li>\n<li>write code that makes the test pass - a.k.a. Green<\/li>\n<li>...<\/li>\n<li>...<\/li>\n<li>evolve the test to test to its final state, leaving you with a now failing test - a.k.a. Refactor, Red<\/li>\n<li>write code that makes the test pass - a.k.a. Green<\/li>\n<li>clean up the the implementation that makes the test pass by refactoring it - a.k.a. Refactor<\/li>\n<\/ol>\n<p>I don't think this overloaded adaption to this micro usecase is necessarily the cleanest mapping. But it is what a decent portion of the community has starting using it to refer two. So we might as well at least be clear about it.<\/p>\n<p>As I stated before these are basically just the naturaly processes that occur when doing \"Test First\" <a rel=\"external\" href=\"https:\/\/en.wikipedia.org\/wiki\/Test-driven_development\">TDD<\/a> so we naturally abide by them.<\/p>\n<h2 id=\"state-over-behavior\">State over Behavior<\/h2>\n<p>The concept of <strong>State over Behavior<\/strong> means that we ideally want to do <strong>State<\/strong> based testing whenever possible and only use <strong>Behavior<\/strong> testing when explicitly necessary.<\/p>\n<p>We hold this stance because <strong>State<\/strong> based testing aids with application architecture design and is naturally long living as well as interface abiding. This means that once we have written a <strong>State<\/strong> based test it is useful when we go to refector the implementation of a function\/method. <strong>Behavior<\/strong> tests are <strong>not<\/strong> useful and have to be completely reworked when refactoring implementation of a function\/method.<\/p>\n<p>This position does have implications. For example to support isolation (via stubs\/test doubles) in our tests we need to modify how we write our code to make sure we are using Dependency Injection. This is an implication we are more than happy to take on as we have found that classic Dependency Injection has aided massively in our application architectures over the years.<\/p>\n<h2 id=\"unit-integration\">Unit &amp; Integration<\/h2>\n<p>There are two main categories of tests <strong>Unit<\/strong> and <strong>Integration<\/strong>. We are proponents of both of them.<\/p>\n<p><strong>Unit<\/strong> tests are focused around validating a particular unit of code, generally a function\/method. Therefore, they isolate away collaborator objects. An example might look something like the following.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>stub_shopping_cart = ShoppingCartTestDouble()<\/span><\/span>\n<span class=\"giallo-l\"><span>add_item(item, stub_shopping_cart)<\/span><\/span>\n<span class=\"giallo-l\"><span>assert(shopping_cart.items.include(item))<\/span><\/span><\/code><\/pre>\n<p>In the above example we replaced the shopping cart by giving the <code>add_item<\/code> function the <code>stub_shopping_cart<\/code>. This isolates away any functionality that a real shopping cart whould have had and allows us to strictly test the functionality of just the <code>add_item<\/code> function.<\/p>\n<p><strong>Integration<\/strong> tests are focused around validation of many units of code working together in an integrated fashion. An example might look as follows:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>data_store = DataStore()<\/span><\/span>\n<span class=\"giallo-l\"><span>real_shopping_cart = ShoppingCart(data_store)<\/span><\/span>\n<span class=\"giallo-l\"><span>add_item(item, real_shopping_cart)<\/span><\/span>\n<span class=\"giallo-l\"><span>assert(shopping_cart.items.include(item))<\/span><\/span><\/code><\/pre>\n<p>You can see in the above that it is using real objects rather than Test Doubles or Stub objects. The benefit of this is that it can be used to verify functionality of all those different units of code working together which is also important. Especially to make sure that integrated results are what we expect.<\/p>\n<p>Therefore we write both <strong>Unit<\/strong> and <strong>Integration<\/strong> tests.<\/p>\n<h2 id=\"setup-in-test\">Setup in Test<\/h2>\n<p><strong>Setup in Test<\/strong> is the concept of making sure that the <strong>setup<\/strong> portion of each test is included directly in the test itself and not in some <code>before each<\/code> or <code>describe<\/code> block or some parenting test framework concept that provides initial state setup across multiple tests.<\/p>\n<p>You might be thinking to yourself well this concept goes against the DRY principle which we follow in implementation code. Shouldn't we be following it in our tests as well? Technically it doesn't go againts the DRY principle overall. It just goes against DRYing test setup up using framework concepts like <code>before each<\/code> and <code>describe<\/code>.<\/p>\n<p>The reason that <strong>Setup in Test<\/strong> exists as a concept is because extracting test setup into <code>before each<\/code> or <code>describe<\/code> blocks or other test framework constructs have massive negative implications when you are coming back to a test later on. They make it very difficult to understand what the exact test setup is for a particular test. You often have to go hunt to try and figure out what test setups are done earlier in the suite and even then there are likely test setup steps that aren't needed in your particular test but exist earlier in the suite.<\/p>\n<p>To get the best of both worlds, <strong>clarity of test setup<\/strong> and <strong>DRYing up test setup<\/strong> we follow the common strategy of extracting test setup into utility functions in the test suite. This enables us to have clear, concise, well named utility functions that we can use in each of the tests that clearly communicate the setup. Even in cases where the naming don't clearly communicate the setup, it is easy enough for us to dive into any setup functions and see what is going. This is superior as there is a formal relationship and we don't have to guess if setup is used or not.<\/p>\n<h2 id=\"full-stack-integration-within-reason\">Full-Stack Integration within Reason<\/h2>\n<p>Full-Stack integration testing is the same concept as <strong>integration<\/strong> testing from above. The difference is that it is generally at the highest level of the application and integrates through all the various layers of the application. We are big believers in this for the same reason we are <strong>integration<\/strong> testing.<\/p>\n<p>However, when you are doing Full-Stack integration testing you often have to deal with things like a database or 3rd party APIs, etc. This is why we have the caveat of \"within Reason\". Doing true Full-Stack integration testing is very difficult and not practical\/pragmatic in most cases. It often makes more sense to stub out 3rd party integrations and do some sort of datbase resetting\/transaction management, or maybe an in memory database substitution. In some cases maybe it even makes sense to start a layer lower in the architecture.<\/p>\n<p>The point is that given the impracticality of doing true Full-Stack Integration Testing we instead figure out for each of the different projects we work on what we can do \"within reason\" to get as close to Full-Stack Integration as possible.<\/p>\n"},{"title":"Uptech Studio Versioning Standards","published":"2021-05-01T00:00:00+00:00","updated":"2021-05-01T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/uptech-versioning\/"}},"id":"https:\/\/drewdeponte.com\/blog\/uptech-versioning\/","content":"<h2 id=\"versioning-libraries\">Versioning Libraries<\/h2>\n<p>We use <a rel=\"external\" href=\"https:\/\/semver.org\">Semantic Versioning<\/a> for our standard of choice for versioning libraries.<\/p>\n<h2 id=\"versioning-apis\">Versioning APIs<\/h2>\n<p>There are few options.<\/p>\n<ul>\n<li>URL based versioning, e.g. <code>\/api\/v1\/route<\/code><\/li>\n<li>HTTP Header based versioning\n<ul>\n<li>use <a rel=\"external\" href=\"https:\/\/docs.github.com\/en\/rest\/overview\/media-types\">Media types<\/a>, e.g. <code>application\/vnd.github[.version].param[+json]<\/code> or <code>application\/vnd.github.v3+json<\/code><\/li>\n<\/ul>\n<\/li>\n<li>HTTP Custom Headers versioning (any header beginning with <code>X-<\/code>), e.g. <code>X-GitHub-Media-Type<\/code><\/li>\n<\/ul>\n<p>We prefer and will use <strong>HTTP Header based versioning with Media Types<\/strong> as it is the most robust solution that generally also has support built into libraries or frameworks.<\/p>\n<p>The <strong>HTTP Custom Headers versioning<\/strong> is technically more robust as there are no standards around it. However, it then requires more custom work to do be done for clients interacting with it.<\/p>\n<p>The <strong>URL based versioning<\/strong> is <strong>NOT<\/strong> robust because it requires either duplicating code, or implementing some sort proxying, etc. to deal with situations where you want to create another version of all the routes. It also makes it harder to mix them as well in terms of code management.<\/p>\n"},{"title":"How we should be using Git","published":"2021-01-30T00:00:00+00:00","updated":"2021-01-30T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/how-we-should-be-using-git\/"}},"id":"https:\/\/drewdeponte.com\/blog\/how-we-should-be-using-git\/","content":"<p>Git was created by Linus Torvalds out of a need. At the time the Linux Kernel team was using a proprietary Distributed Source Control Management (DSCM) system. However, due to licensing issues the Linux Kernel team could no longer use this proprietary DSCM system. Therefore, Linus decided to build Git as the DSCM system he always wished they had.<\/p>\n<p>You might also know Linus as the creator of Linux. In fact, Linus still manages the Linux Kernel today. As of 2020, the Linux kernel had over 27.8 million lines of code spread across ~66 thousand files from ~21 thousand different contributors. It has continued to be successfully developed, maintained, and extended since it was publicly announced in 1991. It is also worth noting that Git itself, another large, successful open source project, is managed in the same way.<\/p>\n<p>So there must be some useful insights around long term software development and maintenance practices we can glean by looking at how Linus and his team use Git for their development and peer review workflows.<\/p>\n<h2 id=\"how-the-linux-kernel-team-works\">How the Linux Kernel Team works?<\/h2>\n<p>It may sound crazy but it turns out that the Linux Kernel and Git teams use email for both their code review process and their code submission process. They use a concept from the Unix days called a patch.<\/p>\n<p>A patch is a simple text file that describes a change to another text file's content. In Unix there exists a tool called <strong>diff<\/strong> that takes two files and produces a patch file. People often refer to these patch files as <em>diffs<\/em>. Another Unix tool called <strong>patch<\/strong> takes a patch file and applies it to another file.<\/p>\n<p>Back in the day this Unix patch concept is actually how open source development was done. The contributor would make a change to some source code, produce a patch, and email that patch to the maintainers requesting feedback. The contributor would then take the feedback and make the adjustments to the code. Then they would produce a new patch and email it back to the maintainers for further review and possible integration.<\/p>\n<p>Fundamentally this is how peer review is done with the Linux Kernel and Git teams today. The primary difference is that they use Git to manage changes locally and then use a number of Git commands to generate a patch or a sequences of patches and email them to the maintainers. They also use a number of Git commands to consume and apply patches from emails. See <code>git apply<\/code>, <code>git am<\/code>, <code>git format-patch<\/code>, <code>git imap-send<\/code>, and <code>git send-email<\/code>.<\/p>\n<p>In addition the team also requires that patches have certain characteristics.<\/p>\n<ul>\n<li>a patch must be <strong>small<\/strong>, <strong>logical<\/strong>, <strong>independent<\/strong>, <strong>buildable<\/strong>, &amp; <strong>testable<\/strong><\/li>\n<li>a patch must provide a clear description of what changed, why it was changed, and the necessary context of how it is or will be used<\/li>\n<li>patches must be submitted individually for review<\/li>\n<\/ul>\n<p>If we dig a bit further into the Linux Kernel team's process we find that they have integrators responsible for the various subsystems. These integrators receive numerous patches from various contributors and manage them locally as a series of patches (using <strong>quilt<\/strong>, another Unix command). This involves reordering, updating and dropping them on a regular basis. Linus then regularly pulls the changes from the various subsystem integrators into the upstream repository's mainline.<\/p>\n<h2 id=\"what-did-we-learn\">What did we learn?<\/h2>\n<p>From a small dive into the Linux Kernel team's development process we can make three important observations.<\/p>\n<p>First, the process imposes a strict set of requirements for each patch before it may be sent for review or submission. Second, the team's process works well over email, a very asynchronous communication mechanism. Finally, they locally use the concept of managing a series of patches while preparing changes for integration into upstream.<\/p>\n<h2 id=\"so-what-s-the-magic\">So what's the Magic?<\/h2>\n<p>Is it the Kernel team's avoidance of powerful peer review tools that makes them so successful? Or the fact that they do this all over email?<\/p>\n<p>I think not. I believe their success is primarily due to their process. The specific requirements for patches are fundamentally in alignment with the core design principles of Git. They are also structured around the concepts of boundaries of the application architecture.<\/p>\n<p>Beyond that, it seems this mental model of locally managing a series of patches as they are prepared to be submitted for review or submission is key.<\/p>\n<h2 id=\"outside-in\">Outside-In<\/h2>\n<p>Let's assume for the time being I am correct and we trust that following these rules and this mental model will greatly benefit us. (Later we can explore the actual values we get from these rules and why they are so important.) How can we use this to benefit our own projects?<\/p>\n<p>Before we can apply these rules we need to first understand how to break down our code changes into small logical independent chunks that are buildable and testable. To help us with this, let's introduce a new mental model for thinking about application architecture in relation to boundaries, called <em>Outside-In<\/em>.<\/p>\n<p>With Outside-In you begin your approach to code changes by starting from the outermost (UI\/UX) layer of the architecture, and drive layer-by-layer down into the architecture until you reach your core dependency, the inner most layer of the relevant architecture; you then reverse direction and work your way out again, wiring the layers together as you go.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/how-we-should-be-using-git\/outside-in.gif\" alt=\"Animation showing Outside-In development\" \/><\/p>\n<p>This method ensures that you are building exactly what you need-nothing more, nothing less. It also helps as a tool to recoognize different layers of abstraction within your application which aids massively in thinking about the overall application architecture. Last, it also drives the interfaces of your abstracitons so you end up with more clearly defined boundaries.<\/p>\n<h2 id=\"sounds-great-in-theory\">Sounds great in theory<\/h2>\n<p>But what does this actually look like?<\/p>\n<p>Let's say we are tasked with adding the ability to login to our mobile app. If we follow Outside-In this might look something like the following series of steps:<\/p>\n<ol>\n<li>add login view (incomplete because depends on a API client that doesn't exist)<\/li>\n<li>add login support to API client (incomplete because depends on REST API lib)<\/li>\n<li>add lib to facilitate making REST API requests (our core dependency)<\/li>\n<li>complete add login support to API client (wiring it up to the lib added in the prev step)<\/li>\n<li>complete add login view (wiring it up to the API client login ability added in the prev step)<\/li>\n<\/ol>\n<p>If we created commits locally for each of these steps our local commit tree would look like the following:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>* complete add login view (HEAD, main)<\/span><\/span>\n<span class=\"giallo-l\"><span>* complete add login support to API client<\/span><\/span>\n<span class=\"giallo-l\"><span>* add lib to facilitate making REST API requests<\/span><\/span>\n<span class=\"giallo-l\"><span>* WIP: add login support to API client<\/span><\/span>\n<span class=\"giallo-l\"><span>* WIP: add login view<\/span><\/span>\n<span class=\"giallo-l\"><span>* ... (origin\/main)<\/span><\/span><\/code><\/pre>\n<p>This is fine, but these don't look like logically chunked commits. They are also in the incorrect order based on dependencies.<\/p>\n<p>Luckily we are working in Git, which provides the interactive rebase (<code>git rebase -i<\/code>) command which facilitate reordering and squashing commits together, amongst other valuable things.<\/p>\n<p>Before we fix these issues, let's just look at what the tree would look like if we reordered it based on dependencies and didn't squash the commits.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>* WIP: add login view (HEAD, main)<\/span><\/span>\n<span class=\"giallo-l\"><span>* complete add login view<\/span><\/span>\n<span class=\"giallo-l\"><span>* WIP: add login support to API client<\/span><\/span>\n<span class=\"giallo-l\"><span>* complete add login support to API client<\/span><\/span>\n<span class=\"giallo-l\"><span>* add lib to facilitate making REST API requests<\/span><\/span>\n<span class=\"giallo-l\"><span>* ... (origin\/main)<\/span><\/span><\/code><\/pre>\n<p>From this ordering we can start to actually see the relation and the logical chunking. For example the \"WIP: add login view\" and the \"complete add login view\" are two commits that are part of the same logical chunk.<\/p>\n<p>If we identify these commits and squash them together into their respective logical chunks we would end up with a local Git tree that looks as follows:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>* add login view (HEAD, main)<\/span><\/span>\n<span class=\"giallo-l\"><span>* add login support to API client<\/span><\/span>\n<span class=\"giallo-l\"><span>* add lib to facilitate making REST API requests<\/span><\/span>\n<span class=\"giallo-l\"><span>* ... (origin\/main)<\/span><\/span><\/code><\/pre>\n<p>The Kernel team describes a series of patches as a \"proof of work\". Think back to when you were a kid in school and your teacher required you show how you got to a solution for each problem. This concept is effectively the same thing. Each logical patch in the series is a step of you showing how you are solving the problem. This is a another great mental model and is useful for making sure your series of patches make sense.<\/p>\n<h2 id=\"where-are-we-with-these-rules\">Where are we with these rules?<\/h2>\n<p>At this point we have figured out how to locally iterate on commits to get them to a point where they effectively are a series of <strong>small<\/strong>, <strong>logical<\/strong>, <strong>independent<\/strong>, <strong>buildable<\/strong>, and <strong>testable<\/strong> patches.<\/p>\n<p>That leaves us with the following:<\/p>\n<ul>\n<li>a patch must provide clear description of what changed, why it was changed, and the necessary context of how it is or will be used<\/li>\n<li>patches must be submitted individually for review<\/li>\n<\/ul>\n<p>Meeting the first of these is simply writing a good commit message. Check!<\/p>\n<p>But how do we follow the second requirement to submit patches individually?<\/p>\n<h2 id=\"submit-for-review-or-integration\">Submit for Review or Integration<\/h2>\n<p>We could, of course, setup a mailing list like the Linux Kernel team and use the same Git commands they do. However, this seems archaic when we have tools like GitHub, Bitbucket, Gitlab, etc. that provide the concept of a Pull Request, as well as code review tools that are much nicer than email. Not to mention the fact that the majority of the development community is already comfortable with these tools due to the unjust popularity of Feature Branch development.<\/p>\n<p>So how do we create a pull request of an individual patch once it is ready for review?<\/p>\n<ol>\n<li>create a branch based on the current branch's upstream remote branch (e.g. <code>origin\/main<\/code>)<\/li>\n<li>cherry pick the patch you want reviewed to this new branch<\/li>\n<li>push this branch up to the remote (e.g. <code>origin\/this-patches-branch<\/code>)<\/li>\n<li>create a pull request from this new branch (<code>origin\/this-patches-branch<\/code>) into upstream mainline (<code>origin\/main<\/code>)<\/li>\n<\/ol>\n<p>Now reviewers can review the changes as they normally would. They will have a clear description of what changed, why it was changed, and the necessary context of how it is or will be used, and will be able to easily review the actual code, testing them out, and providing feedback.<\/p>\n<p>A little later we will introduce a tool that automates these steps for you, making this quick and easy.<\/p>\n<h2 id=\"so-what-s-the-point\">So What's the point?<\/h2>\n<p>Interestingly, it turns out that structuring your patches (a.k.a. commits) logically around the concepts in your application architecture is a major part of the magic. This specifically aids with understanding concepts, how they are introduced, and how they are modified over time to support higher level concepts and features. This is what largely facilitates <strong>long term maintenance of software<\/strong>.<\/p>\n<p>This method also facilitates <strong>requesting review for individual patches as soon as they are completed<\/strong>. I can't emphasize how important this is. This enables us to basically develop Outside-In but request review Inside-Out. Meaning as soon as the inner most dependency is complete we can request review of that and get feedback all while we are still working on the layers above it.<\/p>\n<p>Beyond that these small logical, buildable, testable patches result in <strong>a faster mean time to repair<\/strong>, <strong>extremely focused and valuable reviews<\/strong>, <strong>support for fault isolation<\/strong>, <strong>decreased code review time<\/strong>, and <strong>promote test reliability<\/strong>. They also facilitate <strong>integrating patches sooner<\/strong>, which in turn <strong>promotes sharing<\/strong>, <strong>refactoring<\/strong>, <strong>discovery<\/strong>, and <strong>natural evolution<\/strong> of application architecture concepts. Not to mention helping to <strong>prevent duplicate efforts<\/strong> and <strong>improve the overall release rate<\/strong>.<\/p>\n<p>Again these small logical, buildable, testable patches fall in line with Git's principles of design and in turn help to <strong>unlock the full powers of Git<\/strong>. Specifically in Git there are certain extremely powerful commands (e.g. <code>git bisect<\/code>) that only function well if your Git tree is composed of logical, buildable, testable commits.<\/p>\n<p>The concept of a <strong>series of patches<\/strong> also ends up having a huge benefit of <strong>eliminating urgency around having one's pull requests reviewed immediately<\/strong>. This is because even after you have submitted a patch for review, you still have that patch in your series of patches locally and can continue building on it without issue. This significantly aids in reducing interruptions between developers, breaking their focus, and inevitably making things take longer. This can't be obtained using feature branches without huge overhead and it is a key differentiator in supporting asynchronous workflows.<\/p>\n<p>How do you make sure people are aware of new pull requests without interrupting them? Don't worry-this is a problem that all pre-review based development methodologies suffer from. To help address this issue we built <a rel=\"external\" href=\"https:\/\/pullwalla.com\">Pullwalla<\/a>, a unified pull request manager.<\/p>\n<p>Finally, following these rules also results in your Git commits and in turn your Git tree representing \"proofs of work\", the mental construct the Kernel team uses to vet a series of patches. Turns out these \"proofs of work\" provide an immense amount of guidance and value in simply understanding a code base's application architecture and the intentions behind the various logical changes.<\/p>\n<h2 id=\"patch-stack\">Patch Stack<\/h2>\n<p>Given that we have found this development methodology to be unmatched in value, we decided we must formalize it a bit more. We decided to call this the <strong>Patch Stack<\/strong> methodology, as it resembles a stack of papers that can be reordered and modified.<\/p>\n<p>Achieving the full value of the methodology requires that these rules are followed and that you locally manage your commits as if they are conceptually a stack of patches being reordered and modified in preparation for submitting for review or integration.<\/p>\n<ul>\n<li>a patch must be <strong>small<\/strong>, <strong>logical<\/strong>, <strong>independent<\/strong>, <strong>buildable<\/strong>, &amp; <strong>testable<\/strong><\/li>\n<li>a patch must provide a clear description of what changed, why it was changed, and the necessary context of how it is or will be used<\/li>\n<li>patches must be submitted individually for review<\/li>\n<\/ul>\n<p>In addition to formally defining the rules, we have built <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/git-ps-rs\">git-ps-rs<\/a>, an OSS Git command line extension to facilitate making this workflow and methodology even easier to use.<\/p>\n<h2 id=\"getting-the-feel-for-it\">Getting the Feel for It<\/h2>\n<p>To get a better feel for how this methodology works in real life let's walk through our example from before. If you recall we are tasked with adding Login ability to a mobile app.<\/p>\n<h3 id=\"where-do-we-start\">Where do we start?<\/h3>\n<p>We take guidance from Outside-In and start at the outer most layer of our application architecture (UI\/UX). We first look at the existing UI layer of the application architecture to find out if it provides any support for login.<\/p>\n<p>Turns out it doesn't. So we are off to the races, building out a login screen and adding it to the flow of the app. We get to the point where we are attempting to wire up the submit button of the login screen and realize our client API for our backend doesn't support login.<\/p>\n<p>So we figure out the ideal interface that should get added to the our client API to facilitate login. We also implement code to use this imaginary interface. This code obviously won't build and isn't complete, but we include it anyways as it acts as a note to ourselves for what the ideal interface should be.<\/p>\n<p>Given we are following Outside-In we also recognize that we are at a boundary between application architecture concepts, specifically the UI and the client API. This is our clue that we need a patch to house this logical (UI) piece of the puzzle.<\/p>\n<p>So we add a patch on top of our stack by first staging the UI changes, <code>gps add .<\/code>, and then creating the patch.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>gps create-patch<\/span><\/span><\/code><\/pre>\n<p>There is an alias of <code>gps c<\/code> for short as this operation is so common.<\/p>\n<p>When it prompts for the commit message we provide something like the following, making sure to provide a clear description of what changed, why it was changed, and the necessary context of how it is or will be used.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>WIP: Add login screen<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>So that users will be able to login to their account.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>This interfaces with the client API to actually perform the login and get the<\/span><\/span>\n<span class=\"giallo-l\"><span>authentication token.<\/span><\/span><\/code><\/pre>\n<p>We used the convention of prefixing our commit summary with \"WIP:\" as a reminder to us that this patch is still a Work In Progress.<\/p>\n<h3 id=\"onto-the-client-api\">Onto the Client API<\/h3>\n<p>Now that we have created our WIP patch for the UI layer. We move onto to the next layer down, the client API.<\/p>\n<p>So we extend the client API by adding our ideal interface and starting to implement the functionality behind it, making any necessary adjustments to the interface as we go.<\/p>\n<p>Soon we realize that we don't have a library to facilitate interacting with the REST API. We also recognize this as yet another boundary in the application architecture. Therefore, we create another patch on top of our stack by staging the changes, <code>gps add .<\/code> and then committing.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>gps create-patch<\/span><\/span><\/code><\/pre>\n<p>We provide the following message with the patch.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>WIP: Add BackendClient.login(username:password:) method<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>So that our application has the ability to make the login request to the<\/span><\/span>\n<span class=\"giallo-l\"><span>Backend REST API given the username and password.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>This is intended to be used by the login screen to help facilitate performing<\/span><\/span>\n<span class=\"giallo-l\"><span>the actual login. It will be interfacing with some REST API to actually make<\/span><\/span>\n<span class=\"giallo-l\"><span>the REST call.<\/span><\/span><\/code><\/pre><h3 id=\"what-s-our-patch-stack-look-like\">What's our Patch Stack look like?<\/h3>\n<p>We have added a couple patches to our Patch Stack and are ready to check where we are in our process. To do this we simply list our patch stack using the following.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>gps list<\/span><\/span><\/code><\/pre>\n<p>An alias for this command is provided as well, <code>gps ls<\/code>.<\/p>\n<p>It outputs a view of all the patches on our stack.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>  1     23d7a6 WIP: Add BackendClient.login(username:password:) method<\/span><\/span>\n<span class=\"giallo-l\"><span>  0     af73d2 WIP: Add login screen<\/span><\/span><\/code><\/pre>\n<p>Things look good and we are reminded that we just added the <code>BackendClient.login()<\/code> method and it is incomplete because of a missing dependency, the REST API library.<\/p>\n<h3 id=\"onto-the-rest-api-library\">Onto the REST API library<\/h3>\n<p>So we do some research and find what we think is the best REST API library and add it to our <code>Package.swift<\/code>, making it available to our app.<\/p>\n<p>This is yet another boundary in our application architecture and therefore we create a new logical patch for it by staging the changes, <code>gps add .<\/code>, and committing.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>gps create-patch<\/span><\/span><\/code><\/pre>\n<p>With the following message:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>Add Siesta, the elegant way to write REST clients<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>So that we can easily and most importantly make elegant REST requests.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>We intend to use this inside of our BackendClient.login(username:password:)<\/span><\/span>\n<span class=\"giallo-l\"><span>method to elegantly make the login REST API request.<\/span><\/span><\/code><\/pre>\n<p>It is worth noting that we do <strong>not<\/strong> use the \"WIP:\" prefix in our patch summary here. This is because it is a complete independent logical patch and happens to also be our core dependency in the context of Outside-In.<\/p>\n<h3 id=\"where-is-our-stack-at\">Where is our Stack at?<\/h3>\n<p>Let's check out our stack of patches now.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>gps list<\/span><\/span><\/code><\/pre>\n<p>It outputs the following.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>  2     4387d1 Add Siesta, the elegant way to write REST clients<\/span><\/span>\n<span class=\"giallo-l\"><span>  1     23d7a6 WIP: Add BackendClient.login(username:password:) method<\/span><\/span>\n<span class=\"giallo-l\"><span>  0     af73d2 WIP: Add login screen<\/span><\/span><\/code><\/pre><h3 id=\"reorder-our-patches-by-dependency\">Reorder our Patches by Dependency<\/h3>\n<p>Things are looking pretty good but our patches are not ordered correctly based on dependencies. Let's fix this by reordering them using <code>gps rebase<\/code> and changing them into the following order.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>pick 4387d1 Add Siesta, the elegant way to write REST clients<\/span><\/span>\n<span class=\"giallo-l\"><span>pick 23d7a6 WIP: Add BackendClient.login(username:password:) method<\/span><\/span>\n<span class=\"giallo-l\"><span>pick af73d2 WIP: Add login screen<\/span><\/span><\/code><\/pre>\n<p>This order is correctly ordered by dependency, as \"Add Siesta...\" is the inner most core dependency, followed by \"WIP: Add BackendClient.login...\" being the layer that directly depends on \"Add Siesta...\". Following that is the \"WIP: Add login screen\" patch which depends on \"WIP: Add BackendClient.login...\".<\/p>\n<h3 id=\"pull-changes-from-upstream\">Pull Changes from Upstream<\/h3>\n<p>At this point we have a logical, buildable, testable patch that is ready to be submitted for review. However, lets make sure that our patch stack is good with all the other work people have recently published to upstream. We do this as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>gps pull<\/span><\/span><\/code><\/pre>\n<p>This takes the stack of patches and essentially lifts them up and replays them on top of the upstream changes.<\/p>\n<p>The <code>gps pull<\/code> ran successfully with no conflicts. So all the patches in our stack are good.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>  2     af73d2 WIP: Add login screen<\/span><\/span>\n<span class=\"giallo-l\"><span>  1     23d7a6 WIP: Add BackendClient.login(username:password:) method<\/span><\/span>\n<span class=\"giallo-l\"><span>  0     4387d1 Add Siesta, the elegant way to write REST clients<\/span><\/span><\/code><\/pre><h3 id=\"request-review\">Request Review<\/h3>\n<p>We now request review of our \"Add Siesta...\" patch as it is a complete logical patch and we want people to be able to review it while we are working on implementing the rest of our feature.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>gps request-review 0<\/span><\/span><\/code><\/pre>\n<p>An alias for this command is provided as well, <code>gps rr &lt;patch-index&gt;<\/code>.<\/p>\n<p>For this example lets assume that we are using <a rel=\"external\" href=\"https:\/\/github.com\">GitHub<\/a> and have installed the <code>request_review_post_sync<\/code> hook that integrates with <a rel=\"external\" href=\"https:\/\/github.com\">GitHub<\/a>.<\/p>\n<p>In the case where we initially request review of a patch it creates a pull request for us on <a rel=\"external\" href=\"https:\/\/github.com\">GitHub<\/a>.<\/p>\n<p>Getting feedback on this inner most dependency first is important as the architectural decisions around it can impact the work in the layers above.<\/p>\n<h3 id=\"back-to-the-client-api\">Back to the Client API<\/h3>\n<p>One of the benefits of working in a stack of patches is that we can continue building on top of our previous patches even after we have requested review of them.<\/p>\n<p>Now that we have the Siesta library available to us, we go back to our <code>BackendClient.login(username:password:)<\/code> method and finish implementing it using the Siesta library.<\/p>\n<p>Once we are done we create a new patch on our stack with the message, \"finish BackendClient.login(username:password:)\", using <code>gps add<\/code> &amp; <code>gps create-patch<\/code>. <em>Note<\/em>: We don't care so much about this patch message as it is a temporary patch. We just need to know what it does in relation to the other patches in our stack.<\/p>\n<p>If we checkout our stack of patches with <code>gps list<\/code> it looks something like this now.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>  3     df71da finish BackendClient.login(username:password:)<\/span><\/span>\n<span class=\"giallo-l\"><span>  2     af73d2 WIP: Add login screen<\/span><\/span>\n<span class=\"giallo-l\"><span>  1     23d7a6 WIP: Add BackendClient.login(username:password:) method<\/span><\/span>\n<span class=\"giallo-l\"><span>  0 rr  4387d1 Add Siesta, the elegant way to write REST clients<\/span><\/span><\/code><\/pre><h3 id=\"reorder-squash\">Reorder &amp; Squash<\/h3>\n<p>Looks like we have the makings of another logical patch. However, it is made up of two separate patches, the \"WIP: Add BackendClient.login...\" patch and the \"finish BackendClient.login...\" patch.<\/p>\n<p>Lets reorder these patches and squash them together using <code>gps rebase<\/code> to turn them into a single logical patch.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>pick 4387d1 Add Siesta, the elegant way to write REST clients<\/span><\/span>\n<span class=\"giallo-l\"><span>pick 23d7a6 WIP: Add BackendClient.login(username:password:) method<\/span><\/span>\n<span class=\"giallo-l\"><span>s df71da finish BackendClient.login(username:password:)<\/span><\/span>\n<span class=\"giallo-l\"><span>pick af73d2 WIP: Add login screen<\/span><\/span><\/code><\/pre>\n<p>When we save and quit the editor it will open the squashed patch descriptions up to create the singular patch message. We remove the temporary patch message and remove the \"WIP:\" prefix from the summary as this is now a complete, buildable, testable, logical patch.<\/p>\n<p>If we list our patches with <code>gps list<\/code> again. Our patch stack now looks as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>  2     af73d2 WIP: Add login screen<\/span><\/span>\n<span class=\"giallo-l\"><span>  1     23d7a6 Add BackendClient.login(username:password:) method<\/span><\/span>\n<span class=\"giallo-l\"><span>  0 rr+ 4387d1 Add Siesta, the elegant way to write REST clients<\/span><\/span><\/code><\/pre><h3 id=\"review-feedback\">Review Feedback<\/h3>\n<p>We just got a notification that someone reviewed our first patch and requested some changes. Instead of using <em>Siesta<\/em> as our REST library they want to use <em>Alamofire<\/em> and have some decent arguments for doing so.<\/p>\n<p>So looking at our stack of patches we need to edit the \"Add Siesta...\" patch to change it to Alamofire.<\/p>\n<p>We can accomplish this by using <code>gps rebase<\/code> and changing the \"Add Siesta...\" patches rebase command from <code>pick<\/code> to <code>edit<\/code>. When we save and quit the editor it will drop us into being able to edit the patch we marked with <code>edit<\/code>.<\/p>\n<p>So we make the necessary changes, removing Siesta from the <code>Package.swift<\/code> and adding Alamofire.<\/p>\n<p>We then stage these changes with <code>gps add .<\/code> and amend them to the \"Add Siesta...\" patch using <code>gps amend-patch<\/code>. This in turn opens our editor with the patch message allowing us to update it.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>Add Alamofire as a REST client<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>So that we can easily make REST requests.<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>We intend to use this inside of our BackendClient.login(username:password:)<\/span><\/span>\n<span class=\"giallo-l\"><span>method to make the login REST API request.<\/span><\/span><\/code><\/pre>\n<p>Once we have saved and quit the editor the patch has been updated. However, we still need to complete the rebase which stopped in the middle.<\/p>\n<p>To do this we simply run <code>gps rebase --continue<\/code>.<\/p>\n<h3 id=\"re-request-review\">Re-Request Review<\/h3>\n<p>If we checkout our patch stack it should look something like this.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>  2     af73d2 WIP: Add login screen<\/span><\/span>\n<span class=\"giallo-l\"><span>  1     23d7a6 Add BackendClient.login(username:password:) method<\/span><\/span>\n<span class=\"giallo-l\"><span>  0 rr+ 4387d1 Add Alamofire as a REST client<\/span><\/span><\/code><\/pre>\n<p>The <code>rr+<\/code> is indicating that there have been changes made to that patch since we last requested review of it. So we need to re-request review so that the reviewer can see the recent changes we have made.<\/p>\n<p>We accomplish this by simply running <code>gps request-review 0<\/code> again.<\/p>\n<p>This will update the previously created pull request for this patch so that the reviewer and can re-review it.<\/p>\n<h3 id=\"rework-backendclient\">Rework BackendClient<\/h3>\n<p>Now that our core dependency is ready for review again we can focus on the layer above that again. Since our lower layer dependency changed we need to update our \"Add BackendClient.login...\" patch to use Alamofire instead of Siesta.<\/p>\n<p>To do this we simply use <code>gps rebase<\/code> again and mark the \"Add BackendClient.login...\" patch for <code>edit<\/code>. It drops us back to the console waiting for us to edit that patch.<\/p>\n<p>We do so by making the necessary changes, staging them with <code>gps add .<\/code>, and then amending them with <code>gps amend-patch<\/code>. When prompted to the update the message we simply save and quit the editor because the message is still correct.<\/p>\n<p>Then we run <code>gps rebase --continue<\/code> to have it finish the rebase process.<\/p>\n<p>Leaving our patch stack as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>  2     af73d2 WIP: Add login screen<\/span><\/span>\n<span class=\"giallo-l\"><span>  1     23d7a6 Add BackendClient.login(username:password:) method<\/span><\/span>\n<span class=\"giallo-l\"><span>  0 rr+ 4387d1 Add Alamofire as a REST client<\/span><\/span><\/code><\/pre><h3 id=\"request-review-of-the-2nd-patch\">Request Review of the 2nd patch<\/h3>\n<p>Now that we have our second patch complete. We pull the upstream changes again with <code>gps pull<\/code> and then request review of our second patch with the following.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>gps request-review 1<\/span><\/span><\/code><\/pre>\n<p>Creating a pull request and leaving our stack of patches in the following state.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>  2     af73d2 WIP: Add login screen<\/span><\/span>\n<span class=\"giallo-l\"><span>  1 rr  23d7a6 Add BackendClient.login(username:password:) method<\/span><\/span>\n<span class=\"giallo-l\"><span>  0 rr+ 4387d1 Add Alamofire as a REST client<\/span><\/span><\/code><\/pre><h3 id=\"approval-arrives\">Approval Arrives<\/h3>\n<p>Meanwhile the approval notice has come in for our \"Add Alamofire...\" patch.<\/p>\n<p>So we integrate this patch upstream by running the following.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>gps integrate 0<\/span><\/span><\/code><\/pre>\n<p>There is an alias for this command as well, <code>gps int &lt;patch-index&gt;<\/code>.<\/p>\n<p>This will push that patch up to the remote branch leaving our stack of patches in the following state.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>  2     af73d2 WIP: Add login screen<\/span><\/span>\n<span class=\"giallo-l\"><span>  1 rr  23d7a6 Add BackendClient.login(username:password:) method<\/span><\/span>\n<span class=\"giallo-l\"><span>  0 int 4387d1 Add Alamofire as a REST client<\/span><\/span><\/code><\/pre><h3 id=\"pull-and-collapse\">Pull and Collapse<\/h3>\n<p>We decided to pull in any changes from upstream with <code>gps pull<\/code> making our patch stack now look as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>  1     af73d2 WIP: Add login screen<\/span><\/span>\n<span class=\"giallo-l\"><span>  0 rr  23d7a6 Add BackendClient.login(username:password:) method<\/span><\/span><\/code><\/pre>\n<p>You will notice that the \"Add Alamofire...\" patch has left our patch stack. This is because it has conceptually moved from our stack of patches into its upstream branch.<\/p>\n<h3 id=\"back-to-the-ui\">Back to the UI<\/h3>\n<p>We have now worked our way back up to the outer most layer of our application architecture, the UI. Here we make changes to wire the submit button of the Login screen up to the <code>BackendClient.login(username:password:)<\/code> method completing this feature.<\/p>\n<p>Since our \"WIP: Add login screen\" patch is the top most patch. We can simply amend these changes to that patch by staging the changes with <code>gps add .<\/code> and doing the following:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>gps amend-patch<\/span><\/span><\/code><\/pre>\n<p>This opens the patch message in our editor and allows us to remove the \"WIP:\" prefix from the patch summary.<\/p>\n<p>If we list our patch stack with <code>gps list<\/code> it should look as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>  1     af73d2 Add login screen<\/span><\/span>\n<span class=\"giallo-l\"><span>  0 rr  23d7a6 Add BackendClient.login(username:password:) method<\/span><\/span><\/code><\/pre>\n<p>Then we integrate upstream changes with <code>gps pull<\/code> and request review of our final patch using <code>gps request-review 1<\/code>.<\/p>\n<p>Leaving our patch stack in the following state.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>  1 rr  af73d2 Add login screen<\/span><\/span>\n<span class=\"giallo-l\"><span>  0 rr+ 23d7a6 Add BackendClient.login(username:password:) method<\/span><\/span><\/code><\/pre><h3 id=\"onto-the-next\">Onto the Next<\/h3>\n<p>At this point we have requested review of all our patches for this feature and are waiting for feedback. However, we don't just sit idle, waiting for the feedback. We naturally start our next task in the same manner, Outside-In, and adding, reording, and updating patches on top of our stack.<\/p>\n<p>You might be saying to yourself, \"Hey, but we have this other feature's patches here.\" You are correct we do. This is fine and in lots of cases it is actually more beneficial to continue building new features on top of the ones you have locally. And if for some reason you find out that this new feature has priority over the one that was just completed. Simply reorder the patches in your stack to represent that.<\/p>\n<h3 id=\"more-acceptance\">More Acceptance<\/h3>\n<p>Over the next couple hours reviewers find time to review the other two patches and they are both approved.<\/p>\n<p>We integrate them using <code>gps integrate 0<\/code> and <code>gps integrate 1<\/code> changing our patch stack state to the following.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>  1 int af73d2 Add login screen<\/span><\/span>\n<span class=\"giallo-l\"><span>  0 int 23d7a6 Add BackendClient.login(username:password:) method<\/span><\/span><\/code><\/pre>\n<p>After integrating these patches we run <code>gps pull<\/code> to integrate any upstream changes. In this case it includes the two patches we just published. So when we run <code>gps list<\/code> now we see an empty patch stack.<\/p>\n<h3 id=\"feature-complete\">Feature Complete<\/h3>\n<p>At this point our feature has been developed, reviewed, and integrated. We are done with this task and can now move on to the next task.<\/p>\n<h2 id=\"it-is-just-git\">It is just Git<\/h2>\n<p>Fundamentally <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/git-ps-rs\">git-ps-rs<\/a> is just a light layer of automation of underlying Git commands. So it is <strong>very important<\/strong> to know Git. Especially in relation to this idea of iterating on your patches to a point where they are ready for review.<\/p>\n<p>If we just look at <a rel=\"external\" href=\"https:\/\/git-scm.com\/docs\/git-rebase\">interactive rebases<\/a> in Git we see that there are many things you can do within an interactive rebase. For example you can squash commits, fixup commits, edit commits, reorder commits, reword commit descriptions, and many more. Also there are other Git commands that can aid in this evolution of your patches. For example <a rel=\"external\" href=\"https:\/\/drewdeponte.com\/blog\/git-add-patch-wont-split\/\"><code>git add -p<\/code><\/a> and <a rel=\"external\" href=\"https:\/\/git-scm.com\/docs\/git-commit\"><code>git commit --amend<\/code><\/a>. We have provided some commands to help with these, <code>gps add -p<\/code> &amp; <code>gps amend-patch<\/code>.<\/p>\n<p>Beyond that, it is worth understanding how you can use these tools to do things like <a rel=\"external\" href=\"https:\/\/drewdeponte.com\/blog\/git-splitting-commits\/\">Split Commits Up<\/a>. Please take the time to make sure you understand these Git commands and how to use them. It will not only improve your workflow within this methodology but with using Git in general.<\/p>\n<p>And remember, this is just Git. If we ever run into situations where we need to step outside this methodology-say, to use a branch-there is nothing preventing us from doing so. In fact <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/git-ps-rs\">git-ps-rs<\/a> sees any branch with a tracked upstream branch as a patch stack. So you can think of branches as separate stacks of patches.<\/p>\n<p>We have found this flexibility to be useful when working with clients that don't follow the Patch Stack methodology. It enables us to still follow the practices of Patch Stack locally despite our client using a different methodology, e.g. Feature Branch development. This enables us to get at least some of the beneficial values of the Patch Stack methodology while still playing nice with others.<\/p>\n"},{"title":"Pullwalla - A New Era","published":"2021-01-20T00:00:00+00:00","updated":"2021-01-20T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/pullwalla-a-new-era\/"}},"id":"https:\/\/drewdeponte.com\/blog\/pullwalla-a-new-era\/","content":"<p>Woot woot! The first couple releases of the year are out \ud83c\udf89. These releases include major features as well as important bug fixes.<\/p>\n<h2 id=\"ios\">iOS<\/h2>\n<p>Pullwalla iOS has always helped you <strong>discover<\/strong> and <strong>examine<\/strong> pull requests you want to review across all your accounts &amp; repositories.<\/p>\n<p>This release marks the beginning of a new era where Pullwalla begins to achieve it's true vision as <strong>the primary interface for Pull Request Management<\/strong>, regardless of platform or repository strategy.<\/p>\n<p>One of the big pieces of the <strong>review<\/strong> and <strong>approve<\/strong> puzzle that was missing was the new Pull Request Diffs view. With Pullwalla iOS <a rel=\"external\" href=\"https:\/\/pullwalla.com\/changelog-ios\/\">v1.5.0<\/a> and <a rel=\"external\" href=\"https:\/\/pullwalla.com\/changelog-ios\/\">v1.5.1<\/a> you can now access it via the \"File Changes\" button on the Pull Request Details view. Below is a screenshot in it's beautiful dark mode theme.<\/p>\n<p><img src=\"https:\/\/files.pullwalla.com\/pullwalla-diffs-view-iphone-dark-mode@2x.png\" alt=\"Pullwalla iPhone Diffs View Dark Mode\" \/><\/p>\n<p>This view presents all of the diffs associated with the given pull request and their associated hunks in horizontally scrollable panes. Letting you <strong>review<\/strong> every last change.<\/p>\n<p>The Pull Request Diffs view also looks great in light mode.<\/p>\n<p><img src=\"https:\/\/files.pullwalla.com\/pullwalla-diffs-view-ipad-light-mode@2x.png\" alt=\"Pullwalla iPad Diffs View Light Mode\" \/><\/p>\n<h2 id=\"bug-fixes\">Bug Fixes<\/h2>\n<p>In addition to bringing Pullwalla iOS into a state where it completes the natural chain of <strong>discover<\/strong>, <strong>examine<\/strong>, <strong>review<\/strong>, <strong>discuss<\/strong>, and <strong>approve<\/strong>, these releases also include a number of critical bug fixes to further stabilize support for Bitbucket Server in both Pullwalla iOS and macOS.<\/p>\n<p>If you want to see the nitty gritty details, check out the Pullwalla <a rel=\"external\" href=\"https:\/\/pullwalla.com\/changelog-ios\/\">iOS Changelog<\/a> and <a rel=\"external\" href=\"https:\/\/pullwalla.com\/changelog\/\">macOS Changelog<\/a>.<\/p>\n<h3 id=\"developer-notes\">Developer Notes<\/h3>\n<p>In this new era we are continuing our work to add support for other platforms (e.g. GitHub Enterprise), extend functionality to facilitate the peer review process, and bridge the gap between Pullwalla iOS &amp; macOS with the use of shared <a rel=\"external\" href=\"https:\/\/developer.apple.com\/xcode\/swiftui\/\">SwiftUI<\/a> components, like our new Pull Request Diffs view.<\/p>\n<h3 id=\"thoughts-or-suggestions\">Thoughts or suggestions?<\/h3>\n<p>As always, we\u2019d love to hear from you.<\/p>\n<p>Cheers,\nThe Pullwalla Team<\/p>\n"},{"title":"git add -p won't split","published":"2021-01-19T00:00:00+00:00","updated":"2021-01-19T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/git-add-patch-wont-split\/"}},"id":"https:\/\/drewdeponte.com\/blog\/git-add-patch-wont-split\/","content":"<p>It is amazing how many developers I run into that don't seem to either know\nabout <code>git add -p<\/code> (a.k.a. git add patch), and ever fewer that know what to do\nwhen <code>git add -p<\/code> won't let you split your hunks up any further. Given how much\nI use this command in my workflow to facilitate creating logically chunked,\nbuildable commits I figured I might as well make a post just touching on it.<\/p>\n<p>Yes, this is something that less keyboard driven developers often ignore as\nthey use a GUI (ex: <a rel=\"external\" href=\"https:\/\/www.sublimemerge.com\">Sublime Merge<\/a>) to stage things at a smaller\ngranularity. To be completely honest I use <a rel=\"external\" href=\"https:\/\/www.sublimemerge.com\">Sublime Merge<\/a> to stage changes\nmyself sometimes. However, there are a large number of times where I am already\nworking on the command line with <a rel=\"external\" href=\"http:\/\/git-scm.com\">Git<\/a> and therefore it is quicker and easier\njust to stay on the keyboard and use <code>git add -p<\/code>.<\/p>\n<p>So lets break things down a bit.<\/p>\n<h2 id=\"git-add\">git add<\/h2>\n<p><code>git add<\/code> is a command that allows you to take local changes and move them to\nthe staging area in preparation for making a commit. The idea is that you move\nall the changes you want in your commit into the staging area and then turn the\nchanges in the staging area into your commit.<\/p>\n<h2 id=\"git-add-p\">git add -p<\/h2>\n<p>When <code>git add<\/code> is executed with the <code>-p<\/code> option it instructs <code>git add<\/code> to allow\nyou to select a portion of the changes in a local file for staging rather than\nthe entire set of changes in that local file.<\/p>\n<p>Lets look at an example to see exactly how this works.<\/p>\n<p>For sake of discussion lets say we have the following local, unstaged changes.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>\u276f git diff<\/span><\/span>\n<span class=\"giallo-l\"><span>diff --git a\/README b\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>index d325bcb..f8f8d46 100644<\/span><\/span>\n<span class=\"giallo-l\"><span>--- a\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>+++ b\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>@@ -5,3 +5,9 @@ Subtitle of README<\/span><\/span>\n<span class=\"giallo-l\"><span> Description of the README<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span> First paragraph of the README<\/span><\/span>\n<span class=\"giallo-l\"><span>+<\/span><\/span>\n<span class=\"giallo-l\"><span>+Second paragraph<\/span><\/span>\n<span class=\"giallo-l\"><span>+<\/span><\/span>\n<span class=\"giallo-l\"><span>+Third paragraph<\/span><\/span>\n<span class=\"giallo-l\"><span>+<\/span><\/span>\n<span class=\"giallo-l\"><span>+Fourth paragraph<\/span><\/span><\/code><\/pre>\n<p>Given that we are following best practices because we want all the awesome Git\ncommands to be able to work on our repo and we want our Git history to be\nuseful we decide we want to create three separate commits, one to add each of\nthe paragraphs in the diff.<\/p>\n<p>To do this we first start out by running the following:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>git add -p README<\/span><\/span><\/code><\/pre>\n<p>This prompts us with:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>\u276f git add -p README<\/span><\/span>\n<span class=\"giallo-l\"><span>diff --git a\/README b\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>index d325bcb..f8f8d46 100644<\/span><\/span>\n<span class=\"giallo-l\"><span>--- a\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>+++ b\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>@@ -5,3 +5,9 @@ Subtitle of README<\/span><\/span>\n<span class=\"giallo-l\"><span> Description of the README<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span> First paragraph of the README<\/span><\/span>\n<span class=\"giallo-l\"><span>+<\/span><\/span>\n<span class=\"giallo-l\"><span>+Second paragraph<\/span><\/span>\n<span class=\"giallo-l\"><span>+<\/span><\/span>\n<span class=\"giallo-l\"><span>+Third paragraph<\/span><\/span>\n<span class=\"giallo-l\"><span>+<\/span><\/span>\n<span class=\"giallo-l\"><span>+Fourth paragraph<\/span><\/span>\n<span class=\"giallo-l\"><span>(1\/1) Stage this hunk [y,n,q,a,d,e,?]?<\/span><\/span><\/code><\/pre><h2 id=\"oh-no-where-is-s\">Oh No! Where is s?<\/h2>\n<p>Most of the time when you run <code>git add -p<\/code> it will present <code>s<\/code> as one of the\nchoices in the output. <code>s<\/code> stands for split and tells <code>git add -p<\/code> to split the\ncurrently presented hunk into smaller hunks and then ask you if you want to\nstage the smaller hunks.<\/p>\n<p>But in this case and generally when you get your hunks small enough it won't\nsplit them any further. At this point you have use one of the other options it\nprovides.<\/p>\n<h2 id=\"woot-woot-e-here-we-come\">Woot woot, e here we come!<\/h2>\n<p>The <code>e<\/code> option stands for edit and it puts us into manual hunk edit mode in our\neditor. This looks like the following.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span># Manual hunk edit mode -- see bottom for a quick guide.<\/span><\/span>\n<span class=\"giallo-l\"><span>@@ -5,3 +5,9 @@ Subtitle of README<\/span><\/span>\n<span class=\"giallo-l\"><span> Description of the README<\/span><\/span>\n<span class=\"giallo-l\"><span> <\/span><\/span>\n<span class=\"giallo-l\"><span> First paragraph of the README<\/span><\/span>\n<span class=\"giallo-l\"><span>+<\/span><\/span>\n<span class=\"giallo-l\"><span>+Second paragraph<\/span><\/span>\n<span class=\"giallo-l\"><span>+<\/span><\/span>\n<span class=\"giallo-l\"><span>+Third paragraph<\/span><\/span>\n<span class=\"giallo-l\"><span>+<\/span><\/span>\n<span class=\"giallo-l\"><span>+Fourth paragraph<\/span><\/span>\n<span class=\"giallo-l\"><span># ---<\/span><\/span>\n<span class=\"giallo-l\"><span># To remove &#39;-&#39; lines, make them &#39; &#39; lines (context).<\/span><\/span>\n<span class=\"giallo-l\"><span># To remove &#39;+&#39; lines, delete them.<\/span><\/span>\n<span class=\"giallo-l\"><span># Lines starting with # will be removed.<\/span><\/span>\n<span class=\"giallo-l\"><span># <\/span><\/span>\n<span class=\"giallo-l\"><span># If the patch applies cleanly, the edited hunk will immediately be<\/span><\/span>\n<span class=\"giallo-l\"><span># marked for staging.<\/span><\/span>\n<span class=\"giallo-l\"><span># If it does not apply cleanly, you will be given an opportunity to<\/span><\/span>\n<span class=\"giallo-l\"><span># edit again.  If all lines of the hunk are removed, then the edit is<\/span><\/span>\n<span class=\"giallo-l\"><span># aborted and the hunk is left unchanged.<\/span><\/span><\/code><\/pre>\n<p>As we can see from the comments we can simply delete the '+' lines we don't\nwant in this hunk. So in this case I edit the hunk to look as follows and save\nand quit the editor.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>@@ -5,3 +5,9 @@ Subtitle of README<\/span><\/span>\n<span class=\"giallo-l\"><span> Description of the README<\/span><\/span>\n<span class=\"giallo-l\"><span> <\/span><\/span>\n<span class=\"giallo-l\"><span> First paragraph of the README<\/span><\/span>\n<span class=\"giallo-l\"><span>+<\/span><\/span>\n<span class=\"giallo-l\"><span>+Second paragraph<\/span><\/span><\/code><\/pre>\n<p>If we run <code>git status<\/code> we can see it resulted in part of the changes be staged\nand the other part not. Lets verify the parts are as we expect.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>\u276f git diff --staged<\/span><\/span>\n<span class=\"giallo-l\"><span>diff --git a\/README b\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>index d325bcb..8f6f97e 100644<\/span><\/span>\n<span class=\"giallo-l\"><span>--- a\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>+++ b\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>@@ -5,3 +5,5 @@ Subtitle of README<\/span><\/span>\n<span class=\"giallo-l\"><span> Description of the README<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span> First paragraph of the README<\/span><\/span>\n<span class=\"giallo-l\"><span>+<\/span><\/span>\n<span class=\"giallo-l\"><span>+Second paragraph<\/span><\/span><\/code><\/pre>\n<p>The staged changes are correct.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>\u276f git diff<\/span><\/span>\n<span class=\"giallo-l\"><span>diff --git a\/README b\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>index 8f6f97e..f8f8d46 100644<\/span><\/span>\n<span class=\"giallo-l\"><span>--- a\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>+++ b\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>@@ -7,3 +7,7 @@ Description of the README<\/span><\/span>\n<span class=\"giallo-l\"><span> First paragraph of the README<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span> Second paragraph<\/span><\/span>\n<span class=\"giallo-l\"><span>+<\/span><\/span>\n<span class=\"giallo-l\"><span>+Third paragraph<\/span><\/span>\n<span class=\"giallo-l\"><span>+<\/span><\/span>\n<span class=\"giallo-l\"><span>+Fourth paragraph<\/span><\/span><\/code><\/pre>\n<p>And the unstaged changes are correct.<\/p>\n<p>So now we can make our first commit, <code>git commit -m \"first commit\"<\/code>.<\/p>\n<p>From here we can simply rinse and repeat the process to end up with isolated\nlogically chunked commits.<\/p>\n"},{"title":"Git - Splitting Commits","published":"2021-01-19T00:00:00+00:00","updated":"2021-01-19T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/git-splitting-commits\/"}},"id":"https:\/\/drewdeponte.com\/blog\/git-splitting-commits\/","content":"<p>When you first discover the importance of logically structured <a rel=\"external\" href=\"http:\/\/git-scm.com\">Git<\/a> commits,\nwhich is by the way a core fundamental expectation &amp; design characteristic of\n<a rel=\"external\" href=\"http:\/\/git-scm.com\">Git<\/a> and how it is intended to be used. You are probably still following\nyour bad habits and creating commits that aren't logical commits.<\/p>\n<p>This naturally leads to the question, \"How do I split a commit up into multiple\ncommits?\" Below I present a simple contrived example so that you can learn the\nmechanics and process of doing this, as the mechanics &amp; process don't change.<\/p>\n<p>The other topic related to this which this post <strong>does not<\/strong> cover is the\nprocess of taking code and splitting it up into logical chunks. This generally\ntakes an understanding of the application architecture involved &amp; the\ndependency relationship between the various elements.<\/p>\n<p>So lets get to it.<\/p>\n<h2 id=\"tldr\">TLDR<\/h2>\n<p>For those who just want a quick reminder reference here is the TLDR. For those\nwho need a bit more context and detail through the walk through read the\nsections below.<\/p>\n<ul>\n<li><code>git rebase -i<\/code> - do an interactive rebase &amp; mark the commit you want to\nsplit for <code>edit<\/code>, it will drop you out into the shell at that commit<\/li>\n<li><code>git reset HEAD^<\/code> - soft reset the commit<\/li>\n<li><code>git add -p<\/code> - stage just the parts you want in the first commit<\/li>\n<li><code>git commit<\/code> - commit the first commit<\/li>\n<li><code>git add -p<\/code> - stage just the parts you want in the next commit<\/li>\n<li><code>git commit<\/code> - commit the next commit<\/li>\n<li><code>git rebase --continue<\/code> - continue the rebase to play the other commits on\ntop of the new commits you created<\/li>\n<\/ul>\n<h2 id=\"initial-state\">Initial State<\/h2>\n<p>For this example lets assume that we have a Git tree that has the following\ncommits.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>49d9eb4 - (HEAD -&gt; main) Add first paragraph of the README<\/span><\/span>\n<span class=\"giallo-l\"><span>d3e4eee - Add Subtitle &amp; Description of README<\/span><\/span>\n<span class=\"giallo-l\"><span>56fa164 - Add title of README<\/span><\/span>\n<span class=\"giallo-l\"><span>8da3dd9 - (base) Initial commit<\/span><\/span><\/code><\/pre>\n<p>As we can see from the first commit on <code>base<\/code>, <code>8da3dd9 - (base) Initial commit<\/code>, it adds the title to the README.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>\u276f git show 56fa164<\/span><\/span>\n<span class=\"giallo-l\"><span>commit 56fa1649afa886c309ce8e83c5518b48c8600b6f<\/span><\/span>\n<span class=\"giallo-l\"><span>Author: Andrew De Ponte &lt;cyphactor@gmail.com&gt;<\/span><\/span>\n<span class=\"giallo-l\"><span>Date:   Tue Jan 19 23:48:13 2021 -0500<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\t\tAdd title of README<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>diff --git a\/README b\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>index e79c5e8..4b3b7a8 100644<\/span><\/span>\n<span class=\"giallo-l\"><span>--- a\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>+++ b\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>@@ -1 +1,2 @@<\/span><\/span>\n<span class=\"giallo-l\"><span> initial<\/span><\/span>\n<span class=\"giallo-l\"><span>+Title of README<\/span><\/span><\/code><\/pre>\n<p>The second commit, <code>d3e4eee - Add Subtitle &amp; Description of README<\/code>, adds the\nsubtitle and the description as seen in the diff below.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>\u276f git show d3e4eee<\/span><\/span>\n<span class=\"giallo-l\"><span>commit d3e4eeec96f9c3cd3eadeb4d95256a6326485ed4<\/span><\/span>\n<span class=\"giallo-l\"><span>Author: Andrew De Ponte &lt;cyphactor@gmail.com&gt;<\/span><\/span>\n<span class=\"giallo-l\"><span>Date:   Tue Jan 19 23:48:52 2021 -0500<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\t\tAdd Subtitle &amp; Description of README<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>diff --git a\/README b\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>index 4b3b7a8..cf3aba6 100644<\/span><\/span>\n<span class=\"giallo-l\"><span>--- a\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>+++ b\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>@@ -1,2 +1,5 @@<\/span><\/span>\n<span class=\"giallo-l\"><span> initial<\/span><\/span>\n<span class=\"giallo-l\"><span> Title of README<\/span><\/span>\n<span class=\"giallo-l\"><span>+Subtitle of README<\/span><\/span>\n<span class=\"giallo-l\"><span>+<\/span><\/span>\n<span class=\"giallo-l\"><span>+Description of the README<\/span><\/span><\/code><\/pre>\n<p>The third commit, <code>49d9eb4 - (HEAD -&gt; main) Add first paragraph of the README<\/code>\nadds the first paragraph in the diff below.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>\u276f git show 49d9eb4<\/span><\/span>\n<span class=\"giallo-l\"><span>commit 49d9eb41bd3a0031f537b3865f6c90329f173bc4 (HEAD -&gt; main)<\/span><\/span>\n<span class=\"giallo-l\"><span>Author: Andrew De Ponte &lt;cyphactor@gmail.com&gt;<\/span><\/span>\n<span class=\"giallo-l\"><span>Date:   Tue Jan 19 23:49:51 2021 -0500<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\t\tAdd first paragraph of the README<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>diff --git a\/README b\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>index cf3aba6..d325bcb 100644<\/span><\/span>\n<span class=\"giallo-l\"><span>--- a\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>+++ b\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>@@ -3,3 +3,5 @@ Title of README<\/span><\/span>\n<span class=\"giallo-l\"><span> Subtitle of README<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span> Description of the README<\/span><\/span>\n<span class=\"giallo-l\"><span>+<\/span><\/span>\n<span class=\"giallo-l\"><span>+First paragraph of the README<\/span><\/span><\/code><\/pre><h2 id=\"non-logically-structured-commit\">Non-Logically Structured Commit<\/h2>\n<p>Looking at the commit summaries &amp; the diffs themselves we can see that the\nsecond commit, <code>d3e4eee - Add Subtitle &amp; Description of README<\/code> is actually\ndoing two logical things. First it is adding the subtitle. Secondly it is\nadding the description to the README.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>\u276f git show d3e4eee<\/span><\/span>\n<span class=\"giallo-l\"><span>commit d3e4eeec96f9c3cd3eadeb4d95256a6326485ed4<\/span><\/span>\n<span class=\"giallo-l\"><span>Author: Andrew De Ponte &lt;cyphactor@gmail.com&gt;<\/span><\/span>\n<span class=\"giallo-l\"><span>Date:   Tue Jan 19 23:48:52 2021 -0500<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\t\tAdd Subtitle &amp; Description of README<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>diff --git a\/README b\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>index 4b3b7a8..cf3aba6 100644<\/span><\/span>\n<span class=\"giallo-l\"><span>--- a\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>+++ b\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>@@ -1,2 +1,5 @@<\/span><\/span>\n<span class=\"giallo-l\"><span> initial<\/span><\/span>\n<span class=\"giallo-l\"><span> Title of README<\/span><\/span>\n<span class=\"giallo-l\"><span>+Subtitle of README<\/span><\/span>\n<span class=\"giallo-l\"><span>+<\/span><\/span>\n<span class=\"giallo-l\"><span>+Description of the README<\/span><\/span><\/code><\/pre>\n<p>Instead of the above what we really wanted to have was one commit that adds\nthe subtitle and a separate commit that adds the description as two isolated\nlogical chunks.<\/p>\n<h3 id=\"edit-mode\">Edit Mode<\/h3>\n<p>To accomplish this we need to utilize an interactive rebase to enter \"edit\"\nmode in the correct place in the <a rel=\"external\" href=\"http:\/\/git-scm.com\">Git<\/a> history. In this particular case we\nwant to rebase the 3 commits we looked at onto <code>base<\/code>.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>git rebase -i base<\/span><\/span><\/code><\/pre>\n<p>This will bring up the following in your editor.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>pick 56fa164 Add title of README<\/span><\/span>\n<span class=\"giallo-l\"><span>pick d3e4eee Add Subtitle &amp; Description of README<\/span><\/span>\n<span class=\"giallo-l\"><span>pick 49d9eb4 Add first paragraph of the README<\/span><\/span><\/code><\/pre>\n<p>In the interactive rebase buffer we can change the action for the middle commit\nto <code>edit<\/code> so it as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>pick 56fa164 Add title of README<\/span><\/span>\n<span class=\"giallo-l\"><span>edit d3e4eee Add Subtitle &amp; Description of README<\/span><\/span>\n<span class=\"giallo-l\"><span>pick 49d9eb4 Add first paragraph of the README<\/span><\/span><\/code><\/pre>\n<p>When you save &amp; quit the editor it will run the specified interactive rebase\ncommands. In this case pick (meaning keep) the first commit and then stop on\nthe second commit allowing for editing because we specified, <code>edit<\/code>. When it\ndoes this will drop you back to the console with a message similar to the\nfollowing:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>Stopped at d3e4eee...  Add Subtitle &amp; Description of README<\/span><\/span>\n<span class=\"giallo-l\"><span>You can amend the commit now, with<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\tgit commit --amend &#39;-S&#39;<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>Once you are satisfied with your changes, run<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\tgit rebase --continue<\/span><\/span><\/code><\/pre><h3 id=\"split-the-commit\">Split the Commit<\/h3>\n<p>We want to split the changes currently held in this commit into multiple\ncommits. To do this we need to reset the commit that we are currently on and\nthen partially stage the changes back commit, then stage the other part and\ncommit, and then continue the rebase.<\/p>\n<p>So first we have to reset just the commit that we are checked out on and we\nwant to do a soft reset. So we do the following:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>git reset HEAD^<\/span><\/span><\/code><\/pre>\n<p>Now if we run <code>git diff<\/code> to see the now local ustagged changes we see the\nfollowing.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>\u276f git di<\/span><\/span>\n<span class=\"giallo-l\"><span>diff --git a\/README b\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>index 4b3b7a8..cf3aba6 100644<\/span><\/span>\n<span class=\"giallo-l\"><span>--- a\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>+++ b\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>@@ -1,2 +1,5 @@<\/span><\/span>\n<span class=\"giallo-l\"><span> initial<\/span><\/span>\n<span class=\"giallo-l\"><span> Title of README<\/span><\/span>\n<span class=\"giallo-l\"><span>+Subtitle of README<\/span><\/span>\n<span class=\"giallo-l\"><span>+<\/span><\/span>\n<span class=\"giallo-l\"><span>+Description of the README<\/span><\/span><\/code><\/pre>\n<p>As we can see we now have the changes that add both the subtitle &amp; the\ndescription locally.<\/p>\n<p>So we first want to stage a commit with just the <code>Subtitle of README<\/code> portion.\nTo do this we need to use <code>git add -p README<\/code> to do a partial stage of the\nREADME files changes to just stage the <code>Subtitle of README<\/code> portion. See my\npost, <a href=\"\/blog\/git-add-patch-wont-split\/\">git add patch won't split<\/a> for details\non how to accomplish this.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>+<\/span><\/span>\n<span class=\"giallo-l\"><span>+Description of the README<\/span><\/span><\/code><\/pre>\n<p>If you run <code>git status<\/code> to check on things at this point it should look like this.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>\u276f git status<\/span><\/span>\n<span class=\"giallo-l\"><span>interactive rebase in progress; onto 8da3dd9<\/span><\/span>\n<span class=\"giallo-l\"><span>Last commands done (2 commands done):<\/span><\/span>\n<span class=\"giallo-l\"><span>\t pick 56fa164 Add title of README<\/span><\/span>\n<span class=\"giallo-l\"><span>\t edit d3e4eee Add Subtitle &amp; Description of README<\/span><\/span>\n<span class=\"giallo-l\"><span>Next command to do (1 remaining command):<\/span><\/span>\n<span class=\"giallo-l\"><span>\t pick 49d9eb4 Add first paragraph of the README<\/span><\/span>\n<span class=\"giallo-l\"><span>\t(use &quot;git rebase --edit-todo&quot; to view and edit)<\/span><\/span>\n<span class=\"giallo-l\"><span>You are currently splitting a commit while rebasing branch &#39;main&#39; on &#39;8da3dd9&#39;.<\/span><\/span>\n<span class=\"giallo-l\"><span>\t(Once your working directory is clean, run &quot;git rebase --continue&quot;)<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>Changes to be committed:<\/span><\/span>\n<span class=\"giallo-l\"><span>\t(use &quot;git restore --staged &lt;file&gt;...&quot; to unstage)<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\tmodified:   README<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>Changes not staged for commit:<\/span><\/span>\n<span class=\"giallo-l\"><span>\t(use &quot;git add &lt;file&gt;...&quot; to update what will be committed)<\/span><\/span>\n<span class=\"giallo-l\"><span>\t(use &quot;git restore &lt;file&gt;...&quot; to discard changes in working directory)<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\tmodified:   README<\/span><\/span><\/code><\/pre>\n<p>If we specifically check out the staged changes with <code>git diff --staged<\/code> we get\nthe following.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>\u276f git diff --staged<\/span><\/span>\n<span class=\"giallo-l\"><span>diff --git a\/README b\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>index 4b3b7a8..70e7d0a 100644<\/span><\/span>\n<span class=\"giallo-l\"><span>--- a\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>+++ b\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>@@ -1,2 +1,3 @@<\/span><\/span>\n<span class=\"giallo-l\"><span> initial<\/span><\/span>\n<span class=\"giallo-l\"><span> Title of README<\/span><\/span>\n<span class=\"giallo-l\"><span>+Subtitle of README<\/span><\/span><\/code><\/pre>\n<p>This is exactly what we wanted. But lets make sure the unstaged changes also\nrepresent what we want by running <code>git diff<\/code>.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>\u276f git diff<\/span><\/span>\n<span class=\"giallo-l\"><span>diff --git a\/README b\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>index 70e7d0a..cf3aba6 100644<\/span><\/span>\n<span class=\"giallo-l\"><span>--- a\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>+++ b\/README<\/span><\/span>\n<span class=\"giallo-l\"><span>@@ -1,3 +1,5 @@<\/span><\/span>\n<span class=\"giallo-l\"><span> initial<\/span><\/span>\n<span class=\"giallo-l\"><span> Title of README<\/span><\/span>\n<span class=\"giallo-l\"><span> Subtitle of README<\/span><\/span>\n<span class=\"giallo-l\"><span>+<\/span><\/span>\n<span class=\"giallo-l\"><span>+Description of the README<\/span><\/span><\/code><\/pre>\n<p>Yep looks like they do. So now we just need create the first of the two commits\nthat will replace the commit we marked for edit. This can be done as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>git commit -m &quot;Add Subtitle to README&quot;<\/span><\/span><\/code><\/pre>\n<p>From there we can stage the rest of the changes with <code>git add README<\/code> and\ncreate the second commit with the following.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>git commit -m &quot;Add Description to README&quot;<\/span><\/span><\/code><\/pre><h3 id=\"finish-the-rebase\">Finish the Rebase<\/h3>\n<p>Now that the changes have been split up into separate commits like we wanted we\nnow need to instruct it to finish the interactive rebase that we started with\nthe edit. This is done as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>git rebase --continue<\/span><\/span><\/code><\/pre>\n<p>Once it is complete if we checkout our tree it will look as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>fe12bd6 - (HEAD -&gt; main) Add first paragraph of the README<\/span><\/span>\n<span class=\"giallo-l\"><span>3566dec - Add Description to README<\/span><\/span>\n<span class=\"giallo-l\"><span>f676187 - Add Subtitle to README<\/span><\/span>\n<span class=\"giallo-l\"><span>56fa164 - Add title of README<\/span><\/span>\n<span class=\"giallo-l\"><span>8da3dd9 - (base) Initial commit<\/span><\/span><\/code><\/pre>\n<p>Exactly what we wanted!<\/p>\n"},{"title":"Understanding zsh Startup & Configuration","published":"2021-01-18T00:00:00+00:00","updated":"2021-01-18T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/understanding-zsh-startup-configuration\/"}},"id":"https:\/\/drewdeponte.com\/blog\/understanding-zsh-startup-configuration\/","content":"<p>This is a little diagram I put together years ago when macOS El Capitan came\nout and I ran into issues with how it was impacting my <code>PATH<\/code> environment\nvariable. It ends up that every so often I need to reference it.<\/p>\n<p>Today was one of those days. However, I couldn't remember where I put it. The\nfirst place I looked was my blog. But it wasn't there. Therefore once I found\nit it I decided it was finally time to put it up on my blog. I hope it ends up\nbeing as useful for you as it has been for me over the years.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/understanding-zsh-startup-configuration\/understanding_zsh_startup_on_mac_os_x.png\" alt=\"Understanding zsh Startup Diagram\" \/><\/p>\n<p><strong>Note:<\/strong> This diagram was built with a tool that I very much enjoy using when\nit makes sense, <a rel=\"external\" href=\"https:\/\/monodraw.helftone.com\">Monodraw<\/a>.<\/p>\n"},{"title":"Git Changelog v3.3.2","published":"2021-01-11T00:00:00+00:00","updated":"2021-01-11T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/git-cl-v3-3-2-released\/"}},"id":"https:\/\/drewdeponte.com\/blog\/git-cl-v3-3-2-released\/","content":"<p>I just released <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/git-cl\">Git Changelog<\/a> v3.3.2. To update simply run the following:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>brew update &amp;&amp; brew upgrade git-cl<\/span><\/span><\/code><\/pre><h2 id=\"what-s-changed\">What's Changed?<\/h2>\n<h3 id=\"support-any-branch\">Support any Branch<\/h3>\n<p>It used to be that prior to this release that it would only work on the <code>master<\/code> branch. However with this release it now functions properly when currently checked out on any branch. It walks the Git tree from where you are currently checked out on and builds the history of the changelog. So you generally probably want to be checked out on your mainline branch when running it.<\/p>\n<h2 id=\"availability\">Availability<\/h2>\n<p>This release of [Git Patch Stack][] is available via our Homebrew tap for both macOS Big Sur &amp; macOS Catalina. See the <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/git-cl\">README<\/a> for details this method and others.<\/p>\n"},{"title":"Git Patch Stack v0.5.0","published":"2020-12-10T00:00:00+00:00","updated":"2020-12-10T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/git-ps-v0-5-0-released\/"}},"id":"https:\/\/drewdeponte.com\/blog\/git-ps-v0-5-0-released\/","content":"<p>I just released <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/git-ps\">Git Patch Stack<\/a> v0.5.0. To update simply run the following:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>brew update &amp;&amp; brew upgrade git-ps<\/span><\/span><\/code><\/pre><h2 id=\"what-s-changed\">What's Changed?<\/h2>\n<h3 id=\"multiple-stacks-of-patches\">Multiple Stacks of Patches?<\/h3>\n<p>This release makes it so that <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/git-ps\">Git Patch Stack<\/a> now works with any branch that has an upstream branch, instead of having the patch stack be limited to the previously hard coded <code>master<\/code> &amp; <code>origin\/master<\/code> upstream branch.<\/p>\n<h4 id=\"other-mainlines\">Other Mainlines<\/h4>\n<p>This is useful for making <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/git-ps\">Git Patch Stack<\/a> work with repositories that have other mainlines, e.g. <code>main<\/code> &amp; <code>origin\/main<\/code>.<\/p>\n<h4 id=\"stuck-in-feature-branches\">Stuck in Feature Branches<\/h4>\n<p>It is also useful for managing branches that have upstreams in general. Lets say you are working with a client that doesn\u2019t allow for individual patch pull requests but instead wants a feature branch with a series of logically chunked commits (a.k.a. patches, \ud83d\ude1c). <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/git-ps\">Git Patch Stack<\/a> can now be used to manage the patch stack within that feature branch.<\/p>\n<h3 id=\"removed-explicit-base-branch\">Removed Explicit Base Branch<\/h3>\n<p>This release removes the explicit base branch option from the <code>rr<\/code> and <code>pub<\/code> subcommands because I don't think it is needed now that <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/git-ps\">Git Patch Stack<\/a> has dynamic upstream branch detection. Also, the explicit base branch option was confusing as it existed. If we find the need in the future to facilitate cross-upstream <code>rr<\/code>s or <code>pub<\/code>s we will add it back in a better way.<\/p>\n<h2 id=\"availability\">Availability<\/h2>\n<p>This release of <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/git-ps\">Git Patch Stack<\/a> is available via our Homebrew tap for both macOS Big Sur &amp; macOS Catalina. See the <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/git-ps\">README<\/a> for details this method and others.<\/p>\n"},{"title":"Pullwalla - Big Sur & The Future","published":"2020-11-18T00:00:00+00:00","updated":"2020-11-18T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/pullwalla-macos-big-sur-bitbucket-server\/"}},"id":"https:\/\/drewdeponte.com\/blog\/pullwalla-macos-big-sur-bitbucket-server\/","content":"<p>Hello!<\/p>\n<p>I wanted to clue you into some details around a few recent releases.<\/p>\n<h2 id=\"macos\">macOS<\/h2>\n<p>Pullwalla macOS <a rel=\"external\" href=\"https:\/\/pullwalla.com\/changelog\/v2-6-0-30\/\">v2.6.0<\/a> &amp; <a rel=\"external\" href=\"https:\/\/pullwalla.com\/changelog\/v2-6-1-31\/\">v2.6.1<\/a> both address the following items.<\/p>\n<h4 id=\"macos-big-sur-support\">macOS Big Sur Support<\/h4>\n<p>As I am sure you already know. macOS Big Sur was officially released. With it comes a completely new style that makes macOS Apps feel a little more like iPad apps. Which <a rel=\"external\" href=\"https:\/\/pullwalla.com\">Pullwalla macOS<\/a> now supports.<\/p>\n<p><img src=\"https:\/\/files.pullwalla.com\/pullwalla-macos-2-6-1-31-on-big-sur.png\" alt=\"Pullwalla on Big Sur\" \/><\/p>\n<h4 id=\"missing-pull-requests\">Missing Pull Requests<\/h4>\n<p>One of the ongoing challenges that we have had all along is the lack of documentation across the various platforms we support.  This results in JSON decoding failures on our end, in turn causing a PR or two to not show up every once and a blue moon.<\/p>\n<p>We are constantly addressing these issues as we discover them to make sure you never miss a PR. These releases are no different. Combined we addressed five such issues making sure Pullwalla never misses a PR.<\/p>\n<h2 id=\"ios\">iOS<\/h2>\n<p>Pullwalla iOS <a rel=\"external\" href=\"https:\/\/pullwalla.com\/changelog-ios\/v1-3-2-9\/\">v1.3.2<\/a> &amp; <a rel=\"external\" href=\"https:\/\/pullwalla.com\/changelog-ios\/v1-3-1-8\/\">v1.3.1<\/a> were released addressing the same missing pull requests issues as above in the macOS release.<\/p>\n<h3 id=\"bridging-the-gap-swiftui\">Bridging the Gap (SwiftUI)<\/h3>\n<p>These Pullwalla releases on both macOS &amp; iOS mark the beginning of a new age where we have started building cross-platform UI components using <a rel=\"external\" href=\"https:\/\/developer.apple.com\/xcode\/swiftui\/\">SwiftUI<\/a>.<\/p>\n<p>These releases now use a shared SwiftUI View between both Pullwalla macOS &amp; iOS for adding a Bitbucket Server Account.<\/p>\n<p>This is just the beginning as all the new features &amp; UIs we are building are being done with SwiftUI. Also any views that we have to adjust or rework are being done in SwiftUI. This will help us bring the same set of features to both Pullwalla macOS &amp; iOS.<\/p>\n<h3 id=\"thoughts-or-suggestions\">Thoughts or suggestions?<\/h3>\n<p>As always, we\u2019d love to hear from you.<\/p>\n<p>Cheers,\nThe Pullwalla team<\/p>\n"},{"title":"Keep a Changelog without Conflicts","published":"2020-05-11T00:00:00+00:00","updated":"2020-05-11T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/keep-a-changelog-without-conflicts\/"}},"id":"https:\/\/drewdeponte.com\/blog\/keep-a-changelog-without-conflicts\/","content":"<p>For years, like many others, we have maintained <code>CHANGELOG.md<\/code> files inside our\n<a rel=\"external\" href=\"https:\/\/git-scm.com\">Git<\/a> repositories for our apps and libraries. We have followed the standards\noutlined at <a rel=\"external\" href=\"https:\/\/keepachangelog.com\">keepachangelog.com<\/a> and we have appreciated the benefits it has\nprovided us for preparing releases and for maintaining a clear record of\nchanges included in each release while still being targeted at the appropriate\naudience.<\/p>\n<p>However, these benefits didn't come without drawbacks. The biggest being that\nwhile working on a repo with multiple people or developing locally using a\npatch stack workflow you continually run into <a rel=\"external\" href=\"https:\/\/git-scm.com\">Git<\/a> conflicts solely with the\n<code>CHANGELOG.md<\/code> file. These conflicts aren't meaningful in anyway. They are just\nan impedance to our development flow and process, both as a team and as\nindividual contributors.<\/p>\n<p>Well a few weeks ago <a rel=\"external\" href=\"http:\/\/anthonycastelli.me\">Anthony Castelli<\/a> and <a rel=\"external\" href=\"https:\/\/drewdeponte.com\">I<\/a> decided it was time to\nremove this impedance not only for our local workflows but for the development\nworkflow the team. We started out by analyzing the characteristics of the\n<code>CHANGELOG.md<\/code> approach that we valued and the characteristics that were\ncausing the issues. The biggest turned out to be one and the same, the tight\ncoupling of the source code change to the <code>CHANGELOG.md<\/code> entry. This tight\ncoupling is what made it easy to prep releases of libs\/apps because the\ndeveloper that made the change, the person with context, also created the\nassociated <code>CHANGELOG.md<\/code> entry. However, this was also the practice that was\ntriggering the useless conflicts in the <code>CHANGELOG.md<\/code>.<\/p>\n<p>So we hashed this out and eventually came to the realization\nthat we could have the tight coupling of the Changelog entry and the code while\navoiding the conflicts all together. We just had to lift the Changelog entry out\nto the <a rel=\"external\" href=\"https:\/\/git-scm.com\">Git<\/a> commit message. After contemplating implementation approaches\nand realizing at the core it would be similar to <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/git-ps\">Git Patch Stack<\/a> we\nimmediately jumped into building a proof of concept <a rel=\"external\" href=\"https:\/\/git-scm.com\">Git<\/a> command line tool\nextension to facilitate building a Changelog from entries in the <a rel=\"external\" href=\"https:\/\/git-scm.com\">Git<\/a>\ncommits.<\/p>\n<p>Over the last few weeks we have been using it in our workflows and iterating on\nit to make sure it meets everyones needs. This work has resulted in our latest\nopen source project, <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/git-cl\">Git Changelog<\/a>. Below is a ~15 min video where I\nintroduce the concepts, values, the <code>git-cl<\/code> tool, as well as it's best\npractices.<\/p>\n<div style=\"text-align: center;\">\n<iframe width=\"560\" height=\"315\" style=\"margin: 0 auto;\" src=\"https:\/\/www.youtube.com\/embed\/4JKs2761UuY\" frameborder=\"0\" allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen><\/iframe>\n<\/div>\n"},{"title":"Pullwalla - Keepin' it Going","published":"2020-03-18T00:00:00+00:00","updated":"2020-03-18T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/pullwalla-keepin-it-going\/"}},"id":"https:\/\/drewdeponte.com\/blog\/pullwalla-keepin-it-going\/","content":"<p>Hello!<\/p>\n<p>You're receiving this email because you subscribed to updates for Pullwalla. Your unified pull request manager.<\/p>\n<h4 id=\"where-are-we-at\">Where are we at?<\/h4>\n<p>We are happy to share with you that since the last email update we have made some <strong>very significant<\/strong> changes. These are all available now in the releases in the App Stores.<\/p>\n<h5 id=\"now-on-ios-ipad-macos\">Now on iOS, iPad &amp; macOS<\/h5>\n<p>One of these changes is that we have ported Pullwalla macOS over to work on iOS and iPad.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/pullwalla-keepin-it-going\/hero-v2.4.1-23.png\" alt=\"Pullwalla Hero\" \/><\/p>\n<p>Now you can enjoy managing pull requests on your Apple devices whether you are on the couch with your iPad and just need to make a quick response or at your computer and doing a deep review.<\/p>\n<h5 id=\"bitbucket\">Bitbucket<\/h5>\n<p>We have also added support to manage pull requests from Bitbucket Cloud and are in the process of adding support for Bitbucket Server &amp; Data Center.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/pullwalla-keepin-it-going\/bitbucket-atlassian-logo-1.png\" alt=\"Bitbucket Logo\" \/><\/p>\n<p>This enables you to manage pull requests across all your GitHub &amp; Bitbucket accounts whether you are a consultant with client repos spread across multiple accounts or just a dev on a team that uses Bitbucket for hosting their library, service, and app repositories.<\/p>\n<h5 id=\"diff-stats-feature\">Diff Stats Feature<\/h5>\n<p>Let's say you are leaving for lunch in 15 mins and want to jump on a quick PR. How do you find a bite-sized PR to review without digging through repos and wasting those precious minutes?<\/p>\n<p>Diff Stats is a new addition to the Pull Request Characteristics that solves this problem by facilitating visual scanning of PR size while also exposing details of additions, removals, and changes when you want them.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/pullwalla-keepin-it-going\/characteristics-v2.2.0-18.png\" alt=\"Screenshot of Characteristics Feature\" \/><\/p>\n<p>If there is a PR that has 1000 additions and 1 removal, the PR will show a large green segment for the additions and a small red segment for the removals. Combined these provide you with a quick visual indication of size.<\/p>\n<h5 id=\"improvements-bug-fixes\">Improvements &amp; Bug Fixes<\/h5>\n<p>Beyond the above feature additions we have made numerous improvements and bug fixes. The following are some highlights.<\/p>\n<ul>\n<li>showing window when dock icon is clicked on macOS<\/li>\n<li><strong>notifications<\/strong> for new\/updated pull requests for Pro users<\/li>\n<li>a <strong>compact view<\/strong> mode that limits pull request description body to only one line of text<\/li>\n<\/ul>\n<p>To get a full breakdown checkout the <a rel=\"external\" href=\"https:\/\/pullwalla.com\/changelog\/\">Pullwalla macOS Change Log<\/a> or <a rel=\"external\" href=\"https:\/\/pullwalla.com\/changelog-ios\/\">Pullwalla iOS Change Log<\/a>.<\/p>\n<h4 id=\"where-to-next\">Where to next?<\/h4>\n<p>At a high level next steps are as follow to start.<\/p>\n<ul>\n<li>add <strong>Shared Subscriptions across Devices<\/strong><\/li>\n<li>add <strong>iCloud Sync of Accounts<\/strong><\/li>\n<li>add <strong>Enterprise Integration<\/strong> with both GitHub and Bitbucket<\/li>\n<\/ul>\n<p>Beyond that we are also working on implementing PR interactions, <strong>Details View<\/strong>, <strong>PR Comments<\/strong>, <strong>Approve\/Reject<\/strong>, etc. To keep tabs on where we are going you can always check out our public <a rel=\"external\" href=\"https:\/\/pullwalla.com\/roadmap\/\">Roadmap<\/a>.<\/p>\n<h4 id=\"thoughts-or-suggestions\">Thoughts or suggestions?<\/h4>\n<p>As always, we\u2019d love to hear from you.<\/p>\n<h4 id=\"keepin-it-going\">Keepin' it Going<\/h4>\n<p>Our thoughts and prayers are with all those dealing with COVID-19. With the onslaught of the virus, we have been lucky enough to be a company that is able to shift to fully remote work and stay productive. To help us remain connected with our team, we built <a rel=\"external\" href=\"https:\/\/apps.apple.com\/us\/app\/teamsmash\/id1487662886?mt=12\">TeamSmash<\/a>, a visual presence tool for remote teams that integrates with Slack. We find it very helpful, so we wanted to share in case it could help your team with the adjustment to remote work. It is a completely free app.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/pullwalla-keepin-it-going\/teamsmash-screenshot-001.png\" alt=\"Screenshot of TeamSmash in action\" \/><\/p>\n<p>Stay safe, stay positive, and keep it going!<\/p>\n<p>The Pullwalla team<\/p>\n"},{"title":"Constraid v7.1.0","published":"2020-01-09T00:00:00+00:00","updated":"2020-01-09T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/constraid-v7-1-0\/"}},"id":"https:\/\/drewdeponte.com\/blog\/constraid-v7-1-0\/","content":"<p><a rel=\"external\" href=\"https:\/\/github.com\/uptech\/Constraid\">Constraid<\/a> v7.1.0 is available and includes a new API.<\/p>\n<h2 id=\"api-layers\">API Layers<\/h2>\n<p>In this release we provide a higher level API based on the <a rel=\"external\" href=\"https:\/\/en.wikipedia.org\/wiki\/Proxy_pattern\">Proxy pattern<\/a> to make things nicer. <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/Constraid\">Constraid<\/a> effectively provides two <strong>public<\/strong> API layers now.<\/p>\n<ol>\n<li>high level <a rel=\"external\" href=\"https:\/\/en.wikipedia.org\/wiki\/Proxy_pattern\">Proxy pattern<\/a> based API<\/li>\n<li>low level function API<\/li>\n<\/ol>\n<p>Don't worry this new API layer is strictly a convenience API layer. So this release is <strong>100% backwards compatible<\/strong> and all the low level function API will continue to work. In fact the new convenience API layer is actually built on top of the lower level function API.<\/p>\n<h2 id=\"syntactic-sugar-chaining\">Syntactic Sugar &amp; Chaining<\/h2>\n<p>This new API layer is all about providing a nicer, more convenient API. It does so by utilizing the <a rel=\"external\" href=\"https:\/\/en.wikipedia.org\/wiki\/Proxy_pattern\">Proxy pattern<\/a> to support chaining. In turn providing a much nicer API.<\/p>\n<p>The original API layer is effectively a set of functions that create and return Autolayout Constraint Collections which can be combined together to make larger constraint collections. An example of this is as follows:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span>(<\/span><span style=\"color: #B57EDC;\">flush<\/span><span>(childView, <\/span><span style=\"color: #B57EDC;\">withVerticalEdgesOf<\/span><span>: parentView)<\/span><span style=\"color: #E06C75;\"> +<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">  center<\/span><span>(childView, <\/span><span style=\"color: #B57EDC;\">verticallyWithin<\/span><span>: parentView)).<\/span><span style=\"color: #B57EDC;\">activate<\/span><span>()<\/span><\/span><\/code><\/pre>\n<p>Well, dealing with the <code>+<\/code> operator and the parens is no longer necessary. As the above can now be implemented using the <a rel=\"external\" href=\"https:\/\/en.wikipedia.org\/wiki\/Proxy_pattern\">Proxy pattern<\/a> API as follows:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span>childView.<\/span><span style=\"color: #C6CCD7;\">constraid<\/span><\/span>\n<span class=\"giallo-l\"><span>  .<\/span><span style=\"color: #B57EDC;\">flush<\/span><span>(<\/span><span style=\"color: #B57EDC;\">withVerticalEdgesOf<\/span><span>: parentView)<\/span><\/span>\n<span class=\"giallo-l\"><span>  .<\/span><span style=\"color: #B57EDC;\">center<\/span><span>(<\/span><span style=\"color: #B57EDC;\">verticallyWithin<\/span><span>: parentView)<\/span><\/span>\n<span class=\"giallo-l\"><span>  .<\/span><span style=\"color: #B57EDC;\">activate<\/span><span>()<\/span><\/span><\/code><\/pre>\n<p>I hope you can agree this is an easier to read API style and it still provides all the important features and functionality of the lower level API.<\/p>\n<p>But don't worry. If we somehow accidentally missed something you can always fall back to using the lower level function API we all used before.<\/p>\n"},{"title":"Pullwalla is now in the App Store","published":"2019-11-18T00:00:00+00:00","updated":"2019-11-18T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/pullwalla-is-now-in-the-app-store\/"}},"id":"https:\/\/drewdeponte.com\/blog\/pullwalla-is-now-in-the-app-store\/","content":"<h2 id=\"pra-is-now-pullwalla\">Pra is now Pullwalla!<\/h2>\n<p>The app that began as a command line tool named Pra has made the next step in its journey with a new name and new features.<\/p>\n<p>The goal remains the same: Pullwalla is designed to be a single, unified interface that allows you to easily manage all the pull requests you care about.<\/p>\n<h2 id=\"new-name-and-logo\">New Name and Logo<\/h2>\n<p>The new name reflects a change in focus from a minimal, text-based list of pull requests to a full-featured, modern client that is both informative and a pleasure to use.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/pullwalla-is-now-in-the-app-store\/dock.png\" alt=\"Pullwalla - Screenshot of the Pullwalla icon in the macOS dock\" \/><\/p>\n<p>The roots of the app haven\u2019t changed\u2014it aims to be simple, helpful, and unobtrusive. Pullwalla, just like its namesake wallaby, takes advantage of its diminutive size to remain fast, nimble, and quiet.<\/p>\n<h2 id=\"new-features\">New Features<\/h2>\n<p>This release features more than just a makeover. We have added characteristics to help you quickly identify the state of your pull requests. New quick filters help you quickly drill down to exactly what you need. Bugs have been squashed.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/pullwalla-is-now-in-the-app-store\/characteristics.png\" alt=\"Pullwalla - Screenshot of the app highlighting pull request characteristics\" \/><\/p>\n<p>And that\u2019s not all\u2014plenty more features are in the works.<\/p>\n<h2 id=\"new-website\">New Website<\/h2>\n<p>A new name deserves a fresh <a rel=\"external\" href=\"https:\/\/pullwalla.com\/?utm_source=uptech&amp;utm_medium=blog-post&amp;utm_campaign=uptech-blog-pullwalla-launch\">website<\/a>. We have created a landing page to highlight features.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/pullwalla-is-now-in-the-app-store\/website.png\" alt=\"Screenshot of pullwalla.com landing page\" \/><\/p>\n<p>A <a rel=\"external\" href=\"https:\/\/pullwalla.com\/support\/?utm_source=uptech&amp;utm_medium=blog-post&amp;utm_campaign=uptech-blog-pullwalla-launch\">support page<\/a> with frequently asked questions will help you get started quickly. A <a rel=\"external\" href=\"https:\/\/pullwalla.com\/changelog\/?utm_source=uptech&amp;utm_medium=blog-post&amp;utm_campaign=uptech-blog-pullwalla-launch\">change log<\/a> lets you know what we\u2019ve been working on in the past, while the new <a rel=\"external\" href=\"https:\/\/pullwalla.com\/roadmap\/?utm_source=uptech&amp;utm_medium=blog-post&amp;utm_campaign=uptech-blog-pullwalla-launch\">roadmap<\/a> allows us to highlight what we\u2019re working on now and what we have planned for the future.<\/p>\n<h2 id=\"more-to-come\">More to come...<\/h2>\n<p>We\u2019re excited about all the changes we have in store! We hope you find Pullwalla as useful and pleasurable as we do.<\/p>\n<p>Please <a href=\"mailto:pullwalla@uptechstudio.com\">drop us a line<\/a> if you have any questions, comments, or suggestions.<\/p>\n"},{"title":"Constraid v7.0.0","published":"2019-10-08T00:00:00+00:00","updated":"2019-10-08T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/constraid-v7-0-0\/"}},"id":"https:\/\/drewdeponte.com\/blog\/constraid-v7-0-0\/","content":"<p>I just released v7.0.0 of <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/Constraid\">Constraid<\/a>. This release is small in terms of features however is quite significant.<\/p>\n<p>In this release we now provide support for <a rel=\"external\" href=\"https:\/\/swift.org\/package-manager\/\">Swift Package Manager<\/a> (SPM). This is a huge improvement with the recent support for SPM in Xcode. It already seems to be a superior experience to CocoaPods or even Carthage with a few more edge cases that need to be ironed out. But, hopefully those edge cases will be resolved in the near future.<\/p>\n"},{"title":"Constraid v6.0.0","published":"2019-09-12T00:00:00+00:00","updated":"2019-09-12T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/constraid-v6-0-0-released\/"}},"id":"https:\/\/drewdeponte.com\/blog\/constraid-v6-0-0-released\/","content":"<p>I just released v6.0.0 of <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/Constraid\">Constraid<\/a>, your favorite iOS &amp; macOS Auto Layout framework, \ud83d\ude09.<\/p>\n<p>This release is a <strong>major version bump<\/strong> because I included a breaking change, though relatively minor. I strictly follow <a rel=\"external\" href=\"https:\/\/semver.org\">Semantic Versioning<\/a>.<\/p>\n<h3 id=\"discardableresult\">@discardableResult<\/h3>\n<p>In this release I removed the <code>@discardableResult<\/code> declaration attribute which is a <strong>breaking change<\/strong>.<\/p>\n<p>I did this because I came to the realization that ever since we removed the automatic constraint activation to better support Auto Layout animations we haven't needed the <code>@discardableResult<\/code> attribute. In fact when I was pairing with one of our engineers and he forgot to activate a returned constraint collection, I realized it was actually hindering users.<\/p>\n<p>Therefore, I removed the <code>@discardableResult<\/code> declaration attributes. This now causes the compiler to remind you to do something with the returned constraint collection from any of the functions. Hopefully, this will help keep people from running into, \"Oh crap, I forgot to activate the constaints\".<\/p>\n<p>Having the compiler require you to do something with the results shouldn't be a problem as you will either be.<\/p>\n<ol>\n<li>activating the constraint collection immediately, ex: <code>Constraid.flush(viewA, withEdgesOf: viewB).activate()<\/code><\/li>\n<li>storing constraint collection for later use<\/li>\n<li>using the constraint collection in an expression<\/li>\n<\/ol>\n<p>All of which satisify the compilers requirement.<\/p>\n<h3 id=\"relative-position-to-center\">Relative Position to Center<\/h3>\n<p>The other item included in <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/Constraid\">Constraid<\/a> v6.0.0 is the addition of the following methods:<\/p>\n<ul>\n<li><code>follow(theCenterOf:with:)<\/code><\/li>\n<li><code>precede(theCenterOf:with:)<\/code><\/li>\n<li><code>set(viewA, aboveTheCenterOf:)<\/code><\/li>\n<li><code>set(viewA, beloweTheCenterOf:)<\/code><\/li>\n<\/ul>\n<p>Lets take the first two and look at an example of where they would come in useful. To, start lets look at the <a rel=\"external\" href=\"https:\/\/bear.app\">Bear<\/a> app's subscriptions view.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/constraid-v6-0-0-released\/bear_subscription.png\" alt=\"Bear.app Subscriptions View\" \/><\/p>\n<p>If we imagine a dashed line going down the middle of the view you can see what elements are naturally related to the center of the view.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/constraid-v6-0-0-released\/bear_subscription_marked_up.png\" alt=\"Bear.app Subscriptions View Marked Up\" \/><\/p>\n<p>In this particular case both the <em>Terms of Service<\/em> and <em>Privacy Policy<\/em> links at the bottom of the view are relative to the center.<\/p>\n<p>To replicate the layout of the <em>Terms of Service<\/em> and <em>Privacy Policy<\/em> buttons using Constraid we would do the following:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span>Constraid.<\/span><span style=\"color: #B57EDC;\">follow<\/span><span>(<\/span><span style=\"color: #B57EDC;\">theCenterOf<\/span><span>: view, <\/span><span style=\"color: #B57EDC;\">with<\/span><span>: privacyPolicyButton, <\/span><span style=\"color: #B57EDC;\">by<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> 16<\/span><span>).<\/span><span style=\"color: #B57EDC;\">activate<\/span><span>()<\/span><\/span>\n<span class=\"giallo-l\"><span>Constraid.<\/span><span style=\"color: #B57EDC;\">flush<\/span><span>(privacyPolicyButton, <\/span><span style=\"color: #B57EDC;\">withBottomEdgeOf<\/span><span>: view, <\/span><span style=\"color: #B57EDC;\">insetBy<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> 16<\/span><span>).<\/span><span style=\"color: #B57EDC;\">activate<\/span><span>()<\/span><\/span>\n<span class=\"giallo-l\"><span>Constraid.<\/span><span style=\"color: #B57EDC;\">precede<\/span><span>(<\/span><span style=\"color: #B57EDC;\">theCenterOf<\/span><span>: view, <\/span><span style=\"color: #B57EDC;\">with<\/span><span>: termsOfServiceButton, <\/span><span style=\"color: #B57EDC;\">by<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> 16<\/span><span>).<\/span><span style=\"color: #B57EDC;\">activate<\/span><span>()<\/span><\/span>\n<span class=\"giallo-l\"><span>Constraid.<\/span><span style=\"color: #B57EDC;\">flush<\/span><span>(termsOfServiceButton, <\/span><span style=\"color: #B57EDC;\">withBottomEdgeOf<\/span><span>: view, <\/span><span style=\"color: #B57EDC;\">insetBy<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> 16<\/span><span>).<\/span><span style=\"color: #B57EDC;\">activate<\/span><span>()<\/span><\/span><\/code><\/pre>\n<p>From here you can see that you could handle the subscribe buttons in a similar fashion. Or, if you wanted to do something around the vertical center you can do so using <code>set(viewA, aboveTheCenterOf:)<\/code> or <code>set(viewA, belowTheCenterOf:)<\/code>.<\/p>\n<p>That about covers it. If you found this valuable or interesting please subscribe to my mailing list using the form below.<\/p>\n"},{"title":"Swift Failable Decodable","published":"2019-08-30T00:00:00+00:00","updated":"2019-08-30T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/swift-failable-decodable\/"}},"id":"https:\/\/drewdeponte.com\/blog\/swift-failable-decodable\/","content":"<h2 id=\"problem\">Problem<\/h2>\n<p>First things first. Lets understand the problem. To do this lets look at situation based on the real world issue that drove me to tackle this.<\/p>\n<p>Recently while working on <a rel=\"external\" title=\"Pra - App Store Page\" href=\"https:\/\/itunes.apple.com\/us\/app\/pra\/id1447158795?mt=12\">Pra<\/a> I ran into a situation where it was fetching pull requests from GitHub and then failing to decode the JSON payload response containing the collection of pull requests, resulting in getting back zero pull requests. This is an unacceptable experience.<\/p>\n<p>The decoding was implemented in the default fashion as follows:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">public<\/span><span style=\"color: #61AFEF;\"> struct<\/span><span style=\"color: #E5C07B;\"> PullRequestSearchResponseNode<\/span><span>:<\/span><span style=\"color: #D19A66;\"> Decodable<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  public let<\/span><span> author: PullRequestSearchResponseAccount<\/span><span style=\"color: #E06C75;\">?<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  public let<\/span><span> title:<\/span><span style=\"color: #E5C07B;\"> String<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  public let<\/span><span> body:<\/span><span style=\"color: #E5C07B;\"> String<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  public let<\/span><span> closedAt: Date<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  public let<\/span><span> permalink: URL<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  ...<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">public<\/span><span style=\"color: #61AFEF;\"> struct<\/span><span style=\"color: #E5C07B;\"> PullRequestSearchResponseDataSearch<\/span><span>:<\/span><span style=\"color: #D19A66;\"> Decodable<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  public let<\/span><span> nodes: [PullRequestSearchResponseNode]<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  public let<\/span><span> pageInfo: PullRequestSearchResponsePageInfo<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>After some investigation I came to understand that it was failing due to not being able to properly decode one specific pull request because of effectively a type miss-match. For simplicity sake lets say the miss-match is happening because the <code>PullRequestSearchResponseNode.closedAt<\/code> property has a value in some pull request payloads but is <code>null<\/code> in others. This is a problem because our <code>struct<\/code> that we are trying to decode into states that it is expecting <code>closedAt<\/code> to always have a value.<\/p>\n<h2 id=\"optionals\">Optionals<\/h2>\n<p>The correct, easiest, and most direct answer in the case of <code>closedAt<\/code> is to adjust the <code>PullRequestSearchResponseNode<\/code> struct so that the <code>closedAt<\/code> property is an <code>Optional<\/code>.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">public<\/span><span style=\"color: #61AFEF;\"> struct<\/span><span style=\"color: #E5C07B;\"> PullRequestSearchResponseNode<\/span><span>:<\/span><span style=\"color: #D19A66;\"> Decodable<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  public let<\/span><span> author: PullRequestSearchResponseAccount<\/span><span style=\"color: #E06C75;\">?<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  public let<\/span><span> title:<\/span><span style=\"color: #E5C07B;\"> String<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  public let<\/span><span> body:<\/span><span style=\"color: #E5C07B;\"> String<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  public let<\/span><span> closedAt: Date<\/span><span style=\"color: #E06C75;\">?<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  public let<\/span><span> permalink: URL<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  ...<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre><h2 id=\"the-catch\">The Catch<\/h2>\n<p>Using an <code>Optional<\/code> is great. It fixes the problem and <a rel=\"external\" title=\"Pra - App Store Page\" href=\"https:\/\/itunes.apple.com\/us\/app\/pra\/id1447158795?mt=12\">Pra<\/a> is able to successfully decode all the pull requests again. However, there is one major catch. Sadly as much as we feel we know the specifications for a JSON payload it is likely we will miss something or maybe the JSON payload will change over time. Given this will inevitably happen we can't have that breaking our applications.<\/p>\n<h2 id=\"possible-paths\">Possible Paths<\/h2>\n<p>So we need to come up with some way of handling the fact that we will miss something in the specification or that the specification will change.<\/p>\n<h3 id=\"all-properties-optional\">All Properties Optional<\/h3>\n<p>One approach, not that I recommend it, is to make every property on the decodable struct an <code>Optional<\/code>. This would address a chunk of possible issues in decoding but <strong>not<\/strong> all possible issues. Also, it has the massive downside of then forcing the rest of the app to deal with both <code>Optional<\/code> paths everywhere in the code. Even if it is a property that will always be present.<\/p>\n<h3 id=\"optional-wrapper\">Optional Wrapper<\/h3>\n<p>Another approach is to create a generic struct around <code>T<\/code> that wraps an option property of type <code>T<\/code> and provides decoding implementation. This could look something like the following:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">public<\/span><span style=\"color: #61AFEF;\"> struct<\/span><span style=\"color: #E5C07B;\"> Failable<\/span><span>&lt;<\/span><span style=\"color: #E06C75;\">T<\/span><span>:<\/span><span style=\"color: #D19A66;\"> Decodable<\/span><span>&gt;:<\/span><span style=\"color: #D19A66;\"> Decodable<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  let<\/span><span> wrappedValue:T<\/span><span style=\"color: #E06C75;\">?<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">  init<\/span><span>(<\/span><span style=\"color: #B57EDC;\">from<\/span><span style=\"color: #C6CCD7;\"> decoder<\/span><span>: Decoder)<\/span><span style=\"color: #61AFEF;\"> throws<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    let<\/span><span> container <\/span><span style=\"color: #E06C75;\">= try<\/span><span> decoder.<\/span><span style=\"color: #B57EDC;\">singleValueContainer<\/span><span>()<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    self<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">wrappedValue<\/span><span style=\"color: #E06C75;\"> = try?<\/span><span> container.<\/span><span style=\"color: #B57EDC;\">decode<\/span><span>(T.<\/span><span style=\"color: #E06C75;\">self<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span>  }<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>We would use this as follows:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">public<\/span><span style=\"color: #61AFEF;\"> struct<\/span><span style=\"color: #E5C07B;\"> PullRequestSearchResponseDataSearch<\/span><span>:<\/span><span style=\"color: #D19A66;\"> Decodable<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  public let<\/span><span> nodes: [Failable&lt;PullRequestSearchResponseNode&gt;]<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  public let<\/span><span> pageInfo: PullRequestSearchResponsePageInfo<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>This is nice as it effectively makes it so that we can make decoding failable at any of the various object boundaries. In the scenarios where decoding fails for some reason our <code>wrappedValue<\/code> would simply be <code>null<\/code>. This is definitely a step in the right direction. However, it has one major drawback. We have no idea why the decoding failed.<\/p>\n<h3 id=\"result-based-failable-decodable\">Result based Failable Decodable<\/h3>\n<p>Enter the <code>Result<\/code> type. This is exactly it's purpose, to manage optionality while also maintaining an associated error. So, instead of building a wrapper struct we can simply extend <code>Result<\/code> to implement the <code>Decodable<\/code> protocol and implement the constructor to handle appropriately assigning the <code>success<\/code> state in successful decodes, and assigning the <code>failure<\/code> state when decoding fails. This looks as follows:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">extension<\/span><span style=\"color: #E5C07B;\"> Result<\/span><span>:<\/span><span style=\"color: #D19A66;\"> Decodable<\/span><span style=\"color: #E06C75;\"> where<\/span><span> Success<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #D19A66;\"> Decodable<\/span><span>, Failure <\/span><span style=\"color: #E06C75;\">==<\/span><span> DecodingError {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  public<\/span><span style=\"color: #61AFEF;\"> init<\/span><span>(<\/span><span style=\"color: #B57EDC;\">from<\/span><span style=\"color: #C6CCD7;\"> decoder<\/span><span>: Decoder)<\/span><span style=\"color: #61AFEF;\"> throws<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    do<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">      let<\/span><span> container <\/span><span style=\"color: #E06C75;\">= try<\/span><span> decoder.<\/span><span style=\"color: #B57EDC;\">singleValueContainer<\/span><span>()<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">      self =<\/span><span> .<\/span><span style=\"color: #B57EDC;\">success<\/span><span>(<\/span><span style=\"color: #E06C75;\">try<\/span><span> container.<\/span><span style=\"color: #B57EDC;\">decode<\/span><span>(Success.<\/span><span style=\"color: #E06C75;\">self<\/span><span>))<\/span><\/span>\n<span class=\"giallo-l\"><span>    }<\/span><span style=\"color: #E06C75;\"> catch let<\/span><span> err <\/span><span style=\"color: #E06C75;\">as<\/span><span style=\"color: #B57EDC;\"> Failure<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">      self =<\/span><span> .<\/span><span style=\"color: #B57EDC;\">failure<\/span><span>(err)<\/span><\/span>\n<span class=\"giallo-l\"><span>    }<\/span><\/span>\n<span class=\"giallo-l\"><span>  }<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>This is used as follows:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">public<\/span><span style=\"color: #61AFEF;\"> struct<\/span><span style=\"color: #E5C07B;\"> PullRequestSearchResponseDataSearch<\/span><span>:<\/span><span style=\"color: #D19A66;\"> Decodable<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  public let<\/span><span> nodes: [Result&lt;PullRequestSearchResponseNode, DecodingError&gt;]<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">  public let<\/span><span> pageInfo: PullRequestSearchResponsePageInfo<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>This is great as we get the same ability to make objects failable decodables at any of the object boundaries but we also maintain all of the decoding errors to let us know why decoding failed for a given node.<\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>If you are dealing with this situation you probably want to use the <strong>Result based Failable Decodable<\/strong> approach. I used this technique to solve the problems described in the <a href=\"\/blog\/pra-v1-4-0-released\/\">Pra v1.4.0 Announcement<\/a>. It facilitated collecting the decode failures and building the debug report so users can send them back to us.<\/p>\n"},{"title":"Pra v1.4.0","published":"2019-08-21T00:00:00+00:00","updated":"2019-08-21T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/pra-v1-4-0-released\/"}},"id":"https:\/\/drewdeponte.com\/blog\/pra-v1-4-0-released\/","content":"<p>Version v1.4.0 of Pra just made it to the App Store.<\/p>\n<p>This release was focused on reliability and robustness. The last release was focused more heavily on new features. If you missed that checkout the <a href=\"\/blog\/pra-v1-3-0-released\">Pra v1.3.0<\/a> post.<\/p>\n<h3 id=\"reliability\">Reliability<\/h3>\n<p>After the <a href=\"\/blog\/pra-v1-3-0-released\">v1.3.0 release<\/a> I quickly got feedback that there were some reliability issues. First there were reports of an infinite loading spinner. After some investigation we discovered it was a side effect of JSON decoding failing in the scenario where a pull request has a requested reviewer that is a GitHub team. So, we resolved both of these bugs in this release.<\/p>\n<h3 id=\"robustness\">Robustness<\/h3>\n<p>We also realized while digging into the above mentioned reliability issues that we had the following robustness issues.<\/p>\n<ul>\n<li>The existing logging wasn\u2019t generally valuable<\/li>\n<li>The decoding of the fetched pull requests would fail if any of the decoding failed. So, if it couldn\u2019t decode 1 out of 20 pull requests the user would still get 0 pull requests back.<\/li>\n<li>When JSON decoding failures happened we had no way of easily finding out or getting useful information that would help us resolve them in the future.<\/li>\n<\/ul>\n<p>To address these robustness issues I made tho following changes:<\/p>\n<ul>\n<li>Migrated logging from mix of <code>NSLog<\/code> and <code>print<\/code> calls over to the newer standard <code>OSLog<\/code> interface.<\/li>\n<li>Eliminated useless logging, cleaned up and categorized the logging I kept so that users would have a much easier time finding valuable logs.<\/li>\n<li>Added more detailed error logging when JSON decoding fails happen in the hopes that it is more helpful in identifying &amp; fixing issues.<\/li>\n<li>Added fail-able Pull Request decoding so that successfully decoded pull requests are presented to the user and unsuccessful pull request decodings are logged.<\/li>\n<li>Added a flash notice system to be able to inform the user in-app about things. Specifically, I used this to inform the user of pull request decode failures and provide a call to action enabling them to send a debug report so that we might address the issue.<\/li>\n<\/ul>\n<img src=\"decoding_failure_flash_dark_mode.png\" alt=\"Pra - Decoding Failure Flash Dark Mode\" class=\"img-fluid\" \/>\n<img src=\"decoding_failure_flash_light_mode.png\" alt=\"Pra - Decoding Failure Flash Light Mode\" class=\"img-fluid\" \/>\n"},{"title":"Pra v1.3.0","published":"2019-07-18T00:00:00+00:00","updated":"2019-07-18T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/pra-v1-3-0-released\/"}},"id":"https:\/\/drewdeponte.com\/blog\/pra-v1-3-0-released\/","content":"<p>This release includes a mix of foundational features, small improvement features, as well as some bug fixes, performance improvements, etc.<\/p>\n<h2 id=\"ignore-organizations\">Ignore Organizations<\/h2>\n<p>We added the ability to <strong>Ignore Organizations<\/strong> in addition to already being able to ignore a repository. This is an extemely useful feature that can help you gain focus by ignoring pull requests that belong to organizations you are a member of but aren't interested in reviewing.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/pra-v1-3-0-released\/ignore-organization.png\" alt=\"Pra - Ignore Organizations\" \/><\/p>\n<h2 id=\"quick-filters\">Quick Filters<\/h2>\n<p>We also added a foundational feature, Quick Filters, in this release. It includes the <em>Created<\/em>, <em>Assigned<\/em>, <em>Mentioned<\/em>, and <em>Review Requests<\/em> quick filters while also laying the foundation for the future. Quick Filters enable quickly filtering the set of pull requests down based on a particular mindset. Below are a few mindsets and their associated quick filters.<\/p>\n<ul>\n<li>Trying to catch up on what last happened on one of your pull requests? Use the <strong>Created<\/strong> quick filter.<\/li>\n<li>Trying to remember what pull requests you are responsible for? Use the <strong>Assigned<\/strong> quick filter.<\/li>\n<li>Want to know who is talking about you? Use the <strong>Mentioned<\/strong> quick filter and see the pull requests that mention you.<\/li>\n<li>Can't keep track of all the requests for reviews? No, problem see them with the <strong>Review Requests<\/strong> quick filter.<\/li>\n<\/ul>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/pra-v1-3-0-released\/quick-filters.png\" alt=\"Pra - Quick Filters\" \/><\/p>\n<h2 id=\"bug-fixes-performance-improvements-etc\">Bug Fixes, Performance Improvements, etc.<\/h2>\n<p>In addition to the above changes we also did the following.<\/p>\n<ul>\n<li>fixed a small layout issue in Preferences<\/li>\n<li>switched from GitHub v3 REST API to GitHub v4 GraphQL API<\/li>\n<li>made some performance improvements<\/li>\n<\/ul>\n<p>The most significant of these changes was switch to GitHub v4 GraphQL API as it facilitates getting the right data out of GitHub in a more efficient fashion. It also puts us on their latest API so that we don't have to be worried about the API being deprecated out from under us.<\/p>\n<p>Overall we think this release was an excellent step in the right direction and hope you enjoy the new features. As always we love hearing from you. So, don't hesitate ping us on <a rel=\"external\" href=\"https:\/\/twitter.com\/uptechstudio\">Twitter<\/a> or <a href=\"mailto:pullwalla@uptechstudio.com\">email<\/a>.<\/p>\n"},{"title":"Pra - The App Store Audible","published":"2019-02-22T00:00:00+00:00","updated":"2019-02-22T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/pra-the-app-store-audible\/"}},"id":"https:\/\/drewdeponte.com\/blog\/pra-the-app-store-audible\/","content":"<p>It is Sunday, Feb 17th, 2019 at 10:58 pm and I have just submitted the <strong>Minimal Free Version<\/strong> of <a rel=\"external\" href=\"https:\/\/pullwalla.com\">Pra<\/a> to the Apple App Store for review. I post in our team Slack channel that I've submitted and I cross my fingers as we wait for Apple to respond.<\/p>\n<h2 id=\"minimal-free-version\">Minimal Free Version<\/h2>\n<p>It is probably worth explaining that our <strong>Minimal Free Version<\/strong> is pretty small in terms of functionality. It lets you do the following things:<\/p>\n<ul>\n<li><strong>Manage an Account<\/strong> - Add or remove your GitHub account.<\/li>\n<li><strong>See a Feed of Pull Requests<\/strong> - See a continuously updating feed of pull requests you potentially want to review, aggregated across all repos you have access to, with the necessary data to quickly assess if you want to review one of them.<\/li>\n<li><strong>View a Pull Request<\/strong> - As you're looking through open requests, if you see one you want to review, you can quickly view the pull request details in your default browser.<\/li>\n<li><strong>Ignore Repositories<\/strong> - Select a pull request in the feed and ignore\/hide the repository it's in from the feed of pull requests. Extremely helpful in reducing noise in your pull request feed.<\/li>\n<\/ul>\n<p>We thought that the tool was already valuable in this state and we just wanted to get it in the hands of users to validate our assumptions. Would other people find this minimal state of the product valuable? If they don\u2019t, it would be extremely useful to learn why not. If they do find it valuable, that is reassurance we are on the right path. Either way, we're interested in feedback that will help us decide which features to focus on and when, including what should be in the paid version and what should be in the free version.<\/p>\n<h2 id=\"the-app-store-response\">The App Store Response<\/h2>\n<p>It is now Feb 18, 2019 at 7:22 PM and we just received a rejection response from Apple.<\/p>\n<blockquote>\n<p><strong>Guideline 4.2.2 - Design<\/strong><\/p>\n<p>The experience that your app provides is not sufficiently different from browsing a content aggregator web site.<\/p>\n<p><strong>Next Steps<\/strong><\/p>\n<p>While your app may facilitate access to content from a range of web sites, even when including native macOS features, the experience it provides is not significantly different from using Safari. Such apps do not include enough native macOS functionality to be appropriate for the App Store.<\/p>\n<\/blockquote>\n<h2 id=\"headspace\">Headspace<\/h2>\n<p>To be honest, getting this response was frustrating and a little disheartening. Part of why I feel that way I think is because Apple's response is so generic and unclear. What do they consider native macOS functionality? The app is 100% written in Swift, doesn\u2019t use any web views, and integrates with macOS Keychain and UIKit. There are also plenty of apps on the App Store that don't seem to include any more native macOS functionality than <a rel=\"external\" href=\"https:\/\/pullwalla.com\">Pra<\/a>, even in its current, minimal state. Somehow those apps were allowed on the App Store. So a few of us (mostly me) start venting a bit about this in Slack, while we slowly start to calm down.<\/p>\n<p>Once I calmed down, we started throwing out ideas about what \"enough native macOS functionality\" could mean to Apple. We look at RSS Feed Reader apps because they are \"content aggregators\" and there are a ton of them in the App Store. Most of them provide a viewer interface for the posts, so we think maybe we need to add a <em>pull request viewer<\/em> in the app rather than just open the PR in the default browser. This is already in our idea backlog, so it's not out of left field.<\/p>\n<p>Adam suggested trying to find one or two features that would take minimal effort to build, but be unique to the desktop, to see if Apple would then accept the app as having \"enough\" native functionality. He threw out a few ideas:<\/p>\n<ul>\n<li>Some sort of notifications<\/li>\n<li>Badge on dock icon<\/li>\n<li>Banners\/alerts<\/li>\n<li>Menu bar icon<\/li>\n<\/ul>\n<p>Another thought I had rolling around in my head was how much of the rejection was a side effect of our App Store copy not explaining the true value of <a rel=\"external\" href=\"https:\/\/pullwalla.com\">Pra<\/a>?<\/p>\n<p>I was having a hard time homing in on exactly what we should do. So I went back to why we were trying to launch the app in the first place: to get crucial feedback and find out if other people find it valuable in its current state. At this stage, we're more interested in getting an idea of whether or not we are on the right track and gathering some feedback than we are interested in wide distribution.<\/p>\n<h2 id=\"the-audible\">The Audible<\/h2>\n<p>At this point, I started thinking maybe we should just launch via direct download from our website rather than via the App Store. This would allow us to get versions out quickly and gather feedback while we continue the review process with Apple.<\/p>\n<p>So, we hashed it out as a team in Slack and decided to put the download on our site (and start to gather feedback and iterate on the <strong>Minimal Free Version<\/strong>) while we continue to work with Apple to get clarification and get the app ready to resubmit to the App Store.<\/p>\n<h2 id=\"the-non-app-store-launch\">The Non-App Store Launch<\/h2>\n<p>Feb 21st, 2019 at 1:39 PM we launched a landing page with the direct download button to get the app, and shared it with small group of colleagues in another Slack team.<\/p>\n<h3 id=\"sam-s-feedback\">Sam\u2019s Feedback<\/h3>\n<p>That afternoon I got feedback from a friend named Sam now using it and asking the following questions:<\/p>\n<ol>\n<li><strong>How do I add more than one account?<\/strong> - We realized this was an indicator that our communication via Tooltip on the add account button was too subtle to properly communicate that the free version only supports one account. Also, it made us think we should probably have a mechanism at that point to allow people to submit their email address to join the mailing list to find out about releases and when multi-account support is added.<\/li>\n<li><strong>How do I connect to my Enterprise GitHub account?<\/strong> - This is a feature we talked about being part of the paid features previously as well. So, we took this as reassurance that there is at least some need for this feature.<\/li>\n<\/ol>\n<h3 id=\"nico-s-feedback\">Nico\u2019s Feedback<\/h3>\n<p>Later that evening I got feedback from another friend asking about what PRs are included in the feed. He wasn\u2019t seeing a PR that he knew he could see in the <em>Created at<\/em> filter on GitHub. He thought it was because he is an External Collaborator on the repository, and it turns out he was right that that functionality was missing. So, I quickly made a ticket to resolve the bug and started working on a fix. I shipped the fix to Nico so he could test it and find out if it resolved his issue.<\/p>\n<p>Unfortunately, the new version still didn\u2019t show the PR Nico was missing, but it did add the missing functionality of showing pull requests from repositories that you are an external collaborator of. Digging a little deeper with Nico, we eventually figured out that his missing PR was from a fork of a repository he no longer had access to.<\/p>\n<p>He thought it might not be worth the effort to fix (maybe this is just an edge case?). I responded that we probably wouldn't add this to the main feed view (because the main purpose of the feed is to show pull requests you might be interested in reviewing, not necessarily all the pull requests you can access). But, I also let him know that we were contemplating adding other feed views via quick filters, including a \u201cCreated\u201d filter which would show this pull request. This would support a different user goal of checking in on the status of their own PRs, and if there is anything they can do to move them forward. Our conversation and interaction gave me reassurance that what we are thinking in terms of features, contexts\/modes, etc. were right on track at least with Nico.<\/p>\n<h3 id=\"the-high\">The High<\/h3>\n<p>Both of these interactions gave us a ton of value and amped me up.  This completely made up for the feelings I had around the App Store rejection.<\/p>\n<h2 id=\"the-second-app-store-response\">The Second App Store Response<\/h2>\n<p>Back to Apple \u2014 we decided to try and get clarification. So, Matt and Adam put together a response to Apple.<\/p>\n<blockquote>\n<p>Is there a way that we can get clarification on this rejection? Our app provides a significantly better experience than Safari, because it stores your GitHub credentials so that you can have a persistent and live-updating view of pull requests in your GitHub account on your desktop, without having to open a browser and log into your account.<\/p>\n<p>This view of pull requests isn\u2019t available anywhere on the web today and having it persistently available via a desktop app provides unique value that you couldn\u2019t create with just a web site. To do this in Safari would require going into each team in Github and going to each repository for each team to see what Pull Requests are open. This is a massive headache for a developer that is working in multiple teams with multiple repos per team.<\/p>\n<p>Happy to get on a call with you guys and walk you through how it works and what our roadmap is for enhancements to the desktop experience.<\/p>\n<\/blockquote>\n<p>Apple came back with the following response:<\/p>\n<blockquote>\n<p><strong>4.2 Design: Minimum Functionality (macOS)<\/strong><\/p>\n<p>Hello,<\/p>\n<p>Thank you for your response. It would be appropriate to add native macOS functionality to be appropriate for the App Store.<\/p>\n<p>Regards,<\/p>\n<p>App Store Review<\/p>\n<\/blockquote>\n<h2 id=\"what-s-next\">What\u2019s Next?<\/h2>\n<p>Well, Apple's response didn't give us much to go on. So, we just need to put some thought into what \u201cmacOS Features\u201d we can easily add (and that make sense), then submit to the App Store again.<\/p>\n<p>Meanwhile, we continue to tell more people about the <strong>Minimum Free Version,<\/strong> collect feedback, and iterate along. From this initial round of feedback, we've identified a few tasks:<\/p>\n<ul>\n<li>add pull requests for External Collaborator repositories to the feed<\/li>\n<li>add support for joining a mailing list in the Account Preferences<\/li>\n<li>add processing of pagination to GitHub API requests<\/li>\n<\/ul>\n<p>From the App Store rejection we're planning to:<\/p>\n<ul>\n<li>review the App Store messaging and see if we can communicate the value better<\/li>\n<li>figure out what native macOS features we think make sense to add in order to get it approved for the App Store<\/li>\n<\/ul>\n<p>Overall, I feel very confident that the decision to release the app outside of the App Store this early in its lifespan was one of the best things we could have done. The feedback is invaluable and each iteration we make improves the app. In retrospect this really cost about a day's worth of work to research (and implement) the proper way to package and release the app outside of the store. In the scheme of things, a relatively small price to pay for the feedback we obtained in the first round.<\/p>\n"},{"title":"Create macOS SideBar in Swift","published":"2019-01-10T00:00:00+00:00","updated":"2019-01-10T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/create-macos-sidebar-in-swift\/"}},"id":"https:\/\/drewdeponte.com\/blog\/create-macos-sidebar-in-swift\/","content":"<p>Last night I started digging into building a SideBar for <a rel=\"external\" href=\"https:\/\/pullwalla.com\">Pra<\/a>.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/create-macos-sidebar-in-swift\/finder-screenshot.png\" alt=\"Finder Screenshot\" \/><\/p>\n<p>Sadly, I struggled to find a decent example of how to programmatically create a SideBar in Swift for macOS.<\/p>\n<p>After a little bit of trial and error the following is the working solution I came up with.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">import<\/span><span style=\"color: #E5C07B;\"> Cocoa<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">extension<\/span><span style=\"color: #E5C07B;\"> NSSplitViewController<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">  convenience init<\/span><span>(<\/span><span style=\"color: #B57EDC;\">sideBarViewController<\/span><span>: NSViewController, <\/span><span style=\"color: #B57EDC;\">contentViewController<\/span><span>: NSViewController) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    self<\/span><span>.<\/span><span style=\"color: #61AFEF;\">init<\/span><span>(<\/span><span style=\"color: #B57EDC;\">nibName<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> nil<\/span><span>, <\/span><span style=\"color: #B57EDC;\">bundle<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> nil<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    let<\/span><span> sideBarSplitViewItem <\/span><span style=\"color: #E06C75;\">=<\/span><span style=\"color: #B57EDC;\"> NSSplitViewItem<\/span><span>(<\/span><span style=\"color: #B57EDC;\">sidebarWithViewController<\/span><span>: sideBarViewController)<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    let<\/span><span> contentSplitViewItem <\/span><span style=\"color: #E06C75;\">=<\/span><span style=\"color: #B57EDC;\"> NSSplitViewItem<\/span><span>(<\/span><span style=\"color: #B57EDC;\">viewController<\/span><span>: contentViewController)<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    self<\/span><span>.<\/span><span style=\"color: #B57EDC;\">addSplitViewItem<\/span><span>(sideBarSplitViewItem)<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">    self<\/span><span>.<\/span><span style=\"color: #B57EDC;\">addSplitViewItem<\/span><span>(contentSplitViewItem)<\/span><\/span>\n<span class=\"giallo-l\"><span>  }<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/create-macos-sidebar-in-swift\/pra-with-sidebar.png\" alt=\"Pra with SideBar\" \/><\/p>\n"},{"title":"Pra: Status Update 001","published":"2019-01-07T00:00:00+00:00","updated":"2019-01-07T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/pra-status-update-001\/"}},"id":"https:\/\/drewdeponte.com\/blog\/pra-status-update-001\/","content":"<p>This is the first status update of the Pra macOS application. I figured what better time to do this than right after 2018 ends and 2019 begins. My goal with this is to give you an idea of where I am with the development, give you some insight into the things I am doing and some of the decisions I am making along the way. If you would like a more real-time feed of what is happening and want to provide feedback you can always follow me on Twitter <a rel=\"external\" href=\"https:\/\/twitter.com\/drewdeponte\">@drewdeponte<\/a>.<\/p>\n<h2 id=\"initial-setup\">Initial Setup<\/h2>\n<p>The first step I took with this application was to start a basic macOS application. Immediately following that I:<\/p>\n<ul>\n<li>replaced all the Interface Builder xib stuff with straight up source code because I prefer to develop apps without Interface Builder or xibs<\/li>\n<li>built out the Main Menu, Main Window, Preferences Window, and the Preferences Window\u2019s tab interface<\/li>\n<li>built out the Account Management Interface<\/li>\n<li>built out Main Window for Pull Request list<\/li>\n<\/ul>\n<p>Building out the basic skeleton of App in terms of flow is part of the outside-in approach I like to take when developing applications.<\/p>\n<figure>\n\t<img src=\"basic_skeleton_of_app.png\" alt=\"Pra Basic Skeleton\" style=\"\" class=\"img-fluid\">\n\t<figcaption>Basic App Skeleton<\/figcaption>\t\n<\/figure>\n<h2 id=\"making-account-management-real\">Making Account Management Real<\/h2>\n<p>Once I had the skeleton of the macOS App together in terms of windows, etc. It was time to make adding GitHub accounts and the persistence of them and their associated API keys a thing. I also built out a very basic stubbed out pull request list view in the main window as I knew that the next step would be to actually add fetching and displaying of pull requests.<\/p>\n<p><em>Note:<\/em> I decided to implement GitHub integration initially because it is what I primarily use and I believe it has the largest user base.<\/p>\n<figure>\n\t<img src=\"stubbed_pull_request_list_and_account_management.png\" alt=\"Pra Stubbed Pull Request List & Account Management\" style=\"\" class=\"img-fluid\">\n\t<figcaption>Now with some content<\/figcaption>\n<\/figure>\n<div class=\"row\">\n\t<figure class=\"col\">\n\t\t<img src=\"add_account_sheet.png\" alt=\"Add Account Sheet\" style=\"\" class=\"img-fluid\">\n\t\t<figcaption>Account selection<\/figcaption>\n\t<\/figure>\n\t<figure class=\"col\">\n\t\t<img src=\"github_oauth_screen.png\" alt=\"GitHub OAuth Screen\" style=\"\" class=\"img-fluid\">\n\t\t<figcaption>Real GitHub integration<\/figcaption>\n\t<\/figure>\n<\/div>\n<h2 id=\"making-pull-requests-real\">Making Pull Requests Real<\/h2>\n<p>After adding basic Account Management I proceeded to drive toward making it so the App could fetch, present, and support basic interaction with Pull Requests. My goal with this phase of development was to get the minimal aggregation functional, interaction and UI implemented so that we could try it out and see if we felt it was functional at all in this rough state.<\/p>\n<p>So, I implemented the following:<\/p>\n<ul>\n<li>Fetching &amp; aggregating Pull Requests from accounts<\/li>\n<li>Scheduling of Pull Requests refresh at 5 mins<\/li>\n<li>Open Pull Request by double clicking<\/li>\n<li>Refresh Pull Requests on account addition\/removal so user doesn\u2019t have to wait ~5 mins after adding an account to see their PRs<\/li>\n<\/ul>\n<figure>\n\t<img src=\"initial_pull_request_list_and_account_management.png\" alt=\"Initial Pull Request List & Account Management\" style=\"\" class=\"img-fluid\" \/>\n\t<figcaption>The most basic list of pull requests<\/figcaption>\n<\/figure>\n<h2 id=\"making-things-functional-round-1\">Making Things Functional - Round 1<\/h2>\n<p>At this point I distributed it to <a rel=\"external\" href=\"https:\/\/twitter.com\/Ryan_Hedges\">Ryan<\/a>, <a rel=\"external\" href=\"https:\/\/www.linkedin.com\/in\/mdnew\/\">Matt<\/a>, <a rel=\"external\" href=\"https:\/\/twitter.com\/adamkorman\">Adam<\/a>, and <a rel=\"external\" href=\"https:\/\/twitter.com\/cciocan\">Claude<\/a> so they could start running it to see how functional\/useful it was at this point. We decided that it was functional, supporting our theory that we believe there is value in this App. However, there were some immediate things that we thought would make it easily more valuable. So, I added:<\/p>\n<ul>\n<li>Displaying Repository name into the pull request list<\/li>\n<li>Ability to ignore Pull Requests from repositories by right clicking on a pull request and selecting <em>ignore repository<\/em>.<\/li>\n<li>Adding organization, description &amp; avatar to Pull Request list - <em>Note:<\/em> I did this without caching of avatar images, etc. so that I could get feedback quicker, with and with less investment.<\/li>\n<\/ul>\n<figure>\n\t<img src=\"non_markdown_pull_request_list_and_repository_ignores.png\" alt=\"Non-Markdown Pull Request List & Repository Ignores\" style=\"\" class=\"img-fluid\" \/>\n\t<figcaption>It's starting to look like a real app now<\/figcaption>\n<\/figure>\n<h2 id=\"some-polish-round-1\">Some Polish - Round 1<\/h2>\n<p>I sent the latest build to <a rel=\"external\" href=\"https:\/\/twitter.com\/Ryan_Hedges\">Ryan<\/a>, <a rel=\"external\" href=\"https:\/\/www.linkedin.com\/in\/mdnew\/\">Matt<\/a>, <a rel=\"external\" href=\"https:\/\/twitter.com\/adamkorman\">Adam<\/a>, and <a rel=\"external\" href=\"https:\/\/twitter.com\/cciocan\">Claude<\/a> and we started using it again. After a day or so of use the feedback was pretty unanimous. The added features aided significantly and we felt we were on the right path.  However, we did identify a number of small things that just didn\u2019t make it feel polished enough or didn\u2019t make it feel like a real product. I like to tackle these types of issues as I develop an application for two main reasons.<\/p>\n<ul>\n<li>it is a pain to have to worry about them all at the end<\/li>\n<li>it is hard sometimes to not have feedback swayed by some of these polish things<\/li>\n<\/ul>\n<p>This lead me to start designing an icon for the App. It is weird how much of mental game not having an icon for your App is. But, once you have one (even if it isn\u2019t the greatest final version) it does a huge amount to make you feel like you are moving forward and that your app is becoming a real thing. So, I designed the first draft of the icon. I took ideas from both the Pull Request Icon that GitHub uses and the Git logo, threw them in a blender and came up with the following to get started.<\/p>\n<figure>\n\t<img src=\"pra_icon_first_draft.jpg\" alt=\"Pra Icon First Draft\" style=\"\" class=\"img-fluid\" \/>\n\t<figcaption>App icon, v1<\/figcaption>\n<\/figure>\n<p>I also added Microsoft App Center integration for Analytics Tracking and Crash reporting. These are crucial as a feedback mechanism as I distribute the app internally and as I prepare to distribute the free version publicly.<\/p>\n<p>Beyond that, I made cut, copy, paste, undo &amp; redo work. This was especially important in the OAuth WebView because when you would try to fill out the OAuth form you were not able to paste your password from a password manager. Extremely annoying to say the least. So I had to fix that one.<\/p>\n<p><a rel=\"external\" href=\"https:\/\/twitter.com\/adamkorman\">Adam Korman<\/a> had some time to catch up with me and work off of the various draft variations of the app icon I did, this time taking Apple macOS App Icon guidelines into account (which I completely ignored, whoops) and we ended up with the second draft of the Pra macOS App Icon.<\/p>\n<figure>\n\t<img src=\"pra_icon_second_draft.jpg\" alt=\"Pra Icon Second Draft\" style=\"\" class=\"img-fluid\" \/>\n\t<figcaption>App icon, v2<\/figcaption>\n<\/figure>\n<h2 id=\"making-things-functional-round-2\">Making Things Functional Round 2<\/h2>\n<p>After adding some polish and sharing the build with <a rel=\"external\" href=\"https:\/\/twitter.com\/Ryan_Hedges\">Ryan<\/a>, <a rel=\"external\" href=\"https:\/\/www.linkedin.com\/in\/mdnew\/\">Matt<\/a>, <a rel=\"external\" href=\"https:\/\/twitter.com\/adamkorman\">Adam<\/a>, and <a rel=\"external\" href=\"https:\/\/twitter.com\/cciocan\">Claude<\/a> and using the app for a bit we quickly identified a few other changes that we thought would help improve the value of the core functionality.<\/p>\n<p>So I added assignees to the Pull Request view when they exist for a given Pull Request. This gives people the ability to quickly determine from the list of Pull Requests which PRs have assignees and who they are. This aids with the user deciding if it is a PR they need to review and provide input on or not.<\/p>\n<figure class=\"dropshaddow-screenshot\">\n\t<img src=\"non_markdown_pull_request_list_with_assignees.png\" alt=\"Pra Non-Markdown Pull Request List with Assignees\" style=\"\" class=\"img-fluid\" \/>\n\t<figcaption>More polish<\/figcaption>\n<\/figure>\n<p>I also added Pull Request body Markdown rendering. I did this for two reasons.<\/p>\n<ul>\n<li>the bodies of GitHub pull requests are often written in Markdown as GitHub supports Markdown rendering.<\/li>\n<li>the Pull Request bodies were coming back with hard breaks, and this made word wrapping look weird.<\/li>\n<\/ul>\n<p>Markdown rendering allows us to interpret paragraphs, ignoring hard breaks, making word wrap work as expected as well as support Markdown formatting used in Pull Request bodies.<\/p>\n<figure class=\"dropshaddow-screenshot\">\n\t<img src=\"markdown_pull_request_list.png\" alt=\"Markdown Pull Request List\" style=\"\" class=\"img-fluid\" \/>\n\t<figcaption>Now with Markdown rendering<\/figcaption>\n<\/figure>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>We have been using the app in this state for a little bit now and have some more polish changes I need to make. For instance:<\/p>\n<ul>\n<li>use slightly smaller fonts<\/li>\n<li>support dark mode\/light mode explicitly so that we have better control of color and theming<\/li>\n<\/ul>\n<p>I also have a number of features I need to add before I feel the initial free version will be ready:<\/p>\n<ul>\n<li>figure out a good in-app feedback mechanism and make sure people can trivially find it and know we want feedback (feedback is key to success)<\/li>\n<li>add welcome tutorial that walks user through initial setup and educates them on how it is intended to be used<\/li>\n<li>add buttons to the UI to support interactions with pull requests so that the interactions are more discoverable than mysterious right clicks, etc.<\/li>\n<li>add relative time to the pull requests so the user can see how old PRs are<\/li>\n<li>add avatar caching to make the app feel more responsive<\/li>\n<li>restrict the account management to 1 account and provide in-app education about why user can only have 1 account in the free version<\/li>\n<\/ul>\n<p>Outside of that I have to do the following:<\/p>\n<ul>\n<li>build out a landing page<\/li>\n<li>get our Privacy Policy together<\/li>\n<li>get our Support page together<\/li>\n<li>write up all the App Store marketing material<\/li>\n<\/ul>\n<p>This will get us to an initial App Store launch at which point we can start promoting the app and talking about the app to get more users and get more feedback while we start working on the premium tier of the application. At least that is the high level plan as of right now.<\/p>\n"},{"title":"Pra: Thinking about Money","published":"2018-12-19T00:00:00+00:00","updated":"2018-12-19T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/pra-thinking-about-money\/"}},"id":"https:\/\/drewdeponte.com\/blog\/pra-thinking-about-money\/","content":"<p>While I am waiting to collect information from the <a href=\"\/writing\/pull-request-notifications-an-anti-pattern\/\">survey<\/a>, I started thinking about how I could make money with Pra. I thought about it before at a high level. But now I have started thinking about it in more depth.<\/p>\n<h2 id=\"expectations\">Expectations<\/h2>\n<p>It is important to understand that my expectations aren't that this product be some sort of unicorn. I share the mindset of <a rel=\"external\" href=\"https:\/\/pjrvs.com\">Paul Jarvis<\/a> \u2014 I want to build a successful, sustainable business rather than shoot for explosive growth \u2014 and we largely operate our current boutique consulting\/contracting business that way. Paul just launched a book titled <a rel=\"external\" href=\"https:\/\/ofone.co\">Company of One<\/a> where he talks about this mindset. If I boil it down, my expectations for Pra are to be something that:<\/p>\n<ul>\n<li>actually helps people<\/li>\n<li>I can be proud of<\/li>\n<li>isn't overly complex or requires massive amount of effort to develop<\/li>\n<li>I can sell and make a relatively small amount of money from<\/li>\n<li>I can use to provide content to start building an audience<\/li>\n<\/ul>\n<h2 id=\"how-do-i-make-money\">How Do I Make Money?<\/h2>\n<p>After talking in depth about this with my co-workers, Ryan and Adam, it seems like I want to have a free tier that provides \"real\" value. Ideally I want people to find enough value in the free version that they use it over GitHub (or any of the other providers) directly and are willing to tell other people about it.<\/p>\n<p>Then, to make money, I need to find compelling additional features that at least some people would be willing to pay for.<\/p>\n<p>The natural organizational entities are <em>accounts<\/em>, <em>organizations<\/em>, and <em>repositories<\/em>.<\/p>\n<h3 id=\"stances\">Stances<\/h3>\n<p>There are two main stances I have thought of related to this product that I could take:<\/p>\n<ul>\n<li><strong>Core Value Paywalled.<\/strong> - In this stance, I would provide a free version of the app that wouldn\u2019t do any aggregation. For this to work, I think it would have to be an app that works similar to GitHub, where you get access to the repos belonging to a single account and the ability to click on one and see the pull request for that specific repository. I believe it would also need to have some additional (free) features that make it more useful than just using Github itself. Then, I would have a paywall gating access to the aggregation feature. I would then have to provide some sort of trial experience to let the user experience the value of aggregation. The concern with this stance is that it would reduce the number of people willing to try the product out because they are hit with a paywall\/trial right off the bat.<\/li>\n<li><strong>Core Value Free<\/strong> - In the <em>core value free<\/em> stance, I would provide the aggregation for free within a single account. Then the stuff behind the paywall would be <em>N<\/em> accounts, as well as the set of tertiary features that make the overall experience better. The high-level thinking about this strategy is that if it is valuable enough, I may be able to get a larger number of people using the free version (since they get the core value for free). I would then have a channel through paywalled tertiary features that I would communicate on a personal level about who I am, what our mission is, what they get, and talk about it in terms of supporting our mission. The thinking is I could get a much wider net of free users to build our following for communicating other maker tools, etc. in the future, and some subset of people will pay for a Pro version. The concern with this approach is that if I provide the core value for free, there's less additional value (that people would pay for) in the Pro version.<\/li>\n<\/ul>\n<p>My current stance is that I am not 100% sure which route to take. My instinct is that I should take the <em>Core Value Free<\/em> stance as it facilitates growing a potentially larger following for other products in the future, and I can envision how to make it happen. Also, in the <em>Core Value Paywalled<\/em> stance I would potentially have to build out a larger more complicated version of the app prior to the paywall being a thing, as it wouldn\u2019t make much sense for people to use it if it was a worse tool than what GitHub already provides.<\/p>\n<p>The above leads us to the following questions:<\/p>\n<h4 id=\"do-i-charge-by-the-repository-and-limit-the-free-version-to-1-repo-or-maybe-some-fixed-number-of-repositories-like-3\">Do I charge by the repository and limit the free version to 1 repo, or maybe some fixed number of repositories like 3?<\/h4>\n<p>After hashing it out I think the answer to this question is <strong>no<\/strong>. I don\u2019t think it provides\/communicates\/shows the value of aggregation when it is limited to simply 3 repositories.<\/p>\n<h4 id=\"do-i-charge-by-the-organization\">Do I charge by the organization?<\/h4>\n<p>Well, I could. However, the largest use case of people are probably people that have one primary Organization (the org of the company they work for), and their personal organization. The question then becomes is there enough value of aggregation seen for them to then purchase a subscription just to get their personal org added in, or would they just deal with the annoyance? After talking about it with Adam and Ryan I think most people would just deal with the annoyance. So, the answer is no.<\/p>\n<p>I also need to keep in mind that I want the largest possible group of people using and finding the free version valuable so that I can convince them to pay for even more great value.<\/p>\n<h4 id=\"do-i-charge-for-multiple-accounts\">Do I charge for multiple accounts?<\/h4>\n<p>Initially the thinking around this was that I charge for multiple accounts, as it is probably a niche subset of the market (consultants\/contractors). However, I know personal developers who have their personal repos in BitBucket and their company repos in GitHub. So, I am not sure where I net on this at the moment. It still needs some consideration.<\/p>\n<h3 id=\"so-where-am-i\">So Where am I?<\/h3>\n<p>If I don\u2019t charge based on core organizing principles what do I charge for? Right now the thinking is that I would charge for features. For instance, provide some of the Quick Filters for free and some of them would require you to be a subscriber. I could also make the search functionality, keyboard shortcuts, enriched PRs, etc. paid features.<\/p>\n<h3 id=\"principles-of-purchase\">Principles of Purchase<\/h3>\n<p>One thing I don\u2019t like is the complexity around purchases and nickel and diming people. So I think it should basically be two tiers:<\/p>\n<ul>\n<li>Free \u2013 Roughly feature parity with seeing PRs in GitHub, with the added value of the aggregated views and some other nice to have features<\/li>\n<li>Pro (probably a monthly subscription) - All The Features. I think that getting people to pay is as much about access to the features as it is finding supporters around our mission of helping makers, and who we are. So they aren\u2019t just buying <em>a thing,<\/em> they are supporting a small boutique group of real people they can identify with.<\/li>\n<\/ul>\n<p>One thing I have talked about as a possible 3rd tier would be Enterprise, which would potentially get you integrations with GitHub Enterprise, etc. The thinking around this is some individuals might pay for it because they have become dependent on Pra and love it. But I think the real money with that is in group licensing to companies that use Enterprise GitHub, etc.<\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>Although I don't have all the answers, my instinct is still to take the <strong>Core Value Free<\/strong> stance. I do believe thinking through some of this prior to getting the results from the survey back is a good thing. It helps me look at the data I get back in ways I may not have looked at it originally. Hopefully, the results of the survey will help clarify answers to some of these questions.<\/p>\n"},{"title":"Pull Request Notifications: an Anti-Pattern","published":"2018-12-11T00:00:00+00:00","updated":"2018-12-11T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/pull-request-notifications-an-anti-pattern\/"}},"id":"https:\/\/drewdeponte.com\/blog\/pull-request-notifications-an-anti-pattern\/","content":"<p>Back in 2013, I was lead of a software development team at ReachLocal, Inc. At the time we were using GitHub for open source projects and Atlassian Stash for our private Git repositories.<\/p>\n<p>We required at least one reviewer to review each pull request before it could be merged into the mainline. We also generally didn\u2019t assign specific people to pull requests as reviewers because we wanted to promote knowledge sharing throughout the company. So, people had to go grab a pull request or two every once in a while and review them.<\/p>\n<h2 id=\"problems\">Problems<\/h2>\n<p>Email notifications didn't really work well in this scenario and they have a tendency to get lost in peoples\u2019 inboxes. So naturally we progressed to annoying our teammates via our company chat system (Slack, now) until someone eventually reviewed the pull requests.<\/p>\n<p>It didn\u2019t take me long to realize that we had fallen into a communication anti-pattern \u2014 people didn\u2019t need instant feedback on their pull requests. Also, the chat messages announcing pull requests ready for review generally got lost in the mix of the other chat messages, and the author would end up posting a message again and again to get peoples\u2019 attention and hopefully get a review. This was also having the negative impact of breaking the developers on the team out of \u201cdeep work\u201d and costing a huge amount in terms of context switching.<\/p>\n<p>At the same time, we were having problems losing track of pull requests across a number of repositories (and across different Git repository management systems). This would happen all the time, as we had different repositories for various libraries, services, and micro-services we had built out for the systems my team was responsible for. Since the only way to proactively check for pull requests to review was to go to each individual repository and check the pull request tab, it's not surprising that they would fall through the cracks.<\/p>\n<h2 id=\"baby-step\">Baby Step<\/h2>\n<p>The culmination of these problems drove me to try and come up with a solution. Something that would ideally <strong>show me all of the pull requests that I cared about seeing, none of the ones I didn\u2019t, and exactly when I wanted to see them<\/strong>.<\/p>\n<p>As a developer, my natural tendency is to build a software application to solve my problems. So that is exactly what I did. I created an open source terminal based curses application in Ruby called <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/pra\">pra<\/a> (Pull Request Aggregator).<\/p>\n<img src=\"pra-cli-screenshot.png\" alt=\"Pra cli\" style=\"\" class=\"img-fluid\" \/>\n<p>This app was a great step in the right direction and many people continued to use it as we went on to work at other companies. But, it was definitely lacking in certain areas largely due to the limitations of curses. For instance, if it had GUI then it could provide more information in a more concise view as well as provide other functionality around viewing specific types of pull requests, etc.<\/p>\n<h2 id=\"state-of-the-world\">State of the World<\/h2>\n<p>Jump forward about 5 years to December 2018 and sadly not much has changed in the pull request world. On my front, I am the co-founder &amp; co-owner of a very small (<a href=\"\/about\">5 of us in total<\/a>) boutique Software Product, Design, and Development Consultancy, <a rel=\"external\" href=\"https:\/\/uptechstudio.com\">Uptech Studio<\/a> where our mission is to provide services and tools to Makers that help them bring their dreams to life.<\/p>\n<p>The team here at <a rel=\"external\" href=\"https:\/\/uptechstudio.com\">Uptech Studio<\/a> was going through a list of potential products we could build. Looking at pull requests and the space around them, we still believe there is a gap in the workflows and the lack of tooling around them. Therefore we have decided to tackle this problem space. We believe building a GUI version of <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/pra\">pra<\/a> is a great place to start and iterate from. Although we have envisioned a much more feature rich application that we think will solve other problems, we are approaching a point where we plan to release a free version that even in an early form would be useful and valuable. To get a taste, below is a snapshot of a very early, very rough internal build.<\/p>\n<img src=\"pra-macos-screenshot.png\" alt=\"Pra macOS\" style=\"\" class=\"img-fluid\" \/>\n<h2 id=\"the-future\">The Future<\/h2>\n<p>Being a very small boutique (yet experienced) product, design, and development team, we know better than to just build something and hope that our vision is right. We need to vet our theories before we invest lots of time and money into making them a reality.<\/p>\n<p>This is where you come in. I know it is a lot to ask, but we need help vetting some directions we are thinking about taking the product. So, we have put together a <a rel=\"external\" href=\"https:\/\/goo.gl\/forms\/otHy58AVGkRvbZMm1\">small single page survey<\/a> to help guide us on some of these decisions.<\/p>\n<div class=\"text-center my-4\"><a class=\"btn btn-lg btn-outline-dark text-center\" href=\"https:\/\/goo.gl\/forms\/otHy58AVGkRvbZMm1\">Fill Out the Survey<\/a><\/div>\n<p>If you have read this and have experienced some of these issues and would value a product focused on solving problems in this space please provide your email below showing your support for this product. We promise we won\u2019t spam you. We will provide you with details about launches, ask for feedback about certain features, and invite some of you to alpha or beta releases.<\/p>\n"},{"title":"Sizing and Optimizing Images for iOS","published":"2018-11-09T00:00:00+00:00","updated":"2018-11-09T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/sizing-and-optimizing-images-for-ios\/"}},"id":"https:\/\/drewdeponte.com\/blog\/sizing-and-optimizing-images-for-ios\/","content":"<h2 id=\"why\">Why?<\/h2>\n<p>Sizing and Optimizing images for iOS can have a huge impact on performance of your app and the overall user experience. Therefore, it is important to take the time to figure out the proper sizing for your images and to make sure that they get appropriately resized and optimized for your needs.<\/p>\n<h2 id=\"understanding-your-constraints\">Understanding your constraints<\/h2>\n<p>I like to start by figuring out the devices that I am interested in supporting. I was recently working on an app where we wanted to support the iPhone 6 all the way up to the latest iPhone Xs Max. So to figure out the sizes of the images I needed some context as to what size in points the screens on those devices are and at what resolution they get rendered. I use <a rel=\"external\" href=\"https:\/\/www.paintcodeapp.com\/news\/ultimate-guide-to-iphone-resolutions\">The Ultimate Guide To iPhone Resolutions<\/a> all the time for this type of reference.<\/p>\n<p>In this case since I am supporting iPhone 6 as the lowest size which is <strong>375 x 667<\/strong> points and the iPhone Xs Max as the largest size, <strong>414 x 896<\/strong> points. If you look at all the point sizes between that range of phones. There are really two different aspect ratios I need to support <strong>0.46<\/strong> for the iPhone Xs Max, iPhone Xr, iPhone X, iPhone Xs, and <strong>0.56<\/strong> for the iPhone 6+, 6s+, 7+, 8+, 6, 6s, 7, and 8.<\/p>\n<p>This means that I am most likely going to have to do some cropping depending on which device the images are loaded on.<\/p>\n<h3 id=\"image-space-in-points\">Image space in points<\/h3>\n<p>Now that I have some context in terms of max size and min size. I need to figure out what the point size of the actual image space is going to be.\nI approached this in this scenario by adding a log line to the app that logged point size of the frame of the image area.<\/p>\n<p>For <a rel=\"external\" href=\"https:\/\/itunes.apple.com\/us\/app\/odyssey-3d\/id1435538334?&amp;mt=8\">this particular app<\/a> I am implementing a carousel of profile pics of people participating in the application. At the moment I had an iPhone X and a iPhone 7+ with me. So, I built the app and ran it on both the iPhone 7+ and the iPhone X to get the point size of the profile cards on those devices from the logs.<\/p>\n<br\/>\n<table style=\"width: 300px;\">\n  <tr>\n    <th>\n      Device\n    <\/th>\n    <th>\n      Size in Points\n    <\/th>\n    <th>\n      Aspect Ratio\n    <\/th>\n  <\/tr>\n  <tr>\n    <td>\n      iPhone 7+\n    <\/td>\n    <td>\n      254 x 416\n    <\/td>\n    <td>\n      0.61\n    <\/td>\n  <\/tr>\n  <tr>\n    <td>\n      iPhone X\n    <\/td>\n    <td>\n      215 x 434\n    <\/td>\n    <td>\n      0.49\n    <\/td>\n  <\/tr>\n<\/table>\n<br\/>\n<p>From there I could deduce what the point size of the Profile Card would be on the iPhone 6 and the iPhone Xs Max by looking at the difference in points between the screen sizes. For example the iPhone 6 screen size is <strong>375 x 667<\/strong> and the test device I had was an iPhone 7+ at <strong>414 x 736<\/strong> which is <strong>39<\/strong> points wider and <strong>69<\/strong> points taller. Knowing that, I can apply those difference to the iPhone 7+'s Profile card point size of <strong>254 x 416<\/strong> resulting in the Profile Card being <strong>215 x 347<\/strong> points on iPhone 6.<\/p>\n<p>The same concept can be applied going from The iPhone X Profile Card point size to the iPhone Xs Max Profile Card point size. In this case the iPhone X Profile Card point size is <strong>215 x 434<\/strong> and the there is a difference of <strong>39 x 84<\/strong> between iPhone X and iPhone Xs Max point sizes. When applied this results in the Profile Card being <strong>254 x 518<\/strong> points on iPhone Xs Max.<\/p>\n<br\/>\n<table style=\"width: 300px;\">\n  <tr>\n    <th>\n      Device\n    <\/th>\n    <th>\n      Size in Points\n    <\/th>\n    <th>\n      Aspect Ratio\n    <\/th>\n  <\/tr>\n  <tr>\n    <td>\n      iPhone 6\n    <\/td>\n    <td>\n      215 x 347\n    <\/td>\n    <td>\n      0.619\n    <\/td>\n  <\/tr>\n  <tr>\n    <td>\n      iPhone Xs Max\n    <\/td>\n    <td>\n      254 x 518\n    <\/td>\n    <td>\n      0.490\n    <\/td>\n  <\/tr>\n<\/table>\n<br\/>\n<p>All of that was to understand the point size dimensions of the smallest image and largest image spaces that I want to support and make sure they look beautiful. From this data I see one very important thing. The aspect ratios are not the same. This means that there is going to be some cropping happening.<\/p>\n<h3 id=\"where-to-crop\">Where to crop<\/h3>\n<img src=\"head_cropped.png\" alt=\"Headshot Cropped\" style=\"height: 250px; float: right; border-radius: 12px; margin: 16px;\" \/>\n<p>In this case I was prepping these images for profile pictures of people and they were sending their headshots to us. Most of these headshots had one thing in common, the persons face was toward the top of the image and relatively centered horizontally. For this particular type of content it is safer to crop off the sides of the images generally, rather than cropping off the top as I don't want an image to crop someones face.<\/p>\n<p>If I were for example to make the size of the images match the largest devices height, on the smaller devices with the different aspect ratios peoples heads would get chopped off like you see in the example.<\/p>\n<img src=\"head_not_cropped.png\" alt=\"Headshot Not Cropped\" style=\"height: 250px; float: left; border-radius: 12px; margin: 16px;\" \/>\n<p>Because I don't want to lose any of the tops of the images. I needed to use the smallest height and the widest width from our device extremes for the image. This covers me in all the sizes between and will make it so only the sides get cropped. Yes, it does mean that I have some upscaling happening on the larger devices. But, in this case we did this optimization and tested it and found the upscaling looks fine as it is relatively minimal.<\/p>\n<p>In my case that meant that the image size in terms of points that I wanted to produce for these profile cards was:<\/p>\n<br\/>\n<table style=\"width: 300px;\">\n  <tr>\n    <th>\n      Universal Image Size (points)\n    <\/th>\n  <\/tr>\n  <tr>\n    <td>\n      (Longest Width) x (Shortest Height)\n    <\/td>\n  <\/tr>\n  <tr>\n    <td>\n      (iPhone Xs Max Width) x (iPhone 6 Height)\n    <\/td>\n  <\/tr>\n  <tr>\n    <td>\n      254 x 347\n    <\/td>\n  <\/tr>\n<\/table>\n<br\/>\n<h3 id=\"getting-the-resolution-right\">Getting the Resolution Right<\/h3>\n<p>Now that I have an understanding of the image space I need to worry about in points for the image. I need to figure out what size the actual image should be in pixels. Given that points are the representation in iOS land when you are developing and pixels are what points get translated into and rendered out to on the screen. We need to have an understanding of how these devices render pixels out. Luckily <a rel=\"external\" href=\"https:\/\/www.paintcodeapp.com\/news\/ultimate-guide-to-iphone-resolutions\">The Ultimate Guide To iPhone Resolutions<\/a> provides this information for us as well.<\/p>\n<p>If we look at the iPhone Xs Max we can see that it renders to the screen at <strong>3x<\/strong>. This means at a value of it's screen's point size multiplied by 3. The iPhone 6 on the other hand looks to only render at 2x. Meaning 2 times it's point size. So, I am now presented with a choice. Option one, pick the 2x rendering of the iPhone 6. This would result in a pixel size of <strong>508 x 694<\/strong>. This size would leave a pretty big gap for the larger devices to fill with upscaling. The other option is to use 3x rendering of the iPhone Xs Max which would result in a pixel size of <strong>762 x 1041<\/strong>. This seems like a much more reasonable size for the larger devices and it will just be scaled down to fit on the smaller screen devices like the iPhone 6.<\/p>\n<p>Therefore, I opted for the later option of a pixel size of <strong>762 x 1041<\/strong>. I took this size and added an extra 30 pixels to both the width and the height as I am planning on using some image techniques later on in the app that would require having that overage. So I really resulted in wanting images at <strong>792 x 1071<\/strong>.<\/p>\n<h3 id=\"batch-scaling-images\">Batch Scaling Images<\/h3>\n<p>Now, that the hard part of figuring out what the best size for the image is. The easy part can begin. I found this wonderful little app named <a rel=\"external\" href=\"https:\/\/veprit.com\/resizesense\">Resize Sense<\/a>. It provides the ability to batch apply various transformations to images. In this particular case it will help me fit, scale, and crop the images that people sent me to the appropriate size. It is worth noting that it isn't the most intuitive app but it does work well. So, experiment with it a bit and it will be well worth it.<\/p>\n<img src=\"resize_sense_main_screen.png\" alt=\"Resize Sense - Custom Preset\" class=\"img-fluid\" style=\"\" \/>\n<p>The first thing I did after launching the <a rel=\"external\" href=\"https:\/\/veprit.com\/resizesense\">Resize Sense<\/a> was to check out the <strong>Preferences<\/strong>. I quickly noticed that it has a <strong>Default image resolution:<\/strong> option which is defaulted to 300 pixels per inch. This got me wondering what the pixels per inch on the iPhones where. A little googling lead me to <strong>458<\/strong> pixels per inch so I updated that preference. I am not sure why this is a global setting. It seems like it should be a setting in the presets but I haven't seen it in there so I just went with it as a global setting.<\/p>\n<img src=\"resize_sense_preferences.png\" alt=\"Resize Sense - Custom Preset\" class=\"img-fluid\" style=\"\" \/>\n<p>After that I discovered that I need to build what they call a Custom Preset. I did this by clicking the <strong>Manage Presets<\/strong> button and defining a preset as follows. <em>Note<\/em>: According to their documentation it is important that presets have unique names. You will notice that the pixel dimensions of the image preset are the same ones we determined above.<\/p>\n<img src=\"resize_sense_custom_preset.png\" alt=\"Resize Sense - Custom Preset\" class=\"img-fluid\" style=\"\" \/>\n<p>After creating the preset I needed to select it and click the <strong>Apply selected preset<\/strong> button which then moved it to the applied presents list. And then I went and selected the first batch of headshot images people sent me and dragged them into the lower right pane of the app and selected one of the images. This applied the transformations and showed me a preview of the resulting image in the upper right pane.<\/p>\n<img src=\"resize_sense_apply_preset_load_images.png\" alt=\"Resize Sense - Custom Preset\" class=\"img-fluid\" style=\"\" \/>\n<p>From there I simply selected <strong>Save All images<\/strong> from the <strong>File<\/strong> menu and selected a destination for the files, loaded the saving preset from the custom preset I made earlier and clicked <strong>Save images<\/strong>. This batch processes all the images I gave it and produced a resulting set of images that had been sized appropriately, cropped, etc.<\/p>\n<img src=\"resize_sense_save_all.png\" alt=\"Resize Sense - Custom Preset\" style=\"\" class=\"img-fluid\" \/>\n<p>The only issue was that the JPEG image optimization in <a rel=\"external\" href=\"https:\/\/veprit.com\/resizesense\">Resize Sense<\/a> isn't very good. This isn't actually a huge problem as there is a community defacto standard that is used which is easy and we know is good. Therefore, I ran the batch scaling, etc. to output at 100% JPG quality as I knew that it would be optimized later.<\/p>\n<h3 id=\"optimization\">Optimization<\/h3>\n<p>Now, that I had a batch of appropriately sized images for the App's Profile Pictures I wanted to compress and optimize them to try and reduce their file size even further. It is worth noting that when I started this process I was dealing with multi-megabyte file sizes for these images.  So, next I opened <a rel=\"external\" href=\"https:\/\/imageoptim.com\/mac\">ImageOptim<\/a>, a wonderful little macOS application that is as simple as they come, and I configured the preferences to be as follows by clicking the little gear icon.<\/p>\n<img src=\"image_optim_preferences.png\" alt=\"ImageOptim - Preferencs\" style=\"\" class=\"img-fluid\" \/>\n<p>Then, I dragged all the image files that were output by <a rel=\"external\" href=\"https:\/\/veprit.com\/resizesense\">Resize Sense<\/a> and <a rel=\"external\" href=\"https:\/\/imageoptim.com\/mac\">ImageOptim<\/a> did it's thing and I ended up with great looking images that were now sub 100kb.<\/p>\n<p>This is a huge game changer for the performance of the application as these images are downloaded over often slow cellular networks.<\/p>\n<p>So, please make sure you take the time to properly figure out the right image sizes for your app and please optimize them.<\/p>\n"},{"title":"Inline Links in Text in iOS Apps","published":"2018-10-19T00:00:00+00:00","updated":"2018-10-19T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/inline-links-in-text-in-ios-apps\/"}},"id":"https:\/\/drewdeponte.com\/blog\/inline-links-in-text-in-ios-apps\/","content":"<p>Today, I was working on a client project. As usual a couple of  designers had done some high fidelity designs in <a rel=\"external\" href=\"https:\/\/www.sketchapp.com\">Sketch<\/a> that I was riffing off. In the designs on the Sign Up screen near the bottom there was the classic notice stating that by signing up you are agreeing to the <strong>User Agreement<\/strong> and <strong>Privacy Policy<\/strong>.<\/p>\n<img src=\"user_agreement_notice_screenshot.jpg\" alt=\"User Agreement Notice\" style=\"display: block; margin-left: auto; margin-right: auto; width: 50%;\" \/>\n<p>There are two interesting things about this. First and foremost the phrases <strong>User Agreement<\/strong> and <strong>Privacy Policy<\/strong> are visually called out using blue like they would be if they were anchor tags (a.k.a. links) in a website. Secondly, both these phrases are inline with other text. If I was doing this on a website this would be trivial as I would just put two anchor tags inline in a paragraph tag with the rest of  the text. However, on iOS things aren\u2019t so straight forward.<\/p>\n<h2 id=\"assumptions\">Assumptions<\/h2>\n<p>My assumptions going into this were that I was going to have to somehow detect a tap on each of  those phrases and then present a new modal View Controller containing a web view that loads the <strong>User Agreement<\/strong> or <strong>Privacy Policy<\/strong> respectively.  I knew off hand how to do all of these things except for the detection of the phrases being tapped. Though, I did have enough knowledge to know that with some work it could be done.<\/p>\n<h2 id=\"research\">Research<\/h2>\n<p>I started out searching for \u201cUILabel tappable text\u201d. It led me to a variety of half baked attempts at solving this with code that seemed way more complicated then it should need to be in my mind. I am talking like 60+ line solutions. So, I kept digging and stumbled across a post referencing the existence of some sort of <code>NSAttributedString<\/code> link feature. I thought this was interesting but sadly the post did not give me much direction in terms of actually making it happens. So, I searched a bit more and found an example of applying the <code>NSAttributedString.Key.link<\/code> attribute to an <code>NSMutableAttributedString<\/code>. An example is as follows:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">let<\/span><span> attributedString <\/span><span style=\"color: #E06C75;\">=<\/span><span style=\"color: #B57EDC;\"> NSMutableAttributedString<\/span><span>(<\/span><span style=\"color: #B57EDC;\">string<\/span><span>: \u201cby signing up, you are agreeing to <\/span><span style=\"color: #E5C07B;\">Float<\/span><span>\u2019s User Agreement and Privacy Policy.\u201d)<\/span><\/span>\n<span class=\"giallo-l\"><span>attributedString.<\/span><span style=\"color: #B57EDC;\">addAttribute<\/span><span>(NSAttributeString.<\/span><span style=\"color: #E5C07B;\">Key<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">link<\/span><span>, <\/span><span style=\"color: #B57EDC;\">value<\/span><span>: \u201chttps<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #5F6672;font-style: italic;\">\/\/example.com\/policy\u201d, range: NSRange(location: 43, length: 14)<\/span><\/span><\/code><\/pre>\n<p>When I applied the above and then assigned it to the <code>UILabel<\/code> \u2018s <code>attributedText<\/code> property I got excited because it resulted in the <strong>User Agreement<\/strong> phrase showing up as blue with an underline. This was close to what I wanted design wise and gave me hope that it might actually do a thing. Sadly, tapping on what I believed was a link had no impact. So, I dug further and tried a number of various properties on <code>UILabel<\/code> such as <code>isUserInteractionEnabled<\/code> but no luck.<\/p>\n<p>After searching a bit more I stumbled upon an article that referenced further some delegate methods from <code>UITextViewDelegate<\/code> that people were using with the <code>NSAttributedString.Key.link<\/code> to handle opening them in a browser. This gave me a new path to explore.<\/p>\n<h2 id=\"uitextview\">UITextView<\/h2>\n<p>So, step one I converted my <code>UILabel<\/code> to a <code>UITextView<\/code>. When I initially did this the view was <code>UITextView<\/code> was not visible and inspecting the view I could see that there was layout constraint issues. So, I gave it a hard coded height using auto layout constraints. Magically, it was visible and the layout constraint issue went away. So, now I knew that it was not being sized based on it\u2019s content. After a quick search I was able to find out it is because it has a <code>isScrollEnabled<\/code> property that is by default set to <code>true<\/code>. So, I flipped it to <code>false<\/code> and got rid of the explicit height constraint. Like magic it was there and visible just liked I wanted with the correct styling.<\/p>\n<p>Next I attempted to tap the link. But, to my surprise the keyboard popped up. Of course that is because there is another property <code>isEditable<\/code> that is by default <code>true<\/code>. So, after flipping that to <code>false<\/code> when I tapped the link it actually opened Safari to the URL of the link.<\/p>\n<p>Finally some success! At this point my <code>UITextView<\/code> was configured as follows:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span>textView.<\/span><span style=\"color: #C6CCD7;\">isEditable<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #56B6C2;\"> false<\/span><\/span>\n<span class=\"giallo-l\"><span>textView.<\/span><span style=\"color: #C6CCD7;\">isScrollEnabled<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #56B6C2;\"> false<\/span><\/span>\n<span class=\"giallo-l\"><span>textView.<\/span><span style=\"color: #C6CCD7;\">backgroundColor<\/span><span style=\"color: #E06C75;\"> =<\/span><span> UIColor.<\/span><span style=\"color: #C6CCD7;\">clear<\/span><\/span><\/code><\/pre><h2 id=\"breaking-assumptions\">Breaking Assumptions<\/h2>\n<p>At this point it was actually functioning though in a different fashion than I had originally assumed it would. This made me question my initial assumptions of the need for having a separate view be presented modally containing a web view that would load the content.<\/p>\n<p>The default behavior I was seeing is actually pretty much inline with those concepts. It is basically modally presenting Safari with the content loaded in Safari. My immediate next thought was well how easy is it for the user to get back to the app where they left off in either scenario. In the separate view scenario they would have to hit a close button to dismiss the modal view and go back to the Sign Up view. In the Safari scenario the user simply hits the back to last app button in the upper left.<\/p>\n<p>So, turns out that the default behavior experience feels great, is minimal effort and models how most apps on macOS handle links as well. At this point I am pretty confident this the ideal path and I am driving full force ahead.<\/p>\n<h2 id=\"another-problem\">Another Problem<\/h2>\n<p>Turns out there is one problem. I have been localizing this application up to this point using <code>NSLocalizedString<\/code>. Looking at the example:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">let<\/span><span> attributedString <\/span><span style=\"color: #E06C75;\">=<\/span><span style=\"color: #B57EDC;\"> NSMutableAttributedString<\/span><span>(<\/span><span style=\"color: #B57EDC;\">string<\/span><span>: \u201cby signing up, you are agreeing to <\/span><span style=\"color: #E5C07B;\">Float<\/span><span>\u2019s User Agreement and Privacy Policy.\u201d)<\/span><\/span>\n<span class=\"giallo-l\"><span>attributedString.<\/span><span style=\"color: #B57EDC;\">addAttribute<\/span><span>(NSAttributeString.<\/span><span style=\"color: #E5C07B;\">Key<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">link<\/span><span>, <\/span><span style=\"color: #B57EDC;\">value<\/span><span>: \u201chttps<\/span><span style=\"color: #E06C75;\">:<\/span><span style=\"color: #5F6672;font-style: italic;\">\/\/example.com\/policy\u201d, range: NSRange(location: 43, length: 14)<\/span><\/span><\/code><\/pre>\n<p>We are clearly coupling the link to a specific starting location and length within the string. So, sadly this breaks localization because different languages will have different starting locations and different lengths, and with some googling there is no built in way for localization to support <code>NSAttributedString<\/code>.<\/p>\n<h2 id=\"solution\">Solution<\/h2>\n<p>But, wait there is still hope. At least in my opinion the above linking strategy is by far the best solution I have been able to find. So, if your app does not need localization you can use it straight away. If on the other hand your app does need localization. Don\u2019t worry I have an idea.<\/p>\n<p>The thought is to actually use a subset of <a rel=\"external\" href=\"https:\/\/daringfireball.net\/projects\/markdown\/syntax\">Markdown<\/a> in the localized string content and then write a method that will fetch a <code>NSLocalizedString<\/code> and parse the subset of <a rel=\"external\" href=\"https:\/\/daringfireball.net\/projects\/markdown\/syntax\">Markdown<\/a> into an <code>NSAttributedString<\/code>. This would in theory resolve the localization issue and potentially give localization a decent bit more functionality in terms of representing text.<\/p>\n<p>In fact this can trivially be done with existing Markdown parsing libraries such as <a rel=\"external\" href=\"https:\/\/github.com\/iwasrobbed\/Down\">iwasrobbed\/Down<\/a>. You could use it as follows to get your attributed string with links.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">let<\/span><span> down <\/span><span style=\"color: #E06C75;\">=<\/span><span style=\"color: #B57EDC;\"> Down<\/span><span>(<\/span><span style=\"color: #B57EDC;\">markdownString<\/span><span>:<\/span><span style=\"color: #B57EDC;\"> NSLocalizedString<\/span><span>(\u201cUSER_AGREEMENT_NOTICE\u201d, <\/span><span style=\"color: #B57EDC;\">comment<\/span><span>: \u201cUser Agreement Notice\u201d))<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">let<\/span><span> attributedString <\/span><span style=\"color: #E06C75;\">= try?<\/span><span> down.<\/span><span style=\"color: #B57EDC;\">toAttributedString<\/span><span>()<\/span><\/span><\/code><\/pre>\n<p>The only down side to this approach, at least with this Markdown library, is that the <code>NSAttributedString<\/code> it produces has the links styled with the <code>NSAttributedString.Key.link<\/code> attribute as well as the underline style. This makes it look different than the designs.  With a quick read of the <a rel=\"external\" href=\"https:\/\/github.com\/iwasrobbed\/Down\">iwasrobbed\/Down<\/a> README.md and some spelunking of the <a rel=\"external\" href=\"https:\/\/github.com\/iwasrobbed\/Down\/blob\/master\/Source\/Renderers\/DownAttributedStringRenderable.swift\">DownAttributedStringRenderable.swift<\/a> file I quickly discovered that the <code>toAttributedString()<\/code> method has a <code>toAttriubtedString(stylesheet:)<\/code> variant that allows passing a <code>String<\/code> of CSS in to control the styling. In my case this just meant that I need to remove the <code>text-decoration<\/code> on anchor tags.<\/p>\n<p>I thought about making a library to support this. However, now that I have seen how trivial it is to implement I decided I would just implement a helper method to do this. The helper I now write looks as follows:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">func<\/span><span style=\"color: #B57EDC;\"> localizedMarkdownString<\/span><span>(<\/span><span style=\"color: #B57EDC;\">_<\/span><span style=\"color: #C6CCD7;\"> key<\/span><span>: <\/span><span style=\"color: #E5C07B;\">String<\/span><span>, <\/span><span style=\"color: #B57EDC;\">comment<\/span><span>: <\/span><span style=\"color: #E5C07B;\">String<\/span><span>)<\/span><span style=\"color: #E06C75;\"> -&gt;<\/span><span> NSAttributedString<\/span><span style=\"color: #E06C75;\">?<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\tlet<\/span><span> down <\/span><span style=\"color: #E06C75;\">=<\/span><span style=\"color: #B57EDC;\"> Down<\/span><span>(<\/span><span style=\"color: #B57EDC;\">markdownString<\/span><span>:<\/span><span style=\"color: #B57EDC;\"> NSLocalizedString<\/span><span>(key, <\/span><span style=\"color: #B57EDC;\">comment<\/span><span>: comment))<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\treturn try?<\/span><span> Down.<\/span><span style=\"color: #B57EDC;\">toAttributedString<\/span><span>(<\/span><span style=\"color: #B57EDC;\">stylesheet<\/span><span>: &quot;<\/span><span style=\"color: #98C379;\">a { text-decoration: none; }<\/span><span>&quot;)<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>That\u2019s it! Relish in the beauty of localized Markdown and <code>NSAttributedString<\/code> coming together so harmoniously to solve this problem.<\/p>\n"},{"title":"Decided what to Build","published":"2018-03-11T00:00:00+00:00","updated":"2018-03-11T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/decided-what-to-build\/"}},"id":"https:\/\/drewdeponte.com\/blog\/decided-what-to-build\/","content":"<p>We were struggling to sell one of our side projects <a rel=\"external\" href=\"https:\/\/orangecal.com\">OrangeCal<\/a> while also selling our consulting services as they were for completely different audiences. So, we decided to build and sell products that fit with our clients (software product makers) in the hope that we would find some overlap in sales and at least streamline mental focus for us in terms of sales.<\/p>\n<p>So we looked at the various product spikes we had created as side projects \/ open source projects and laid them all out on the whiteboard and had a team discussion to weigh pros &amp; cons of each one.<\/p>\n<p>We ended up netting on building a product based on an open source project I built as a ruby command line tool called <a rel=\"external\" href=\"https:\/\/github.com\/uptech\/pra\">Pra CLI<\/a> (Pull Request Aggregator). At the time it had approx <strong>15,419 downloads<\/strong>. We also knew that some of the developers on our teams at the last few companies had used it and others didn't specifically because it was a command line tool.<\/p>\n<p>So we decided that was enough interest for us to build a macOS Desktop MVP and see if we could get some traction with it. The plan was to essentially provide the same core functionality in a nice macOS Desktop app and solve some of the problems that prevented people from using the command line version. We figured we could expand on it with other features and figure out the paid product beyond that. I had written down a small list of possible features that the guys seemed to think were good. So, I started coding.<\/p>\n"},{"title":"Git Commit and Tag Signing","published":"2017-08-04T00:00:00+00:00","updated":"2017-08-04T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/git-commit-and-tag-signing\/"}},"id":"https:\/\/drewdeponte.com\/blog\/git-commit-and-tag-signing\/","content":"<p>I believe that there is a large gap in current development practices in terms of association of identity and source code. This is something that a large percentage of people have been ignoring for quite some time. I was one of those people until recently. I started asking myself the question: How hard would it be for someone to write code and claim me as the author, basically forging code in my name? I realized that it was as simple as someone setting their authorship email and name in their <code>.gitconfig<\/code> to the values of my email address and name.<\/p>\n<p>The more I thought about this, the more it concerned me, and the more it seemed there must be a solution to this problem. This especially becomes a problem when you start talking about regulations around software development and the legal responsibility involved with software that is tightly coupled to human life and well-being, for example software in self-driving cars.<\/p>\n<h2 id=\"the-solution\">The Solution<\/h2>\n<p>Coming from a security background, I knew that a digital signature could solve this problem. So, being familar with <a rel=\"external\" href=\"https:\/\/gnupg.org\">GPG<\/a>, an encryption and digital signing tool that is often used to digitally sign and encrypt emails, I decided to search and see if <a rel=\"external\" href=\"https:\/\/git-scm.com\">Git<\/a> supported integration with <a rel=\"external\" href=\"https:\/\/gnupg.org\">GPG<\/a>. Sure enough it does, specifically for signing commits and tags. Knowing this, the next step was to setup and configure my machine, which is running macOS Ventura Version 13.0.1.<\/p>\n<h2 id=\"how-i-did-it\">How I Did It<\/h2>\n<p>The following is a breakdown of the final set of steps I took to get <a rel=\"external\" href=\"https:\/\/git-scm.com\">Git<\/a> and <a rel=\"external\" href=\"https:\/\/gnupg.org\">GPG<\/a> functioning together. It took me a number of tries to get to this point, as numerous articles didn't seem to work for me with version 2.3.8 of <a rel=\"external\" href=\"https:\/\/gnupg.org\">GPG<\/a>, which is what I am using. It is also probably worth mentionining that I am using <a rel=\"external\" href=\"https:\/\/git-scm.com\">Git<\/a> version 2.38.1.<\/p>\n<h3 id=\"install-dependencies\">Install Dependencies<\/h3>\n<p>The first thing is to make sure that you have the necessary dependencies installed. I did this via <a rel=\"external\" href=\"https:\/\/brew.sh\">Homebrew<\/a>, and recommend the same.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>brew install gnupg pinentry-mac<\/span><\/span><\/code><\/pre>\n<p>The above installs <code>gnupg<\/code> (a.k.a. <code>gpg<\/code>) the tool set for digital signing, and <code>pinentry-mac<\/code>, a tool to securely prompt and manage password\/pin entry.<\/p>\n<h3 id=\"gnupg-key-pair-generation\">GnuPG Key Pair Generation<\/h3>\n<p>Once you have the dependencies installed, assuming you don't have a <a rel=\"external\" href=\"https:\/\/gnupg.org\">GPG<\/a> key pair already, you need to create one. This is done by running the following command and following the on screen instructions.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>gpg --gen-key<\/span><\/span><\/code><\/pre>\n<p>Next, I recommend backing up your key pair. This is important, as you probably won't want to create a new identity if you get a new computer. I recommend backing it up in a physically secured space, like a safe. Given the depth and size of debate around proper key management, I will simply point you to the <a rel=\"external\" href=\"http:\/\/csrc.nist.gov\/publications\/nistpubs\/800-57\/sp800-57_part1_rev3_general.pdf\">NIST Recommendation for Key Management<\/a>.<\/p>\n<p>The crucial thing is that you <strong>never ever<\/strong> share your private key, otherwise all this effort is pointless, as they would be able to forge your commits and tags.<\/p>\n<h3 id=\"get-your-gpg-key-identifier\">Get your GPG Key Identifier<\/h3>\n<p>Once you have your key, the next step is getting your key identifer. This is needed to configure <a rel=\"external\" href=\"https:\/\/git-scm.com\">Git<\/a> properly later.<\/p>\n<p>The key id is actually a component of the fingerprent of the public key certificate, the thing holding all your subkeys and meta data that are used for signing and encryption. Specifically, the \"Key ID\" (a.k.a. Long Key ID) is the last 16 characters of the fingerprint. You can get it by running the following:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>gpg --fingerprint &lt;email you tied to the key during generation&gt;<\/span><\/span><\/code><\/pre>\n<p>The output should look something like the following:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>pub   rsa2048 2017-07-28 [SC] [expires: 2019-07-28]<\/span><\/span>\n<span class=\"giallo-l\"><span>            678A A67F A701 CFA7 D666  BCE8 41C5 D2C6 E5AF 944C<\/span><\/span>\n<span class=\"giallo-l\"><span>uid           [ultimate] Andrew De Ponte &lt;cyphactor@gmail.com&gt;<\/span><\/span>\n<span class=\"giallo-l\"><span>sub   rsa2048 2017-07-28 [E] [expires: 2019-07-28]<\/span><\/span><\/code><\/pre>\n<p>In the above output the following is the fingerprint:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>678A A67F A701 CFA7 D666  BCE8 41C5 D2C6 E5AF 944C<\/span><\/span><\/code><\/pre>\n<p>The last 16 characters are:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>41C5 D2C6 E5AF 944C<\/span><\/span><\/code><\/pre>\n<p>If you remove the spaces you have the Key ID, which you will need very shortly:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>41C5D2C6E5AF944C<\/span><\/span><\/code><\/pre>\n<p><strong>Note:<\/strong> It is ok to expose your fingerprint. It is part of the public key certificate and is something that you would share with someone to prove that the public key they have for you is actually your public key.<\/p>\n<h3 id=\"tell-git-about-your-gpg-key\">Tell Git about your GPG Key<\/h3>\n<p>Next you need to tell <a rel=\"external\" href=\"https:\/\/git-scm.com\">Git<\/a> that you have a <a rel=\"external\" href=\"https:\/\/gnupg.org\">GPG<\/a> key and to use it to sign your commits and tags. This is done by running the following:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>git config --global user.signingkey &lt;your gpg key id&gt;<\/span><\/span>\n<span class=\"giallo-l\"><span>git config --global commit.gpgsign true<\/span><\/span>\n<span class=\"giallo-l\"><span>git config --global tag.gpgsign true<\/span><\/span><\/code><\/pre><h3 id=\"configure-gpg\">Configure GPG<\/h3>\n<p>Then you need to configure <a rel=\"external\" href=\"https:\/\/gnupg.org\">GPG<\/a> appropriately to work on macOS. In order to do this you need to create the <code>~\/.gnupg\/gpg-agent.conf<\/code> file, with the following content:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>pinentry-program \/usr\/local\/bin\/pinentry-mac<\/span><\/span><\/code><\/pre>\n<p>The the above config tells <a rel=\"external\" href=\"https:\/\/gnupg.org\">GPG<\/a> to work with the <code>pinentry-mac<\/code> program we installed at the beginning, allowing it to properly prompt us for the password when needed.<\/p>\n<h3 id=\"testing-it-out\">Testing It Out<\/h3>\n<p>Now that everything should be set up, you need to test it out and verify that everything is working properly. Below are the steps I took to create a test repository with an initial signed commit.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>$ mkdir -p \/tmp\/test-git-signing<\/span><\/span>\n<span class=\"giallo-l\"><span>$ cd \/tmp\/test-git-signing<\/span><\/span>\n<span class=\"giallo-l\"><span>$ git init .<\/span><\/span>\n<span class=\"giallo-l\"><span>$ echo &quot;Hello&quot; &gt; README.md<\/span><\/span>\n<span class=\"giallo-l\"><span>$ git add README.md<\/span><\/span>\n<span class=\"giallo-l\"><span>$ git commit -m &quot;Initial signed commit&quot;<\/span><\/span><\/code><\/pre>\n<p>You will probably be prompted by <code>pinentry-mac<\/code> to enter your <a rel=\"external\" href=\"https:\/\/gnupg.org\">GPG<\/a> key password. <strong>Note:<\/strong> You can check the box to have it store and manage your password in Apple Keychain. This will make it so you don't have to enter your password all the time.<\/p>\n<p>Now you can check out your signature in all it's glory by doing the following:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>$ git log --show-signature<\/span><\/span><\/code><\/pre>\n<p>The output should look something like this:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>commit 1a5fed0757e2c8600318595f1681cf508033e72f (HEAD -&gt; master)<\/span><\/span>\n<span class=\"giallo-l\"><span>gpg: Signature made Sat Aug  5 01:30:43 2017 PDT<\/span><\/span>\n<span class=\"giallo-l\"><span>gpg:                using RSA key 678AA67FA701CFA7D666BCE841C5D2C6E5AF944C<\/span><\/span>\n<span class=\"giallo-l\"><span>gpg: Good signature from &quot;Andrew De Ponte &lt;cyphactor@gmail.com&gt;&quot; [ultimate]<\/span><\/span>\n<span class=\"giallo-l\"><span>Author: Andrew De Ponte &lt;cyphactor@gmail.com&gt;<\/span><\/span>\n<span class=\"giallo-l\"><span>Date:   Sat Aug 5 01:30:43 2017 -0700<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>        Initial signed commit<\/span><\/span><\/code><\/pre>\n<p>The above shows you that the commit is signed and that the signature is <strong>verified<\/strong>. If it was a commit by someone else and wasn't verified, maybe because you didn't have their verified public key in your <a rel=\"external\" href=\"https:\/\/gnupg.org\">GPG<\/a> key ring, it would look something like the following:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>gpg: Signature made Wed Sep 13 02:08:25 2006 PDT using DSA key ID F3119B9A<\/span><\/span>\n<span class=\"giallo-l\"><span>gpg: Can&#39;t check signature: public key not found<\/span><\/span>\n<span class=\"giallo-l\"><span>error: could not verify the tag &#39;v1.4.2.1&#39;<\/span><\/span><\/code><\/pre><h3 id=\"great-so-how-does-this-help\">Great. So how does this help?<\/h3>\n<p>This makes it possible for people to verify via cryptography that you are actually the person that authored the commit or tag. If they are signing their commits and tags it also allows you to do the same. In fact, <a rel=\"external\" href=\"https:\/\/git-scm.com\">Git<\/a> even allows you to have it reject merges and pulls if the commits are <strong>not<\/strong> signed by a verified person. To see more details on this, check out the <a rel=\"external\" href=\"https:\/\/git-scm.com\/book\/en\/v2\/Git-Tools-Signing-Your-Work\">Git Tools Signing Your Work in the Git SCM Book<\/a>.<\/p>\n<p>To really understand exactly how that is done you have to understand that you have two keys, a public key and a private key, and that they are related. You share, and generally publish, your public key. This is because the public key is used by others to do two things. First, to verify signatures you have made with your private key, and second, to encrypt information to make sure you are the only one that can access\/decrypt it using your private key.<\/p>\n<h4 id=\"publish-your-public-key\">Publish Your Public Key<\/h4>\n<p>You need to make sure that you have given people your public key, that you have the other collaborators' public keys, and that everyone has verified everyone's public keys. To facilitate sharing public keys there are key servers you can publish your keys to. Beyond that, <a rel=\"external\" href=\"https:\/\/github.com\">GitHub<\/a> also provides a mechanism for publishing your <a rel=\"external\" href=\"https:\/\/gnupg.org\">GPG<\/a> public keys as well. Just go to your Settings, SSH &amp; GPG Keys, and add your GPG Public Key. You can also publish your public key to a key server using <a rel=\"external\" href=\"https:\/\/gnupg.org\">GPG<\/a> directly. The following is an example:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>$ gpg --send-keys 41C5D2C6E5AF944C<\/span><\/span>\n<span class=\"giallo-l\"><span>gpg: sending key 41C5D2C6E5AF944C to hkps:\/\/hkps.pool.sks-keyservers.net<\/span><\/span><\/code><\/pre>\n<p><strong>Note:<\/strong> The major key servers share and sync the public keys so you shouldn't have to worry about dealing with multiple key servers.<\/p>\n<h4 id=\"verify-and-sign\">Verify and Sign<\/h4>\n<p>Once you have published your key, you need to work with people to verify that the key they have actually belongs to you. This is often done at a <a rel=\"external\" href=\"https:\/\/en.wikipedia.org\/wiki\/Key_signing_party\">Key Signing Party<\/a> to be the most secure. However, I have known many to verify their keys over the phone, as long as the people can answer questions proving they are who they say they are.<\/p>\n<p>You can search for someone's public keys via their email address on the key servers using the following:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>$ gpg --search-keys &lt;their email address&gt;<\/span><\/span><\/code><\/pre>\n<p>After you find and import their public key, you need to verify it and sign it. To verify it you can have your own little signing party, or do it via phone if you are comfortable with that. Either way, the process of verification is the same. First ask the person for the fingerprint of their public key. Next verify that it matches the fingerprint of the public key you have for them. Remember, you can see the fingerprint of their key by doing, <code>gpg --fingerprint &lt;their email address&gt;<\/code>. Once you have verified they match, you can define your level of trust for them using the following:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>$ gpg --edit-keys &lt;their email address&gt;<\/span><\/span>\n<span class=\"giallo-l\"><span>&gt; trust<\/span><\/span><\/code><\/pre>\n<p>For details around the different levels of trust and what they mean you can refer to the <a rel=\"external\" href=\"https:\/\/www.gnupg.org\/gph\/en\/manual\/x334.html\">Trust in a key's owner<\/a>. You can also fully sign a public key by doing the following:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>$ gpg --edit-keys &lt;their email address&gt;<\/span><\/span>\n<span class=\"giallo-l\"><span>&gt; sign<\/span><\/span><\/code><\/pre>\n<p>You can find out more about validating keys in the <a rel=\"external\" href=\"https:\/\/www.gnupg.org\/gph\/en\/manual\/x334.html\">GPG Manual - Validating other keys on your public keyring<\/a>. More information is also available on exchanging keys and signing keys in the <a rel=\"external\" href=\"https:\/\/www.gnupg.org\/gph\/en\/manual\/x56.html\">GPG Manual - Exchanging keys<\/a>.<\/p>\n<h4 id=\"confusion\">Confusion<\/h4>\n<p>I know this topic can be a little confusing at times. A few parts are still confusing for me when I haven't dealt with them in a while. So, if you are confused with terms, or want a deeper understanding of the entities involved and their attributes with <a rel=\"external\" href=\"https:\/\/gnupg.org\">GPG<\/a>, I recommend checking out the article <a rel=\"external\" href=\"https:\/\/davesteele.github.io\/gpg\/2014\/09\/20\/anatomy-of-a-gpg-key\/\">Anatomy of a GPG Key<\/a>. If you are confused about <a rel=\"external\" href=\"https:\/\/gnupg.org\">GPG<\/a> itself, they have decent <a rel=\"external\" href=\"https:\/\/gnupg.org\/documentation\/\">documentation<\/a>.<\/p>\n<p>Once all of the above has been done you should be able to cryptographically verify and manage your source code with true identity representation and trust.<\/p>\n"},{"title":"Remote Stand-ups at UpTech","published":"2017-07-10T00:00:00+00:00","updated":"2017-07-10T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/remote-standups-at-uptech\/"}},"id":"https:\/\/drewdeponte.com\/blog\/remote-standups-at-uptech\/","content":"<h3 id=\"the-problem\">The Problem<\/h3>\n<p>Given that UpTech is a consultancy, our team is often pretty fragmented in terms of physical location. For example, I might be on-site at a client's office while another member is at a different client's office, and yet other members are at our office. This physical fragmentation makes it difficult to stay on top of what is going on across the organization on a day-to-day basis.<\/p>\n<h3 id=\"normal-solution\">Normal Solution<\/h3>\n<p>Normally, in a startup we would have a stand-up every morning to kick-off the day. If you aren't familiar, a stand-up is a time-boxed meeting that is used in many agile methodologies to facilitate communication around individuals' daily commitments, as well as raise awareness of any challenges (a.k.a.  blockers). The reason it is called a stand-up is because the thinking is that if everyone has to stand during the meeting it would help keep the meeting short. For further details on stand-ups and their intent check out the <a rel=\"external\" href=\"https:\/\/en.m.wikipedia.org\/wiki\/Stand-up_meeting\">Wikipedia page<\/a>.<\/p>\n<h3 id=\"our-solution\">Our Solution<\/h3>\n<p>Given that our team is rarely in the same physical location at the same time, we have taken the principles and intent from the stand-up and adapted it to our situation by using a <a rel=\"external\" href=\"https:\/\/slack.com\">Slack<\/a> <code>#standups<\/code> channel to share our commitments and blockers every morning at 10 am. To aid with making sure we have the stand-up right at 10am everyday, we set up a <a rel=\"external\" href=\"https:\/\/get.slack.help\/hc\/en-us\/articles\/208423427-Set-a-reminder\">Slack reminder<\/a> in the <code>#standups<\/code> channel.<\/p>\n<p>Then in the <code>#standups<\/code> channel we communicate the classic stand-up information: What did I do yesterday?, What am I doing today?, Do I have any blockers? Specifically, we use the following template and generally prepare the mornings stand-up in a text buffer before pasting it into the <code>#standups<\/code> channel. The template\/example below is based on the <a rel=\"external\" href=\"https:\/\/get.slack.help\/hc\/en-us\/articles\/202288908-Format-your-messages\">Slack message format<\/a>.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>_Tue July 11, 2017 - Standup_<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>*Yesterday*<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>_client_1_<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\u2022 One thing I did yesterday<\/span><\/span>\n<span class=\"giallo-l\"><span>\u2022 Another thing I did yesterday<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>_client_2_<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\u2022 Yet another thing I did yesterday<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>*Today*<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>_client_1_<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\u2022 A thing I am going to do today<\/span><\/span>\n<span class=\"giallo-l\"><span>\u2022 Something else I am going to do today<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>_client_2_<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\u2022 Another thing I am going to do today<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>*Blockers*<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\u2022 A blocker that is currently preventing me from accomplishing something or will in the near future<\/span><\/span><\/code><\/pre>\n<p>When the above is pasted into <a rel=\"external\" href=\"https:\/\/slack.com\">Slack<\/a> it looks as follows:<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/remote-standups-at-uptech\/slack-screenshot.png\" alt=\"Slack Screenshot\" \/><\/p>\n<p>Given that this is such a core part of our process and that I am a heavy <a rel=\"external\" href=\"http:\/\/www.vim.org\">Vim<\/a> user, I created the <a rel=\"external\" href=\"http:\/\/github.com\/uptech\/vim-slack-format\">Vim Slack Format<\/a> plugin to provide syntax highlighting in <a rel=\"external\" href=\"http:\/\/www.vim.org\">Vim<\/a> for the <a rel=\"external\" href=\"https:\/\/get.slack.help\/hc\/en-us\/articles\/202288908-Format-your-messages\">Slack message format<\/a>. When using this plugin it looks as follows:<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/remote-standups-at-uptech\/vim-slack-format-screenshot.png\" alt=\"Vim Slack Format Screenshots\" \/><\/p>\n<p>Without this virtual stand-up every morning we wouldn't be able to successfully run our business. It constantly triggers discussions, questions, suggestions, etc. following the stand-ups.<\/p>\n<h3 id=\"how-you-can-use-this\">How you can use this?<\/h3>\n<p>If you work for a consultancy yourself, you might find this solution and template above valuable for the same reasons we do. However, if instead you are simply on a remote team, where there aren't multiple clients you could easily modify the format to the following simpler version and get the same value out of it.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>_Tue July 11, 2017 - Standup_<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>*Yesterday*<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\u2022 One thing I did yesterday<\/span><\/span>\n<span class=\"giallo-l\"><span>\u2022 Another thing I did yesterday<\/span><\/span>\n<span class=\"giallo-l\"><span>\u2022 Yet another thing I did yesterday<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>*Today*<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\u2022 A thing I am going to do today<\/span><\/span>\n<span class=\"giallo-l\"><span>\u2022 Something else I am going to do today<\/span><\/span>\n<span class=\"giallo-l\"><span>\u2022 Another thing I am going to do today<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>*Blockers*<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\u2022 A blocker that is currently preventing me from accomplishing something or will in the near future<\/span><\/span><\/code><\/pre>\n<p>At the end of the day, like any tool\/process. It comes down to having an open minded collaborative team that understands the value of process and communication to really get the value out of a practice like this. Tools are never a solution for culture.<\/p>\n"},{"title":"RSpec Basics Screencast","published":"2017-02-04T00:00:00+00:00","updated":"2017-02-04T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/rspec-basics-screencast\/"}},"id":"https:\/\/drewdeponte.com\/blog\/rspec-basics-screencast\/","content":"<p>This is yet another screencast I dug up while cleaning out some of the digital clutter. This one provides a great basic walk through of outside in development despite using a sligtly older version of RSpec. The biggest difference in RSpec versions at this point is the way expections are written. In this screencast it references the old style:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"ruby\"><span class=\"giallo-l\"><span style=\"color: #61AFEF;\">Catalog<\/span><span>.<\/span><span style=\"color: #B57EDC;\">should_receive<\/span><span>(:<\/span><span style=\"color: #56B6C2;\">all<\/span><span>)<\/span><\/span><\/code><\/pre>\n<p>The new style avoids implementations that require monkey patching. This now looks as follows.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"ruby\"><span class=\"giallo-l\"><span style=\"color: #B57EDC;\">expect<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">Catalog<\/span><span>).<\/span><span style=\"color: #B57EDC;\">to receive<\/span><span>(:<\/span><span style=\"color: #56B6C2;\">all<\/span><span>)<\/span><\/span><\/code><\/pre>\n<p>Despite this small deviation this screencast is still extremly valuable and hopefully helps people get a grasp on outside in development and the basics of RSpec. You should be able to easily adjust for the difference by referencing the <a rel=\"external\" href=\"https:\/\/github.com\/rspec\/rspec-expectations\">rspec-expectations<\/a> documentation.<\/p>\n<video controls=\"controls\">\n  <source src=\"\/\/media.upte.ch\/tcb-0003-rspec-basics.m4v\" type=\"video\/mp4\">\n  <source src=\"\/\/media.upte.ch\/tcb-0003-rspec-basics.ogv\" type=\"video\/ogg\">\n  <source src=\"\/\/media.upte.ch\/tcb-0003-rspec-basics.mov\" type=\"video\/quicktime\">\n  Your browser does not support the <code>video<\/code> element. Please upgrade\/switch to a more modern browser that does if you want to be able to view videos.\n<\/video>\n<p><a href=\"\/\/media.upte.ch\/tcb-0003-rspec-basics.m4v\" class=\"btn btn-primary navbar-btn\"><i class=\"fa fa-download\"><\/i> .m4v<\/a>\n<a href=\"\/\/media.upte.ch\/tcb-0003-rspec-basics.ogv\" class=\"btn btn-success navbar-btn\"><i class=\"fa fa-download\"><\/i> .ogv<\/a>\n<a href=\"\/\/media.upte.ch\/tcb-0003-rspec-basics.mov\" class=\"btn btn-info navbar-btn\"><i class=\"fa fa-download\"><\/i> .mov<\/a><\/p>\n<p>In this episode we reset the state of code to the point where in episode 2 we should have dropped down and fleshed out the RSpec tests for the controller. This episode covers the very basics of RSpec while we drive out a Rails controller action as part of the feature outlined in episode 2.<\/p>\n<table>\n  <tr>\n    <th>Release Date<\/th>\n    <td>2013-05-15<\/td>\n  <\/tr>\n  <tr>\n    <th>Size<\/th>\n    <td>79.7 MB<\/td>\n  <\/tr>\n  <tr>\n    <th>Duration<\/th>\n    <td>21 mins<\/td>\n  <\/tr>\n  <tr>\n    <th>Resolution<\/th>\n    <td>1920 x 1080<\/td>\n  <\/tr>\n<\/table>\n"},{"title":"Cucumber Basics Screencast","published":"2017-01-07T00:00:00+00:00","updated":"2017-01-07T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/cucumber-basics-screencast\/"}},"id":"https:\/\/drewdeponte.com\/blog\/cucumber-basics-screencast\/","content":"<p>This is another screencast I dug up while cleaning out some of the digital clutter in my life. This one still provides a great basic walk through of how to properly define acceptance tests and use them to drive out code with Cucumber.<\/p>\n<video controls=\"controls\">\n  <source src=\"\/\/media.upte.ch\/tcb-0002-cucumber-basics.m4v\" type=\"video\/mp4\">\n  <source src=\"\/\/media.upte.ch\/tcb-0002-cucumber-basics.ogv\" type=\"video\/ogg\">\n  <source src=\"\/\/media.upte.ch\/tcb-0002-cucumber-basics.mov\" type=\"video\/quicktime\">\n  Your browser does not support the <code>video<\/code> element. Please upgrade\/switch to a more modern browser that does if you want to be able to view videos.\n<\/video>\n<p><a href=\"\/\/media.upte.ch\/tcb-0002-cucumber-basics.m4v\"><i class=\"fa fa-download\"><\/i> .m4v<\/a>\n<a href=\"\/\/media.upte.ch\/tcb-0002-cucumber-basics.ogv\"><i class=\"fa fa-download\"><\/i> .ogv<\/a>\n<a href=\"\/\/media.upte.ch\/tcb-0002-cucumber-basics.mov\"><i class=\"fa fa-download\"><\/i> .mov<\/a><\/p>\n<p>In this screencast I give a basic introduction to Cucumber \u2014 what Cucumber is and how to use it. I use a real-world example of adding the list screencasts feature to The Code Breakdown Rails app. I start out writing a Cucumber feature and explaining it, then dive into using that feature to drive out the implementation.<\/p>\n<table>\n  <tr>\n    <th>Release Date<\/th>\n    <td>2012-12-04<\/td>\n  <\/tr>\n  <tr>\n    <th>Size<\/th>\n    <td>33.6 MB<\/td>\n  <\/tr>\n  <tr>\n    <th>Duration<\/th>\n    <td>16 mins<\/td>\n  <\/tr>\n  <tr>\n    <th>Resolution<\/th>\n    <td>1920 x 1080<\/td>\n  <\/tr>\n<\/table>\n"},{"title":"Git Under the Hood Screencast","published":"2016-12-18T00:00:00+00:00","updated":"2016-12-18T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/git-under-the-hood-screencast\/"}},"id":"https:\/\/drewdeponte.com\/blog\/git-under-the-hood-screencast\/","content":"<p>Back in 2013 I did a series of screencasts with a good friend of mine, <a rel=\"external\" href=\"https:\/\/www.linkedin.com\/in\/brimil01\">Brian\nMiller<\/a>. I was going through things trying to remove some of the digital clutter and noise in my life. In the process I found this screencast and realized it is still very applicable today. Therefore, I figured I might as well share it.<\/p>\n<video controls=\"controls\">\n  <source src=\"\/\/media.upte.ch\/tcb-0010-git-part-1.m4v\" type=\"video\/mp4\">\n  <source src=\"\/\/media.upte.ch\/tcb-0010-git-part-1.ogv\" type=\"video\/ogg\">\n  <source src=\"\/\/media.upte.ch\/tcb-0010-git-part-1.mov\" type=\"video\/quicktime\">\n  Your browser does not support the <code>video<\/code> element. Please upgrade\/switch to a more modern browser that does if you want to be able to view videos.\n<\/video>\n<p><a href=\"\/\/media.upte.ch\/tcb-0010-git-part-1.m4v\" class=\"btn btn-primary navbar-btn\"><i class=\"fa fa-download\"><\/i> .m4v<\/a>\n<a href=\"\/\/media.upte.ch\/tcb-0010-git-part-1.ogv\" class=\"btn btn-success navbar-btn\"><i class=\"fa fa-download\"><\/i> .ogv<\/a>\n<a href=\"\/\/media.upte.ch\/tcb-0010-git-part-1.mov\" class=\"btn btn-info navbar-btn\"><i class=\"fa fa-download\"><\/i> .mov<\/a><\/p>\n<p>In this episode, Brian and I cover the data structure behind git, what commits are really composed of, and how we think people should try and think when using Git. The hope is that this will help get people over one of the larger hurdles in the learning curve of Git.<\/p>\n<table>\n  <tr>\n    <th>Release Date<\/th>\n    <td>2013-07-30<\/td>\n  <\/tr>\n  <tr>\n    <th>Size<\/th>\n    <td>189.9 MB<\/td>\n  <\/tr>\n  <tr>\n    <th>Duration<\/th>\n    <td>16 mins<\/td>\n  <\/tr>\n  <tr>\n    <th>Resolution<\/th>\n    <td>1920 x 1080<\/td>\n  <\/tr>\n<\/table>\n"},{"title":"Building a SegmentedViewController","published":"2016-12-16T00:00:00+00:00","updated":"2016-12-16T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/building-a-segmented-view-controller\/"}},"id":"https:\/\/drewdeponte.com\/blog\/building-a-segmented-view-controller\/","content":"<p>Let's say you run into a scenario where you need to build a mobile interface that has tabs at the top of the screen. If you are working on Android this isn't too much of a problem as it is natively supported and part of their <a rel=\"external\" href=\"https:\/\/material.io\/guidelines\/components\/tabs.html\">Material Design<\/a>. If you are building an iOS app it is a bit of a different story as the native <code>UITabBarController<\/code> doesn't really work at the top of the screen. Yes, there are some nasty things you can do to hack it. But, we really shouldn't be hacking things, should we? If we want to not run into crazy issues later and continue the hack, we should generally use the framework and systems as they were designed to be used.<\/p>\n<p>If we dig into Apple's <a rel=\"external\" href=\"https:\/\/developer.apple.com\/ios\/human-interface-guidelines\/overview\/design-principles\/\">iOS Human Interface Guidelines<\/a>\nthe closest thing I can find to tabs at the top of the screen is in the <a rel=\"external\" href=\"https:\/\/developer.apple.com\/ios\/human-interface-guidelines\/ui-bars\/navigation-bars\/\">UI Bars -&gt; Navigation Bars<\/a> section.<\/p>\n<img src=\"nav_bar_segmented_control.png\" alt=\"Nav Bar Segmented Control\" style=\"width: 300px; float: left; margin: 40px 40px 40px 0px;\" \/>\n<p>Looking through the images you will find the image to the left and an explanation suggesting that you use <code>UISegmentedControl<\/code> in the navigation bar at the top to help flatten your information hierarchy.<\/p>\n<p>So, how do we actually make something like this happen?<\/p>\n<p>It turns out that constructing an instance of a <code>UISegmentedControl<\/code>, injecting it into the <code>UiNavigationBar<\/code>, and getting callbacks when the various segments are selected is pretty straight forward.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">var<\/span><span> segmentedControl <\/span><span style=\"color: #E06C75;\">=<\/span><span style=\"color: #B57EDC;\"> UISegmentedControl<\/span><span>(<\/span><span style=\"color: #B57EDC;\">items<\/span><span>: [&quot;<\/span><span style=\"color: #98C379;\">New<\/span><span>&quot;, &quot;<\/span><span style=\"color: #98C379;\">Replied<\/span><span>&quot;])<\/span><\/span>\n<span class=\"giallo-l\"><span>segmentedControl<\/span><span style=\"color: #E06C75;\">?<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">selectedSegmentIndex<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #56B6C2;\"> 0<\/span><\/span>\n<span class=\"giallo-l\"><span>segmentedControl<\/span><span style=\"color: #E06C75;\">?<\/span><span>.<\/span><span style=\"color: #B57EDC;\">addTarget<\/span><span>(<\/span><span style=\"color: #E06C75;\">self<\/span><span>, <\/span><span style=\"color: #B57EDC;\">action<\/span><span>:<\/span><span style=\"color: #B57EDC;\"> #selector<\/span><span>(segmentSelected), <\/span><span style=\"color: #B57EDC;\">for<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">valueChanged<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span>yourViewController.<\/span><span style=\"color: #C6CCD7;\">navigationItem<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">titleView<\/span><span style=\"color: #E06C75;\"> =<\/span><span> segmentedControl<\/span><\/span><\/code><\/pre>\n<p>The above constructs a <code>UISegmentedControl<\/code> with two segements, <strong>New<\/strong>, and <strong>Replied<\/strong>. It also selects <strong>New<\/strong>. <em>Note:<\/em> This is only a visual indication it doesn't trigger any callbacks. Then we call <code>addTarget<\/code> to register an action to be called when the value of the segmented control changes. This is the spiked ground work for most of the components necessary. However, there is a lot more involved to have the <code>UISegmentedControl<\/code> trigger changing the views.<\/p>\n<p>The intended approach for this is to use a concept Apple created called a <a rel=\"external\" href=\"https:\/\/developer.apple.com\/library\/content\/featuredarticles\/ViewControllerPGforiPhoneOS\/index.html#\/\/apple_ref\/doc\/uid\/TP40007457-CH2-SW1\">Container View Controller<\/a>. These are basically responsible for owning and managing a collection of view controllers and their associated views. Prime examples of these are <code>UITabBarController<\/code> and <code>UINavigationController<\/code>. Turns out none of the existing <a rel=\"external\" href=\"https:\/\/developer.apple.com\/library\/content\/featuredarticles\/ViewControllerPGforiPhoneOS\/index.html#\/\/apple_ref\/doc\/uid\/TP40007457-CH2-SW1\">Container Controllers<\/a> do what we want. So, we will have to <a rel=\"external\" href=\"https:\/\/developer.apple.com\/library\/content\/featuredarticles\/ViewControllerPGforiPhoneOS\/ImplementingaContainerViewController.html#\/\/apple_ref\/doc\/uid\/TP40007457-CH11-SW1\">Implement a Container View Controller<\/a> of our own that takes care of owning\/managing the <code>UISegmentedControl<\/code>, the parenting view,and managing a collection of View Controllers and their paired Views that are coupled to each segment of the <code>UISegmentedControl<\/code>.<\/p>\n<h2 id=\"walk-through\">Walk Through<\/h2>\n<p>Given that we now have an understanding of the core components and concepts. Lets walk through implementing a rough initial version of our custom container view controller.<\/p>\n<p>Let's start by creating a class that inherits from <code>UIViewController<\/code> called <code>SegmentedViewController<\/code>.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">import<\/span><span style=\"color: #E5C07B;\"> UIKit<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">open<\/span><span style=\"color: #61AFEF;\"> class<\/span><span style=\"color: #E5C07B;\"> SegmentedViewController<\/span><span>:<\/span><span style=\"color: #D19A66;\"> UIViewController<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>Now we know that the <code>SegmentedViewController<\/code> needs to manage a collection of view controllers as well as a segmented control. So, let's create stored properties for both of those.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">import<\/span><span style=\"color: #E5C07B;\"> UIKit<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">open<\/span><span style=\"color: #61AFEF;\"> class<\/span><span style=\"color: #E5C07B;\"> SegmentedViewController<\/span><span>:<\/span><span style=\"color: #D19A66;\"> UIViewController<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\topen var<\/span><span> segmentViewControllers: [UIViewController]<\/span><span style=\"color: #E06C75;\"> =<\/span><span> []<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\topen var<\/span><span> segmentedControl: UISegmentedControl<\/span><span style=\"color: #E06C75;\">?<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>Now that we have properties to hold the managed values we need to initialize them. We do this by defining a constructor that takes <code>viewControllers: [UIViewController]<\/code> and assigns them to our stored property <code>self.segmentViewControllers<\/code>. <em>Note:<\/em> We also have to define the required constructor enforced by <code>UIViewController<\/code>. However, we don't want to support compilation via that mechanism so we have it fatal error out.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">import<\/span><span style=\"color: #E5C07B;\"> UIKit<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">open<\/span><span style=\"color: #61AFEF;\"> class<\/span><span style=\"color: #E5C07B;\"> SegmentedViewController<\/span><span>:<\/span><span style=\"color: #D19A66;\"> UIViewController<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\topen var<\/span><span> segmentViewControllers: [UIViewController]<\/span><span style=\"color: #E06C75;\"> =<\/span><span> []<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\topen var<\/span><span> segmentedControl: UISegmentedControl<\/span><span style=\"color: #E06C75;\">?<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\tpublic<\/span><span style=\"color: #61AFEF;\"> init<\/span><span>(<\/span><span style=\"color: #B57EDC;\">_<\/span><span style=\"color: #C6CCD7;\"> viewControllers<\/span><span>: [UIViewController]) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tsuper<\/span><span>.<\/span><span style=\"color: #61AFEF;\">init<\/span><span>(<\/span><span style=\"color: #B57EDC;\">nibName<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> nil<\/span><span>, <\/span><span style=\"color: #B57EDC;\">bundle<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> nil<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tself<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">segmentViewControllers<\/span><span style=\"color: #E06C75;\"> =<\/span><span> viewControllers<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\tpublic<\/span><span style=\"color: #61AFEF;\"> required init<\/span><span>(<\/span><span style=\"color: #B57EDC;\">coder<\/span><span style=\"color: #C6CCD7;\"> aDecoder<\/span><span>: NSCoder) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\tfatalError<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">init(coder:) has not been implemented<\/span><span>&quot;)<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>Given that we can now have an object that knows about a collection of view controllers to manage, we need to use those to set up our other stored property, the <code>UISegmentedControl<\/code>. We can do this by using the standard <code>viewDidLoad<\/code> method of the <code>UIViewController<\/code> and defining a couple more methods.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">import<\/span><span style=\"color: #E5C07B;\"> UIKit<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">open<\/span><span style=\"color: #61AFEF;\"> class<\/span><span style=\"color: #E5C07B;\"> SegmentedViewController<\/span><span>:<\/span><span style=\"color: #D19A66;\"> UIViewController<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\topen var<\/span><span> segmentViewControllers: [UIViewController]<\/span><span style=\"color: #E06C75;\"> =<\/span><span> []<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\topen var<\/span><span> segmentedControl: UISegmentedControl<\/span><span style=\"color: #E06C75;\">?<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\tpublic<\/span><span style=\"color: #61AFEF;\"> init<\/span><span>(<\/span><span style=\"color: #B57EDC;\">_<\/span><span style=\"color: #C6CCD7;\"> viewControllers<\/span><span>: [UIViewController]) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tsuper<\/span><span>.<\/span><span style=\"color: #61AFEF;\">init<\/span><span>(<\/span><span style=\"color: #B57EDC;\">nibName<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> nil<\/span><span>, <\/span><span style=\"color: #B57EDC;\">bundle<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> nil<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tself<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">segmentViewControllers<\/span><span style=\"color: #E06C75;\"> =<\/span><span> viewControllers<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\tpublic<\/span><span style=\"color: #61AFEF;\"> required init<\/span><span>(<\/span><span style=\"color: #B57EDC;\">coder<\/span><span style=\"color: #C6CCD7;\"> aDecoder<\/span><span>: NSCoder) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\tfatalError<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">init(coder:) has not been implemented<\/span><span>&quot;)<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\topen<\/span><span style=\"color: #61AFEF;\"> override func<\/span><span style=\"color: #B57EDC;\"> viewDidLoad<\/span><span>() {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tsuper<\/span><span>.<\/span><span style=\"color: #B57EDC;\">viewDidLoad<\/span><span>()<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tself<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">segmentedControl<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #B57EDC;\"> UISegmentedControl<\/span><span>(<\/span><span style=\"color: #B57EDC;\">items<\/span><span>:<\/span><span style=\"color: #B57EDC;\"> segmentTitles<\/span><span>())<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tself<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">segmentedControl<\/span><span style=\"color: #E06C75;\">?<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">selectedSegmentIndex<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #56B6C2;\"> 0<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tself<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">segmentedControl<\/span><span style=\"color: #E06C75;\">?<\/span><span>.<\/span><span style=\"color: #B57EDC;\">addTarget<\/span><span>(<\/span><span style=\"color: #E06C75;\">self<\/span><span>, <\/span><span style=\"color: #B57EDC;\">action<\/span><span>:<\/span><span style=\"color: #B57EDC;\"> #selector<\/span><span>(segmentSelected), <\/span><span style=\"color: #B57EDC;\">for<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">valueChanged<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tself<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">navigationItem<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">titleView<\/span><span style=\"color: #E06C75;\"> = self<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">segmentedControl<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\tfileprivate<\/span><span style=\"color: #61AFEF;\"> func<\/span><span style=\"color: #B57EDC;\"> segmentTitles<\/span><span>()<\/span><span style=\"color: #E06C75;\"> -&gt;<\/span><span> [<\/span><span style=\"color: #E5C07B;\">String<\/span><span>] {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tvar<\/span><span> segmentTitles: [<\/span><span style=\"color: #E5C07B;\">String<\/span><span>]<\/span><span style=\"color: #E06C75;\"> =<\/span><span> []<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tfor<\/span><span> viewController <\/span><span style=\"color: #E06C75;\">in self<\/span><span>.segmentViewControllers {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\t\t\tif let<\/span><span> title <\/span><span style=\"color: #E06C75;\">=<\/span><span> viewController.title {<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\t\t\tsegmentTitles.<\/span><span style=\"color: #B57EDC;\">append<\/span><span>(title)<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\t}<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t}<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\treturn<\/span><span> segmentTitles<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\topen<\/span><span style=\"color: #61AFEF;\"> func<\/span><span style=\"color: #B57EDC;\"> segmentSelected<\/span><span>(<\/span><span style=\"color: #B57EDC;\">_<\/span><span style=\"color: #C6CCD7;\"> segmentControl<\/span><span>: UISegmentedControl) {<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>In the above, we added in some of the basic ground work that we covered at the beginning of the article. Specifically, we constructed the <code>UISegemntedControl<\/code> and assigned it to our stored property, <code>self.segementedControl<\/code>. We marked the first segment as the active one, visually, and we added a target called <code>segmentSelected<\/code> when the value of the segmented control changes. The <code>segmentTitles<\/code> method is a simply helper method that extracts the standard <code>title<\/code> property from the injected view controllers. This allows us to easily use them when constructing the <code>UISegmentedControl<\/code>.<\/p>\n<p>Now we need to add the ability for it to display the first view controller, as we already configured the <code>self.segmentedControl<\/code> telling it that the first one is selected.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">import<\/span><span style=\"color: #E5C07B;\"> UIKit<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">open<\/span><span style=\"color: #61AFEF;\"> class<\/span><span style=\"color: #E5C07B;\"> SegmentedViewController<\/span><span>:<\/span><span style=\"color: #D19A66;\"> UIViewController<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\topen var<\/span><span> segmentViewControllers: [UIViewController]<\/span><span style=\"color: #E06C75;\"> =<\/span><span> []<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\topen var<\/span><span> segmentedControl: UISegmentedControl<\/span><span style=\"color: #E06C75;\">?<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\tpublic<\/span><span style=\"color: #61AFEF;\"> init<\/span><span>(<\/span><span style=\"color: #B57EDC;\">_<\/span><span style=\"color: #C6CCD7;\"> viewControllers<\/span><span>: [UIViewController]) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tsuper<\/span><span>.<\/span><span style=\"color: #61AFEF;\">init<\/span><span>(<\/span><span style=\"color: #B57EDC;\">nibName<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> nil<\/span><span>, <\/span><span style=\"color: #B57EDC;\">bundle<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> nil<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tself<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">segmentViewControllers<\/span><span style=\"color: #E06C75;\"> =<\/span><span> viewControllers<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\tpublic<\/span><span style=\"color: #61AFEF;\"> required init<\/span><span>(<\/span><span style=\"color: #B57EDC;\">coder<\/span><span style=\"color: #C6CCD7;\"> aDecoder<\/span><span>: NSCoder) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\tfatalError<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">init(coder:) has not been implemented<\/span><span>&quot;)<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\topen<\/span><span style=\"color: #61AFEF;\"> override func<\/span><span style=\"color: #B57EDC;\"> viewDidLoad<\/span><span>() {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tsuper<\/span><span>.<\/span><span style=\"color: #B57EDC;\">viewDidLoad<\/span><span>()<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tself<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">segmentedControl<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #B57EDC;\"> UISegmentedControl<\/span><span>(<\/span><span style=\"color: #B57EDC;\">items<\/span><span>:<\/span><span style=\"color: #B57EDC;\"> segmentTitles<\/span><span>())<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tself<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">segmentedControl<\/span><span style=\"color: #E06C75;\">?<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">selectedSegmentIndex<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #56B6C2;\"> 0<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tself<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">segmentedControl<\/span><span style=\"color: #E06C75;\">?<\/span><span>.<\/span><span style=\"color: #B57EDC;\">addTarget<\/span><span>(<\/span><span style=\"color: #E06C75;\">self<\/span><span>, <\/span><span style=\"color: #B57EDC;\">action<\/span><span>:<\/span><span style=\"color: #B57EDC;\"> #selector<\/span><span>(segmentSelected), <\/span><span style=\"color: #B57EDC;\">for<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">valueChanged<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\tdisplaySegmentViewController<\/span><span>(<\/span><span style=\"color: #56B6C2;\">0<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tself<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">navigationItem<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">titleView<\/span><span style=\"color: #E06C75;\"> = self<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">segmentedControl<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\tfileprivate<\/span><span style=\"color: #61AFEF;\"> func<\/span><span style=\"color: #B57EDC;\"> segmentTitles<\/span><span>()<\/span><span style=\"color: #E06C75;\"> -&gt;<\/span><span> [<\/span><span style=\"color: #E5C07B;\">String<\/span><span>] {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tvar<\/span><span> segmentTitles: [<\/span><span style=\"color: #E5C07B;\">String<\/span><span>]<\/span><span style=\"color: #E06C75;\"> =<\/span><span> []<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tfor<\/span><span> viewController <\/span><span style=\"color: #E06C75;\">in self<\/span><span>.segmentViewControllers {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\t\t\tif let<\/span><span> title <\/span><span style=\"color: #E06C75;\">=<\/span><span> viewController.title {<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\t\t\tsegmentTitles.<\/span><span style=\"color: #B57EDC;\">append<\/span><span>(title)<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\t}<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t}<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\treturn<\/span><span> segmentTitles<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\topen<\/span><span style=\"color: #61AFEF;\"> func<\/span><span style=\"color: #B57EDC;\"> segmentSelected<\/span><span>(<\/span><span style=\"color: #B57EDC;\">_<\/span><span style=\"color: #C6CCD7;\"> segmentControl<\/span><span>: UISegmentedControl) {<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\tfileprivate<\/span><span style=\"color: #61AFEF;\"> func<\/span><span style=\"color: #B57EDC;\"> displaySegmentViewController<\/span><span>(<\/span><span style=\"color: #B57EDC;\">_<\/span><span style=\"color: #C6CCD7;\"> segmentViewControllerIndex<\/span><span>: <\/span><span style=\"color: #E5C07B;\">Int<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tlet<\/span><span> viewController <\/span><span style=\"color: #E06C75;\">= self<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">segmentViewControllers<\/span><span>[segmentViewControllerIndex]<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tself<\/span><span>.<\/span><span style=\"color: #B57EDC;\">addChildViewController<\/span><span>(viewController)<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tself<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">view<\/span><span>.<\/span><span style=\"color: #B57EDC;\">addSubview<\/span><span>(viewController.<\/span><span style=\"color: #C6CCD7;\">view<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\t\tviewController.<\/span><span style=\"color: #C6CCD7;\">view<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">translatesAutoresizingMaskIntoConstraints<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #56B6C2;\"> false<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\tNSLayoutConstraint<\/span><span>(<\/span><span style=\"color: #B57EDC;\">item<\/span><span>: viewController.<\/span><span style=\"color: #C6CCD7;\">view<\/span><span>, <\/span><span style=\"color: #B57EDC;\">attribute<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">left<\/span><span>, <\/span><span style=\"color: #B57EDC;\">relatedBy<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">equal<\/span><span>, <\/span><span style=\"color: #B57EDC;\">toItem<\/span><span>:<\/span><span style=\"color: #E06C75;\"> self<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">view<\/span><span>, <\/span><span style=\"color: #B57EDC;\">attribute<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">left<\/span><span>, <\/span><span style=\"color: #B57EDC;\">multiplier<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> 1.0<\/span><span>, <\/span><span style=\"color: #B57EDC;\">constant<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> 0.0<\/span><span>).<\/span><span style=\"color: #C6CCD7;\">isActive<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #56B6C2;\"> true<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\tNSLayoutConstraint<\/span><span>(<\/span><span style=\"color: #B57EDC;\">item<\/span><span>: viewController.<\/span><span style=\"color: #C6CCD7;\">view<\/span><span>, <\/span><span style=\"color: #B57EDC;\">attribute<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">right<\/span><span>, <\/span><span style=\"color: #B57EDC;\">relatedBy<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">equal<\/span><span>, <\/span><span style=\"color: #B57EDC;\">toItem<\/span><span>:<\/span><span style=\"color: #E06C75;\"> self<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">view<\/span><span>, <\/span><span style=\"color: #B57EDC;\">attribute<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">right<\/span><span>, <\/span><span style=\"color: #B57EDC;\">multiplier<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> 1.0<\/span><span>, <\/span><span style=\"color: #B57EDC;\">constant<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> 0.0<\/span><span>).<\/span><span style=\"color: #C6CCD7;\">isActive<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #56B6C2;\"> true<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\tNSLayoutConstraint<\/span><span>(<\/span><span style=\"color: #B57EDC;\">item<\/span><span>: viewController.<\/span><span style=\"color: #C6CCD7;\">view<\/span><span>, <\/span><span style=\"color: #B57EDC;\">attribute<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">top<\/span><span>, <\/span><span style=\"color: #B57EDC;\">relatedBy<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">equal<\/span><span>, <\/span><span style=\"color: #B57EDC;\">toItem<\/span><span>:<\/span><span style=\"color: #E06C75;\"> self<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">view<\/span><span>, <\/span><span style=\"color: #B57EDC;\">attribute<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">top<\/span><span>, <\/span><span style=\"color: #B57EDC;\">multiplier<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> 1.0<\/span><span>, <\/span><span style=\"color: #B57EDC;\">constant<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> 0.0<\/span><span>).<\/span><span style=\"color: #C6CCD7;\">isActive<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #56B6C2;\"> true<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\tNSLayoutConstraint<\/span><span>(<\/span><span style=\"color: #B57EDC;\">item<\/span><span>: viewController.<\/span><span style=\"color: #C6CCD7;\">view<\/span><span>, <\/span><span style=\"color: #B57EDC;\">attribute<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">bottom<\/span><span>, <\/span><span style=\"color: #B57EDC;\">relatedBy<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">equal<\/span><span>, <\/span><span style=\"color: #B57EDC;\">toItem<\/span><span>:<\/span><span style=\"color: #E06C75;\"> self<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">view<\/span><span>, <\/span><span style=\"color: #B57EDC;\">attribute<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">bottom<\/span><span>, <\/span><span style=\"color: #B57EDC;\">multiplier<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> 1.0<\/span><span>, <\/span><span style=\"color: #B57EDC;\">constant<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> 0.0<\/span><span>).<\/span><span style=\"color: #C6CCD7;\">isActive<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #56B6C2;\"> true<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\t\tviewController.<\/span><span style=\"color: #B57EDC;\">didMove<\/span><span>(<\/span><span style=\"color: #B57EDC;\">toParentViewController<\/span><span>:<\/span><span style=\"color: #E06C75;\"> self<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>In order to accomplish this, we added a <code>displaySegmentViewController<\/code> method that given a <code>segmentViewControllerIndex<\/code> appropriately displays and manages the lifecycle of the child view controller and its view.<\/p>\n<p>Now we need to add support so that when a segement is selected it appropriately displays the selected segment's view and hides the previously selected segment's view.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">import<\/span><span style=\"color: #E5C07B;\"> UIKit<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">open<\/span><span style=\"color: #61AFEF;\"> class<\/span><span style=\"color: #E5C07B;\"> SegmentedViewController<\/span><span>:<\/span><span style=\"color: #D19A66;\"> UIViewController<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\topen var<\/span><span> segmentViewControllers: [UIViewController]<\/span><span style=\"color: #E06C75;\"> =<\/span><span> []<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\topen var<\/span><span> segmentedControl: UISegmentedControl<\/span><span style=\"color: #E06C75;\">?<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\tfileprivate var<\/span><span> lastDisplayedSegmentViewControllerIndex:<\/span><span style=\"color: #E5C07B;\"> Int<\/span><span style=\"color: #E06C75;\">?<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\tpublic<\/span><span style=\"color: #61AFEF;\"> init<\/span><span>(<\/span><span style=\"color: #B57EDC;\">_<\/span><span style=\"color: #C6CCD7;\"> viewControllers<\/span><span>: [UIViewController]) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tsuper<\/span><span>.<\/span><span style=\"color: #61AFEF;\">init<\/span><span>(<\/span><span style=\"color: #B57EDC;\">nibName<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> nil<\/span><span>, <\/span><span style=\"color: #B57EDC;\">bundle<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> nil<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tself<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">segmentViewControllers<\/span><span style=\"color: #E06C75;\"> =<\/span><span> viewControllers<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\tpublic<\/span><span style=\"color: #61AFEF;\"> required init<\/span><span>(<\/span><span style=\"color: #B57EDC;\">coder<\/span><span style=\"color: #C6CCD7;\"> aDecoder<\/span><span>: NSCoder) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\tfatalError<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">init(coder:) has not been implemented<\/span><span>&quot;)<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\topen<\/span><span style=\"color: #61AFEF;\"> override func<\/span><span style=\"color: #B57EDC;\"> viewDidLoad<\/span><span>() {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tsuper<\/span><span>.<\/span><span style=\"color: #B57EDC;\">viewDidLoad<\/span><span>()<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tself<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">segmentedControl<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #B57EDC;\"> UISegmentedControl<\/span><span>(<\/span><span style=\"color: #B57EDC;\">items<\/span><span>:<\/span><span style=\"color: #B57EDC;\"> segmentTitles<\/span><span>())<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tself<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">segmentedControl<\/span><span style=\"color: #E06C75;\">?<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">selectedSegmentIndex<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #56B6C2;\"> 0<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tself<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">segmentedControl<\/span><span style=\"color: #E06C75;\">?<\/span><span>.<\/span><span style=\"color: #B57EDC;\">addTarget<\/span><span>(<\/span><span style=\"color: #E06C75;\">self<\/span><span>, <\/span><span style=\"color: #B57EDC;\">action<\/span><span>:<\/span><span style=\"color: #B57EDC;\"> #selector<\/span><span>(segmentSelected), <\/span><span style=\"color: #B57EDC;\">for<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">valueChanged<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\tdisplaySegmentViewController<\/span><span>(<\/span><span style=\"color: #56B6C2;\">0<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tself<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">navigationItem<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">titleView<\/span><span style=\"color: #E06C75;\"> = self<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">segmentedControl<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\tfileprivate<\/span><span style=\"color: #61AFEF;\"> func<\/span><span style=\"color: #B57EDC;\"> segmentTitles<\/span><span>()<\/span><span style=\"color: #E06C75;\"> -&gt;<\/span><span> [<\/span><span style=\"color: #E5C07B;\">String<\/span><span>] {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tvar<\/span><span> segmentTitles: [<\/span><span style=\"color: #E5C07B;\">String<\/span><span>]<\/span><span style=\"color: #E06C75;\"> =<\/span><span> []<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tfor<\/span><span> viewController <\/span><span style=\"color: #E06C75;\">in self<\/span><span>.segmentViewControllers {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\t\t\tif let<\/span><span> title <\/span><span style=\"color: #E06C75;\">=<\/span><span> viewController.title {<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\t\t\tsegmentTitles.<\/span><span style=\"color: #B57EDC;\">append<\/span><span>(title)<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\t}<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t}<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\treturn<\/span><span> segmentTitles<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\topen<\/span><span style=\"color: #61AFEF;\"> func<\/span><span style=\"color: #B57EDC;\"> segmentSelected<\/span><span>(<\/span><span style=\"color: #B57EDC;\">_<\/span><span style=\"color: #C6CCD7;\"> segmentControl<\/span><span>: UISegmentedControl) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tvar<\/span><span> lastDisplayedIndex:<\/span><span style=\"color: #E5C07B;\"> Int<\/span><span style=\"color: #E06C75;\">? =<\/span><span style=\"color: #56B6C2;\"> nil<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tif let<\/span><span> i <\/span><span style=\"color: #E06C75;\">= self<\/span><span>.lastDisplayedSegmentViewControllerIndex {<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\tlastDisplayedIndex <\/span><span style=\"color: #E06C75;\">=<\/span><span> i<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\tdisplaySegmentViewController<\/span><span>(segmentControl.<\/span><span style=\"color: #C6CCD7;\">selectedSegmentIndex<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tif let<\/span><span> j <\/span><span style=\"color: #E06C75;\">=<\/span><span> lastDisplayedIndex {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\t\t\thideSegmentViewController<\/span><span>(j)<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t}<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\tfileprivate<\/span><span style=\"color: #61AFEF;\"> func<\/span><span style=\"color: #B57EDC;\"> displaySegmentViewController<\/span><span>(<\/span><span style=\"color: #B57EDC;\">_<\/span><span style=\"color: #C6CCD7;\"> segmentViewControllerIndex<\/span><span>: <\/span><span style=\"color: #E5C07B;\">Int<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tlet<\/span><span> viewController <\/span><span style=\"color: #E06C75;\">= self<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">segmentViewControllers<\/span><span>[segmentViewControllerIndex]<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tself<\/span><span>.<\/span><span style=\"color: #B57EDC;\">addChildViewController<\/span><span>(viewController)<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tself<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">view<\/span><span>.<\/span><span style=\"color: #B57EDC;\">addSubview<\/span><span>(viewController.<\/span><span style=\"color: #C6CCD7;\">view<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\t\tviewController.<\/span><span style=\"color: #C6CCD7;\">view<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">translatesAutoresizingMaskIntoConstraints<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #56B6C2;\"> false<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\tNSLayoutConstraint<\/span><span>(<\/span><span style=\"color: #B57EDC;\">item<\/span><span>: viewController.<\/span><span style=\"color: #C6CCD7;\">view<\/span><span>, <\/span><span style=\"color: #B57EDC;\">attribute<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">left<\/span><span>, <\/span><span style=\"color: #B57EDC;\">relatedBy<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">equal<\/span><span>, <\/span><span style=\"color: #B57EDC;\">toItem<\/span><span>:<\/span><span style=\"color: #E06C75;\"> self<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">view<\/span><span>, <\/span><span style=\"color: #B57EDC;\">attribute<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">left<\/span><span>, <\/span><span style=\"color: #B57EDC;\">multiplier<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> 1.0<\/span><span>, <\/span><span style=\"color: #B57EDC;\">constant<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> 0.0<\/span><span>).<\/span><span style=\"color: #C6CCD7;\">isActive<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #56B6C2;\"> true<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\tNSLayoutConstraint<\/span><span>(<\/span><span style=\"color: #B57EDC;\">item<\/span><span>: viewController.<\/span><span style=\"color: #C6CCD7;\">view<\/span><span>, <\/span><span style=\"color: #B57EDC;\">attribute<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">right<\/span><span>, <\/span><span style=\"color: #B57EDC;\">relatedBy<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">equal<\/span><span>, <\/span><span style=\"color: #B57EDC;\">toItem<\/span><span>:<\/span><span style=\"color: #E06C75;\"> self<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">view<\/span><span>, <\/span><span style=\"color: #B57EDC;\">attribute<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">right<\/span><span>, <\/span><span style=\"color: #B57EDC;\">multiplier<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> 1.0<\/span><span>, <\/span><span style=\"color: #B57EDC;\">constant<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> 0.0<\/span><span>).<\/span><span style=\"color: #C6CCD7;\">isActive<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #56B6C2;\"> true<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\tNSLayoutConstraint<\/span><span>(<\/span><span style=\"color: #B57EDC;\">item<\/span><span>: viewController.<\/span><span style=\"color: #C6CCD7;\">view<\/span><span>, <\/span><span style=\"color: #B57EDC;\">attribute<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">top<\/span><span>, <\/span><span style=\"color: #B57EDC;\">relatedBy<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">equal<\/span><span>, <\/span><span style=\"color: #B57EDC;\">toItem<\/span><span>:<\/span><span style=\"color: #E06C75;\"> self<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">view<\/span><span>, <\/span><span style=\"color: #B57EDC;\">attribute<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">top<\/span><span>, <\/span><span style=\"color: #B57EDC;\">multiplier<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> 1.0<\/span><span>, <\/span><span style=\"color: #B57EDC;\">constant<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> 0.0<\/span><span>).<\/span><span style=\"color: #C6CCD7;\">isActive<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #56B6C2;\"> true<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\tNSLayoutConstraint<\/span><span>(<\/span><span style=\"color: #B57EDC;\">item<\/span><span>: viewController.<\/span><span style=\"color: #C6CCD7;\">view<\/span><span>, <\/span><span style=\"color: #B57EDC;\">attribute<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">bottom<\/span><span>, <\/span><span style=\"color: #B57EDC;\">relatedBy<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">equal<\/span><span>, <\/span><span style=\"color: #B57EDC;\">toItem<\/span><span>:<\/span><span style=\"color: #E06C75;\"> self<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">view<\/span><span>, <\/span><span style=\"color: #B57EDC;\">attribute<\/span><span>: .<\/span><span style=\"color: #C6CCD7;\">bottom<\/span><span>, <\/span><span style=\"color: #B57EDC;\">multiplier<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> 1.0<\/span><span>, <\/span><span style=\"color: #B57EDC;\">constant<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> 0.0<\/span><span>).<\/span><span style=\"color: #C6CCD7;\">isActive<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #56B6C2;\"> true<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\t\tviewController.<\/span><span style=\"color: #B57EDC;\">didMove<\/span><span>(<\/span><span style=\"color: #B57EDC;\">toParentViewController<\/span><span>:<\/span><span style=\"color: #E06C75;\"> self<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tself<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">lastDisplayedSegmentViewControllerIndex<\/span><span style=\"color: #E06C75;\"> =<\/span><span> segmentedControl<\/span><span style=\"color: #E06C75;\">?<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">selectedSegmentIndex<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\tfileprivate<\/span><span style=\"color: #61AFEF;\"> func<\/span><span style=\"color: #B57EDC;\"> hideSegmentViewController<\/span><span>(<\/span><span style=\"color: #B57EDC;\">_<\/span><span style=\"color: #C6CCD7;\"> segmentViewControllerIndex<\/span><span>: <\/span><span style=\"color: #E5C07B;\">Int<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tlet<\/span><span> viewController <\/span><span style=\"color: #E06C75;\">= self<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">segmentViewControllers<\/span><span>[segmentViewControllerIndex]<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\t\tviewController.<\/span><span style=\"color: #B57EDC;\">willMove<\/span><span>(<\/span><span style=\"color: #B57EDC;\">toParentViewController<\/span><span>:<\/span><span style=\"color: #56B6C2;\"> nil<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\tviewController.<\/span><span style=\"color: #C6CCD7;\">view<\/span><span>.<\/span><span style=\"color: #B57EDC;\">removeFromSuperview<\/span><span>()<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\tviewController.<\/span><span style=\"color: #B57EDC;\">removeFromParentViewController<\/span><span>()<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre>\n<p>To do this we added a fileprivate stored property called <code>lastDisplayedSegmentViewControllerIndex<\/code> to track the previously selected segment. This was useful when we fleshed out the <code>segmentSelected<\/code> method. We also updated the <code>displaySegmentViewController<\/code> method to update <code>lastDisplayedSegmentViewControllerIndex<\/code> with a new index after it has displayed the segment's view. The details on the required lifecycle management of the child view controllers that we implemented in the <code>displaySegementViewController<\/code> and <code>hideSegmentViewConroller<\/code> were provided in <a rel=\"external\" href=\"https:\/\/developer.apple.com\/library\/content\/featuredarticles\/ViewControllerPGforiPhoneOS\/ImplementingaContainerViewController.html#\/\/apple_ref\/doc\/uid\/TP40007457-CH11-SW1\">Implement a Container View Controller<\/a>.<\/p>\n<p>This completes our initial implementation of our <code>SegmentedViewController<\/code>.<\/p>\n<h2 id=\"usage\">Usage<\/h2>\n<p>Let's look at how we would use this thing now. The following is a simple <code>AppDelegate<\/code> that would use the <code>SegemntedViewController<\/code>.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"swift\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">import<\/span><span style=\"color: #E5C07B;\"> UIKit<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>@<\/span><span style=\"color: #61AFEF;\">UIApplicationMain<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">class<\/span><span style=\"color: #E5C07B;\"> AppDelegate<\/span><span>:<\/span><span style=\"color: #D19A66;\"> UIResponder<\/span><span>, <\/span><span style=\"color: #D19A66;\">UIApplicationDelegate<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\tvar<\/span><span> window: UIWindow<\/span><span style=\"color: #E06C75;\">? =<\/span><span style=\"color: #B57EDC;\"> UIWindow<\/span><span>(<\/span><span style=\"color: #B57EDC;\">frame<\/span><span>: UIScreen.<\/span><span style=\"color: #C6CCD7;\">main<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">bounds<\/span><span>)<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">\tfunc<\/span><span style=\"color: #B57EDC;\"> application<\/span><span>(<\/span><span style=\"color: #B57EDC;\">_<\/span><span style=\"color: #C6CCD7;\"> application<\/span><span>: UIApplication, <\/span><span style=\"color: #B57EDC;\">didFinishLaunchingWithOptions<\/span><span style=\"color: #C6CCD7;\"> launchOptions<\/span><span>: [UIApplicationLaunchOptionsKey:<\/span><span style=\"color: #E5C07B;\"> Any<\/span><span>]<\/span><span style=\"color: #E06C75;\">?<\/span><span>)<\/span><span style=\"color: #E06C75;\"> -&gt;<\/span><span style=\"color: #E5C07B;\"> Bool<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tlet<\/span><span> vcOrange <\/span><span style=\"color: #E06C75;\">=<\/span><span style=\"color: #B57EDC;\"> UIViewController<\/span><span>()<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\tvcOrange.<\/span><span style=\"color: #C6CCD7;\">title<\/span><span style=\"color: #E06C75;\"> =<\/span><span> &quot;<\/span><span style=\"color: #98C379;\">Orange<\/span><span>&quot;<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\tvcOrange.<\/span><span style=\"color: #C6CCD7;\">view<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">backgroundColor<\/span><span style=\"color: #E06C75;\"> =<\/span><span> .<\/span><span style=\"color: #C6CCD7;\">orange<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tlet<\/span><span> vcYellow <\/span><span style=\"color: #E06C75;\">=<\/span><span style=\"color: #B57EDC;\"> UIViewController<\/span><span>()<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\tvcYellow.<\/span><span style=\"color: #C6CCD7;\">title<\/span><span style=\"color: #E06C75;\"> =<\/span><span> &quot;<\/span><span style=\"color: #98C379;\">Yellow<\/span><span>&quot;<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\tvcYellow.<\/span><span style=\"color: #C6CCD7;\">view<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">backgroundColor<\/span><span style=\"color: #E06C75;\"> =<\/span><span> .<\/span><span style=\"color: #C6CCD7;\">yellow<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\tlet<\/span><span> segmentedViewController <\/span><span style=\"color: #E06C75;\">=<\/span><span style=\"color: #B57EDC;\"> SegmentedViewController<\/span><span>([vcOrange, vcYellow])<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span>\t\twindow<\/span><span style=\"color: #E06C75;\">!<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">rootViewController<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #B57EDC;\"> UINavigationController<\/span><span>(<\/span><span style=\"color: #B57EDC;\">rootViewController<\/span><span>: segmentedViewController)<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\twindow<\/span><span style=\"color: #E06C75;\">!<\/span><span>.<\/span><span style=\"color: #B57EDC;\">makeKeyAndVisible<\/span><span>()<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\treturn<\/span><span style=\"color: #56B6C2;\"> true<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><span>}<\/span><\/span><\/code><\/pre><img src=\"demo_segmented_view_controller.png\" alt=\"Demo Segmented View Controller\" style=\"display: block; margin-left: auto; margin-right: auto; width: 300px;\" \/>\n<h2 id=\"that-s-it\">That's It<\/h2>\n<p>All in all it isn't too bad. I hope you found this valuable and if you have any questions don't hesitate to contact me. This is just a little more complex than I would want to have to deal with in an app code base. So, I will probably break this out into a framework soon.<\/p>\n"},{"title":"The UpDesk UpWrite","published":"2013-12-11T00:00:00+00:00","updated":"2013-12-11T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/updesk-upwrite-initial-setup\/"}},"id":"https:\/\/drewdeponte.com\/blog\/updesk-upwrite-initial-setup\/","content":"<h2 id=\"backstory\">Backstory<\/h2>\n<p>Over the last few years I have been paying more and more attention to\nergonamics in my workspace. It all began when I started having issues with\nmy wrists and given I am a software developer I need my hands to work\nproperly. This sent me down a path of exploring ergonomic keyboards and\nstanding desks. I started trying out the cheaper ergonomic keyboards and\nnever had any luck. Enter the Kinesis Advantage Pro, I finally decided to drop\na chunk of change on it and I really haven't looked back. My next exploration\nwill be the ErgoDox which I recently ordered all the parts for.<\/p>\n<p>A little bit before I switched to the Kinesis Advantage Pro I switched to a\nfixed height standing desk which I bought from Ikea for approx. $150. I have\nbeen using the fixed height standing desk for over a year now and I love it.\nHowever, the more research I do around the topic the more I have found that it\nisn't good to stand all day, which is what I currently do. Therefore, I\nstarted looking into motorized standing\/sitting desks. After a lot of\nresearch I finally decided on the <a rel=\"external\" href=\"http:\/\/myupdesk.com\/upwrite\">UpDesk\nUpWrite<\/a>.  It is at the lower end of the price\nrange, $1,149.00, for motorized standing\/sitting desks but it has the specs of\na lot of the more expensive alternatives. It also has the added benefit that\nit is a dry erase\/wet erase surface so you can scribble notes on it while\nworking.<\/p>\n<h2 id=\"the-setup\">The Setup<\/h2>\n<h3 id=\"putting-it-together\">Putting it Together<\/h3>\n<p>My <a rel=\"external\" href=\"http:\/\/myupdesk.com\/upwrite\">UpDesk UpWrite<\/a> arrived tonight and it was\nactually very straightforward in terms of physical setup. The included\ninstruction sheet explained things very well. It is actually much simpler in\nmy opinion than most Ikea furniture. It probably took me 25 mins to get it\nput together. I was extremely impressed with the quality of packaging and the\nquality of the parts them selves. It seems to be an extremely solid piece of\nfurniture that will last a long time.<\/p>\n<h3 id=\"initializing-and-calibrating\">Initializing and Calibrating<\/h3>\n<p>Once I physically setup the desk I tried to initialize it and callibrate it.\nInitializing the desk was very straightforward on their instruction sheet and\nworked like a champ. Calibrating it on the other hand did not work following\nthe instructions they provide. The instructions for calibration that were\nprovided are as follows:<\/p>\n<ul>\n<li>At any height, measure the distance from the floor to the top of the desk.<\/li>\n<li>Press both the \"UP\" and \"DOWN\" arrows at the same time until the display\nshows three bars (---).<\/li>\n<li>Release and immediately set the correct height by pressing either the \"UP\"\nor \"DOWN\" arrow until the correct height is displayed.<\/li>\n<\/ul>\n<p>I tried the above steps numerous times and every time I got to the third step\nand pressed the \"UP\" or \"DOWN\" arrows to adjust the displayed height it would\nalso move the desk up or down which made no sense if the goal of the process\nwas to callibrate the height.<\/p>\n<p>So, it took a little playing around but I finally figured out how to properly\ncallibrate it. The directions to calibrate it should really be as follows:<\/p>\n<ul>\n<li>At any height, measure the distance from the floor to the top of the desk.<\/li>\n<li>Press and hold both the \"UP\" and \"DOWN\" arrows at the same time, the display\nwill immediately display three bars (---), continue to hold the \"UP\" and\n\"DOWN\" arrows until the display flashes from (---) to the displayed height.<\/li>\n<li>Release and immediately set the display to height you previously measured in\nthe first step by pressing either the \"UP\" or \"DOWN\" arrow until the correct\nheight is displayed.<\/li>\n<\/ul>\n<h3 id=\"dialing-in-presets\">Dialing in Presets<\/h3>\n<p>Once I had it all calibrated I followed the rest of the instructions to set\nthe presets to heights that I like for standing and sitting. All worked great.<\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>So far the <a rel=\"external\" href=\"https:\/\/myupdesk.com\/upwrite\">UpDesk UpWrite<\/a> seems like a great\ndesk. It was a little frustrating that the directions weren't clear enough, or\nrather just plain wrong, for how to calibrate it. However, I am willing to\nlook past that small hickup and I hope this blog aids others in getting their\ndesks calibrated quicker than I did.<\/p>\n"},{"title":"Shrinking Vagrant Linux Boxes","published":"2013-10-29T00:00:00+00:00","updated":"2013-10-29T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/shrinking-vagrant-linux-boxes\/"}},"id":"https:\/\/drewdeponte.com\/blog\/shrinking-vagrant-linux-boxes\/","content":"<p>So, lately I have been using Virtual Machines via\n<a rel=\"external\" href=\"http:\/\/www.vagrantup.com\/\">Vagrant<\/a> a siginificant amount as we are now using\nthem to host our development environments and dependencies for all the\ndevelopment work we do. We have been using <a rel=\"external\" href=\"http:\/\/puppetlabs.com\/\">Puppet<\/a> to\nautomate the configuration of our environments and to make building our boxes\neasier.<\/p>\n<p>Once you have provisioned a VM and you have it all dialed, you will most\nlikely want to create a <a rel=\"external\" href=\"http:\/\/docs.vagrantup.com\/v2\/boxes.html\">Vagrant Box<\/a>\nfrom that VM and host it up on S3 so that new developers can get started\nsimply by installing <a rel=\"external\" href=\"http:\/\/www.vagrantup.com\/\">Vagrant<\/a>, cloning your\nrepository and running:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>$ vagrant up<\/span><\/span><\/code><\/pre>\n<p>This actually works great. However, I found that the VM disks both for the\n<a rel=\"external\" href=\"http:\/\/docs.vagrantup.com\/v2\/vmware\/index.html\">Vagrant VMware Provider<\/a> and\nthe <a rel=\"external\" href=\"http:\/\/docs.vagrantup.com\/v2\/virtualbox\/index.html\">VirtualBox Provider<\/a>\ngrow dynamically, but they never shrink. So, when I was going through the\nprocess of building the VM and installing all the dependency software, etc. it\nwas bloating the base machine.  This was the case even though I removed the\ntemporary cruft from the filesystem on the guest.<\/p>\n<p>With a little research and some trial and error I found that you can shrink\nthese disks as long as they are ext3 or ext4 file systems with the help of the\nfollowing command:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>$ vagrant ssh<\/span><\/span>\n<span class=\"giallo-l\"><span>$ sudo dd if=\/dev\/zero of=wipefile bs=1024x1024; rm wipefile<\/span><\/span><\/code><\/pre>\n<p>What the above command is doing is simply writing zero bytes to the wipefile in\nchunks of 1024 bytes until there is no disk space left in your VM's disk. Then\nit is removing the wipefile. This basically leaves all those excess bytes\nzero'd out.<\/p>\n<p>This is necessary because the shrink\/compaction tools provided by either\n<a rel=\"external\" href=\"http:\/\/www.vmware.com\/\">VMWare<\/a> or <a rel=\"external\" href=\"https:\/\/www.virtualbox.org\/\">VirtualBox<\/a>\nboth have no way of identifying space they can free up in the disks unless\nthey are zero'd out.<\/p>\n<p>Before shrinking\/compacting you should always make sure to exit the guest\nmachine using the following commands:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>$ exit<\/span><\/span>\n<span class=\"giallo-l\"><span>$ vagrant halt<\/span><\/span><\/code><\/pre>\n<p>With <a rel=\"external\" href=\"http:\/\/www.vmware.com\/\">VMware<\/a> you can shrink the vmdk disk by doing\nthe following:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>$ vmware-vdiskmanager -d \/path\/to\/main.vmdk<\/span><\/span>\n<span class=\"giallo-l\"><span>$ vmware-vdiskmanager -k \/path\/to\/main.vmdk<\/span><\/span><\/code><\/pre>\n<p>I discovered the above commands in the Vagrant VMware documentation,\n<a rel=\"external\" href=\"http:\/\/docs.vagrantup.com\/v2\/vmware\/boxes.html\">http:\/\/docs.vagrantup.com\/v2\/vmware\/boxes.html<\/a><\/p>\n<p>With <a rel=\"external\" href=\"https:\/\/www.virtualbox.org\">VirtualBox<\/a> the only way I was able to\nshrink the disk image was to clone it to a smaller copy using the following\ncommand:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>$ VboxManage clonehd name-of-original-vm.vdi name-of-clone-vm.vdi<\/span><\/span><\/code><\/pre>\n<p>Once you have cloned the vdi you can then import it into the VM through\nVirtualBox and get rid of the original vdi.<\/p>\n<p>That is how you \"Shrink Vagrant Linux Boxes!\"<\/p>\n"},{"title":"AT&T Samsung Galaxy S3 with Google Voice","published":"2013-01-06T00:00:00+00:00","updated":"2013-01-06T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/att-samsung-galaxy-s3-with-google-voice\/"}},"id":"https:\/\/drewdeponte.com\/blog\/att-samsung-galaxy-s3-with-google-voice\/","content":"<p>Hi All,<\/p>\n<p>It seems there is a little hickup with the instructions google provides you\nwith to \"Activate Google voicemail no this phone\". That is at least for the\ninstructions for AT&amp;T while trying to get Google Voice setup on an AT&amp;T Galaxy\nS3. The instructions Google provides say to enter the following exactly as\nwritten as if dialing someone.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>*004*1&lt;your 10 digit phone number&gt;#<\/span><\/span><\/code><\/pre>\n<p>Every time I tried the above instructions my phone would return an error code.\nI got so frustrated I even got online with AT&amp;T tech support so that they\ncould verify the state of things as I was trying various other commands that I\nfound on the web. None, of the ones that worked allowed me to setup Google\nVoice as my voicemail provider appropriately. After being online with AT&amp;T\ntech support for a while. I tried simply prepending another * character to the\nentire thing. Guess what, magically it worked. I even had AT&amp;T tech support\nverify that it made the proper configuration change on my account. So the\ncorrect command that Google should be telling people to enter exactly as\nwritten is really the following:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>**004*1&lt;your 10 digit phone number&gt;#<\/span><\/span><\/code><\/pre>\n<p>To disable voicemail forwarding I used the following MMI code.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>##004#<\/span><\/span><\/code><\/pre>\n<p>Anyways, hope this blog post helps some other people struggling with this out\nthere. I know when I was searching I couldn't find any solution though I did\nsee a number of message boards with people looking for an answer to this\nproblem. If I were a better person I would probably go back and update all\nthose message boards with my solution. I just can't bring myself to do it.<\/p>\n"},{"title":"RVM Installed Ruby (SSL certificate verify failed)","published":"2012-09-07T00:00:00+00:00","updated":"2012-09-07T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/rvm-installed-ruby-ssl-certificate-verify-failed\/"}},"id":"https:\/\/drewdeponte.com\/blog\/rvm-installed-ruby-ssl-certificate-verify-failed\/","content":"<p>Recently a few of our developers were having problems getting SSL certificate verify\nfailures in Ruby. The error was the following:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed<\/span><\/span>\n<span class=\"giallo-l\"><span>\tfrom \/Users\/adeponte\/.rvm\/rubies\/ruby-1.9.3-p125\/lib\/ruby\/1.9.1\/net\/http.rb:799:in `connect&#39;<\/span><\/span>\n<span class=\"giallo-l\"><span>\tfrom \/Users\/adeponte\/.rvm\/rubies\/ruby-1.9.3-p125\/lib\/ruby\/1.9.1\/net\/http.rb:799:in `block in connect&#39;<\/span><\/span>\n<span class=\"giallo-l\"><span>\tfrom \/Users\/adeponte\/.rvm\/rubies\/ruby-1.9.3-p125\/lib\/ruby\/1.9.1\/timeout.rb:54:in `timeout&#39;<\/span><\/span>\n<span class=\"giallo-l\"><span>\tfrom \/Users\/adeponte\/.rvm\/rubies\/ruby-1.9.3-p125\/lib\/ruby\/1.9.1\/timeout.rb:99:in `timeout&#39;<\/span><\/span>\n<span class=\"giallo-l\"><span>\tfrom \/Users\/adeponte\/.rvm\/rubies\/ruby-1.9.3-p125\/lib\/ruby\/1.9.1\/net\/http.rb:799:in `connect&#39;<\/span><\/span>\n<span class=\"giallo-l\"><span>\tfrom \/Users\/adeponte\/.rvm\/rubies\/ruby-1.9.3-p125\/lib\/ruby\/1.9.1\/net\/http.rb:755:in `do_start&#39;<\/span><\/span>\n<span class=\"giallo-l\"><span>\tfrom \/Users\/adeponte\/.rvm\/rubies\/ruby-1.9.3-p125\/lib\/ruby\/1.9.1\/net\/http.rb:744:in `start&#39;<\/span><\/span>\n<span class=\"giallo-l\"><span>\tfrom \/Users\/adeponte\/.rvm\/rubies\/ruby-1.9.3-p125\/lib\/ruby\/1.9.1\/open-uri.rb:306:in `open_http&#39;<\/span><\/span>\n<span class=\"giallo-l\"><span>\tfrom \/Users\/adeponte\/.rvm\/rubies\/ruby-1.9.3-p125\/lib\/ruby\/1.9.1\/open-uri.rb:775:in `buffer_open&#39;<\/span><\/span>\n<span class=\"giallo-l\"><span>\tfrom \/Users\/adeponte\/.rvm\/rubies\/ruby-1.9.3-p125\/lib\/ruby\/1.9.1\/open-uri.rb:203:in `block in open_loop&#39;<\/span><\/span>\n<span class=\"giallo-l\"><span>\tfrom \/Users\/adeponte\/.rvm\/rubies\/ruby-1.9.3-p125\/lib\/ruby\/1.9.1\/open-uri.rb:201:in `catch&#39;<\/span><\/span>\n<span class=\"giallo-l\"><span>\tfrom \/Users\/adeponte\/.rvm\/rubies\/ruby-1.9.3-p125\/lib\/ruby\/1.9.1\/open-uri.rb:201:in `open_loop&#39;<\/span><\/span>\n<span class=\"giallo-l\"><span>\tfrom \/Users\/adeponte\/.rvm\/rubies\/ruby-1.9.3-p125\/lib\/ruby\/1.9.1\/open-uri.rb:146:in `open_uri&#39;<\/span><\/span>\n<span class=\"giallo-l\"><span>\tfrom \/Users\/adeponte\/.rvm\/rubies\/ruby-1.9.3-p125\/lib\/ruby\/1.9.1\/open-uri.rb:677:in `open&#39;<\/span><\/span>\n<span class=\"giallo-l\"><span>\tfrom \/Users\/adeponte\/.rvm\/rubies\/ruby-1.9.3-p125\/lib\/ruby\/1.9.1\/open-uri.rb:33:in `open&#39;<\/span><\/span><\/code><\/pre>\n<p>The error was reproducable by running the following Ruby snippet in an <strong>irb<\/strong>\ninstance of the RVM installed Ruby version.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"ruby\"><span class=\"giallo-l\"><span style=\"color: #E06C75;\">require<\/span><span> &#39;<\/span><span style=\"color: #98C379;\">open-uri<\/span><span>&#39;<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">open<\/span><span> &#39;<\/span><span style=\"color: #98C379;\">https:\/\/google.com<\/span><span>&#39;<\/span><\/span><\/code><\/pre>\n<p>I spent a good amount of time digging into things and trying to figure out\nexactly what the issue was. I came to the conclusion that they either had an\nout of date Root CA Certificate Bundle or SSL couldn't find it for some reason.\nSo, I went down the path of trying to import cacert.pem from\n<a rel=\"external\" href=\"http:\/\/curl.haxx.se\/ca\/cacert.pem\">http:\/\/curl.haxx.se\/ca\/cacert.pem<\/a> (cURL's\nwebsite).<\/p>\n<p>I went down this path because I vaguely remember doing this at some point in\nthe past to solve the Twitter API SSL Verify fail issue when they changed their\ncerts.  I ran into a huge amount of difficulty trying to get the thing imported\nor converted into a format that Mac OS X Keychain Access would be happy with\nimporting. I tried pem, crt, pck12, etc. Nothing I did seemed to make a\ndifference.<\/p>\n<p>This is when I saw a post of someone talking about SSL being installed inside\nof RVM and I looked at the latest output from <code>rvm requirements<\/code>.  I\nquickly noticed that it now says to install SSL via the following command:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>rvm pkg install openssl<\/span><\/span><\/code><\/pre>\n<p>Come to find out if you have run the above command before installing your Ruby\nversion it will be using the RVM packaged openssl instead of the system\nopenssl. That also means that the Root CA Certificate Bundle is different, or\nin this case, <strong>missing completely<\/strong>.<\/p>\n<p>After discovering this and doing a little googling I found the solution to be\nas simple as the following command:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>curl http:\/\/curl.haxx.se\/ca\/cacert.pem -o ~\/.rvm\/usr\/ssl\/cert.pem<\/span><\/span><\/code><\/pre>\n<p>I found this solution on the following blog post\n<a rel=\"external\" href=\"http:\/\/fredwu.me\/post\/28834446907\/fix-openssl-error-on-mountain-lion-and-rvm\">http:\/\/fredwu.me\/post\/28834446907\/fix-openssl-error-on-mountain-lion-and-rvm<\/a>.\nHopefully, writing another blog post on the subject and linking to this one\nwill help it gain its ranks in google search so that other people can find it\nquicker.<\/p>\n<p>After running the above command you should now be able to restart your <strong>irb<\/strong>\ninstance and re-run the test snippet described at the beginning of this post.\nThis time you should get output looking similar to the following:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"ruby\"><span class=\"giallo-l\"><span> =&gt;<\/span><span style=\"color: #5F6672;font-style: italic;\"> #&lt;File:\/var\/folders\/j_\/9nxyjmd10js8g1sjx446vrsm0000gn\/T\/open-uri20120907-36935-4i4y64&gt; <\/span><\/span><\/code><\/pre>\n<p>Just like that your SSL woes are gone.<\/p>\n"},{"title":"Sublime Text 2 Guard Plugin Updates","published":"2012-08-07T00:00:00+00:00","updated":"2012-08-07T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/sublime-text-2-guard-plugin-updates-008\/"}},"id":"https:\/\/drewdeponte.com\/blog\/sublime-text-2-guard-plugin-updates-008\/","content":"<p>I woke up early this morning and thought it was time for a little\n<a rel=\"external\" href=\"http:\/\/github.com\/drewdeponte\/sublime_guard\">sublime_guard<\/a> love. I went through\nand tackled all of the syntax highlighting issues. This included the following\nissues (<a rel=\"external\" href=\"https:\/\/github.com\/drewdeponte\/sublime_guard\/issues\/27\">#27<\/a>,\n<a rel=\"external\" href=\"https:\/\/github.com\/drewdeponte\/sublime_guard\/issues\/30\">#30<\/a>,\n<a rel=\"external\" href=\"https:\/\/github.com\/drewdeponte\/sublime_guard\/issues\/31\">#31<\/a>,\n<a rel=\"external\" href=\"https:\/\/github.com\/drewdeponte\/sublime_guard\/issues\/38\">#38<\/a>,\n<a rel=\"external\" href=\"https:\/\/github.com\/drewdeponte\/sublime_guard\/issues\/43\">#43<\/a>,\n<a rel=\"external\" href=\"https:\/\/github.com\/drewdeponte\/sublime_guard\/issues\/29\">#29<\/a>).<\/p>\n<p>I also cleaned up a number of tickets that were lying around and had either\nbeen completed already or were tickets that were invalid.<\/p>\n<p>I also started a branch for adding python3 support to the plugin. The branch is\nnamed <strong>python3_support<\/strong>. I would love it if anyone out there in the ether\nwould grab that branch, test it out, and give me some feedback.<\/p>\n<p>Anyways, I hope you guys enjoy the updates. As usual if you installed from\nPackage Control just restart Sublime Text 2. If you installed manually then\njust pull the latest version down with Git.<\/p>\n<p>See you all next time.<\/p>\n"},{"title":"Ruby and Rails Mountain Lion Hiccups","published":"2012-07-27T00:00:00+00:00","updated":"2012-07-27T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/ruby-and-rails-mountain-lion-hiccups\/"}},"id":"https:\/\/drewdeponte.com\/blog\/ruby-and-rails-mountain-lion-hiccups\/","content":"<p>So when Mac OS X Mountain Lion was released a few days ago I jumped on\nupgrading and got switched over as soon as I could. My mentality for upgrading\nis that I would much rather deal with the pain of upgrading in smaller chunks\nwhen needed rather than waiting. Plus, I just like to be on the latest and\ngreatest in terms of the tools I use for my job.<\/p>\n<p>Anyways, below is going to be a listing of some of the hiccups that either I or\nother developers on my team have run into in terms of their Ruby and Rails\ndevelopment environment setups.<\/p>\n<h2 id=\"git\">Git<\/h2>\n<p>Some of the devs on my team had Git installed via the\n<a rel=\"external\" href=\"http:\/\/code.google.com\/p\/git-osx-installer\/\">git-osx-installer<\/a>. These\ndevelopers seemed to lose their Git installs when they upgraded. I was using a\n<a rel=\"external\" href=\"http:\/\/mxcl.github.com\/homebrew\/\">Homebrew<\/a> install of Git and it worked just\nfine for me after upgrading. So be aware of what your situation is and resort\nto brew because it is awesome.<\/p>\n<h2 id=\"xcode-4-4\">XCode 4.4<\/h2>\n<p>In order to build gems with the native exensions and just to have the basic\ndevelopment tools around I installed the latest XCode 4.4. Once installed I\nwent to the preferences menu, in the download tab, and chose to install the\n<em>Command Line Tools<\/em> because that is really what I was looking for more than\nanything. I have read on other posts online that you can download the <em>Command\nLine Tools<\/em> alone and install just them. I personally chose to install XCode as\nI do use XCode and the native tools to work on iPhone\/iPad app development as\nwell.<\/p>\n<h2 id=\"gcc-4-2\">gcc-4.2<\/h2>\n<p>The latest XCode now comes with clang\/llvm which is a new C\/C++\/Objective-C\ncompiler. The problem is that the Ruby language isn't fully compatible with\nclang and llvm. Therefore, you need to install GCC 4.2 using Homebrew. GCC 4.2\nis the latest version of the GCC compiler that apple provided before switching\nto clang\/llvm. Anyways, you can install GCC 4.2 by doing the following with\nbrew:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>brew tap homebrew\/dupes<\/span><\/span>\n<span class=\"giallo-l\"><span>brew install apple-gcc42<\/span><\/span><\/code><\/pre>\n<p>The above will install the apple gcc-4.2 in a side by side fashion with XCode.\nThis means that it won't mess up your existing XCode install like other\nsolutions.<\/p>\n<p>Once you install gcc-4.2 then you can install\/reinstall your ruby version\ninside of RVM as needed.<\/p>\n<h2 id=\"apache-2-web-sharing\">Apache 2 (Web Sharing)<\/h2>\n<p>In the upgrade it seems that they have revamped the Sharing preference pane. In\nthe process of this it seems that Apple has decided to remove the Web sharing\ncomponent of this preference pane. Don't worry though. Apache is still there\nand running. You just can't start it and stop it from the preferences pane\nanymore. You can use the following commands instead.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>sudo apachectl stop<\/span><\/span>\n<span class=\"giallo-l\"><span>sudo apachectl start<\/span><\/span>\n<span class=\"giallo-l\"><span>sudo apachectl restart<\/span><\/span><\/code><\/pre>\n<p><strong>Note:<\/strong> When the upgrade happened it nuked all my <code>\/etc\/apache2\/users\/<\/code>\nconfigs and all my other apache configs. So, if you have any custom apache\nconfigs as I did you should probably back them up before upgrading.<\/p>\n<h2 id=\"x11\">X11<\/h2>\n<p>It seems that another thing that was changed with the upgrade to Mac OS X\nMountain Lion was that they removed the X11 that I had previously installed and\nseemed to no longer provide it. Thats ok though because XQuartz 2.7.2 is what\nyou want and it is available freely at\n<a rel=\"external\" href=\"http:\/\/xquartz.macosforge.org\/landing\/\">http:\/\/xquartz.macosforge.org\/landing\/<\/a>.\nJust install that bad boy and follow the on screen instructions to reboot when\nit is done and you should be good to go in terms of X11.<\/p>\n<h2 id=\"imagemagick\">ImageMagick<\/h2>\n<p>Before upgrading I had ImageMagick installed via Homebrew. After, the upgrade was finished my ImageMagick was exiting with failures about some dylib. To solve this problem I simply ran the following and I was back in running order with respect to ImageMagick:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>brew upgrade imagemagick<\/span><\/span><\/code><\/pre><h2 id=\"pygments\">Pygments<\/h2>\n<p>I use Pygments to handle syntax highlighting in my jekyll sites and it seemed to not be working after the upgrade. I believe this is because it is now using Python 2.7.2. To resolve this issue I just re-installed Pygments using the following:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>sudo easy_install Pygments<\/span><\/span><\/code><\/pre>"},{"title":"Sublime Text 2 Guard Plugin Updates","published":"2012-07-25T00:00:00+00:00","updated":"2012-07-25T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/sublime-text-2-guard-plugin-updates-007\/"}},"id":"https:\/\/drewdeponte.com\/blog\/sublime-text-2-guard-plugin-updates-007\/","content":"<p>It has been a while since I have provided an update on my <a rel=\"external\" href=\"http:\/\/github.com\/drewdeponte\/sublime_guard\">Sublime Text 2 Guard\nPlugin<\/a> and I just made some changes\nso I figured I would make a quick post. The project has recently had a number\nof contributions which I will list in the changes below.<\/p>\n<ul>\n<li>Fixed default Linux key-mappings\n(<a rel=\"external\" href=\"http:\/\/github.com\/drewdeponte\/sublime_guard\/issues\/52\">#52<\/a>)<\/li>\n<li>Fixed default Windows key-mappings\n(<a rel=\"external\" href=\"http:\/\/github.com\/drewdeponte\/sublime_guard\/issues\/52\">#52<\/a>)<\/li>\n<li>Added Run all Tests &amp; Show Guard Output, Thanks\n<a rel=\"external\" href=\"http:\/\/github.com\/whitequark\">@whitequark<\/a>\n(<a rel=\"external\" href=\"http:\/\/github.com\/drewdeponte\/sublime_guard\/issues\/44\">#44<\/a>,\n<a rel=\"external\" href=\"http:\/\/github.com\/drewdeponte\/sublime_guard\/issues\/53\">#53<\/a>)<\/li>\n<li>Made it work without Bundler, Thanks\n<a rel=\"external\" href=\"http:\/\/github.com\/brandonblack\">@brandonblack<\/a>\n(<a rel=\"external\" href=\"http:\/\/github.com\/drewdeponte\/sublime_guard\/issues\/51\">#51<\/a>,\n<a rel=\"external\" href=\"http:\/\/github.com\/drewdeponte\/sublime_guard\/issues\/50\">#50<\/a>)<\/li>\n<li>Clear output when predefined text found, Thanks\n<a rel=\"external\" href=\"http:\/\/github.com\/diogomafra\">@diogomafra<\/a>\n(<a rel=\"external\" href=\"http:\/\/github.com\/drewdeponte\/sublime_guard\/issues\/48\">#48<\/a>)<\/li>\n<li>Added support for <em>rbenv<\/em>, Thanks\n<a rel=\"external\" href=\"http:\/\/github.com\/chrislerum\">@chrislerum<\/a>\n(<a rel=\"external\" href=\"http:\/\/github.com\/drewdeponte\/sublime_guard\/issues\/45\">#45<\/a>)<\/li>\n<\/ul>\n<p>I have been completely stoked with the growing usage of this plugin as well as\nthe abundance of contributions people have been making to the plugin. In this\nupdate there are a number of people that need to be called out with special thanks.<\/p>\n<p><a rel=\"external\" href=\"http:\/\/github.com\/diogomafra\">@diogomafra<\/a> provided a <em>Clear output when\npredefined text found<\/em> feature. I am sure many users will find this feature a\nnice addition to smoothing out their workflow. Thank you for your contribution\n<a rel=\"external\" href=\"http:\/\/github.com\/diogomafra\">@diogomafra<\/a>.<\/p>\n<p><a rel=\"external\" href=\"http:\/\/github.com\/chrislerum\">@chrislerum<\/a> provided <strong>rbenv support<\/strong>. I know\nthat this feature will be huge for a number of the users as this was a highly\nrequestd feature.  Thank you so much for your contribution\n<a rel=\"external\" href=\"http:\/\/github.com\/chrislerum\">@chrislerum<\/a>.<\/p>\n<p><a rel=\"external\" href=\"http:\/\/github.com\/brandonblack\">@brandonblack<\/a> provided the ability to\nfunction without bundler. This will allow users who aren't using bundler to be\nable to use this plugin.  I think this is a great addition as the more people\nwe can get using this plugin the more feedback we can get. Hence, the better it\nwill become. Thanks for your contribution\n<a rel=\"external\" href=\"http:\/\/github.com\/brandonblack\">@brandonblack<\/a>.<\/p>\n<p><a rel=\"external\" href=\"http:\/\/github.com\/whitequark\">@whitequark<\/a> provided the basis for the <strong>Run\nall Tests &amp; Show Guard Output<\/strong> feature. I know a number of developers who have\nbeen waiting for something like this to fit their personal workflow. Thank you\nvery much for your contribution <a rel=\"external\" href=\"http:\/\/github.com\/whitequark\">@whitequark<\/a>.<\/p>\n<p>A big thanks goes out to all the above listed contributors and all of our\nprior contributors. Thank you all for helping make this a solid very useful\nplugin for the development community. As usual please make issues for any bugs\nor feature requsets you have at <a rel=\"external\" href=\"http:\/\/github.com\/drewdeponte\/sublime_guard\">Sublime Text 2 Guard\nPlugin<\/a>.<\/p>\n"},{"title":"A New Rails Tagging Plugin","published":"2012-07-22T00:00:00+00:00","updated":"2012-07-22T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/a-new-rails-tagging-plugin\/"}},"id":"https:\/\/drewdeponte.com\/blog\/a-new-rails-tagging-plugin\/","content":"<p>A little over 3 months ago I had the need for a good tagging plugin for Rails.\nI tried out a number of the popular ones but they just weren't up to my\nstandards in terms of their APIs and their functionality. To accomplish the\nuser experience goals that had been set forth for the project I would have had\nto hack the existing Rails tagging plugins to pieces.<\/p>\n<p>So after some discussion about the Rails plugins with my team. We decided that\nI should just start my own Rails tagging plugin. Anyways, I started developing\n<a rel=\"external\" href=\"http:\/\/github.com\/uptech\/tagalong\">Tagalong<\/a>, my Rails tagging plugin,\nimmediately. The library is now 3 months old and I figured I should let people\nknow that it exists and provide a breakdown of its features.<\/p>\n<p><a rel=\"external\" href=\"http:\/\/github.com\/uptech\/tagalong\">Tagalong<\/a> has two major concepts that a\ndeveloper needs to understand before being able to use it. The first concept is\na <em>Tagger<\/em>. A <em>Tagger<\/em> is simply an object that can perform the act of tagging\nother objects (<em>Taggables<\/em>). As I am sure you have guessed the second concept\nis a <em>Taggable<\/em>. A <em>Taggable<\/em> is simply an object in the system that can be\ntagged.<\/p>\n<p>The <a rel=\"external\" href=\"http:\/\/github.com\/uptech\/tagalong\">Tagalong<\/a> Rails plugin is built\nentirely around these two concepts a <em>Tagger<\/em> and a <em>Taggable<\/em>. This\narchitecture decision is one of the major differences between this Rails\ntagging plugin and the other tagging plugins out there. A brief breakdown of\nthe plugin is as follows.<\/p>\n<ul>\n<li>clean easy to understand API<\/li>\n<li>does NOT require saving of the model when tagging\/untagging<\/li>\n<li>keeps history of tags <em>Taggers<\/em> have used<\/li>\n<li>allows defining multiple <em>Taggers<\/em> and <em>Taggables<\/em><\/li>\n<li>tracks number of times tags are used<\/li>\n<li>returns tag lists in alphabetical order<\/li>\n<\/ul>\n<p>The above are simply a few brief points. For details on all its features and\nhow to use it please refer to the GitHub project\n<a rel=\"external\" href=\"http:\/\/github.com\/uptech\/tagalong\">http:\/\/github.com\/uptech\/tagalong<\/a>.<\/p>\n"},{"title":"OctopusCI now has job rerun","published":"2012-07-22T00:00:00+00:00","updated":"2012-07-22T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/octopusci-now-has-job-rerun\/"}},"id":"https:\/\/drewdeponte.com\/blog\/octopusci-now-has-job-rerun\/","content":"<p>I recently released v0.3.12 of my CI server\n<a rel=\"external\" href=\"https:\/\/github.com\/drewdeponte\/octopusci\">OctopusCI<\/a>.  This release provided a\nfeature which people have been asking for a while now.  I am glad to tell you\nall that it is finally here.\n<a rel=\"external\" href=\"https:\/\/github.com\/drewdeponte\/octopusci\">OctopusCI<\/a> now has the capability of\nre-running jobs simply by clicking a link.<\/p>\n"},{"title":"Sublime Text 2 Guard Plugin Updates","published":"2012-04-15T00:00:00+00:00","updated":"2012-04-15T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/sublime-text-2-guard-plugin-updates-006\/"}},"id":"https:\/\/drewdeponte.com\/blog\/sublime-text-2-guard-plugin-updates-006\/","content":"<p>Well, I am back with some more updates on my <a rel=\"external\" href=\"http:\/\/github.com\/drewdeponte\/sublime_guard\">Sublime Text 2 Guard\nPlugin<\/a>.  This is a smaller release\nthan the last as I have been very busy. Despite its small stature it is an\nexciting one as we have another contribution to honor. The following are the\nrecent updates:<\/p>\n<ul>\n<li>Fixed comma, single quote, question mark color issues\n(<a rel=\"external\" href=\"https:\/\/github.com\/drewdeponte\/sublime_guard\/issues\/25\">#25<\/a>)<\/li>\n<li>Fixed small color issue caused by <code>!<\/code> char\n(<a rel=\"external\" href=\"http:\/\/github.com\/drewdeponte\/sublime_guard\/issues\/26\">#26<\/a>)<\/li>\n<li>Better console colors, change <code>0 failures<\/code> from red to green (Thanks\n<a rel=\"external\" href=\"http:\/\/github.com\/lidanh\">@lidanh<\/a>)<\/li>\n<\/ul>\n<p>A special thanks goes out to <a rel=\"external\" href=\"http:\/\/github.com\/lidanh\">@lidanh<\/a> for the code\ncontribution. The new colors are much nicer and the <code>0 failures<\/code> being green\ninstead of red makes it easier to detect success vs failure.<\/p>\n<p>Thanks also goes out to all the users of the <a rel=\"external\" href=\"http:\/\/github.com\/drewdeponte\/sublime_guard\">Sublime Text 2 Guard\nPlugin<\/a>.  Especially, the users that\nhave been submitting bug reports and feature requests. This plugin wouldn't\nexist without the direction and feedback from you all.<\/p>\n<p>Please continue to provide feedback via Sublime Text 2 Guard Plugin\n<a rel=\"external\" href=\"http:\/\/github.com\/drewdeponte\/sublime_guard\/issues\">issues<\/a> on our GitHub page.<\/p>\n"},{"title":"My zsh Setup","published":"2012-04-13T00:00:00+00:00","updated":"2012-04-13T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/my-zsh-setup\/"}},"id":"https:\/\/drewdeponte.com\/blog\/my-zsh-setup\/","content":"<p>Relatively recently I switched to ZSH for my shell. The primary reason I\nswitched was that I had more flexibility in prompts as I can setup a right\nprompt and a left prompt. I also like the completion interfacing that ZSH\nprovides. Anyways, I finally got around to generalizing my ZSH setup and\nmaking it available on GitHub as a backup for myself so that I can easily set\nit up again in the future, as well as a means of sharing my setup with all of\nyou. It is available on GitHub at <a rel=\"external\" href=\"http:\/\/github.com\/drewdeponte\/dotzsh\">http:\/\/github.com\/drewdeponte\/dotzsh<\/a>\nwith instructions on how to install it using git.<\/p>\n"},{"title":"My VIM Setup","published":"2012-04-11T00:00:00+00:00","updated":"2012-04-11T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/my-vim-setup\/"}},"id":"https:\/\/drewdeponte.com\/blog\/my-vim-setup\/","content":"<p>So, as some of you may know I pulled my old .vimrc out of hiding and started\nplaying around with vim again to see if I would potentially want to switch back\nto it from Sublime Text 2. The reason being that I still fealt I was missing\nsomething that I had in vim (the quick keyboard navigation and general\nflexibility). Anyways, I pulled my .vimrc out and added the basics of what I\nthink I am going to need for it to be an acceptable replacement for Sublime\nText 2. This includes fuzzy file search, tab completion, etc. Basically all the\nthings necessary for it to be a competitor to Sublime Text 2 in my mind. It\nisn't perferct yet but it is a start.<\/p>\n<p>Note: The only reason I am doing this is because I already knew VIM so I don't\nhave a learning curve to really explore this. In my eyes Sublime Text 2 and VIM\nare pretty damn close in terms of functionality but Sublime Text 2 provides a\nmuch smaller learning curve for the majority of people.<\/p>\n<p>This does NOT mean that I am giving up on Sublime Text 2 or that I am going to\nstop supporting my Sublime Text 2 plugins. If it comes to that at some point in\nthe future I will find a new mantainer for them and let you all know.<\/p>\n<p>Anyways, I have made my vim setup available on github via\n<a rel=\"external\" href=\"http:\/\/github.com\/cyphactor\/dotvim\">http:\/\/github.com\/cyphactor\/dotvim<\/a>. The\nREADME for the repository provides instructions on how to install using git.<\/p>\n"},{"title":"Sublime Text 2 Guard Plugin Updates","published":"2012-02-28T00:00:00+00:00","updated":"2012-02-28T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/sublime-text-2-guard-plugin-updates-005\/"}},"id":"https:\/\/drewdeponte.com\/blog\/sublime-text-2-guard-plugin-updates-005\/","content":"<p>Well, I got some more updates for you all.<\/p>\n<p>My Sublime Text 2 Guard Plugin has recently been recognized as an essential\nSublime Text 2 plugin on a nettuts+ blog post. Thanks to this publicity and the\nexposure via Package Control, Twitter, GitHub, and Google I have gotten some\ndecent feedback, especially around my recent addition of colored output. Thanks\nto all that feedback I have pushed out the following improvements to the\nSublime Text 2 Guard Plugin:<\/p>\n<ul>\n<li>Support for directory paths with spaces (<a rel=\"external\" href=\"http:\/\/github.com\/cyphactor\/sublime_guard\/pull\/19\">#19<\/a>, Thanks <a rel=\"external\" href=\"http:\/\/github.com\/ffmike\">@ffmike<\/a>)<\/li>\n<li>Fixed dash based coloring issue (<a rel=\"external\" href=\"http:\/\/github.com\/cyphactor\/sublime_guard\/issues\/16\">#16<\/a>)<\/li>\n<li>Fixed single quote based coloring issue (<a rel=\"external\" href=\"http:\/\/github.com\/cyphactor\/sublime_guard\/issues\/17\">#17<\/a>)<\/li>\n<li>Fixed question mark based coloring issue (<a rel=\"external\" href=\"http:\/\/github.com\/cyphactor\/sublime_guard\/issues\/20\">#20<\/a>)<\/li>\n<li>Fixed period based coloring issue (<a rel=\"external\" href=\"http:\/\/github.com\/cyphactor\/sublime_guard\/issues\/21\">#21<\/a>)<\/li>\n<li>Fixed success dot section identification &amp; coloring issue (<a rel=\"external\" href=\"http:\/\/github.com\/cyphactor\/sublime_guard\/issues\/22\">#22<\/a>)<\/li>\n<li>Fixed a second single quote based coloring issue (<a rel=\"external\" href=\"http:\/\/github.com\/cyphactor\/sublime_guard\/issues\/23\">#23<\/a>)<\/li>\n<li>Added command relativity (<a rel=\"external\" href=\"http:\/\/github.com\/cyphactor\/sublime_guard\/issues\/24\">#24<\/a>)<\/li>\n<\/ul>\n<p>The above break down of improvements include primarily a bunch of coloring\nfixes, command relativity, and one very important issue that deserves some\nrecognition, issue <a rel=\"external\" href=\"http:\/\/github.com\/cyphactor\/sublime_guard\/pull\/19\">#19<\/a> by\n<a rel=\"external\" href=\"http:\/\/github.com\/ffmike\">@ffmike<\/a>. This is the Sublime Text 2 Guard Plugin's\nfirst contribution. Much thanks goes out to <a rel=\"external\" href=\"http:\/\/github.com\/ffmike\">@ffmike<\/a>\nfor being our first contributor.<\/p>\n<p>As usual if any of you have identified bugs or features that you would like\nadded please submit them on the GitHub Issues page for the <a rel=\"external\" href=\"http:\/\/github.com\/cyphactor\/sublime_guard\/issues?sort=created&amp;direction=desc&amp;state=open\">Sublime Text 2\nGuard\nPlugin<\/a>.<\/p>\n"},{"title":"Sublime Text 2 Guard Plugin in Color","published":"2012-02-22T00:00:00+00:00","updated":"2012-02-22T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/sublime-text-2-guard-plugin-updates-004\/"}},"id":"https:\/\/drewdeponte.com\/blog\/sublime-text-2-guard-plugin-updates-004\/","content":"<p>Hey all,<\/p>\n<p>I know there are a number of you who have been eagerly awaiting this moment. I\nsure have been. I am proud to announce the first release of the <a rel=\"external\" href=\"http:\/\/github.com\/cyphactor\/sublime_guard\">Sublime Text 2\nGuard Plugin<\/a> that provides colored\noutput. Basically, it ended up that I had to define a language file to identify\nthe entities in the output and then define a theme to color those identified\nentities. Once, I did that I simply programatically set the color_scheme and\nsyntax options for the output panel and like magic colors worked. Given the\ncomplexity of the language definition and the limited test cases I had when\ndeveloping I am sure there are going to be some bugs to work out. So, as usual\nplease create issues if you find any of these\n<a rel=\"external\" href=\"http:\/\/github.com\/cyphactor\/sublime_guard\/issues\">issues<\/a>.<\/p>\n"},{"title":"Sublime Text 2 Guard Plugin Updates","published":"2012-02-09T00:00:00+00:00","updated":"2012-02-09T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/sublime-text-2-guard-plugin-updates-003\/"}},"id":"https:\/\/drewdeponte.com\/blog\/sublime-text-2-guard-plugin-updates-003\/","content":"<p>Hey all,<\/p>\n<p>My Sublime Text 2 Guard plugin has been out for a couple weeks now and it has\ndefinitely gotten some more usage under its belt. Thanks to everyone for your\nusage and feedback. As a result of the feedback and user generated issues I\nhave released a new version to tighten it up a bit both from a user experience\nstand point as well as a stability standpoint. This release includes the\nfollowing:<\/p>\n<ul>\n<li>Now installable via <a rel=\"external\" href=\"http:\/\/wbond.net\/sublime_packages\/package_control\">Package Control<\/a><\/li>\n<li>Fixed Guard won't start when installled via Package Control (<a rel=\"external\" href=\"http:\/\/github.com\/cyphactor\/sublime_guard\/issues\/11\">issue #11<\/a>)<\/li>\n<li>Added auto show output feature (<a rel=\"external\" href=\"http:\/\/github.com\/cyphactor\/sublime_guard\/issues\/12\">issue #12<\/a>)<\/li>\n<li>Replaced <code>#!\/bin\/sh<\/code> with <code>#!\/bin\/bash<\/code> in internal scripts<\/li>\n<\/ul>\n<p>For info on how to get the latest version or how to report bugs, feature\nrequsts, etc. Please check out the GitHub page for the <a rel=\"external\" href=\"http:\/\/github.com\/cyphactor\/sublime_guard\">sublime_guard<\/a> project.<\/p>\n"},{"title":"Sublime Text 2 Guard Plugin Updates","published":"2012-02-01T00:00:00+00:00","updated":"2012-02-01T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/sublime-text-2-guard-plugin-updates-002\/"}},"id":"https:\/\/drewdeponte.com\/blog\/sublime-text-2-guard-plugin-updates-002\/","content":"<p>Hey all,<\/p>\n<p>I was having some difficulties with a couple bugs in the Sublime Text 2 Guard\nplugin I have been working on. So, I decided I would drop Jon Skinner,\ndeveloper of Sublime Text 2, an e-mail and see if he knew about the issues I\nwas having and hopefully of solutions\/work arounds for them. Jon, was very\nhelpful in terms of better understanding the issues as well as suggesting some\nwork arounds.<\/p>\n<p>Anyways, given the input from Jon I took another look at the bugs\/features of\nthe Guard plugin and decided to tackle them tonight. The following items show\nthe progress that I made tonight with respect to the Sublime Text 2 Guard\nplugin:<\/p>\n<ul>\n<li>Enabled native Sublime Text 2 word wrap in the Guard output view.<\/li>\n<li>Guard now exits properly when Sublime Text 2 exits\/dies.<\/li>\n<li>Added default key bindings to show Guard output view (super+shift+c)<\/li>\n<li>Submitted a pull request to get the Guard plugin added to the <a rel=\"external\" href=\"http:\/\/wbond.net\/sublime_packages\/package_control\">Package\nControl<\/a> repository list.<\/li>\n<\/ul>\n<p>As usual I am looking for feedback issues (feature requests, bug fixes, etc.)\nYou can find the project as\n<a rel=\"external\" href=\"http:\/\/github.com\/cyphactor\/sublime_guard\">sublime_guard<\/a> on GitHub. As of\ntonight I am down to two feature requests. So, please create bugs in the issues\nif you run into any so that I can work on fixing them sooner rather than later.<\/p>\n"},{"title":"Sublime Text 2 Guard Plugin Updates","published":"2012-01-27T00:00:00+00:00","updated":"2012-01-27T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/sublime-text-2-guard-plugin-updates-001\/"}},"id":"https:\/\/drewdeponte.com\/blog\/sublime-text-2-guard-plugin-updates-001\/","content":"<p>Just dropping a quick note to all that follow my blog. Last night I made a\nnumeber updates to the <a rel=\"external\" href=\"http:\/\/github.com\/cyphactor\/sublime_guard\">Guard\nPlugin<\/a> for <a rel=\"external\" href=\"http:\/\/www.sublimetext.com\/2\">Sublime Text\n2<\/a>. Most of these were in direct response to\nfeedback that I got from users after making the initial release on my blog, I\nthink, two days ago. The modifications I made are enumarted below:<\/p>\n<ul>\n<li>The biggest addition is <code>RVM detection and integration<\/code>. The plugin is now\nintelligent enough to detect if you have RVM installed and detect if you are\nusing a Project specific .rvmrc and of course respond by calling Guard\nappropriately depending on the case.<\/li>\n<li>Another relatively small fix is a <code>vertical only auto-scroll<\/code>. Before, if Guard\noutput went to the right past the width of the pane it was auto-scrolling to\nthe right and then bouncing back to the left at the next line. Now, it always\nscrolls down the left most column of the output basically making it\nauto-scroll vertically.<\/li>\n<li>Third I added some output preprocessing that <code>strips out the Terminal Color Codes<\/code> so that the output is a bit cleaner.<\/li>\n<li>I also added a lot of <code>descriptive output and error handling and output<\/code> to let\nthe user better know what is failing so they might be able to debug the\nsituation a bit better.<\/li>\n<\/ul>\n<p>As always if you have any feedback (feature requests, bug reports, etc.) you\ncan simply create an issue for the project at the projects <a rel=\"external\" href=\"http:\/\/github.com\/cyphactor\/sublime_guard\/issues\">GitHub Issues\nPage<\/a>.<\/p>\n<p>If you are new to the <a rel=\"external\" href=\"http:\/\/github.com\/cyphactor\/sublime_guard\">Guard Plugin<\/a>\nfor <a rel=\"external\" href=\"http:\/\/www.sublimetext.com\/2\">Sublime Text 2<\/a> you can find installation\ninstructions and document on the <a rel=\"external\" href=\"http:\/\/github.com\/cyphactor\/sublime_guard\">GitHub\nPage<\/a>.<\/p>\n<p>For instructions on upgrading please checkout the <a rel=\"external\" href=\"http:\/\/github.com\/cyphactor\/sublime_guard\">GitHub\nPage<\/a>.<\/p>\n"},{"title":"Sublime Text 2 Guard Plugin Debut","published":"2012-01-25T00:00:00+00:00","updated":"2012-01-25T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/sublime-text-2-guard-plugin-debut\/"}},"id":"https:\/\/drewdeponte.com\/blog\/sublime-text-2-guard-plugin-debut\/","content":"<p>Hi all. I know it has been a while. Please forgive me. My life has been a\nlittle crazy. I am still in the process of buliding my house. I also recently\nbecame engaged so I am trying to deal with wedding planning. Beyond that I am\nstill trying to get all my development work done for RealPractice, my side\ncompany, and all of my open source projects.<\/p>\n<p>Anyways, I needed a break from serious projects and decided it was time for a\nsmall fun project that would potentially help me develop the more serious\nprojects more efficiently. A while back I started using a new text editor\ncalled <a rel=\"external\" href=\"http:\/\/www.sublimetext.com\/2\">Sublime Text 2<\/a>. Basically it is what\nTextMate 2 promised (and failed) to be and more.<\/p>\n<p>So, since I have been using this for a while and I have been developing Ruby\nand Rails apps lately using BDD and TDD I decided it was time to remove the\nannoyance of having to switch to the terminal after each little development\niteration to check Guard's output to see why my tests are failing. Wow that was\na run on sentence. Oh, well it is 2 am and I just finished the plugin so bare\nwith me. Anyways. Therefore, I decided it was the perfect opportunity to build\na <a rel=\"external\" href=\"http:\/\/www.sublimetext.com\/2\">Sublime Text 2<\/a> plugin that integrates Guard\ninto it.<\/p>\n<p>It is available with its documentation on GitHub at\n<a rel=\"external\" href=\"http:\/\/github.com\/cyphactor\/sublime_guard\">http:\/\/github.com\/cyphactor\/sublime_guard<\/a>.\nPlease don't hesitate to go grab it, use it, extend it, contribute back, report\nbugs, or throw me new feature requests.<\/p>\n"},{"title":"Archaic - Archived Chats and Dev Helper","published":"2010-12-13T00:00:00+00:00","updated":"2010-12-13T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/archaic-archived-chats-and-dev-helper\/"}},"id":"https:\/\/drewdeponte.com\/blog\/archaic-archived-chats-and-dev-helper\/","content":"<p>So for those of that don't know, I lead of team of software developers. For a\nlong time now I have noticed a number of gaps in the industry with respect to\nsoftware development and team collaboration. One area that I always felt was\nmissing some love was group chat.<\/p>\n<p>Sure, there are a large number of different chat protocols and applications\nthat implement them all. However, they generally miss an archived history that\nhas full text searching. Beyond the group chat is prime location to distribute\npush notifications when Git repositories that the team is working with have\nchanges pushed to them. Because of these two issues and number of other that I\nreally don't want to get into at this point I have started a new open source\nproject called <a rel=\"external\" href=\"http:\/\/github.com\/cyphactor\/archaic\">\"Archaic\"<\/a>.<\/p>\n<p><a rel=\"external\" href=\"http:\/\/github.com\/cyphactor\/archaic\">Archaic<\/a> is a Rails application that\nprovides archiving of IRC channel chats with a full text search interface as\nwell as general developer helpers. It is composed of two pieces, an IRC Bot and\nthe Rails application. Technically they both share the rails environment as the\nIRC Bot should be run with rails runner. You can checkout the project at the\nfollowing:\n<a rel=\"external\" href=\"http:\/\/github.com\/cyphactor\/archaic\">http:\/\/github.com\/cyphactor\/archaic<\/a>.<\/p>\n<p>I plan to keep the master branch functional so feel free to use the master\nbranch. It currently has the following features.<\/p>\n<ul>\n<li>IRC Chat Archiving<\/li>\n<li>Full Text Chat Archive Searching with Context (to expand context simply click\non resulting match)<\/li>\n<li>Week number helper (when anyone in IRC channel types \"weeknum\" it replies\nwith current week number)<\/li>\n<li>Ticket number helper (when anyone in IRC channel types \"#\\d+\" it replies with\nlink to that ticket)<\/li>\n<\/ul>\n<p>This is the initial introduction to this project and I am sure it has numerous\ndirections that it can and will grow into. I very much welcome your thoughts,\nsuggestions, feature requests, bug reports, testing, etc.<\/p>\n<p>I believe that having a good central place of communication for a team even if\nthey are working in the same office helps at least as a mechanism for keeping\nyour devs notified about repo pushes close to real time.<\/p>\n<p>Anyways, enjoy!<\/p>\n"},{"title":"Rails friendly_id Plugin","published":"2010-08-26T00:00:00+00:00","updated":"2010-08-26T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/rails-friendly-id-plugin\/"}},"id":"https:\/\/drewdeponte.com\/blog\/rails-friendly-id-plugin\/","content":"<p>Hey all. I recently discovered the friendly_id plugin for Rails at a Orange\nCountry Ruby Group meetup. It basically provides a bunch of awesome\nfunctionality with respect to permalinking and slugging. Anyways, I very much\nrecommend that you all check it out and play around with it. You can get to the\nprojects GitHub page <a rel=\"external\" href=\"http:\/\/github.com\/norman\/friendly_id\">here<\/a> and its\ndocumentation <a rel=\"external\" href=\"http:\/\/norman.github.com\/friendly_id\/\">here<\/a>.<\/p>\n"},{"title":"Ruby Class vs. Instance Diagram","published":"2010-08-13T00:00:00+00:00","updated":"2010-08-13T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/ruby-class-vs-instance-diagram\/"}},"id":"https:\/\/drewdeponte.com\/blog\/ruby-class-vs-instance-diagram\/","content":"<p>One of the many things that I have changed on my new team is the language. They\nwere originally all PHP developers and my boss and I who have been using Ruby\nand Rails for years now decided to develop the new products in Ruby and Rails\nwhere fitting. Anyways, as a part of the process I have been educating (and\nbeing educated) by my engineers on Ruby and Rails as they go through the\nlearning process. One of the topics that seems to have some confusion around it\nis Class vs. Instance variables with respect to classes and objects. Because\nthis confusion I spent some time with one of the engineers to put together a\nquick diagram to try and help clarify what is actually happening with respect\nto Class vs. Instance variables and methods. You can find the diagram\n<a href=\"https:\/\/drewdeponte.com\/blog\/ruby-class-vs-instance-diagram\/ruby_class_vs_instance.pdf\">here<\/a>.<\/p>\n"},{"title":"Git, finding abandoned commits...","published":"2010-07-30T00:00:00+00:00","updated":"2010-07-30T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/git-finding-abandoned-commits\/"}},"id":"https:\/\/drewdeponte.com\/blog\/git-finding-abandoned-commits\/","content":"<p>So, as many of you may know I recently took a position heading up a new\nsoftware engineering team that is responsible for producing the new products.\nOne of my first actions when I came on board was switching my team over from\nSVN to Git. One of the engineers on the team abandoned a branch in one of our\nGit repositories because he didn't think he would need it anymore. Soon, after\nhe realized that he wanted something from that branch but now he couldn't see\nthe brach anymore in 'gitk --all' or any of the other tools because he\nabandoned it. But, it has to still be there right, it is Git.<\/p>\n<p>I knew there was a way to do this in Git and I have done it in the past but I\ncouldn't remember how. After I explained a bit more to him about the internals\nof Git and he went off and did some exploration he came back to me with his\nfound solution which follows:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"plain\"><span class=\"giallo-l\"><span>git fsck --unreachable| cut -f3 -d&#39; &#39;|xargs git rev-list --header|less<\/span><\/span><\/code><\/pre>\n<p>I know there is some other way to find abandoned nodes in Git but I still can't\nremember. Anyways, just figured I would share this method in the hopes that it\nwould help someone out there. It may not be exactly what you want with respect\nto the 'cut' or 'xargs' but I will let you all play with it a bit to get\nexactly what you want.<\/p>\n"},{"title":"jQuery Comparative Histogram Plugin","published":"2009-10-31T00:00:00+00:00","updated":"2009-10-31T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/jquery-comparative-histogram-plugin\/"}},"id":"https:\/\/drewdeponte.com\/blog\/jquery-comparative-histogram-plugin\/","content":"<p>I recently wrote a jQuery Plugin that I am calling jquery_comphist. It is a\njQuery Plugin that draws a Comparative Histogram of a provided population\ndivided into two major groups with any number of shared sub-groups. One of the\nmost common uses for such a feature is when you are trying to display\ndemographics data on some population. Anyways, as usual the code is available\nhere (<a href=\"https:\/\/drewdeponte.com\/blog\/jquery-comparative-histogram-plugin\/jquery.comphist.js\">jquery.comphist.js<\/a>,\n<a href=\"https:\/\/drewdeponte.com\/blog\/jquery-comparative-histogram-plugin\/jquery.comphist.css\">jquery.comphist.css<\/a>) and you can see an example of it\nbelow.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/jquery-comparative-histogram-plugin\/jquery-comparative-histogram.png\" alt=\"jQuery Comparative Histogram\" \/><\/p>\n<p>With the jQuery Comparative Histogram (CompHist) Plugin you can easily\nrepresent populations and information about them. A prime usage exampe of this\nis demographics. All the styling is done via CSS. Hence the style is easily\nmodified. It does the calculations for you and it allows for non listed group\ncounts via a <code>non_demo_count<\/code> data option.<\/p>\n<p>The above demo was produced by defining a few code snippets. The first snippet\nis a snippet of HTML that includes the jQuery Comparative Histogram javascript\nsource file and CSS style sheet. Note: The jQuery Comparative Histogram\njavascript source file needs to come after the standard jQuery javascript\nsource file. An example of this snippet is as follows:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"html\"><span class=\"giallo-l\"><span>&lt;<\/span><span style=\"color: #E5C07B;\">script<\/span><span style=\"color: #D19A66;\"> type<\/span><span>=&quot;<\/span><span style=\"color: #98C379;\">text\/javascript<\/span><span>&quot;<\/span><span style=\"color: #D19A66;\"> src<\/span><span>=&quot;<\/span><span style=\"color: #98C379;\">..\/jquery.comphist.js<\/span><span>&quot;&gt;&lt;\/<\/span><span style=\"color: #E5C07B;\">script<\/span><span>&gt;<\/span><\/span>\n<span class=\"giallo-l\"><span>&lt;<\/span><span style=\"color: #E5C07B;\">link<\/span><span style=\"color: #D19A66;\"> rel<\/span><span>=&quot;<\/span><span style=\"color: #98C379;\">stylesheet<\/span><span>&quot;<\/span><span style=\"color: #D19A66;\"> type<\/span><span>=&quot;<\/span><span style=\"color: #98C379;\">text\/css<\/span><span>&quot;<\/span><span style=\"color: #D19A66;\"> href<\/span><span>=&quot;<\/span><span style=\"color: #98C379;\">..\/stylesheets\/jquery.comphist.css<\/span><span>&quot;&gt;<\/span><\/span><\/code><\/pre>\n<p>Note: In the above snippet the href values are likely to be different in your\ncase. The second code snippet is simply a single line of HTML where the div\nthat is to be the Comparative Histogram. It is key that this div has an id that\nis easily referenced and a set height and width. An example of this snippet is\nas follows:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"html\"><span class=\"giallo-l\"><span>&lt;<\/span><span style=\"color: #E5C07B;\">div<\/span><span style=\"color: #D19A66;\"> id<\/span><span>=&quot;<\/span><span style=\"color: #98C379;\">compplaceholder<\/span><span>&quot;<\/span><span style=\"color: #D19A66;\"> style<\/span><span>=&quot;<\/span><span style=\"color: #98C379;\">width: 375px; height: 200px;<\/span><span>&quot;&gt;&lt;\/<\/span><span style=\"color: #E5C07B;\">div<\/span><span>&gt;<\/span><\/span><\/code><\/pre>\n<p>The third code snippet needed is the javascript that defines the data, creates\nthe Comparative Histogram in the specified div, and handles the hover\nfunctionality. An example of this is seen below.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"html\"><span class=\"giallo-l\"><span>&lt;<\/span><span style=\"color: #E5C07B;\">script<\/span><span style=\"color: #D19A66;\"> type<\/span><span>=&quot;<\/span><span style=\"color: #98C379;\">text\/javascript<\/span><span>&quot;&gt;<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">$<\/span><span>(<\/span><span style=\"color: #61AFEF;\">function<\/span><span> () {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">\tfunction<\/span><span style=\"color: #B57EDC;\"> showCompHistTooltip<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">x<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> y<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> contents<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\t\tconsole<\/span><span>.<\/span><span style=\"color: #B57EDC;\">log<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">called my shit<\/span><span>&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\t$<\/span><span>(&#39;<\/span><span style=\"color: #98C379;\">&lt;div id=&quot;comphist1tooltip&quot;&gt;<\/span><span>&#39;<\/span><span style=\"color: #E06C75;\"> +<\/span><span style=\"color: #C6CCD7;\"> contents<\/span><span style=\"color: #E06C75;\"> +<\/span><span> &#39;<\/span><span style=\"color: #98C379;\">&lt;\/div&gt;<\/span><span>&#39;).<\/span><span style=\"color: #B57EDC;\">css<\/span><span>({<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\tposition: &#39;<\/span><span style=\"color: #98C379;\">absolute<\/span><span>&#39;,<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\tdisplay: &#39;<\/span><span style=\"color: #98C379;\">none<\/span><span>&#39;,<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\ttop:<\/span><span style=\"color: #C6CCD7;\"> y<\/span><span style=\"color: #E06C75;\"> -<\/span><span style=\"color: #56B6C2;\"> 20<\/span><span>,<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\tleft:<\/span><span style=\"color: #C6CCD7;\"> x<\/span><span style=\"color: #E06C75;\"> -<\/span><span style=\"color: #56B6C2;\"> 30<\/span><span>,<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\tborder: &#39;<\/span><span style=\"color: #98C379;\">1px solid #fdd<\/span><span>&#39;,<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\tpadding: &#39;<\/span><span style=\"color: #98C379;\">2px<\/span><span>&#39;,<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t&#39;<\/span><span style=\"color: #98C379;\">background-color<\/span><span>&#39;: &#39;<\/span><span style=\"color: #98C379;\">#fee<\/span><span>&#39;,<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\topacity:<\/span><span style=\"color: #56B6C2;\"> 1<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t}).<\/span><span style=\"color: #B57EDC;\">appendTo<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">body<\/span><span>&quot;).<\/span><span style=\"color: #B57EDC;\">fadeIn<\/span><span>(<\/span><span style=\"color: #56B6C2;\">200<\/span><span>);<\/span><\/span>\n<span class=\"giallo-l\"><span>\t}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">\tvar<\/span><span style=\"color: #C6CCD7;\"> comphist_data<\/span><span style=\"color: #E06C75;\"> =<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\tpopulation_label: &#39;<\/span><span style=\"color: #98C379;\">Active Fans This Week<\/span><span>&#39;,<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\tgroup_1_label: &#39;<\/span><span style=\"color: #98C379;\">Male<\/span><span>&#39;, group_2_label: &#39;<\/span><span style=\"color: #98C379;\">Female<\/span><span>&#39;,<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\tsubgroups: [<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t{ label: &#39;<\/span><span style=\"color: #98C379;\">13-17<\/span><span>&#39;, group_1_count:<\/span><span style=\"color: #56B6C2;\"> 3<\/span><span>, group_2_count:<\/span><span style=\"color: #56B6C2;\"> 16<\/span><span> },<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t{ label: &#39;<\/span><span style=\"color: #98C379;\">18-24<\/span><span>&#39;, group_1_count:<\/span><span style=\"color: #56B6C2;\"> 7<\/span><span>, group_2_count:<\/span><span style=\"color: #56B6C2;\"> 27<\/span><span> },<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t{ label: &#39;<\/span><span style=\"color: #98C379;\">25-34<\/span><span>&#39;, group_1_count:<\/span><span style=\"color: #56B6C2;\"> 3<\/span><span>, group_2_count:<\/span><span style=\"color: #56B6C2;\"> 29<\/span><span> },<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t{ label: &#39;<\/span><span style=\"color: #98C379;\">35-44<\/span><span>&#39;, group_1_count:<\/span><span style=\"color: #56B6C2;\"> 2<\/span><span>, group_2_count:<\/span><span style=\"color: #56B6C2;\"> 9<\/span><span> },<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t{ label: &#39;<\/span><span style=\"color: #98C379;\">45-54<\/span><span>&#39;, group_1_count:<\/span><span style=\"color: #56B6C2;\"> 0<\/span><span>, group_2_count:<\/span><span style=\"color: #56B6C2;\"> 4<\/span><span> },<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t{ label: &#39;<\/span><span style=\"color: #98C379;\">55+<\/span><span>&#39;, group_1_count:<\/span><span style=\"color: #56B6C2;\"> 0<\/span><span>, group_2_count:<\/span><span style=\"color: #56B6C2;\"> 0<\/span><span> }<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t],<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\tnon_demo_count:<\/span><span style=\"color: #56B6C2;\"> 0<\/span><\/span>\n<span class=\"giallo-l\"><span>\t};<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t$<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">#compplaceholder<\/span><span>&quot;).<\/span><span style=\"color: #B57EDC;\">comphist<\/span><span>({},<\/span><span style=\"color: #C6CCD7;\"> comphist_data<\/span><span>);<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t$<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">#compplaceholder<\/span><span>&quot;).<\/span><span style=\"color: #B57EDC;\">bind<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">barover<\/span><span>&quot;,<\/span><span style=\"color: #61AFEF;\"> function<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">e<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> perc<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> pos<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\t$<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">#comphist1tooltip<\/span><span>&quot;).<\/span><span style=\"color: #B57EDC;\">remove<\/span><span>();<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\tshowCompHistTooltip<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">pos<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">left<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> pos<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">top<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> perc<\/span><span>);<\/span><\/span>\n<span class=\"giallo-l\"><span>\t});<\/span><\/span>\n<span class=\"giallo-l\"><span>\t<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t$<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">#compplaceholder<\/span><span>&quot;).<\/span><span style=\"color: #B57EDC;\">bind<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">barout<\/span><span>&quot;,<\/span><span style=\"color: #61AFEF;\"> function<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">e<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> perc<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> pos<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\t$<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">#comphist1tooltip<\/span><span>&quot;).<\/span><span style=\"color: #B57EDC;\">remove<\/span><span>();<\/span><\/span>\n<span class=\"giallo-l\"><span>\t});<\/span><\/span>\n<span class=\"giallo-l\"><span>});<\/span><\/span>\n<span class=\"giallo-l\"><span>&lt;\/<\/span><span style=\"color: #E5C07B;\">script<\/span><span>&gt;<\/span><\/span><\/code><\/pre>\n<p>Hopefully, the example helps get you up and running quickly.<\/p>\n"},{"title":"Flot Multi-series Bar Graph Support","published":"2009-08-22T00:00:00+00:00","updated":"2009-08-22T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/flot-multi-series-bar-graph-support\/"}},"id":"https:\/\/drewdeponte.com\/blog\/flot-multi-series-bar-graph-support\/","content":"<p>Hey boys and girls. I just got finished coding support for Multi-series bar\ngraphs into Flot (a jQuery based JavaScript graphing library). Hence, I figured\nI would share not only the code but a little screenshot of it below. The code\nfor it can be found <a href=\"https:\/\/drewdeponte.com\/blog\/flot-multi-series-bar-graph-support\/jquery.flot.js\">here<\/a>. Below is an example of how to use it.<\/p>\n<p><img src=\"https:\/\/drewdeponte.com\/blog\/flot-multi-series-bar-graph-support\/flot-multi-serios-bar-graph.png\" alt=\"Flot Multi-Series Bar Graph\" \/><\/p>\n<p>With the multi-series bar graph support, you can have Flot display the series\nside by side. This is useful when you are interested in visually comparing\nseries and grouping segments you want to see columns in series that you are\nspecifically interested in comparing.<\/p>\n<p>The above screenshot was produced by defining a few code snippets. The first snippet\nis a snippet of HTML that includes the customized Flot javascript source file.\nNote: The Flot javascript source file needs to come after the standard jQuery\njavascript source file. An example of this snippet is as follows:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"html\"><span class=\"giallo-l\"><span style=\"color: #5F6672;font-style: italic;\">&lt;!--[if IE]&gt;&lt;script language=&quot;javascript&quot; type=&quot;text\/javascript&quot; src=&quot;..\/excanvas.min.js&quot;&gt;&lt;\/script&gt;&lt;![endif]--&gt;<\/span><\/span>\n<span class=\"giallo-l\"><span>&lt;<\/span><span style=\"color: #E5C07B;\">script<\/span><span style=\"color: #D19A66;\"> language<\/span><span>=&quot;<\/span><span style=\"color: #98C379;\">javascript<\/span><span>&quot;<\/span><span style=\"color: #D19A66;\"> type<\/span><span>=&quot;<\/span><span style=\"color: #98C379;\">text\/javascript<\/span><span>&quot;<\/span><span style=\"color: #D19A66;\"> src<\/span><span>=&quot;<\/span><span style=\"color: #98C379;\">..\/jquery.js<\/span><span>&quot;&gt;&lt;\/<\/span><span style=\"color: #E5C07B;\">script<\/span><span>&gt;<\/span><\/span>\n<span class=\"giallo-l\"><span>&lt;<\/span><span style=\"color: #E5C07B;\">script<\/span><span style=\"color: #D19A66;\"> language<\/span><span>=&quot;<\/span><span style=\"color: #98C379;\">javascript<\/span><span>&quot;<\/span><span style=\"color: #D19A66;\"> type<\/span><span>=&quot;<\/span><span style=\"color: #98C379;\">text\/javascript<\/span><span>&quot;<\/span><span style=\"color: #D19A66;\"> src<\/span><span>=&quot;<\/span><span style=\"color: #98C379;\">..\/jquery.flot.js<\/span><span>&quot;&gt;&lt;\/<\/span><span style=\"color: #E5C07B;\">script<\/span><span>&gt;<\/span><\/span><\/code><\/pre>\n<p>Note: In the above snippet the href values are likely to be different in your\ncase. The second code snippet is simply a single line of HTML that is the div\nthat is to be the Multi-series Bar Graph. It is key that this div has an id\nthat is easily referenced and a set height and width. An example of this\nsnippet is as follows:<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"html\"><span class=\"giallo-l\"><span>&lt;<\/span><span style=\"color: #E5C07B;\">div<\/span><span style=\"color: #D19A66;\"> id<\/span><span>=&quot;<\/span><span style=\"color: #98C379;\">placeholder<\/span><span>&quot;<\/span><span style=\"color: #D19A66;\"> style<\/span><span>=&quot;<\/span><span style=\"color: #98C379;\">width: 375px; height: 200px;<\/span><span>&quot;&gt;&lt;\/<\/span><span style=\"color: #E5C07B;\">div<\/span><span>&gt;<\/span><\/span><\/code><\/pre>\n<p>The third code snippet needed is the javascript that defines the data, creates\nthe Multi-series Bar Graph in the specified div, and handles the hover\nfunctionality. An example of this is seen below.<\/p>\n<pre class=\"giallo\" style=\"color: #A9B2C3; background-color: #21252B;\"><code data-lang=\"javascript\"><span class=\"giallo-l\"><span>&lt;<\/span><span style=\"color: #E5C07B;\">script<\/span><span style=\"color: #D19A66;\"> id<\/span><span style=\"color: #E06C75;\">=<\/span><span>&quot;<\/span><span style=\"color: #98C379;\">source<\/span><span>&quot;&gt;<\/span><\/span>\n<span class=\"giallo-l\"><span>$(function () {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\tvar ms_data<\/span><span style=\"color: #E06C75;\"> =<\/span><span> [{&quot;<\/span><span style=\"color: #98C379;\">label<\/span><span>&quot;:&quot;<\/span><span style=\"color: #98C379;\">FOO<\/span><span>&quot;,&quot;<\/span><span style=\"color: #98C379;\">data<\/span><span>&quot;:[[<\/span><span style=\"color: #56B6C2;\">0<\/span><span>,<\/span><span style=\"color: #56B6C2;\">80<\/span><span>],[<\/span><span style=\"color: #56B6C2;\">1<\/span><span>,<\/span><span style=\"color: #56B6C2;\">70<\/span><span>],[<\/span><span style=\"color: #56B6C2;\">2<\/span><span>,<\/span><span style=\"color: #56B6C2;\">100<\/span><span>],[<\/span><span style=\"color: #56B6C2;\">3<\/span><span>,<\/span><span style=\"color: #56B6C2;\">60<\/span><span>],[<\/span><span style=\"color: #56B6C2;\">4<\/span><span>,<\/span><span style=\"color: #56B6C2;\">102<\/span><span>]]},<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\t\t\t\t\t {&quot;<\/span><span style=\"color: #98C379;\">label<\/span><span>&quot;:&quot;<\/span><span style=\"color: #98C379;\">BAR<\/span><span>&quot;,&quot;<\/span><span style=\"color: #98C379;\">data<\/span><span>&quot;:[[<\/span><span style=\"color: #56B6C2;\">0<\/span><span>,<\/span><span style=\"color: #56B6C2;\">10<\/span><span>],[<\/span><span style=\"color: #56B6C2;\">1<\/span><span>,<\/span><span style=\"color: #56B6C2;\">20<\/span><span>],[<\/span><span style=\"color: #56B6C2;\">2<\/span><span>,<\/span><span style=\"color: #56B6C2;\">30<\/span><span>],[<\/span><span style=\"color: #56B6C2;\">3<\/span><span>,<\/span><span style=\"color: #56B6C2;\">40<\/span><span>],[<\/span><span style=\"color: #56B6C2;\">4<\/span><span>,<\/span><span style=\"color: #56B6C2;\">80<\/span><span>]]},<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\t\t\t\t\t {&quot;<\/span><span style=\"color: #98C379;\">label<\/span><span>&quot;:&quot;<\/span><span style=\"color: #98C379;\">CAR<\/span><span>&quot;,&quot;<\/span><span style=\"color: #98C379;\">data<\/span><span>&quot;:[[<\/span><span style=\"color: #56B6C2;\">0<\/span><span>,<\/span><span style=\"color: #56B6C2;\">5<\/span><span>],[<\/span><span style=\"color: #56B6C2;\">1<\/span><span>,<\/span><span style=\"color: #56B6C2;\">10<\/span><span>],[<\/span><span style=\"color: #56B6C2;\">2<\/span><span>,<\/span><span style=\"color: #56B6C2;\">15<\/span><span>],[<\/span><span style=\"color: #56B6C2;\">3<\/span><span>,<\/span><span style=\"color: #56B6C2;\">20<\/span><span>],[<\/span><span style=\"color: #56B6C2;\">4<\/span><span>,<\/span><span style=\"color: #56B6C2;\">25<\/span><span>]]}]<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\tvar ms_ticks<\/span><span style=\"color: #E06C75;\"> =<\/span><span> [[<\/span><span style=\"color: #56B6C2;\">0<\/span><span>,&quot;<\/span><span style=\"color: #98C379;\">1\/28<\/span><span>&quot;],[<\/span><span style=\"color: #56B6C2;\">1<\/span><span>,&quot;<\/span><span style=\"color: #98C379;\">1\/29<\/span><span>&quot;],[<\/span><span style=\"color: #56B6C2;\">2<\/span><span>,&quot;<\/span><span style=\"color: #98C379;\">1\/30<\/span><span>&quot;],[<\/span><span style=\"color: #56B6C2;\">3<\/span><span>,&quot;<\/span><span style=\"color: #98C379;\">1\/31<\/span><span>&quot;],[<\/span><span style=\"color: #56B6C2;\">4<\/span><span>,&quot;<\/span><span style=\"color: #98C379;\">1\/32<\/span><span>&quot;]];<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">\t\tfunction<\/span><span style=\"color: #B57EDC;\"> plotWithOptions<\/span><span>() {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\t\t\t$<\/span><span>.<\/span><span style=\"color: #B57EDC;\">plot<\/span><span>(<\/span><span style=\"color: #B57EDC;\">$<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">#placeholder<\/span><span>&quot;),<\/span><span style=\"color: #C6CCD7;\"> ms_data<\/span><span>, {<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\tbars: { show:<\/span><span style=\"color: #56B6C2;\"> true<\/span><span>, barWidth:<\/span><span style=\"color: #56B6C2;\"> 0.6<\/span><span>, series_spread:<\/span><span style=\"color: #56B6C2;\"> true<\/span><span>, align: &quot;<\/span><span style=\"color: #98C379;\">center<\/span><span>&quot; },<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\txaxis: { ticks:<\/span><span style=\"color: #C6CCD7;\"> ms_ticks<\/span><span>, autoscaleMargin:<\/span><span style=\"color: #56B6C2;\"> .10<\/span><span> },<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\tgrid: { hoverable:<\/span><span style=\"color: #56B6C2;\"> true<\/span><span>, clickable:<\/span><span style=\"color: #56B6C2;\"> true<\/span><span> }<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t});<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t}<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">\t\tfunction<\/span><span style=\"color: #B57EDC;\"> showTooltip<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">x<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> y<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> contents<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\t\t\t$<\/span><span>(&#39;&#39;).<\/span><span style=\"color: #B57EDC;\">css<\/span><span>( {<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\t\t\tposition: &#39;<\/span><span style=\"color: #98C379;\">absolute<\/span><span>&#39;,<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\t\t\tdisplay: &#39;<\/span><span style=\"color: #98C379;\">none<\/span><span>&#39;,<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\t\t\ttop:<\/span><span style=\"color: #C6CCD7;\"> y<\/span><span style=\"color: #E06C75;\"> +<\/span><span style=\"color: #56B6C2;\"> 5<\/span><span>,<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\t\t\tleft:<\/span><span style=\"color: #C6CCD7;\"> x<\/span><span style=\"color: #E06C75;\"> +<\/span><span style=\"color: #56B6C2;\"> 5<\/span><span>,<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\t\t\tborder: &#39;<\/span><span style=\"color: #98C379;\">1px solid #fdd<\/span><span>&#39;,<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\t\t\tpadding: &#39;<\/span><span style=\"color: #98C379;\">2px<\/span><span>&#39;,<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\t\t\t&#39;<\/span><span style=\"color: #98C379;\">background-color<\/span><span>&#39;: &#39;<\/span><span style=\"color: #98C379;\">#fee<\/span><span>&#39;,<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\t\t\topacity:<\/span><span style=\"color: #56B6C2;\"> 0.80<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\t}).<\/span><span style=\"color: #B57EDC;\">appendTo<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">body<\/span><span>&quot;).<\/span><span style=\"color: #B57EDC;\">show<\/span><span>();<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t}<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\tplotWithOptions<\/span><span>();<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\t$<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">#placeholder<\/span><span>&quot;).<\/span><span style=\"color: #B57EDC;\">bind<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">plothover<\/span><span>&quot;,<\/span><span style=\"color: #61AFEF;\"> function<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">event<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> pos<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> item<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\t\t$<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">#x<\/span><span>&quot;).<\/span><span style=\"color: #B57EDC;\">text<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">pos<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">x<\/span><span>.<\/span><span style=\"color: #B57EDC;\">toFixed<\/span><span>(<\/span><span style=\"color: #56B6C2;\">2<\/span><span>));<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\t\t$<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">#y<\/span><span>&quot;).<\/span><span style=\"color: #B57EDC;\">text<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">pos<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">y<\/span><span>.<\/span><span style=\"color: #B57EDC;\">toFixed<\/span><span>(<\/span><span style=\"color: #56B6C2;\">2<\/span><span>));<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\t\t\tif<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">item<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\t\t\t\t\tif<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">previousPoint<\/span><span style=\"color: #E06C75;\"> !=<\/span><span style=\"color: #C6CCD7;\"> item<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">datapoint<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\t\t\t\t\t\t\t\tpreviousPoint<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #C6CCD7;\"> item<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">datapoint<\/span><span>;<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\t\t\t\t<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\t\t\t\t\t\t\t$<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">#tooltip<\/span><span>&quot;).<\/span><span style=\"color: #B57EDC;\">remove<\/span><span>();<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #61AFEF;\">\t\t\t\t\t\t\t\tvar<\/span><span style=\"color: #C6CCD7;\"> x<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #C6CCD7;\"> item<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">datapoint<\/span><span>[<\/span><span style=\"color: #56B6C2;\">0<\/span><span>].<\/span><span style=\"color: #B57EDC;\">toFixed<\/span><span>(<\/span><span style=\"color: #56B6C2;\">2<\/span><span>),<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\t\t\t\t\t\t\t\t\t\ty<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #C6CCD7;\"> item<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">datapoint<\/span><span>[<\/span><span style=\"color: #56B6C2;\">1<\/span><span>].<\/span><span style=\"color: #B57EDC;\">toFixed<\/span><span>(<\/span><span style=\"color: #56B6C2;\">2<\/span><span>);<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\t\t\t\t<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\t\t\t\t\t\t\tshowTooltip<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">item<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">pageX<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> item<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">pageY<\/span><span>,<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\t\t\t\t\t\t\t\t\t\t\t\t\t\titem<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">series<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">label<\/span><span style=\"color: #E06C75;\"> +<\/span><span> &quot;<\/span><span style=\"color: #98C379;\"> Group id: <\/span><span>&quot;<\/span><span style=\"color: #E06C75;\"> +<\/span><span style=\"color: #C6CCD7;\"> Math<\/span><span>.<\/span><span style=\"color: #B57EDC;\">floor<\/span><span>(<\/span><span style=\"color: #C6CCD7;\">x<\/span><span>)<\/span><span style=\"color: #E06C75;\"> +<\/span><span> &quot;<\/span><span style=\"color: #98C379;\">, y = <\/span><span>&quot;<\/span><span style=\"color: #E06C75;\"> +<\/span><span style=\"color: #C6CCD7;\"> y<\/span><span style=\"color: #E06C75;\"> +<\/span><span> &quot;<\/span><span style=\"color: #98C379;\">, seriesIndex: <\/span><span>&quot;<\/span><span style=\"color: #E06C75;\"> +<\/span><span style=\"color: #C6CCD7;\"> item<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">seriesIndex<\/span><span>);<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\t\t\t}<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\t}<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\t\t\telse<\/span><span> {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\t\t\t\t\t$<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">#tooltip<\/span><span>&quot;).<\/span><span style=\"color: #B57EDC;\">remove<\/span><span>();<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #C6CCD7;\">\t\t\t\t\t\tpreviousPoint<\/span><span style=\"color: #E06C75;\"> =<\/span><span style=\"color: #56B6C2;\"> null<\/span><span>;            <\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\t}<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t});<\/span><\/span>\n<span class=\"giallo-l\"><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\t$<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">#placeholder<\/span><span>&quot;).<\/span><span style=\"color: #B57EDC;\">bind<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">plotclick<\/span><span>&quot;,<\/span><span style=\"color: #61AFEF;\"> function<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">event<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> pos<\/span><span>,<\/span><span style=\"color: #C6CCD7;\"> item<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #E06C75;\">\t\t\t\tif<\/span><span> (<\/span><span style=\"color: #C6CCD7;\">item<\/span><span>) {<\/span><\/span>\n<span class=\"giallo-l\"><span style=\"color: #B57EDC;\">\t\t\t\t\t\t$<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">#clickdata<\/span><span>&quot;).<\/span><span style=\"color: #B57EDC;\">text<\/span><span>(&quot;<\/span><span style=\"color: #98C379;\">You clicked bar <\/span><span>&quot;<\/span><span style=\"color: #E06C75;\"> +<\/span><span style=\"color: #C6CCD7;\"> item<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">dataIndex<\/span><span style=\"color: #E06C75;\"> +<\/span><span> &quot;<\/span><span style=\"color: #98C379;\"> in <\/span><span>&quot;<\/span><span style=\"color: #E06C75;\"> +<\/span><span style=\"color: #C6CCD7;\"> item<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">series<\/span><span>.<\/span><span style=\"color: #C6CCD7;\">label<\/span><span style=\"color: #E06C75;\"> +<\/span><span> &quot;<\/span><span style=\"color: #98C379;\">.<\/span><span>&quot;);<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t\t\t}<\/span><\/span>\n<span class=\"giallo-l\"><span>\t\t});<\/span><\/span>\n<span class=\"giallo-l\"><span>});<\/span><\/span>\n<span class=\"giallo-l\"><span>&lt;\/<\/span><span style=\"color: #E5C07B;\">script<\/span><span>&gt;<\/span><\/span><\/code><\/pre>\n<p>Hopefully the code example helps get you up and running.<\/p>\n"},{"title":"TextMate Autotools Screencast","published":"2007-09-05T00:00:00+00:00","updated":"2007-09-05T00:00:00+00:00","author":{"name":"\n            \n              Unknown\n            \n          "},"link":{"@attributes":{"rel":"alternate","type":"text\/html","href":"https:\/\/drewdeponte.com\/blog\/textmate-autotools-screencast\/"}},"id":"https:\/\/drewdeponte.com\/blog\/textmate-autotools-screencast\/","content":"<p>I just threw together a screencast that covers a little TextMate Bundle I have\nbeen working. The TextMate Autotools Bundle is an extension for the TextMate\neditor which integrates TextMate with the standard GNU Autotools suite with a\nspecific emphasis on cross-platform building. Check it out below to see the\nfeatures and how to use them in this alpha version.<\/p>\n<p><em>Note:<\/em> The video for this has been lost to the inter-web somewhere.<\/p>\n"}]}