<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Zack Bartel</title><link>https://zackbartel.com/</link><description></description><generator>Gozer</generator><lastBuildDate>Tue, 28 Apr 2026 19:38:55 -0700</lastBuildDate><item><title>C++ is Great (again)!</title><link>https://zackbartel.com/blog/2025/11/cpp-is-great/</link><description>&lt;h3&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/C_(programming_language)&#34;&gt;C&lt;/a&gt; is my favorite programming language.&lt;/h3&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;It always has been and probably always will be. It allowed me to build cool stuff at a young age, create iOS and Mac software (via Objective-C), contribute to the Linux kernel, and work on embedded systems on airplanes.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;When I first learned C++ in college, it was great. It was like having a super-charged version of C. Classes, inheritance, and polymorphism, it felt modern and powerful.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;I last used C++ heavily around 2010. At that time the &lt;a href=&#34;https://learncodethehardway.com/blog/31-c-plus-plus-is-an-absolute-blast#the-c-template-metapocalypse&#34;&gt;Template Metapocalypse&lt;/a&gt; was in full swing. That experience made me not like programming.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;I was tired of dealing with the complexities. The syntax was verbose, error messages unusable, build systems a nightmare, and the community divided over best practices. I moved on to other languages: Go, Java/Scala, Swift, Python, Javascript, but I would often miss the power of C++.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Whenever I had to use JNI or some other &lt;a href=&#34;https://en.wikipedia.org/wiki/Foreign_function_interface&#34;&gt;FFI&lt;/a&gt; (Foreign Function Interface), I felt weak. I don&#39;t want to have to wait for some third-party library to bind to my favorite language. I want to write high-performance code directly. But at that time I just didn&#39;t feel like C++ was a reasonable choice.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;C++11 was on the horizon, promising to fix many of the issues, but the complexity of the language had already scared off many developers.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;I took a long break from C++.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h3&gt;Necessity is the mother of invention.&lt;/h3&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Fast forward to 2024, and I found myself needing to write some high-performance code for a project that couldn&#39;t tolerate garbage collection pauses. Python was too slow, Go&#39;s GC was unpredictable, and Rust&#39;s learning curve felt steep (and frankly I don&#39;t like it - &lt;code&gt;Arc&amp;lt;Mutex&amp;lt;DeezNutz&amp;gt;&amp;gt;&lt;/code&gt;).&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;I reluctantly opened up a C++ project, expecting the same old frustrations. What I found instead was a revelation.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h3&gt;Modern C++ is a different language.&lt;/h3&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;The C++ I discovered was not the C++ I had left behind. C++11, C++14, C++17, and C++20 had fundamentally transformed the language. The improvements weren&#39;t just incremental, they were transformative.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;&lt;strong&gt;Auto type deduction&lt;/strong&gt; means I&#39;m no longer writing &lt;code&gt;std::vector&amp;lt;std::string&amp;gt;::iterator&lt;/code&gt; everywhere. Now it&#39;s just &lt;code&gt;auto&lt;/code&gt;. The compiler figures it out, and my code is cleaner and more maintainable. It feels like I&#39;m writing Python, but with all the performance of C++.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;&lt;strong&gt;Lambda functions&lt;/strong&gt; changed everything about how I write callbacks and functional-style code. Before, I&#39;d create a functor class or a separate function. Now I can write inline lambda expressions that capture variables from the surrounding scope. Threading code went from painful to pleasant.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Speaking of which, &lt;strong&gt;the standard threading library&lt;/strong&gt; finally arrived. No more &lt;code&gt;pthread&lt;/code&gt; or platform-specific APIs. &lt;code&gt;std::thread&lt;/code&gt;, &lt;code&gt;std::mutex&lt;/code&gt;, &lt;code&gt;std::atomic&lt;/code&gt;—it&#39;s all there, cross-platform, and actually usable. I can write concurrent code that works on Windows, Linux, and Mac without conditional compilation everywhere.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;But the feature that truly won me back was &lt;strong&gt;smart pointers&lt;/strong&gt;. &lt;code&gt;std::unique_ptr&lt;/code&gt; and &lt;code&gt;std::shared_ptr&lt;/code&gt; eliminated most of my memory management headaches. No more agonizing over when to call &lt;code&gt;delete&lt;/code&gt;. No more memory leaks from forgotten cleanup in error paths. &lt;a href=&#34;https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization&#34;&gt;RAII&lt;/a&gt; (Resource Acquisition Is Initialization) finally felt natural and automatic. I know there&#39;s a lot of RAII hate out there, but for me it&#39;s very natural and makes sense. If it doesn&#39;t for you, that&#39;s okay too.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-cpp&#34;&gt;// old way - error prone&#xA;Widget* w = new Widget();&#xA;// ... do stuff ...&#xA;delete w; // Did I remember? What if there was an exception?&#xA;// new way - automatic cleanup&#xA;auto w = std::make_unique&amp;lt;Widget&amp;gt;();&#xA;// ... do stuff ...&#xA;// automatically cleaned up, exception safe&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;br&gt;&#xA;&lt;h3&gt;The ecosystem caught up too.&lt;/h3&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;It&#39;s not just the language. The tooling improved dramatically. CMake became tolerable. Package managers like Conan and vcpkg emerged. Compiler error messages, while still pretty bad, are lightyears better than the template error novels of 2010.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://microsoft.github.io/language-server-protocol/&#34;&gt;LSPs&lt;/a&gt; made code completion and navigation great. &lt;a href=&#34;https://github.com/zackb/dots/blob/main/.clang-format&#34;&gt;Clang format&lt;/a&gt; standardized code style. Sanitizers made debugging memory issues trivial. The whole development experience modernized. All of these things made editting code in &lt;a href=&#34;https://github.com/zackb/dots/tree/main/.config/nvim&#34;&gt;nvim&lt;/a&gt; a joy.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h3&gt;AI assistants are a secret weapon.&lt;/h3&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Here&#39;s something unsurprising: AI coding assistants have made C++ significantly more approachable. Those cryptic template errors that used to send me down rabbit holes for hours? I paste them into Claude or ChatGPT and get a clear explanation and fix in seconds.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Writing boilerplate for move constructors, copy assignment operators (did i forget the third &lt;code&gt;&amp;amp;&lt;/code&gt;?), or template specializations? The AI generates it correctly while I focus on the actual logic. Debugging subtle lifetime issues with references and pointers? The AI spots the problem I&#39;ve been staring at for twenty minutes.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;C++ has always had a steep learning curve with tons of gotchas: the rule of three, the rule of five, argument-dependent lookup, template SFINAE, undefined behavior landmines. AI assistants act like an expert pair programmer who knows all these pitfalls and helps you avoid them. They catch my mistakes before they cause the old headaches.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;It&#39;s ironic: C++ became easier to write precisely when we got AI tools trained on decades of C++ code, patterns, and best practices. The accumulated wisdom of the C++ community is now available on-demand, making the language&#39;s complexity manageable in a way it never was before.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;They still can&#39;t do the hard code, I recently wrote a &lt;a href=&#34;https://github.com/zackb/hyprwat/tree/main/src/wayland&#34;&gt;Wayland&lt;/a&gt;  + &lt;a href=&#34;https://github.com/zackb/hyprwat/tree/main/src/renderer&#34;&gt;EGL backend&lt;/a&gt; for &lt;a href=&#34;https://github.com/ocornut/imgui&#34;&gt;Dear ImGui&lt;/a&gt; and the AI was totally useless, but they handle the tedious stuff that used to make C++ a chore.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h3&gt;C++ is great again.&lt;/h3&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;I&#39;m writing C++ daily now, and I&#39;m enjoying it. I never, ever would have thought I&#39;d say that again. I am having fun, and getting excited about &lt;a href=&#34;https://github.com/zackb/code/blob/master/cpp/mimic/src/main.cpp&#34;&gt;programming things I couldn&#39;t do in other languages&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;The language gives me the control and performance I need without the tedious manual memory management and verbose boilerplate that drove me away. It&#39;s the super language and probably always will be.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Modern C++ found the sweet spot: it kept the zero-cost abstractions and raw power, but added the ergonomics that developers expect in 2024. It&#39;s not perfect of course, the language is still complex, and there&#39;s legacy baggage, but it&#39;s legitimately great.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;If you walked away from C++ years ago, give it another look. You might be surprised at what you find.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;If you wish you could learn C++ and harness its power without the frustrations, now is the time, fire up &lt;a href=&#34;https://antigravity.google/&#34;&gt;Antigravity&lt;/a&gt; and get going.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;The king has reclaimed its throne.&lt;/p&gt;&#xA;</description><pubDate>Mon, 24 Nov 2025 00:00:00 +0000</pubDate><guid>https://zackbartel.com/blog/2025/11/cpp-is-great/</guid></item><item><title>Hyprwat: A Wayland native menu tool for Hyprland</title><link>https://zackbartel.com/blog/2025/10/hyprwat/</link><description>&lt;br&gt;&#xA;&lt;h3&gt;What is &lt;a href=&#34;https://github.com/zackb/hyprwat/&#34;&gt;Hyprwat&lt;/a&gt;?&lt;/h3&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://github.com/zackb/hyprwat/&#34;&gt;Hyprwat&lt;/a&gt; is a menu tool designed specifically for Hyprland and Wayland compositor. It allows users to create and manage customizable menus that can be accessed directly from the Hyprland environment.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;It creates a popup menu at your cursor position where you can select from a list of options. It also has built-in support for WiFi network selection, audio input/output device selection, wallpapers, and custom menus. Custom menus can be defined using simple YAML configuration files.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;The name is inspired by some &lt;a href=&#34;https://www.destroyallsoftware.com/talks/wat&#34;&gt;javascript bashing video&lt;/a&gt; I saw many years ago. But, also like &amp;quot;What do you need?&amp;quot;... because it&#39;s a menu tool.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Right now you can:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Create a list of key / value pairs that show up in a menu, the selected value is printed to stdout.&lt;/li&gt;&#xA;&lt;li&gt;Create an input menu, where the user can type in a value that is printed to stdout.&lt;/li&gt;&#xA;&lt;li&gt;Create password menu, where the user can type in a masked value that is printed to stdout.&lt;/li&gt;&#xA;&lt;li&gt;Select from available WiFi networks (dbus + NetworkManager).&lt;/li&gt;&#xA;&lt;li&gt;Select audio input/output devices (pipewire).&lt;/li&gt;&#xA;&lt;li&gt;Select wallpaper (hyprpaper).&lt;/li&gt;&#xA;&lt;li&gt;Create custom menus with a bunch of GUI controls using YAML configuration files.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3&gt;What does it look like?&lt;/h3&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Integrate with powerprofilesctl to switch power profiles:&#xA;&lt;img src=&#34;/img/hyprwat/powerprofiles.png&#34; alt=&#34;Hyprwat Screenshot&#34;&gt;&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Change wallpapers and lockscreen:&#xA;&lt;img src=&#34;/img/hyprwat/wallpapers.png&#34; alt=&#34;Hyprwat Screenshot&#34;&gt;&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Connect to wifi networks:&#xA;&lt;img src=&#34;/img/hyprwat/wifi.png&#34; alt=&#34;Hyprwat Screenshot&#34;&gt;&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h3&gt;Why?&lt;/h3&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;I created hyprwat to fill a gap in the Wayland ecosystem for a simple, flexible, and visually appealing menu system that can be easily integrated into various use cases. Particularly, for waybar I needed a menu selection for wifi networks, power profiles, pavcontrol, etc.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Then, I wanted an easier way to switch wallpapers and lockscreen and found the existing solutions too hard, so I just added it into hyprwat. I&#39;ll keep doing this, it&#39;s fun.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://github.com/zackb/code/tree/master/cpp/wat&#34;&gt;My first attempt&lt;/a&gt; used SDL2/3 and ImGui, but I didn&#39;t like the menu being a window (there&#39;s no &lt;a href=&#34;https://wayland.app/protocols/wlr-layer-shell-unstable-v1&#34;&gt;layer shell&lt;/a&gt; support in SDL). So I rewrote it using pure Wayland protocols and EGL for rendering.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;If nothing else it&#39;s a pretty &lt;a href=&#34;https://github.com/zackb/hyprwat/tree/main/src/wayland&#34;&gt;cool cpp wrapper for Wayland protocols&lt;/a&gt;.&lt;/p&gt;&#xA;</description><pubDate>Mon, 06 Oct 2025 00:00:00 +0000</pubDate><guid>https://zackbartel.com/blog/2025/10/hyprwat/</guid></item><item><title>Returning to Linux</title><link>https://zackbartel.com/blog/2025/02/return-to-linux/</link><description>&lt;h3&gt;Mac OS X is getting worse&lt;/h3&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Update&lt;/strong&gt;&lt;/em&gt;: Liquid Glass is awful, and the number of bugs in all of Apple&#39;s platforms is no longer acceptable. My 2017 &lt;em&gt;&lt;strong&gt;iMac Pro&lt;/strong&gt;&lt;/em&gt; was working fine for what I needed. Yes, it&#39;s old but for what I do (edit C code in vim, play chess online) it was great. But after installing Tahoe it&#39;s noticeably slower, buggier, and plaqued with permission popus and &amp;quot;unexpected error&amp;quot; dialogs. My Apple &lt;em&gt;&lt;strong&gt;TV&lt;/strong&gt;&lt;/em&gt; is annoying now, there are too many clicks to do anything, it&#39;s slower, the remote stopped working correctly. My &lt;em&gt;&lt;strong&gt;watch&lt;/strong&gt;&lt;/em&gt; is slower, battery life worse, again with the UI taking more taps to do what I used to be able to do. Hopefully the post-Allan Dye era will be a renaissance for Apple software like the post-Johny Ive era is for hardware.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Apple&#39;s hardware is by far the best on the market, but I feel the software has been deteriorating for some time. Gatekeeper is becoming more and more invasive, and the constant permissions dialogs are getting annoying.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;For average users It&#39;s probably all for the better, but as a developer I no longer want to use a computer I can&#39;t make software (freely) for and share with people.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;In addition, I feel I&#39;ve become too reliant on iCloud and some other overpriced Apple services.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;I love the Mac. Some of the &lt;a href=&#34;https://techcrunch.com/2012/12/06/frequency-iphone-app/&#34;&gt;most&lt;/a&gt; &lt;a href=&#34;https://github.com/zackb/iOS&#34;&gt;important&lt;/a&gt; &lt;a href=&#34;https://apps.apple.com/us/developer/zack-bartel/id1069647834&#34;&gt;software&lt;/a&gt; I&#39;ve ever written was for Apple OSs. When Mac OS X first came out it seemed like the perfect OS. I was an Apple and Unix geek so it felt magical.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h3&gt;I Love Linux&lt;/h3&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;I was a Linux desktop user for many years before switching to Mac OS X in 2009.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;My &lt;a href=&#34;/dad&#34;&gt;dad&lt;/a&gt; and I installed a Redhat distribution with kernel version 1.1.0 around 1995, and I&#39;ve been using Linux on and off ever since. As a kid, I was absolutely fascinated by the idea of open source software and the ability to modify and share code freely.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;In college I worked at the &lt;a href=&#34;https://osuosl.org/&#34;&gt;OSU Open Source Lab&lt;/a&gt;, where I was introduced to &lt;a href=&#34;https://en.wikipedia.org/wiki/Ion_(window_manager)&#34;&gt;ion3&lt;/a&gt;. I was instantly hooked on tiling window managers and used ion3 for years. This was the ultimate developer experience.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h3&gt;i use arch btw&lt;/h3&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;&lt;img src=&#34;/img/linux/rice.png&#34; alt=&#34;Hyprland Rice&#34; title=&#34;Hyprland Rice&#34;&gt;&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;After a completely unrelated need arose to have a Linux development environment, I decided to dip my toe in the water. I bought a $300 Acer laptop and installed Arch, and Kali (I work in cybersec).&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;The funniest thing about the whole process is how awful the pre-loaded Windows experience was. Their beloved OOBE experience is a joke. It took me hours to get the system into a usable state, and it was unusably slow.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Anyway, I missed ion3, tried i3 (it&#39;s not for me), and then found &lt;a href=&#34;https://hyprland.org/&#34;&gt;Hyprland&lt;/a&gt;. It&#39;s amazing. I can&#39;t believe how amazing it is. It is absolutely perfect for me. I can&#39;t impress upon you enough how amazing Hyprland is.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Hyprland did take days to setup, but that&#39;s what I wanted. Although, you can get up and running in a matter of minutes, tweaking everything to be just how I wanted it took a while. And (hopefully) I&#39;ll keep improving it forever. If you don&#39;t want that there are plenty of good alternatives (xfce).&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;The other thing is, this setup is &lt;strong&gt;SO FAST&lt;/strong&gt;. Even on a budget laptop, everything is snappy and responsive.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h3&gt;A non-exhaustive list of things I use&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://hypr.land&#34;&gt;Hyprland&lt;/a&gt; / Wayland (tiling window manager)&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://ghostty.org/&#34;&gt;Ghostty&lt;/a&gt; (terminal)&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://neovim.io/&#34;&gt;Neovim&lt;/a&gt; (for everything)&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://wiki.hypr.land/Hypr-Ecosystem/&#34;&gt;hypr*&lt;/a&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;hyprpaper&lt;/li&gt;&#xA;&lt;li&gt;hypridle&lt;/li&gt;&#xA;&lt;li&gt;hyprlock&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/Alexays/Waybar&#34;&gt;Waybar&lt;/a&gt; (status bar)&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/davatorium/rofi&#34;&gt;Rofi&lt;/a&gt; (app launcher)&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.thunderbird.net&#34;&gt;Thunderbird&lt;/a&gt;  (email - I can&#39;t find anything better)&#xA;&lt;ul&gt;&#xA;&lt;li&gt;aerc was pretty cool but there is way too much html email these days&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://zen-browser.app/&#34;&gt;Zen&lt;/a&gt; (browser)&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://bitwarden.com/&#34;&gt;Bitwarden&lt;/a&gt; (password manager)&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://thunar.org/&#34;&gt;Thunar&lt;/a&gt; (file manager)&lt;/li&gt;&#xA;&lt;li&gt;A bunch of hacks to get iCloud CardDav and CalDav working&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;br&gt;&#xA;&lt;h3&gt;Anyway,&lt;/h3&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;I&#39;m back on Linux full time now, and I couldn&#39;t be happier. It&#39;s not just for developers anymore. &lt;code&gt;time.Now().Format(&amp;quot;2006&amp;quot;)&lt;/code&gt; is the year of the Linux desktop.&lt;/p&gt;&#xA;</description><pubDate>Wed, 05 Feb 2025 00:00:00 +0000</pubDate><guid>https://zackbartel.com/blog/2025/02/return-to-linux/</guid></item><item><title>Why I Stopped Trying to Improve at Chess</title><link>https://zackbartel.com/blog/2023/03/chess-for-fun/</link><description>&lt;h3&gt;I&#39;ve played chess off and on since I was a kid&lt;/h3&gt;&#xA;&lt;p&gt;I remember the excitement of learning the rules, beating my dad (he let me win), the thrill of my first rated victory, and the countless hours spent poring over strategies and tactics. Over the years, I reached a respectable level of play, competing in local tournaments and climbing the ranks.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;However, as time went on, I found myself increasingly frustrated. The more I studied and practiced, the more I realized how much there was to learn. Chess is a game of infinite complexity, and no matter how hard I tried, I felt like I was hitting a ceiling. My progress slowed to a crawl, and the joy I once found in the game began to wane. What was really the kicker was when I hit an all-time high rating of 1895 (chess.com) and then promptly dropped back down to 1700 within a few weeks. It was disheartening to see my hard work seemingly unravel and never quite reach the heights I once had.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;I was spending nights and weekends reading books, doing tactics puzzles, and analyzing games, but it felt like I was running in place. The more I learned, the more I realized how much I didn&#39;t know. It was a humbling experience, but also a frustrating one. I began to question whether the time and effort I was putting into chess was worth it.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Games weren&#39;t fun anymore; they felt like chores. I found myself dreading the start of a game, which used to be adrenaline filled excitement. The pressure to improve and perform well was taking the joy out of the game. I started to see chess as a source of stress rather than a source of enjoyment.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h3&gt;Then, one night I said &amp;quot;fuck it&amp;quot;&lt;/h3&gt;&#xA;&lt;p&gt;and decided to just blitz out a bunch of games. It made me realize that chess was supposed to be fun, not a source of frustration. I&#39;m never going to be a grandmaster and I don&#39;t have to feel like I should aspire to that. I started playing for the sheer enjoyment of the game, without worrying about my rating or performance. I played casual games with friends, tried tricky and trappy gambits online, and simply enjoyed the process of playing. The thrill of finding a brilliancy, laughing at silly blunders that lost me games. I can&#39;t understate how upset I would typically get if I made a mistake and/or lost a game.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;This realization reinvigorated my play. I once again look forward to starting a game, tough situations in the mid-game, king-and-pawn endgames. The joy for the game returned! I know I will probably not improve my rating much for the remainder of my playing days, but I really don&#39;t care. What I care about is being in a community of people who love the game as much as I do, and enjoying the process of playing. Chess is a beautiful game, and I&#39;m grateful to have it in my life, even if I&#39;m not the best player out there.&lt;/p&gt;&#xA;</description><pubDate>Tue, 14 Mar 2023 00:00:00 +0000</pubDate><guid>https://zackbartel.com/blog/2023/03/chess-for-fun/</guid></item><item><title>I love the English Opening</title><link>https://zackbartel.com/blog/2022/05/the-english-opening/</link><description>&lt;h3&gt;I fell out of love with 1.d4 and the London System.&lt;/h3&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;I&#39;ll always have a soft spot for it in my heart as it&#39;s responsible for a lot of my early chess success. But I&#39;m not a system player, and the London System felt like playing chess on autopilot.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;On paper, it makes sense. Low theory, solid structure, you can play it against almost anything. Just put your pieces on their natural squares: Bf4, e3, Nf3, Nbd2, Bd3, c3 and then what? London players always fall down here.&lt;/p&gt;&#xA;&lt;p&gt;I&#39;d clawed my way up to around 1600, feeling pretty good about my chess. My London was solid, my Queen&#39;s Gambit sharp, and I could navigate the gambits well enough to stay out of danger. Then something shifted.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Once I reached a certain rating threshold everyone knew their theory. I would get crushed with 1.e4 by opponents who had memorized the best lines.&lt;/p&gt;&#xA;&lt;p&gt;Suddenly, every opponent knew their theory cold. They&#39;d rattle off the first fifteen moves without thinking. They&#39;d steer me into playing their game. And I realized: I was spending more time memorizing opening lines than actually improving at chess.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;I needed a change. But I wasn&#39;t about to become &lt;em&gt;that guy&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Except it felt soulless. Every game looked the same. I wasn&#39;t responding to what my opponent was doing; I was just executing a setup regardless of what was happening on the other side of the board. It was chess on autopilot.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;And the players I encountered who played the London? They had this glazed look in their eyes during the opening, like they were going through a checklist. &amp;quot;Bf4, check. E3, check. Nf3, check.&amp;quot; No spark, no creativity, no actual chess decisions until move 15.&lt;/p&gt;&#xA;&lt;p&gt;I distinctly remember sitting across from an opponent who played the London against my King&#39;s Indian setup, and I could see him mentally running through his system. He wasn&#39;t looking at &lt;em&gt;my&lt;/em&gt; position. He was following a recipe. When I threw in an early ...Ne4 to disrupt his flow, he sat there for twenty minutes because he&#39;d never actually learned to think in the opening. He&#39;d only learned to &lt;em&gt;deploy&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;I refused to become that player. I&#39;d rather memorize theory than give up on actually playing chess in the opening.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;But there had to be a better way.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h2&gt;Hello 1.c4&lt;/h2&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;That&#39;s when I discovered the English Opening, and it completely transformed my game.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;The English Opening (1.c4) seemed almost too quiet when I first looked at it. After years of fighting for the center with 1.e4 or 1.d4, starting with a flank pawn felt... indirect? But then I started playing it, and I realized: this was everything the London pretended to be, but actually delivered.&lt;/p&gt;&#xA;&lt;p&gt;The English gets you out of theory fast, not because you&#39;re ignoring your opponent&#39;s moves, but because you&#39;re creating unique positions they haven&#39;t memorized. There&#39;s no &amp;quot;English System&amp;quot; where you mindlessly deploy pieces to predetermined squares. Every game demands actual chess thinking from move one.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h2&gt;Why It Catches Everyone Off Guard&lt;/h2&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Here&#39;s the beautiful thing: most players at the club level have prepared something against 1.e4 and 1.d4. They&#39;ve got their Sicilian or French or King&#39;s Indian or Nimzo-Indian ready to go.&lt;/p&gt;&#xA;&lt;p&gt;But 1.c4? They pause. You can see it on their face. &amp;quot;Wait, what&#39;s he doing?&amp;quot;&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;They&#39;re out of their prep by move two. Not because you&#39;ve done something gimmicky, but because the English is genuinely flexible and can transpose to many different structures. They can&#39;t just follow their memorized lines - they have to actually evaluate the position.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;One of my favorite moments was against a 1900-rated player who clearly had a Najdorf repertoire memorized to move 25. After 1.c4, he thought for fifteen minutes on move one. He ended up playing 1...e5, we reached a Reversed Sicilian, and suddenly &lt;em&gt;I&lt;/em&gt; was the one playing Sicilian structures (from the better side!) while he struggled with positions he&#39;d never studied.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h2&gt;Aggressive or Positional: Your Choice&lt;/h2&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;What really sold me on the English is its flexibility - and this is where it completely diverges from something like the London.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;The London commits you to a specific structure and plan. You&#39;re playing for a slow positional squeeze almost every game. Sure, sometimes you get a kingside attack going, but you&#39;re fundamentally locked into a style.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;The English? It can be whatever you need it to be.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Against a King&#39;s Indian setup, I can play the Botvinnik System (my favorite) with g3, Bg2, and Nc3, building for a slow positional grind where I control the center from afar.&lt;/p&gt;&#xA;&lt;p&gt;Against a symmetrical structure (1.c4 c5), I can play aggressively with Nc3, g3, Bg2, e4!, blasting open the center and creating tactical complications.&lt;/p&gt;&#xA;&lt;p&gt;Against the more classical setups with ...e5, I can reverse my beloved Sicilian structures and play for the same types of attacks my opponents used to play against me, except now I have an extra tempo.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;The choice is mine. I&#39;m responding to what&#39;s on the board, reading the position, making real chess decisions. You know, actually playing chess.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h2&gt;Getting Out of Theory, Into Chess&lt;/h2&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Here&#39;s the thing about theory: it&#39;s useful until it isn&#39;t. And at some point, every opening becomes about understanding ideas rather than memorizing moves.&lt;/p&gt;&#xA;&lt;p&gt;The English forced me to understand chess rather than memorize it. Because there&#39;s no set &amp;quot;system,&amp;quot; I had to learn:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;How to control the center from a distance&lt;/li&gt;&#xA;&lt;li&gt;When to break with d4 or e4 vs. when to keep the pawn structure fluid&lt;/li&gt;&#xA;&lt;li&gt;How to evaluate transpositions (Is this Reversed Sicilian better than a normal one? Should I allow this transposition to a Queen&#39;s Gambit Declined?)&lt;/li&gt;&#xA;&lt;li&gt;How to handle space disadvantages when opponents claim the center with ...e5 and ...d5&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;These are real chess skills. Pattern recognition, positional understanding, strategic thinking. Not &amp;quot;if he plays ...Na5, I play b4, and if he takes, I play a3.&amp;quot; And it has made me a much better player overall.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h2&gt;The Freedom of Flexibility&lt;/h2&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Maybe the best part? No two English Opening games feel the same.&lt;/p&gt;&#xA;&lt;p&gt;With the London, every game felt like déjà vu. Same structure, same pieces on the same squares, same middlegame ideas.&lt;/p&gt;&#xA;&lt;p&gt;With the English, I might get a Reversed Dragon one game, a Hedgehog structure the next, a Symmetrical Variation the game after that, and occasionally it transposes into something completely different like a Catalan or Queen&#39;s Indian.&lt;/p&gt;&#xA;&lt;p&gt;It keeps chess fresh. It keeps me engaged. And it keeps my opponents off-balance.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h2&gt;The Dirty Little Secret&lt;/h2&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Want to know the truth? The English has plenty of theory. There are whole books on the Symmetrical Variation alone. The Reversed Sicilian lines can get incredibly deep.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;But here&#39;s the difference: your opponents haven&#39;t studied it.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;At the club level, nearly everyone has studied the Open Sicilian or the Najdorf or the French Defense. Almost nobody has studied the Symmetrical English or the Reversed Dragon in depth. So even though theory exists, you&#39;re functionally out of book by move five.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;And unlike the London, where being &amp;quot;out of book&amp;quot; just means you&#39;re playing generic moves, being out of book in the English means you&#39;re in rich, complex positions where understanding matters more than memory.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h2&gt;My Rating Thanks Me&lt;/h2&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Since switching to the English, my rating has climbed steadily. It didn&#39;t at first, I took a big hit, but in the long run it has gotten me farther than I think the London could have. More importantly, I&#39;ve improved at chess in general.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;I&#39;m not winning because I memorized better lines. I&#39;m winning because I&#39;m making better decisions, understanding positions more deeply, and outplaying opponents in the middlegame.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;The opening gets me to playable, interesting positions where both sides have chances. Then the game is decided by who understands chess better, not who memorized more theory the night before.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;And I&#39;ve avoided becoming a London player, which frankly might be the biggest victory of all.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h2&gt;Tired of Theory?&lt;/h2&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;If you&#39;re facing the theory wall, if you&#39;re tired of memorizing, if you want to play chess instead of reciting it, give the English a try.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Just promise me one thing: don&#39;t treat it like the London. Don&#39;t look for a &amp;quot;system&amp;quot; you can play mindlessly. The English demands that you think, that you evaluate, that you respond to what your opponent is doing.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;That&#39;s what makes it beautiful.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Start with 1.c4, follow basic opening principles, and see where the game takes you. You might be surprised at how much chess you&#39;ve been missing while you were memorizing theory.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;And you definitely won&#39;t end up with that glazed London System look in your eyes.&lt;/p&gt;&#xA;</description><pubDate>Thu, 05 May 2022 00:00:00 +0000</pubDate><guid>https://zackbartel.com/blog/2022/05/the-english-opening/</guid></item><item><title>Kubernetes Loves Go</title><link>https://zackbartel.com/blog/2022/01/kubernetes-and-go/</link><description>&lt;p&gt;I&#39;ve been working with Kubernetes and Go for years now. There&#39;s something deeply satisfying about this combination. Kubernetes gives you this incredibly powerful orchestration platform, and Go provides the perfect language to extend it. The two were made for each other, and nowhere is this more apparent than when you need a custom controller to solve a particularly gnarly scaling problem.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h2&gt;One Pod Per User&lt;/h2&gt;&#xA;&lt;p&gt;Our system had an unusual but deliberate architecture: one pod per user. Each user would &amp;quot;reserve&amp;quot; a pod for their exclusive use during a session. Think of it like a personalized sandbox environment, isolated, secure, and fully theirs.&lt;/p&gt;&#xA;&lt;p&gt;There were a lot of challenges. These pods weren&#39;t instant. Startup time was significant enough that we couldn&#39;t just spin them up on-demand. Users expect immediate access, so we needed a pool of pre-warmed pods sitting ready to go.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h2&gt;Why Standard Scaling Wasn&#39;t Enough&lt;/h2&gt;&#xA;&lt;p&gt;My first attempt was using Kubernetes&#39; Horizontal Pod Autoscaler with custom metrics from Prometheus. After all, that&#39;s what it&#39;s for, right? But as time went on it became clear that HPA wasn&#39;t sophisticated enough for what we needed.&lt;/p&gt;&#xA;&lt;p&gt;Our scaling logic had to account for:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Time-based patterns&lt;/strong&gt;: We needed different pool sizes at 2 PM versus 2 AM. User demand followed predictable daily rhythms.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Velocity and acceleration&lt;/strong&gt;: A steady increase in reservations required different handling than a sudden spike. If someone tweeted a link to our service and we got a flood of new users, we needed to scale &lt;em&gt;fast&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Smart pool management&lt;/strong&gt;: The pool couldn&#39;t just be &amp;quot;any X pods.&amp;quot; We needed to track which pods were reserved, which were ready, and which were warming up.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Standard metrics-based scaling gave us crude reactive behavior. We needed something predictive and loaded with business logic.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h2&gt;The Custom Controller&lt;/h2&gt;&#xA;&lt;p&gt;This is where Go and Kubernetes really shine together. I decided to build a custom controller (technically an operator, since it manages a custom resource) using the excellent &lt;code&gt;controller-runtime&lt;/code&gt; library.&lt;/p&gt;&#xA;&lt;p&gt;None of this is the actual code, but here&#39;s a simplified version.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h3&gt;The Custom Resource Definition&lt;/h3&gt;&#xA;&lt;p&gt;I wanted it to be cute, so I defined a CRD to represent our pod pool:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion: apiextensions.k8s.io/v1&#xA;kind: CustomResourceDefinition&#xA;metadata:&#xA;  name: podpools.scaling.example.com&#xA;spec:&#xA;  group: scaling.example.com&#xA;  versions:&#xA;    - name: v1&#xA;      served: true&#xA;      storage: true&#xA;      schema:&#xA;        openAPIV3Schema:&#xA;          type: object&#xA;          properties:&#xA;            spec:&#xA;              type: object&#xA;              properties:&#xA;                minPoolSize:&#xA;                  type: integer&#xA;                  minimum: 0&#xA;                maxPoolSize:&#xA;                  type: integer&#xA;                  minimum: 1&#xA;                targetReadyPods:&#xA;                  type: integer&#xA;                  minimum: 0&#xA;                scaleUpVelocityThreshold:&#xA;                  type: number&#xA;                scaleUpAccelerationThreshold:&#xA;                  type: number&#xA;                timeBasedScaling:&#xA;                  type: array&#xA;                  items:&#xA;                    type: object&#xA;                    properties:&#xA;                      hourStart:&#xA;                        type: integer&#xA;                      hourEnd:&#xA;                        type: integer&#xA;                      targetPoolSize:&#xA;                        type: integer&#xA;                podTemplate:&#xA;                  type: object&#xA;                  x-kubernetes-preserve-unknown-fields: true&#xA;            status:&#xA;              type: object&#xA;              properties:&#xA;                readyPods:&#xA;                  type: integer&#xA;                reservedPods:&#xA;                  type: integer&#xA;                warmingPods:&#xA;                  type: integer&#xA;                currentVelocity:&#xA;                  type: number&#xA;                currentAcceleration:&#xA;                  type: number&#xA;                lastScaleTime:&#xA;                  type: string&#xA;                  format: date-time&#xA;  scope: Namespaced&#xA;  names:&#xA;    plural: podpools&#xA;    singular: podpool&#xA;    kind: PodPool&#xA;    shortNames:&#xA;    - pp&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;This CRD let us declare a &lt;code&gt;PodPool&lt;/code&gt; resource with all the knobs I needed: time-based scaling windows, velocity thresholds, and the pod template to use.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h3&gt;The Controller Logic&lt;/h3&gt;&#xA;&lt;p&gt;The controller&#39;s reconciliation loop is the brain of the operation. Here&#39;s the core logic I implemented in Go:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;func (r *PodPoolReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {&#xA;&#xA;    // find the PodPool&#xA;    podPool := &amp;amp;scalingv1.PodPool{}&#xA;    if err := r.Get(ctx, req.NamespacedName, podPool); err != nil {&#xA;        return ctrl.Result{}, client.IgnoreNotFound(err)&#xA;    }&#xA;&#xA;    // list all pods managed by this pool&#xA;    pods := &amp;amp;corev1.PodList{}&#xA;    if err := r.List(ctx, pods, &#xA;        client.InNamespace(req.Namespace),&#xA;        client.MatchingLabels{&amp;quot;pool&amp;quot;: podPool.Name}); err != nil {&#xA;        return ctrl.Result{}, err&#xA;    }&#xA;&#xA;    // pods by state&#xA;    ready, reserved, warming := r.categorizePods(pods.Items)&#xA;&#xA;    // calculate reservation velocity and acceleration&#xA;    velocity, acceleration := r.calculateMetrics(podPool, len(reserved))&#xA;&#xA;    // determine target pool size based on time of day&#xA;    targetSize := r.getTimeBasedTarget(podPool)&#xA;&#xA;    // check for spike conditions&#xA;    if velocity &amp;gt; podPool.Spec.ScaleUpVelocityThreshold || &#xA;       acceleration &amp;gt; podPool.Spec.ScaleUpAccelerationThreshold {&#xA;        targetSize = r.calculateSpikeTarget(targetSize, velocity, acceleration)&#xA;    }&#xA;&#xA;    // scale the pool&#xA;    currentTotal := len(ready) + len(warming)&#xA;    if currentTotal &amp;lt; targetSize {&#xA;        r.scaleUp(ctx, podPool, targetSize-currentTotal)&#xA;    } else if currentTotal &amp;gt; targetSize &amp;amp;&amp;amp; len(ready) &amp;gt; podPool.Spec.TargetReadyPods {&#xA;        r.scaleDown(ctx, podPool, ready[podPool.Spec.TargetReadyPods:])&#xA;    }&#xA;&#xA;    // update state&#xA;    podPool.Status.ReadyPods = len(ready)&#xA;    podPool.Status.ReservedPods = len(reserved)&#xA;    podPool.Status.WarmingPods = len(warming)&#xA;    podPool.Status.CurrentVelocity = velocity&#xA;    podPool.Status.CurrentAcceleration = acceleration&#xA;&#xA;    return ctrl.Result{RequeueAfter: 10 * time.Second}, r.Status().Update(ctx, podPool)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The beauty of this approach is that everything is declarative. I could define different scaling behaviors for different environments just by creating different &lt;code&gt;PodPool&lt;/code&gt; resources.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h3&gt;Measuring Velocity and Acceleration&lt;/h3&gt;&#xA;&lt;p&gt;The most interesting part was tracking reservation velocity and acceleration. I kept a sliding window of reservation timestamps in the controller&#39;s memory (later moved to the PodPool status for persistence):&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;func (r *PodPoolReconciler) calculateMetrics(podPool *scalingv1.PodPool, currentReserved int) (float64, float64) {&#xA;    now := time.Now()&#xA;&#xA;    // reservation history from status or cache&#xA;    history := r.getReservationHistory(podPool)&#xA;&#xA;    // add current count&#xA;    history = append(history, ReservationDataPoint{&#xA;        Timestamp: now,&#xA;        Count: currentReserved,&#xA;    })&#xA;&#xA;    // keep only last 5 minutes&#xA;    cutoff := now.Add(-5 * time.Minute)&#xA;    history = filterAfter(history, cutoff)&#xA;&#xA;    // calculate velocity (reservations per minute)&#xA;    velocity := 0.0&#xA;    if len(history) &amp;gt;= 2 {&#xA;        deltaCount := history[len(history)-1].Count - history[0].Count&#xA;        deltaTime := history[len(history)-1].Timestamp.Sub(history[0].Timestamp).Minutes()&#xA;        velocity = float64(deltaCount) / deltaTime&#xA;    }&#xA;&#xA;    // calculate acceleration (change in velocity)&#xA;    acceleration := 0.0&#xA;    if len(history) &amp;gt;= 3 {&#xA;        midpoint := len(history) / 2&#xA;        recentVelocity := calculateVelocityBetween(history[midpoint:])&#xA;        olderVelocity := calculateVelocityBetween(history[:midpoint])&#xA;        acceleration = recentVelocity - olderVelocity&#xA;    }&#xA;&#xA;    return velocity, acceleration&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;This gave us real-time awareness of not just &lt;em&gt;how many&lt;/em&gt; reservations were happening, but &lt;em&gt;how fast&lt;/em&gt; the rate was changing. When acceleration spiked, we knew something unusual was happening and could preemptively scale.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h2&gt;The Results&lt;/h2&gt;&#xA;&lt;p&gt;The controller worked beautifully. During normal hours, the pool would smoothly adjust based on our time-based configuration. During the night, we&#39;d have just enough pods to handle occasional use. During peak hours, the pool would be pre-scaled.&lt;/p&gt;&#xA;&lt;p&gt;But the real magic happened during spikes. When (hypothetically) some CEO accidentally tweeted a direct link to our service, the controller would detect the acceleration in reservations and immediately start scaling up. The pool would stay ahead of demand.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h2&gt;Why I Love This Stack&lt;/h2&gt;&#xA;&lt;p&gt;This project exemplified everything I love about Kubernetes and Go together. Kubernetes provides this amazing extension mechanism through CRDs and controllers. It doesn&#39;t try to solve every problem. Instead, it gives you the tools to solve your own problems in a native, first-class way.&lt;/p&gt;&#xA;&lt;p&gt;Go makes building these controllers almost effortless. The &lt;code&gt;controller-runtime&lt;/code&gt; library handles all the boilerplate of watching resources, managing work queues, and handling retries. The type safety catches bugs at compile time. The standard library has everything you need. And the resulting binary is a single executable that&#39;s trivial to containerize and deploy. This is why I love Go.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;I could have built a separate service that called the Kubernetes API to manage pods, but making it a proper operator meant it integrated seamlessly with the rest of our Kubernetes infrastructure. We could use &lt;code&gt;kubectl&lt;/code&gt; to inspect pool status, GitOps to manage pool configurations, and RBAC to control access.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;If you&#39;re working with Kubernetes and facing a scaling problem that doesn&#39;t fit the standard tools, don&#39;t be afraid to build a custom controller. With Go and &lt;code&gt;controller-runtime&lt;/code&gt;, it&#39;s easier than you might think, and the results can be truly powerful.&lt;/p&gt;&#xA;</description><pubDate>Tue, 18 Jan 2022 00:00:00 +0000</pubDate><guid>https://zackbartel.com/blog/2022/01/kubernetes-and-go/</guid></item><item><title>Callout: The Boxing App</title><link>https://zackbartel.com/blog/2020/08/callout/</link><description>&lt;br&gt;&#xA;&lt;img src=&#34;/img/callout/Box.png&#34; width=&#34;250&#34;/&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;I started boxing when I was in high school. My football coach didn&#39;t want some of us to have too much free time on our hands during the winter, and I got my ass kicked in wrestling. So, he took us to the local gym and taught us the basics. I&#39;d go on to compete in Golden Gloves, becoming a long time member of a local gym, and continuing the practice on and off into my 30s. Despite what you&#39;d think, I&#39;ve met some of the most compassionate and empatheic people in my life in boxing gyms. It&#39;s been a big part of my life.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Since I put the gloves down, I&#39;ve wanted a better way to train at home. So much so that I actually &lt;a href=&#34;https://github.com/zackb/code/blob/master/c/callout/callout.c&#34;&gt;wrote a C program&lt;/a&gt; to help.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;What I really wanted was someone, or something to call out punch combinations during a &amp;quot;round&amp;quot;.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h3&gt;Callout&lt;/h3&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;So I wrote &lt;a href=&#34;https://apps.apple.com/us/app/callout-the-boxing-app/id1473350118&#34;&gt;Callout - The Boxing App&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;The idea is pretty simple, but nothing like it exists. You select or create punch combinations to be called during a round, and iOS SpeechSythesizer does the rest. You can customize the speed, rest time, and variance of combinations called. You can even create your own combinations.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;It&#39;s available on the &lt;a href=&#34;https://apps.apple.com/us/app/callout-the-boxing-app/id1473350118&#34;&gt;App Store&lt;/a&gt; for free with an in-app purchase to unlock all features.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;</description><pubDate>Mon, 17 Aug 2020 00:00:00 +0000</pubDate><guid>https://zackbartel.com/blog/2020/08/callout/</guid></item><item><title>Back to Chess</title><link>https://zackbartel.com/blog/2020/06/back-to-chess/</link><description>&lt;h3&gt;Rediscovering Chess&lt;/h3&gt;&#xA;&lt;p&gt;I used to play chess quite a bit, and my brother Jake would always beat me. It kinda made me lose interest and not want to play at all. I&#39;ve never really liked things I&#39;m not good at, or at least things my brother is better at. But recently, I had a motivator I could not ignore.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;My &lt;a href=&#34;/dad&#34;&gt;father&lt;/a&gt; passed away last month, and while he was in the hospital, my brother and I had a lot of time to kill. We played chess a lot, and he beat me a lot.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Our entire lives have been a competition. We were the best baseball players in our town as kids, I hit more homeruns but he was better at almost everything else. But that doesn&#39;t matter because &lt;a href=&#34;https://www.youtube.com/watch?v=UjkuJPvMrI8&#34;&gt;chicks dig the longball&lt;/a&gt;, so I was kinda cool with my spot. He was always better at me at Madden, but I could cheese him with Mike Vick.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;But now, sitting in a hospital room 14 hours a day getting beat by him out of the opening almost every game made me hit my limit. I &lt;em&gt;really&lt;/em&gt; wanted to beat him.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;That&#39;s when I googled &amp;quot;chess openings&amp;quot; for the first time. I went back to the hotel, read some articles, watched some videos, and learned a few new openings. A couple days in, I started beating him. It felt amazing. That competitive fire reigniting and giving me a clear goal was just what I needed.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;So, I&#39;m back to playing chess. I&#39;m not sure how far I&#39;ll go with it, but for now, it&#39;s fun to have a new challenge and a way to connect with my brother. I&#39;m back on Lichess and can &lt;strong&gt;not&lt;/strong&gt; wait to see how far I can push it.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Checkmate, Jake.&lt;/p&gt;&#xA;</description><pubDate>Sat, 20 Jun 2020 00:00:00 +0000</pubDate><guid>https://zackbartel.com/blog/2020/06/back-to-chess/</guid></item><item><title>ForceCode: Morse with Force Touch</title><link>https://zackbartel.com/blog/2020/03/forcecode/</link><description>&lt;br&gt;&#xA;&lt;p&gt;My &lt;a href=&#34;/dad&#34;&gt;dad&lt;/a&gt; is in the hostpital and is physically impaired. He can still kinda use his iPhone, but with very limited dexterity. His speech is also impaired, so communicating with him is difficult.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;He&#39;s a ham radio operator and has been since he was a teenager. So I figured Morse was worth a shot.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Putting aside the physical limitations, I was sorely dissapointed in the existing Morse code apps. Most of them are learning apps, which is cool, but there&#39;s not much in the way of simple, efficient keying apps. The ones that do exist are clunky, ugly, and not very user friendly. It&#39;s probably okay, I mean who uses Morse code anymore.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;I downloaded a few apps and tried them out with him. None of them worked well for his situation. He couldn&#39;t lift his finger off the screen quickly enough to get the timing right.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Having an ungodly amount of free time on my hands, I decided to make my own app for him.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h3&gt;ForceCode&lt;/h3&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;I made an app called ForceCode that uses Force Touch to send Morse code. The idea is that instead of lifting your finger off the screen to end a dot or dash, you just vary the pressure of your touch. A light press is a dot, a hard press is a dash. This way, you can keep your finger on the screen the whole time and just vary the pressure as needed.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;I&#39;ve been programming for 15 years but this was life changing when it worked.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;The &lt;a href=&#34;https://github.com/zackb/forcecode&#34;&gt;source code&lt;/a&gt; is openly available as is the &lt;a href=&#34;https://apps.apple.com/us/app/force-code-morse-with-force/id1500814267&#34;&gt;free app&lt;/a&gt;. A neat &lt;a href=&#34;https://news.ycombinator.com/item?id=22651404&#34;&gt;Hacker News discussion&lt;/a&gt; happened that really made me feel good.&lt;/p&gt;&#xA;</description><pubDate>Sun, 22 Mar 2020 00:00:00 +0000</pubDate><guid>https://zackbartel.com/blog/2020/03/forcecode/</guid></item><item><title>Makyō Kicks Ass</title><link>https://zackbartel.com/blog/2019/09/makyo-kicks-ass/</link><description>&lt;br&gt;&#xA;&lt;h3&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Maky%C5%8D&#34;&gt;Makyō&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;One of my &lt;a href=&#34;https://brightwayzen.org/practice/zen-teachers/teachersbio/&#34;&gt;teacher&#39;s&lt;/a&gt; &lt;a href=&#34;https://stationhill.org/authors/kyogen-carlson/&#34;&gt;teachers&lt;/a&gt; called these &amp;quot;moments that make us dance&amp;quot;. These are moment during &lt;a href=&#34;https://en.wikipedia.org/wiki/Zazen&#34;&gt;zazen&lt;/a&gt; where we have some experience that feels profound, enlightening, or otherwise special. It might be a vision, a feeling of bliss, a sense of unity with the universe, or some other extraordinary experience.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Makyō (魔境) literally means &amp;quot;devil&#39;s realm&amp;quot; or &amp;quot;demon&#39;s realm&amp;quot;, and refers to these hallucinations or visions that can occur during &lt;a href=&#34;https://en.wikipedia.org/wiki/Zazen&#34;&gt;zazen&lt;/a&gt;. In Zen practice, makyō is often seen as a distraction or obstacle to true enlightenment, but I think it kicks ass.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Mean old Zen teachers usually dismiss these experiences as &amp;quot;makyō&amp;quot;, or illusions, and warn students not to get attached to them. There is a lot of wisdom in that. Getting attached to these kinds of things, while maybe pleasant, can lead us away from the point of Zen practice, which is to cultivate an understanding of the nature of reality and our place in it.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;You don&#39;t even have to have been practicing very long to experience really interesting &amp;quot;moments that make us dance&amp;quot;. I had only been sitting for a couple of months when I had my first &amp;quot;dance&amp;quot; experience. I thought something important had happenend, that I had broken through some new level of understanding. Holly shit, am I enlightened?&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;This of course was completely unhelpful. I grasped on tight to that feeling of accomplishment. And agonized as the feeling slipped away over the next couple of days. Then I was right back where I started, on my &lt;a href=&#34;https://en.wikipedia.org/wiki/Zafu&#34;&gt;zafu&lt;/a&gt; thinking with my legs crossed.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;h3&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Sesshin&#34;&gt;Sesshin&lt;/a&gt; - Silent Retreat&lt;/h3&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Recently, I went, for the second time, on a week-long silent retreat with &lt;a href=&#34;https://brightwayzen.org/&#34;&gt;Bright Way Zen&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;My first retreat was unpleasant. I was uncomfortable the whole time, I had no insights, I missed home, missed my kids, missed my wife, felt guilty about missing work, and was generally frustrated the whole time. I know it sounds weird, but please don&#39;t let this dissuade you from retreat. I&#39;ve talked to many people about their first experiences with sesshin and almost every single one of them regards their first time as something deeply moving and insightful. I think I&#39;m just a weirdo.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;This second retreat was different. If I&#39;m honest I think a big part of it was that my family had my back. Before, I was new to Zen and it was still this weird thing that dad did on the side. This time, my wife and kids were totally supportive and encouraging simply because it wasn&#39;t weird anymore. I know, practice shouldn&#39;t depend on circumstances, but it does.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;I enjoyed the retreat. Almost the entire time I felt comfortable, at ease, and happy to be there. The traditional &amp;quot;bump&amp;quot; of the first day or two was relatively easy, work periods, meeting with the teacher, meal and kitchen duties, and long periods of sitting all felt rhythmic (for lack of a better word). I &lt;a href=&#34;/blog/2017/05/zen-is-easy/&#34;&gt;shikantaza&lt;/a&gt;&#39;d the shit out of that retreat.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Towards the end, I got hit by it. A feeling of insight and understanding that was profoundly moving. I felt like I was seeing the world in a new way. Colors were brighter, sounds were clearer, and everything felt more alive. I felt a deep sense of connection to everything around me, and a sense of peace and contentment that I had never experienced before. All those Zen books and talks clicked into place.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;But most importantly, I recognized it for what it was. Makyō. Nothing more, nothing less. Oh, I &amp;quot;danced&amp;quot; but I didn&#39;t cling. I didn&#39;t even want the feeling to last, because I knew, while very pleasant, it wasn&#39;t real.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;But I also don&#39;t want to dismiss it. It was a beautiful experience, and I&#39;m grateful to have had it. It reminded me of the beauty of the world around me, and the potential for joy and connection that exists in every moment. It was a reminder that even though these experiences may be fleeting and illusory, they can still be deeply meaningful and transformative.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;&lt;p&gt;Falsely enlightened? Hell yeah. Makyō kicks ass.&lt;/p&gt;&#xA;&lt;br&gt;&#xA;</description><pubDate>Sun, 01 Sep 2019 00:00:00 +0000</pubDate><guid>https://zackbartel.com/blog/2019/09/makyo-kicks-ass/</guid></item></channel></rss>