<?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" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Various Failures]]></title><description><![CDATA[web, containerization, linux, and more.]]></description><link>https://blog.kenforthewin.com/</link><image><url>https://blog.kenforthewin.com/favicon.png</url><title>Various Failures</title><link>https://blog.kenforthewin.com/</link></image><generator>Ghost 1.19</generator><lastBuildDate>Sat, 11 Apr 2026 03:54:33 GMT</lastBuildDate><atom:link href="https://blog.kenforthewin.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[TripJam: Collaborative Trip Planning]]></title><description><![CDATA[<div class="kg-card-markdown"><p><a href="https://tripjam.com">TripJam</a> was born out of a frustration with planning trips with my friends: conversations were scattered between group chats and quickly buried by other topics; the important details of the trip were split between spreadsheets, Google Maps, and chats; it was difficult to determine what needed to happen next to</p></div>]]></description><link>https://blog.kenforthewin.com/tripjam/</link><guid isPermaLink="false">67e96591fd04700001f1d6bf</guid><dc:creator><![CDATA[Kenny Bergquist]]></dc:creator><pubDate>Sun, 30 Mar 2025 16:04:41 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p><a href="https://tripjam.com">TripJam</a> was born out of a frustration with planning trips with my friends: conversations were scattered between group chats and quickly buried by other topics; the important details of the trip were split between spreadsheets, Google Maps, and chats; it was difficult to determine what needed to happen next to keep the planning on track.</p>
<p>TripJam is an all-in-one application for collaborative trip planning. Your chat history, map, and itinerary are all in a single location; no more context switching or endless scrolling through the chat history to determine what to do next. A built-in AI assistant is deeply integrated with the platform, and can help with anything from destination recommendations to planning your entire itinerary for you. You can seamlessly invite your friends to the trip and all updates are shared in real-time to tripmates. Try it out today! <a href="https://tripjam.com">https://tripjam.com</a></p>
<p><a href="https://tripjam.com"><img src="https://cdn.tripjam.app/desktop.png" alt="App"></a></p>
</div>]]></content:encoded></item><item><title><![CDATA[A meditation on three dead side projects]]></title><description><![CDATA[<div class="kg-card-markdown"><p>If you're building a side project your motivation likely fits in the overlap of two broad categories: education (and, in general, practicing the joy of your craft) and money (clout, fame, etc). The story of these three dead projects is ultimately the story of how (not) to balance these motivations</p></div>]]></description><link>https://blog.kenforthewin.com/a-meditation-on-three-dead-side-projects/</link><guid isPermaLink="false">6136449037c03000015ea328</guid><dc:creator><![CDATA[Kenny Bergquist]]></dc:creator><pubDate>Mon, 06 Sep 2021 18:04:04 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>If you're building a side project your motivation likely fits in the overlap of two broad categories: education (and, in general, practicing the joy of your craft) and money (clout, fame, etc). The story of these three dead projects is ultimately the story of how (not) to balance these motivations depending on your goals.</p>
<p>On the one hand, these projects have made me into a better developer, taught me a ton, and scratched a creative itch. On the other, I've collectively sunk hundreds or thousands of hours into these projects that ultimately have no users and zero revenue. Looking back, I feel that some of this time was wasted because of my hope that a better product would attract more users without any investment in marketing or consistent attempt to bring in these users.</p>
<h1 id="litchan"><a href="https://litchan.com">Litchan</a></h1>
<p><img src="https://storage.googleapis.com/brrrr/7a4a2331-2104-4366-a758-4468004f8330.png" alt="litchan"></p>
<p><strong>Description:</strong> Litchan started out as a Reddit clone using Phoenix LiveView. The goal was a minimalist UI like Hacker News but with live updating comments, typing indicators, presence, etc. You can also sync RSS feeds to any boards (subreddits) you create.</p>
<p><strong>Tech</strong></p>
<ul>
<li>Elixir/Phoenix LiveView</li>
<li>PostgreSQL</li>
<li>Docker / Google Cloud</li>
<li>TailwindCSS</li>
</ul>
<p><strong>Status:</strong> Dead for the most part. Because it syncs RSS feeds, this is the only side project that I actively use. The core functionality is all there, but with no users I don't see reason to keep adding refinements or QoL improvements.</p>
<p><strong>What I learned</strong> Phoenix LiveView is insanely cool. A project like litchan is not going to get off the ground without a sufficient nexus of active users, which would likely require sizeable marketing investment. Sometimes building a product that only you use is ok.</p>
<h1 id="quickq"><a href="https://quickq.app">QuickQ</a></h1>
<p><img src="https://storage.googleapis.com/brrrr/quickq/messages-newest-web.png" alt="quickq"></p>
<p><strong>Description</strong>: QuickQ is a knowledge base / Q&amp;A platform for organizations with deep slack integration. Answer questions on Slack then save it to QuickQ, allowing you to search and post the answer all through Slack.</p>
<p><strong>Tech</strong></p>
<ul>
<li>Ruby on Rails</li>
<li>Redis / Sidekiq</li>
<li>React / Semantic UI</li>
<li>PostgresSQL</li>
<li>Docker / Google Cloud</li>
</ul>
<p><strong>Status</strong>: Dead.. occasionally folks will find the app on the Slack directory and try it out, but nobody is on the paid plan.</p>
<p><strong>What I learned:</strong> Building a slack app can be fun, especially when it's solving a problem you've seen in your own day job. Getting traction for a B2B product like this is notoriously difficult with giants like atlassian (and to a lesser extent stack overflow). I didn't really know where to start with that.</p>
<h1 id="metachat"><a href="https://metachat.app/">Metachat</a></h1>
<p><img src="https://storage.googleapis.com/brrrr/ghosty/Screen%20Shot%202019-01-10%20at%209.21.47%20PM.png" alt="metachat"></p>
<p><strong>Description</strong> A group chat web app utilizing e2e encryption and message tagging.</p>
<p><strong>Tech</strong></p>
<ul>
<li>React</li>
<li>OpenPGPJS</li>
<li>Elixir / Phoenix Websockets</li>
<li>PostgreSQL</li>
</ul>
<p><strong>Status</strong> Dead, aside from the occassional sign up from users who want to test it out.</p>
<p><strong>What I learned</strong> Learned a ton about public key encryption, web workers, and websockets. Metachat would never have really gained users without mobile apps and a ton of other features commonly found in the free apps like Messenger, WhatsApp, etc.</p>
<h1 id="">~</h1>
<p>You'll notice a common theme amongst these three. I learned a lot across a broad variety of technologies and even built a project that I use on a daily basis. But as the project came to maturity, I began investing too much time into refining the project in hopes that it would attract users, while opting only to share the product through niche channels such as hacker news, product hunt, and reddit.</p>
<p>What I learned ultimately is that you should be honest with yourself about your goals for a side project. If your intention is to learn, then build something that will be interesting enough to you to keep your attention, and build until you feel comfortable with the tech you've chosen. If you're looking to make money and attract users, you can't just start building something and hope the users come to it. Your goal at that point should not be to learn Rust or whatever new tech interests you, it should be to recruit as many users as possible in the smallest development time possible so you can quickly begin iterating. In that sense learning new languages or technologies is antithetical to the goal, and you're simply fooling yourself into thinking it's time well spent.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Microservices encourage knowledge siloing]]></title><description><![CDATA[<div class="kg-card-markdown"><p>My motivation for developing <a href="https://quickq.app">QuickQ</a>, a knowledge sharing platform, is to eliminate knowledge silos within growing teams. One of the most common sources of knowledge siloing is from microservices. I don't oppose microservices - once you're past a certain scale they have their place - but I have noticed they</p></div>]]></description><link>https://blog.kenforthewin.com/microservices-encourage-knowledge-siloing/</link><guid isPermaLink="false">5d8a59210a47f100011028ab</guid><dc:creator><![CDATA[Kenny Bergquist]]></dc:creator><pubDate>Sat, 01 Feb 2020 18:59:17 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>My motivation for developing <a href="https://quickq.app">QuickQ</a>, a knowledge sharing platform, is to eliminate knowledge silos within growing teams. One of the most common sources of knowledge siloing is from microservices. I don't oppose microservices - once you're past a certain scale they have their place - but I have noticed they can isolate teams and reduce knowledge sharing. Specifically, I believe the <a href="https://microservices.io/patterns/decomposition/service-per-team.html">service per team</a> approach is an anti-pattern and leads to an increase of single failure points in your org.</p>
<p>Let's take infrastructure as an example: a common practice as your team grows is to assign a group of engineers to infra/devops and other internal tooling. Typically, services that wrap common developer tasks such as spinning up a new Kubernetes cluster or provisioning a database are deployed. These tools can allow for greater productivity but they can also encourage developers to rely on a less-sophisticated abstraction of an underlying tool instead of connecting their knowledge of the application requirements with the infrastructure that runs it. If your cluster service removes the ability to fine-tune and understand the underlying Kubernetes concepts, then this knowledge has been siloed into a single team that will always be fighting to stay ahead of the next incident.</p>
<p>The flip side of this approach is an organization that empowers developers at every level to dive deep into devops tooling and add functionality to your internal tools as needed by their services. But this only gets you so far: now instead of having a service per team, you're creating a platform per team, with each team contributing to the underlying infrastructure of their platforms but siloed from each other. Without a culture of collaboration your teams will inevitably work as islands, missing opportunity after opportunity to integrate and create a unified application. Instead, common requirements between teams will result in redundant work and wasted time.</p>
<p>Realtime communication tools such as Slack can help with the problem but only if there's a culture of knowledge sharing within the organization. Empower your product and engineering folks to make announcements and ask questions, and importantly, record the answers to these questions in a place that everyone can access. Engineers don't want to feel enclosed by a single service: they want to be empowered to create and contribute at any layer needed to make a beautiful product.</p>
<p>I created <a href="https://quickq.app">QuickQ</a>, a knowledge sharing platform and Slack app, as a way to collect knowledge in the place where it's actually being shared: directly within your communication app. If the barrier to sharing knowledge becomes so low that as soon as a question is answered it gets recorded for future devs to search through and easily find, knowledge silos can be eliminated. It's free forever for up to five users so give it a try.</p>
<p>I don't think an application alone can fix knowledge siloing. It certainly requires a shift in culture and procedure. But so often valuable knowledge gets buried in the firehose that is your communication app. QuickQ can help prevent this and get your org started on the path to creating strong, interconnected engineering teams.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Knowledge sharing optimization for startups]]></title><description><![CDATA[<div class="kg-card-markdown"><p>In the rapid-growth phase of a startup, one of the most challenging problems is ensuring that your growing team has access to the required knowledge to properly and efficiently do their jobs. This is an issue in every company, but it's particularly difficult for a startup for several reasons. With</p></div>]]></description><link>https://blog.kenforthewin.com/preventing-knowledge-silos/</link><guid isPermaLink="false">5d88d0b50a47f100011028a8</guid><dc:creator><![CDATA[Kenny Bergquist]]></dc:creator><pubDate>Fri, 27 Sep 2019 13:04:37 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>In the rapid-growth phase of a startup, one of the most challenging problems is ensuring that your growing team has access to the required knowledge to properly and efficiently do their jobs. This is an issue in every company, but it's particularly difficult for a startup for several reasons. With a sudden and massive increase in hiring, the core team finds itself juggling two roles, maintenance of the platform and onboarding/mentorship, and becomes overwhelmed. The technologies and techniques used are also in flux as the platform is forced to scale. And employees are often not incentivized to share knowledge, or in the worst cases are implicitly incentivized against knowledge sharing.</p>
<p>While the first two issues can largely be solved with the proper tooling to document and share knowledge, the last issue cannot be fully solved with technology; the solution must include a strong culture of rewarding knowledge sharing and preventing knowledge siloing.</p>
<h1 id="makesharingknowledgeeasy">Make sharing knowledge easy</h1>
<p>To empower your employees to share knowledge, you should make it as easy as possible for them to document information relevant to the company, find the right information when needed, and quickly answer questions without getting overwhelmed. There are countless tools to collect and categorize a team's knowledge, but from my experience many of these solutions fall short when it comes to actually finding and sharing the information contained in them.</p>
<p>As a software engineer at a rapidly-growing startup, I noticed that tools like Confluence often went unused or became outdated, and as a result the same questions were asked and answered on Slack time and time again. That's part of the reason why I developed <a href="https://quickq.app">QuickQ</a>, a web platform and Slack app designed to solve this &quot;last mile&quot; issue of knowledge sharing. By making it easy to collect and share knowledge within the main communication tool of the company, my hope is that knowledge sharing becomes more collaborative, more real-time, and less of a time sink.</p>
<p><img src="https://storage.googleapis.com/brrrr/quickq/messages-newest-web.png" alt="QuickQ"></p>
<h1 id="rewardknowledgesharing">Reward knowledge sharing</h1>
<p>Regardless of how easy it is to share knowledge, it won't become a part of your company's culture if it's not properly incentivized. Knowledge indexing and sharing requires careful thought, time, and empathy for those who are learning, yet it is often ignored or taken for granted. Managers should explicitly encourage knowledge sharing, account for it when planning work, and offer praise when they see it happening. Consistently answering questions related to technical processes or onboarding saves the company a massive amount of time and effort, ultimately affecting the bottom line.</p>
<p>I'm currently working on a detailed admin dashboard for <a href="https://quickq.app">QuickQ</a> that will track who is answering the most questions and sharing these answers when needed so you can easily identify the greatest contributors on your team. But technology alone cannot solve this problem: part of a knowledge sharing culture must account for contributions when determining employee compensation and raises.</p>
<h1 id="discourageknowledgesiloing">Discourage knowledge siloing</h1>
<p>The problem of knowledge silos is perhaps the most difficult to solve, and while tools like QuickQ certainly help, the most effective solution is a culture of knowledge sharing. Part of this is that managers should be adept at identifying knowledge siloing and explicitly discourage it. For example, when one engineer is always fixing the same issue that crops up in your production environment, ask yourself if their process can be easily documented and shared to new hires. If you instead reward the engineer for fixing the same problem a fifth time, this is implicitly encouraging siloing and may be preventing a more collaborative approach to permanently addressing the issue. Other strategies for discouraging siloing include promoting cross-team collaboration and introduction of shared goals and metrics.</p>
<p>I became interested in addressing the problem space of knowledge sharing after seeing patterns and pain-points in a rapid-growth startup. If you're curious about a new and evolving approach to knowledge sharing, check out my new platform, <a href="https://quickq.app">QuickQ</a>.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Two-factor auth with public-key cryptography]]></title><description><![CDATA[<div class="kg-card-markdown"><p><b>Note:</b> As was pointed out in numerous comments on Hacker News and here, this approach is problematic for a number of reasons. Transferring a secret key between devices in this manner can leave you vulnerable to serverside exploits and increases the likelyhood that your key will be exposed. This method</p></div>]]></description><link>https://blog.kenforthewin.com/two-factor-auth-with-public-key-cryptography/</link><guid isPermaLink="false">5c5737c20a47f10001102899</guid><dc:creator><![CDATA[Kenny Bergquist]]></dc:creator><pubDate>Sun, 03 Feb 2019 19:44:25 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p><b>Note:</b> As was pointed out in numerous comments on Hacker News and here, this approach is problematic for a number of reasons. Transferring a secret key between devices in this manner can leave you vulnerable to serverside exploits and increases the likelyhood that your key will be exposed. This method also doesn't allow key revokation or forward secrecy. The goal of my project, <a href="https://github.com/kenforthewin/mentat">Mentat</a>, is to strike a good balance between privacy and convenience/features; it's an early-stage project and I'm still trying to find that balance. If your goal is ultimate privacy, there are a bunch of projects already out there that are better-suited for your needs: check out Signal, Matrix, etc. That being said, what follows is the original article...</p>
<p>When it came time to implement 2FA in my open-source project <a href="https://github.com/kenforthewin/mentat">Mentat</a>, I wanted to try something a little different. As an end-to-end encrypted chat app, asymmetric encryption was already an important aspect of the platform, and was easy enough to implement using <a href="https://github.com/openpgpjs/openpgpjs">OpenPGP.js</a>. When a user signs up for the platform, a keypair is generated and the public key is saved in the database as part of that user's identity. But an issue arises when the user wants to sign into a different device: how can the user's private key be transmitted in a way that doesn't reveal their credentials to the server? As it turns out, I was able to solve this issue and add a second authentication factor in the same step.</p>
<p>Signing in on Mentat starts with the user inputting their email and password on a new device. When this occurs, the device will generate a brand new keypair and send the public key to the server. The server will check if this public key matches the one stored for the user, and this check will fail because a keypair has already been added as part of the signup process. The user is then shown a wall explaining that the device needs to be authenticated:</p>
<img alt="Auth request" src="https://storage.googleapis.com/brrrr/ghosty/Screen%20Shot%202019-02-03%20at%202.21.30%20PM.png" style="width: 500px; height: 500px">
<p>Meanwhile, a request will be sent to all of the user's previously-authenticated devices. The request will contain the public key of the new device and will ask if this request should be accepted:</p>
<img alt="Auth request" src="https://storage.googleapis.com/brrrr/ghosty/Screen%20Shot%202019-02-03%20at%202.21.22%20PM.png" style="width: 500px; height: 500px">
<p>If the request is accepted, the authenticated device will encrypt the user's private key using the new device's public key and transmit this packet to the new device. The new device will decrypt the user private key and replace its keypair with the valid keys, thus authenticating this device and receiving the user keypair at the same time. With the valid keys, the new device is able to decrypt group chat messages received from the server and send new messages under a single identity between devices.</p>
<p>Some work still needs to be done to increase the security of this process. For example, the server (or another device) should verify that the new device truly owns the private key before lifting the 2FA gate on the new device. This can be achieved by simply signing a message and having this signature verified. Additionally, the request could list some details, including model or OS, of the new device requesting access, in case a fraudulent request was sent.</p>
<p>Feedback or security concerns? Let me know in the comments. Wanna try the platform? <a href="https://metachat.app/">Sign up here!</a></p>
</div>]]></content:encoded></item><item><title><![CDATA[Next-gen group chat: The power of tags]]></title><description><![CDATA[<div class="kg-card-markdown"><p><video width="100%" height="auto" loop controls src="https://storage.googleapis.com/brrrr/mentat-mpeg.mp4"></video></p>
<p>When I announced my group chat application, <a href="https://groupchat.kenforthewin.com/">Mentat</a>, on some forums a few weeks ago, a common question was &quot;Why would I use this over the apps already out there?&quot; Fair enough; while I've derived a lot of personal enjoyment from designing this side project from the ground</p></div>]]></description><link>https://blog.kenforthewin.com/why-i-made-a-group-chat-app-the-power-of-tags/</link><guid isPermaLink="false">5b3670d30a47f10001102894</guid><dc:creator><![CDATA[Kenny Bergquist]]></dc:creator><pubDate>Fri, 29 Jun 2018 19:44:26 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p><video width="100%" height="auto" loop controls src="https://storage.googleapis.com/brrrr/mentat-mpeg.mp4"></video></p>
<p>When I announced my group chat application, <a href="https://groupchat.kenforthewin.com/">Mentat</a>, on some forums a few weeks ago, a common question was &quot;Why would I use this over the apps already out there?&quot; Fair enough; while I've derived a lot of personal enjoyment from designing this side project from the ground up, that alone can't be expected to attract users. What I'd like to outline in this post is the key feature that sets Mentat apart from other group chat platforms, its intended userbase, and a set of design choices that I consider crucial to the success of an app like this.</p>
<h1 id="thekeyfeaturetagallthethings">The key feature: Tag all the things</h1>
<p>Realtime chat solutions seem to fall into two broad categories: business and casual. With a business-oriented app like Slack, users and topics are divided into clearly defined groups or channels. This segmentation of the conversation allows for businesses large and small to keep their chats organized and as free of noise as possible. Importantly, it also serves to exclude users from conversations that they aren't needed on or shouldn't see.</p>
<p>Casual platforms like Facebook Messenger on the other hand are intended to be inclusive, linear conversations where every group user can view every message. The intended userbase is a group of friends rather than coworkers. While Messenger and others have crafted great features for linear group chat, what seems to be lost on the road between business and casual solutions is categorization of messages: a fantastic realtime chat app can fall apart when a user wants to view past messages by a particular topic or category, even with good built-in search features.</p>
<p>Mentat is an attempt to bridge this gap. With Mentat, the goal is to have categorization of messages be as fluid and natural as possible while still maintaining the inclusive community of a casual chat app. It achieves this with message tags (think Twitter hashtags). If you're posting a funny meme in your group chat, for example, you can post the link followed by #meme to have the tag immediately present on the posted message. Alternatively,  after you've posted the meme any member of the group can tag your message for easy retrieval later. The conversation continues linearly, but if you're interested in viewing this meme or one posted in the past, you can select the tag and immediately see all past messages tagged #meme. In the same way, a message can be tagged with multiple categories and you can select multiple tags to see an ever-growing list of messages relevant to your current focus.</p>
<p>Mentat is intended for groups of friends, but I can also see a use-case for small teams. In the latter case, creating &quot;channels&quot; wouldn't be this laborious manual process, they would be created on the fly by embedding tags. It eschews the exclusive aspect of traditional channels in lieu of a tight-knit, deeply-categorized linear conversation.</p>
<h1 id="asolidfoundation">A solid foundation</h1>
<p>Tagging is a key feature of Mentat but certainly not the only one. I view some features and design choices as crucial to the success of a chat app:</p>
<ul>
<li><strong>Privacy.</strong> Users don't want the feeling that someone is staring over their shoulder as they type. A chat app should end-to-end encrypt its messages; I use OpenPGP.js for this.</li>
<li><strong>Open-source.</strong> Some may view this as optional. But how can you gain user trust that you're not harvesting data and that conversations are indeed private unless they can see the source and optionally host it themselves? You can view the source <a href="https://github.com/kenforthewin/mentat">here</a>.</li>
<li><strong>Link previews.</strong> Users want to see where a link leads before clicking. In the case of an image link, the preview suffices without having to click in most cases. This feature requires the server to ping the link first; if you don't want the server to know your links, you can turn this feature off.</li>
<li><strong>Notification system</strong> Users want good defaults for notifications. I used Web Notifications for this, and I'm planning push notifications as the platform expands.</li>
</ul>
<p>Thanks for reading. If this sort of thing interests you, check out the live demo <a href="https://groupchat.kenforthewin.com/">here</a> or the <a href="https://github.com/kenforthewin/mentat">Github repo</a>.</p>
</div>]]></content:encoded></item><item><title><![CDATA[The Relentless Optimism of Software Tooling]]></title><description><![CDATA[<div class="kg-card-markdown"><p>At work I've recently had to build and release an Android app many times over, each time with slight bug fixes and updates, for a client to test. I'm hoping to incorporate Fastlane and/or a CI tool to automate this soon, but in the meantime I'm burdened with the</p></div>]]></description><link>https://blog.kenforthewin.com/the-relentless-optimism-of-software-tooling/</link><guid isPermaLink="false">5ad8b1080a47f1000110288f</guid><dc:creator><![CDATA[Kenny Bergquist]]></dc:creator><pubDate>Fri, 20 Apr 2018 13:53:15 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>At work I've recently had to build and release an Android app many times over, each time with slight bug fixes and updates, for a client to test. I'm hoping to incorporate Fastlane and/or a CI tool to automate this soon, but in the meantime I'm burdened with the banal task of building an APK, signing, optimizing, uploading, and repeating many times over. Although it's the same boring process every time, the amount of software tooling involved is staggering: Gradle, jarsigner, zipalign, and since this is a React Native project, node, yarn, Webpack... you get the idea. Each one of these tools produces dozens or hundreds of lines of logs as well, so for better or worse you learn to ignore the vast majority of tooling output. On this note, I just recently noticed a warning which I had never previously paid attention to:</p>
<p><img src="https://storage.googleapis.com/brrrr/ghosty/Screen%20Shot%202018-04-19%20at%2011.08.13%20AM.png" alt="jarsigner" title="jarsigner."></p>
<p>After jarsigner does its thing, it warns that my certificate expires, and that this may start affecting users... in 2045! (or after revokation). What a vote of confidence that my app will not only be initially successful but users will be signing up in droves to verify the APK's signature 30 years from now. Thanks jarsigner!</p>
<p>Do you have a funny or interesting example of software tooling being relentlessly optimistic? Leave a comment!</p>
</div>]]></content:encoded></item><item><title><![CDATA[What Erlang Taught Me About Distributed Systems]]></title><description><![CDATA[<div class="kg-card-markdown"><p>Learning Erlang, and the OTP framework in particular, has given me a better understanding of distributed systems and their fundamental building blocks. Before this, my experience with distributed systems was solely in the realm of Kubernetes, which we use at work for deploying scalable, distributed web services.</p>
<p><a href="https://www.amazon.com/Erlang-OTP-Action-Martin-Logan/dp/1933988789/ref=as_li_ss_il?ie=UTF8&qid=1523746571&sr=8-1&keywords=erlang+and+otp+in+action&dpID=51hiUgdU90L&preST=_SX218_BO1,204,203,200_QL40_&dpSrc=srch&linkCode=li1&tag=dron05d-20&linkId=a574bcbfab19e27b79ab5d57b0f5438f" target="_blank"><img border="0" style="height:300px;width:250px;display: block;margin-left: auto;margin-right: auto;" src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=1933988789&Format=_SL110_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=dron05d-20"></a></p>
<p>I was introduced</p></div>]]></description><link>https://blog.kenforthewin.com/what-erlang-taught-me-about-distributed-systems/</link><guid isPermaLink="false">5ad278e30a47f1000110288c</guid><dc:creator><![CDATA[Kenny Bergquist]]></dc:creator><pubDate>Sat, 14 Apr 2018 23:20:54 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>Learning Erlang, and the OTP framework in particular, has given me a better understanding of distributed systems and their fundamental building blocks. Before this, my experience with distributed systems was solely in the realm of Kubernetes, which we use at work for deploying scalable, distributed web services.</p>
<p><a href="https://www.amazon.com/Erlang-OTP-Action-Martin-Logan/dp/1933988789/ref=as_li_ss_il?ie=UTF8&qid=1523746571&sr=8-1&keywords=erlang+and+otp+in+action&dpID=51hiUgdU90L&preST=_SX218_BO1,204,203,200_QL40_&dpSrc=srch&linkCode=li1&tag=dron05d-20&linkId=a574bcbfab19e27b79ab5d57b0f5438f" target="_blank"><img border="0" style="height:300px;width:250px;display: block;margin-left: auto;margin-right: auto;" src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=1933988789&Format=_SL110_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=dron05d-20"></a></p>
<p>I was introduced to Erlang through Elixir. The Phoenix framework leverages Elixir to build performant, functional web applications and it often comes up in the Rails community as a better-performing alternative to Ruby. Elixir depends on the decades of development behind the Erlang VM and in many ways is just syntactic sugar on top of Erlang/OTP. For that reason I wanted to get a better understanding of the underlying technologies so I picked up the book you see above, <a href="https://www.amazon.com/Erlang-OTP-Action-Martin-Logan/dp/1933988789/ref=as_li_ss_il?ie=UTF8&amp;qid=1523746571&amp;sr=8-1&amp;keywords=erlang+and+otp+in+action&amp;dpID=51hiUgdU90L&amp;preST=_SX218_BO1,204,203,200_QL40_&amp;dpSrc=srch&amp;linkCode=li1&amp;tag=dron05d-20&amp;linkId=a574bcbfab19e27b79ab5d57b0f5438f">Erlang And OTP In Action</a>.</p>
<h2 id="buildingblocksofadistributedsystem">Building Blocks of a Distributed System</h2>
<p>In Erlang one can take distributed computing for granted. To start off, all code within Erlang runs in processes. You can look at a process in Erlang and Elixir as a basic building block of the language much like one might view a class within an object-oriented language. An application is thus a tree of interconnected processes. Because processes are so native to Erlang, all of these individual processes can automatically be spread across the available cores of the system running them. And because processes are each isolated environments which depend on a built-in system of message passing, the Erlang environment can treat a distributed network of systems the same way it treats a single one. This may sound like magic, but in reality it is leveraging the work that experts have put into building possibly the most scalable environment currently available for development.</p>
<p>Although Kubernetes may immediately seem to solve a very different problem than Erlang (for one, Kubernetes isn't a programming language), there are some important similarities when it comes to the structure of its distributed implementation. Much like Erlang uses the basic building block of a process, Kubernetes uses the container, or more generally the pod, as a base. Every node in a kube cluster can run one or more pods, just the same way that every node in an Erlang cluster can run one or more processes.</p>
<h2 id="comparingapis">Comparing APIs</h2>
<p>If we want to discuss implementation details it would be helpful to introduce OTP. OTP is a built-in framework within Erlang for building applications. Building on top of the process-based system, OTP (Open Telecom Platform) offers a further abstraction known as a GenServer. GenServer adds message sending and receiving, storage, TCP, and a ton of other functionality to a process, allowing you to spin up a server that reacts to input and performs useful functions very quickly. Furthermore, OTP adds the concept of a supervisor, a parent process that manages the lifecycle of the child GenServers. In cases where a child process fails unexpectedly, the supervisor can restart that individual process without having it affect the other process environments.</p>
<p>Kubernetes leverages many of the concepts we just discussed. To name a few, Kubernetes has an ApiServer running on the master which acts as the supervisor for its cluster. Like OTP, this API will ensure that the pods which are running are healthy based on their underlying deployment (very similar to a child specification in Erlang). And if a pod goes down unexpectedly, it will be restarted based on the restart strategy; same with OTP!</p>
<p>To wrap up, it's evident that much of the work that went into Erlang has inspired the work of Kubernetes. You can look at Kubernetes as a language-agnostic implementation of the same kind of distributed system that's found in the Erlang environment. It's inspiring to see the way open source projects have built, incremented on and in many cases directly inspired each other over the years.</p>
</div>]]></content:encoded></item><item><title><![CDATA[DataTables for React: APIs in Phoenix and Rails]]></title><description><![CDATA[<div class="kg-card-markdown"><p>A while ago I published <a href="https://blog.kenforthewin.com/bring-datatables-to-react-redux/">redux-remote-datatable</a>. It's a React and Redux-based table for serverside-processed data, and it looks like this:</p>
<p><img src="https://storage.googleapis.com/brrrr/better-datatable.gif" alt="project example"></p>
<p>At the request of a GitHub user, I added an example implementation of the API written in Ruby on Rails. You can find that <a href="https://github.com/kenforthewin/legislators-api">here</a>. This is the server I</p></div>]]></description><link>https://blog.kenforthewin.com/datatables-for-react-apis-in-phoenix-and-rails/</link><guid isPermaLink="false">5ab83f517b1ae80001344433</guid><dc:creator><![CDATA[Kenny Bergquist]]></dc:creator><pubDate>Mon, 26 Mar 2018 01:35:43 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>A while ago I published <a href="https://blog.kenforthewin.com/bring-datatables-to-react-redux/">redux-remote-datatable</a>. It's a React and Redux-based table for serverside-processed data, and it looks like this:</p>
<p><img src="https://storage.googleapis.com/brrrr/better-datatable.gif" alt="project example"></p>
<p>At the request of a GitHub user, I added an example implementation of the API written in Ruby on Rails. You can find that <a href="https://github.com/kenforthewin/legislators-api">here</a>. This is the server I used to capture the above gif.</p>
<p>Over the weekend I made another API implementation in an effort to showcase the ease of switching backend services for the component and to learn a new framework. I chose Phoenix (Elixir), and the result can be found <a href="https://github.com/kenforthewin/legislators-api-phoenix">here</a>. Both this and the Rails project are fully dockerized and utilize docker-compose.</p>
<p>I really enjoyed my development experience in Phoenix. Rails knowledge ports easily to the new framework and I find myself being fairly productive in it already. In terms of performance, I can only speak to development but I recorded 3x faster speeds with Phoenix, with a page for the table rendering in 200ms compared to Rail's 600ms. This made the datatable noticably snappier.</p>
<p>By the way, props to the <a href="https://github.com/unitedstates/congress-legislators">congress-legislators</a> GitHub project which the seeds for both APIs depend on.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Bringing DataTables to React-Redux]]></title><description><![CDATA[<div class="kg-card-markdown"><p><img src="https://storage.googleapis.com/brrrr/better-datatable.gif" alt="project example"></p>
<p>One of my most frequently-used open-source tools is datatables.net, a jquery-based interactive table with dynamic sorting and searching. I sought to bring the simplicity of that project's serverside API to React, using Redux to handle state changes. The result is <a href="https://github.com/kenforthewin/react-redux-datatable">redux-remote-datatable</a>.</p>
<p>I limited the scope solely to server-processed data</p></div>]]></description><link>https://blog.kenforthewin.com/bring-datatables-to-react-redux/</link><guid isPermaLink="false">5aa354025962b100012b215a</guid><dc:creator><![CDATA[Kenny Bergquist]]></dc:creator><pubDate>Sat, 10 Mar 2018 03:51:34 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p><img src="https://storage.googleapis.com/brrrr/better-datatable.gif" alt="project example"></p>
<p>One of my most frequently-used open-source tools is datatables.net, a jquery-based interactive table with dynamic sorting and searching. I sought to bring the simplicity of that project's serverside API to React, using Redux to handle state changes. The result is <a href="https://github.com/kenforthewin/react-redux-datatable">redux-remote-datatable</a>.</p>
<p>I limited the scope solely to server-processed data since this is primarily how I used datatables in the past. Take a look at the project readme for more info or to get started.</p>
</div>]]></content:encoded></item><item><title><![CDATA[State of the 10x Programmer in 2018]]></title><description><![CDATA[<div class="kg-card-markdown"><p><strong>tl;dr: A 10x programmer does not necessarily know one language/framework ten times as well. More likely she uses 10 programming tools just as well as others who master only one.</strong></p>
<p>Look up <code>programmer</code> on Indeed and you'll immediately see how <em>fragmented</em> CS jobs are. It's the nature of</p></div>]]></description><link>https://blog.kenforthewin.com/state-of-the-10x-programmer-in-2018/</link><guid isPermaLink="false">5a8994405962b100012b2156</guid><dc:creator><![CDATA[Kenny Bergquist]]></dc:creator><pubDate>Sun, 18 Feb 2018 15:24:35 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p><strong>tl;dr: A 10x programmer does not necessarily know one language/framework ten times as well. More likely she uses 10 programming tools just as well as others who master only one.</strong></p>
<p>Look up <code>programmer</code> on Indeed and you'll immediately see how <em>fragmented</em> CS jobs are. It's the nature of the field: each discipline could perhaps take multiple lifetimes to completely master, so of course there's no one who &quot;knows it all&quot;. But in this post I'd like to make the point that titles have become too fragmented, and that a 10x programmer is really simply anyone who is able to cross these boundaries to find the right tool and employ it proficiently.</p>
<p>Spend a decent amount of time in this field and you'll run into the idea that overusing a single tool/framework can make one <em>myopic</em>. I've certainly noticed this with Ruby on Rails.</p>
<p>10x: &quot;Why use RoR for an API-only app with no database when you could go with Sinatra? Or even serverless?&quot;</p>
<p>1x: &quot;Well, those other options aren't RoR&quot;.</p>
<p>The difference here is obviously not that 10x is so much better at Rails than 1x. Rather it's that 10x has the knowledge that there are better solutions. This distinction becomes even more important when you're crossing languages, because languages are designed to solve, in some cases, massively different problems depending on their architecture. In the real world, problems don't conform to a particular framework or even job title. Therefore, 10x crosses these language barriers when necessary, even if she has a preferred language.</p>
<p>In my field, devops is just another tool in the toolbelt. But also it's a whole job title because, like I mentioned in the beginning, every discipline has a massive knowledge depth. A 10x programmer understands that tools like Kubernetes and serverless aren't magical realms for devops to deal with, they are simply tools that abstract away concepts that she already knows. Kubernetes abstracts away distributed computing just like nginx abstracts away http.</p>
<p>Finally, I don't believe 10x is some exclusive club. In many cases, a 10x programmer has put ten times the effort/time into learning these tools. And so the other side of that is any 1x programmer can become 10x with time and effort. If you're able to become proficient at one modern tool, you can certainly become proficient in others. And that's the path to advanced, senior, 10x, guru, or whatever you want to call it.</p>
</div>]]></content:encoded></item><item><title><![CDATA[A NetHack4-Server Docker Image]]></title><description><![CDATA[<div class="kg-card-markdown"><p><img src="https://blog.kenforthewin.com/content/images/2018/02/Screen-Shot-2018-02-02-at-12.50.31-PM.png" alt="Screen-Shot-2018-02-02-at-12.50.31-PM"></p>
<p>If you've ever played NetHack, the beloved roguelike, then you'll be very familiar with the above image. It's the first message you receive when beginning a game and, due to the permadeath nature of roguelikes, you're likely to begin several games in a single session of nethack. Out of the</p></div>]]></description><link>https://blog.kenforthewin.com/a-nethack4-server-docker-image/</link><guid isPermaLink="false">5a74a4ce62bf740001ea9900</guid><dc:creator><![CDATA[Kenny Bergquist]]></dc:creator><pubDate>Fri, 02 Feb 2018 18:17:17 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p><img src="https://blog.kenforthewin.com/content/images/2018/02/Screen-Shot-2018-02-02-at-12.50.31-PM.png" alt="Screen-Shot-2018-02-02-at-12.50.31-PM"></p>
<p>If you've ever played NetHack, the beloved roguelike, then you'll be very familiar with the above image. It's the first message you receive when beginning a game and, due to the permadeath nature of roguelikes, you're likely to begin several games in a single session of nethack. Out of the few reincarnations of the classic game that have appeared in the last couple decades, I'm partial to <a href="http://nethack4.org/">NetHack4</a>. It provides a nice polish to the original codebase as well as a practically endless list of improvements.</p>
<p>NetHack4's community offers a server to play on but I was interested in hosting my own. After a bit of digging, I was a little surprised that I didn't find a docker image for those who'd like to host their own server. So I created one <a href="https://github.com/kenforthewin/nethack4-server-docker">here</a>!</p>
<p>The most notable aspect of the NetHack4's stack is its use of <a href="https://man.openbsd.org/inetd.8">inetd</a>.  I hadn't previously encountered this tool but its been heavily used in the past as a sort of &quot;superserver&quot; which routes Internet requests by spawning other processes, and its main purpose is to conserve server resources by only requiring one daemon to be running to route requests. By passing the <code>-i</code> option to inetd on runtime, the process runs in the foreground and therefore docker can attach to this process on <code>docker run</code>.</p>
<p>You can also use the image as a way to play locally or to connect to a server. Do this by running something like this <code>docker run --entrypoint /app/nethack4 -it kenforthewin/nethack4-server</code>. And here's how to connect to my server:</p>
<p><img src="https://blog.kenforthewin.com/content/images/2018/02/Screen-Shot-2018-02-02-at-1.15.36-PM.png" alt="Screen-Shot-2018-02-02-at-1.15.36-PM"></p>
<p><img src="https://blog.kenforthewin.com/content/images/2018/02/Screen-Shot-2018-02-02-at-1.15.48-PM.png" alt="Screen-Shot-2018-02-02-at-1.15.48-PM"></p>
<p><img src="https://blog.kenforthewin.com/content/images/2018/02/Screen-Shot-2018-02-02-at-1.16.01-PM.png" alt="Screen-Shot-2018-02-02-at-1.16.01-PM"></p>
</div>]]></content:encoded></item><item><title><![CDATA[Introducing ZID - Zero Inventory Delete]]></title><description><![CDATA[<div class="kg-card-markdown"><p>ZID is my first foray into Shopify app development. I used the official <a href="https://github.com/Shopify/shopify_app">Shopify Rails engine</a> to get up and running quickly. After toying with a few ideas, I settled on making an app to find and delete products with 0 inventory. The use case is for vendors with large</p></div>]]></description><link>https://blog.kenforthewin.com/introducing-zid-zero-inventory-delete/</link><guid isPermaLink="false">5a6e173562bf740001ea98fc</guid><dc:creator><![CDATA[Kenny Bergquist]]></dc:creator><pubDate>Tue, 30 Jan 2018 17:59:31 GMT</pubDate><content:encoded><![CDATA[<div class="kg-card-markdown"><p>ZID is my first foray into Shopify app development. I used the official <a href="https://github.com/Shopify/shopify_app">Shopify Rails engine</a> to get up and running quickly. After toying with a few ideas, I settled on making an app to find and delete products with 0 inventory. The use case is for vendors with large amounts of low-inventory products such as a used book store. And the result of this is <a href="https://github.com/kenforthewin/shopify_zero_inv">zid</a>.</p>
<p>Here are a few screenshots of it in action:<br>
<img src="https://blog.kenforthewin.com/content/images/2018/01/Screen-Shot-2018-01-28-at-9.24.20-AM.png" alt="Screen-Shot-2018-01-28-at-9.24.20-AM"></p>
<p><img src="https://blog.kenforthewin.com/content/images/2018/01/Screen-Shot-2018-01-28-at-9.24.34-AM.png" alt="Screen-Shot-2018-01-28-at-9.24.34-AM"></p>
<p><img src="https://blog.kenforthewin.com/content/images/2018/01/Screen-Shot-2018-01-28-at-9.24.51-AM.png" alt="Screen-Shot-2018-01-28-at-9.24.51-AM"></p>
<p><img src="https://blog.kenforthewin.com/content/images/2018/01/Screen-Shot-2018-01-28-at-9.24.59-AM.png" alt="Screen-Shot-2018-01-28-at-9.24.59-AM"></p>
<p>All pretty simple stuff. I used Bootstrap and DataTables on the front-end. The products are indexed in ElasticSearch for quick retrieval.</p>
<p>The app is currently being reviewed by Shopify, so I'll update this article when I have a link to it in the Shopify App Store.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Introducing React Rails Auth]]></title><description><![CDATA[<div class="kg-card-markdown"><p>I chose to learn React due to both the hype and the fact that we had begun to code React Native at work. It's been a mostly positive experience with some exceptions here and there. I still don't see much of a point in it in small to midsize projects,</p></div>]]></description><link>https://blog.kenforthewin.com/untitled/</link><guid isPermaLink="false">5a5281b5820b3d00015ae599</guid><dc:creator><![CDATA[Kenny Bergquist]]></dc:creator><pubDate>Sun, 07 Jan 2018 20:45:35 GMT</pubDate><media:content url="https://blog.kenforthewin.com/content/images/2018/01/react-logo-1.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://blog.kenforthewin.com/content/images/2018/01/react-logo-1.png" alt="Introducing React Rails Auth"><p>I chose to learn React due to both the hype and the fact that we had begun to code React Native at work. It's been a mostly positive experience with some exceptions here and there. I still don't see much of a point in it in small to midsize projects, where speed of development would point me more towards jQuery/Coffeescript spaghetti than a full, modern front-end framework. Reason being, the most important aspect of early development on these projects is speed of iteration. Everything else comes second, including how reactive the front-end is. But React seems like a nice way to organize 1) massive front-end projects, where organizing into a lot of small components makes the project much cleaner, and 2) one-off components when traditional rendering becomes too slow during scaling. In the case of the former, my logical next step was to pair a Rails api-only app with a React frontend, and add user authentication.</p>
<p><a href="https://github.com/kenforthewin/react-rails-auth">react-rails-auth</a> is a fully dockerized repo that contains the app I coded to learn React and Redux. It's meant to be as unopinionated as possible so that I (and you!) can use it as a starter for any future projects in react/rails.</p>
<p>Nowadays, I'm warming up to the idea of <a href="https://github.com/turbolinks/turbolinks">turbolinks</a> and, very recently, <a href="https://github.com/stimulusjs/stimulus">stimulus</a> for the html you already have.</p>
</div>]]></content:encoded></item><item><title><![CDATA[Introducing Nginx Proxy Zero]]></title><description><![CDATA[<div class="kg-card-markdown"><p>Recently, I've been drawing a lot of inspiration from <a href="https://github.com/jwilder/nginx-proxy">nginx-proxy</a>. To summarize, nginx-proxy combines docker event-based nginx config generation with automatic nginx reloading. This allows you to define a <code>VIRTUAL_HOST</code> env variable on any container and have it added to the reverse proxy configuration in real time with no</p></div>]]></description><link>https://blog.kenforthewin.com/introducing-nginx-proxy-zero/</link><guid isPermaLink="false">5a516977820b3d00015ae597</guid><dc:creator><![CDATA[Kenny Bergquist]]></dc:creator><pubDate>Sun, 07 Jan 2018 00:50:35 GMT</pubDate><media:content url="https://blog.kenforthewin.com/content/images/2018/01/nginx-3.jpeg" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://blog.kenforthewin.com/content/images/2018/01/nginx-3.jpeg" alt="Introducing Nginx Proxy Zero"><p>Recently, I've been drawing a lot of inspiration from <a href="https://github.com/jwilder/nginx-proxy">nginx-proxy</a>. To summarize, nginx-proxy combines docker event-based nginx config generation with automatic nginx reloading. This allows you to define a <code>VIRTUAL_HOST</code> env variable on any container and have it added to the reverse proxy configuration in real time with no additional effort. This saves me a ton of time at work where we host dozens of integrations across several servers, and host as much as 10 different integrations on a single server. There is minimal downtime on deploy as well: just pull the new changes and start a new container with the same virtual host, and nginx-proxy does the rest (make sure to stop the old container though). Since I was doing this kind of deployment a lot, I thought I'd take a shot at automating this process through an api that runs in its own container. From what I've seen, there isn't much out there that fills this sort of niche. You can always host a single-node kubernetes cluster but this introduces a lot of overhead and configuration, definitely not as simple as running a container.</p>
<p><a href="https://github.com/kenforthewin/nginx_proxy_zero">nginx_proxy_zero</a> is my ongoing effort to create this sort of simple, idiomatic interface around deployments on a single instance. It's still rough in terms of features, and there's plenty I want to add to it. For now, there's one endpoint available to you: <code>POST /update_deployment</code>, and the body of your request will look something like this:</p>
<pre><code class="language-json">    {
      &quot;name&quot;: &quot;nginxproxyzero_some-zerodowntime-service_1&quot;,
      &quot;network&quot;: &quot;nginxproxyzero_default&quot;,
      &quot;image&quot;: &quot;jwilder/whoami&quot;,
      &quot;virtual_host&quot;: &quot;whoami.deve&quot;
    }
</code></pre>
<p>So, what's happening here? This setup assumes there is already a running container named <code>nginxproxyzero_some-zerodowntime-service_1</code> and that the intended action is to update this container via a rolling deployment. Zero will pull the new image and start a new container, then perform a health check. When the new container is healthy, it will rename the new container to the <code>name</code> param and stop/remove the old container. The API is a ruby/sinatra app and, imo, is very easy to read and track what's happening with the underlying API.</p>
<p>Are there tools out there like this? Would you find something like this useful or want to contribute to it? If so please reach out on <a href="https://news.ycombinator.com/item?id=16088424">HN</a> or <a href="https://twitter.com/qbernetes">Twitter</a>.</p>
</div>]]></content:encoded></item></channel></rss>