<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Kallmanation]]></title><description><![CDATA[Husband; Father; Software Engineer; Gamer; Tinkerer; Writing about code.]]></description><link>https://www.kallmanation.com</link><generator>RSS for Node</generator><lastBuildDate>Sun, 19 Apr 2026 12:12:55 GMT</lastBuildDate><atom:link href="https://www.kallmanation.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Swagger Codegen and Multiple Tags]]></title><description><![CDATA[Today ChatGPT lied to me, so I’m setting the record straight. This is my question: “If an endpoint in an OpenAPI spec has multiple tags, what will Swagger Codegen do?”. Because Swagger Codegen organized the generated code into api/tag_name_api files....]]></description><link>https://www.kallmanation.com/swagger-codegen-and-multiple-tags</link><guid isPermaLink="true">https://www.kallmanation.com/swagger-codegen-and-multiple-tags</guid><category><![CDATA[swagger]]></category><category><![CDATA[codegen]]></category><category><![CDATA[OpenApi]]></category><dc:creator><![CDATA[Nathan Kallman]]></dc:creator><pubDate>Mon, 09 Feb 2026 14:37:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/FPNnKfjcbNU/upload/9fb493d117fae9ab4334036ad6e56ffa.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Today ChatGPT lied to me, so I’m setting the record straight. This is my question: “If an endpoint in an OpenAPI spec has multiple tags, what will Swagger Codegen do?”. Because Swagger Codegen organized the generated code into <code>api/tag_name_api</code> files. So what if there’s multiple tags? Does it take the first tag? The last tag? Or does it generate a file for each tag and give it the same behavior? ChatGPT claimed it takes only the first tag. I’ve already revealed that’s false, so I guess I don’t have any dramatic reveal, so let me just spout the details for you.</p>
<h2 id="heading-methodology">Methodology</h2>
<p>I used Swagger Codegen V3 Online Generator: <a target="_blank" href="https://swagger.io/docs/open-source-tools/swagger-codegen/codegen-v3/online-generators/">https://swagger.io/docs/open-source-tools/swagger-codegen/codegen-v3/online-generators/</a></p>
<p>I <code>POST</code> ‘ed to <a target="_blank" href="https://generator3.swagger.io/api/generate">https://generator3.swagger.io/api/generate</a> on 2/9/2026 with a payload looking like this:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"specURL"</span> : <span class="hljs-string">"my.code/openapi/v3/swagger.yaml"</span>,
  <span class="hljs-attr">"lang"</span> : <span class="hljs-string">"ruby"</span>,
  <span class="hljs-attr">"options"</span> : {
    <span class="hljs-attr">"gemName"</span>: <span class="hljs-string">"liar-liar-pants-on-fire"</span>,
    <span class="hljs-attr">"gemRequiredRubyVersion"</span>: <span class="hljs-string">"&gt;= 3.1.4"</span>,
    <span class="hljs-attr">"gemSummary"</span>: <span class="hljs-string">"Liar Liar Pants on FIRE"</span>, 
    <span class="hljs-attr">"gemDescription"</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">"gemAuthor"</span>: <span class="hljs-string">"Me"</span>
  },
  <span class="hljs-attr">"type"</span> : <span class="hljs-string">"CLIENT"</span>,
  <span class="hljs-attr">"codegenVersion"</span> : <span class="hljs-string">"V3"</span>
}
</code></pre>
<p>The swagger had tag sections that looked like this (two endpoints, each with one differing tag and one common tag)</p>
<pre><code class="lang-yaml"><span class="hljs-attr">get:</span>
  <span class="hljs-attr">summary:</span> <span class="hljs-string">Get</span> <span class="hljs-string">thingy</span>
  <span class="hljs-attr">operationId:</span> <span class="hljs-string">getThingy</span>
  <span class="hljs-attr">tags:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">Better</span> <span class="hljs-string">Grouping</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">Thingies</span>
<span class="hljs-attr">get:</span>
  <span class="hljs-attr">summary:</span> <span class="hljs-string">Get</span> <span class="hljs-string">whatchamacallit</span>
  <span class="hljs-attr">operationId:</span> <span class="hljs-string">getWhatchamacallit</span>
  <span class="hljs-attr">tags:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">Better</span> <span class="hljs-string">Grouping</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">Whatchamacallits</span>
</code></pre>
<h2 id="heading-results">Results</h2>
<p>The generated code had three files <code>api/better_grouping_api.rb</code>, <code>api/thingies_api.rb</code>, and <code>api/whatchamacallits_api.rb</code>.</p>
<p>And wouldn’t you know it, <code>better_grouping_api</code> had the code to call both endpoints <em>and</em> the <code>thingies_api</code> and <code>whatchamacallits_api</code> had the duplicated code for their correspondingly tagged endpoint.</p>
<p>So there you go, if you have OpenAPI spec with multiple tags, it looks like Swagger Codegen v3 will yield the behavior under each of the <code>tag_api</code> files it generates for each tag.</p>
]]></content:encoded></item><item><title><![CDATA[My Folly]]></title><description><![CDATA[Foreward
This is a short story. I hope you enjoy it. It is not like anything else on this blog. If you’ve come here hoping for a new post like my others, I’m sorry to disappoint you. If you’ve come here hoping for other short stories like this one, I...]]></description><link>https://www.kallmanation.com/my-folly</link><guid isPermaLink="true">https://www.kallmanation.com/my-folly</guid><dc:creator><![CDATA[Nathan Kallman]]></dc:creator><pubDate>Fri, 02 Jan 2026 02:24:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/O-rCs_m4dvM/upload/872a2f84cf3a52c1f0697414c26395d9.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-foreward">Foreward</h1>
<p>This is a short story. I hope you enjoy it. It is not like anything else on this blog. If you’ve come here hoping for a new post like my others, I’m sorry to disappoint you. If you’ve come here hoping for other short stories like this one, I’m sorry to disappoint you.</p>
<h1 id="heading-my-folly">My Folly</h1>
<p>“Could you spare a buck for bus fair?”, asked an old man on the street corner. He was bent over and holding a battered cane. Tattered mismatching clothes hung from his thin body.</p>
<p>My budget for the month was already accounted to my last dime. But, what can I do? How can I refuse, when I myself had begged on corners of streets to eat for the day. Shuffling through my wallet I had a ten dollar bill and some change from a morning coffee, “Here’s thirteen dollars. It’s all I have on me. Hopefully that will get you where you need to go”.</p>
<p>The old man smiled, showing his teeth that he hadn’t been able to care for in a long time. I was already running late so I turned quickly and stepped into the street while I heard horns blaring and tires squealing on the asphalt.</p>
<p>I looked up to see what the commotion was about. In front of me there was a large wooden desk. A young man sat undisturbed behind the desk. Or maybe it was a middle aged woman. If they sat in front of me right now I still couldn’t describe them to you they were so incredibly ordinary looking. I saw a neatly stacked set of wooden rectangles behind them. To their right a foot-high stack of folders. To their left a smaller stack of tickets. Immediately before them a folder lay open on the desk. I was seated in a chair in front of the desk and didn’t see anything else around. No street, no old man, no honking cars or trucks.</p>
<p>“Brickmold, reincarnation, or a visit paradise for 10,000 years before being cast into outer darkness”, said the staffer behind the desk without looking.</p>
<p>“I’m sorry?” I answered in confusion. How did I get here? My head had a slight ache.</p>
<p>“It’s too late to offer apologies after you’re dead”, answered the deskperson, “would you like a brickmold, a reincarnation , or a visit paradise for 10,000 years before being cast into outer darkness”, in a tone that I would say was becoming agitated, except it was clear they had said that phrase so many times they were now incapable of saying it in any tone except the monotonic rhythm they’d used so many times before.</p>
<p>“Brickmold?”, I wondered to myself, but apparently too loudly because the deskperson answered.</p>
<p>“A rare choice, but here you go”. They turned around and picked up one of the wooden rectangles. I could see now that it had handles on both ends, and an internal rectangle open to the top and bottom, exactly the size of a brick.</p>
<p>“I’m sorry, but”, exasperated at afterlife bureaucratic misunderstandings apparently being a possibility, I struggled for words, “what am I supposed to do with a brickmold?”</p>
<p>“Make bricks with it”, the deskperson looked at me for the first time, up and down, “that’s pretty self explanatory in the name, ‘brickmold’”.</p>
<p>“Right, but”, I trailed off, not knowing how to argue anything. Or what even the point would be. I looked to my right and saw a second desk I hadn’t noticed before. Staffed by another deskperson, identical in ordinariness but somehow distinct from my deskperson. Sitting in front, a man I can only describe as gluttonous. You may think I’m being mean or uncharitable in my description. Please trust my goodwill that if you looked upon this person, ‘gluttonous’ would be the most charitable description you could muster. For I do not mean simply outward physical appearance. This man’s demeanor, his very eyes looked hungry, hunting for the next convenience to consume.</p>
<p>The same choices were rattled off to the man in the same monotonous rhythm as I had. And the man answered quickly, “a visit to paradise! Obviously, does anyone pick anything else?”  Promptly his deskperson picked up one of the tickets at their desk and handed it over to the man.</p>
<p>Still wondering what to do I now looked to my left and saw a third desk, a third deskperson, and a sharply dressed woman in a suit sitting before it, seemingly ready to sign some multimillion dollar business deal. Again I heard, “Brickmold, reincarnation, or a visit paradise for 10,000 years before being cast into outer darkness”, rattled off by her deskperson.</p>
<p>“Reincarnation”, the woman proudly answered, as if she had hacked the system and had it all figured out. “What are my choices?”</p>
<p>Her deskperson began going through the stack of folders to their right, “I have multiple Oriental positions open, let’s see, an Eastern European farming family too. Oh interesting, I have an Icelandic opening, don’t see that everyday”.</p>
<p>My deskperson caught my attention by speaking, “You can’t stay here forever. Please take your brickmold and move along”, holding out the brickmold for me to take.</p>
<p>I stood and took the brickmold they offered to me. Not quite sure where exactly to ‘move along’ to, I turned around to see if there was a way forward that direction. I saw an endless field of light brown clay. I turned back and saw more clay. Clay to the left. Clay to the right. Flat and soft clay. No desks. No deskperson. I was alone.</p>
<p>I was alone in a field of clay with a brickmold in my hand. High above me, faintly in the clouds, I could see a shining city. I thought that must be heaven. And the purpose of my brickmold became clear, I hadn’t measured up in life so I must close the gap in this purgatory after death. One brick at a time.</p>
<p>I started my first brick. Filling the mold with the clay. Slumping out the damp brick on the ground. My cornerstone has been laid. Or is it a corner brick? I’m no mason. But I knew I couldn’t stack wet bricks on top of one another. I wondered how long it would take to dry them.</p>
<p>I began making many bricks, ten in a row with ten columns beside. After making one hundred bricks in this way, I returned to my first brick to see if it was dry. Being still wet, I made another hundred bricks in the same way before returning again.</p>
<p>I made nearly one thousand bricks in rows and columns before the first brick was dry to the touch. I was still no brick expert. Surmising there may be a difference between dry and fully hardened, I decided to continue making bricks before using any. I must have an excellent foundation, rushing at the beginning may double the work near the end.</p>
<p>After making ten thousand bricks, I returned to the first row of bricks to begin the tower. I realized I had no mortar. Nor any idea how any sort of mortar could be made. The only thing around me was clay. And besides I had no tools for laying bricks with mortar. Even if the tools and materials all fell on my head from heaven, would I even know how to use them?</p>
<p>After long consideration, I thought there was nothing for it than to just dry stack these bricks in a pyramid shape. Hopefully that would be stable enough. I knew I would need an immense base. So my first thousand bricks I laid in a straight line. The next thousand I laid beside it. I realized I should offset my bricks but, not having half-bricks I wondered how. Wedging a brick between two others, jutting out about halfway, and striking it with a fourth, I was able to make a decent pair of half-bricks.</p>
<p>After laying these two thousand bricks I realized that, while an impressive tower it would make, it would be a paltry height in light of what was required to match my goal. I returned to making bricks by the ten thousand, laying the prior batch in my tower after setting a new one to cure.</p>
<p>I laid one hundred thousand bricks end to end. It now took half the day to walk its length. Although, “day” is not the right word. There was no night here. I was never hungry or thirsty. Always tired, but never needed slumber, nor even seemed  to have the ability to sleep when I laid down. There was plenty of time to think while walking. I could only mark the passage of time by the shadows the bricks cast. Though it seemed ambiguous where the light came from, feeling like it came from everywhere all at once.  The subtle shadows pointed first on one side of the line of bricks and later the other, eventually circling back around to the start.</p>
<p>I thought about the dimensions of my tower. How high would it be? I couldn’t quite remember my geometry. I drew out a pyramid in the clay. I drew the height as a line coming down. I didn’t want to reinvent geometry, but had a vague remembrance. I drew the pyramid again, this time with the base exactly the length of my two feet, one in front of the other. The height I drew and put my foot beside it, matching its length exactly. So, my pyramid would be about half as high as it was wide, depending on the angle I chose to build to. I started to think a pyramid half as high as half a days walk wouldn’t be nearly tall enough.</p>
<p>I walked back to where I was making new bricks, determined to make a bigger foundation. I laid a second hundred thousand bricks. Instead of making bricks in one area and carrying them to place, I now simply made bricks adjacent to where they would be placed. I waded off the end of my tower edge in this way, making brick after brick, one after another. I didn’t bother counting now, I would go mad if I tried. Instead I counted by days making bricks. I knew ten thousand bricks took slightly less than a day for me to make, since I’d gotten so much practice. Doubling the side length of my tower would take over two weeks just to make the bricks.</p>
<p>But what is time in this place? I doubled it and doubled it again plus some extra for good measure. I now had to walk a week straight to cover the length of this one side of my tower.</p>
<p>Save those second thousand bricks I prematurely laid, after all this work I had but one row of my tower. Not a whole layer, just one row of one layer! But what is time in this place? The deskperson didn’t mention any time limit like the visit to paradise had.</p>
<p>I took my brickmold back to the start. I began making the bricks for the second row. Brick next to brick, all drying in the light, marching off to infinity. Or more accurately, a few days walk. Once I reached the end, I walked for a week back to the start and began placing the now well cured bricks in their row. As I walked back again, as I had left my brickmold at the start, I realized I was spending an extra two weeks walking for no reason. I ought to think of it more like mowing a yard than writing lines of prose, making bricks all the way down as well as all the way back before placing bricks back and forth so there would be no walking without working.</p>
<p>I did just that for a while until a difficulty arose. As I worked deeper into my tower’s foundation, I had to walk farther back to the edge where I dug more clay for bricks. Not wanting to dig a hole in the middle of my towers foundation! Progress slowed with each row laid. Halfway through the base of my tower I considered trudging the other direction to where the final edge of my tower would lie. But hiking three days straight into a landmarkless field of clay made me nervous. Once I lost sight of my tower, how would I find my way back? How would I measure the distances correctly? Better to slog through the progress I had than lose all of it.</p>
<p>It took several years to lay that first layer. But it was done. I rested in the knowledge that the first layer had the most bricks in it, so the next layers will become easier and easier to finish.</p>
<p>I began the second course of bricks. For variety, I placed the bricks at a ninety degree angle to the first course, so my first row of the second layer ran across all the rows of the first layer. It was a breath of fresh air being able to make and lay bricks in such a rapid succession again, the way I had been able to years ago at the start of my building when I made bricks down and then back to then lay them all the way down and then back to do it all again.</p>
<p>A few weeks less than several years and I finished the second layer of my pyramid to heaven. A few weeks less than that and a third course rose from the ground. There was a point a few layers up where I spent exactly one year to lay one layer. The pyramid was rising at an exciting pace now. Well, exciting for one already decades into solitary madness.</p>
<p>I neared the top of my pyramid now. A ten foot by ten foot square was the summit. And my goal shimmered a clearly unreachable distance away still. A measly five additional feet to cap this pyramid off would be as effective as a single grain of rice given to a starving orphan. Or as effective as those thirteen dollars I gave to that beggar? Discouraged, I laid on the top of my tower. How long had it been? A century? More? Why would I think penance so simple or purgatory so short?</p>
<p>I descended to the base of my tower. Along two edges I began filling in the great trenches I had dug clay out of while making bricks, preparing a wider foundation to extend my pyramid. I added row upon row of bricks to these two sides. Each row at the bottom, many miles long, would add a few inches of height to the top. And I built more and more. Clay, bricks, time; I have thought all my thoughts and only have routine left. Put clay in the brickmold. Carry a brick. Place a brick. Walk and walk. The centuries wound on as I built a larger pyramid with my first pyramid as its corner.</p>
<p>Again I stood at the top. Again heaven was out of my reach. I descended further. It took a full month to reach the bottom. I made more bricks. Like a sole surviving ant, toiling on their deserted colony’s hill, I built more.  Why did I continue? What Sysaphusian task had been born of a single word? “Brickmold”, I had said. Why not visit paradise for this whole time? Or reincarnate? I would rather be abused for a lifetime than suffer this another millennia. Or just send me to Hades. Maybe a nice crow could come peck my liver out everyday, that sounds more enjoyable than this. “I don’t mean that”, I apologized to no one in particular. I still had a chance after all, hadn’t I? Diligence and hard work, that’s all I needed. A millennia turned as I built a larger pyramid with my second pyramid as its corner.</p>
<p>Again I stood at the top. Now on a mountain Everest would wilt under. Now a monument Olympus Mons would be shamed beside. Now a tower Babel’s architects would covet. Again heaven was out of my reach. Again I descended further.</p>
<p>“I can’t do it”, I screeched while trodding down. I could not continue this insanity. I could not move another mountain to gain a single inch, followed by two mountains for a second inch, it was too much. Perhaps I had missed something.</p>
<p>At the bottom I began experimenting other building techniques. I tried making a single column, which was stable enough but I had no way of climbing it. I tried making a spiral staircase around the column but either the upper steps kept collapsing onto the lower steps, since nothing supported the outer edge, or I destabilized the column by my weight being so off center. I attempted an open circle of bricks, like a well built upwards. If I put some of the bricks in sideways to make small footholds I could climb up the interior. Except there was no way into the base of this well-tower to begin the climb.</p>
<p>I attempted ways to make arches, thinking I could make an entrance. If I first built a supporting scaffold of brick, I could make an arch big enough to crawl into after laboriously removing the scaffold bricks. Making six archways with some extra interior thickness of the columns would make a near circular base that I could get into to climb the well-tower. Making the interior as narrow as possible while still allowing easy climbing and descending would reduce the bricks needed to build. Also, while experimenting I realized I needn’t make the circle of bricks solid. I could easily leave half-brick gaps, stacking the next brick so it just barely bridged two below it, alternating the gaps. This saved a great deal of bricks, which would be extremely useful with how long it took to reach the top of the pyramid.</p>
<p>And so, I trudged back up towards the top of my pyramid to build this new tower on top of it. I could only carry two bricks at a time. Not wanting to spend a decade building just the six arches for me to crawl into, I began borrowing bricks from my pyramid. I could remove some and leave the pyramid a step pyramid like those of the Aztecs rather than the smooth pyramids of Egypt I had started with.</p>
<p>Brick by brick I built this new tower from the top of the pyramid. I reached a sickening height. Then dizzying. Then so absurdly high that it felt like I was standing in a well at ground level. The clay ground stretched out in every direction and my pyramid was so far down beneath me it was almost imperceptible. I gazed up at heaven high above me. An uncomfortable thought entered my mind. I descended further. It took over a year to reach the bottom of my tower now. I had stolen every brick I dared from my pyramid.</p>
<p>I made two bricks, then I picked up the two bricks destined for the top that I had made years earlier for this day. I had learned how to climb the well-tower with two bricks. I looked up at heaven again with the same uncomfortable thought.</p>
<p>While still standing on the clay, I stretched out a brick in my hand. At arms length, the brick perfectly covered the city. Perhaps I could gauge my progress. If objects farther away appear smaller, the closer I get the larger it will be and the less my brick will cover when at the top of my tower. I began my heavenbound ascent.</p>
<p>Another year later, I stood again where two years earlier I had just finished laying a circle of bricks in my well-tower. Before placing the bricks I had carried all this way, I raised my arm, just as I had done at the base, with brick in hand. At arms length, the brick perfectly covered the shining city, just as it had at the base. I was no closer. I screamed. I yelled like a she-bear finding a dead cub. Like an air raid siren mounted atop its post.</p>
<p>I slung the brick off the edge with what remained of my strength then collapsed onto my face, slumped over the edge of my great folly.</p>
<p>A voice spoke to me from the other edge of the circle, “Why do you weep, my child?”</p>
<p>“I can’t do it myself!”, I howled before I even wondered how there was a voice besides my own. Anguish, true despair, suppresses all logical questions. And it had been so long since I’d spoken aloud, it was a wonder I had a voice still. That I still remembered the language I learned so long ago. Was this my language? It felt strange in my ears. Yet I understood the words perfectly.</p>
<p>“You never could little one”, He said with such searing truth it cauterized my wounded heart. But spoken with such tender love, like a cool salve on a burn. “Do you know me?”</p>
<p>I looked up at the voice. But I could not look. He was blindingly bright. I could have looked directly at the sun more easily than to look upon this man.</p>
<p>“How can I know you?”, I said, still with some bitter tears on my tongue. Hanging my head back down.</p>
<p>“You gave me thirteen dollars on the street corner before the accident”, I shot my head around to look at the man, who surely couldn’t be the same haggard and bent over old man I had given that pittance to those millennia ago. He continued, “and I remember you helped me cross the street in front of the courthouse when my eyes had failed me”. That had been a middle aged woman, they can’t be the same person. “And I was a hungry teenager, turned out of his home the day after Thanksgiving. That leftover Turkey you gave me on the ride to a safehouse filled me like you can’t know”. The man trailed off as though he had thought of seven more examples, but instead of listing them he simply looked at me and asked again, “Do you know me?”</p>
<p>“How can I know you?”, I answered, still in confusion and doubt.</p>
<p>“Believe. Know by faith what you have not seen with your eyes”, and without looking away from me, he asked again, “Do you know me?”</p>
<p>I knew, and was afraid to know.  “How can I know you?”, I answered the third time.</p>
<p>“Call on my name”, he said simply.</p>
<p>I knew. I knew I had fallen short. I knew I could not do it on my own. I knew there was no other way. I swallowed and hung my head, “Christ Jesus, please”, I trailed off with too many words to say. He answered, “Rise up. Pick up your brick”.</p>
<p>The brick I still had was now solid gold. I obeyed, but struggled under the weight. I slowly and carefully crawled out of the circle of bricks, up onto the edge. He instructed, “Give it to me, my child”. I gave it to him. “Look behind you”, he said and I turned around. We were no longer perched atop a lonely, useless tower, but on a solid street paved with gold.</p>
<p>On each side there were houses. Beautiful and big. But not uselessly large like mansions of earth. They lacked no rooms needed, but no useless rooms were present. Each room large enough to enjoy without being the cold, uninviting, overly large rooms the wealthy of the world pretend to own. The houses were just as close as three best friends would want their houses to be together. Never so close as to feel crowded. Never so far as to be inconvenient for a walk on a cool day together as neighbors.</p>
<p>In one direction the road continued over a small glassy stream before branching into more streets. The other, it ran a few paces before trailing off where it clearly needed to be paved further.</p>
<p>“Take your brick and place it”, he told me. I took it, ready for the weight of solid gold, but it was now lighter than imaginable, lighter than the clay bricks I had moved so many of. A foam brick even would have felt heavier in hand. I happily placed it, extending the pathway in paradise.</p>
<p>“So heaven is”, I wondered if such silly questions were allowed, but asked anyway, “still under construction?”</p>
<p>“I said I would prepare a place with many rooms”, he answered, “did I say I would build the Kingdom of Heaven alone?” He smiled at me.</p>
<p>I pondered a great many things. How meaningless all the things I had done, both in death and in life, had turned out. But yet what impact they all could have when given to him beside me.</p>
<p>“Before you come to rest here, there is one more thing you should see. Migdalel will take you.”</p>
<p>And beside me another bright man stepped into view. Though less bright, he was still as bright as the sun. I squinted at the man to see. Was this my deskperson who gave me that brickmold? Without a word Migdalel gently touched my arm and we were away.</p>
<p>We alighted in a hospital room. The person in the bed was unrecognizable. To put it simply, they looked like they had been run over by a truck. Many machines hooked to the patient in the bed beeped and whirred, clicked and cycled. Dripping medicines and saline into the patients veins. Pushing air into the patient’s lungs before releasing it.</p>
<p>My breath left me as well. The patient in the bed was me. “Am I,” trailing off, I changed my question, “Was any of that real? The desk? The brickmold? The tower?”</p>
<p>Migdalel answered, “You aren’t able to understand my answer yet. Trust in time all this will make sense. ‘With the Lord one day is as a thousand years’, as it has been written.” Migdalel continued, “The doctors refer to your body as ‘brain dead’. Silly. If they had half a brain between them—anyway, you weren’t brought here to listen to me. They’ll be disconnecting their life support machines soon, and what’s left of you on earth will follow your brain.”</p>
<p>I looked upon myself. My battered body. Brain dead. But not dead? Then I had a memory of a parable my Lord once taught of a man finding the truth about life only after death, like me, and he had asked for someone to be sent to tell his friends and family. Maybe, like in that story, it wouldn’t make a difference. But maybe.</p>
<p>A boldness welled up within me that I didn’t recognize. “Didn’t he raise Lazarus from the dead? Didn’t he say faith can move a mountain?Are there not more people to be reached here in life?”</p>
<p>“‘The harvest is plentiful, but the laborers are few’”, quoted Migdalel, “if you are asking for more time on earth, that is not for me to grant. It will be difficult, but you might lay back down into your body and pray to our Lord. I will wait with you”.</p>
<p>I laid myself down. After the out of body experience this “in body” experience was even more surreal. I heard doctors enter the room, but couldn’t see them. I couldn’t move my eyelids. They began disconnecting me. My breathing stopped. My heart was slowing down. I faced an impossible thing, to live in spite of dying. But I had faced an impossible task already. And this time, I was not alone. “Christ Jesus, please”, I began my prayer.</p>
]]></content:encoded></item><item><title><![CDATA[Has AI Taken My Job Yet?]]></title><description><![CDATA[AI will not change the world (the way you think it will). Exactly as the Internet did not change the world (the way early adopters thought it would).
Of course, our daily lives have drastically shifted in the 30 years since home internet connected us...]]></description><link>https://www.kallmanation.com/has-ai-taken-my-job-yet</link><guid isPermaLink="true">https://www.kallmanation.com/has-ai-taken-my-job-yet</guid><category><![CDATA[AI]]></category><category><![CDATA[chatgpt]]></category><category><![CDATA[Software Engineering]]></category><dc:creator><![CDATA[Nathan Kallman]]></dc:creator><pubDate>Tue, 19 Sep 2023 20:30:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1695155223669/41a245a0-682e-4275-9cfa-c70bcb79d09d.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>AI will not change the world (the way you think it will). Exactly as the Internet did not change the world (the way early adopters thought it would).</p>
<p>Of course, our daily lives have drastically shifted in the 30 years since home internet connected us. And of course, our lives 30 years from now will look even more different after consumer AI weasels its way into everything we touch.</p>
<p>But if you don't remember the late 1900s, the internet advent came with a different optimism. The quintessential phrase of the day, "The Information Super Highway!!", encapsulates all the hopes and expectations of the technology. We still echo those hopes when we wax on about the sheer knowledge available a tap away. With all those learnings we'll all make better decisions, self-educate throughout our lives, and society will grow greener each day. Now I hear the emptiness in our echoes. This is not The Internet I was promised. And AI will not become what the sirens sing of today.</p>
<h1 id="heading-it-all-started-at-babel">It all started at Babel</h1>
<p>You may already know, or you may be <a target="_blank" href="https://xkcd.com/1053/">today's lucky 10,000</a>, about the <a target="_blank" href="https://libraryofbabel.info/">Library of Babel</a>. Each time it makes a new round on social media there are a dozen reposts about how mind-blowing it is that everything ever written or that ever will be written already exists in the library.</p>
<p>Nearly every introduction to the Library of Babel leaves off at this unspoken existentialism. Implying that it could somehow change our lives if only we looked in the library for all the knowledge we are missing. They have not spent enough time in the library if they believe this, and it shows.</p>
<p>The creator of the library said <a target="_blank" href="https://libraryofbabel.info/theory4.html">this</a> after building a search function able to take us directly to the book and page for a given passage of text:</p>
<blockquote>
<p>Interestingly, this leaves the frustration of using the library unaltered. One can find only text one has already written, and any attempt to find it in among other meaningful prose is certain to fail. The tantalizing promise of the universal library is the potential to discover what hasn’t been written, or what once was written and now is lost. But there is still no way for us to find what we don’t know how to look for. Unless, of course, <a target="_blank" href="https://libraryofbabel.info/bookmark.cgi?thefrustrationofthelibrary">you’re brave enough to browse</a>...</p>
</blockquote>
<p>The reason the Library of Babel has not changed your life is because any useful knowledge is buried in a hopeless mire of gibberish.</p>
<p>Take this thought experiment: we are literally trapped in the Library of Babel, our world only consisting of bookshelf beside bookshelf with no escape, having never seen the sky outside. One day we miraculously find a page with the words "<a target="_blank" href="https://libraryofbabel.info/bookmark.cgi?theskyisorange">The sky outside is orange</a>". Astounding scientific discovery?! No, the statement "<a target="_blank" href="https://libraryofbabel.info/bookmark.cgi?theskyisblue100">The sky outside is blue</a>" must also exist within a different book, elsewhere in the library. The library contains all statements, both true and false. This is the nature of Universal Libraries. With no way to verify this statement, we can do nothing with the knowledge the library tried to bestow on us.</p>
<h1 id="heading-universal-libraries">Universal Libraries</h1>
<p>Such is the fate of all Universal Libraries. By containing information both useful and useless, only the librarians already knowing which books are useful can find a good book. By containing statements both true and false only the researchers already knowing what's true can find veracity. By containing stories both beautiful and ugly, only already discerning eyes can find the aesthetic.</p>
<p>Authorship bestows value to the works. Books, images, and songs are not worthwhile because they exist, but are valuable because they were authored. Out of all the sounds, of all the colors, of all the letters, this particular group was chosen and placed together; or more importantly, all the other combinations were removed, bringing focus to the worthwhile.</p>
<h1 id="heading-the-internet">The Internet</h1>
<p>It strikes me that the Internet is a Universal Library. Perhaps the most universal we've built to date. (of course, containing the entire Library of Babel makes it more universal than that library on its own). Like Bo Burnham sang in <em>Welcome to the Internet</em> from his 2021 special, <em>Bo Burnham: Inside</em>:</p>
<blockquote>
<p>Obama sent the immigrants to vaccinate your kids!</p>
</blockquote>
<p>Wait. No, that isn't the quote I was looking for. This library is too big. Here:</p>
<blockquote>
<p>Could I interest you in everything, all of the time? A little bit of everything, all of the time? [...] Anything and everything, all of the time</p>
</blockquote>
<p>It certainly feels as though everything is here. This article is here. The Library of Babel is here. Infinite scrolling on every page.</p>
<p>Let me propose a test for you though; for each thing you see, ask yourself three questions, and be honest with how much of the content you consume satisfies all three:</p>
<ol>
<li><p>Is this mostly new to me?</p>
</li>
<li><p>Is this useful to me?</p>
</li>
<li><p>Is this true? Or is it misleading?</p>
</li>
</ol>
<p>Be honest. Does 80% of the Internet satisfy just three questions? Even 20%? I was promised an "Information Super Highway" and instead I wade through an information swamp. The reins to unimaginable knowledge now bind me to a worthless merry-go-round of diversion.</p>
<p>Google boasts millions of results for every search, and yet I find the same shallow parroted lines behind every link. Worse than the Library of Babel, which mindlessly serves me gibberish, the Algorithms of the Internet have been crafted to serve me intentionally manipulative information: attempts to sway my votes and attempts to spend my dollars, mostly attempts at my dollars; never useful for <em>me</em> unless it's more useful to <em>them</em>.</p>
<p>And when you last searched, was there a <em>primary</em> source listed on the first page? I do not mean Wikipedia. I do not mean an article about news coverage of a new study. I mean the <em>actual source of information</em>. I find ten of the same rehashed summaries, probably written by a computer, and if I'm lucky one of them has linked to their source (which is paywalled and out of date).</p>
<p>We fell for the siren song of a Universal Library. Now I hear a remix crescendoing.</p>
<h1 id="heading-generative-ai">Generative AI</h1>
<p>So far, Artificial Intelligence (AI) manufactures uncanny mimicries of human intelligence. AI, like humans, <em>mostly</em> recombines information into non-novel restatements of the same. <em>Occasionally</em> an AI, like a human, forms a novel combination: emergent work from previous learnings. Of those emergent works, some portion are truthful (that is, the predictions drawn from them will match reality) and some portion are false.</p>
<p>Unlike a human, an AI has no means to assess its novel works. Like the librarians of Babel, the AI is trapped within its Universal Library: the Internet. It cannot know if the sky is blue or orange outside of words and images transmitted to it via the Internet.</p>
<p>When training human minds, our teachers take great care to bring new, useful, truthful information into the developing intelligence. Have we taken the same care when training artificial minds? Or do we blindly feast them on a Universal Library?</p>
<p>When done blindly, the AI becomes a larger Universal Library than the content it consumed. As it becomes more universal, <a target="_blank" href="https://futurism.com/ai-trained-ai-generated-data-interview">it paradoxically becomes less useful</a>. Interactions devolve into hallucinations and false citations; how could the AI know any better? That is all it experienced on the Internet. Employing the AI only gives legs to the Universal Library of the Internet. It is no more true than before. It is no more useful than before. The difference is that now it enacts its disheveled will without the filter of external curation.</p>
<h1 id="heading-what-will-change">What will change?</h1>
<p>We finally come to the biggest headline (that I see) from Generative AI advancements: "Programmers around the world, out of a job". Except, that headline <a target="_blank" href="https://www.kallmanation.com/nolow-code-why-hasnt-it-won">has been running for 30 years</a>. And it does not sound any different than the existential dread in the shallow sharing of those discovering the <a target="_blank" href="https://libraryofbabel.info/">Library of Babel</a>. They both have it wrong.</p>
<p>As I said, programmers have been "replaced by technology" since before I was born. How did I make a career doing something that shouldn't exist? How do I know I'll still exist despite AI "taking my job"?</p>
<p>The answer is simple. Each time a new technology enters meant to replace the skills of a professional, a new set of skilled professionals arises to interact with that technology. We saw it again this time around, before the ink on the "out of a job" headline dried, a new job title arose: <a target="_blank" href="https://en.wikipedia.org/wiki/Prompt_engineering">Prompt Enginee</a>r.</p>
<p>The skills we can see change; less documentation on syntax, perhaps a touch more psychology. But the jobs continue. And <em>why</em> these classes of "technical" people always arise is because the skills we <em>can't see</em> stay the same. A craving for learning things, tenacity when facing challenging puzzles, creatively bringing different solutions to the same situation.</p>
<p>So, until AI does <em>everyone's</em> jobs, there will be jobs titled <em>something</em> engineer to take care of the rest. I'll see you all there.</p>
]]></content:encoded></item><item><title><![CDATA[Double-entry Bookkeeping for Programmers]]></title><description><![CDATA[This is the article I wished existed when I needed to support accounting functions. Double-entry Bookkeeping may seem esoteric and unnecessarily complicated, but actually describes a simple framework for maintaining a correctible and auditable record...]]></description><link>https://www.kallmanation.com/double-entry-bookkeeping-for-programmers</link><guid isPermaLink="true">https://www.kallmanation.com/double-entry-bookkeeping-for-programmers</guid><category><![CDATA[Business and Finance ]]></category><category><![CDATA[Databases]]></category><category><![CDATA[graph database]]></category><dc:creator><![CDATA[Nathan Kallman]]></dc:creator><pubDate>Mon, 30 May 2022 11:12:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/bnW9O5ZOys4/upload/v1643059111150/9Bi7EzgZg.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is the article I wished existed when I needed to support accounting functions. Double-entry Bookkeeping may seem esoteric and unnecessarily complicated, but actually describes a simple framework for maintaining a correctible and auditable record of state changes.</p>
<p>This is not a practical guide on how to actually do modern accounting. It only looks at some mechanics of Accounting, not practical operating concerns.</p>
<h1 id="heading-defining-words">Defining words</h1>
<p><strong>Credits</strong> and <strong>Debits</strong>. Forget anything you think you know about what these words "mean". One of them isn't "positive" or "negative". Neither are "good" or "bad". They may as well be called left and right. They don't "mean" anything. They are only mirrors of one another. They only have words assigned to know their chirality.</p>
<p>Next up, <strong>Accounts</strong>. A confusing word from thousands of years of accounting. But I'll forgive them, naming things is hard. If a programmer were inventing accounting it would probably be called a type or category or collection. An Account is just a bucket giving a name to some money (distinguishing it from the other money being accounted for... oh wait, maybe that's why they're called accounts... who knew).</p>
<p><strong>Accounts</strong> come in two flavors: <strong>Credit-based</strong> and <strong>Debit-based</strong>. Describing which way all the Credits and Debits against the account should be added up. Credit-based Accounts subtract the sum of Debits from the sum of Credits (Σ Credits - Σ Debits). And following chirality, Debit-based Accounts subtract the sum of Credits from the sum of Debits (Σ Debits - Σ Credits). Note in accounting practice accountants often subdivide these two flavors into <a target="_blank" href="https://en.wikipedia.org/wiki/Double-entry_bookkeeping#Accounting_equation_approach">~5 main accounts</a> which are put into <a target="_blank" href="https://en.wikipedia.org/wiki/Accounting_equation">the accounting equation</a>. By convention, the asset-ish accounts are Debit-based and liability-ish accounts are Credit-based. Again, this article is not meant to delve into accounting practice; knowing the fundamental mechanics suffices for today.</p>
<p>To summarize:</p>
<ul>
<li><strong>Credit:</strong> The opposite of a Debit</li>
<li><strong>Debit:</strong>  The opposite of a Credit</li>
<li><strong>Account:</strong> A label for certain Credits &amp; Debits to be summarized together</li>
<li><strong>Credit-based Account:</strong> Account whose Total = Σ Credits - Σ Debits</li>
<li><strong>Debit-based Account:</strong>  Account whose Total = Σ Debits - Σ Credits</li>
</ul>
<p>Hopefully Double-entry Bookkeeping just became slightly more approachable. Slightly. Three words in five concepts form our core. But now let's actually use these things...</p>
<h1 id="heading-double-entry-bookkeeping-is-not-a-table">Double-entry Bookkeeping is NOT a Table</h1>
<p>Crack open any accounting book or introductory blog post. Not long into the text there will be tables looking like this:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Account</td><td>Credit</td><td>Debit</td></tr>
</thead>
<tbody>
<tr>
<td>Cash</td><td>42</td><td></td></tr>
<tr>
<td>Loan</td><td></td><td>42</td></tr>
</tbody>
</table>
</div><p>This is how double-entry bookkeeping gets its name; it takes two entries to record a single action. It may also be the most unintuitive way to present financial activity. </p>
<p>A layman's description would be that $42 was taken from Cash and given to (or paid to) the Loan. To and From... hmmm, let's rewrite that table:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>From Account</td><td>To Account</td><td>Amount</td></tr>
</thead>
<tbody>
<tr>
<td>Cash</td><td>Loan</td><td>42</td></tr>
</tbody>
</table>
</div><p>This format hints at a new way to break our thinking free from our table-based accounting mindset. What if we thought of our record as a graph?</p>
<h1 id="heading-double-entry-bookkeeping-is-a-graph">Double-entry Bookkeeping is a Graph</h1>
<p>Make each account a node and each credit/debit pair a single arrow (aka directed edge; credits/debits start/end the "arrow" respectively) with the amount (aka weight) and each account (aka node) involved. Here's what that weighted directed graph would look like:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1653850418582/5uiDAZ26B.png" alt="A graph with two nodes, labeled Cash and Loan, with an arrow pointing from the former to the latter labeled $42" /></p>
<p>Now you may have heard the rule of Double-entry Bookkeeping: "Every Credit must have a Debit and every Debit must have a Credit". Obviously, an arrow always has a start and an end (or to and from). So the graphical form of Double-entry Bookkeeping makes following the rules inescapable.</p>
<p>But how do we represent external movement of money? We do get paid for our work after all. Not only that, but I made five whole dollars from this blog. For a moment, let us relax that rule and let money come from nothing into our Cash account.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1653851081168/zEz5Wsi56.png" alt="A graph of two nodes, Cash and Loan, with two arrows coming from nothing entering Cash labeled $1000 and $100; also still having the arrow between Cash and Loan labeled $42" /></p>
<p>This works, but there is some unhelpful lack of information here. If we back this graph into a table, we might label the "from" account of those two new actions as <code>null</code>.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>From Account</td><td>To Account</td><td>Amount</td></tr>
</thead>
<tbody>
<tr>
<td>Cash</td><td>Loan</td><td>42</td></tr>
<tr>
<td>null</td><td>Cash</td><td>100</td></tr>
<tr>
<td>null</td><td>Cash</td><td>1000</td></tr>
</tbody>
</table>
</div><p>We don't have to label it as <code>null</code> though. In graph theory, we could label <code>null</code> as anything and the graph would be equivalent. Choosing something appropriately vague, we can draw an equivalent graph of our situation looking like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1653851100310/dsG6qMSH3.png" alt="A graph of three nodes, Outside, Cash, and Loan, with two arrows between Outside and Cash labeled $1000 and $100; also still having the arrow between Cash and Loan labeled $42" /></p>
<p>While these accounts are not particularly helpful (why did I get $1000? why the $100?) we have answered the question of how to represent money moving to and from external sources. Simply make up a new node (aka account)! Instead of one, vaguely named, "Outside" account, make one for each source we need to account for. And to round out our example, buy some food and set aside some money for later:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1653851135064/at828bF-k.png" alt="A graph of six nodes, labeled Employer, Side Hustle, Cash, Savings, McTaco King and Loan, with an arrow between Employer and Cash labeled $1000, an arrow between Side Hustle and Cash labeled $100, an arrow between Cash and Savings labeled $50, an arrow between Cash and McTaco King labeled $12, and an arrow between Cash and Loan labeled $42" /></p>
<p>Representing external parties as their own nodes brings some interesting qualities: Adding up our Employer and Side Hustle accounts results in total income while looking at them separately tells me which pays better. Same for expenses (McTaco King's looking a little over-visited); but also internal money maneuvering. Keeping an emergency fund, for example, can be viewed as a "loan" that is "repaid" when the fund is used and rebuilt; graphically there is no difference between a loan from an "internal" account and an "external" account (besides interest; I gave myself quite the deal).</p>
<h1 id="heading-but-keep-the-double-entry-book-in-a-table-anyway">But Keep the Double-entry Book in a Table Anyway</h1>
<p>While it is useful to <em>think</em> about our accounting as a graph it will not be useful to <em>see</em> our accounting as a graph after a year when we have 24 edges between <code>Employer</code> and <code>Cash</code> and 200 between <code>Cash</code> and <code>McTaco King</code> (don't judge me). </p>
<p>How about we backtrack our graph into tabular representations and see what we get out of it:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>From Account</td><td>To Account</td><td>Amount</td></tr>
</thead>
<tbody>
<tr>
<td>Cash</td><td>Loan</td><td>42</td></tr>
<tr>
<td>Employer</td><td>Cash</td><td>1000</td></tr>
<tr>
<td>Side Hustle</td><td>Cash</td><td>100</td></tr>
<tr>
<td>Cash</td><td>McTacoKing</td><td>12</td></tr>
<tr>
<td>Cash</td><td>Savings</td><td>50</td></tr>
</tbody>
</table>
</div><p>Being a table, shoving this data into SQL (or CSV or Excel) becomes trivial. But this format takes a little trickery to group and add up the account values correctly. If we return to the "classic" table form:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Account</td><td>Credit</td><td>Debit</td></tr>
</thead>
<tbody>
<tr>
<td>Cash</td><td>42</td><td></td></tr>
<tr>
<td>Loan</td><td></td><td>42</td></tr>
<tr>
<td>Employer</td><td>1000</td><td></td></tr>
<tr>
<td>Cash</td><td></td><td>1000</td></tr>
<tr>
<td>Side Hustle</td><td>100</td><td></td></tr>
<tr>
<td>Cash</td><td></td><td>100</td></tr>
<tr>
<td>Cash</td><td>12</td><td></td></tr>
<tr>
<td>McTaco King</td><td></td><td>12</td></tr>
<tr>
<td>Cash</td><td>50</td><td></td></tr>
<tr>
<td>Savings</td><td></td><td>50</td></tr>
</tbody>
</table>
</div><p>We can easily total this table's accounts using either <code>sum(Credit) - sum(Debit)</code> or <code>sum(Debit) - sum(Credit)</code> and grouping by the account. But we have traded the easier reading of what any one action has done by needing two lines instead of one for this easier aggregation.</p>
<p><em>Rabbit hole: if you want some psuedo-SQL on how this second table could be made from the first, it would probably look like this:</em></p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span>
   FromAccount <span class="hljs-keyword">AS</span> <span class="hljs-keyword">Account</span>,
   Amount <span class="hljs-keyword">AS</span> Credit,
   <span class="hljs-literal">NULL</span> <span class="hljs-keyword">AS</span> Debit
<span class="hljs-keyword">FROM</span> actions

<span class="hljs-keyword">UNION</span>

<span class="hljs-keyword">SELECT</span>
  ToAccount <span class="hljs-keyword">AS</span> <span class="hljs-keyword">Account</span>,
  <span class="hljs-literal">NULL</span> <span class="hljs-keyword">AS</span> Credit,
  Amount <span class="hljs-keyword">AS</span> Debit
<span class="hljs-keyword">FROM</span> actions
</code></pre>
<p><em>And what trickery queries these two tables for aggregation? Consider these two queries, which is simpler?</em></p>
<pre><code class="lang-sql"><span class="hljs-comment">-- To aggregate the "classic" table for the Cash account</span>
<span class="hljs-keyword">SELECT</span>
  <span class="hljs-keyword">SUM</span>(<span class="hljs-keyword">COALESCE</span>(Debit, <span class="hljs-number">0</span>)) - <span class="hljs-keyword">SUM</span>(<span class="hljs-keyword">COALESCE</span>(Credit, <span class="hljs-number">0</span>)) <span class="hljs-keyword">AS</span> CashTotal
<span class="hljs-keyword">FROM</span> actions
<span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">Account</span> = <span class="hljs-string">'Cash'</span>

<span class="hljs-comment">-- To aggregate the former table for the Cash account</span>
<span class="hljs-keyword">SELECT</span>
  <span class="hljs-keyword">SUM</span>(
    <span class="hljs-keyword">CASE</span>
      <span class="hljs-keyword">WHEN</span> ToAccount = <span class="hljs-string">'Cash'</span> <span class="hljs-keyword">THEN</span> Amount
      <span class="hljs-keyword">ELSE</span> -Amount
    <span class="hljs-keyword">END</span>
  ) <span class="hljs-keyword">AS</span> CashTotal
<span class="hljs-keyword">FROM</span> actions
<span class="hljs-keyword">WHERE</span> ToAccount = <span class="hljs-string">'Cash'</span>
<span class="hljs-keyword">OR</span> FromAccount = <span class="hljs-string">'Cash'</span>
</code></pre>
<p>What format should your financial system store these in? The answer (like many things) is it depends. But the beauty of our modern computing machines means we don't have to choose! We can store our history in one format and automatically transform it to any other whenever we find it convenient.</p>
<h1 id="heading-why-use-double-entry-bookkeeping-at-all">Why Use Double-entry Bookkeeping at All?</h1>
<p>You've read this whole article and have a better grasp of <em>what</em> Double-entry Bookkeeping is; but I haven't really spoken to <em>why</em> we want to use it. Sure we need to store history to report our finances and make sure we aren't going broke, but why not just keep a snapshot of each account's value at each change? We'll even record which external thing made the change, so we should be fine and can answer all of our accountant's questions, right? I'm here to tell you <strong>don't do that</strong>. <strong>Please do not do that</strong>. Double-entry Bookkeeping has been independently invented and re-invented across multiple cultures spanning up to 2000 years! Those inventors found multiple benefits from writing this way:</p>
<ol>
<li>Double-entries protect against mis-writing</li>
<li>Double-entries protect against mis-reading</li>
<li>Double-entries protect against arithmetic mistakes</li>
<li>Double-entries protect against damaged or incomplete records</li>
</ol>
<p>But all these advantages I believe are mostly erased by modern databases and computers; automatic-checksums and data integrity algorithms, automatic calculations that never mis-read or make mistakes with built-in error-correcting codes, machines counting cash, machines reading checks, machines communicating financial actions directly with one another. But I see benefits that still make record keeping this way a good idea:</p>
<ol>
<li>Correcting historical mistakes</li>
<li>Hypothetical alternate histories</li>
<li>Hypothetical futures</li>
<li>Flexible storage and migration options</li>
</ol>
<p>Breaking news: people still make mistakes. I guarantee that one day the accountants will come to you and say they've mis-recorded something and now they <strong>need</strong> it changed and that correction <strong>needs to be in the past</strong>. What will we do if we've only been keeping a snapshot of the account value at each step? Sure we insert the one new value in the past, but that makes <strong>all the subsequent history wrong!</strong> So then we have to correct <strong>all</strong> the ones following that correction; don't forget these accounts are live and being updated constantly. Good luck with that.</p>
<p>In a double-entry system, resolution is child's play: find the mistaken action and insert another action with the exact opposite to and from (or credit and debit) and mark that inserted action as happening at the same time as the original. Poof, it's like the mistake never happened (and yet, when asked, we can confirm that indeed that mistake was made and corrected appropriately).</p>
<p>And by its nature of never using mutation ("updates" are done with the insertion of new records), Double-entry Bookkeeping makes moving our data about simple. Have SQL and want NoSQL? Just copy the rows into documents and done. Things changed since the migration? Just check for the new records and copy those. Export to Excel; no problem. Even a text file with lines appended to it satisfies the requirements of our storage needs.</p>
<p>By making our data easy to migrate, we can copy data into a staging environment with  hypothetical records to test fixing history or to test potential futures. All made possible by Double-entry. And to those of you saying "just add X" or "modify Y" to a snapshot history, congratulations, you are the latest in a long line to re-invent double-entry bookkeeping. But go ahead, leave that comment, as if you needed my permission.</p>
<p><em>(Psst, if you want to practice these accounting principles, I am accepting donations at my <a target="_blank" href="https://www.buymeacoffee.com/kallmanation">Buy Me a Coffee</a>. Here's a little graph of what happens: <code>[You]-$4-&gt;[Me]</code>)</em></p>
<h1 id="heading-additional-reading">Additional Reading</h1>
<ul>
<li><a target="_blank" href="https://martin.kleppmann.com/2011/03/07/accounting-for-computer-scientists.html">Accounting for Computer Scientists</a></li>
<li><a target="_blank" href="https://arxiv.org/pdf/1407.1898.pdf">On Double-Entry Bookkeeping:
The Mathematical Treatment</a></li>
<li><a target="_blank" href="https://en.wikipedia.org/wiki/Debits_and_credits">Wikipedia: Debits and Credits</a></li>
<li><a target="_blank" href="https://en.wikipedia.org/wiki/Double-entry_bookkeeping#Accounting_equation_approach">Wikipedia: Accounting equation approach</a></li>
<li><a target="_blank" href="https://en.wikipedia.org/wiki/Accounting_equation">Wikipedia: Accounting equation</a></li>
<li><a target="_blank" href="https://en.wikipedia.org/wiki/Double-entry_bookkeeping#History">Wikipedia: Double-entry Bookkeeping history</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Hot Swapping Data Models]]></title><description><![CDATA[Patrik: "So, we need to track where our employees work. Can we do that?"Devin: "Like, just what store each person works at? Not too hard. Does anyone work at more than one store? Like a regional manager or something?"Patrik: "No. That's not possible....]]></description><link>https://www.kallmanation.com/hot-swapping-data-models</link><guid isPermaLink="true">https://www.kallmanation.com/hot-swapping-data-models</guid><category><![CDATA[Databases]]></category><category><![CDATA[data]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Nathan Kallman]]></dc:creator><pubDate>Fri, 13 May 2022 20:03:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/AcW1ZwD-qC0/upload/v1652278404521/FXU2Ylqf6.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Patrik</strong>: "So, we need to track where our employees work. Can we do that?"<br /><strong>Devin</strong>: "Like, just what store each person works at? Not too hard. Does anyone work at more than one store? Like a regional manager or something?"<br /><strong>Patrik</strong>: "No. That's not possible. Each employee works at exactly one store."<br /><strong>Devin</strong>: "Alright, easy enough. I'll work something up this week."  </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652277720676/TNwVgyDV8.png" alt="Diagram of an employee table in a many-to-one relationship with a stores table" /></p>
<p><strong>...Later...</strong><br /><strong>Patrik</strong>: "Devin! We've been loving the employee tracker you built, thanks. Just one little issue-"<br /><strong>Devin</strong>: "Let me guess."<br /><strong>Patrik</strong>: "We <em>do</em> need some employees to be marked as working at multiple stores. Can we make that change?"<br /><strong>Devin</strong>: <em> sigh </em> "Yeah, let me schedule a maintenance window to make the structural changes to the database-"<br /><strong>Patrik</strong>: "No, we can't have any downtime for this app. Everyone needs to run their reports.  We need to make the changes without a maintenance window."<br /><strong>Devin</strong>: "Uhhhhhh...."<br /><strong>Devin</strong>: <em> Googling </em> "How to make database changes with no downtime; Migrate tables without downtime; How to hot swap a data model" </p>
<p>Do not worry Devin! Changing models without downtime, while tedious, can be done rather easily. First, let us look at where we need to get to:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652277862438/_zRMGt8uB.png" alt="Diagram of an employee table in a many-to-many relationship with a stores table via a employeestores join table" /></p>
<p>And remember, where we are:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652277720676/TNwVgyDV8.png" alt="Diagram of an employee table in a many-to-one relationship with a stores table" /></p>
<p>Patrik's requirements pair up to be a one-two punch: change the structure of our database, carry the pre-existing data into the new model, and all without any interruption of service. I have used six simple steps to accomplish just that, more than once, where there could be zero interruption of service and there could be zero data loss or corruption. Here is how I do it:</p>
<ol>
<li>Implement new models</li>
<li>Write to both models</li>
<li>Backfill old to new</li>
<li>Read from new models</li>
<li>Stop writing to old models</li>
<li>Remove old models</li>
</ol>
<p>Or IWBRSR for short (alright, alright <a target="_blank" href="https://www.kallmanation.com/stahp">I'll stop</a>). That's it. That's the whole article. Simple, but effective.</p>
<p>Working through Devin's conundrum, our six steps would work out like this. First, Devin must add the join table we need for the many-to-many relationship between <code>employees</code> and <code>stores</code>. However, Devin does not remove the old foreign key from <code>employees</code> to <code>stores</code> (yet).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652277751125/eyJC-2AHX.png" alt="Diagram of an employees table in a many-to-one relationship with a stores table while also being in a many-to-many relationship via a employeestores join table" /></p>
<p>With this database structure change made; Devin now begins adjusting the code wherever an insert, update, or delete is made that affects the foreign key from <code>employees</code> to <code>stores</code> to also insert, update, or delete a corresponding <code>employeestores</code> record. (These code changes do not even need to all be in a single release; anything not correctly mirrored between the old and new models will be handled by the next step).</p>
<p>After those changes, Devin has completed steps 1 and 2. Devin can now prepare a backfill. For each relationship that the old foreign key has that the new join table does not have, Devin will create a row on the join table between <code>employees</code> and <code>stores</code>. Since all new activity keeps the relationships that the new join table and the old foreign key have in sync (from the code changes of step 2), once Devin completes this backfill the old model and the new model are perfect mirrors and will stay perfect mirrors of each others' data.</p>
<p>At this point, it may be a good idea to pause for a step 3.5 where we inspect the database to make sure the new and old models are in fact perfect mirrors of each other (like I just claimed).</p>
<p>Once satisfied, we can move on to step 4 and the work will be downhill from here. Devin begins making code changes wherever <code>employees</code> and <code>stores</code> are queried to use the new join table instead of the old foreign key. (Like step 2, this also does not need to be released at once; since the old and new models mirror one another, reading data from one gives the same results as reading from the other until step 5).</p>
<p>Step 5, Devin is almost done! At this point, the new model effectively runs the application (being written to and read from). Devin can safely go to those places they changed in step 2 and remove the inserts, updates, and deletes to the old foreign key. (Again, this does not need to be done in a single release).</p>
<p>To put the cherry on top, Devin goes in and drops the old foreign key column from <code>employees</code>. Completing step 6 and leaving Patrik with what they wanted, with absolutely no downtime!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1652277862438/_zRMGt8uB.png" alt="Diagram of an employee table in a many-to-many relationship with a stores table via a employeestores join table" /></p>
<hr />
<p>Now, this was a somewhat contrived and simple example. When doing this for real many, many complications can arise in each step. But, the steps are exactly the same! They work well because each step can be done in parts and redone, little by little, iteration by iteration, until the step works correctly and the next step can be undertaken. So remember, IWBRSR:</p>
<ol>
<li>Implement new models</li>
<li>Write to both models</li>
<li>Backfill old to new</li>
<li>Read from new models</li>
<li>Stop writing to old models</li>
<li>Remove old models</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Attracting & Retaining in a Boring Industry]]></title><description><![CDATA[I work in a boring industry. Not only do I work in insurance (already boring) I work in claims handling. I know, just writing those words some of you already clicked away. For a fun party trick, answer the what do you do question with "I'm a software...]]></description><link>https://www.kallmanation.com/attracting-and-retaining-in-a-boring-industry</link><guid isPermaLink="true">https://www.kallmanation.com/attracting-and-retaining-in-a-boring-industry</guid><category><![CDATA[hiring]]></category><category><![CDATA[Productivity]]></category><dc:creator><![CDATA[Nathan Kallman]]></dc:creator><pubDate>Tue, 10 May 2022 12:18:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/jZi0Ih47EDY/upload/v1652107751069/APC-_U8VB.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I work in a boring industry. Not only do I work in insurance (already boring) I work in claims handling. I know, just writing those words some of you already clicked away. For a fun party trick, answer the what do you do question with "I'm a software engineer handling insurance claims" and watch eyes glaze faster than a Krispy Kreme conveyor belt.</p>
<p>Despite the stigma, I've been here for several years. And plan to be here for more. Why?</p>
<p><strong>Because it has not been boring to me.</strong></p>
<p>And I'll tell you why it has not bored me. From my experiences and observations, these are some things that will attract and retain developers and engineers.</p>
<div class="hn-embed-widget" id="come-work-with-me"></div><h2 id="heading-tell-the-stories-of-their-work">Tell the Stories of their Work</h2>
<p>A boring industry's measurements of performance are usually boring. "Oh how interesting, loss ratio has gone down from 87% to 82%", thought no one. Hearing how average claim times dropped from 5 days to 3 days only grabs your attention so many times.</p>
<p>We especially want our work to feel like we have an impact on our world. We feel rewarded when we do good, more than just bagging a big paycheck. Stories like Sharonne keeping her job because she didn't miss a shift while her car was repaired are powerful. Kyle was able to make rent because he was paid today, not in 30 days (a typical, but unacceptable, turn-around time in claims). A criminal organization getting busted on their intricately fabricated fraud scheme? That's better than a movie. </p>
<p>Tell us those stories. Keep us engaged and our restless minds want to stay put.</p>
<h2 id="heading-let-them-work-on-hard-problems">Let them work on hard problems</h2>
<p>Nothing may be more soul sapping to the mind of a technical person than having to do the same thing twice. Our brains crave challenge. If we are not challenged at work, we will find a challenge somewhere else.</p>
<p>Too often boring industries bore us because they lack vision. Decisions are made at only the highest level. And often those decision makers only want to change a few metrics they already measure by a few percentage points. Any engineer or developer faced with uninspired and unqualified prescriptions from above finds a new environment that has inspiration with decisions made by qualified, intelligent people.</p>
<p>Hire people who are smart enough to recognize problems and build solutions at every level, from small to big. Then give them the power and trust to do what they do.</p>
<h2 id="heading-let-them-work-on-other-things">Let them work on other things</h2>
<p>Sometimes, the field truly does not have enough challenges for all your employees all the time. This is true and a fair point. But we can still satisfy the need for challenge.</p>
<p>One thing I've long loved about working at <a target="_blank" href="https://root.engineering">Root</a> are the "Hack Days". Every 3 months, for 3 days, engineers are left to work on whatever they like. Read books or whitepapers or blog posts. Build a minigame hidden in the app. Make a tool we use daily just a little bit better. Play with the Javascript framework of the week. Whatever we feel is best for our personal/professional improvement, do it!</p>
<h2 id="heading-invest-in-them">Invest in them</h2>
<p>Expanding on the last point. Make sure to grow talent, not just use the talent. In the calculus developers and engineers use to decide where to work next, after salary and personal fulfillment, choosing environments that advance our skills and career will take precedence.</p>
<p>Give us a stipend for books. Recommend books and articles. Let them spend some of their time presenting and discussing topics. All these things not only make us better at our jobs; but entices us to stay to keep growing.</p>
<h2 id="heading-pay-them">Pay them</h2>
<p>An obvious and critical factor. I did not list this earlier because even with higher salary than any competitor, failing in other areas will completely sabotage the benefits of higher pay. A massively overpaid employee may still leave if they become so unhappy with their employment that they cannot enjoy the money they have made. Pay alone does not retain or even attract talent. But doing everything else right without good compensation makes those all hollow and worthless. So do pay well.</p>
<p>Also, be honest about compensation. Do not make us bargain and bicker and negotiate persistently just to be paid like our peers. We will find out the new hires are being paid more than us. We will leave. How much does hiring anew cost? No, those are just hard costs; we have not counted the brain drain and morale effects yet. How much does hiring a replacement actually cost? Paying us half that and pocketing the difference might just do wonders for keeping us around. Unlike hardware and software, wetware cannot be replaced so easily.</p>
<h2 id="heading-share-profits">Share Profits</h2>
<p>While we're here, compensating the creation of software with a fixed amount of dollars has always been strange to me. In one afternoon, I'll write something that nets millions of dollars per year (every year, year after year, for decades). The next, nothing, because my idea turned out to be garbage (I have a lot of bad ideas).</p>
<p>And software often provides much more value as a whole than the sum of its parts. So why would we only pay a simple salary to each developer or engineer? How important is that signup page if there's no features afterwards? How valuable is your service if the login is totally insecure?</p>
<p>Rewarding us all when we all have done well builds the trust and incentives to stay and continue performing well.</p>
]]></content:encoded></item><item><title><![CDATA[S T A H P]]></title><description><![CDATA[Just STAHP already!
SomeTimesAcronyms (and Abbreviations)HurtProductivity
Sometimes
Before I rant too much, all right, yes. Some acronyms are OK and even helpful. Because (hopefully):

Acronyms (should) save effort in communication.

But whose effort...]]></description><link>https://www.kallmanation.com/stahp</link><guid isPermaLink="true">https://www.kallmanation.com/stahp</guid><category><![CDATA[Productivity]]></category><category><![CDATA[communication]]></category><dc:creator><![CDATA[Nathan Kallman]]></dc:creator><pubDate>Fri, 11 Mar 2022 16:23:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/81QkOoPGahY/upload/v1646941566459/SIv2XC9kV.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-just-stahp-already">Just STAHP already!</h1>
<p><strong>S</strong>ome<br /><strong>T</strong>imes<br /><strong>A</strong>cronyms (and Abbreviations)<br /><strong>H</strong>urt<br /><strong>P</strong>roductivity</p>
<h1 id="heading-sometimes">Sometimes</h1>
<p>Before I rant too much, all right, yes. <em>Some</em> acronyms are <a target="_blank" href="https://en.wiktionary.org/wiki/oll_korrect#English">OK</a> and even helpful. Because (hopefully):</p>
<blockquote>
<p>Acronyms (should) save effort in communication.</p>
</blockquote>
<p>But whose effort? Communication requires the effort of two parties; senders and recipients (and for most conversations we obviously switch roles rapidly between sending and receiving texts/calls/etc.).</p>
<p>"Good" acronyms save effort from both the sender and the receiver. These are often well known acronyms like ASAP or <a target="_blank" href="https://www.kallmanation.com/lets-re-acronym-kiss">KISS</a>. They are quick to say; quick to write; and quickly understood because of how widespread and memorable they are (being memorable and not too specific makes them widely useable).</p>
<p>All acronyms save a minuscule amount of effort from the sender. But many drastically increase the effort of the receiver (as they mentally translate; or worse go dig through some archaic document of abbreviations; or costliest of all, ask someone the meaning).  These are the "bad" ones that I'd like you to STAHP making up. Just remember when weighing the costs and benefits of your acronym, in business, most communication has more recipients than senders. So you should probably STAHP.</p>
<h1 id="heading-acronyms-and-abbreviations-hurt">Acronyms (and Abbreviations) Hurt</h1>
<p>Each acronym and abbreviation we use adds one more point of confusion and catching up for new members of our teams. After a while, new people nearly learn a whole new language of words in order to communicate with you. Not only that, but acronyms aren't unambiguous even in the same business. Imagine reading this in your email inbox:</p>
<blockquote>
<p>PR repo ea Mon by ISO for REV to be uploaded to GH</p>
</blockquote>
<p>👁👄👁</p>
<p>What? Pull Requests repossessed each Month by the International Standards Organization for review will be in Github?</p>
<p>No, no, no. <em>Obviously</em> I'm telling you that the public relations report each Monday by the Internal Service Organization for Recruitment, Engagement, and Volunteerism will be uploaded to Greenhouse. Duh.</p>
<p>This example may be a bit over-contrived. But it also falls painfully close to the reality of corporate communication. I wonder how much time we waste each day explaining and decoding these acronyms instead of spending a fraction of a second to say what we mean (and mean what we say). So just STAHP.</p>
<h1 id="heading-productivity">Productivity</h1>
<p>I will accept some acronym use. But please, STAHP inventing new abbreviations and acronyms for every word you say! It usually wastes more time than it saves. This concludes my rant, thank you for reading (and STAHP'ing 😅).</p>
<hr />
<p>Do you want someone to STAHP? Here's a markdown STAHP linked to this article for you to use:</p>
<pre><code>[<span class="hljs-string">STAHP</span>](<span class="hljs-link">https://kallmanation.com/stahp</span>)
</code></pre>]]></content:encoded></item><item><title><![CDATA[Hashnode + DEV]]></title><description><![CDATA[In the midst of taking a hiatus from writing, I've decided to level up my setup to post on a personal blog at my own domain (www.kallmanation.com). I wanted to accomplish a few things:

Have control of where my content is published
Have a backup of m...]]></description><link>https://www.kallmanation.com/hashnode-dev</link><guid isPermaLink="true">https://www.kallmanation.com/hashnode-dev</guid><category><![CDATA[writing]]></category><dc:creator><![CDATA[Nathan Kallman]]></dc:creator><pubDate>Mon, 24 Jan 2022 20:38:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/vcF5y2Edm6A/upload/v1643055940731/UcTShG6hM.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the midst of taking a hiatus from writing, I've decided to level up my setup to post on a personal blog at my own domain (<a target="_blank" href="https://kallmanation.com">www.kallmanation.com</a>). I wanted to accomplish a few things:</p>
<ol>
<li>Have control of where my content is published</li>
<li>Have a backup of my articles</li>
<li>Easily post to both my own domain and still to dev.to</li>
<li>Keep up my Web Monetization that I had on DEV</li>
</ol>
<p>I found that in <a target="_blank" href="https://hashnode.com">Hashnode</a>. Hashnode has settings to automatically backup every post I publish on their platform in my own <a target="_blank" href="https://github.com/kallmanation/kallmanation">github repo</a> (giving me number 2 and a little 1); while also letting me host my blog on my own domain (number 1). And as a bonus, my posts will also come up in the main <a target="_blank" href="https://hashnode.com">Hashnode feed</a>. They even have out-of-the-box support for Web Monitization!</p>
<p>To cover the cross-posting, DEV (and the forem platform) is fantastic about this. Simply go to the <a target="_blank" href="https://dev.to/settings/extensions">extension settings</a> and set the RSS feed to my Hashnode blog's RSS and check the <code>Mark the RSS source as canonical URL by default</code> option (this will give me the internet juice for any interaction on DEV, while making it nice to DEV users to simply read on their preferred platform).</p>
<p>Now that I've copied all my historical posts from DEV to Hashnode, I hope to blog a little more (maybe not every week though). This post serves as something of a test run of my cross-posting setup. Follow me on DEV or Hashnode and I'll see you around!</p>
]]></content:encoded></item><item><title><![CDATA[Install PWAs in Vivaldi]]></title><description><![CDATA[PWAs are coming to desktop! If you use Vivaldi (like me), then you too can join the PWA gang.
Update: PWAs are now fully supported and no longer "experimental"; you can skip this first step and go right to the installation.
As of today, you'll need t...]]></description><link>https://www.kallmanation.com/install-pwas-in-vivaldi</link><guid isPermaLink="true">https://www.kallmanation.com/install-pwas-in-vivaldi</guid><category><![CDATA[PWA]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[Browsers]]></category><dc:creator><![CDATA[Nathan Kallman]]></dc:creator><pubDate>Thu, 02 Sep 2021 13:32:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643050053296/7vjnChLC6.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>PWAs are coming to desktop! If you use <a target="_blank" href="https://vivaldi.com">Vivaldi</a> (like me), then you too can join the PWA gang.</p>
<p><em>Update: PWAs are now <a target="_blank" href="https://vivaldi.com/blog/vivaldi-gets-more-private-delivers-an-all-new-capture-pwa-support/">fully supported</a> and no longer "experimental"; you can skip this first step and go right to the installation.</em></p>
<p>As of today, you'll need to enable the experimental feature allowing installation by visiting <a target="_blank">vivaldi://experiments/</a> and checking the box:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1630589211154/p2tEk1MWp.png" alt="Box labeled &quot;Menu entries for installing Progressive Web Apps.&quot; should be checked" /></p>
<p>Once that's done, visit the PWA you'd like to install, like <a target="_blank" href="https://meet.google.com">Google meet</a>. Right click the tab and in the context menu there should now be an option like "Install Google Meet...". Click it and congratulations! Welcome to the future built of Progressive Web Apps.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1630589379077/kfEl9tMgk.png" alt="Screenshot of context menu revealed from right clicking a PWA capable tab in Vivaldi" /></p>
]]></content:encoded></item><item><title><![CDATA[De Morgan's Theorem]]></title><description><![CDATA[In my last post, I hinted that De Morgan's Theorem makes "boolean code more readable" but that "we'll talk more about that later". Later is now! Let's talk about De Morgan's Theorem (or De Morgan's Laws).
De Morgan's Theorem comes in two parts, each ...]]></description><link>https://www.kallmanation.com/de-morgans-theorem</link><guid isPermaLink="true">https://www.kallmanation.com/de-morgans-theorem</guid><category><![CDATA[Beginner Developers]]></category><category><![CDATA[learning]]></category><category><![CDATA[Computer Science]]></category><dc:creator><![CDATA[Nathan Kallman]]></dc:creator><pubDate>Tue, 27 Apr 2021 15:14:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643054776071/VObF6i-rY.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In my <a target="_blank" href="https://www.kallmanation.com/all-you-need-is-nand-nand-nand-nand-is-all-you-need">last post</a>, I hinted that De Morgan's Theorem makes "boolean code more readable" but that "we'll talk more about that later". Later is now! Let's talk about De Morgan's Theorem (or <a target="_blank" href="https://en.wikipedia.org/wiki/De_Morgan's_laws">De Morgan's Laws</a>).</p>
<p>De Morgan's Theorem comes in two parts, each in the same pattern:</p>
<pre><code class="lang-js">!(a &amp;&amp; b) == !a || !b
!(a || b) == !a &amp;&amp; !b
</code></pre>
<h1 id="heading-why-does-this-work">Why Does This Work?</h1>
<p>It may not be immediately clear <em>why</em> this works. Why can we just swap between <code>and</code> 'ing, and <code>or</code> 'ing, if we twiddle with the <code>not</code> 's? One way to think about it is that <code>and</code>, and <code>or</code> are "twiddled" opposites of each other:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>X</td><td>Y</td><td>=</td><td>AND</td><td>OR</td></tr>
</thead>
<tbody>
<tr>
<td>false</td><td>false</td><td>=</td><td>false</td><td>false</td></tr>
<tr>
<td>false</td><td>true</td><td>=</td><td>false</td><td>true</td></tr>
<tr>
<td>true</td><td>false</td><td>=</td><td>false</td><td>true</td></tr>
<tr>
<td>true</td><td>true</td><td>=</td><td>true</td><td>true</td></tr>
</tbody>
</table>
</div><p><code>and</code> has three falses and a true while <code>or</code> has three trues and a false, and the order is also "twiddled" to be reversed.</p>
<p>First <code>not</code> 'ing the input can be thought of as reversing the order of the output:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>NOT X</td><td>NOT Y</td><td>=</td><td>AND</td></tr>
</thead>
<tbody>
<tr>
<td>true</td><td>true</td><td>=</td><td>true</td></tr>
<tr>
<td>true</td><td>false</td><td>=</td><td>false</td></tr>
<tr>
<td>false</td><td>true</td><td>=</td><td>false</td></tr>
<tr>
<td>false</td><td>false</td><td>=</td><td>false</td></tr>
</tbody>
</table>
</div><p>Now we can see this output is the exact opposite of an <code>or</code> against the opposite inputs. So:</p>
<pre><code class="lang-js">!(!a &amp;&amp; !b) == a || b
</code></pre>
<p>Which is just a rearranged version of one of the relationships above:</p>
<pre><code class="lang-js">!a &amp;&amp; !b == !(a || b)
</code></pre>
<p>And giving the same treatment to <code>or</code> first instead of <code>and</code> first will yield the other relationship.</p>
<h1 id="heading-but-intuitively-why-does-this-work">But Intuitively Why Does This Work?</h1>
<p>Well, if an apple is not red and not green, would you say that apple is also not red or green? You probably did not even question that those two statements are equivalent: <code>not red and not green</code> has the same meaning as <code>not red or green</code>. But that's exactly the De Morgan's Theorem we worked through above!</p>
<pre><code class="lang-js">!red &amp;&amp; !green == !(red || green)
</code></pre>
<p>And if that apple was not not red nor not green; you may eventually see through my double negatives that I mean to say the apple is red and green.</p>
<pre><code class="lang-js">!(!red || !green) == red &amp;&amp; green
</code></pre>
<p>I propose that one of those statements is much more readily understood (both in code and in english). And why De Morgan's Theorem is worth remembering: cleaning up sometimes confusing boolean expressions.</p>
<h1 id="heading-whats-this-to-do-with-wireworld">What's This To Do With Wireworld?</h1>
<p>Look again at the NAND from the <a target="_blank" href="https://www.kallmanation.com/all-you-need-is-nand-nand-nand-nand-is-all-you-need">last post</a> in this series.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643054771409/MPi2-rOehk.png" alt="Image of NAND in Wireworld" /></p>
<p>Now let me show you what an OR and a NOT look like in Wireworld.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643054772873/TohXS26Ko.png" alt="Image of OR in Wireworld" />
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643054774307/yLEbqlweJ1.png" alt="Image of NOT in Wireworld" /></p>
<p>Look back to the NAND and do you see the pattern? My NAND is actually just an OR with its inputs first NOT'ed. That's exactly what De Morgan told us!</p>
<pre><code class="lang-js">!(a &amp;&amp; b) == !a || !b
</code></pre>
]]></content:encoded></item><item><title><![CDATA[All you need is NAND, NAND, NAND; NAND is all you need!]]></title><description><![CDATA[All animations courtesy of my Wireworld built in Svelte

If you've spent any time programming, you may recognize the "big three" boolean operators: AND, OR, and NOT (often written &&, ||, ! in many programming languages). Together these operators are...]]></description><link>https://www.kallmanation.com/all-you-need-is-nand-nand-nand-nand-is-all-you-need</link><guid isPermaLink="true">https://www.kallmanation.com/all-you-need-is-nand-nand-nand-nand-is-all-you-need</guid><category><![CDATA[Beginner Developers]]></category><category><![CDATA[Computer Science]]></category><category><![CDATA[learning]]></category><dc:creator><![CDATA[Nathan Kallman]]></dc:creator><pubDate>Mon, 19 Apr 2021 14:21:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643054547383/_1Oi6e-ni.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>All animations courtesy of my <a target="_blank" href="https://www.kallmanation.com/wireworld-svelte-edition">Wireworld built in Svelte</a></em></p>
<hr />
<p>If you've spent any time programming, you may recognize the "big three" boolean operators: AND, OR, and NOT (often written <code>&amp;&amp;</code>, <code>||</code>, <code>!</code> in many programming languages). Together these operators are <a target="_blank" href="https://en.wikipedia.org/wiki/Functional_completeness">functionally complete</a>, meaning they can be used to build <em>any</em> boolean logic. Combined with some sort of memory, functionally complete operators can be used to build a <a target="_blank" href="https://en.wikipedia.org/wiki/Turing_completeness">turing complete</a> system.</p>
<p>What if I told you we need only one operator to be functionally complete? That's right! All you need is NAND (short for Not AND; <code>!(x &amp;&amp; y)</code>). But don't trust me, let me show you.</p>
<h1 id="heading-proving-nand-is-all-you-need">Proving NAND is all you need</h1>
<h2 id="heading-nand-is-not">NAND is NOT</h2>
<p>You've seen the animation of a NAND in a Wireworld above (NAND-imation anyone?). What would happen if instead of two inputs, we tied them both together as one input?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643054538294/mRP85DZX1.gif" alt="Animation of NAND acting as a NOT" /></p>
<p>Notice how this new operator constantly outputs until an input is received and then it stops (aka NOT logic). In JavaScript, if we had a <code>nand(x, y)</code> function, we could make <code>not()</code> like this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> not = <span class="hljs-function">(<span class="hljs-params">x</span>) =&gt;</span> nand(x, x);
</code></pre>
<p>Look at NAND's truth table where both inputs are the same:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>X</td><td>Y</td><td>=</td><td>O</td></tr>
</thead>
<tbody>
<tr>
<td>false</td><td>false</td><td>=</td><td>true</td></tr>
<tr>
<td>true</td><td>true</td><td>=</td><td>false</td></tr>
</tbody>
</table>
</div><p>Looks a lot like NOT to me! <strong>NAND is NOT.</strong></p>
<h2 id="heading-nand-is-and">NAND is AND</h2>
<p>Look at NAND's full truth table compared to AND:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>X</td><td>Y</td><td>=</td><td>NAND</td><td>AND</td></tr>
</thead>
<tbody>
<tr>
<td>false</td><td>false</td><td>=</td><td>true</td><td>false</td></tr>
<tr>
<td>false</td><td>true</td><td>=</td><td>true</td><td>false</td></tr>
<tr>
<td>true</td><td>false</td><td>=</td><td>true</td><td>false</td></tr>
<tr>
<td>true</td><td>true</td><td>=</td><td>false</td><td>true</td></tr>
</tbody>
</table>
</div><p>It's perfectly inverse (that is the definition of Not AND after all). If only we could NOT the output of our NAND, we would have AND... if you read the last section, that's exactly what we have! Just put the output of a NAND as both inputs to another NAND and viola! an AND appears:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643054540926/Waml3hExF.gif" alt="Animation of two NANDs acting as an AND" /></p>
<p>Again to relate this to code it might look something like this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> and = <span class="hljs-function">(<span class="hljs-params">x, y</span>) =&gt;</span> nand(nand(x, y), nand(x, y));
</code></pre>
<h2 id="heading-nand-is-or">NAND is OR</h2>
<p>Perhaps the trickiest one yet. AND and NOT were fairly obvious looking at their truth tables. But OR?</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>X</td><td>Y</td><td>=</td><td>NAND</td><td>OR</td></tr>
</thead>
<tbody>
<tr>
<td>false</td><td>false</td><td>=</td><td>true</td><td>false</td></tr>
<tr>
<td>false</td><td>true</td><td>=</td><td>true</td><td>true</td></tr>
<tr>
<td>true</td><td>false</td><td>=</td><td>true</td><td>true</td></tr>
<tr>
<td>true</td><td>true</td><td>=</td><td>false</td><td>true</td></tr>
</tbody>
</table>
</div><p>OR is tantalizingly close to NAND. They both have three outputs of <code>true</code> and only one case of <code>false</code>. But the tables are backwards! What are we supposed to do with this? What if I showed you a table with the opposite inputs:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>NOT X</td><td>NOT Y</td><td>=</td><td>OR</td></tr>
</thead>
<tbody>
<tr>
<td>true</td><td>true</td><td>=</td><td>true</td></tr>
<tr>
<td>true</td><td>false</td><td>=</td><td>true</td></tr>
<tr>
<td>false</td><td>true</td><td>=</td><td>true</td></tr>
<tr>
<td>false</td><td>false</td><td>=</td><td>false</td></tr>
</tbody>
</table>
</div><p>Does that pattern look familiar? If we NOT our inputs first (using the same NAND as NOT trick) and put those as the inputs to our NAND, we'll get an OR!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643054545343/Df9kJsGDz.gif" alt="Animation of three NANDs acting as an OR" /></p>
<p>We'll finish out our code examples:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> or = <span class="hljs-function">(<span class="hljs-params">x, y</span>) =&gt;</span> nand(nand(x, x), nand(y, y));
</code></pre>
<p>_(We just did <a target="_blank" href="https://en.wikipedia.org/wiki/De_Morgan's_laws">DeMorgan's Theorem</a> a_ very <em>cool trick for making boolean code more readable; but we'll talk more about that later)</em></p>
<h2 id="heading-nand-is-all-you-need">NAND is all you need</h2>
<p>If you accept that AND, OR, and NOT are functionally complete, since NAND can build each of those three things, NAND, all by itself, is functionally complete!</p>
<p>So yes, NAND <em>is</em> all you need... but please don't write all your booleans using only NANDs. This is for your edification and education.</p>
<h1 id="heading-why-should-i-care">Why should I care?</h1>
<h2 id="heading-problem-transformations-solve-problems">Problem Transformations Solve Problems</h2>
<p>What is the difference between a Bubble Sort and a Quick Sort? They will both give an equally sorted output after all. The importance comes in performance. Because while they are <em>logically equivalent</em> they are not <em>operationally equivalent</em>; one of them will finish faster when a physical computer executes the sort.</p>
<p>Many, <em>many</em> things in the business of software development see drastic improvements from transforming the physical operations while keeping logical equivalence. The better we become at this skill, the better programmers we become. (One way to become better is seeing examples, like this).</p>
<h2 id="heading-nand-runs-the-world">NAND Runs the World</h2>
<p>It just so happens that in real-world computer chips NAND is one of the easiest things to build. If you've ever heard of <a target="_blank" href="https://searchstorage.techtarget.com/definition/3D-NAND-flash">3D NAND</a>, you now know what that NAND stands for. And when your code works the first time around, thank a NAND gate.</p>
<h2 id="heading-its-just-cool">It's Just Cool!</h2>
<p>Who would have thought that just one operator can run the whole world?</p>
]]></content:encoded></item><item><title><![CDATA[Wireworld! Svelte Edition]]></title><description><![CDATA[This is Wireworld! Sorry, you don't know what a Wireworld is? How dare you not intimately know something I learned a few minutes ago!
A Wireworld is similar to the famous Game of Life. An infinite world of square cells, each in a distinct, finite sta...]]></description><link>https://www.kallmanation.com/wireworld-svelte-edition</link><guid isPermaLink="true">https://www.kallmanation.com/wireworld-svelte-edition</guid><category><![CDATA[Svelte]]></category><category><![CDATA[ShowHashnode]]></category><category><![CDATA[Games]]></category><dc:creator><![CDATA[Nathan Kallman]]></dc:creator><pubDate>Wed, 14 Apr 2021 14:49:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643053320750/Q_oFqSJwV.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is Wireworld! Sorry, you don't know what a Wireworld is? How dare you not intimately know something I learned a few minutes ago!</p>
<p>A Wireworld is similar to the famous <a target="_blank" href="https://playgameoflife.com">Game of Life</a>. An infinite world of square cells, each in a distinct, finite state. But unlike boring life that has only two states: alive and dead; Wireworld has four! That's like... twice as good?</p>
<p>A Wireworld cell could either be: nothing, a conductor (wire), an electron tail, or an electron head. An electron head always becomes an electron tail which always becomes a wire, while a wire will stay a wire unless exactly one or two neighbors are an electron head, in which case it will follow suit (and nothing continues to be nothing).</p>
<p>You can see those rules in action in the animation above. You can also play with those rules over here: <a target="_blank" href="https://wireworld.klmntn.com">wireworld.klmntn.com</a> (warning, it's only somewhat useable on mobile)</p>
<h1 id="heading-the-making-of">The Making Of</h1>
<p>Every three months, <a target="_blank" href="https://root.engineering">Root</a> sets aside three days (called "hack days") for all its engineers to work on something of their choice. This time around I wanted to have some fun and also learn a little about <a target="_blank" href="https://svelte.dev">Svelte</a> (Root mostly uses React &amp; React Native). So I chose to make a browser-based Wireworld using Svelte! (<a target="_blank" href="https://github.com/kallmanation/wireworld">repo</a>)</p>
<h2 id="heading-decisions-decisions-decisions">Decisions, Decisions, Decisions</h2>
<h3 id="heading-framework">Framework</h3>
<p>How did I settle on Svelte? I'm already working in React and Vue and have worked a little with Ember long ago (I've even played with the now abandoned <a target="_blank" href="https://github.com/intercellular/cell">Cell.js</a>). Angular seems to be a different flavor of the React/Vue/Ember gang. Svelte though looks to have some novel ideas that I wanted to expose myself to.</p>
<h3 id="heading-graphics">Graphics</h3>
<p>There's really only three options for displaying anything on web:</p>
<ol>
<li>HTML + CSS</li>
<li>SVG (+ light CSS)</li>
<li>Canvas</li>
</ol>
<p>The nature of a Wireworld's rendering requirements makes HTML + CSS a no-go. Canvas honestly might be the most appropriate as it can be optimized for high-frequency re-rendering. But future things I'd like to build would work well in SVG and I've already played with Canvas in the past, so I wanted to learn about graphics in SVG!</p>
<h3 id="heading-world-loop">World Loop</h3>
<p>At the base of the simulation, something will need to decide what the next state should be based on the current state. This could be done in a procedural way with a switch / ifs or functional way or object-oriented. I've <a target="_blank" href="https://www.kallmanation.com/oop-vs-fp-a-comparison-using-unconditional-fizzbuzz">written about the similarities and differences before</a>. I chose an object-oriented approach where each cell will be an object that responds to <code>nextState</code>; call <code>nextState</code> on all the cells and the world's next state has been found.</p>
<h2 id="heading-what-i-learned">What I Learned</h2>
<h3 id="heading-svg-just-works">SVG Just Works</h3>
<p>And by this I mean two things. First, SVG does not present a lot to learn above and beyond HTML + CSS (compared to the whole drawing API of a Canvas). I just put SVG tags right into Svelte components and, <em>bang,</em> graphics.</p>
<p>Second, SVG solves some of my biggest pains of drawing on Canvas. On Canvas, everything needs to be constantly erased and redrawn and if I ever want to move my viewport I'll need to do all the math to scale and translate my graphics (or learn and use another library to do it for me). With SVG, one <code>viewBox</code> attribute on the top <code>&lt;svg&gt;</code> tag handles all the scaling and translations (written by people who know a lot more about graphics than I do <em>and</em> offloaded to the browser so no JS needs spend time on those calculations).</p>
<p>Unless you have a very high paced game or some 3D graphics to render, I would recommend going down the SVG road.</p>
<h3 id="heading-svelte-stores-are-great">Svelte Stores are Great</h3>
<p>I've always heard that Svelte is good because it compiles down to vanilla JavaScript not needing virtual DOM, making it faster. But the state management available with Svelte's stores is fantastic (suck it Redux). The derived stores open even more possibilities. But by far my favorite are the custom stores: I absolutely love the patterns that opens up.</p>
<h3 id="heading-the-state-of-capturing-input-sucks">The State of Capturing Input Sucks</h3>
<p>I had no idea how bad listening for things like key presses and dragging events are today. Given how nice and fairly standard a lot of the APIs across browsers and platforms have become, I was shocked at how rough this space is. I think if I had to do this again, this will be one area where I defer to a library (like <a target="_blank" href="https://hammerjs.github.io">hammer.js</a>).</p>
<h3 id="heading-svelte-seems-to-be-lacking-tutorials">Svelte Seems to be Lacking Tutorials</h3>
<p>There's plenty of <em>examples</em> over on the <a target="_blank" href="https://svelte.dev/repl/hello-world">REPL</a> site. But those examples have next to no explanation on how they work; nearly every search I tried led me to one of those examples, so it was a bit of work piecing the things together, looking at docs, and doing experiments to get things working.</p>
<h3 id="heading-wireworlds-like-to-light-on-fire">Wireworlds Like to Light On Fire</h3>
<p>Very often a misplaced wire or extra spark will cause my whole creation to devolve into closely packed electrons shooting every which way. This happens shockingly easily and I think makes a wonderful allegory to why our real computers are so hard to make and keep working correctly.</p>
<h1 id="heading-things-to-make-and-do-in-a-wireworld">Things To Make and Do in a Wireworld</h1>
<p>If you just want to go play with it now: <a target="_blank" href="https://wireworld.klmntn.com">wireworld.klmntn.com</a>. First, go check out the few <a target="_blank" href="https://wireworld.klmntn.com/examples">examples</a> already included. A main building block in Wireworlds is the "transistor":</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643053316731/ICtSIufaH.gif" alt="Animation of a Wireworld Transistor" /></p>
<p>Like a real <a target="_blank" href="http://www.cburch.com/logisim/docs/2.7/en/html/libs/wiring/transist.html">P-type</a> transistor; our Wireworld transistor allows the signal to pass when nothing is on the gate, but blocks the signal when the gate is "on" (it even looks like a transistor diagram).</p>
<p>The next piece used in most designs is a signal generator:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643053319030/I8zP-FGAh.gif" alt="Animation of a Signal Generator in a Wireworld" /></p>
<p>Any loop of any shape with an electron moving around it can continuously emit electrons at a regular interval.</p>
<p>And go have fun! Export your creations and comment below.</p>
]]></content:encoded></item><item><title><![CDATA[Vim Thinking]]></title><description><![CDATA[I know I've grown tired of the "use VIM to be a super/rockstar/leet hacker dev!" posts... (and I use vim daily!) but give me a chance to flip the script for a second. I don't believe it is vim the software that improves the process of codewriting; ra...]]></description><link>https://www.kallmanation.com/vim-thinking</link><guid isPermaLink="true">https://www.kallmanation.com/vim-thinking</guid><category><![CDATA[vim]]></category><category><![CDATA[Productivity]]></category><category><![CDATA[challenge]]></category><category><![CDATA[General Programming]]></category><dc:creator><![CDATA[Nathan Kallman]]></dc:creator><pubDate>Thu, 01 Apr 2021 14:50:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643052840800/fIriA0nLp.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I know I've grown tired of the "use VIM to be a super/rockstar/leet hacker dev!" posts... (and I use vim daily!) but give me a chance to flip the script for a second. I don't believe it is <code>vim</code> the software that improves the process of codewriting; rather vim the way of thinking. </p>
<h1 id="heading-vim-the-software-kinda-sucks">Vim (the software) kinda sucks</h1>
<p>I'll be honest... Vim kinda sucks.</p>
<p>Selecting, installing, updating, and resolving conflicts between all those plugins takes a lot of time.</p>
<p>And then you'll also want to write your own plugins and set up macros and shortcuts and ...</p>
<p>Yes, this makes Vim incredibly powerful and configurable. But also, makes it a nightmare to actually get into a useable state sometimes.</p>
<h1 id="heading-vim-thinking-is-amazing">Vim (thinking) is amazing</h1>
<p>So if Vim (the software) sucks so much; why do I use it professionally every day?</p>
<p>For one, at <a target="_blank" href="https://root.engineering">Root</a> there are several Vim users that all share a common configuration; spreading that setup cost among multiple people.</p>
<p>For two, after learning Vim (the thought process) every other text editor felt cumbersome and slow.</p>
<p>Vim (thinking) literally changed my brain; it made me faster <em>even in non-Vim editors</em>. Even though I felt slower (than Vim) going back to my previous editors, I was actually faster than my pre-Vim days!</p>
<h1 id="heading-what-is-vim-thinking">What is Vim thinking?</h1>
<p>Today's code "editors" are actually mostly code "writers". What I mean by that is their main mode of interaction is to write new code. Their only means of "editing" is a pointing device, deletion, and a single buffer to hold content being copied (or cut, that special copy/delete combo) from one place to another.</p>
<p>Anyone who has used Vim for more than 10 seconds knows Vim starts in a mode that does not accept typing in new code. Why? Because Vim is an <em>editor</em> not a <em>writer</em>. Everything about Vim is geared towards the <em>mutation</em> of code; rather than the <em>creation</em> of code (and don't we mutate code far more than write new code?).</p>
<p>Let's take this psuedo-code example:<a id="psuedo-code"></a></p>
<pre><code class="lang-js"><span class="hljs-keyword">if</span> (!DevelopmentEnvironment.usingVim) {
  <span class="hljs-keyword">const</span> vimCandidates = [<span class="hljs-string">"vi"</span>, <span class="hljs-string">"vim"</span>, <span class="hljs-string">"nvim"</span>, <span class="hljs-string">"vim_plugin"</span>];
  <span class="hljs-keyword">const</span> randomIndex = randIntBetween(vimCandidates.length, <span class="hljs-number">0</span>);
  DevelopmentEnvironment.setCurrentEditor(vimCandidates[randomIndex]);
}
</code></pre>
<p>Hopefully clear logic: if you are not using a Vim-ish editor; attempt to randomly assign a Vim-ish editor to your dev environment. But whoops! I have a little typo: <code>randIntBetween</code> is supposed to have the smaller number as the first argument. How do we fix this?</p>
<p>In non-Vim editors, I might do something like:</p>
<ol>
<li>Move my hand from the keyboard to the mouse</li>
<li>Track the pointer to reach the location between the <code>0</code> and the closing paren <code>)</code> and click</li>
<li>Move our hand from the mouse to the keyboard and backspace once</li>
<li>Type our <code>vimCandidates.length</code> (maybe we have autocomplete to help a little)</li>
<li>Move our hand from the keyboard to the mouse (again)</li>
<li>Track the pointer and click to select the first <code>vimCandidates.length</code></li>
<li>Move our hand back to the keyboard (again)</li>
<li>Backspace and type the <code>0</code></li>
</ol>
<p>With slightly more of what I call Vim thinking, I would:</p>
<ol>
<li>Move my hand from the keyboard to the mouse</li>
<li>Track the pointer and click to select the <code>vimCandidates.length</code></li>
<li>Cut the text (without having to move my hand from the mouse)</li>
<li>Track the mouse pointer to the beginning of <code>0</code></li>
<li>Paste the text at that location</li>
<li>Still with my hand on the mouse, select the <code>0</code> and cut the text</li>
<li>Track back to before the comma and paste the <code>0</code></li>
</ol>
<p>Let's pause to examine the difference. In the first case we moved our hand between the keyboard and mouse 4 times as much! And you may think, "What's the big deal? My mouse is a few inches away" (I know I thought that). Well what if your app was making 4 network calls when it could be making 1 or even 0? Would you not immediately refactor to be more efficient?</p>
<p>Now see what I would do in Vim:</p>
<ol>
<li>Use <code>j</code> or <code>k</code> to move to the third line</li>
<li>Type <code>fv</code> to move direcly on the start of <code>vimCandidates.length</code></li>
<li>Type <code>dt,</code> to cut the content from there until the comma</li>
<li><code>f0</code> to move onto the zero</li>
<li><code>vp</code> to highlight the zero and paste over it with <code>vimCandidates.length</code></li>
<li><code>F,</code> to move back to the comma</li>
<li><code>P</code> to paste the zero before that comma (because when pasting over in step 5, it also copied the <code>0</code> as I was pasting)</li>
</ol>
<p>In this case I have not moved my hand to the mouse at all! Even more I haven't moved my hands from the home row. And many are scoffing at this point, "Why would I spend the time to memorize and type all those commands to save 5 inches of movement?" To that I say, did you "memorize" how to spell apple or orange? When you type them do you mentally go through each letter: A then p then p then l then e; to control each finger to type those letters? (If so this post isn't for you... take a step back to learn better typing) No! When I type apple, mentally I'm moving my fingers in a single motion "apple" not five distinct movements to type "a-p-p-l-e". And after a few weeks of using Vim, that's what I do there as well: I don't type "f-v - d-t-," I type "fv dt," as though they were words in a sentence.</p>
<p>So what do I mean by Vim thinking? Look back to our first "edit" strategy: I deleted and <em>retyped</em> my code. Compare to the second and third: I <em>edited</em> my code to rearrange the function arguments.</p>
<p>Now do you see why Vim does not start in a mode to type code? It is an <em>editor</em> first; <em>writing</em> is a secondary function.</p>
<h1 id="heading-edit-dont-write">Edit, Don't Write</h1>
<p>Vim was difficult for me at first. Partly because of the dreaded learning cliff. Mostly because I did not understand how Vim wanted to be used.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643052837216/gw0EypuPK.gif" alt="Loop of a man attempting to drive screw into wood using the handle of a hammer" /></p>
<p><em>Footage of me trying to use Vim, 2012, colorized</em></p>
<p>So I put it down. Then when I was onboarding at <a target="_blank" href="https://root.engineering">Root</a>, I sat down with someone who knew <em>Vim thinking</em>. It was a revelation watching them work. There was still the learning cliff to overcome, but now I had seen what sat atop the hill. And I wanted it.</p>
<p>A writer will delete and retype. An editor will transpose, cut, pattern match, and find a path of least change. A writer wants faster typing. An editor uses less typing. A writer offloads writing to the machine. An editor melds the machine and the mind. A writer sees what they want. An editor sees the path to get where they want.</p>
<p>Use an editor, not a writer.</p>
<h1 id="heading-vim-thinking-is-machine-thinking">Vim Thinking is Machine Thinking</h1>
<p>Vim provides a full language of editing semi-structured text of any kind. Like the letters in these words form into sentences transferring my thoughts to your mind; the commands of Vim stack into words, which flow through sentences, and paragraphs, perfectly telling my pile of sand and copper how to rewrite its code.</p>
<p>In Vim, I'm never more than a few keystrokes from code I've imagined to a coded reality. Being that close means I can iterate through my ideas more quickly. Most of my ideas are bad to awful; throwing them away as quickly as possible is the best thing I can do with them.</p>
<p>In a <a target="_blank" href="https://www.kallmanation.com/handwritten-code">recent article</a>, I talked about how the <em>process</em> of making something directly affects the results. Code written with Vim thinking has unique qualities from code written in other ways. Physically enacting the same mechanics that my computer uses to interact with its world gives me an empathy and understanding of it. That understanding makes code easier to read. That understanding makes code easier to write.</p>
<h1 id="heading-getting-into-vim-thinking">Getting into Vim Thinking</h1>
<p>Pick a flavor: a <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=vscodevim.vim">plugin</a>, <a target="_blank" href="https://neovim.io">neovim</a>, <a target="_blank" href="https://www.vim.org">vim</a>. Don't customize it (yet), just dive in. We want to start thinking in Vim, not die in the quagmire of configuration.</p>
<p>If you can learn about two dozen "letters" in the language of Vim, you will be at least as quick in Vim as any other and over the main cliff where patterns will start to emerge making learning easier. Funnily enough that's about how many letters are in english; so if you can read this, I know you can learn Vim thinking. Here is my Vim <a target="_blank" href="https://en.wikipedia.org/wiki/Directed_acyclic_graph">DAG</a> mapping the concepts covering 80% of what I use in Vim.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643052839406/z5O8JJr7F.png" alt="Graph with VIM in the center; branching off are Movement, Undo/Redo, Copy/Paste, and Writing; branching off of Undo/Redo are Undo with u and Redo with control r; branching off of Copy/Paste are Select Lines with Shift-V, Select Characters with v, Copy (Yank) with y, Cut (Delete) with d, Paste with p, Cut &amp; Start Writing with c, and Stop Selecting with escape; branching off of Writing are Start Writing with i, Start Writing at the Start of the Line with Shift-I, Start Writing at the End of the Line with Shift-A, Cut &amp; Start Writing with c, and Stop Writing with escape; branching off of Movement are File, Line, and Character; branching off of File are Top with gg, Bottom with Shift-G, Line Number with number-gg, and Search with /; branching off of Line are Search with /, Down with j, Up with k, and Matching Brackets or Parentheses with %; and branching off of Character are Matching Brackets or Parentheses with %, Left with h, Right with l, and Find Next character with f followed by that character" /></p>
<p>And here is the challenge to get you Vim thinking: </p>
<ol>
<li>Go back to that <a class="post-section-overview" href="#psuedo-code">psuedo-code</a> above, copy it into a file and open with Vim; make the correction; and refactor that psuedo-code into as few lines as possible.</li>
<li>Extra challenge: never enter insert mode (don't use <code>i</code>, <code>o</code>, <code>a</code>, or <code>c</code> from the diagram).</li>
</ol>
<p>If you do that (especially with the extra challenge) I think you'll have enough of a taste of Vim to understand this whole rambling post I call an article (and probably enough to want more Vim in your life).</p>
<h1 id="heading-going-deeper">Going Deeper</h1>
<p>So you want more Vim now? 😈 Good (secretly high-fives Iviminati friends). There are three more things that seriously leveled up my productivity in Vim (that I now miss in most other editors):</p>
<ol>
<li>Multiple <a target="_blank" href="https://vim.fandom.com/wiki/Copy,_cut_and_paste#Multiple_copying">copy/paste buffers</a></li>
<li>Recording and replaying <a target="_blank" href="https://spin.atomicobject.com/2014/11/23/record-vim-macros/">Macros</a></li>
<li>Basic Regular Expressions for searching (and replacing)</li>
</ol>
<p>Have fun!</p>
]]></content:encoded></item><item><title><![CDATA[Handwritten Code]]></title><description><![CDATA[Now that I have your attention. No, do not hand write code (even in interviews). Besides that cover photo, the last time I wrote code by hand was in college.
Speaking of college, there's a particular page in a particular textbook I think about often....]]></description><link>https://www.kallmanation.com/handwritten-code</link><guid isPermaLink="true">https://www.kallmanation.com/handwritten-code</guid><category><![CDATA[coding]]></category><category><![CDATA[software development]]></category><category><![CDATA[Productivity]]></category><dc:creator><![CDATA[Nathan Kallman]]></dc:creator><pubDate>Tue, 23 Mar 2021 12:46:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643052529699/29zgMY2Zh.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Now that I have your attention. No, do not hand write code (even in interviews). Besides that cover photo, the last time I wrote code by hand was in college.</p>
<p>Speaking of college, there's a particular page in a particular textbook I think about often. Not for its content, but rather as an analogy to a concept I struggle to express any other way.</p>
<p>I want to dig deep into that concept today. If you came for a squabble on scribbling out software; you may leave, this is not the post you are looking for.</p>
<p>First, the page I think about, page 56 of <em>Introduction to Graphics Communications for Engineers, Fourth Edition</em> by Gary R. Bertoline (ISBN 978-0-07-352264-7)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643051639898/fmgCJ8aZyr.jpeg" alt="Page 56 of Introduction to Graphics Communications for Engineers, describing why legible writing is important in engineering with diagrams for each letter and number to draw them in a consistent and legible way" /></p>
<p>Look through those diagrams of how to write out letters and numbers. That's probably not the way you were taught your lettering and almost certainly not the steps you follow when writing today (I definitely didn't). But how legible is your writing?</p>
<h1 id="heading-the-crafting-process-directly-correlates-to-the-results-quality">The Crafting <em>Process</em> Directly Correlates to the Result's <em>Quality</em>.</h1>
<p>The <em>steps</em> and <em>tools</em> used in making leave indelible marks on the <em>end result</em>. Really I'm just retelling <a target="_blank" href="https://en.wikipedia.org/wiki/Conway's_law">Conway's Law</a>:</p>
<blockquote>
<p>Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization's communication structure.</p>
<p>— Melvin E. Conway</p>
</blockquote>
<p>But now I have an analogy for <em>why</em> Conway's Law is true. Look back at each of those letters; do you notice a pattern? Pen strokes are limited to going down and to the right (not strictly, but generally). No coincidence those are the directions a right-handed writer has most control over.</p>
<p>Could we write letters just the same without following such a stringent rule set? We could try. And we would have good results for a little while. But once we stopped focusing our handwriting would degrade once again back to our old pattern.</p>
<p>An organization <em>can</em> ship a system designed differently than its own structure. This happens regularly. But it will quickly <em>devolve</em> into matching the organization structure just as our handwriting devolves to the <em>natural</em> forms our grip and strokes encourage. Changing the strokes taken makes it <em>natural</em> to fall into that particular legible style. Changing organization structure makes it <em>natural</em> to produce software of a particular style.</p>
<h1 id="heading-beware-labelling-every-cost-as-a-problem">Beware Labelling Every Cost as a Problem</h1>
<p>Go try writing using those instructions for a minute. It will be very slow at first. After a few minutes you should have a little rhythm and won't need to look at the instructions much.</p>
<p>But this stroke pattern will always be slower than others. The added guidelines to only go down and to the right forces the hand to lift and move the pen more often.</p>
<p>This is a <strong><em>cost</em></strong> not a <strong><em>problem</em></strong>. Problem's can be solved. Costs are the requirement to obtain what we want. We want legible handwriting. The cost is writing slower to follow the guidelines.</p>
<p>I have often seen discussions start up around all sorts of "problems" looking for any manner of "solution" in the business of software. I can hear them now, "If we could go up and left, we could write letters so much faster!", but that is not a problem; that was intentional!</p>
<p>I truly believe that particular habit, more than anything else, locks larger and older companies into all the odd behavior, poor customer service, and lack of innovation we see of them. They have straight-jacketed themselves with decades of "solutions" to "problems". But those "problems" were just the price of being the best in the business!</p>
<h1 id="heading-monitor-output">Monitor Output</h1>
<p>I know of no way to truly predict the final results of changing a making process. We have gross heuristics (fist gripping a pen will be less precise than gripping with forefingers; agile-ish practices see better utility production than waterfall-ish practices).</p>
<p>But to truly know? Specifically? We must try it and observe the results. In software development, this means the practices of the team (agile, cross-functional team-members, red-green-refactor TDD, etc.). The output to observe being the code written (if the people deciding what practices should be used can't understand the code output*, you have a Big Problem™).</p>
<p>Is the code produced of a similar quality? Maintainable? Extendable? Bug-free? Robust to unexpected situations?</p>
<p>No? Congratulations, the "problem" you solved was actually the price of quality.</p>
<p><em>* Don't forget understanding requires communication. Communication is a two-player game. If the bosses can't (or won't) understand the basic technical aspects of the software, you have a Big Problem™. If the code you've written is so poorly documented and described that the bosses can't understand it, you have a Big Problem™.</em></p>
<h1 id="heading-refine-practices">Refine Practices</h1>
<p>I am now attempting to describe a process of designing a process intent on producing better processes. It's rabbit holes all the way down. But here is the best advice I have found:</p>
<ol>
<li>Constantly adjust, observe, re-orient, and re-adjust yourself</li>
<li>Observe others; are they achieving things by doing something "stupid" or "crazy" or "obviously wrong" that "makes no sense"? They probably aren't stupid or crazy or wrong... go back to point one and give it a try.</li>
</ol>
<h1 id="heading-some-examples">Some Examples</h1>
<h2 id="heading-testing">Testing</h2>
<ul>
<li><strong>No Tests</strong> - not writing any tests at all encourages a certain type of codebase to develop. Either you know what I mean or you are blessed to not know yet.</li>
<li><strong>Test Everything Last</strong> - at least we're writing tests now. Writing last though encourages the lazier developers (aka me) or the pushier product owners to skip some thoroughness in the test suite. Often leading to missing or broken tests.</li>
<li><strong>Refactor-Green-Green</strong> - tests are written intermingled with code writing. But often written post-code, meaning the writer never sees them fail. Intermingling leaves less chance for missing tests, but does not help much with broken tests.</li>
<li><strong>Red-Green-Refactor</strong> - a minimum set of tests are added, then a minimum code change is made to make all tests pass, then optionally the code is re-arranged into a more readable format. Tests written in this way often have good coverage and are fairly healthy. Tests can become disorganized though.</li>
<li><strong>Test Everything First</strong> - write them all; write them now. Tests have good coverage and are fairly healthy; also well organized. Testing this way has the drawback of being "waterfall-y", if something is discovered late in the process to be incorrect a huge amount of time is wasted re-writing tests.</li>
</ul>
<h2 id="heading-release-cycles">Release Cycles</h2>
<ul>
<li><strong>Months Between Releases</strong> - leads to a lot of formal processes, reviews, approvals, and change management. An incorrect feature release can be devastating, so rather than ship incomplete features, years are wasted waiting (and it needs to be redone anyway because the business has changed by the time it is released).</li>
<li><strong>Weekly Releases</strong> - time spent meeting cut drastically. Smaller portions of features are released. Changes are manageable and users don't mind waiting for next week for their request. Still some process to review ensuring a week won't be wasted.</li>
<li><strong>Daily Releases</strong> - almost no manual process around releases. "Planning" becomes a loose guideline as changes are requested and executed daily.</li>
<li><strong>Continuous (or near-continuous) Releases</strong> - manual processes nearly evaporate. All the time spent on them are re-invested in the actual building of software. Bug fixes are released within hours. Fear of making mistakes is gone because the consequences of mistakes are nearly gone.</li>
</ul>
<h2 id="heading-team-organization">Team Organization</h2>
<ul>
<li><strong>Technical Area</strong> - back-end, front-end, database, etc. common technical concerns (e.g. poor DB performance, UI components) are easily dealt with but every new feature must go through multiple teams increasing cost and complexity.</li>
<li><strong>Business Area</strong> - invoicing-team, marketing-team, etc. new features have a clear, single owner cutting their development costs but cross cutting concerns fall into a "tragedy of the commons".</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[ICO: Incentive Couch Options]]></title><description><![CDATA[Cover photo by Phillip Goldsberry on Unsplash

Explaining Stock Options with a Couch
ISOs, ICOs, IPOs... all the financial TLAs (Three Letter Acronyms) get confusing to anyone who has not worked in finance or done trading. I know I didn't understand....]]></description><link>https://www.kallmanation.com/ico-incentive-couch-options</link><guid isPermaLink="true">https://www.kallmanation.com/ico-incentive-couch-options</guid><category><![CDATA[Startups]]></category><category><![CDATA[Business and Finance ]]></category><category><![CDATA[Career]]></category><dc:creator><![CDATA[Nathan Kallman]]></dc:creator><pubDate>Mon, 15 Mar 2021 12:38:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643051306447/KElco8tit.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><i>Cover photo by <a target="_blank" href="https://unsplash.com/@phillipgold?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Phillip Goldsberry</a> on <a target="_blank" href="https://unsplash.com/s/photos/sofa?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></i></p>
<hr />
<h1 id="heading-explaining-stock-options-with-a-couch">Explaining Stock Options with a Couch</h1>
<p>ISOs, ICOs, IPOs... all the financial TLAs (Three Letter Acronyms) get confusing to anyone who has not worked in finance or done trading. I know I didn't understand.</p>
<p>I'm here to try to explain the main concepts with a couch. Specifically my friend's couch. I'll phone my friend, Xavier:</p>
<blockquote>
<p>Me: Hey, Xavier! Heard you're gettin' a new couch, could I have your old one?
Xavier: Sure, ten bucks and it's yours. Come over anytime after Friday.
Me: Thanks X!</p>
</blockquote>
<p>Now, I don't have a couch. I have the <em>option</em> to get a couch. That option has a price: $10 ("strike price" in stock speak). And Xavier isn't a couch store; my option has a limit: 1 couch. I also can't just get the couch whenever I want; I must wait until after Friday to get the couch ("vesting" schedule in stock speak).</p>
<p>So on Saturday, I drive over to Xavier's with $10 in hand and get the couch (in stock speak, "exercise"; and moving couches definitely counts as exercise 😅). Now the couch is probably worth more than $10. If I didn't want the couch, just some cash from selling, I could bring someone willing to pay $100; once we get there, they pay Xavier the $10 I owe and pay me the remaining $90, instant cash!</p>
<p><em>Do not forget the government has a word for "instant cash": income. And they will be taxing me accordingly. There are ways to lower those taxes. Generally buying the couch early and waiting a long time before selling may subject me to capital gains taxes instead of normal income taxes, which are lower. But there's also a lot of particulars over the specific type of options I have (incentivized or not), what the price is when I get it vs when I sell it vs the actual price I paid, when all those things happened, etc. Probably best to ask a Tax expert or Accountant who can run the numbers in your particular case instead of a random article online talking about couches...</em></p>
<p>But let's go back to the exercise of getting the couch. What if the couch is actually worth $5? Nothing says I <em>have</em> to buy Xavier's couch. He may be a nice guy, but I'll go get the same couch for half price. Unless Xavier put an expiration date on my option, I can always come back for the agreed $10 if these couches suddenly become collectors items and sell for $500 a piece. </p>
<h1 id="heading-summary">Summary</h1>
<p>And that's it. Substitute "couch" with "stock" to get an explanation of stock options.</p>
<ul>
<li><strong>Couch</strong>: It's... a couch</li>
<li><strong>Couch option</strong>: Not a couch; just an offer to buy a couch at a certain price (regardless of the actual value of said couch)</li>
<li><strong>Vesting</strong>: The predetermined time in the future when the couch can be purchased <em>(after Friday in my story)</em></li>
<li><strong>Exercising</strong>: Buying said <strong>Couch</strong> offered in the <strong>Couch option</strong> (aka turning that <strong>Couch option</strong> into an actual <strong>Couch</strong>)</li>
<li><strong>Strike price</strong>: The price to buy the <strong>Couch</strong> from the <strong>Couch option</strong> <em>($10 in my story)</em></li>
<li><strong>Fair Market Value (FMV)</strong>: How much the <strong>Couch</strong> is currently actually worth to other people <em>($100 in my story)</em></li>
</ul>
<h1 id="heading-a-warning">A Warning</h1>
<p>Couches aren't a good investment strategy. Neither are stock options or equity in a start-up.</p>
<p>It may be possible your options turn in to thousands or millions just like it may be possible the couch you got off your friend turns into a collectors' item. Enjoy it when it happens; Don't depend on it. Don't listen to the person who won the lottery telling you to invest all your cash in lotto tickets. Don't accept equity in return for lower pay and benefits.</p>
<p>You probably are not sitting on a nest egg. You are probably just sitting on a dirty old couch.</p>
]]></content:encoded></item><item><title><![CDATA[Let's Re-acronym KISS]]></title><description><![CDATA[KISS
Keep It Simple, Stupid
Okay, no need to insult me. I get it! I'll keep my designs simple...
I'm not usually one to call things "problematic", but I do think the way we talk affects the way we think. There's no need to have a put down in one of o...]]></description><link>https://www.kallmanation.com/lets-re-acronym-kiss</link><guid isPermaLink="true">https://www.kallmanation.com/lets-re-acronym-kiss</guid><category><![CDATA[impostor syndrome]]></category><category><![CDATA[writing]]></category><category><![CDATA[watercooler]]></category><dc:creator><![CDATA[Nathan Kallman]]></dc:creator><pubDate>Mon, 08 Mar 2021 16:56:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/ofpr9Cw8Rj8/upload/v1643051043261/V7MA3JUOL.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-kiss">KISS</h1>
<h2 id="heading-keep-it-simple-stupid">Keep It Simple, Stupid</h2>
<p>Okay, no need to insult me. I get it! I'll keep my designs simple...</p>
<p>I'm not usually one to call things "problematic", but I do think the way we talk affects the way we think. There's no need to have a put down in one of our best pieces of advice. We're already struggling enough under our own imposter syndromes 😅.</p>
<h2 id="heading-keep-it-stupid-simple">Keep It Stupid Simple</h2>
<p>A simple transposition (<em>stupidly</em> simple even 😏) changes our message from insulting to doubling down on itself.</p>
<p>Keep our designs simple (<a target="_blank" href="https://www.kallmanation.com/dear-slack-simple-is-not-what-you-think-it-is">Don't confuse simple with easy</a>. Not just simple: <strong>stupidly</strong> simple! So simple we can explain them to our non-tech friends and coworkers. Simple enough that our new hire can work on the system within the week (without breaking it on misunderstandings).</p>
<p><strong>Let's re-acronym KISS from <code>Keep It Simple, Stupid</code> to <code>Keep It Stupid Simple</code>.</strong></p>
<h1 id="heading-thats-it">That's It</h1>
<p>Agree? Disagree? Made up problem? I love hearing your thoughts in the comments!</p>
]]></content:encoded></item><item><title><![CDATA[My Mental Model of a Server]]></title><description><![CDATA[Something I struggled with when I was beginning web development and I see many new devs struggle with as well: What is a "Server" actually capable of?
I've already given it away with the cover photo; but why not look at it top-to-bottom instead of le...]]></description><link>https://www.kallmanation.com/my-mental-model-of-a-server</link><guid isPermaLink="true">https://www.kallmanation.com/my-mental-model-of-a-server</guid><category><![CDATA[Beginner Developers]]></category><category><![CDATA[server]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[webdev]]></category><category><![CDATA[software architecture]]></category><dc:creator><![CDATA[Nathan Kallman]]></dc:creator><pubDate>Mon, 01 Mar 2021 13:58:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643050472872/sP28-LBHM.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Something I struggled with when I was beginning web development and I see many new devs struggle with as well: What is a "Server" actually capable of?</p>
<p>I've already given it away with the cover photo; but why not look at it top-to-bottom instead of left-to-right 😅 (and I will talk about each part in more detail)</p>
<h1 id="heading-simplest-mental-model-of-a-server">Simplest Mental Model of a Server</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643050469850/-4cQsqMjj.png" alt="Diagram of a Simple Server: inputs are a Request and Storage; outputs are a Response and updated Storage" /></p>
<hr />
<p><strong>A server is this: software that starts upon request, runs using the information in the request and data from longer term storage without further input, completing execution by updating the stored data and returning a response.</strong></p>
<hr />
<p>This model eradicates three main misconceptions:</p>
<ol>
<li>No processing or execution <em>after</em> the response</li>
<li>No processing or execution <em>before</em> the request</li>
<li>No interrupting or user interaction <em>during</em> the process</li>
</ol>
<p>The above three things can <em>only</em> be achieved using the "storage" aspect in the diagram to coordinate <em>something else</em> to achieve it. The better we understand that <em>something else</em> is a <strong>completely separate process</strong>, the better prepared we are to handle the hazards in those patterns. The <em>only</em> way to do any of the above three things is with "storage".</p>
<h2 id="heading-what-is-storage">What is "Storage"?</h2>
<p>I'm using this as a stand-in for any non-volatile data store.</p>
<p>Most commonly a relational database. But it may also be a document database, key-value store, even an in-memory store like <a target="_blank" href="https://redis.io">Redis</a>* or just files on the local hard drive. In large or needy applications, more than one may be used, but we can mentally lump them all together into "storage".</p>
<p>This current state gets put in the top and updates come out the other side (if anything needs to update).</p>
<p><em>* Memory stores are usually considered "volatile" in comparison to hard drives, but to the length of an average request-response, a store like Redis comparably keeps data for eons.</em></p>
<h2 id="heading-the-request">The Request</h2>
<p>The first big half of the "request-response lifecycle". Nothing, I repeat <em>nothing</em>, happens in HTTP without a request to start it.</p>
<p>A request can have all sorts of information, but the highlights are:</p>
<ol>
<li>Verb - aka <code>GET</code>, <code>POST</code>, <code>PUT</code>, <code>PATCH</code>, <code>DELETE</code>, etc. each has a meaning; please use them appropriately</li>
<li>Host - like <a target="_blank" href="https://buymeacoff.ee/kallmanation"><code>www.buymeacoffee.com</code></a></li>
<li>Path - the slashy bits, like <a target="_blank" href="https://buymeacoff.ee/kallmanation"><code>/kallmanation</code></a>; but also contains the raw query parameters like <a target="_blank" href="https://buymeacoff.ee/?via=kallmanation"><code>?via=kallmanation</code></a></li>
<li>Various Headers - all sorts of meta-data about the request, mostly look these up as needed; but one is worth mentioning here:</li>
<li>Cookies - the thing we always have to "accept"; just a bunch of little pieces of text the requester keeps track of for the server</li>
<li>Body - not on all requests; usually JSON, but can be any text for the server to use</li>
</ol>
<p>Without data in either the storage or somewhere in this request (mainly in cookies), the server can not know about it and in its opinion that information <em>does not exist</em> and has never existed.</p>
<h2 id="heading-the-response">The Response</h2>
<p>The end of the "request-response lifecycle". Truly, <em>The End</em>, Finito. Nothing happens in HTTP after the response has been returned.</p>
<p>Like requests, responses have all sorts of information, but the big parts are:</p>
<ol>
<li>Status - <a target="_blank" href="https://http.cat/200">200</a> for OK, <a target="_blank" href="https://http.cat/401">401</a> for unauthorized, <a target="_blank" href="https://http.cat/418">418</a> I'm a teapot, etc.; please use the appropriate status</li>
<li>Headers - again all sorts of meta-data, look up as needed</li>
<li>Cookies - a specific header can tell the requester to send the given cookie with any future requests (they can also be set by JavaScript)</li>
<li>Body - HTML/CSS/JS/JSON/XML/etc./etc. whatever we want to send back</li>
</ol>
<h1 id="heading-nuanced-mental-model-of-a-server">Nuanced Mental Model of a Server</h1>
<p>To be slightly more pedantic and technical: an application server does not exactly have the entire database stuffed into it on each request. Instead some pre-processing needs to use the request information to formulate a query to storage to load only the needed data.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643050471508/fgBpnPxf4.png" alt="Diagram of a Server: input Request goes to the main process and a side process; the side process queries storage and passes data into the main process; the main process outputs a Response and updated Storage" /></p>
<p>This "Query" might be a regular old SQL query. But it might be just finding the right file on disk by name and path. Or it could be a request to a separate web service managing storage (like in a Database-as-a-Service). Once the data is fetched, the rest of the server continues as it did in the simpler mental model.</p>
<p>To get even more technical; I've never seen a server application written in such distinct steps of fetch all data, process data, update data, return response. The fetching, processing, and updating usually muddles all together (sometimes rightly, oftentimes wrongly). But still, from an external perspective, a requester could not perceive a real difference between that technicality and the more controlled mental model diagrammed above. So I believe it to be a very useful model of an application server.</p>
]]></content:encoded></item><item><title><![CDATA[Towards Sustainable Source Software?]]></title><description><![CDATA[Cover photo by Elijah Hiett on Unsplash

I won't beat the dead horse of how unsustainably enterprise depends on open source software. Maintainers treated like indentured servants. Projects abandoned. Dependencies overtaken by malicious actors. Burn-o...]]></description><link>https://www.kallmanation.com/towards-sustainable-source-software</link><guid isPermaLink="true">https://www.kallmanation.com/towards-sustainable-source-software</guid><category><![CDATA[Open Source]]></category><category><![CDATA[software development]]></category><dc:creator><![CDATA[Nathan Kallman]]></dc:creator><pubDate>Mon, 15 Feb 2021 14:08:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643050110522/1qi-Sztdy.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><span>Cover photo by <a href="https://unsplash.com/@elijahdhiett?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Elijah Hiett</a> on <a href="https://unsplash.com/s/photos/waterfall?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></p>
<hr />
<p>I won't beat the dead horse of how unsustainably enterprise depends on open source software. Maintainers treated like indentured servants. Projects abandoned. Dependencies overtaken by malicious actors. Burn-out. It's ugly. Financial support is too <a target="_blank" href="https://en.wikipedia.org/wiki/Zipf's_law">Zipf</a>-y. We need a change.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643050107381/uIHQzh1vj.png" alt="Graph showing exponential decline from a &quot;Livable Wage&quot; quickly down past the price of a Coffee approaching zero as the number of open source projects increase" /></p>
<h1 id="heading-i-do-not-know-what-i-am-talking-about">I Do Not Know What I am Talking About</h1>
<p>I have not really contributed to open source. My income has always come from an employer. I have not felt the pressure of maintaining a project for other people. I have not tried any other methods to "sustainably" source software. <strong>I do not know what I am talking about.</strong> I am sure this idea has a multitude of problems and unintended consequences. Please discuss below.</p>
<p>You should just leave now. This "article" is being written to future me so I do not forget. Thank you; have a nice day.</p>
<h1 id="heading-proposing-an-idea-anyway">Proposing an Idea Anyway</h1>
<p>I am inspired by the <a target="_blank" href="https://www.nspe.org">Professional Engineer</a> groups in the US, the <a target="_blank" href="https://en.wikipedia.org/wiki/Guild">guilds</a> and <a target="_blank" href="https://en.wikipedia.org/wiki/Apprenticeship">apprenticeships</a> of yore, and a system of <a target="_blank" href="https://en.wikipedia.org/wiki/Universal_basic_income">Universal Basic Income</a>.</p>
<p>And so, the general aspects of this proposal are this:</p>
<ol>
<li>Only professional* software developers have membership</li>
<li>Much** software produced by the organization is open to all members but closed to non-members</li>
<li>Members may use said software for any purpose the organization approves of (even for profit or for an employer's profit)</li>
<li>Members gain shares in the organization only by the promotion votes of other members (these cannot be purchased and non-members cannot hold shares)</li>
<li>Shares pay weekly dividends based on organization income from licensing software and membership dues</li>
<li>Membership dues are based on individual income (outside of share dividends)</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1643050109164/3C4trY_dhK.png" alt="Diagram: Members support, create, and use Sustainable Source Software and support, create, and use Open Source Software; Non-members support(?), create, and (ab)use Open Source Software and license use of Sustainable Source Software, the costs of which go to Membership Funds; Members also pay dues to Membership Funds; Membership Funds support Sustainable Source Software and pay weekly dividends to Members" /></p>
<p><em>* Professional, here, used in the sense of <a target="_blank" href="https://www.nspe.org/resources/ethics/code-ethics">professionalism</a> not just being paid for a job.</em>
<em>** Members are still free to create fully open source software or closed source software. The organization simply provides a third option of exclusive source software.</em></p>
<h1 id="heading-intended-effects">Intended Effects</h1>
<p>By paying weekly dividends to members, those focusing on the exclusive source software for everyone else are guaranteed a minimum level of financial support, regardless of how "important" or "useful" the contributions. It also provides an instant safety net to developers suddenly losing their jobs or those between jobs (giving all members a boost to negotiation power; able to leave at any time with less consequence).</p>
<p>By allowing increased shareholding only through the voting of peers, the domination by those already financially well-off should be averted. This should provide a mechanism to promote and increase the compensation of those most deserving and contributing to the organization as a whole. There may need to be mechanisms to avoid extremely popular developers from being voted an undue number of shares, simply because of their celebrity status.</p>
<p>The exclusivity means that exploitation of completely open source can be reduced as utilization becomes limited to people and organizations actively paying back in contributions or dollars (if not both). It should also reduce low quality "contribution" to sources as only members (assumably qualified developers) participate.</p>
<h1 id="heading-how-do-members-get-in">How Do Members Get In?</h1>
<p>For people already developing high-quality software, there should be a nomination and approval process by a minimum number of members.</p>
<p>But for those looking to become developers, there should be an apprenticeship / developer-in-training status for them to learn over time. Apprenticeship in various forms has been proven to work extremely well in grooming skilled workers with practical (not just theoretical) competence.</p>
<p>But those rules leads the question: <strong>How does the first "minimum number" of members, become members?</strong> Just decreeing myself a member of an exclusive "professional software developers" club seems self-serving, egotistical, and generally unhelpful. There are also legal questions around how "shares" of such an organization would be regulated and how the software would actually be licensed and the licenses enforced etc...</p>
<h1 id="heading-comments-concerns-certainties-and-conversations">Comments, Concerns, Certainties, and Conversations</h1>
<p>Bring it on! My flimsily published idea deserves the fiery furnace of healthy debate. Be kind and courteous. Source facts. Attack ideas not people. Thank you for your time!</p>
]]></content:encoded></item><item><title><![CDATA[One Liner to Create and Move into a Directory (combining mkdir + cd)]]></title><description><![CDATA[Almost every new project seems to start with the same thing:
$ mkdir new-project
$ cd new-project

In a GUI we would be stuck; but in the command line, we can make this one command instead of two!
Let's make a little function called mcd (for make and...]]></description><link>https://www.kallmanation.com/one-liner-to-create-and-move-into-a-directory-combining-mkdir-cd</link><guid isPermaLink="true">https://www.kallmanation.com/one-liner-to-create-and-move-into-a-directory-combining-mkdir-cd</guid><category><![CDATA[command line]]></category><category><![CDATA[zsh]]></category><category><![CDATA[Bash]]></category><dc:creator><![CDATA[Nathan Kallman]]></dc:creator><pubDate>Mon, 08 Feb 2021 19:01:10 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643049610817/8RiVJjNjx.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Almost every new project seems to start with the same thing:</p>
<pre><code class="lang-sh">$ mkdir new-project
$ <span class="hljs-built_in">cd</span> new-project
</code></pre>
<p>In a GUI we would be stuck; but in the command line, we can make this one command instead of two!</p>
<p>Let's make a little function called <code>mcd</code> (for make and change directory). First we need to make the directory:</p>
<pre><code class="lang-sh"><span class="hljs-function"><span class="hljs-title">mcd</span></span>() { mkdir <span class="hljs-string">"<span class="hljs-variable">$@</span>"</span> }
</code></pre>
<p>The <code>"$@"</code> refers to all the arguments given to <code>mcd</code> when it is used. At this point, we've basically made a round about <code>alias</code> for <code>mkdir</code>. So how about adding the <code>cd</code> part now?</p>
<pre><code class="lang-sh"><span class="hljs-function"><span class="hljs-title">mcd</span></span>() { mkdir <span class="hljs-string">"<span class="hljs-variable">$@</span>"</span> &amp;&amp; <span class="hljs-built_in">cd</span> <span class="hljs-string">"<span class="hljs-variable">$@</span>"</span> }
</code></pre>
<p>And this <em>kind of</em> works. The problem is <code>mkdir</code> can receive a whole bunch of flags and even multiple directories to create at once, but <code>cd</code> really only expects one argument, the directory to move into. The good thing about how these commands are structured is <code>mkdir</code> expects the last argument to be a directory, which makes it easy for us to pick out the one directory that was created (or if many were created, pick out the last one created) using <code>"$_"</code>, which refers to the last argument of the previously executed command.</p>
<pre><code class="lang-sh"><span class="hljs-function"><span class="hljs-title">mcd</span></span>() { mkdir <span class="hljs-string">"<span class="hljs-variable">$@</span>"</span> &amp;&amp; <span class="hljs-built_in">cd</span> <span class="hljs-string">"<span class="hljs-variable">$_</span>"</span> }
</code></pre>
<p>And we could be done here, but I added one more thing. If <code>mkdir</code> prints errors, they will all be written in terms of <code>mkdir</code>. But that makes little sense to someone who just ran a command called <code>mcd</code>. I fixed that with <code>sed</code> and some redirection:</p>
<pre><code class="lang-sh"><span class="hljs-function"><span class="hljs-title">mcd</span></span>() { mkdir <span class="hljs-string">"<span class="hljs-variable">$@</span>"</span> 2&gt; &gt;(sed s/mkdir/mcd/ 1&gt;&amp;2) &amp;&amp; <span class="hljs-built_in">cd</span> <span class="hljs-string">"<span class="hljs-variable">$_</span>"</span>; }
</code></pre>
<p>For more examples of how this is used, check out <a target="_blank" href="https://gist.github.com/kallmanation/2027bb23242e59cb90141c803ffe2703">my gist</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Get your code sample rejected in 3 easy steps]]></title><description><![CDATA[Cover photo by Mark Rohan on Unsplash

Disclaimer: this post should be considered humorous and not a comprehensive guide on submitting a good code sample. While it is based on my experience interviewing at Root, it does not reflect the company's opin...]]></description><link>https://www.kallmanation.com/get-your-code-sample-rejected-in-3-easy-steps</link><guid isPermaLink="true">https://www.kallmanation.com/get-your-code-sample-rejected-in-3-easy-steps</guid><category><![CDATA[Beginner Developers]]></category><category><![CDATA[interview]]></category><category><![CDATA[Career]]></category><dc:creator><![CDATA[Nathan Kallman]]></dc:creator><pubDate>Mon, 01 Feb 2021 14:34:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643049483376/IH1oQfEqu.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><span>Cover photo by <a href="https://unsplash.com/@wackomac007?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Mark Rohan</a> on <a href="https://unsplash.com/s/photos/no-sign?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></p>
<hr />
<p><em>Disclaimer: this post should be considered humorous and not a comprehensive guide on submitting a good code sample. While it is based on my experience interviewing at <a target="_blank" href="https://root.engineering/">Root</a>, it does not reflect the company's opinions or stances on how it conducts interviews; these opinions are solely my own.</em></p>
<hr />
<h1 id="heading-1-include-bugs">1. Include bugs</h1>
<p>Do not bother validating that your code sample gives the correct output.</p>
<p>In fact, the more bugs the better! If you show your reviewer that you cannot write bug-free code in a small task you efficiently convince them that you will write bug-riddled code when they hire you.</p>
<p>Letting your reviewer see your lack of attention now lets you get rejected at the code sample instead of at the last interview round (or worse, being hired and then fired later for poor performance).</p>
<p>And the best way to make sure you have plenty of bugs?</p>
<h1 id="heading-2-do-not-write-tests">2. Do not write tests</h1>
<p>Tests are a waste of time. You have a lot of code samples to shotgun out to employers; absolutely do not do any quality control. And more tests will make it harder to include bugs, which was step number one!</p>
<p>If you <em>must</em> write a test; do not make it useful. Make it pass even when multiple lines of the function are deleted. And whatever you do: <strong>do not</strong> write an end-to-end or integration test; those things take the most time (even more waste!) and find even the subtlest bugs in your code. Just don't do it.</p>
<h1 id="heading-3-do-not-include-instructions-to-build-andamp-run-the-code">3. Do not include instructions to build &amp; run the code</h1>
<p>Remember, you want this sample rejected. If the reviewer cannot run your sample (or can only run after hours of trial and error) they will need to review the code even more carefully.</p>
<p>If you have successfully executed steps one and two, that careful combing of your code will give you the maximum chance of rejection.</p>
<p>Definitely use undocumented dependencies. Even better, depend on something inherent to only your computer (the absolute path to a particular file for instance). Do not use a package manager if it can be avoided (or if you do use it in a non-standard way or use private packages).</p>
<hr />
<p><em>P.S. Root has cranked up its hiring machine for 2021. If you would like to practice these 3 steps, go ahead and <a target="_blank" href="https://root.engineering">apply</a></em></p>
]]></content:encoded></item></channel></rss>